Compare commits
20 Commits
739dd6613b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a14bc78b3b | ||
|
|
e81900fa79 | ||
|
|
16f5f49c99 | ||
|
|
989c5c05a5 | ||
|
|
5f69fc664a | ||
|
|
82153d31c9 | ||
|
|
a647fc5186 | ||
|
|
847631d94e | ||
|
|
5d9c8b4d0c | ||
|
|
60b7b34708 | ||
|
|
45f8d9bd26 | ||
|
|
671814fc81 | ||
|
|
f7fde32897 | ||
|
|
3bb1ca9b1c | ||
|
|
f2ac3d5fa6 | ||
|
|
a5d0729bba | ||
|
|
9eb15a4a5f | ||
|
|
f2d5263244 | ||
|
|
270a256444 | ||
|
|
23e9f624f4 |
244
Cargo.lock
generated
244
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.17.1"
|
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 = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
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.51"
|
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 = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.23"
|
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 = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "serde"
|
||||||
version = "1.0.107"
|
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 = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
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,33 +115,79 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "toml"
|
||||||
version = "1.0.6"
|
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 = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "toml_datetime"
|
||||||
version = "1.0.2"
|
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 = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
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.44.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 = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
|
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-interface",
|
"windows-interface",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.44.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 = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6"
|
checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -82,9 +196,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-interface"
|
name = "windows-interface"
|
||||||
version = "0.44.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 = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f"
|
checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -92,14 +206,33 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-result"
|
||||||
version = "0.42.1"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
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 = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm",
|
||||||
@@ -108,42 +241,67 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.42.1"
|
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 = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.42.1"
|
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 = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.42.1"
|
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 = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
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.42.1"
|
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 = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.42.1"
|
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 = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.1"
|
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 = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.42.1"
|
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 = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
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",
|
||||||
|
]
|
||||||
|
|||||||
31
Cargo.toml
31
Cargo.toml
@@ -1,28 +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.2"
|
|
||||||
once_cell = "1.17.1"
|
|
||||||
|
|
||||||
[dependencies.windows]
|
|
||||||
version = "0.44.0"
|
|
||||||
features = [
|
|
||||||
"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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
mute.txt
7
mute.txt
@@ -14,3 +14,10 @@ sm64.us.f3dex2e.exe
|
|||||||
teardown.exe
|
teardown.exe
|
||||||
underrail.exe
|
underrail.exe
|
||||||
valheim.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 |
318
src/main.rs
318
src/main.rs
@@ -1,318 +0,0 @@
|
|||||||
mod pid_to_exe;
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::{
|
|
||||||
collections::HashSet,
|
|
||||||
error::Error,
|
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
path::Path,
|
|
||||||
ptr::null_mut,
|
|
||||||
sync::{Arc, Mutex}, fs::File, io::{BufReader, BufRead},
|
|
||||||
};
|
|
||||||
|
|
||||||
use widestring::U16CStr;
|
|
||||||
use windows::{
|
|
||||||
core::{Interface, PCWSTR},
|
|
||||||
Win32::{
|
|
||||||
Foundation::{HINSTANCE, HWND},
|
|
||||||
Media::Audio::{
|
|
||||||
eRender, IAudioSessionControl, IAudioSessionControl2, IAudioSessionManager2,
|
|
||||||
IAudioSessionNotification, IAudioSessionNotification_Impl, IMMDeviceEnumerator,
|
|
||||||
IMMNotificationClient, IMMNotificationClient_Impl, ISimpleAudioVolume,
|
|
||||||
MMDeviceEnumerator, DEVICE_STATE_ACTIVE,
|
|
||||||
},
|
|
||||||
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, GetForegroundWindow,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
// 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?
|
|
||||||
GetWindowThreadProcessId(GetForegroundWindow(), Some(&mut pid));
|
|
||||||
let path = pid_to_exe_path(pid).unwrap_or("".to_string());
|
|
||||||
MUTER.lock().unwrap().notify_window_changed(&path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[windows::core::implement(IAudioSessionNotification)]
|
|
||||||
struct SessionNotification {}
|
|
||||||
|
|
||||||
impl IAudioSessionNotification_Impl for SessionNotification {
|
|
||||||
fn OnSessionCreated(
|
|
||||||
self: &SessionNotification,
|
|
||||||
newsession: &core::option::Option<IAudioSessionControl>,
|
|
||||||
) -> windows::core::Result<()> {
|
|
||||||
let ses: IAudioSessionControl2 = newsession.as_ref().unwrap().cast().unwrap();
|
|
||||||
MUTER.lock()
|
|
||||||
.unwrap()
|
|
||||||
.add_session_if_interesting(ses)
|
|
||||||
.unwrap();
|
|
||||||
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<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn OnDeviceAdded(&self, pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
|
|
||||||
println!("OnDeviceAdded");
|
|
||||||
MUTER.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");
|
|
||||||
// TODO: Remove device and all its sessions
|
|
||||||
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::UI::Shell::PropertiesSystem::PROPERTYKEY,
|
|
||||||
) -> windows::core::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SessionMuter {
|
|
||||||
sessions: Arc<Mutex<Vec<IAudioSessionControl2>>>,
|
|
||||||
device_enumerator: IMMDeviceEnumerator,
|
|
||||||
device_notification_client: IMMNotificationClient,
|
|
||||||
session_notification: IAudioSessionNotification,
|
|
||||||
mute_executables: HashSet<String>,
|
|
||||||
mute_flag: bool,
|
|
||||||
session_managers: Vec<IAudioSessionManager2>
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_mute_txt() -> HashSet<String> {
|
|
||||||
let file = File::open("mute.txt").unwrap();
|
|
||||||
return HashSet::from_iter(BufReader::new(file).lines().map(|line| line.unwrap()));
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SessionMuter {
|
|
||||||
fn new() -> SessionMuter {
|
|
||||||
let s = Arc::new(Mutex::new(Vec::new()));
|
|
||||||
SessionMuter {
|
|
||||||
session_managers: Vec::new(),
|
|
||||||
sessions: s.clone(),
|
|
||||||
device_enumerator: unsafe {
|
|
||||||
CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap()
|
|
||||||
},
|
|
||||||
device_notification_client: IMMNotificationClient::from(DeviceNotificationClient {}),
|
|
||||||
session_notification: IAudioSessionNotification::from(SessionNotification {}),
|
|
||||||
mute_executables: load_mute_txt(),
|
|
||||||
mute_flag: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_session_if_interesting(
|
|
||||||
self: &mut SessionMuter,
|
|
||||||
session: IAudioSessionControl2,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let file_name = self.session_to_filename(&session);
|
|
||||||
if file_name.is_err() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let fn_str = file_name
|
|
||||||
.unwrap()
|
|
||||||
.to_os_string()
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string();
|
|
||||||
println!("Adding session from: {:?}", fn_str);
|
|
||||||
if self.mute_executables.contains(&fn_str) {
|
|
||||||
unsafe {
|
|
||||||
let volume: ISimpleAudioVolume = session.cast()?;
|
|
||||||
volume.SetMute(self.mute_flag, null_mut())?;
|
|
||||||
}
|
|
||||||
self.sessions.lock().unwrap().push(session);
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn set_mute_all(self: &mut SessionMuter, mute: bool) {
|
|
||||||
let sessions = self.sessions.lock().unwrap();
|
|
||||||
let results = sessions
|
|
||||||
.iter()
|
|
||||||
.map(|session_control2| session_control2.cast::<ISimpleAudioVolume>())
|
|
||||||
.map(|vol_result| vol_result.map(|volume| volume.SetMute(mute, null_mut())));
|
|
||||||
for err in results.filter_map(|x| x.err()) {
|
|
||||||
println!("Error muting a session: {:?}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn notify_window_changed(self: &mut SessionMuter, path: &str) {
|
|
||||||
let binding = Path::new(path).file_name().unwrap_or(OsStr::new(""));
|
|
||||||
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 boot_devices(self: &mut SessionMuter) -> Result<(), Box<dyn Error>> {
|
|
||||||
unsafe {
|
|
||||||
self.device_enumerator
|
|
||||||
.RegisterEndpointNotificationCallback(&self.device_notification_client)?;
|
|
||||||
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)?;
|
|
||||||
}
|
|
||||||
println!("Boot done");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_device_by_id(self: &mut SessionMuter, 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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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")?;
|
|
||||||
return Ok(file_name.to_os_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn add_device(
|
|
||||||
self: &mut SessionMuter,
|
|
||||||
mmdevice: windows::Win32::Media::Audio::IMMDevice,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let sm: IAudioSessionManager2 = mmdevice.Activate(CLSCTX_ALL, None)?;
|
|
||||||
sm.RegisterSessionNotification(&self.session_notification)?;
|
|
||||||
let sesenum = sm.GetSessionEnumerator()?;
|
|
||||||
let sn_count = sesenum.GetCount()?;
|
|
||||||
let device_sessions = (0..sn_count)
|
|
||||||
.map(|idx| sesenum.GetSession(idx))
|
|
||||||
.map(|x| match x {
|
|
||||||
Err(e) => Err(e),
|
|
||||||
Ok(v) => v.cast(),
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<IAudioSessionControl2>, _>>()?;
|
|
||||||
for session in device_sessions {
|
|
||||||
self.add_session_if_interesting(session)?;
|
|
||||||
}
|
|
||||||
let str = U16CStr::from_ptr_str(mmdevice.GetId()?.0).to_string_lossy();
|
|
||||||
println!("Device Added: {} {}", str, sn_count);
|
|
||||||
self.session_managers.push(sm);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for SessionMuter {}
|
|
||||||
unsafe impl Sync for SessionMuter {}
|
|
||||||
|
|
||||||
static MUTER: Lazy<Arc<Mutex<SessionMuter>>> = Lazy::new(|| Arc::new(Mutex::new(SessionMuter::new())));
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
unsafe {
|
|
||||||
CoInitializeEx(None, COINIT_MULTITHREADED).unwrap();
|
|
||||||
}
|
|
||||||
MUTER.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