diff --git a/Cargo.lock b/Cargo.lock index 59073a0..dc02374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,26 +2,11 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "libc" -version = "0.2.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "libc-print" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a660208db49e35faf57b37484350f1a61072f2a5becf0592af6015d9ddd4b0" -dependencies = [ - "libc", -] - [[package]] name = "muter" version = "0.1.0" dependencies = [ - "libc-print", + "thiserror", "windows", ] @@ -45,15 +30,35 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.13" diff --git a/Cargo.toml b/Cargo.toml index 3a44733..29e4550 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,21 @@ [package] name = "muter" +publish = false +authors = ["newt "] version = "0.1.0" edition = "2021" [dependencies] -libc-print = "0.1.23" +thiserror = "2.0.3" [dependencies.windows] version = "0.58.0" features = [ "Win32_Media_Audio_Endpoints", - "Win32_System_Com" + + "Win32_System_Com", + "Win32_System_Threading", + + "Win32_UI_Input_KeyboardAndMouse", + "Win32_UI_WindowsAndMessaging", ] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..77c374c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to initialize COM library: {0}")] + Windows(#[from] windows::core::Error), +} diff --git a/src/main.rs b/src/main.rs index 9a5f180..4472411 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,69 @@ -#![no_std] - -use libc_print::std_name::println; +use std::sync::atomic::{AtomicPtr, Ordering}; +use std::time::Duration; +use std::{ptr, thread}; +use windows::Win32::UI::Input::KeyboardAndMouse::{ + MapVirtualKeyW, SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYBD_EVENT_FLAGS, + KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, MAPVK_VK_TO_VSC, +}; +use windows::Win32::UI::WindowsAndMessaging::{FindWindowExW, GetClassNameW, SetForegroundWindow}; use windows::Win32::{ + Foundation::{BOOL, HWND, LPARAM, WPARAM}, Media::Audio::{ eCapture, eMultimedia, Endpoints::IAudioEndpointVolume, IMMDevice, IMMDeviceEnumerator, MMDeviceEnumerator, }, System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED}, + UI::{ + Input::KeyboardAndMouse::{VIRTUAL_KEY, VK_CONTROL, VK_M, VK_SHIFT}, + WindowsAndMessaging::{EnumWindows, GetWindowTextW, PostMessageW, WM_KEYDOWN, WM_KEYUP}, + }, }; -fn main() -> windows::core::Result<()> { +static mut DISCORD_WHND: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +unsafe extern "system" fn search_windows(hwnd: HWND, _: LPARAM) -> BOOL { + let mut name = [0u16; 1024]; + let len = unsafe { GetWindowTextW(hwnd, &mut name) }; + let name = String::from_utf16_lossy(&name[..len as usize]); + + if name.to_lowercase().contains(&"- discord".to_string()) { + println!("Discord window found: {:?}", name); + DISCORD_WHND.store(Box::into_raw(Box::new(hwnd)), Ordering::SeqCst); + } + + BOOL(1) +} + +unsafe fn press_keys(window: HWND, keys: &[VIRTUAL_KEY]) -> windows::core::Result<()> { + let keys = keys.iter().map(|key| { + ( + key.0 as usize, + MapVirtualKeyW(key.0.into(), MAPVK_VK_TO_VSC) as isize, + ) + }); + let down = keys + .clone() + .map(|(key, scan)| (WPARAM(key), LPARAM(scan << 16))) + .collect::>(); + let up = keys + .map(|(key, scan)| (WPARAM(key), LPARAM(0x7FFF | (scan << 16) | (0b11 << 30)))) + .collect::>(); + + for (wparam, lparam) in down { + println!("Pressing key: {:?}", wparam); + PostMessageW(window, WM_KEYDOWN, wparam, lparam)?; + } + + thread::sleep(Duration::from_secs(10)); + + for (wparam, lparam) in up { + PostMessageW(window, WM_KEYUP, wparam, lparam)?; + } + + Ok(()) +} + +fn main() -> Result<(), muter::Error> { unsafe { // Initialize the COM library CoInitializeEx(None, COINIT_MULTITHREADED).unwrap(); @@ -24,6 +78,20 @@ fn main() -> windows::core::Result<()> { // Activate the IAudioEndpointVolume interface for the capture device let endpoint_volume: IAudioEndpointVolume = default_device.Activate(CLSCTX_ALL, None)?; + // Attach process to Discord + EnumWindows(Some(search_windows), LPARAM(0))?; + + let discord = { + let hwnd = DISCORD_WHND.load(Ordering::SeqCst); + + if hwnd.is_null() { + println!("Discord window not found"); + return Ok(()); + }; + + *hwnd + }; + // Check if the microphone is muted let mut is_muted = endpoint_volume.GetMute()?.as_bool(); @@ -31,11 +99,13 @@ fn main() -> windows::core::Result<()> { let is_new_muted = endpoint_volume.GetMute()?.as_bool(); if is_new_muted != is_muted { is_muted = is_new_muted; - if is_muted { - println!("Microphone is muted."); - } else { - println!("Microphone is not muted."); - } + println!( + "Microphone is {}", + if is_muted { "muted" } else { "unmuted" } + ); + + // send keybind to discord (ctrl+shift+m) + press_keys(discord, &[VK_CONTROL, VK_SHIFT, VK_M])?; } } }