use crate::state::KeyboardState;
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
use std::sync::{Mutex, OnceLock, RwLock};
use std::thread;
use std::time::Duration;
use windows::Win32::Foundation::{LPARAM, LRESULT, WPARAM};
use windows::Win32::UI::Input::KeyboardAndMouse::{
SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYBD_EVENT_FLAGS, KEYEVENTF_KEYUP,
VIRTUAL_KEY,
};
use windows::Win32::UI::WindowsAndMessaging::{
CallNextHookEx, DispatchMessageW, GetMessageW, SetWindowsHookExW, TranslateMessage,
UnhookWindowsHookEx, KBDLLHOOKSTRUCT, MSG, WH_KEYBOARD_LL, WM_KEYDOWN, WM_SYSKEYDOWN,
};
const TIMEOUT: Duration = Duration::from_millis(250);
const SILENT_KEY: VIRTUAL_KEY = VIRTUAL_KEY(0xE8);
pub static HOOK_EVENT_TX: RwLock<Option<Sender<KeyboardEvent>>> = RwLock::new(None);
static HOOK_RESPONSE_RX: RwLock<Option<Receiver<KeyAction>>> = RwLock::new(None);
static HOOK_CONTROL_RX: RwLock<Option<Receiver<ControlFlow>>> = RwLock::new(None);
static KEYBOARD_STATE: OnceLock<Mutex<KeyboardState>> = OnceLock::new();
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum KeyAction {
Allow,
Block,
Replace,
}
#[derive(Debug, Copy, Clone, PartialEq)]
enum ControlFlow {
Exit,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum KeyboardEvent {
KeyDown {
vk_code: u16,
keyboard_state: KeyboardState,
},
KeyUp {
key_code: u16,
keyboard_state: KeyboardState,
},
}
pub struct KeyboardHook {
ke_rx: Receiver<KeyboardEvent>,
action_tx: Sender<KeyAction>,
cf_tx: Sender<ControlFlow>,
}
impl KeyboardHook {
pub fn recv(&self) -> Result<KeyboardEvent, RecvError> {
self.ke_rx.recv()
}
pub fn key_action(&self, value: KeyAction) {
self.action_tx.send(value).unwrap();
}
pub fn exit(&self) {
self.cf_tx.send(ControlFlow::Exit).unwrap();
}
}
pub fn start() -> KeyboardHook {
let (ke_tx, ke_rx) = unbounded();
let (action_tx, action_rx) = unbounded();
let (cf_tx, cf_rx) = unbounded();
let mut hook_event_tx = HOOK_EVENT_TX.write().unwrap();
*hook_event_tx = Some(ke_tx);
let mut hook_response_rx = HOOK_RESPONSE_RX.write().unwrap();
*hook_response_rx = Some(action_rx);
let mut hook_control_tx = HOOK_CONTROL_RX.write().unwrap();
*hook_control_tx = Some(cf_rx);
let mutex = KEYBOARD_STATE.get_or_init(|| Mutex::new(KeyboardState::new()));
let mut state = mutex.lock().unwrap();
state.clear();
unsafe {
thread::spawn(|| {
let hhook = SetWindowsHookExW(WH_KEYBOARD_LL, Some(hook_proc), None, 0).unwrap();
if let Some(cf_rx) = &*HOOK_CONTROL_RX.read().unwrap() {
loop {
let mut msg = MSG::default();
if GetMessageW(&mut msg, None, 0, 0).into() {
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if let Ok(cf) = cf_rx.try_recv() {
match cf {
ControlFlow::Exit => {
let mut hook_event_tx = HOOK_EVENT_TX.write().unwrap();
*hook_event_tx = None;
let mut hook_response_rx = HOOK_RESPONSE_RX.write().unwrap();
*hook_response_rx = None;
let mut hook_control_tx = HOOK_CONTROL_RX.write().unwrap();
*hook_control_tx = None;
UnhookWindowsHookEx(hhook).unwrap();
break;
}
}
}
}
}
});
}
KeyboardHook {
ke_rx,
action_tx,
cf_tx,
}
}
fn update_keyboard_state(vk_code: u16) {
let mutex = KEYBOARD_STATE.get();
let mut keyboard = mutex.unwrap().lock().unwrap();
keyboard.sync();
keyboard.keydown(vk_code);
}
unsafe fn send_silent_key() {
let inputs = [
INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: INPUT_0 {
ki: KEYBDINPUT {
wVk: SILENT_KEY,
wScan: 0,
dwFlags: KEYBD_EVENT_FLAGS(0),
time: 0,
dwExtraInfo: 0,
},
},
},
INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: INPUT_0 {
ki: KEYBDINPUT {
wVk: SILENT_KEY,
wScan: 0,
dwFlags: KEYEVENTF_KEYUP,
time: 0,
dwExtraInfo: 0,
},
},
},
];
SendInput(&inputs, size_of::<INPUT>() as i32);
}
unsafe extern "system" fn hook_proc(code: i32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
if code >= 0 {
let event_guard = HOOK_EVENT_TX.read().unwrap();
let event_tx = event_guard.as_ref().unwrap();
let response_guard = HOOK_RESPONSE_RX.read().unwrap();
let response_rx = response_guard.as_ref().unwrap();
let event_type = wparam.0 as u32;
let vk_code = (*(lparam.0 as *const KBDLLHOOKSTRUCT)).vkCode as u16;
if vk_code == SILENT_KEY.0 {
return CallNextHookEx(None, code, wparam, lparam);
}
match event_type {
WM_KEYDOWN | WM_SYSKEYDOWN => {
while let Ok(_) = response_rx.try_recv() {}
update_keyboard_state(vk_code);
event_tx
.send(KeyboardEvent::KeyDown {
vk_code,
keyboard_state: *KEYBOARD_STATE.get().unwrap().lock().unwrap(),
})
.unwrap();
if let Ok(action) = response_rx.recv_timeout(TIMEOUT) {
match action {
KeyAction::Block => {
return LRESULT(1);
}
KeyAction::Replace => {
send_silent_key();
return LRESULT(1);
}
KeyAction::Allow => {}
}
}
}
_ => {}
};
}
CallNextHookEx(None, code, wparam, lparam)
}