feat: latch to discord window

This commit is contained in:
newt 2024-11-17 06:17:47 +00:00
parent 2c80d4a41d
commit edc9db64de
4 changed files with 116 additions and 29 deletions

41
Cargo.lock generated
View file

@ -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"

View file

@ -1,14 +1,21 @@
[package]
name = "muter"
publish = false
authors = ["newt <hi@newty.dev>"]
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",
]

5
src/lib.rs Normal file
View file

@ -0,0 +1,5 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Failed to initialize COM library: {0}")]
Windows(#[from] windows::core::Error),
}

View file

@ -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<HWND> = 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::<Vec<_>>();
let up = keys
.map(|(key, scan)| (WPARAM(key), LPARAM(0x7FFF | (scan << 16) | (0b11 << 30))))
.collect::<Vec<_>>();
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])?;
}
}
}