Allow dropping old muters.
This commit is contained in:
@@ -46,3 +46,9 @@ impl IMMNotificationClient_Impl for DeviceNotificationClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for DeviceNotificationClient {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("DNC Drop");
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/main.rs
67
src/main.rs
@@ -16,7 +16,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use sm_session_notifier::SMSessionNotifier;
|
use sm_session_notifier::SMSessionNotifier;
|
||||||
use window_change::{await_win_change_events};
|
use window_change::WindowChangeMonitor;
|
||||||
use windows::{
|
use windows::{
|
||||||
core::Interface,
|
core::Interface,
|
||||||
Win32::{
|
Win32::{
|
||||||
@@ -31,20 +31,38 @@ struct SessionMuter {
|
|||||||
sessions: Vec<IAudioSessionControl2>,
|
sessions: Vec<IAudioSessionControl2>,
|
||||||
mute_executables: HashSet<String>,
|
mute_executables: HashSet<String>,
|
||||||
mute_flag: bool,
|
mute_flag: bool,
|
||||||
}
|
win: Option<WindowChangeMonitor>
|
||||||
|
|
||||||
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 {
|
impl SessionMuter {
|
||||||
fn new() -> SessionMuter {
|
fn start(mute_executables: HashSet<String>) -> Arc<Mutex<SessionMuter>> {
|
||||||
SessionMuter {
|
let muter = Arc::new(Mutex::new(SessionMuter {
|
||||||
sessions: Vec::new(),
|
sessions: Vec::new(),
|
||||||
mute_executables: load_mute_txt(),
|
mute_executables,
|
||||||
mute_flag: true,
|
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(
|
fn add_session(
|
||||||
@@ -114,24 +132,23 @@ impl SessionMuter {
|
|||||||
|
|
||||||
unsafe impl Send for SessionMuter {}
|
unsafe impl Send for SessionMuter {}
|
||||||
|
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_muter() {
|
||||||
|
SessionMuter::start(load_mute_txt());
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
CoInitializeEx(None, COINIT_MULTITHREADED).unwrap();
|
CoInitializeEx(None, COINIT_MULTITHREADED).unwrap();
|
||||||
}
|
}
|
||||||
let muter = Arc::new(Mutex::new(SessionMuter::new()));
|
loop {
|
||||||
let muter2 = muter.clone();
|
test_muter();
|
||||||
let sn = SMSessionNotifier::new(Box::new(move |session| {
|
println!("Test_muter done");
|
||||||
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)
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,3 +23,9 @@ impl IAudioSessionNotification_Impl for SessionNotification {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for SessionNotification {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("SN drop");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,8 +31,8 @@ impl SMSessionNotifier {
|
|||||||
) -> Arc<Mutex<Option<SMSessionNotifier>>> {
|
) -> Arc<Mutex<Option<SMSessionNotifier>>> {
|
||||||
let session_notifier_arc: Arc<Mutex<Option<SMSessionNotifier>>> =
|
let session_notifier_arc: Arc<Mutex<Option<SMSessionNotifier>>> =
|
||||||
Arc::new(Mutex::new(None));
|
Arc::new(Mutex::new(None));
|
||||||
let sn2 = session_notifier_arc.clone();
|
let sn2 = Arc::downgrade(&session_notifier_arc);
|
||||||
let sn3 = session_notifier_arc.clone();
|
let sn3 = sn2.clone();
|
||||||
let notifier = SMSessionNotifier {
|
let notifier = SMSessionNotifier {
|
||||||
session_managers: Vec::new(),
|
session_managers: Vec::new(),
|
||||||
device_enumerator: unsafe {
|
device_enumerator: unsafe {
|
||||||
@@ -40,7 +40,7 @@ impl SMSessionNotifier {
|
|||||||
},
|
},
|
||||||
device_notification_client: IMMNotificationClient::from(DeviceNotificationClient {
|
device_notification_client: IMMNotificationClient::from(DeviceNotificationClient {
|
||||||
callback: Box::new(move |id| {
|
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(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to add new device: {:?}", e);
|
println!("Failed to add new device: {:?}", e);
|
||||||
@@ -50,13 +50,13 @@ impl SMSessionNotifier {
|
|||||||
}),
|
}),
|
||||||
session_notification: IAudioSessionNotification::from(SessionNotification {
|
session_notification: IAudioSessionNotification::from(SessionNotification {
|
||||||
callback: Box::new(move |session| {
|
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,
|
notification_function: callback,
|
||||||
};
|
};
|
||||||
*session_notifier_arc.lock().unwrap() = Some(notifier);
|
*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<dyn Error>> {
|
pub(crate) fn boot_devices(self: &mut SMSessionNotifier) -> Result<(), Box<dyn Error>> {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use std::sync::Mutex;
|
use std::{sync::{Mutex, atomic::AtomicU32, Arc}, thread::{self, JoinHandle}};
|
||||||
|
|
||||||
use windows::Win32::{
|
use windows::Win32::{
|
||||||
Foundation::{HINSTANCE, HWND},
|
Foundation::{HINSTANCE, HWND, WPARAM, LPARAM},
|
||||||
UI::{
|
UI::{
|
||||||
Accessibility::{SetWinEventHook, HWINEVENTHOOK},
|
Accessibility::{SetWinEventHook, HWINEVENTHOOK},
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
DispatchMessageW, GetForegroundWindow, GetMessageW, GetWindowThreadProcessId,
|
DispatchMessageW, GetForegroundWindow, GetMessageW, GetWindowThreadProcessId,
|
||||||
TranslateMessage, EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG,
|
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;
|
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<dyn Fn(&str) + Send>) {
|
type WinCallback = Box<dyn Fn(&str) + Send>;
|
||||||
|
|
||||||
|
pub fn await_win_change_events(callback: WinCallback) {
|
||||||
*WIN_CHANGE_CALLBACK.lock().unwrap() = Some(callback);
|
*WIN_CHANGE_CALLBACK.lock().unwrap() = Some(callback);
|
||||||
unsafe {
|
unsafe {
|
||||||
SetWinEventHook(
|
SetWinEventHook(
|
||||||
@@ -71,9 +73,53 @@ pub fn await_win_change_events(callback: Box<dyn Fn(&str) + Send>) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut msg: MSG = MSG::default();
|
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);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&msg);
|
DispatchMessageW(&msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
5
todo.txt
5
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
|
- 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
|
||||||
|
|||||||
Reference in New Issue
Block a user