good_web_game/input/
keyboard.rs

1use bitflags::bitflags;
2
3use crate::Context;
4
5/// A key code.
6pub use miniquad::KeyCode;
7use std::collections::HashSet;
8
9/// Tracks held down keyboard keys, active keyboard modifiers,
10/// and figures out if the system is sending repeat keystrokes.
11#[derive(Clone, Debug)]
12pub struct KeyboardContext {
13    active_modifiers: KeyMods,
14    /// A simple mapping of which key code has been pressed.
15    /// We COULD use a `Vec<bool>` but turning Rust enums to and from
16    /// integers is unsafe and a set really is what we want anyway.
17    pressed_keys_set: HashSet<KeyCode>,
18
19    // These two are necessary for tracking key-repeat.
20    last_pressed: Option<KeyCode>,
21    current_pressed: Option<KeyCode>,
22}
23
24impl KeyboardContext {
25    pub(crate) fn new() -> Self {
26        Self {
27            active_modifiers: KeyMods::empty(),
28            // We just use 256 as a number Big Enough For Keyboard Keys to try to avoid resizing.
29            pressed_keys_set: HashSet::with_capacity(256),
30            last_pressed: None,
31            current_pressed: None,
32        }
33    }
34
35    pub(crate) fn set_key(&mut self, key: KeyCode, pressed: bool) {
36        if pressed {
37            let _ = self.pressed_keys_set.insert(key);
38            self.last_pressed = self.current_pressed;
39            self.current_pressed = Some(key);
40        } else {
41            let _ = self.pressed_keys_set.remove(&key);
42            self.current_pressed = None;
43        }
44
45        self.set_key_modifier(key, pressed);
46    }
47
48    /// Take a modifier key code and alter our state.
49    ///
50    /// Double check that this edge handling is necessary;
51    /// winit sounds like it should do this for us,
52    /// see https://docs.rs/winit/0.18.0/winit/struct.KeyboardInput.html#structfield.modifiers
53    ///
54    /// ...more specifically, we should refactor all this to consistant-ify events a bit and
55    /// make winit do more of the work.
56    /// But to quote Scott Pilgrim, "This is... this is... Booooooring."
57    fn set_key_modifier(&mut self, key: KeyCode, pressed: bool) {
58        if pressed {
59            match key {
60                KeyCode::LeftShift | KeyCode::RightShift => self.active_modifiers |= KeyMods::SHIFT,
61                KeyCode::LeftControl | KeyCode::RightControl => {
62                    self.active_modifiers |= KeyMods::CTRL
63                }
64                KeyCode::LeftAlt | KeyCode::RightAlt => self.active_modifiers |= KeyMods::ALT,
65                KeyCode::LeftSuper | KeyCode::RightSuper => self.active_modifiers |= KeyMods::LOGO,
66                _ => (),
67            }
68        } else {
69            match key {
70                KeyCode::LeftShift | KeyCode::RightShift => self.active_modifiers -= KeyMods::SHIFT,
71                KeyCode::LeftControl | KeyCode::RightControl => {
72                    self.active_modifiers -= KeyMods::CTRL
73                }
74                KeyCode::LeftAlt | KeyCode::RightAlt => self.active_modifiers -= KeyMods::ALT,
75                KeyCode::LeftSuper | KeyCode::RightSuper => self.active_modifiers -= KeyMods::LOGO,
76                _ => (),
77            }
78        }
79    }
80
81    //pub(crate) fn set_modifiers(&mut self, keymods: KeyMods) {
82    //    self.active_modifiers = keymods;
83    //}
84
85    pub(crate) fn is_key_pressed(&self, key: KeyCode) -> bool {
86        self.pressed_keys_set.contains(&key)
87    }
88
89    pub(crate) fn is_key_repeated(&self) -> bool {
90        if self.last_pressed.is_some() {
91            self.last_pressed == self.current_pressed
92        } else {
93            false
94        }
95    }
96
97    pub(crate) fn pressed_keys(&self) -> &HashSet<KeyCode> {
98        &self.pressed_keys_set
99    }
100
101    pub(crate) fn active_mods(&self) -> KeyMods {
102        self.active_modifiers
103    }
104}
105
106impl Default for KeyboardContext {
107    fn default() -> Self {
108        Self::new()
109    }
110}
111
112/// Checks if a key is currently pressed down.
113pub fn is_key_pressed(ctx: &Context, key: KeyCode) -> bool {
114    ctx.keyboard_context.is_key_pressed(key)
115}
116
117/// Checks if the last keystroke sent by the system is repeated, like when a key is held down for a period of time.
118pub fn is_key_repeated(ctx: &Context) -> bool {
119    ctx.keyboard_context.is_key_repeated()
120}
121
122/// Returns a reference to the set of currently pressed keys.
123pub fn pressed_keys(ctx: &Context) -> &HashSet<KeyCode> {
124    ctx.keyboard_context.pressed_keys()
125}
126
127/// Returns currently active keyboard modifiers.
128pub fn active_mods(ctx: &Context) -> KeyMods {
129    ctx.keyboard_context.active_mods()
130}
131
132/// Checks if keyboard modifier (or several) is active.
133pub fn is_mod_active(ctx: &Context, keymods: KeyMods) -> bool {
134    ctx.keyboard_context.active_mods().contains(keymods)
135}
136
137bitflags! {
138    /// Bitflags describing the state of keyboard modifiers, such as `Control` or `Shift`.
139    #[derive(Default)]
140    pub struct KeyMods: u8 {
141        /// No modifiers; equivalent to `KeyMods::default()` and
142        /// [`KeyMods::empty()`](struct.KeyMods.html#method.empty).
143        const NONE  = 0b0000_0000;
144        /// Left or right Shift key.
145        const SHIFT = 0b0000_0001;
146        /// Left or right Control key.
147        const CTRL  = 0b0000_0010;
148        /// Left or right Alt key.
149        const ALT   = 0b0000_0100;
150        /// Left or right Win/Cmd/equivalent key.
151        const LOGO  = 0b0000_1000;
152    }
153}
154
155impl From<miniquad::KeyMods> for KeyMods {
156    fn from(quad_mods: miniquad::KeyMods) -> Self {
157        let mut keymods = KeyMods::NONE;
158
159        if quad_mods.shift {
160            keymods |= KeyMods::SHIFT;
161        }
162        if quad_mods.ctrl {
163            keymods |= KeyMods::CTRL;
164        }
165        if quad_mods.alt {
166            keymods |= KeyMods::ALT;
167        }
168        if quad_mods.logo {
169            keymods |= KeyMods::LOGO;
170        }
171
172        keymods
173    }
174}