feat: two-way sync
This commit is contained in:
parent
bac19ec2b8
commit
1c181a70fd
4 changed files with 29 additions and 74 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "vencord"]
|
||||||
|
path = vencord
|
||||||
|
url = ssh://git@git.newty.dev:2222/newt/muter-vencord.git
|
85
src/main.rs
85
src/main.rs
|
@ -1,10 +1,5 @@
|
||||||
use muter::ORDERING;
|
use muter::ORDERING;
|
||||||
use std::{
|
use std::{ptr, sync::atomic::AtomicBool};
|
||||||
ptr,
|
|
||||||
sync::atomic::{AtomicBool, AtomicPtr},
|
|
||||||
thread,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
use windows::Win32::{
|
use windows::Win32::{
|
||||||
Foundation::{BOOL, HWND, LPARAM, WPARAM},
|
Foundation::{BOOL, HWND, LPARAM, WPARAM},
|
||||||
|
@ -21,50 +16,9 @@ use windows::Win32::{
|
||||||
|
|
||||||
mod ws;
|
mod ws;
|
||||||
|
|
||||||
static mut DISCORD_WHND: AtomicPtr<HWND> = AtomicPtr::new(ptr::null_mut());
|
const PORT: u16 = 3034;
|
||||||
static mut IS_MUTED: AtomicBool = AtomicBool::new(false);
|
static mut IS_MUTED: AtomicBool = AtomicBool::new(false);
|
||||||
|
static mut PREFER: AtomicBool = AtomicBool::new(false);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), muter::Error> {
|
async fn main() -> Result<(), muter::Error> {
|
||||||
|
@ -82,20 +36,6 @@ async fn main() -> Result<(), muter::Error> {
|
||||||
// Activate the IAudioEndpointVolume interface for the capture device
|
// Activate the IAudioEndpointVolume interface for the capture device
|
||||||
let endpoint_volume: IAudioEndpointVolume = default_device.Activate(CLSCTX_ALL, None)?;
|
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);
|
|
||||||
|
|
||||||
if hwnd.is_null() {
|
|
||||||
println!("Discord window not found");
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
*hwnd
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setup WSS serve
|
// Setup WSS serve
|
||||||
let server = warp::serve(
|
let server = warp::serve(
|
||||||
warp::path::end()
|
warp::path::end()
|
||||||
|
@ -104,7 +44,7 @@ async fn main() -> Result<(), muter::Error> {
|
||||||
ws.on_upgrade(move |socket| ws::user_connected(socket))
|
ws.on_upgrade(move |socket| ws::user_connected(socket))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.run(([127, 0, 0, 1], 3034));
|
.run(([127, 0, 0, 1], PORT));
|
||||||
tokio::task::spawn(server);
|
tokio::task::spawn(server);
|
||||||
|
|
||||||
// Check if the microphone is muted
|
// Check if the microphone is muted
|
||||||
|
@ -112,14 +52,19 @@ async fn main() -> Result<(), muter::Error> {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// check if the mute status has changed
|
// check if the mute status has changed
|
||||||
|
let prefer = PREFER.load(ORDERING);
|
||||||
let muted = IS_MUTED.load(ORDERING);
|
let muted = IS_MUTED.load(ORDERING);
|
||||||
let is_new_muted = endpoint_volume.GetMute()?.as_bool();
|
|
||||||
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)
|
if prefer {
|
||||||
// press_keys(discord, &[VK_CONTROL, VK_SHIFT, VK_M])?;
|
endpoint_volume.SetMute(BOOL(muted as i32), ptr::null())?;
|
||||||
|
PREFER.store(false, ORDERING);
|
||||||
|
println!("Microphone is {}", if muted { "muted" } else { "unmuted" });
|
||||||
|
} else {
|
||||||
|
let new = endpoint_volume.GetMute()?.as_bool();
|
||||||
|
if muted != new {
|
||||||
|
IS_MUTED.store(new, ORDERING);
|
||||||
|
println!("Microphone is {}", if new { "muted" } else { "unmuted" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/ws.rs
14
src/ws.rs
|
@ -4,19 +4,25 @@ use std::time::Duration;
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
use warp::{filters::ws::Message, ws::WebSocket};
|
use warp::{filters::ws::Message, ws::WebSocket};
|
||||||
|
|
||||||
use crate::IS_MUTED;
|
use crate::{IS_MUTED, PREFER};
|
||||||
|
|
||||||
pub async fn user_connected(socket: WebSocket) {
|
pub async fn user_connected(socket: WebSocket) {
|
||||||
println!("User connected");
|
println!("User connected");
|
||||||
|
|
||||||
let (mut tx, mut rx) = socket.split();
|
let (mut tx, mut rx) = socket.split();
|
||||||
|
|
||||||
let mut prev_mute = unsafe { !IS_MUTED.load(ORDERING) };
|
let mut prev_mute = unsafe { !IS_MUTED.load(ORDERING) };
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match timeout(Duration::from_millis(100), rx.next()).await {
|
match timeout(Duration::from_millis(100), rx.next()).await {
|
||||||
Ok(Some(Ok(msg))) => {
|
Ok(Some(Ok(msg))) => {
|
||||||
if msg.is_close() {
|
// check for mute message
|
||||||
|
if msg.is_binary() {
|
||||||
|
let mute = msg.as_bytes() != [1];
|
||||||
|
unsafe {
|
||||||
|
IS_MUTED.store(mute, ORDERING);
|
||||||
|
PREFER.store(true, ORDERING);
|
||||||
|
}
|
||||||
|
} else if msg.is_close() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +37,7 @@ pub async fn user_connected(socket: WebSocket) {
|
||||||
let mute = unsafe { IS_MUTED.load(ORDERING) };
|
let mute = unsafe { IS_MUTED.load(ORDERING) };
|
||||||
|
|
||||||
if mute != prev_mute {
|
if mute != prev_mute {
|
||||||
tx.send(Message::binary(vec![if mute { 1 } else { 0 }]))
|
tx.send(Message::binary(if mute { vec![1] } else { vec![] }))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
prev_mute = mute;
|
prev_mute = mute;
|
||||||
|
|
1
vencord
Submodule
1
vencord
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6c6e2098364bc70659c57856375ba10ccb3888e7
|
Loading…
Reference in a new issue