1use std::collections::{HashMap, HashSet};
10use std::time::{Duration, Instant};
11
12#[allow(missing_docs)]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub enum Key {
19 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
20 Digit0, Digit1, Digit2, Digit3, Digit4, Digit5, Digit6, Digit7, Digit8, Digit9,
21 F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
22 Up, Down, Left, Right,
23 Enter, Escape, Space, Tab, Backspace, Delete, Insert,
24 Home, End, PageUp, PageDown,
25 Shift, Control, Alt,
26 Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
27 NumpadAdd, NumpadSubtract, NumpadMultiply, NumpadDivide, NumpadDecimal, NumpadEnter,
28 Comma, Period, Minus, Plus, Slash, Backslash, Semicolon, Quote, LeftBracket, RightBracket, Backquote,
29}
30
31impl Key {
32 pub fn from_char(c: char) -> Option<Key> {
34 use Key::*;
35 const LETTERS: [Key; 26] =
36 [A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z];
37 const DIGITS: [Key; 10] =
38 [Digit0, Digit1, Digit2, Digit3, Digit4, Digit5, Digit6, Digit7, Digit8, Digit9];
39 let c = c.to_ascii_lowercase();
40 match c {
41 'a'..='z' => Some(LETTERS[(c as u8 - b'a') as usize]),
42 '0'..='9' => Some(DIGITS[(c as u8 - b'0') as usize]),
43 _ => None,
44 }
45 }
46
47 pub fn name(self) -> String {
49 let debug = format!("{self:?}");
50 if let Some(d) = debug.strip_prefix("Digit") {
51 return d.to_string();
52 }
53 let mut out = String::new();
55 for (i, c) in debug.chars().enumerate() {
56 if i > 0 && c.is_uppercase() {
57 out.push(' ');
58 }
59 out.push(c);
60 }
61 out
62 }
63}
64
65#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
67pub struct Modifiers {
68 pub shift: bool,
70 pub control: bool,
72 pub alt: bool,
74}
75
76#[derive(Debug, Clone, Copy)]
78pub(crate) enum KeyEvent {
79 Down { key: Key, repeat: bool },
80 Up { key: Key },
81}
82
83#[derive(Debug, Default)]
85pub struct Input {
86 queue: Vec<KeyEvent>,
87 text_queue: String,
88
89 held: HashMap<Key, Instant>,
90 pressed: HashSet<Key>,
91 repeated: HashSet<Key>,
92 released: HashSet<Key>,
93 order: Vec<Key>,
94 text: String,
95 held_now: HashMap<Key, f32>,
96 held_prev: HashMap<Key, f32>,
97
98 blocked: bool,
99}
100
101impl Input {
102 pub(crate) fn push(&mut self, event: KeyEvent) {
103 self.queue.push(event);
104 }
105
106 pub(crate) fn push_text(&mut self, text: &str) {
107 self.text_queue.extend(text.chars().filter(|c| !c.is_control()));
108 }
109
110 pub(crate) fn release_all(&mut self) {
112 let keys: Vec<Key> = self.held.keys().copied().collect();
113 for key in keys {
114 self.queue.push(KeyEvent::Up { key });
115 }
116 }
117
118 pub(crate) fn begin_frame(&mut self, now: Instant) {
120 self.pressed.clear();
121 self.repeated.clear();
122 self.released.clear();
123 self.order.clear();
124 self.text = std::mem::take(&mut self.text_queue);
125 for event in std::mem::take(&mut self.queue) {
126 match event {
127 KeyEvent::Down { key, repeat } => {
128 if repeat || self.held.contains_key(&key) {
129 self.repeated.insert(key);
130 } else {
131 self.held.insert(key, now);
132 self.pressed.insert(key);
133 }
134 self.order.push(key);
135 }
136 KeyEvent::Up { key } => {
137 if self.held.remove(&key).is_some() {
138 self.released.insert(key);
139 }
140 }
141 }
142 }
143 self.held_prev = std::mem::take(&mut self.held_now);
144 for (key, since) in &self.held {
145 self.held_now.insert(*key, now.duration_since(*since).as_secs_f32());
146 }
147 }
148
149 pub(crate) fn set_blocked(&mut self, blocked: bool) {
150 self.blocked = blocked;
151 }
152
153 pub fn is_blocked(&self) -> bool {
155 self.blocked
156 }
157
158 pub fn pressed(&self, key: Key) -> bool {
160 !self.blocked && self.pressed.contains(&key)
161 }
162
163 pub fn pressed_or_repeated(&self, key: Key) -> bool {
166 !self.blocked && (self.pressed.contains(&key) || self.repeated.contains(&key))
167 }
168
169 pub fn held(&self, key: Key) -> bool {
171 !self.blocked && self.held.contains_key(&key)
172 }
173
174 pub fn released(&self, key: Key) -> bool {
176 !self.blocked && self.released.contains(&key)
177 }
178
179 pub fn held_for(&self, key: Key) -> Option<f32> {
181 if self.blocked { None } else { self.held_now.get(&key).copied() }
182 }
183
184 pub fn repeating(&self, key: Key, delay: f32, every: f32) -> bool {
198 if self.blocked {
199 return false;
200 }
201 if self.pressed.contains(&key) {
202 return true;
203 }
204 let (Some(&now), Some(&prev)) = (self.held_now.get(&key), self.held_prev.get(&key)) else {
205 return false;
206 };
207 if now < delay {
208 return false;
209 }
210 let every = every.max(0.001);
211 let ticks = |t: f32| if t < delay { -1.0 } else { ((t - delay) / every).floor() };
213 ticks(now) > ticks(prev)
214 }
215
216 pub fn axis(&self, negative: Key, positive: Key) -> f32 {
219 self.held(positive) as i32 as f32 - self.held(negative) as i32 as f32
220 }
221
222 pub fn movement(&self) -> glam::Vec2 {
225 let x = (self.held(Key::Right) || self.held(Key::D)) as i32 - (self.held(Key::Left) || self.held(Key::A)) as i32;
226 let y = (self.held(Key::Up) || self.held(Key::W)) as i32 - (self.held(Key::Down) || self.held(Key::S)) as i32;
227 glam::Vec2::new(x as f32, y as f32).normalize_or_zero()
228 }
229
230 pub fn movement_3d(&self) -> glam::Vec3 {
234 let flat = self.movement();
235 let z = (self.held(Key::PageUp) || self.held(Key::E)) as i32 - (self.held(Key::PageDown) || self.held(Key::Q)) as i32;
236 glam::Vec3::new(flat.x, flat.y, z as f32).normalize_or_zero()
237 }
238
239 pub fn any_pressed(&self) -> bool {
241 !self.blocked && !self.pressed.is_empty()
242 }
243
244 pub fn keys_pressed(&self) -> &[Key] {
246 if self.blocked { &[] } else { &self.order }
247 }
248
249 pub fn text(&self) -> &str {
251 if self.blocked { "" } else { &self.text }
252 }
253
254 pub fn modifiers(&self) -> Modifiers {
256 let held = |k| !self.blocked && self.held.contains_key(&k);
257 Modifiers { shift: held(Key::Shift), control: held(Key::Control), alt: held(Key::Alt) }
258 }
259
260 pub fn raw(&self) -> RawInput<'_> {
262 RawInput(self)
263 }
264
265 pub fn held_duration(&self, key: Key) -> Option<Duration> {
267 self.held_for(key).map(Duration::from_secs_f32)
268 }
269}
270
271#[derive(Debug, Clone, Copy)]
273pub struct RawInput<'a>(&'a Input);
274
275impl RawInput<'_> {
276 pub fn pressed(&self, key: Key) -> bool {
278 self.0.pressed.contains(&key)
279 }
280
281 pub fn pressed_or_repeated(&self, key: Key) -> bool {
283 self.0.pressed.contains(&key) || self.0.repeated.contains(&key)
284 }
285
286 pub fn held(&self, key: Key) -> bool {
288 self.0.held.contains_key(&key)
289 }
290
291 pub fn released(&self, key: Key) -> bool {
293 self.0.released.contains(&key)
294 }
295
296 pub fn text(&self) -> &str {
298 &self.0.text
299 }
300
301 pub fn keys_pressed(&self) -> &[Key] {
303 &self.0.order
304 }
305
306 pub fn modifiers(&self) -> Modifiers {
308 let held = |k| self.0.held.contains_key(&k);
309 Modifiers { shift: held(Key::Shift), control: held(Key::Control), alt: held(Key::Alt) }
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn press_and_release_in_one_frame_is_seen() {
319 let mut input = Input::default();
320 input.push(KeyEvent::Down { key: Key::Space, repeat: false });
321 input.push(KeyEvent::Up { key: Key::Space });
322 input.begin_frame(Instant::now());
323 assert!(input.pressed(Key::Space));
324 assert!(input.released(Key::Space));
325 assert!(!input.held(Key::Space));
326 }
327
328 #[test]
329 fn blocking_hides_keys() {
330 let mut input = Input::default();
331 input.push(KeyEvent::Down { key: Key::A, repeat: false });
332 input.begin_frame(Instant::now());
333 input.set_blocked(true);
334 assert!(!input.pressed(Key::A));
335 assert!(input.raw().pressed(Key::A));
336 }
337
338 #[test]
339 fn game_repeat() {
340 let mut input = Input::default();
341 let t = Instant::now();
342 input.push(KeyEvent::Down { key: Key::Up, repeat: false });
343 input.begin_frame(t);
344 assert!(input.repeating(Key::Up, 0.3, 0.1));
345 input.begin_frame(t + Duration::from_millis(200));
346 assert!(!input.repeating(Key::Up, 0.3, 0.1));
347 input.begin_frame(t + Duration::from_millis(310));
348 assert!(input.repeating(Key::Up, 0.3, 0.1));
349 input.begin_frame(t + Duration::from_millis(350));
350 assert!(!input.repeating(Key::Up, 0.3, 0.1));
351 input.begin_frame(t + Duration::from_millis(420));
352 assert!(input.repeating(Key::Up, 0.3, 0.1));
353 }
354
355 #[test]
356 fn movement_3d_adds_up_and_down() {
357 let mut input = Input::default();
358 input.push(KeyEvent::Down { key: Key::PageUp, repeat: false });
359 input.begin_frame(Instant::now());
360 assert_eq!(input.movement_3d(), glam::Vec3::Z);
361 input.push(KeyEvent::Down { key: Key::W, repeat: false });
362 input.begin_frame(Instant::now());
363 assert!((input.movement_3d() - glam::Vec3::new(0.0, 1.0, 1.0).normalize()).length() < 1e-5);
364 }
365
366 #[test]
367 fn key_names() {
368 assert_eq!(Key::PageUp.name(), "Page Up");
369 assert_eq!(Key::Digit5.name(), "5");
370 assert_eq!(Key::from_char('Q'), Some(Key::Q));
371 }
372}