Compare commits
22 Commits
1484eada3e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a14bc78b3b | ||
|
|
e81900fa79 | ||
|
|
16f5f49c99 | ||
|
|
989c5c05a5 | ||
|
|
5f69fc664a | ||
|
|
82153d31c9 | ||
|
|
a647fc5186 | ||
|
|
847631d94e | ||
|
|
5d9c8b4d0c | ||
|
|
60b7b34708 | ||
|
|
45f8d9bd26 | ||
|
|
671814fc81 | ||
|
|
f7fde32897 | ||
|
|
3bb1ca9b1c | ||
|
|
f2ac3d5fa6 | ||
|
|
a5d0729bba | ||
|
|
9eb15a4a5f | ||
|
|
f2d5263244 | ||
|
|
270a256444 | ||
|
|
23e9f624f4 | ||
|
|
739dd6613b | ||
|
|
532d404815 |
274
Cargo.lock
generated
274
Cargo.lock
generated
@@ -3,43 +3,111 @@
|
|||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "auto_mute_tool"
|
name = "auto_mute_cli"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"auto_mute_lib",
|
||||||
"widestring",
|
|
||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "auto_mute_gui"
|
||||||
version = "1.12.0"
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"auto_mute_lib",
|
||||||
|
"windows",
|
||||||
|
"windows-core",
|
||||||
|
"winresource",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "auto_mute_lib"
|
||||||
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"windows",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.39"
|
version = "1.0.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "serde"
|
||||||
version = "1.0.96"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.217"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -47,73 +115,193 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "toml"
|
||||||
version = "1.0.0"
|
version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "toml_datetime"
|
||||||
version = "1.0.0"
|
version = "0.6.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5babd2d3fcd28bcf9712ef93e437684156f5c46173a9718829aa57aa4aa37a8"
|
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.37.0"
|
version = "0.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
|
checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement",
|
"windows-implement",
|
||||||
"windows_aarch64_msvc",
|
"windows-interface",
|
||||||
"windows_i686_gnu",
|
"windows-result",
|
||||||
"windows_i686_msvc",
|
"windows-strings",
|
||||||
"windows_x86_64_gnu",
|
"windows-targets",
|
||||||
"windows_x86_64_msvc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.37.0"
|
version = "0.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67a1062e555f7d9d66fd1130ed4f7c6ec41a47529ee0850cd0e926d95b26bb14"
|
checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
"windows-tokens",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-tokens"
|
name = "windows-interface"
|
||||||
version = "0.37.0"
|
version = "0.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169"
|
checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d08106ce80268c4067c0571ca55a9b4e9516518eaa1a1fe9b37ca403ae1d1a34"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b888f919960b42ea4e11c2f408fadb55f78a9f236d5eef084103c8ce52893491"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.53.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.53.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.37.0"
|
version = "0.53.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
|
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.37.0"
|
version = "0.53.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
|
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.53.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.37.0"
|
version = "0.53.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
|
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.37.0"
|
version = "0.53.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
|
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.53.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.37.0"
|
version = "0.53.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
|
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winresource"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7276691b353ad4547af8c3268488d1311f4be791ffdc0c65b8cfa8f41eed693b"
|
||||||
|
dependencies = [
|
||||||
|
"toml",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|||||||
32
Cargo.toml
32
Cargo.toml
@@ -1,29 +1,3 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "auto_mute_tool"
|
members = ["crates/*"]
|
||||||
version = "0.1.0"
|
resolver = "2"
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
widestring = "1.0.0"
|
|
||||||
once_cell = "1.12.0"
|
|
||||||
|
|
||||||
[dependencies.windows]
|
|
||||||
version = "0.37.0"
|
|
||||||
features = [
|
|
||||||
"alloc",
|
|
||||||
"implement",
|
|
||||||
"Win32_Media_Audio",
|
|
||||||
"Win32_UI_Shell_PropertiesSystem",
|
|
||||||
"Data_Xml_Dom",
|
|
||||||
"Win32_UI",
|
|
||||||
"Win32_UI_Accessibility",
|
|
||||||
"Win32_Foundation",
|
|
||||||
"Win32_Security",
|
|
||||||
"Win32_System_Threading",
|
|
||||||
"Win32_UI_WindowsAndMessaging",
|
|
||||||
"Win32_System_Com",
|
|
||||||
"Win32_System_Com_StructuredStorage"
|
|
||||||
|
|
||||||
]
|
|
||||||
BIN
auto_mute_gui.exe
Normal file
BIN
auto_mute_gui.exe
Normal file
Binary file not shown.
26
crates/auto_mute_cli/Cargo.toml
Normal file
26
crates/auto_mute_cli/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "auto_mute_cli"
|
||||||
|
version = "0.2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies.windows]
|
||||||
|
version = "0.59.0"
|
||||||
|
features = [
|
||||||
|
"Win32_Media_Audio",
|
||||||
|
"Win32_UI_Shell_PropertiesSystem",
|
||||||
|
"Data_Xml_Dom",
|
||||||
|
"Win32_UI",
|
||||||
|
"Win32_UI_Accessibility",
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Security",
|
||||||
|
"Win32_System_Threading",
|
||||||
|
"Win32_UI_WindowsAndMessaging",
|
||||||
|
"Win32_System_Com",
|
||||||
|
"Win32_System_Com_StructuredStorage",
|
||||||
|
"Win32_Devices_FunctionDiscovery",
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
"Win32_System_WindowsProgramming",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
auto_mute_lib = { path = "../auto_mute_lib" }
|
||||||
48
crates/auto_mute_cli/src/main.rs
Normal file
48
crates/auto_mute_cli/src/main.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
env,
|
||||||
|
error::Error,
|
||||||
|
fs::{self, File},
|
||||||
|
io::{BufRead, BufReader}
|
||||||
|
};
|
||||||
|
|
||||||
|
use auto_mute_lib::muter::MuterThread;
|
||||||
|
use windows::{
|
||||||
|
Win32::{
|
||||||
|
Foundation::HANDLE,
|
||||||
|
Storage::FileSystem::{
|
||||||
|
FindCloseChangeNotification, FindFirstChangeNotificationW, FindNextChangeNotification,
|
||||||
|
FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||||
|
},
|
||||||
|
System::Threading::{WaitForSingleObject, INFINITE},
|
||||||
|
}, core::w,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn await_file_change(file_name: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
unsafe {
|
||||||
|
let md = fs::metadata(file_name)?.modified()?;
|
||||||
|
let handle = FindFirstChangeNotificationW(w!("."), false, FILE_NOTIFY_CHANGE_LAST_WRITE)?;
|
||||||
|
while md == fs::metadata(file_name)?.modified()? {
|
||||||
|
WaitForSingleObject(HANDLE(handle.0), INFINITE);
|
||||||
|
FindNextChangeNotification(handle)?;
|
||||||
|
}
|
||||||
|
println!("File change detected, restarting");
|
||||||
|
FindCloseChangeNotification(handle)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn load_mute_txt(file_name: &str) -> HashSet<String> {
|
||||||
|
let file = File::open(file_name).unwrap();
|
||||||
|
HashSet::from_iter(BufReader::new(file).lines().map(|line| line.unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
MuterThread::com_init();
|
||||||
|
let mute_file: String = env::args().nth(1).unwrap_or("mute.txt".to_string());
|
||||||
|
loop {
|
||||||
|
let mut _mt = MuterThread::new(load_mute_txt(&mute_file), None);
|
||||||
|
await_file_change(&mute_file).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
25
crates/auto_mute_gui/Cargo.toml
Normal file
25
crates/auto_mute_gui/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "auto_mute_gui"
|
||||||
|
version = "0.2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
winresource = "0.1.17"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
auto_mute_lib = { path = "../auto_mute_lib" }
|
||||||
|
windows-core = "0.59.0"
|
||||||
|
|
||||||
|
[dependencies.windows]
|
||||||
|
version = "0.59.0"
|
||||||
|
features = [
|
||||||
|
"Win32_UI",
|
||||||
|
"Win32_UI_Accessibility",
|
||||||
|
"Win32_UI_WindowsAndMessaging",
|
||||||
|
"Win32_UI_Shell",
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_System_LibraryLoader",
|
||||||
|
"Win32_Graphics_Gdi",
|
||||||
|
"Win32_System_LibraryLoader",
|
||||||
|
"Win32_UI_WindowsAndMessaging",
|
||||||
|
]
|
||||||
11
crates/auto_mute_gui/build.rs
Normal file
11
crates/auto_mute_gui/build.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
extern crate winresource;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if cfg!(target_os = "windows") {
|
||||||
|
winresource::WindowsResource::new()
|
||||||
|
.set_icon(r"C:\Users\ultra\ownCloud\ShitMuter\ShitMuter\icon1.ico")
|
||||||
|
.set_icon_with_id(r"C:\Users\ultra\ownCloud\ShitMuter\ShitMuter\icon1.ico", "ICO_UNMUTE")
|
||||||
|
.set_icon_with_id(r"C:\Users\ultra\ownCloud\ShitMuter\ShitMuter\icon2.ico", "ICO_MUTE")
|
||||||
|
.compile().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
181
crates/auto_mute_gui/src/main.rs
Normal file
181
crates/auto_mute_gui/src/main.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
env,
|
||||||
|
error::Error,
|
||||||
|
ffi::c_void,
|
||||||
|
fs::File,
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
sync::mpsc,
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
use auto_mute_lib::muter::{MuteChangeNotification, MuterThread};
|
||||||
|
use windows::Win32::{
|
||||||
|
Foundation::HWND,
|
||||||
|
System::LibraryLoader::GetModuleHandleW,
|
||||||
|
UI::{
|
||||||
|
Shell::{Shell_NotifyIconW, NIF_ICON, NIF_MESSAGE, NIM_ADD, NIM_MODIFY, NOTIFYICONDATAW},
|
||||||
|
WindowsAndMessaging::{LoadIconW, WM_USER},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use windows::{
|
||||||
|
core::*, Win32::{Foundation::*, Graphics::Gdi::ValidateRect, System::LibraryLoader::GetModuleHandleA, UI::WindowsAndMessaging::*},
|
||||||
|
};
|
||||||
|
|
||||||
|
use windows_core::w;
|
||||||
|
|
||||||
|
const WM_TRAY_MENU: u32 = WM_USER;
|
||||||
|
|
||||||
|
pub struct MagicTray {
|
||||||
|
pub notifdata: NOTIFYICONDATAW,
|
||||||
|
pub hwnd: HWND,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagicTray {
|
||||||
|
fn new() -> MagicTray {
|
||||||
|
unsafe {
|
||||||
|
MagicTray {
|
||||||
|
notifdata: NOTIFYICONDATAW {
|
||||||
|
cbSize: size_of::<NOTIFYICONDATAW>().try_into().unwrap(),
|
||||||
|
hWnd: HWND::default(),
|
||||||
|
uID: 1,
|
||||||
|
uFlags: NIF_ICON | NIF_MESSAGE,
|
||||||
|
uCallbackMessage: WM_TRAY_MENU,
|
||||||
|
hIcon: LoadIconW(
|
||||||
|
Some(GetModuleHandleW(None).unwrap().into()),
|
||||||
|
w!("ICO_UNMUTE"),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
hwnd: MagicTray::main_window().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn main_window() -> std::result::Result<HWND, Box<dyn Error>> {
|
||||||
|
unsafe {
|
||||||
|
let instance = GetModuleHandleA(None)?;
|
||||||
|
let window_class = s!("window");
|
||||||
|
|
||||||
|
let wc = WNDCLASSA {
|
||||||
|
hCursor: LoadCursorW(None, IDC_ARROW)?,
|
||||||
|
hInstance: instance.into(),
|
||||||
|
lpszClassName: window_class,
|
||||||
|
|
||||||
|
style: CS_HREDRAW | CS_VREDRAW,
|
||||||
|
lpfnWndProc: Some(wndproc),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let atom = RegisterClassA(&wc);
|
||||||
|
debug_assert!(atom != 0);
|
||||||
|
|
||||||
|
Ok(CreateWindowExA(
|
||||||
|
WINDOW_EX_STYLE::default(),
|
||||||
|
window_class,
|
||||||
|
s!("Tray Host"),
|
||||||
|
WS_OVERLAPPEDWINDOW,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_loop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
SetWindowLongPtrW(self.hwnd, GWLP_USERDATA, (&mut* self as *mut _) as isize);
|
||||||
|
self.notifdata.hWnd = self.hwnd;
|
||||||
|
Shell_NotifyIconW(NIM_ADD, &self.notifdata).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
let mut message = MSG::default();
|
||||||
|
while GetMessageA(&mut message, None, 0, 0).into() {
|
||||||
|
DispatchMessageA(&message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mute(&mut self, lparam: LPARAM) {
|
||||||
|
unsafe {
|
||||||
|
self.notifdata.hIcon = LoadIconW(
|
||||||
|
Some(GetModuleHandleW(None).unwrap().into()),
|
||||||
|
if lparam.0 == 0 {w!("ICO_UNMUTE")} else {w!("ICO_MUTE")},
|
||||||
|
).unwrap();
|
||||||
|
Shell_NotifyIconW(NIM_MODIFY, &self.notifdata).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MagicTray {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_mute_txt(file_name: &str) -> HashSet<String> {
|
||||||
|
let file = File::open(file_name).unwrap();
|
||||||
|
HashSet::from_iter(BufReader::new(file).lines().map(|line| line.unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
const WM_APP_CUSTOM: u32 = WM_APP + 1;
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
let mut tray = MagicTray::default();
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel::<MuteChangeNotification>();
|
||||||
|
|
||||||
|
MuterThread::com_init();
|
||||||
|
let mute_file: String = env::args().nth(1).unwrap_or("mute.txt".to_string());
|
||||||
|
let _mt = MuterThread::new(load_mute_txt(&mute_file), Some(tx));
|
||||||
|
|
||||||
|
let hwnd = tray.hwnd.0 as usize;
|
||||||
|
|
||||||
|
thread::spawn(move || loop {
|
||||||
|
if let Ok(mpsc_msg) = rx.recv() {
|
||||||
|
match mpsc_msg {
|
||||||
|
MuteChangeNotification::MuteChanged(val) => unsafe {
|
||||||
|
PostMessageA(
|
||||||
|
Some(HWND(hwnd as *mut c_void)),
|
||||||
|
WM_APP_CUSTOM,
|
||||||
|
WPARAM(0),
|
||||||
|
LPARAM(if val { 1 } else { 0 }),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tray.run_loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "system" fn wndproc(hwnd: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
|
||||||
|
unsafe {
|
||||||
|
let mt = (GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut MagicTray).as_mut();
|
||||||
|
match message {
|
||||||
|
WM_PAINT => {
|
||||||
|
println!("WM_PAINT");
|
||||||
|
_ = ValidateRect(Some(hwnd), None);
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
WM_DESTROY => {
|
||||||
|
println!("WM_DESTROY");
|
||||||
|
PostQuitMessage(0);
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
WM_APP_CUSTOM => {
|
||||||
|
mt.unwrap().set_mute(lparam);
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
_ => DefWindowProcA(hwnd, message, wparam, lparam),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
crates/auto_mute_lib/Cargo.toml
Normal file
27
crates/auto_mute_lib/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "auto_mute_lib"
|
||||||
|
version = "0.2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
windows-core = "0.59.0"
|
||||||
|
|
||||||
|
[dependencies.windows]
|
||||||
|
version = "0.59.0"
|
||||||
|
features = [
|
||||||
|
"Win32_Media_Audio",
|
||||||
|
"Win32_UI_Shell_PropertiesSystem",
|
||||||
|
"Data_Xml_Dom",
|
||||||
|
"Win32_UI",
|
||||||
|
"Win32_UI_Accessibility",
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Security",
|
||||||
|
"Win32_System_Threading",
|
||||||
|
"Win32_UI_WindowsAndMessaging",
|
||||||
|
"Win32_System_Com",
|
||||||
|
"Win32_System_Com_StructuredStorage",
|
||||||
|
"Win32_Devices_FunctionDiscovery",
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
"Win32_System_WindowsProgramming",
|
||||||
|
"Win32_System_Variant"
|
||||||
|
]
|
||||||
47
crates/auto_mute_lib/src/device_notification_client.rs
Normal file
47
crates/auto_mute_lib/src/device_notification_client.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use windows::Win32::Media::Audio::{
|
||||||
|
IMMNotificationClient, IMMNotificationClient_Impl, DEVICE_STATE,
|
||||||
|
};
|
||||||
|
pub trait DeviceNotificationObserver {
|
||||||
|
fn add_device(&self, device_id: &windows::core::PCWSTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[windows::core::implement(IMMNotificationClient)]
|
||||||
|
pub(crate) struct DeviceNotificationClient {
|
||||||
|
pub observer: Box<dyn DeviceNotificationObserver>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IMMNotificationClient_Impl for DeviceNotificationClient_Impl {
|
||||||
|
fn OnDeviceStateChanged(
|
||||||
|
&self,
|
||||||
|
_pwstrdeviceid: &windows::core::PCWSTR,
|
||||||
|
_dwnewstate: DEVICE_STATE,
|
||||||
|
) -> windows::core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn OnDeviceAdded(&self, pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
|
||||||
|
self.observer.add_device(pwstrdeviceid);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn OnDeviceRemoved(&self, _pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn OnDefaultDeviceChanged(
|
||||||
|
&self,
|
||||||
|
_flow: windows::Win32::Media::Audio::EDataFlow,
|
||||||
|
_role: windows::Win32::Media::Audio::ERole,
|
||||||
|
_pwstrdefaultdeviceid: &windows::core::PCWSTR,
|
||||||
|
) -> windows::core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn OnPropertyValueChanged(
|
||||||
|
&self,
|
||||||
|
_pwstrdeviceid: &windows_core::PCWSTR,
|
||||||
|
_key: &windows::Win32::Foundation::PROPERTYKEY,
|
||||||
|
) -> windows_core::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
6
crates/auto_mute_lib/src/lib.rs
Normal file
6
crates/auto_mute_lib/src/lib.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
mod device_notification_client;
|
||||||
|
mod pid_to_exe;
|
||||||
|
mod session_notification;
|
||||||
|
mod sm_session_notifier;
|
||||||
|
mod window_change;
|
||||||
|
pub mod muter;
|
||||||
182
crates/auto_mute_lib/src/muter.rs
Normal file
182
crates/auto_mute_lib/src/muter.rs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
error::Error,
|
||||||
|
ffi::OsString,
|
||||||
|
path::Path,
|
||||||
|
ptr::null_mut,
|
||||||
|
sync::mpsc::{self, Receiver, Sender},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::sm_session_notifier::SMSessionNotifierThread;
|
||||||
|
use crate::window_change::WindowChangeMonitor;
|
||||||
|
use windows::{
|
||||||
|
core::Interface,
|
||||||
|
Win32::{
|
||||||
|
Media::Audio::{IAudioSessionControl2, ISimpleAudioVolume},
|
||||||
|
System::Com::{CoInitializeEx, COINIT_MULTITHREADED},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::pid_to_exe::pid_to_exe_path;
|
||||||
|
enum MuterMessage {
|
||||||
|
WindowChange(String),
|
||||||
|
AddSession(IAudioSessionControl2),
|
||||||
|
Exit(),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MuteChangeNotification {
|
||||||
|
MuteChanged(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for MuterMessage {}
|
||||||
|
|
||||||
|
struct SessionMuter {
|
||||||
|
sessions: Vec<IAudioSessionControl2>,
|
||||||
|
mute_executables: HashSet<String>,
|
||||||
|
mute_flag: bool,
|
||||||
|
_session_notifier: SMSessionNotifierThread,
|
||||||
|
_win_change_mon: WindowChangeMonitor,
|
||||||
|
rx: Receiver<MuterMessage>,
|
||||||
|
notify_tx: Option<Sender<MuteChangeNotification>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionMuter {
|
||||||
|
fn new(
|
||||||
|
mute_executables: HashSet<String>,
|
||||||
|
rx: Receiver<MuterMessage>,
|
||||||
|
tx: Sender<MuterMessage>,
|
||||||
|
notify_tx: Option<Sender<MuteChangeNotification>>,
|
||||||
|
) -> SessionMuter {
|
||||||
|
SessionMuter {
|
||||||
|
sessions: Vec::new(),
|
||||||
|
mute_executables,
|
||||||
|
mute_flag: true,
|
||||||
|
_session_notifier: {
|
||||||
|
let tx = tx.clone();
|
||||||
|
SMSessionNotifierThread::new(Box::new(move |session| {
|
||||||
|
tx.send(MuterMessage::AddSession(session)).unwrap();
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
_win_change_mon: {
|
||||||
|
WindowChangeMonitor::start(Box::new(move |s| {
|
||||||
|
tx.send(MuterMessage::WindowChange(s.to_owned())).unwrap();
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
rx,
|
||||||
|
notify_tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) {
|
||||||
|
loop {
|
||||||
|
let msg = self.rx.recv().unwrap();
|
||||||
|
match msg {
|
||||||
|
MuterMessage::WindowChange(win) => self.notify_window_changed(&win),
|
||||||
|
MuterMessage::AddSession(session) => self.add_session(session).unwrap(),
|
||||||
|
MuterMessage::Exit() => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_session(
|
||||||
|
self: &mut SessionMuter,
|
||||||
|
session: IAudioSessionControl2,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
if let Ok(file_name) = self.session_to_filename(&session) {
|
||||||
|
let fn_str = file_name.to_string_lossy().to_string();
|
||||||
|
if self.mute_executables.contains(&fn_str) {
|
||||||
|
println!("Adding session from: {:?}", fn_str);
|
||||||
|
unsafe {
|
||||||
|
let volume: ISimpleAudioVolume = session.cast()?;
|
||||||
|
volume.SetMute(self.mute_flag, null_mut())?;
|
||||||
|
}
|
||||||
|
self.sessions.push(session);
|
||||||
|
} else {
|
||||||
|
println!("Skipping session from: {:?}", fn_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mute_all(self: &mut SessionMuter, mute: bool) {
|
||||||
|
unsafe {
|
||||||
|
self.notify_tx
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.send(MuteChangeNotification::MuteChanged(mute)).ok());
|
||||||
|
let results = self
|
||||||
|
.sessions
|
||||||
|
.iter()
|
||||||
|
.map(|session_control2| session_control2.cast::<ISimpleAudioVolume>())
|
||||||
|
.map(|vol_result| vol_result?.SetMute(mute, null_mut()));
|
||||||
|
for err in results.filter_map(|x| x.err()) {
|
||||||
|
println!("Error muting a session: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify_window_changed(self: &mut SessionMuter, path: &str) {
|
||||||
|
let binding = Path::new(path)
|
||||||
|
.file_name()
|
||||||
|
.expect("failed to extract filename from path");
|
||||||
|
let file_name = binding.to_os_string().to_string_lossy().to_string();
|
||||||
|
let mute_flag = !self.mute_executables.contains(&file_name);
|
||||||
|
if mute_flag != self.mute_flag {
|
||||||
|
self.mute_flag = mute_flag;
|
||||||
|
self.set_mute_all(self.mute_flag);
|
||||||
|
println!(
|
||||||
|
"Mute set to {} due to foreground window: {}",
|
||||||
|
self.mute_flag, file_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn session_to_filename(
|
||||||
|
self: &mut SessionMuter,
|
||||||
|
session: &IAudioSessionControl2,
|
||||||
|
) -> Result<OsString, Box<dyn Error>> {
|
||||||
|
unsafe {
|
||||||
|
let pid = session.GetProcessId()?;
|
||||||
|
let path = pid_to_exe_path(pid)?;
|
||||||
|
let file_name = Path::new(&path)
|
||||||
|
.file_name()
|
||||||
|
.ok_or("Failed to extract filename from path")?;
|
||||||
|
Ok(file_name.to_os_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for SessionMuter {}
|
||||||
|
|
||||||
|
pub struct MuterThread {
|
||||||
|
handle: Option<JoinHandle<()>>,
|
||||||
|
sender: Sender<MuterMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MuterThread {
|
||||||
|
pub fn com_init() {
|
||||||
|
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED).unwrap() };
|
||||||
|
}
|
||||||
|
pub fn new(
|
||||||
|
s: HashSet<String>,
|
||||||
|
notify_tx: Option<Sender<MuteChangeNotification>>,
|
||||||
|
) -> MuterThread {
|
||||||
|
let (sender, receiver) = mpsc::channel::<MuterMessage>();
|
||||||
|
MuterThread {
|
||||||
|
sender: sender.clone(),
|
||||||
|
handle: Some(thread::spawn(move || {
|
||||||
|
let mut muter = SessionMuter::new(s, receiver, sender, notify_tx);
|
||||||
|
muter.run();
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MuterThread {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(handle) = self.handle.take() {
|
||||||
|
self.sender.send(MuterMessage::Exit()).unwrap();
|
||||||
|
handle.join().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
crates/auto_mute_lib/src/pid_to_exe.rs
Normal file
29
crates/auto_mute_lib/src/pid_to_exe.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use windows::Win32::{
|
||||||
|
Foundation::{CloseHandle, MAX_PATH},
|
||||||
|
System::Threading::{
|
||||||
|
OpenProcess, QueryFullProcessImageNameW, PROCESS_QUERY_LIMITED_INFORMATION
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn pid_to_exe_path(pid: u32) -> Result<String, Box<dyn Error>> {
|
||||||
|
let mut exe_name: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
|
||||||
|
let mut size: u32 = exe_name.capacity().try_into().unwrap();
|
||||||
|
unsafe {
|
||||||
|
let process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid)?;
|
||||||
|
QueryFullProcessImageNameW(
|
||||||
|
process,
|
||||||
|
Default::default(),
|
||||||
|
windows::core::PWSTR(exe_name.as_mut_ptr()),
|
||||||
|
&mut size,
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
CloseHandle(process)?;
|
||||||
|
|
||||||
|
exe_name.set_len(size.try_into().unwrap());
|
||||||
|
}
|
||||||
|
let process_name = String::from_utf16_lossy(&exe_name);
|
||||||
|
Ok(process_name)
|
||||||
|
}
|
||||||
23
crates/auto_mute_lib/src/session_notification.rs
Normal file
23
crates/auto_mute_lib/src/session_notification.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use windows::{
|
||||||
|
core::{implement, Interface},
|
||||||
|
Win32::Media::Audio::{
|
||||||
|
IAudioSessionControl, IAudioSessionControl2, IAudioSessionNotification, IAudioSessionNotification_Impl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[implement(IAudioSessionNotification)]
|
||||||
|
pub(crate) struct SessionNotification {
|
||||||
|
pub(crate) observer: Box<dyn SessionObserver>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SessionObserver {
|
||||||
|
fn add_session(&self, session: IAudioSessionControl2);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IAudioSessionNotification_Impl for SessionNotification_Impl {
|
||||||
|
fn OnSessionCreated(&self,newsession:windows_core::Ref<'_, IAudioSessionControl>) -> windows::core::Result<()> {
|
||||||
|
let ses: IAudioSessionControl2 = newsession.as_ref().unwrap().cast().unwrap();
|
||||||
|
self.observer.add_session(ses);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
205
crates/auto_mute_lib/src/sm_session_notifier.rs
Normal file
205
crates/auto_mute_lib/src/sm_session_notifier.rs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
sync::mpsc::{self, Receiver, Sender},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
device_notification_client::{DeviceNotificationClient, DeviceNotificationObserver},
|
||||||
|
session_notification::{SessionNotification, SessionObserver},
|
||||||
|
};
|
||||||
|
use windows::{
|
||||||
|
core::{Interface, PCWSTR},
|
||||||
|
Win32::{
|
||||||
|
Devices::FunctionDiscovery::PKEY_Device_FriendlyName,
|
||||||
|
Media::Audio::{
|
||||||
|
eRender, IAudioSessionControl2, IAudioSessionManager2, IAudioSessionNotification,
|
||||||
|
IMMDeviceEnumerator, IMMNotificationClient, MMDeviceEnumerator, DEVICE_STATE_ACTIVE, IMMDevice,
|
||||||
|
},
|
||||||
|
System::Com::{
|
||||||
|
CoCreateInstance, StructuredStorage::PropVariantClear, CLSCTX_ALL, STGM_READ,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum SMMessage {
|
||||||
|
Session(IAudioSessionControl2),
|
||||||
|
Device(PCWSTR),
|
||||||
|
Exit(),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for SMMessage {}
|
||||||
|
|
||||||
|
struct SessionToMessage {
|
||||||
|
sender: Sender<SMMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionObserver for SessionToMessage {
|
||||||
|
fn add_session(&self, session: IAudioSessionControl2) {
|
||||||
|
self.sender
|
||||||
|
.send(SMMessage::Session(session))
|
||||||
|
.unwrap_or_else(|_| println!("Failed to add new session"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DeviceToMessage {
|
||||||
|
sender: Sender<SMMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceNotificationObserver for DeviceToMessage {
|
||||||
|
fn add_device(&self, device_id: &windows::core::PCWSTR) {
|
||||||
|
self.sender
|
||||||
|
.send(SMMessage::Device(*device_id))
|
||||||
|
.unwrap_or_else(|_| println!("Failed to add new device"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct SMSessionNotifier {
|
||||||
|
device_enumerator: IMMDeviceEnumerator,
|
||||||
|
device_notification_client: IMMNotificationClient,
|
||||||
|
session_notification: IAudioSessionNotification,
|
||||||
|
session_managers: Vec<IAudioSessionManager2>,
|
||||||
|
notification_function: Box<dyn Fn(IAudioSessionControl2)>,
|
||||||
|
receiver: Receiver<SMMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SMSessionNotifier {
|
||||||
|
pub(crate) fn new(
|
||||||
|
callback: Box<dyn Fn(IAudioSessionControl2)>,
|
||||||
|
sender: mpsc::Sender<SMMessage>,
|
||||||
|
receiver: mpsc::Receiver<SMMessage>,
|
||||||
|
) -> SMSessionNotifier {
|
||||||
|
SMSessionNotifier {
|
||||||
|
session_managers: Vec::new(),
|
||||||
|
device_enumerator: unsafe {
|
||||||
|
CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap()
|
||||||
|
},
|
||||||
|
device_notification_client: IMMNotificationClient::from(DeviceNotificationClient {
|
||||||
|
observer: Box::new(DeviceToMessage {
|
||||||
|
sender: sender.clone(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
session_notification: IAudioSessionNotification::from(SessionNotification {
|
||||||
|
observer: Box::new(SessionToMessage { sender }),
|
||||||
|
}),
|
||||||
|
notification_function: callback,
|
||||||
|
receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn boot_devices(self: &mut SMSessionNotifier) -> Result<(), Box<dyn Error>> {
|
||||||
|
unsafe {
|
||||||
|
self.device_enumerator
|
||||||
|
.RegisterEndpointNotificationCallback(&self.device_notification_client)?;
|
||||||
|
let device_collection = self
|
||||||
|
.device_enumerator
|
||||||
|
.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?;
|
||||||
|
for device in (0..device_collection.GetCount()?).map(|x| device_collection.Item(x)) {
|
||||||
|
let mmdevice = device?;
|
||||||
|
self.add_device(mmdevice)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_device_by_id(self: &mut SMSessionNotifier, id: &PCWSTR) -> Result<(), Box<dyn Error>> {
|
||||||
|
unsafe {
|
||||||
|
let device_enumerator: IMMDeviceEnumerator =
|
||||||
|
CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?;
|
||||||
|
let device = device_enumerator.GetDevice(*id)?;
|
||||||
|
self.add_device(device)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn add_device(
|
||||||
|
self: &mut SMSessionNotifier,
|
||||||
|
device: IMMDevice,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
let session_manager: IAudioSessionManager2 = device.Activate(CLSCTX_ALL, None)?;
|
||||||
|
session_manager.RegisterSessionNotification(&self.session_notification)?;
|
||||||
|
let session_enumerator = session_manager.GetSessionEnumerator()?;
|
||||||
|
let session_count = session_enumerator.GetCount()?;
|
||||||
|
let device_sessions = (0..session_count)
|
||||||
|
.map(|idx| {
|
||||||
|
session_enumerator
|
||||||
|
.GetSession(idx)
|
||||||
|
.and_then(|session| session.cast())
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<IAudioSessionControl2>, _>>()?;
|
||||||
|
for session in device_sessions {
|
||||||
|
(self.notification_function)(session);
|
||||||
|
}
|
||||||
|
let prop_store = device.OpenPropertyStore(STGM_READ)?;
|
||||||
|
let mut prop_var = prop_store.GetValue(&PKEY_Device_FriendlyName)?;
|
||||||
|
println!(
|
||||||
|
"Device Added: {} Existing Sessions: {}",
|
||||||
|
prop_var.to_string(),
|
||||||
|
session_count
|
||||||
|
);
|
||||||
|
PropVariantClear(&mut prop_var)?;
|
||||||
|
|
||||||
|
self.session_managers.push(session_manager);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self.boot_devices()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let msg = self.receiver.recv()?;
|
||||||
|
match msg {
|
||||||
|
SMMessage::Session(session) => {
|
||||||
|
(self.notification_function)(session);
|
||||||
|
}
|
||||||
|
SMMessage::Device(id) => {
|
||||||
|
self.add_device_by_id(&id)?;
|
||||||
|
}
|
||||||
|
SMMessage::Exit() => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SMSessionNotifier {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.session_managers
|
||||||
|
.drain(..)
|
||||||
|
.map(|x| x.UnregisterSessionNotification(&self.session_notification))
|
||||||
|
.collect::<Result<(), _>>()
|
||||||
|
.unwrap();
|
||||||
|
self.device_enumerator
|
||||||
|
.UnregisterEndpointNotificationCallback(&self.device_notification_client)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SMSessionNotifierThread {
|
||||||
|
handle: Option<JoinHandle<()>>,
|
||||||
|
sender: Sender<SMMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SMSessionNotifierThread {
|
||||||
|
pub fn new(s: Box<dyn Fn(IAudioSessionControl2) + Send>) -> SMSessionNotifierThread {
|
||||||
|
let (sender, receiver) = mpsc::channel::<SMMessage>();
|
||||||
|
SMSessionNotifierThread {
|
||||||
|
sender: sender.clone(),
|
||||||
|
handle: Some(thread::spawn(move || {
|
||||||
|
let mut session_notifier = SMSessionNotifier::new(s, sender, receiver);
|
||||||
|
session_notifier.run().unwrap();
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SMSessionNotifierThread {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(handle) = self.handle.take() {
|
||||||
|
self.sender.send(SMMessage::Exit()).unwrap();
|
||||||
|
handle.join().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
crates/auto_mute_lib/src/window_change.rs
Normal file
127
crates/auto_mute_lib/src/window_change.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
use std::{
|
||||||
|
sync::{atomic::AtomicU32, Arc, Mutex},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use windows::Win32::{
|
||||||
|
Foundation::{HWND, LPARAM, WPARAM},
|
||||||
|
System::Threading::GetCurrentThreadId,
|
||||||
|
UI::{
|
||||||
|
Accessibility::{SetWinEventHook, UnhookWinEvent, HWINEVENTHOOK},
|
||||||
|
WindowsAndMessaging::{
|
||||||
|
DispatchMessageW, GetForegroundWindow, GetMessageW, GetWindowThreadProcessId,
|
||||||
|
PostThreadMessageW, TranslateMessage, EVENT_SYSTEM_FOREGROUND,
|
||||||
|
EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT, WINEVENT_SKIPOWNPROCESS, WM_QUIT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::pid_to_exe::pid_to_exe_path;
|
||||||
|
|
||||||
|
type WinCallback = Box<dyn Fn(&str) + Send>;
|
||||||
|
static WIN_CHANGE_CALLBACK: Mutex<Option<WinCallback>> = Mutex::new(None);
|
||||||
|
|
||||||
|
unsafe extern "system" fn win_event_proc(
|
||||||
|
_hook: HWINEVENTHOOK,
|
||||||
|
event: u32,
|
||||||
|
_hwnd: HWND,
|
||||||
|
_id_object: i32,
|
||||||
|
_id_child: i32,
|
||||||
|
_dw_event_thread: u32,
|
||||||
|
_dwms_event_time: u32,
|
||||||
|
) {
|
||||||
|
if event == EVENT_SYSTEM_FOREGROUND || event == EVENT_SYSTEM_MINIMIZEEND {
|
||||||
|
let mut pid: u32 = 0;
|
||||||
|
// Instead of using the hwnd passed to us in the wineventproc, call GetForegroundWindow again because Hollow Knight does something weird
|
||||||
|
// and immediately fires again with the hwnd for explorer?
|
||||||
|
let hwnd = GetForegroundWindow();
|
||||||
|
GetWindowThreadProcessId(hwnd, Some(&mut pid));
|
||||||
|
pid_to_exe_path(pid)
|
||||||
|
.map(|path| WIN_CHANGE_CALLBACK.lock().unwrap().as_ref().unwrap()(&path))
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
println!(
|
||||||
|
"Error finding process with pid {} for hwnd: {:?}: {:?}",
|
||||||
|
pid, hwnd, err
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn await_win_change_events(callback: WinCallback) {
|
||||||
|
*WIN_CHANGE_CALLBACK.lock().unwrap() = Some(callback);
|
||||||
|
unsafe {
|
||||||
|
let fg_event = SetWinEventHook(
|
||||||
|
EVENT_SYSTEM_FOREGROUND,
|
||||||
|
EVENT_SYSTEM_FOREGROUND,
|
||||||
|
None,
|
||||||
|
Some(win_event_proc),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
||||||
|
);
|
||||||
|
let min_event = SetWinEventHook(
|
||||||
|
EVENT_SYSTEM_MINIMIZEEND,
|
||||||
|
EVENT_SYSTEM_MINIMIZEEND,
|
||||||
|
None,
|
||||||
|
Some(win_event_proc),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut msg: MSG = MSG::default();
|
||||||
|
while GetMessageW(&mut msg, None, 0, 0).0 > 0 {
|
||||||
|
TranslateMessage(&msg).unwrap();
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
UnhookWinEvent(fg_event).unwrap();
|
||||||
|
UnhookWinEvent(min_event).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WindowChangeMonitor {
|
||||||
|
join_handle: Option<JoinHandle<()>>,
|
||||||
|
win_thread_id: Arc<AtomicU32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WindowChangeMonitor {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(join_handle) = self.join_handle.take() {
|
||||||
|
let tid: u32 = self
|
||||||
|
.win_thread_id
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
if tid != 0 {
|
||||||
|
unsafe {
|
||||||
|
PostThreadMessageW(tid, WM_QUIT, WPARAM(0), LPARAM(0))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
join_handle
|
||||||
|
.join()
|
||||||
|
.expect("Unable to terminate window change thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowChangeMonitor {
|
||||||
|
pub(crate) fn start(f: WinCallback) -> WindowChangeMonitor {
|
||||||
|
let win_thread_id = Arc::new(AtomicU32::new(0));
|
||||||
|
let join_handle = {
|
||||||
|
let win_thread_id = win_thread_id.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
win_thread_id.store(
|
||||||
|
unsafe { GetCurrentThreadId() },
|
||||||
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
await_win_change_events(f);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
WindowChangeMonitor {
|
||||||
|
join_handle: Some(join_handle),
|
||||||
|
win_thread_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
mute.txt
Normal file
23
mute.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Baba is You.exe
|
||||||
|
Celeste.exe
|
||||||
|
Cemu.exe
|
||||||
|
DarkSoulsII.exe
|
||||||
|
GRIS.exe
|
||||||
|
Hades.exe
|
||||||
|
LWIW.exe
|
||||||
|
Super Mario 64 Plus.exe
|
||||||
|
Trials of Mana-Win64-Shipping.exe
|
||||||
|
VampireSurvivors.exe
|
||||||
|
cod.exe
|
||||||
|
hollow_knight.exe
|
||||||
|
sm64.us.f3dex2e.exe
|
||||||
|
teardown.exe
|
||||||
|
underrail.exe
|
||||||
|
valheim.exe
|
||||||
|
Balatro.exe
|
||||||
|
Tactical Breach Wizards.exe
|
||||||
|
eldenring.exe
|
||||||
|
ACValhalla.exe
|
||||||
|
DQMonsters3.exe
|
||||||
|
Patrick's Parabox.exe
|
||||||
|
Signal.exe
|
||||||
BIN
res/muted.ico
Normal file
BIN
res/muted.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
BIN
res/unmuted.ico
Normal file
BIN
res/unmuted.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
230
src/main.rs
230
src/main.rs
@@ -1,230 +0,0 @@
|
|||||||
mod pid_to_exe;
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::{
|
|
||||||
error::Error,
|
|
||||||
ffi::c_void,
|
|
||||||
mem,
|
|
||||||
ptr::{self, null_mut}, sync::{Arc, Mutex}
|
|
||||||
};
|
|
||||||
|
|
||||||
use widestring::U16CStr;
|
|
||||||
use windows::{
|
|
||||||
Win32::{
|
|
||||||
Foundation::{HINSTANCE, HWND},
|
|
||||||
Media::Audio::{
|
|
||||||
eRender, IAudioSessionManager2, IMMDeviceEnumerator, MMDeviceEnumerator,
|
|
||||||
DEVICE_STATE_ACTIVE, IAudioSessionNotification, IAudioSessionControl, IAudioSessionNotification_Impl, IAudioSessionControl2, IMMNotificationClient, IMMNotificationClient_Impl,
|
|
||||||
},
|
|
||||||
System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED},
|
|
||||||
UI::{
|
|
||||||
Accessibility::{SetWinEventHook, HWINEVENTHOOK},
|
|
||||||
WindowsAndMessaging::{
|
|
||||||
DispatchMessageW, GetMessageW, GetWindowThreadProcessId, TranslateMessage,
|
|
||||||
EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT,
|
|
||||||
WINEVENT_SKIPOWNPROCESS,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, core::{Interface, PCWSTR},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::pid_to_exe::pid_to_exe_path;
|
|
||||||
|
|
||||||
unsafe extern "system" fn win_event_proc(
|
|
||||||
_hook: HWINEVENTHOOK,
|
|
||||||
event: u32,
|
|
||||||
hwnd: HWND,
|
|
||||||
_id_object: i32,
|
|
||||||
_id_child: i32,
|
|
||||||
_dw_event_thread: u32,
|
|
||||||
_dwms_event_time: u32,
|
|
||||||
) {
|
|
||||||
if event == EVENT_SYSTEM_FOREGROUND || event == EVENT_SYSTEM_MINIMIZEEND {
|
|
||||||
let mut pid: u32 = 0;
|
|
||||||
GetWindowThreadProcessId(hwnd, &mut pid);
|
|
||||||
println!("{:?}", pid_to_exe_path(pid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn win_event_hook_loop() {
|
|
||||||
unsafe {
|
|
||||||
SetWinEventHook(
|
|
||||||
EVENT_SYSTEM_FOREGROUND,
|
|
||||||
EVENT_SYSTEM_FOREGROUND,
|
|
||||||
HINSTANCE::default(),
|
|
||||||
Some(win_event_proc),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
|
||||||
);
|
|
||||||
SetWinEventHook(
|
|
||||||
EVENT_SYSTEM_MINIMIZEEND,
|
|
||||||
EVENT_SYSTEM_MINIMIZEEND,
|
|
||||||
HINSTANCE::default(),
|
|
||||||
Some(win_event_proc),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut msg: MSG = MSG::default();
|
|
||||||
while GetMessageW(&mut msg, HWND::default(), 0, 0).as_bool() {
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessageW(&msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Okay, the main way this will function:
|
|
||||||
- MMDeviceEnumerator - get all current devices
|
|
||||||
- RegisterEndpointNotificationCallback - get updates on device add/remove
|
|
||||||
- For each new device found
|
|
||||||
- Activate Session Manager on Device
|
|
||||||
- Register Session Notification on Session Manager
|
|
||||||
- Get session enumerator from session manager
|
|
||||||
- Detect sessions from applications we care about by getting session PID and mapping to process names using existing code
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[windows::core::implement(IAudioSessionNotification)]
|
|
||||||
struct SessionNotification {
|
|
||||||
sessions: Arc<Mutex<Vec<IAudioSessionControl>>>
|
|
||||||
}
|
|
||||||
impl IAudioSessionNotification_Impl for SessionNotification {
|
|
||||||
fn OnSessionCreated(self: &SessionNotification,newsession: &core::option::Option<IAudioSessionControl>) -> windows::core::Result<()> {
|
|
||||||
println!("Got OSC call");
|
|
||||||
let ses = newsession.as_ref().unwrap().clone();
|
|
||||||
let s2: IAudioSessionControl2 = ses.cast().unwrap();
|
|
||||||
println!("Holy shit, new session created! {}", unsafe { pid_to_exe_path(s2.GetProcessId().unwrap()).unwrap() });
|
|
||||||
self.sessions.lock().unwrap().push(ses);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for SessionNotification {
|
|
||||||
|
|
||||||
fn drop(&mut self) {
|
|
||||||
println!("SN drop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[windows::core::implement(IMMNotificationClient)]
|
|
||||||
struct DeviceNotificationClient {}
|
|
||||||
|
|
||||||
impl Drop for DeviceNotificationClient {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
println!("DNC drop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IMMNotificationClient_Impl for DeviceNotificationClient {
|
|
||||||
fn OnDeviceStateChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_dwnewstate:u32) -> windows::core::Result<()> {
|
|
||||||
println!("OnDeviceStateChanged");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn OnDeviceAdded(&self,_pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
|
|
||||||
// EUGH!
|
|
||||||
println!("OnDeviceAdded");
|
|
||||||
/*
|
|
||||||
KING.lock().unwrap().add_device_by_id(pwstrdeviceid).map_err(|error| {
|
|
||||||
println!("Error adding device: {:?}", error);
|
|
||||||
}).unwrap_or(());
|
|
||||||
*/
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn OnDeviceRemoved(&self,_pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
|
|
||||||
println!("OnDeviceRemoved");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn OnDefaultDeviceChanged(&self,_flow:windows::Win32::Media::Audio::EDataFlow,_role:windows::Win32::Media::Audio::ERole,_pwstrdefaultdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
|
|
||||||
println!("OnDefaultDeviceChanged");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn OnPropertyValueChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_key: &windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY) -> windows::core::Result<()> {
|
|
||||||
println!("OnPropertyValueChanged");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SessionKing {
|
|
||||||
sessions: Arc<Mutex<Vec<IAudioSessionControl>>>,
|
|
||||||
device_enumerator : IMMDeviceEnumerator,
|
|
||||||
dnc : IMMNotificationClient
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SessionKing {
|
|
||||||
fn new() -> SessionKing {
|
|
||||||
SessionKing {
|
|
||||||
sessions: Arc::new(Mutex::new(Vec::new())),
|
|
||||||
device_enumerator: unsafe { CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap() },
|
|
||||||
dnc: IMMNotificationClient::from(DeviceNotificationClient {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn boot_devices(self: &mut SessionKing) -> Result<String, Box<dyn Error>> {
|
|
||||||
unsafe {
|
|
||||||
(Interface::vtable(&self.device_enumerator).RegisterEndpointNotificationCallback)(Interface::as_raw(&self.device_enumerator), self.dnc.as_raw()).ok()?;
|
|
||||||
let device_collection =
|
|
||||||
self.device_enumerator.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?;
|
|
||||||
let dc = 0..device_collection.GetCount()?;
|
|
||||||
let devices = dc.map(|x| device_collection.Item(x));
|
|
||||||
for x in devices {
|
|
||||||
let mmdevice = x?;
|
|
||||||
self.add_device(mmdevice)?;
|
|
||||||
}
|
|
||||||
return Ok("Done".to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_device_by_id(self: &mut SessionKing, id: &PCWSTR) -> Result<(), Box<dyn Error>> {
|
|
||||||
unsafe {
|
|
||||||
let device_enumerator: IMMDeviceEnumerator =
|
|
||||||
CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?;
|
|
||||||
let device = device_enumerator.GetDevice(id)?;
|
|
||||||
self.add_device(device)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn add_device(self: &mut SessionKing, mmdevice: windows::Win32::Media::Audio::IMMDevice) -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut sm_ptr: *mut c_void = null_mut();
|
|
||||||
mmdevice.Activate(
|
|
||||||
&<IAudioSessionManager2 as ::windows::core::Interface>::IID,
|
|
||||||
CLSCTX_ALL,
|
|
||||||
null_mut(),
|
|
||||||
ptr::addr_of_mut!(sm_ptr),
|
|
||||||
)?;
|
|
||||||
let sm: IAudioSessionManager2 = mem::transmute(sm_ptr);
|
|
||||||
let sesenum = sm.GetSessionEnumerator()?;
|
|
||||||
let sn_count = sesenum.GetCount()?;
|
|
||||||
let mut cdses = (0..sn_count).map(|idx| sesenum.GetSession(idx)).collect::<Result<Vec<IAudioSessionControl>,_>>()?;
|
|
||||||
self.sessions.lock().unwrap().append(&mut cdses);
|
|
||||||
sm.RegisterSessionNotification(IAudioSessionNotification::from( SessionNotification {
|
|
||||||
sessions: self.sessions.clone()
|
|
||||||
}))?;
|
|
||||||
let str = U16CStr::from_ptr_str(mmdevice.GetId()?.0).to_string_lossy();
|
|
||||||
println!("Device Added: {} {}", str, sn_count);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for SessionKing {}
|
|
||||||
unsafe impl Sync for SessionKing {}
|
|
||||||
|
|
||||||
static KING : Lazy<Arc<Mutex<SessionKing>>> = Lazy::new(|| {
|
|
||||||
Arc::new(Mutex::new(SessionKing::new()))
|
|
||||||
});
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
unsafe {
|
|
||||||
CoInitializeEx(null_mut(), COINIT_MULTITHREADED).unwrap();
|
|
||||||
}
|
|
||||||
KING.lock().unwrap().boot_devices().unwrap();
|
|
||||||
win_event_hook_loop();
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use windows::Win32::{
|
|
||||||
Foundation::MAX_PATH,
|
|
||||||
System::Threading::{
|
|
||||||
OpenProcess, QueryFullProcessImageNameW, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub unsafe fn pid_to_exe_path(pid: u32) -> Result<String, Box<dyn Error>> {
|
|
||||||
let mut exe_name: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
|
|
||||||
let mut size: u32 = exe_name.capacity().try_into().unwrap();
|
|
||||||
let process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
|
|
||||||
if !QueryFullProcessImageNameW(
|
|
||||||
process?,
|
|
||||||
Default::default(),
|
|
||||||
windows::core::PWSTR(exe_name.as_mut_ptr()),
|
|
||||||
&mut size,
|
|
||||||
).as_bool()
|
|
||||||
{
|
|
||||||
return Err(Box::new(windows::core::Error::from_win32()));
|
|
||||||
}
|
|
||||||
exe_name.set_len(size.try_into().unwrap());
|
|
||||||
let process_name = String::from_utf16_lossy(&exe_name);
|
|
||||||
return Ok(process_name);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user