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
|
||||
|
||||
[[package]]
|
||||
name = "auto_mute_tool"
|
||||
version = "0.1.0"
|
||||
name = "auto_mute_cli"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"widestring",
|
||||
"auto_mute_lib",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
name = "auto_mute_gui"
|
||||
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"
|
||||
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]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
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 = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -47,33 +115,79 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
name = "toml"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
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]]
|
||||
name = "windows"
|
||||
version = "0.44.0"
|
||||
version = "0.59.0"
|
||||
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 = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.44.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6"
|
||||
checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -82,9 +196,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.44.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f"
|
||||
checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -92,14 +206,33 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.1"
|
||||
name = "windows-result"
|
||||
version = "0.3.0"
|
||||
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 = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
@@ -108,42 +241,67 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.1"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.1"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.1"
|
||||
version = "0.53.0"
|
||||
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]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.1"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.1"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.1"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.1"
|
||||
version = "0.53.0"
|
||||
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]
|
||||
name = "auto_mute_tool"
|
||||
version = "0.1.0"
|
||||
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"
|
||||
|
||||
]
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
resolver = "2"
|
||||
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
|
||||
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 |
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