feat: websocket server
This commit is contained in:
parent
edc9db64de
commit
bac19ec2b8
7 changed files with 1178 additions and 26 deletions
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"files.exclude": {
|
||||
"target": true
|
||||
}
|
||||
}
|
1091
Cargo.lock
generated
1091
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,10 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
futures-util = { version = "0.3.31", default-features = false }
|
||||
thiserror = "2.0.3"
|
||||
tokio = { version = "1.41.1", default-features = false, features = ["macros", "rt-multi-thread"] }
|
||||
warp = { version = "0.3.7", default-features = false, features = ["websocket"] }
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.58.0"
|
||||
|
@ -14,7 +17,6 @@ features = [
|
|||
"Win32_Media_Audio_Endpoints",
|
||||
|
||||
"Win32_System_Com",
|
||||
"Win32_System_Threading",
|
||||
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
Keyboard does not emit raw event, it is handled by firmware.
|
||||
|
||||
Tested with:
|
||||
- ASUS TUF F15
|
||||
todo:
|
||||
- custom port
|
||||
|
|
|
@ -2,4 +2,8 @@
|
|||
pub enum Error {
|
||||
#[error("Failed to initialize COM library: {0}")]
|
||||
Windows(#[from] windows::core::Error),
|
||||
#[error("Failed to share microphone state")]
|
||||
Send(#[from] tokio::sync::mpsc::error::SendError<bool>),
|
||||
}
|
||||
|
||||
pub const ORDERING: std::sync::atomic::Ordering = std::sync::atomic::Ordering::SeqCst;
|
||||
|
|
52
src/main.rs
52
src/main.rs
|
@ -1,11 +1,11 @@
|
|||
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 muter::ORDERING;
|
||||
use std::{
|
||||
ptr,
|
||||
sync::atomic::{AtomicBool, AtomicPtr},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use windows::Win32::UI::WindowsAndMessaging::{FindWindowExW, GetClassNameW, SetForegroundWindow};
|
||||
use warp::Filter;
|
||||
use windows::Win32::{
|
||||
Foundation::{BOOL, HWND, LPARAM, WPARAM},
|
||||
Media::Audio::{
|
||||
|
@ -14,12 +14,15 @@ use windows::Win32::{
|
|||
},
|
||||
System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED},
|
||||
UI::{
|
||||
Input::KeyboardAndMouse::{VIRTUAL_KEY, VK_CONTROL, VK_M, VK_SHIFT},
|
||||
Input::KeyboardAndMouse::{MapVirtualKeyW, MAPVK_VK_TO_VSC, VIRTUAL_KEY},
|
||||
WindowsAndMessaging::{EnumWindows, GetWindowTextW, PostMessageW, WM_KEYDOWN, WM_KEYUP},
|
||||
},
|
||||
};
|
||||
|
||||
mod ws;
|
||||
|
||||
static mut DISCORD_WHND: AtomicPtr<HWND> = AtomicPtr::new(ptr::null_mut());
|
||||
static mut IS_MUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
unsafe extern "system" fn search_windows(hwnd: HWND, _: LPARAM) -> BOOL {
|
||||
let mut name = [0u16; 1024];
|
||||
|
@ -28,7 +31,7 @@ unsafe extern "system" fn search_windows(hwnd: HWND, _: LPARAM) -> BOOL {
|
|||
|
||||
if name.to_lowercase().contains(&"- discord".to_string()) {
|
||||
println!("Discord window found: {:?}", name);
|
||||
DISCORD_WHND.store(Box::into_raw(Box::new(hwnd)), Ordering::SeqCst);
|
||||
DISCORD_WHND.store(Box::into_raw(Box::new(hwnd)), ORDERING);
|
||||
}
|
||||
|
||||
BOOL(1)
|
||||
|
@ -63,7 +66,8 @@ unsafe fn press_keys(window: HWND, keys: &[VIRTUAL_KEY]) -> windows::core::Resul
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), muter::Error> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), muter::Error> {
|
||||
unsafe {
|
||||
// Initialize the COM library
|
||||
CoInitializeEx(None, COINIT_MULTITHREADED).unwrap();
|
||||
|
@ -82,7 +86,7 @@ fn main() -> Result<(), muter::Error> {
|
|||
EnumWindows(Some(search_windows), LPARAM(0))?;
|
||||
|
||||
let discord = {
|
||||
let hwnd = DISCORD_WHND.load(Ordering::SeqCst);
|
||||
let hwnd = DISCORD_WHND.load(ORDERING);
|
||||
|
||||
if hwnd.is_null() {
|
||||
println!("Discord window not found");
|
||||
|
@ -92,20 +96,30 @@ fn main() -> Result<(), muter::Error> {
|
|||
*hwnd
|
||||
};
|
||||
|
||||
// Setup WSS serve
|
||||
let server = warp::serve(
|
||||
warp::path::end()
|
||||
.and(warp::ws())
|
||||
.map(move |ws: warp::ws::Ws| {
|
||||
ws.on_upgrade(move |socket| ws::user_connected(socket))
|
||||
}),
|
||||
)
|
||||
.run(([127, 0, 0, 1], 3034));
|
||||
tokio::task::spawn(server);
|
||||
|
||||
// Check if the microphone is muted
|
||||
let mut is_muted = endpoint_volume.GetMute()?.as_bool();
|
||||
IS_MUTED.store(endpoint_volume.GetMute()?.as_bool(), ORDERING);
|
||||
|
||||
loop {
|
||||
// check if the mute status has changed
|
||||
let muted = IS_MUTED.load(ORDERING);
|
||||
let is_new_muted = endpoint_volume.GetMute()?.as_bool();
|
||||
if is_new_muted != is_muted {
|
||||
is_muted = is_new_muted;
|
||||
println!(
|
||||
"Microphone is {}",
|
||||
if is_muted { "muted" } else { "unmuted" }
|
||||
);
|
||||
if is_new_muted != muted {
|
||||
IS_MUTED.store(is_new_muted, ORDERING);
|
||||
println!("Microphone is {}", if muted { "muted" } else { "unmuted" });
|
||||
|
||||
// send keybind to discord (ctrl+shift+m)
|
||||
press_keys(discord, &[VK_CONTROL, VK_SHIFT, VK_M])?;
|
||||
// press_keys(discord, &[VK_CONTROL, VK_SHIFT, VK_M])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
42
src/ws.rs
Normal file
42
src/ws.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use futures_util::{SinkExt, StreamExt};
|
||||
use muter::ORDERING;
|
||||
use std::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
use warp::{filters::ws::Message, ws::WebSocket};
|
||||
|
||||
use crate::IS_MUTED;
|
||||
|
||||
pub async fn user_connected(socket: WebSocket) {
|
||||
println!("User connected");
|
||||
|
||||
let (mut tx, mut rx) = socket.split();
|
||||
|
||||
let mut prev_mute = unsafe { !IS_MUTED.load(ORDERING) };
|
||||
|
||||
loop {
|
||||
match timeout(Duration::from_millis(100), rx.next()).await {
|
||||
Ok(Some(Ok(msg))) => {
|
||||
if msg.is_close() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Some(Err(e))) => {
|
||||
eprintln!("Error receiving message: {}", e);
|
||||
break;
|
||||
}
|
||||
Ok(None) => break,
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
let mute = unsafe { IS_MUTED.load(ORDERING) };
|
||||
|
||||
if mute != prev_mute {
|
||||
tx.send(Message::binary(vec![if mute { 1 } else { 0 }]))
|
||||
.await
|
||||
.unwrap();
|
||||
prev_mute = mute;
|
||||
}
|
||||
}
|
||||
|
||||
println!("User disconnected");
|
||||
}
|
Loading…
Reference in a new issue