win_text_inject/
modifiers.rs1use windows::Win32::UI::Input::KeyboardAndMouse::{
13 GetAsyncKeyState, SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_KEYUP,
14 VIRTUAL_KEY, VK_LCONTROL, VK_LMENU, VK_LSHIFT, VK_LWIN, VK_RCONTROL, VK_RMENU, VK_RSHIFT,
15 VK_RWIN,
16};
17
18use crate::sendinput::{tagged_keyboard_input, INJECT_TAG};
19use crate::Error;
20
21const MODIFIERS: [VIRTUAL_KEY; 8] = [
23 VK_LSHIFT,
24 VK_RSHIFT,
25 VK_LCONTROL,
26 VK_RCONTROL,
27 VK_LMENU,
28 VK_RMENU,
29 VK_LWIN,
30 VK_RWIN,
31];
32
33const KEY_DOWN_MASK: u16 = 0x8000;
35
36fn is_down(vk: VIRTUAL_KEY) -> bool {
37 (unsafe { GetAsyncKeyState(vk.0 as i32) } as u16 & KEY_DOWN_MASK) != 0
38}
39
40pub fn sanitize() -> Result<Vec<VIRTUAL_KEY>, Error> {
46 let held: Vec<VIRTUAL_KEY> = MODIFIERS.into_iter().filter(|vk| is_down(*vk)).collect();
47 if held.is_empty() {
48 return Ok(held);
49 }
50
51 let inputs: Vec<INPUT> = held
52 .iter()
53 .map(|vk| INPUT {
54 r#type: INPUT_KEYBOARD,
55 Anonymous: INPUT_0 {
56 ki: KEYBDINPUT {
57 wVk: *vk,
58 wScan: 0,
59 dwFlags: KEYEVENTF_KEYUP,
60 time: 0,
61 dwExtraInfo: INJECT_TAG,
62 },
63 },
64 })
65 .collect();
66
67 let sent = unsafe { SendInput(&inputs, std::mem::size_of::<INPUT>() as i32) };
68 if sent as usize != inputs.len() {
69 return Err(Error::SendInputBlocked);
70 }
71 Ok(held)
72}
73
74pub fn any_held() -> bool {
76 MODIFIERS.into_iter().any(is_down)
77}
78
79pub(crate) fn key_up(vk: VIRTUAL_KEY) -> INPUT {
81 tagged_keyboard_input(vk, KEYEVENTF_KEYUP)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn modifier_set_covers_both_sides_of_every_modifier() {
90 assert_eq!(MODIFIERS.len(), 8);
91 for pair in [
92 (VK_LSHIFT, VK_RSHIFT),
93 (VK_LCONTROL, VK_RCONTROL),
94 (VK_LMENU, VK_RMENU),
95 (VK_LWIN, VK_RWIN),
96 ] {
97 assert!(MODIFIERS.contains(&pair.0));
98 assert!(MODIFIERS.contains(&pair.1));
99 }
100 }
101
102 #[test]
103 fn any_held_agrees_with_per_key_state() {
104 let individually = MODIFIERS.into_iter().any(is_down);
106 assert_eq!(any_held(), individually);
107 }
108}