diff --git a/crates/auto_mute_gui/Cargo.toml b/crates/auto_mute_gui/Cargo.toml index 29d452e..0cf564d 100644 --- a/crates/auto_mute_gui/Cargo.toml +++ b/crates/auto_mute_gui/Cargo.toml @@ -19,4 +19,7 @@ features = [ "Win32_UI_Shell", "Win32_Foundation", "Win32_System_LibraryLoader", + "Win32_Graphics_Gdi", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", ] diff --git a/crates/auto_mute_gui/src/main.rs b/crates/auto_mute_gui/src/main.rs index bf79d55..193e508 100644 --- a/crates/auto_mute_gui/src/main.rs +++ b/crates/auto_mute_gui/src/main.rs @@ -1,16 +1,20 @@ use std::{ collections::HashSet, env, + error::Error, + ffi::c_void, fs::File, io::{BufRead, BufReader}, + sync::mpsc, + thread, }; -use auto_mute_lib::muter::MuterThread; +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, NOTIFYICONDATAW}, + Shell::{Shell_NotifyIconW, NIF_ICON, NIF_MESSAGE, NIM_ADD, NIM_MODIFY, NOTIFYICONDATAW}, WindowsAndMessaging::{LoadIconW, WM_USER}, }, }; @@ -20,12 +24,13 @@ const WM_TRAY_MENU: u32 = WM_USER; pub struct MagicTray { pub notifdata: NOTIFYICONDATAW, + pub hwnd: HWND, } impl MagicTray { fn new() -> MagicTray { unsafe { - let mut mt = MagicTray { + MagicTray { notifdata: NOTIFYICONDATAW { cbSize: size_of::().try_into().unwrap(), hWnd: HWND::default(), @@ -39,14 +44,66 @@ impl MagicTray { .unwrap(), ..Default::default() }, - }; - mt.init(); - mt + hwnd: MagicTray::main_window().unwrap(), + } } } - fn init(&mut self) { + fn main_window() -> std::result::Result> { 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(); } } } @@ -62,11 +119,62 @@ pub fn load_mute_txt(file_name: &str) -> HashSet { 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(); - tray.init(); + + let (tx, rx) = mpsc::channel::(); 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), None); + 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() +} + +use windows::{ + core::*, Win32::Foundation::*, Win32::Graphics::Gdi::ValidateRect, + Win32::System::LibraryLoader::GetModuleHandleA, Win32::UI::WindowsAndMessaging::*, +}; + +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), + } + } } diff --git a/mute.txt b/mute.txt index 720862f..464933e 100644 --- a/mute.txt +++ b/mute.txt @@ -19,3 +19,5 @@ Tactical Breach Wizards.exe eldenring.exe ACValhalla.exe DQMonsters3.exe +Patrick's Parabox.exe +Signal.exe \ No newline at end of file