diff --git a/src/device_notification_client.rs b/src/device_notification_client.rs index 741373a..7c30aef 100644 --- a/src/device_notification_client.rs +++ b/src/device_notification_client.rs @@ -46,3 +46,9 @@ impl IMMNotificationClient_Impl for DeviceNotificationClient { Ok(()) } } + +impl Drop for DeviceNotificationClient { + fn drop(&mut self) { + println!("DNC Drop"); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index eae51c3..2af8d48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use std::{ }; use sm_session_notifier::SMSessionNotifier; -use window_change::{await_win_change_events}; +use window_change::WindowChangeMonitor; use windows::{ core::Interface, Win32::{ @@ -31,20 +31,38 @@ struct SessionMuter { sessions: Vec, mute_executables: HashSet, mute_flag: bool, -} - -fn load_mute_txt() -> HashSet { - let file = File::open("mute.txt").unwrap(); - return HashSet::from_iter(BufReader::new(file).lines().map(|line| line.unwrap())); + win: Option } impl SessionMuter { - fn new() -> SessionMuter { - SessionMuter { + fn start(mute_executables: HashSet) -> Arc> { + let muter = Arc::new(Mutex::new(SessionMuter { sessions: Vec::new(), - mute_executables: load_mute_txt(), + mute_executables, mute_flag: true, + win: None + })); + { + let muter = Arc::downgrade(&muter); + let muter_internal = muter.clone(); + muter.upgrade().unwrap().lock().as_mut().unwrap().win = Some(WindowChangeMonitor::start(Box::new(move |s| { + muter_internal.upgrade().map(|f| f.lock().unwrap().notify_window_changed(&s)); + }))); } + + { + let muter = Arc::downgrade(&muter); + let sn = SMSessionNotifier::new(Box::new(move |session| { + muter.upgrade().unwrap().lock().unwrap().add_session(session).unwrap() + })); + sn.lock() + .unwrap() + .as_mut() + .unwrap() + .boot_devices() + .expect("failed to get initial audio devices and sessions"); + } + return muter; } fn add_session( @@ -114,24 +132,23 @@ impl SessionMuter { unsafe impl Send for SessionMuter {} + +fn load_mute_txt() -> HashSet { + let file = File::open("mute.txt").unwrap(); + return HashSet::from_iter(BufReader::new(file).lines().map(|line| line.unwrap())); +} + + +fn test_muter() { + SessionMuter::start(load_mute_txt()); +} + fn main() { unsafe { CoInitializeEx(None, COINIT_MULTITHREADED).unwrap(); } - let muter = Arc::new(Mutex::new(SessionMuter::new())); - let muter2 = muter.clone(); - let sn = SMSessionNotifier::new(Box::new(move |session| { - muter2.lock().unwrap().add_session(session).unwrap() - })); - - sn.lock() - .unwrap() - .as_mut() - .unwrap() - .boot_devices() - .expect("failed to get initial audio devices and sessions"); - - await_win_change_events(Box::new(move |s| { - muter.lock().unwrap().notify_window_changed(&s) - })); + loop { + test_muter(); + println!("Test_muter done"); + } } diff --git a/src/session_notification.rs b/src/session_notification.rs index 08352e2..3ad9583 100644 --- a/src/session_notification.rs +++ b/src/session_notification.rs @@ -23,3 +23,9 @@ impl IAudioSessionNotification_Impl for SessionNotification { Ok(()) } } + +impl Drop for SessionNotification { + fn drop(&mut self) { + println!("SN drop"); + } +} \ No newline at end of file diff --git a/src/sm_session_notifier.rs b/src/sm_session_notifier.rs index 82e0121..263bfca 100644 --- a/src/sm_session_notifier.rs +++ b/src/sm_session_notifier.rs @@ -31,8 +31,8 @@ impl SMSessionNotifier { ) -> Arc>> { let session_notifier_arc: Arc>> = Arc::new(Mutex::new(None)); - let sn2 = session_notifier_arc.clone(); - let sn3 = session_notifier_arc.clone(); + let sn2 = Arc::downgrade(&session_notifier_arc); + let sn3 = sn2.clone(); let notifier = SMSessionNotifier { session_managers: Vec::new(), device_enumerator: unsafe { @@ -40,7 +40,7 @@ impl SMSessionNotifier { }, device_notification_client: IMMNotificationClient::from(DeviceNotificationClient { callback: Box::new(move |id| { - match sn2.lock().unwrap().as_mut().unwrap().add_device_by_id(id) { + match sn2.upgrade().unwrap().lock().unwrap().as_mut().unwrap().add_device_by_id(id) { Ok(_) => {}, Err(e) => { println!("Failed to add new device: {:?}", e); @@ -50,13 +50,13 @@ impl SMSessionNotifier { }), session_notification: IAudioSessionNotification::from(SessionNotification { callback: Box::new(move |session| { - (sn3.lock().unwrap().as_mut().unwrap().notification_function)(session) + (sn3.upgrade().unwrap().lock().unwrap().as_mut().unwrap().notification_function)(session) }), }), notification_function: callback, }; *session_notifier_arc.lock().unwrap() = Some(notifier); - return session_notifier_arc.clone(); + return session_notifier_arc; } pub(crate) fn boot_devices(self: &mut SMSessionNotifier) -> Result<(), Box> { diff --git a/src/window_change.rs b/src/window_change.rs index 0108f5c..7c51bbb 100644 --- a/src/window_change.rs +++ b/src/window_change.rs @@ -1,15 +1,15 @@ -use std::sync::Mutex; +use std::{sync::{Mutex, atomic::AtomicU32, Arc}, thread::{self, JoinHandle}}; use windows::Win32::{ - Foundation::{HINSTANCE, HWND}, + Foundation::{HINSTANCE, HWND, WPARAM, LPARAM}, UI::{ Accessibility::{SetWinEventHook, HWINEVENTHOOK}, WindowsAndMessaging::{ DispatchMessageW, GetForegroundWindow, GetMessageW, GetWindowThreadProcessId, TranslateMessage, EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, - WINEVENT_OUTOFCONTEXT, WINEVENT_SKIPOWNPROCESS, + WINEVENT_OUTOFCONTEXT, WINEVENT_SKIPOWNPROCESS, PostThreadMessageW, WM_QUIT, }, - }, + }, System::Threading::GetCurrentThreadId, }; use crate::pid_to_exe::pid_to_exe_path; @@ -48,7 +48,9 @@ unsafe extern "system" fn win_event_proc( } } -pub fn await_win_change_events(callback: Box) { +type WinCallback = Box; + +pub fn await_win_change_events(callback: WinCallback) { *WIN_CHANGE_CALLBACK.lock().unwrap() = Some(callback); unsafe { SetWinEventHook( @@ -71,9 +73,53 @@ pub fn await_win_change_events(callback: Box) { ); let mut msg: MSG = MSG::default(); - while GetMessageW(&mut msg, HWND::default(), 0, 0).as_bool() { + while GetMessageW(&mut msg, HWND::default(), 0, 0).0 > 0 { TranslateMessage(&msg); DispatchMessageW(&msg); } } } + +pub struct WindowChangeMonitor { + join_handle: Option>, + win_thread_id: Arc +} + +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"); + } + println!("Joined 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() }.into(), std::sync::atomic::Ordering::Relaxed); + await_win_change_events(f); + }) + }; + + { + let win_thread_id = win_thread_id.clone(); + WindowChangeMonitor { + join_handle: Some(join_handle), + win_thread_id + } + } + + } +} \ No newline at end of file diff --git a/todo.txt b/todo.txt index e158405..03e23ef 100644 --- a/todo.txt +++ b/todo.txt @@ -1 +1,4 @@ -- create session-focused API - should provide a callback for all existing sessions and any time a new sessions is created \ No newline at end of file +- Add the ability to reload/restart the mute list - must essentially restart/refresh all sessions if this occurs? + - can we simply release everything in this case? Should work given the way the windows-rs COM wrapper works I think. +- Tray icon +- Simple GUI to select from open windows and add to the list