Skip to main content

care_game/
keyboard.rs

1use std::{collections::HashSet, sync::OnceLock};
2
3use parking_lot::RwLock;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
6/// Keyboard keys
7pub enum Key {
8    /// A text character
9    Char(char),
10    /// The up arrow
11    Up,
12    /// The down arrow
13    Down,
14    /// The left arrow
15    Left,
16    /// The right arrow
17    Right,
18    /// The space key
19    Space,
20    /// The enter key
21    Enter,
22    /// The escape key
23    Escape,
24    /// The backspace key
25    Backspace,
26    /// The delete key
27    Delete,
28    /// The shift key/modifier
29    Shift,
30    /// The control key/modifier
31    Control,
32    /// The alt key/modifier
33    Alt,
34    /// The meta (also sometimes the "windows", "command" or "open apple") key/modifier
35    Meta,
36    /// An unknown or unrecognized key
37    Unknown,
38}
39
40impl From<char> for Key {
41    fn from(value: char) -> Self {
42        if value == ' ' {
43            Self::Space
44        } else {
45            Self::Char(value.to_lowercase().next().unwrap_or(value))
46        }
47    }
48}
49
50impl From<&str> for Key {
51    fn from(value: &str) -> Self {
52        if value.len() == 1 {
53            Self::from(value.chars().next().unwrap())
54        } else {
55            match value.to_lowercase().as_str() {
56                "up" => Self::Up,
57                "down" => Self::Down,
58                "left" => Self::Left,
59                "right" => Self::Right,
60                "space" => Self::Space,
61                "enter" => Self::Enter,
62                "escape" => Self::Escape,
63                "backspace" => Self::Backspace,
64                "delete" => Self::Delete,
65                "shift" => Self::Shift,
66                "control" => Self::Control,
67                "alt" => Self::Alt,
68                "meta" => Self::Meta,
69                _ => Self::Unknown,
70            }
71        }
72    }
73}
74
75impl From<String> for Key {
76    fn from(value: String) -> Self {
77        Key::from(value.as_str())
78    }
79}
80
81#[derive(Debug)]
82struct KeyboardState {
83    pressed: HashSet<Key>,
84    released: HashSet<Key>,
85    held: HashSet<Key>,
86}
87
88impl KeyboardState {
89    fn empty() -> Self {
90        Self {
91            pressed: HashSet::new(),
92            released: HashSet::new(),
93            held: HashSet::new(),
94        }
95    }
96}
97
98static KEYBOARD_STATE: OnceLock<RwLock<KeyboardState>> = OnceLock::new();
99
100fn get_state() -> &'static RwLock<KeyboardState> {
101    KEYBOARD_STATE.get_or_init(|| RwLock::new(KeyboardState::empty()))
102}
103
104/// Get whether a key is currently being held down
105pub fn is_down(key: impl Into<Key>) -> bool {
106    get_state().read().held.contains(&key.into())
107}
108
109/// Get whether a key was just pressed
110pub fn is_pressed(key: impl Into<Key>) -> bool {
111    get_state().read().pressed.contains(&key.into())
112}
113
114/// Get whether a key was just released
115pub fn is_released(key: impl Into<Key>) -> bool {
116    get_state().read().released.contains(&key.into())
117}
118
119/// Process a key event, used internally to handle key events
120pub fn process_key_event(key: Key, pressed: bool) {
121    let mut state = get_state().write();
122    if pressed {
123        state.held.insert(key);
124        state.pressed.insert(key);
125    } else {
126        state.held.remove(&key);
127        state.released.insert(key);
128    }
129}
130
131/// Reset the keyboard's state for this frame
132pub fn reset() {
133    let mut state = get_state().write();
134    state.pressed.clear();
135    state.released.clear();
136}
137
138/// Useful structs to import
139pub mod prelude {
140    pub use super::Key;
141}