crayon/input/
keyboard.rs

1use std::time::Duration;
2
3use crate::utils::hash::{FastHashMap, FastHashSet};
4use crate::utils::time::Timestamp;
5
6/// The setup parameters of keyboard device.
7#[derive(Debug, Clone, Copy)]
8pub struct KeyboardParams {
9    /// The maximum characters that could be captured in one frame.
10    pub max_chars: usize,
11    /// The time duration before a pressing is recognized as repeat operation.
12    pub repeat_timeout: Duration,
13    /// The interval time duration between triggering repeat events.
14    pub repeat_interval_timeout: Duration,
15}
16
17impl Default for KeyboardParams {
18    fn default() -> Self {
19        KeyboardParams {
20            max_chars: 128,
21            repeat_timeout: Duration::from_millis(500),
22            repeat_interval_timeout: Duration::from_millis(250),
23        }
24    }
25}
26
27/// Symbolic name for a keyboard key.
28#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
29pub enum Key {
30    /// The '1' key over the letters.
31    Key1,
32    /// The '2' key over the letters.
33    Key2,
34    /// The '3' key over the letters.
35    Key3,
36    /// The '4' key over the letters.
37    Key4,
38    /// The '5' key over the letters.
39    Key5,
40    /// The '6' key over the letters.
41    Key6,
42    /// The '7' key over the letters.
43    Key7,
44    /// The '8' key over the letters.
45    Key8,
46    /// The '9' key over the letters.
47    Key9,
48    /// The '0' key over the 'O' and 'P' keys.
49    Key0,
50
51    A,
52    B,
53    C,
54    D,
55    E,
56    F,
57    G,
58    H,
59    I,
60    J,
61    K,
62    L,
63    M,
64    N,
65    O,
66    P,
67    Q,
68    R,
69    S,
70    T,
71    U,
72    V,
73    W,
74    X,
75    Y,
76    Z,
77
78    /// The Escape key, next to F1.
79    Escape,
80
81    F1,
82    F2,
83    F3,
84    F4,
85    F5,
86    F6,
87    F7,
88    F8,
89    F9,
90    F10,
91    F11,
92    F12,
93    F13,
94    F14,
95    F15,
96
97    /// Print Screen/SysRq.
98    Snapshot,
99    /// Scroll Lock.
100    Scroll,
101    /// Pause/Break key, next to Scroll lock.
102    Pause,
103
104    /// `Insert`, next to Backspace.
105    Insert,
106    Home,
107    Delete,
108    End,
109    PageDown,
110    PageUp,
111
112    Left,
113    Up,
114    Right,
115    Down,
116
117    /// The Backspace key, right over Enter.
118    // TODO: rename
119    Back,
120    /// The Enter key.
121    Return,
122    /// The space bar.
123    Space,
124
125    /// The "Compose" key on Linux.
126    Compose,
127
128    Caret,
129
130    Numlock,
131    Numpad0,
132    Numpad1,
133    Numpad2,
134    Numpad3,
135    Numpad4,
136    Numpad5,
137    Numpad6,
138    Numpad7,
139    Numpad8,
140    Numpad9,
141
142    Add,
143    Backslash,
144    Calculator,
145    Capital,
146    Colon,
147    Comma,
148    Convert,
149    Decimal,
150    Divide,
151    Equals,
152    LAlt,
153    LBracket,
154    LControl,
155    LShift,
156    LWin,
157    Minus,
158    Multiply,
159    Mute,
160    NavigateForward,  // also called "Prior"
161    NavigateBackward, // also called "Next"
162    NumpadComma,
163    NumpadEnter,
164    NumpadEquals,
165    Period,
166    PlayPause,
167    Power,
168    PrevTrack,
169    RAlt,
170    RBracket,
171    RControl,
172    RShift,
173    RWin,
174    Semicolon,
175    Slash,
176    Sleep,
177    Stop,
178    Subtract,
179    Tab,
180    Underline,
181    Unlabeled,
182    VolumeDown,
183    VolumeUp,
184    Wake,
185}
186
187enum KeyDownState {
188    Start(Timestamp),
189    Press(Timestamp),
190}
191
192pub struct Keyboard {
193    downs: FastHashMap<Key, KeyDownState>,
194    presses: FastHashSet<Key>,
195    releases: FastHashSet<Key>,
196    chars: Vec<char>,
197    setup: KeyboardParams,
198    now: Timestamp,
199}
200
201impl Keyboard {
202    pub fn new(setup: KeyboardParams) -> Self {
203        Keyboard {
204            setup,
205            downs: FastHashMap::default(),
206            presses: FastHashSet::default(),
207            releases: FastHashSet::default(),
208            chars: Vec::with_capacity(setup.max_chars),
209            now: Timestamp::now(),
210        }
211    }
212
213    #[inline]
214    pub fn reset(&mut self) {
215        self.downs.clear();
216        self.presses.clear();
217        self.releases.clear();
218        self.chars.clear();
219    }
220
221    #[inline]
222    pub fn advance(&mut self) {
223        self.presses.clear();
224        self.releases.clear();
225        self.chars.clear();
226
227        let last_frame_ts = self.now;
228        for v in self.downs.values_mut() {
229            match *v {
230                KeyDownState::Start(ts) => {
231                    if (last_frame_ts - ts) > self.setup.repeat_timeout {
232                        *v = KeyDownState::Press(ts);
233                    }
234                }
235                KeyDownState::Press(ts) => {
236                    if (last_frame_ts - ts) > self.setup.repeat_interval_timeout {
237                        *v = KeyDownState::Press(last_frame_ts);
238                    }
239                }
240            }
241        }
242
243        self.now = Timestamp::now();
244    }
245
246    #[inline]
247    pub fn on_key_pressed(&mut self, key: Key) {
248        let presses = &mut self.presses;
249        let now = self.now;
250        self.downs.entry(key).or_insert_with(|| {
251            presses.insert(key);
252            KeyDownState::Start(now)
253        });
254    }
255
256    #[inline]
257    pub fn on_key_released(&mut self, key: Key) {
258        self.downs.remove(&key);
259        self.releases.insert(key);
260    }
261
262    #[inline]
263    pub fn on_char(&mut self, c: char) {
264        if self.chars.len() < self.setup.max_chars {
265            self.chars.push(c);
266        }
267    }
268
269    #[inline]
270    pub fn is_key_down(&self, key: Key) -> bool {
271        self.downs.contains_key(&key)
272    }
273
274    #[inline]
275    pub fn is_key_press(&self, key: Key) -> bool {
276        self.presses.contains(&key)
277    }
278
279    #[inline]
280    pub fn is_key_release(&self, key: Key) -> bool {
281        self.releases.contains(&key)
282    }
283
284    pub fn is_key_repeat(&self, key: Key) -> bool {
285        if let Some(v) = self.downs.get(&key) {
286            match *v {
287                KeyDownState::Start(ts) => (self.now - ts) > self.setup.repeat_timeout,
288                KeyDownState::Press(ts) => (self.now - ts) > self.setup.repeat_interval_timeout,
289            }
290        } else {
291            false
292        }
293    }
294
295    #[inline]
296    pub fn captured_chars(&self) -> &[char] {
297        &self.chars
298    }
299}