use std::{collections::HashSet, sync::OnceLock};
use parking_lot::RwLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Key {
Char(char),
Up,
Down,
Left,
Right,
Space,
Enter,
Escape,
Backspace,
Delete,
Shift,
Control,
Alt,
Meta,
Unknown,
}
impl From<char> for Key {
fn from(value: char) -> Self {
if value == ' ' {
Self::Space
} else {
Self::Char(value.to_lowercase().next().unwrap_or(value))
}
}
}
impl From<&str> for Key {
fn from(value: &str) -> Self {
if value.len() == 1 {
Self::from(value.chars().next().unwrap())
} else {
match value.to_lowercase().as_str() {
"up" => Self::Up,
"down" => Self::Down,
"left" => Self::Left,
"right" => Self::Right,
"space" => Self::Space,
"enter" => Self::Enter,
"escape" => Self::Escape,
"backspace" => Self::Backspace,
"delete" => Self::Delete,
"shift" => Self::Shift,
"control" => Self::Control,
"alt" => Self::Alt,
"meta" => Self::Meta,
_ => Self::Unknown,
}
}
}
}
impl From<String> for Key {
fn from(value: String) -> Self {
Key::from(value.as_str())
}
}
#[derive(Debug)]
struct KeyboardState {
pressed: HashSet<Key>,
released: HashSet<Key>,
held: HashSet<Key>,
}
impl KeyboardState {
fn empty() -> Self {
Self {
pressed: HashSet::new(),
released: HashSet::new(),
held: HashSet::new(),
}
}
}
static KEYBOARD_STATE: OnceLock<RwLock<KeyboardState>> = OnceLock::new();
fn get_state() -> &'static RwLock<KeyboardState> {
KEYBOARD_STATE.get_or_init(|| RwLock::new(KeyboardState::empty()))
}
pub fn is_down(key: impl Into<Key>) -> bool {
get_state().read().held.contains(&key.into())
}
pub fn is_pressed(key: impl Into<Key>) -> bool {
get_state().read().pressed.contains(&key.into())
}
pub fn is_released(key: impl Into<Key>) -> bool {
get_state().read().released.contains(&key.into())
}
pub fn process_key_event(key: Key, pressed: bool) {
let mut state = get_state().write();
if pressed {
state.held.insert(key);
state.pressed.insert(key);
} else {
state.held.remove(&key);
state.released.insert(key);
}
}
pub fn reset() {
let mut state = get_state().write();
state.pressed.clear();
state.released.clear();
}
pub mod prelude {
pub use super::Key;
}