1use std::time::Duration;
2
3use crate::utils::hash::{FastHashMap, FastHashSet};
4use crate::utils::time::Timestamp;
5
6#[derive(Debug, Clone, Copy)]
8pub struct KeyboardParams {
9 pub max_chars: usize,
11 pub repeat_timeout: Duration,
13 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#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
29pub enum Key {
30 Key1,
32 Key2,
34 Key3,
36 Key4,
38 Key5,
40 Key6,
42 Key7,
44 Key8,
46 Key9,
48 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 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 Snapshot,
99 Scroll,
101 Pause,
103
104 Insert,
106 Home,
107 Delete,
108 End,
109 PageDown,
110 PageUp,
111
112 Left,
113 Up,
114 Right,
115 Down,
116
117 Back,
120 Return,
122 Space,
124
125 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, NavigateBackward, 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}