win_hotkeys/
state.rs

1//! This module provides the `KeyboardState` struct to track the state of keyboard keys.
2//! It supports key press (`keydown`), key release (`keyup`), and querying key state (`is_down`).
3
4use crate::VKey;
5use windows::Win32::UI::Input::KeyboardAndMouse::GetAsyncKeyState;
6
7/// Represents a state of pressed keys on a keyboard.
8/// Can be used to track the current state of the keyboard
9/// or to represent a keyboard state for hotkeys.
10///
11/// Tracks pressed keys using two 128-bit flags, allowing
12/// support for 256 keys.
13#[derive(Copy, Clone)]
14pub struct KeyboardState {
15    pub flags: [u128; 2],
16}
17
18impl KeyboardState {
19    /// Creates a new `KeyboardState` with all keys released.
20    pub fn new() -> KeyboardState {
21        KeyboardState { flags: [0, 0] }
22    }
23
24    /// Marks a key as pressed.
25    ///
26    /// Keys VK_Shift, VK_Control, and VK_Menu will be marked
27    /// as pressed when either the left or right version is
28    /// pressed.
29    pub fn keydown(&mut self, key: u16) {
30        let index = (key / 128) as usize;
31        let position = key % 128;
32        self.flags[index] |= 1 << position;
33        match key {
34            _ if key == VKey::LShift.to_vk_code() => self.keydown(VKey::Shift.to_vk_code()),
35            _ if key == VKey::RShift.to_vk_code() => self.keydown(VKey::Shift.to_vk_code()),
36            _ if key == VKey::LControl.to_vk_code() => self.keydown(VKey::Control.to_vk_code()),
37            _ if key == VKey::RControl.to_vk_code() => self.keydown(VKey::Control.to_vk_code()),
38            _ if key == VKey::LMenu.to_vk_code() => self.keydown(VKey::Menu.to_vk_code()),
39            _ if key == VKey::RMenu.to_vk_code() => self.keydown(VKey::Menu.to_vk_code()),
40            _ => {}
41        }
42    }
43
44    /// Marks a key as released.
45    pub fn keyup(&mut self, key: u16) {
46        let index = (key / 128) as usize;
47        let position = key % 128;
48        self.flags[index] &= !(1 << position);
49        if (key == VKey::LShift.to_vk_code() || key == VKey::RShift.to_vk_code())
50            && !self.is_down(VKey::LShift.to_vk_code())
51            && !self.is_down(VKey::RShift.to_vk_code())
52        {
53            self.keyup(VKey::Shift.to_vk_code());
54        } else if (key == VKey::LControl.to_vk_code() || key == VKey::RControl.to_vk_code())
55            && !self.is_down(VKey::LControl.to_vk_code())
56            && !self.is_down(VKey::RControl.to_vk_code())
57        {
58            self.keyup(VKey::Control.to_vk_code());
59        } else if (key == VKey::LMenu.to_vk_code() || key == VKey::RMenu.to_vk_code())
60            && !self.is_down(VKey::LMenu.to_vk_code())
61            && !self.is_down(VKey::RMenu.to_vk_code())
62        {
63            self.keyup(VKey::Menu.to_vk_code());
64        }
65    }
66
67    /// Checks if a key is currently pressed.
68    pub fn is_down(&self, key: u16) -> bool {
69        let index = (key / 128) as usize;
70        let position = key % 128;
71        (self.flags[index] & (1 << position)) != 0
72    }
73
74    /// Checks the state of each pressed key against
75    /// the OS and removes them if they are not pressed.
76    pub fn sync(&mut self) {
77        for vk_code in 0..128 {
78            let mask = 1u128 << vk_code;
79            if self.flags[0] & mask != 0 && !Self::get_async_key_state(vk_code) {
80                self.keyup(vk_code);
81            }
82            if self.flags[1] & mask != 0 && !Self::get_async_key_state(vk_code + 128) {
83                self.keyup(vk_code + 128);
84            }
85        }
86    }
87
88    /// Clears the state of all keys, marking them as released.
89    pub fn clear(&mut self) {
90        self.flags = [0, 0];
91    }
92
93    /// Returns whether a key is currently pressed according to
94    /// the OS.
95    pub fn get_async_key_state(key: u16) -> bool {
96        unsafe { (GetAsyncKeyState(key.into()) & -0x8000) != 0 }
97    }
98}
99
100impl std::fmt::Debug for KeyboardState {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        let mut keys = Vec::new();
103        for vk_code in 0..128 {
104            match vk_code {
105                _ if vk_code == VKey::Shift.to_vk_code() => continue,
106                _ if vk_code == VKey::Control.to_vk_code() => continue,
107                _ if vk_code == VKey::Menu.to_vk_code() => continue,
108                _ => {}
109            }
110            let mask = 1u128 << vk_code;
111            if self.flags[0] & mask != 0 {
112                keys.push(VKey::from_vk_code(vk_code));
113            }
114            if self.flags[1] & mask != 0 {
115                keys.push(VKey::from_vk_code(vk_code + 128));
116            }
117        }
118        f.debug_struct("KeyboardState")
119            //.field("flags[0]",  &format!("{:0128b}", &self.flags[0]))
120            //.field("flags[1]", &format!("{:0128b}", &self.flags[1]))
121            .field("Keys", &keys)
122            .finish()
123    }
124}
125
126impl Default for KeyboardState {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132impl PartialEq for KeyboardState {
133    fn eq(&self, other: &KeyboardState) -> bool {
134        self.flags == other.flags
135    }
136}
137
138impl Eq for KeyboardState {}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_new_keyboard_state() {
146        let keyboard = KeyboardState::new();
147        assert_eq!(
148            keyboard.flags,
149            [0, 0],
150            "New KeyboardState should have all flags cleared"
151        );
152    }
153
154    #[test]
155    fn test_keydown() {
156        let mut keyboard = KeyboardState::new();
157        keyboard.keydown(65);
158        assert_eq!(keyboard.flags[0], 1 << (65 % 128), "Key 65 should be set");
159
160        keyboard.keydown(129);
161        assert_eq!(keyboard.flags[1], 1 << (129 % 128), "Key 129 should be set");
162    }
163
164    #[test]
165    fn test_keyup() {
166        let mut keyboard = KeyboardState::new();
167        keyboard.keydown(65); // Press key 65
168        keyboard.keyup(65); // Release key 65
169        assert_eq!(keyboard.flags[0], 0, "Key 65 should be cleared");
170
171        keyboard.keydown(129); // Press key 129
172        keyboard.keyup(129); // Release key 129
173        assert_eq!(keyboard.flags[1], 0, "Key 129 should be cleared");
174    }
175
176    #[test]
177    fn test_clear() {
178        let mut keyboard = KeyboardState::new();
179        keyboard.keydown(65); // Press key 65
180        keyboard.keydown(129); // Press key 129
181        keyboard.clear(); // Clear all flags
182        assert_eq!(
183            keyboard.flags,
184            [0, 0],
185            "KeyboardState should be cleared after clear()"
186        );
187    }
188
189    #[test]
190    fn test_clone() {
191        let mut keyboard = KeyboardState::new();
192        keyboard.keydown(65); // Press key 65
193        let cloned_keyboard = keyboard.clone();
194        assert_eq!(
195            keyboard, cloned_keyboard,
196            "Cloned KeyboardState should be equal to the original"
197        );
198
199        // Modify the original and ensure the clone is unaffected
200        keyboard.keydown(129);
201        assert_ne!(
202            keyboard, cloned_keyboard,
203            "Cloned KeyboardState should not reflect changes to the original"
204        );
205    }
206
207    #[test]
208    fn test_equality() {
209        let mut keyboard1 = KeyboardState::new();
210        let mut keyboard2 = KeyboardState::new();
211
212        // Both are empty and should be equal
213        assert_eq!(
214            keyboard1, keyboard2,
215            "Two empty KeyboardState instances should be equal"
216        );
217
218        // Modify one and ensure inequality
219        keyboard1.keydown(65);
220        assert_ne!(
221            keyboard1, keyboard2,
222            "KeyboardState instances with different flags should not be equal"
223        );
224
225        // Make them equal again
226        keyboard2.keydown(65);
227        assert_eq!(
228            keyboard1, keyboard2,
229            "KeyboardState instances with the same flags should be equal"
230        );
231    }
232
233    #[test]
234    fn test_multiple_keys() {
235        let mut keyboard = KeyboardState::new();
236
237        // Press multiple keys
238        keyboard.keydown(65);
239        keyboard.keydown(70);
240        keyboard.keydown(129);
241
242        assert_eq!(true, keyboard.is_down(65), "Key 65 should be set");
243        assert_eq!(true, keyboard.is_down(70), "Key 70 should be set");
244        assert_eq!(true, keyboard.is_down(129), "Key 129 should be set");
245
246        // Release some keys
247        keyboard.keyup(65);
248        keyboard.keyup(70);
249
250        assert_eq!(keyboard.flags[0], 0, "Key 65 should be cleared");
251        assert_eq!(keyboard.flags[0], 0, "Key 70 should be cleared");
252        assert_eq!(
253            keyboard.flags[1],
254            1 << (129 % 128),
255            "Key 129 should remain set"
256        );
257    }
258}