From 532d4048150d095b76399990662955c118b96363 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 18 Feb 2023 21:59:02 -0500 Subject: [PATCH] Upgrade windows-rs, get the basics working --- Cargo.lock | 76 ++++++++++++----- Cargo.toml | 3 +- src/main.rs | 238 +++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 224 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f7e57a..795b473 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,60 +60,90 @@ checksum = "b5babd2d3fcd28bcf9712ef93e437684156f5c46173a9718829aa57aa4aa37a8" [[package]] name = "windows" -version = "0.37.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ "windows-implement", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows-interface", + "windows-targets", ] [[package]] name = "windows-implement" -version = "0.37.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a1062e555f7d9d66fd1130ed4f7c6ec41a47529ee0850cd0e926d95b26bb14" +checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" dependencies = [ + "proc-macro2", + "quote", "syn", - "windows-tokens", ] [[package]] -name = "windows-tokens" -version = "0.37.0" +name = "windows-interface" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169" +checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.37.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.37.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.37.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.37.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.37.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/Cargo.toml b/Cargo.toml index 58199d2..9f6a2bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,8 @@ widestring = "1.0.0" once_cell = "1.12.0" [dependencies.windows] -version = "0.37.0" +version = "0.44.0" features = [ - "alloc", "implement", "Win32_Media_Audio", "Win32_UI_Shell_PropertiesSystem", diff --git a/src/main.rs b/src/main.rs index 1f8955b..eecdc43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,19 +2,24 @@ mod pid_to_exe; use once_cell::sync::Lazy; use std::{ + collections::HashSet, error::Error, - ffi::c_void, - mem, - ptr::{self, null_mut}, sync::{Arc, Mutex} + ffi::{OsStr, OsString}, + path::Path, + ptr::null_mut, + sync::{Arc, Mutex}, }; use widestring::U16CStr; use windows::{ + core::{Interface, PCWSTR}, Win32::{ Foundation::{HINSTANCE, HWND}, Media::Audio::{ - eRender, IAudioSessionManager2, IMMDeviceEnumerator, MMDeviceEnumerator, - DEVICE_STATE_ACTIVE, IAudioSessionNotification, IAudioSessionControl, IAudioSessionNotification_Impl, IAudioSessionControl2, IMMNotificationClient, IMMNotificationClient_Impl, + 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::{ @@ -22,10 +27,10 @@ use windows::{ WindowsAndMessaging::{ DispatchMessageW, GetMessageW, GetWindowThreadProcessId, TranslateMessage, EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT, - WINEVENT_SKIPOWNPROCESS, + WINEVENT_SKIPOWNPROCESS, GetForegroundWindow, }, }, - }, core::{Interface, PCWSTR}, + }, }; use crate::pid_to_exe::pid_to_exe_path; @@ -33,7 +38,7 @@ use crate::pid_to_exe::pid_to_exe_path; unsafe extern "system" fn win_event_proc( _hook: HWINEVENTHOOK, event: u32, - hwnd: HWND, + _hwnd: HWND, _id_object: i32, _id_child: i32, _dw_event_thread: u32, @@ -41,8 +46,11 @@ unsafe extern "system" fn win_event_proc( ) { if event == EVENT_SYSTEM_FOREGROUND || event == EVENT_SYSTEM_MINIMIZEEND { let mut pid: u32 = 0; - GetWindowThreadProcessId(hwnd, &mut pid); - println!("{:?}", pid_to_exe_path(pid)); + // 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); } } @@ -84,25 +92,34 @@ Okay, the main way this will function: - Register Session Notification on Session Manager - Get session enumerator from session manager - Detect sessions from applications we care about by getting session PID and mapping to process names using existing code + +- This should really work based on PID and unmute only the open process's PID when focused? Though maybe not due to the weird nature of electron apps and shit? +- Keep it the way it is for now I guess */ #[windows::core::implement(IAudioSessionNotification)] -struct SessionNotification { - sessions: Arc>> -} +struct SessionNotification {} + impl IAudioSessionNotification_Impl for SessionNotification { - fn OnSessionCreated(self: &SessionNotification,newsession: &core::option::Option) -> windows::core::Result<()> { - println!("Got OSC call"); - let ses = newsession.as_ref().unwrap().clone(); - let s2: IAudioSessionControl2 = ses.cast().unwrap(); - println!("Holy shit, new session created! {}", unsafe { pid_to_exe_path(s2.GetProcessId().unwrap()).unwrap() }); - self.sessions.lock().unwrap().push(ses); + fn OnSessionCreated( + self: &SessionNotification, + newsession: &core::option::Option, + ) -> windows::core::Result<()> { + let ses: IAudioSessionControl2 = newsession.as_ref().unwrap().cast().unwrap(); + println!("New session created: {}", unsafe { + ses.GetProcessId() + .and_then(|x| Ok(pid_to_exe_path(x).unwrap().to_string())) + .unwrap_or("UNKNOWN".to_string()) + }); + MUTER.lock() + .unwrap() + .add_session_if_interesting(ses) + .unwrap(); Ok(()) } } impl Drop for SessionNotification { - fn drop(&mut self) { println!("SN drop"); } @@ -118,70 +135,141 @@ impl Drop for DeviceNotificationClient { } impl IMMNotificationClient_Impl for DeviceNotificationClient { - fn OnDeviceStateChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_dwnewstate:u32) -> windows::core::Result<()> { + fn OnDeviceStateChanged( + &self, + _pwstrdeviceid: &windows::core::PCWSTR, + _dwnewstate: u32, + ) -> windows::core::Result<()> { println!("OnDeviceStateChanged"); Ok(()) } - fn OnDeviceAdded(&self,_pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { - // EUGH! + fn OnDeviceAdded(&self, pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { println!("OnDeviceAdded"); - /* - KING.lock().unwrap().add_device_by_id(pwstrdeviceid).map_err(|error| { - println!("Error adding device: {:?}", error); - }).unwrap_or(()); - */ + 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<()> { + 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<()> { + fn OnDefaultDeviceChanged( + &self, + _flow: windows::Win32::Media::Audio::EDataFlow, + _role: windows::Win32::Media::Audio::ERole, + _pwstrdefaultdeviceid: &windows::core::PCWSTR, + ) -> windows::core::Result<()> { println!("OnDefaultDeviceChanged"); Ok(()) } - fn OnPropertyValueChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_key: &windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY) -> windows::core::Result<()> { + fn OnPropertyValueChanged( + &self, + _pwstrdeviceid: &windows::core::PCWSTR, + _key: &windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY, + ) -> windows::core::Result<()> { println!("OnPropertyValueChanged"); Ok(()) } } -struct SessionKing { - sessions: Arc>>, - device_enumerator : IMMDeviceEnumerator, - dnc : IMMNotificationClient +struct SessionMuter { + sessions: Arc>>, + device_enumerator: IMMDeviceEnumerator, + dnc: IMMNotificationClient, + session_notification: IAudioSessionNotification, + good_files: HashSet, + mute_flag: bool, + session_managers: Vec } -impl SessionKing { - fn new() -> SessionKing { - SessionKing { - sessions: Arc::new(Mutex::new(Vec::new())), - device_enumerator: unsafe { CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap() }, - dnc: IMMNotificationClient::from(DeviceNotificationClient {}) +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() + }, + dnc: IMMNotificationClient::from(DeviceNotificationClient {}), + session_notification: IAudioSessionNotification::from(SessionNotification {}), + good_files: HashSet::from(["hollow_knight.exe"].map(|s| s.to_string())), + mute_flag: true, } } + fn add_session_if_interesting( + self: &mut SessionMuter, + session: IAudioSessionControl2, + ) -> Result<(), Box> { + 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!("Got session: {:?}", fn_str); + if self.good_files.contains(&fn_str) { + unsafe { + let volume: ISimpleAudioVolume = session.cast()?; + volume.SetMute(self.mute_flag, null_mut())?; + } + self.sessions.lock().unwrap().push(session); + } + return Ok(()); + } - fn boot_devices(self: &mut SessionKing) -> Result> { + 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::()) + .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(); + self.mute_flag = !self.good_files.contains(&file_name); + self.set_mute_all(self.mute_flag); + println!("Mute set to {} due to selection of {}", self.mute_flag, file_name); + } + + fn boot_devices(self: &mut SessionMuter) -> Result> { unsafe { - (Interface::vtable(&self.device_enumerator).RegisterEndpointNotificationCallback)(Interface::as_raw(&self.device_enumerator), self.dnc.as_raw()).ok()?; - let device_collection = - self.device_enumerator.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?; + self.device_enumerator + .RegisterEndpointNotificationCallback(&self.dnc)?; + 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("Done".to_string()); } } - fn add_device_by_id(self: &mut SessionKing, id: &PCWSTR) -> Result<(), Box> { + fn add_device_by_id(self: &mut SessionMuter, id: &PCWSTR) -> Result<(), Box> { unsafe { let device_enumerator: IMMDeviceEnumerator = CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?; @@ -191,40 +279,54 @@ impl SessionKing { } } - unsafe fn add_device(self: &mut SessionKing, mmdevice: windows::Win32::Media::Audio::IMMDevice) -> Result<(), Box> { - let mut sm_ptr: *mut c_void = null_mut(); - mmdevice.Activate( - &::IID, - CLSCTX_ALL, - null_mut(), - ptr::addr_of_mut!(sm_ptr), - )?; - let sm: IAudioSessionManager2 = mem::transmute(sm_ptr); + fn session_to_filename( + self: &mut SessionMuter, + session: &IAudioSessionControl2, + ) -> Result> { + 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> { + let sm: IAudioSessionManager2 = mmdevice.Activate(CLSCTX_ALL, None)?; + sm.RegisterSessionNotification(&self.session_notification)?; let sesenum = sm.GetSessionEnumerator()?; let sn_count = sesenum.GetCount()?; - let mut cdses = (0..sn_count).map(|idx| sesenum.GetSession(idx)).collect::,_>>()?; - self.sessions.lock().unwrap().append(&mut cdses); - sm.RegisterSessionNotification(IAudioSessionNotification::from( SessionNotification { - sessions: self.sessions.clone() - }))?; + let device_sessions = (0..sn_count) + .map(|idx| sesenum.GetSession(idx)) + .map(|x| match x { + Err(e) => Err(e), + Ok(v) => v.cast(), + }) + .collect::, _>>()?; + 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 SessionKing {} -unsafe impl Sync for SessionKing {} +unsafe impl Send for SessionMuter {} +unsafe impl Sync for SessionMuter {} -static KING : Lazy>> = Lazy::new(|| { - Arc::new(Mutex::new(SessionKing::new())) -}); +static MUTER: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(SessionMuter::new()))); fn main() { unsafe { - CoInitializeEx(null_mut(), COINIT_MULTITHREADED).unwrap(); + CoInitializeEx(None, COINIT_MULTITHREADED).unwrap(); } - KING.lock().unwrap().boot_devices().unwrap(); + MUTER.lock().unwrap().boot_devices().unwrap(); win_event_hook_loop(); }