1use std::ffi::c_void;
4use std::{
5 io,
6 mem,
7};
8
9use num_enum::{
10 FromPrimitive,
11 IntoPrimitive,
12};
13use windows::Win32::UI::Input::KeyboardAndMouse::{
14 GetAsyncKeyState,
15 GetKeyState,
16 INPUT,
17 INPUT_0,
18 INPUT_KEYBOARD,
19 INPUT_MOUSE,
20 KEYBDINPUT,
21 KEYEVENTF_KEYUP,
22 MOUSEEVENTF_LEFTDOWN,
23 MOUSEEVENTF_LEFTUP,
24 MOUSEEVENTF_MIDDLEDOWN,
25 MOUSEEVENTF_MIDDLEUP,
26 MOUSEEVENTF_RIGHTDOWN,
27 MOUSEEVENTF_RIGHTUP,
28 MOUSEEVENTF_WHEEL,
29 MOUSEEVENTF_XDOWN,
30 MOUSEEVENTF_XUP,
31 MOUSEINPUT,
32 SendInput,
33 VIRTUAL_KEY,
34 VK_0,
35 VK_1,
36 VK_2,
37 VK_3,
38 VK_4,
39 VK_5,
40 VK_6,
41 VK_7,
42 VK_8,
43 VK_9,
44 VK_A,
45 VK_ADD,
46 VK_APPS,
47 VK_B,
48 VK_BACK,
49 VK_C,
50 VK_CAPITAL,
51 VK_D,
52 VK_DECIMAL,
53 VK_DELETE,
54 VK_DIVIDE,
55 VK_DOWN,
56 VK_E,
57 VK_END,
58 VK_ESCAPE,
59 VK_F,
60 VK_F1,
61 VK_F2,
62 VK_F3,
63 VK_F4,
64 VK_F5,
65 VK_F6,
66 VK_F7,
67 VK_F8,
68 VK_F9,
69 VK_F10,
70 VK_F11,
71 VK_F12,
72 VK_G,
73 VK_H,
74 VK_HOME,
75 VK_I,
76 VK_INSERT,
77 VK_J,
78 VK_K,
79 VK_L,
80 VK_LBUTTON,
81 VK_LCONTROL,
82 VK_LEFT,
83 VK_LMENU,
84 VK_LSHIFT,
85 VK_LWIN,
86 VK_M,
87 VK_MBUTTON,
88 VK_MEDIA_NEXT_TRACK,
89 VK_MEDIA_PLAY_PAUSE,
90 VK_MEDIA_PREV_TRACK,
91 VK_MEDIA_STOP,
92 VK_MULTIPLY,
93 VK_N,
94 VK_NEXT,
95 VK_NUMLOCK,
96 VK_NUMPAD0,
97 VK_NUMPAD1,
98 VK_NUMPAD2,
99 VK_NUMPAD3,
100 VK_NUMPAD4,
101 VK_NUMPAD5,
102 VK_NUMPAD6,
103 VK_NUMPAD7,
104 VK_NUMPAD8,
105 VK_NUMPAD9,
106 VK_O,
107 VK_OEM_1,
108 VK_OEM_2,
109 VK_OEM_3,
110 VK_OEM_4,
111 VK_OEM_5,
112 VK_OEM_6,
113 VK_OEM_7,
114 VK_OEM_8,
115 VK_OEM_102,
116 VK_OEM_COMMA,
117 VK_OEM_MINUS,
118 VK_OEM_PERIOD,
119 VK_OEM_PLUS,
120 VK_P,
121 VK_PAUSE,
122 VK_PRIOR,
123 VK_Q,
124 VK_R,
125 VK_RBUTTON,
126 VK_RCONTROL,
127 VK_RETURN,
128 VK_RIGHT,
129 VK_RMENU,
130 VK_RSHIFT,
131 VK_RWIN,
132 VK_S,
133 VK_SCROLL,
134 VK_SNAPSHOT,
135 VK_SPACE,
136 VK_SUBTRACT,
137 VK_T,
138 VK_TAB,
139 VK_U,
140 VK_UP,
141 VK_V,
142 VK_VOLUME_DOWN,
143 VK_VOLUME_MUTE,
144 VK_VOLUME_UP,
145 VK_W,
146 VK_X,
147 VK_XBUTTON1,
148 VK_XBUTTON2,
149 VK_Y,
150 VK_Z,
151};
152use windows::Win32::UI::WindowsAndMessaging::{
153 SPI_GETMOUSESPEED,
154 SPI_SETMOUSESPEED,
155 SPIF_SENDCHANGE,
156 SPIF_UPDATEINIFILE,
157 SystemParametersInfoW,
158 WHEEL_DELTA,
159 XBUTTON1,
160 XBUTTON2,
161};
162
163#[expect(clippy::wildcard_imports)]
164use self::private::*;
165use crate::internal::ReturnValue;
166
167pub mod hotkey;
168
169pub trait GenericKey: GenericKeyInternal {
171 fn is_pressed(self) -> io::Result<bool> {
172 let result = unsafe {
173 GetAsyncKeyState(self.into())
174 .if_null_to_error(|| io::ErrorKind::PermissionDenied.into())?
175 };
176 Ok(result.cast_unsigned() >> (u16::BITS - 1) == 1)
177 }
178
179 fn press(self) -> io::Result<()> {
183 self.send_input(false)
184 }
185
186 fn release(self) -> io::Result<()> {
188 self.send_input(true)
189 }
190
191 fn send_combination(keys: &[Self]) -> io::Result<()> {
196 let raw_input_pairs: Vec<_> = keys
197 .iter()
198 .copied()
199 .map(|key: Self| {
200 let raw_input = key.get_press_raw_input(false);
201 let raw_input_release = key.get_press_raw_input(true);
202 (raw_input, raw_input_release)
203 })
204 .collect();
205 let raw_inputs: Vec<_> = raw_input_pairs
206 .iter()
207 .map(|x| x.0)
208 .chain(raw_input_pairs.iter().rev().map(|x| x.1))
209 .collect();
210 send_raw_inputs(raw_inputs.as_slice())
211 }
212}
213
214impl GenericKey for VirtualKey {}
216impl GenericKey for MouseButton {}
217
218mod private {
219 #[expect(clippy::wildcard_imports)]
220 use super::*;
221
222 pub trait GenericKeyInternal: Copy + Into<i32> {
223 fn send_input(self, is_release: bool) -> io::Result<()> {
224 let raw_input = self.get_press_raw_input(is_release);
225 send_raw_inputs(&[raw_input])
226 }
227 fn get_press_raw_input(self, is_release: bool) -> INPUT;
228 }
229
230 impl GenericKeyInternal for VirtualKey {
231 fn get_press_raw_input(self, is_release: bool) -> INPUT {
232 let raw_key: u16 = self.into();
233 let raw_keybdinput = KEYBDINPUT {
234 wVk: VIRTUAL_KEY(raw_key),
235 dwFlags: if is_release {
236 KEYEVENTF_KEYUP
237 } else {
238 Default::default()
239 },
240 ..Default::default()
241 };
242 INPUT {
243 r#type: INPUT_KEYBOARD,
244 Anonymous: INPUT_0 { ki: raw_keybdinput },
245 }
246 }
247 }
248
249 impl GenericKeyInternal for MouseButton {
250 fn get_press_raw_input(self, is_release: bool) -> INPUT {
251 let (flags, mouse_data) = match (self, is_release) {
252 (MouseButton::Left, false) => (MOUSEEVENTF_LEFTDOWN, 0),
253 (MouseButton::Left, true) => (MOUSEEVENTF_LEFTUP, 0),
254 (MouseButton::Right, false) => (MOUSEEVENTF_RIGHTDOWN, 0),
255 (MouseButton::Right, true) => (MOUSEEVENTF_RIGHTUP, 0),
256 (MouseButton::Middle, false) => (MOUSEEVENTF_MIDDLEDOWN, 0),
257 (MouseButton::Middle, true) => (MOUSEEVENTF_MIDDLEUP, 0),
258 (MouseButton::X1, false) => (MOUSEEVENTF_XDOWN, XBUTTON1),
259 (MouseButton::X1, true) => (MOUSEEVENTF_XUP, XBUTTON1),
260 (MouseButton::X2, false) => (MOUSEEVENTF_XDOWN, XBUTTON2),
261 (MouseButton::X2, true) => (MOUSEEVENTF_XUP, XBUTTON2),
262 };
263 INPUT {
264 r#type: INPUT_MOUSE,
265 Anonymous: INPUT_0 {
266 mi: MOUSEINPUT {
267 mouseData: mouse_data.into(),
268 dwFlags: flags,
269 ..Default::default()
270 },
271 },
272 }
273 }
274 }
275}
276
277#[derive(FromPrimitive, IntoPrimitive, Copy, Clone, Eq, PartialEq, Hash, Debug)]
283#[repr(u16)]
284pub enum VirtualKey {
285 Backspace = VK_BACK.0,
286 Tab = VK_TAB.0,
287 Return = VK_RETURN.0,
288 Pause = VK_PAUSE.0,
289 CapsLock = VK_CAPITAL.0,
290 Esc = VK_ESCAPE.0,
291 Space = VK_SPACE.0,
292 PgUp = VK_PRIOR.0,
293 PgDown = VK_NEXT.0,
294 End = VK_END.0,
295 Home = VK_HOME.0,
296 LeftArrow = VK_LEFT.0,
297 UpArrow = VK_UP.0,
298 RightArrow = VK_RIGHT.0,
299 DownArrow = VK_DOWN.0,
300 PrintScreen = VK_SNAPSHOT.0,
301 Insert = VK_INSERT.0,
302 Delete = VK_DELETE.0,
303 Number0 = VK_0.0,
304 Number1 = VK_1.0,
305 Number2 = VK_2.0,
306 Number3 = VK_3.0,
307 Number4 = VK_4.0,
308 Number5 = VK_5.0,
309 Number6 = VK_6.0,
310 Number7 = VK_7.0,
311 Number8 = VK_8.0,
312 Number9 = VK_9.0,
313 A = VK_A.0,
314 B = VK_B.0,
315 C = VK_C.0,
316 D = VK_D.0,
317 E = VK_E.0,
318 F = VK_F.0,
319 G = VK_G.0,
320 H = VK_H.0,
321 I = VK_I.0,
322 J = VK_J.0,
323 K = VK_K.0,
324 L = VK_L.0,
325 M = VK_M.0,
326 N = VK_N.0,
327 O = VK_O.0,
328 P = VK_P.0,
329 Q = VK_Q.0,
330 R = VK_R.0,
331 S = VK_S.0,
332 T = VK_T.0,
333 U = VK_U.0,
334 V = VK_V.0,
335 W = VK_W.0,
336 X = VK_X.0,
337 Y = VK_Y.0,
338 Z = VK_Z.0,
339 LeftWindows = VK_LWIN.0,
340 RightWindows = VK_RWIN.0,
341 Menu = VK_APPS.0,
342 Numpad0 = VK_NUMPAD0.0,
343 Numpad1 = VK_NUMPAD1.0,
344 Numpad2 = VK_NUMPAD2.0,
345 Numpad3 = VK_NUMPAD3.0,
346 Numpad4 = VK_NUMPAD4.0,
347 Numpad5 = VK_NUMPAD5.0,
348 Numpad6 = VK_NUMPAD6.0,
349 Numpad7 = VK_NUMPAD7.0,
350 Numpad8 = VK_NUMPAD8.0,
351 Numpad9 = VK_NUMPAD9.0,
352 Multiply = VK_MULTIPLY.0,
353 Add = VK_ADD.0,
354 Subtract = VK_SUBTRACT.0,
355 Decimal = VK_DECIMAL.0,
356 Divide = VK_DIVIDE.0,
357 F1 = VK_F1.0,
358 F2 = VK_F2.0,
359 F3 = VK_F3.0,
360 F4 = VK_F4.0,
361 F5 = VK_F5.0,
362 F6 = VK_F6.0,
363 F7 = VK_F7.0,
364 F8 = VK_F8.0,
365 F9 = VK_F9.0,
366 F10 = VK_F10.0,
367 F11 = VK_F11.0,
368 F12 = VK_F12.0,
369 NumLock = VK_NUMLOCK.0,
370 ScrollLock = VK_SCROLL.0,
371 LeftShift = VK_LSHIFT.0,
372 RightShift = VK_RSHIFT.0,
373 LeftCtrl = VK_LCONTROL.0,
374 RightCtrl = VK_RCONTROL.0,
375 LeftAlt = VK_LMENU.0,
376 RightAlt = VK_RMENU.0,
377 VolumeMute = VK_VOLUME_MUTE.0,
378 VolumeDown = VK_VOLUME_DOWN.0,
379 VolumeUp = VK_VOLUME_UP.0,
380 MediaNextTrack = VK_MEDIA_NEXT_TRACK.0,
381 MediaPrevTrack = VK_MEDIA_PREV_TRACK.0,
382 MediaStop = VK_MEDIA_STOP.0,
383 MediaPlayPause = VK_MEDIA_PLAY_PAUSE.0,
384 Oem1 = VK_OEM_1.0,
389 OemPlus = VK_OEM_PLUS.0,
391 OemComma = VK_OEM_COMMA.0,
393 OemMinus = VK_OEM_MINUS.0,
395 OemPeriod = VK_OEM_PERIOD.0,
397 Oem2 = VK_OEM_2.0,
402 Oem3 = VK_OEM_3.0,
407 Oem4 = VK_OEM_4.0,
412 Oem5 = VK_OEM_5.0,
417 Oem6 = VK_OEM_6.0,
422 Oem7 = VK_OEM_7.0,
427 Oem8 = VK_OEM_8.0,
428 Oem102 = VK_OEM_102.0,
433 #[num_enum(catch_all)]
435 Other(u16),
436}
437
438impl VirtualKey {
439 pub fn is_lock_toggled(self) -> bool {
441 let result = unsafe { GetKeyState(self.into()).cast_unsigned() };
442 result & 1 == 1
443 }
444}
445
446impl From<VirtualKey> for u32 {
447 fn from(value: VirtualKey) -> Self {
448 Self::from(u16::from(value))
449 }
450}
451
452impl From<VirtualKey> for i32 {
453 fn from(value: VirtualKey) -> Self {
454 u16::from(value).into()
455 }
456}
457
458fn send_raw_inputs(raw_inputs: &[INPUT]) -> io::Result<()> {
459 let raw_input_size = mem::size_of::<INPUT>()
460 .try_into()
461 .expect("Struct size conversion failed");
462
463 let expected_sent_size =
464 u32::try_from(raw_inputs.len()).expect("Inputs length conversion failed");
465 unsafe {
466 SendInput(raw_inputs, raw_input_size)
467 .if_null_get_last_error()?
468 .if_not_eq_to_error(expected_sent_size, || {
469 io::Error::from(io::ErrorKind::Interrupted)
470 })?;
471 }
472 Ok(())
473}
474
475#[derive(IntoPrimitive, Copy, Clone, Eq, PartialEq, Hash, Debug)]
479#[repr(u16)]
480pub enum MouseButton {
481 Left = VK_LBUTTON.0,
482 Right = VK_RBUTTON.0,
483 Middle = VK_MBUTTON.0,
484 X1 = VK_XBUTTON1.0,
485 X2 = VK_XBUTTON2.0,
486}
487
488impl From<MouseButton> for i32 {
489 fn from(value: MouseButton) -> Self {
490 u16::from(value).into()
491 }
492}
493
494#[derive(Copy, Clone, Eq, PartialEq, Debug)]
496pub enum MouseScrollEvent {
497 Up(u16),
501 Down(u16),
505 Continuous(i16),
510}
511
512impl MouseScrollEvent {
513 #[expect(clippy::cast_possible_truncation)]
514 pub const WHEEL_DELTA: i16 = WHEEL_DELTA as _;
515
516 pub fn send(self) -> io::Result<()> {
518 let mouse_data: i32 = match self {
520 MouseScrollEvent::Up(amount) => i32::from(Self::WHEEL_DELTA) * i32::from(amount),
521 MouseScrollEvent::Down(amount) => -i32::from(Self::WHEEL_DELTA) * i32::from(amount),
522 MouseScrollEvent::Continuous(delta) => i32::from(delta),
523 };
524 let raw_input = INPUT {
525 r#type: INPUT_MOUSE,
526 Anonymous: INPUT_0 {
527 mi: MOUSEINPUT {
528 mouseData: mouse_data.cast_unsigned(),
530 dwFlags: MOUSEEVENTF_WHEEL,
531 ..Default::default()
532 },
533 },
534 };
535 send_raw_inputs(&[raw_input])
536 }
537
538 #[cfg(feature = "hooking")]
539 pub(crate) fn from_raw_movement(raw_movement: i16) -> Self {
540 if raw_movement % Self::WHEEL_DELTA != 0 {
541 MouseScrollEvent::Continuous(raw_movement)
542 } else if raw_movement > 0 {
543 MouseScrollEvent::Up((raw_movement / Self::WHEEL_DELTA).cast_unsigned())
544 } else {
545 MouseScrollEvent::Down((-raw_movement / Self::WHEEL_DELTA).cast_unsigned())
546 }
547 }
548}
549
550pub fn get_mouse_speed() -> io::Result<u32> {
552 let mut speed: u32 = 0;
553 unsafe {
554 SystemParametersInfoW(
555 SPI_GETMOUSESPEED,
556 0,
557 Some((&raw mut speed).cast::<c_void>()),
558 Default::default(),
559 )?;
560 }
561 Ok(speed)
562}
563
564pub fn set_mouse_speed(speed: u32, persist: bool) -> io::Result<()> {
568 let flags = if persist {
569 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE
570 } else {
571 SPIF_SENDCHANGE
572 };
573 unsafe {
574 SystemParametersInfoW(
575 SPI_SETMOUSESPEED,
576 0,
577 Some(std::ptr::with_exposed_provenance_mut(
578 usize::try_from(speed).unwrap_or_else(|_| unreachable!()),
579 )),
580 flags,
581 )?;
582 }
583 Ok(())
584}
585
586#[cfg(test)]
587mod tests {
588 use super::*;
589
590 #[test]
591 fn check_get_mouse_speed() -> io::Result<()> {
592 let speed = get_mouse_speed()?;
593 dbg!(speed);
594 assert!((1..=20).contains(&speed));
595 Ok(())
596 }
597}