1use crate::state::KeyboardState;
7use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
8use std::sync::{Mutex, OnceLock, RwLock};
9use std::thread;
10use std::time::Duration;
11use windows::Win32::Foundation::{LPARAM, LRESULT, WPARAM};
12use windows::Win32::UI::Input::KeyboardAndMouse::{
13 SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYBD_EVENT_FLAGS, KEYEVENTF_KEYUP,
14 VIRTUAL_KEY,
15};
16use windows::Win32::UI::WindowsAndMessaging::{
17 CallNextHookEx, DispatchMessageW, GetMessageW, SetWindowsHookExW, TranslateMessage,
18 UnhookWindowsHookEx, KBDLLHOOKSTRUCT, MSG, WH_KEYBOARD_LL, WM_KEYDOWN, WM_SYSKEYDOWN,
19};
20
21const TIMEOUT: Duration = Duration::from_millis(250);
23
24const SILENT_KEY: VIRTUAL_KEY = VIRTUAL_KEY(0xE8);
26
27pub static HOOK_EVENT_TX: RwLock<Option<Sender<KeyboardEvent>>> = RwLock::new(None);
29
30static HOOK_RESPONSE_RX: RwLock<Option<Receiver<KeyAction>>> = RwLock::new(None);
32
33static HOOK_CONTROL_RX: RwLock<Option<Receiver<ControlFlow>>> = RwLock::new(None);
35
36static KEYBOARD_STATE: OnceLock<Mutex<KeyboardState>> = OnceLock::new();
38
39#[derive(Debug, Copy, Clone, PartialEq)]
41pub enum KeyAction {
42 Allow,
43 Block,
44 Replace,
45}
46
47#[derive(Debug, Copy, Clone, PartialEq)]
49enum ControlFlow {
50 Exit,
51}
52
53#[derive(Debug, Copy, Clone, PartialEq)]
55pub enum KeyboardEvent {
56 KeyDown {
57 vk_code: u16,
59 keyboard_state: KeyboardState,
61 },
62 KeyUp {
63 key_code: u16,
65 keyboard_state: KeyboardState,
67 },
68}
69
70pub struct KeyboardHook {
72 ke_rx: Receiver<KeyboardEvent>,
73 action_tx: Sender<KeyAction>,
74 cf_tx: Sender<ControlFlow>,
75}
76
77impl KeyboardHook {
78 pub fn recv(&self) -> Result<KeyboardEvent, RecvError> {
80 self.ke_rx.recv()
81 }
82
83 pub fn key_action(&self, value: KeyAction) {
85 self.action_tx.send(value).unwrap();
86 }
87
88 pub fn exit(&self) {
90 self.cf_tx.send(ControlFlow::Exit).unwrap();
91 }
92}
93
94pub fn start() -> KeyboardHook {
99 let (ke_tx, ke_rx) = unbounded();
101 let (action_tx, action_rx) = unbounded();
102 let (cf_tx, cf_rx) = unbounded();
103
104 let mut hook_event_tx = HOOK_EVENT_TX.write().unwrap();
106 *hook_event_tx = Some(ke_tx);
107 let mut hook_response_rx = HOOK_RESPONSE_RX.write().unwrap();
108 *hook_response_rx = Some(action_rx);
109 let mut hook_control_tx = HOOK_CONTROL_RX.write().unwrap();
110 *hook_control_tx = Some(cf_rx);
111
112 let mutex = KEYBOARD_STATE.get_or_init(|| Mutex::new(KeyboardState::new()));
114 let mut state = mutex.lock().unwrap();
115 state.clear();
116
117 unsafe {
118 thread::spawn(|| {
119 let hhook = SetWindowsHookExW(WH_KEYBOARD_LL, Some(hook_proc), None, 0).unwrap();
120 if let Some(cf_rx) = &*HOOK_CONTROL_RX.read().unwrap() {
121 loop {
122 let mut msg = MSG::default();
123 if GetMessageW(&mut msg, None, 0, 0).into() {
124 let _ = TranslateMessage(&msg);
125 DispatchMessageW(&msg);
126 }
127
128 if let Ok(cf) = cf_rx.try_recv() {
129 match cf {
130 ControlFlow::Exit => {
131 let mut hook_event_tx = HOOK_EVENT_TX.write().unwrap();
132 *hook_event_tx = None;
133 let mut hook_response_rx = HOOK_RESPONSE_RX.write().unwrap();
134 *hook_response_rx = None;
135 let mut hook_control_tx = HOOK_CONTROL_RX.write().unwrap();
136 *hook_control_tx = None;
137 UnhookWindowsHookEx(hhook).unwrap();
138 break;
139 }
140 }
141 }
142 }
143 }
144 });
145 }
146
147 KeyboardHook {
148 ke_rx,
149 action_tx,
150 cf_tx,
151 }
152}
153
154fn update_keyboard_state(vk_code: u16) {
156 let mutex = KEYBOARD_STATE.get();
157 let mut keyboard = mutex.unwrap().lock().unwrap();
158 keyboard.sync();
159 keyboard.keydown(vk_code);
160}
161
162unsafe fn send_silent_key() {
164 let inputs = [
165 INPUT {
166 r#type: INPUT_KEYBOARD,
167 Anonymous: INPUT_0 {
168 ki: KEYBDINPUT {
169 wVk: SILENT_KEY,
170 wScan: 0,
171 dwFlags: KEYBD_EVENT_FLAGS(0),
172 time: 0,
173 dwExtraInfo: 0,
174 },
175 },
176 },
177 INPUT {
178 r#type: INPUT_KEYBOARD,
179 Anonymous: INPUT_0 {
180 ki: KEYBDINPUT {
181 wVk: SILENT_KEY,
182 wScan: 0,
183 dwFlags: KEYEVENTF_KEYUP,
184 time: 0,
185 dwExtraInfo: 0,
186 },
187 },
188 },
189 ];
190 SendInput(&inputs, size_of::<INPUT>() as i32);
191}
192
193unsafe extern "system" fn hook_proc(code: i32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
195 if code >= 0 {
196 let event_guard = HOOK_EVENT_TX.read().unwrap();
197 let event_tx = event_guard.as_ref().unwrap();
198 let response_guard = HOOK_RESPONSE_RX.read().unwrap();
199 let response_rx = response_guard.as_ref().unwrap();
200
201 let event_type = wparam.0 as u32;
202 let vk_code = (*(lparam.0 as *const KBDLLHOOKSTRUCT)).vkCode as u16;
203 if vk_code == SILENT_KEY.0 {
204 return CallNextHookEx(None, code, wparam, lparam);
205 }
206
207 match event_type {
208 WM_KEYDOWN | WM_SYSKEYDOWN => {
210 while let Ok(_) = response_rx.try_recv() {}
212 update_keyboard_state(vk_code);
213 event_tx
214 .send(KeyboardEvent::KeyDown {
215 vk_code,
216 keyboard_state: *KEYBOARD_STATE.get().unwrap().lock().unwrap(),
217 })
218 .unwrap();
219
220 if let Ok(action) = response_rx.recv_timeout(TIMEOUT) {
222 match action {
223 KeyAction::Block => {
224 return LRESULT(1);
225 }
226 KeyAction::Replace => {
227 send_silent_key();
228 return LRESULT(1);
229 }
230 KeyAction::Allow => {}
231 }
232 }
233 }
234 _ => {}
235 };
236 }
237 CallNextHookEx(None, code, wparam, lparam)
238}