use alloc::vec::Vec;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct KeyModifiers(u8);
impl KeyModifiers {
pub const NONE: Self = Self(0);
pub const SHIFT: Self = Self(1 << 0);
pub const CONTROL: Self = Self(1 << 1);
pub const ALT: Self = Self(1 << 2);
pub const SUPER: Self = Self(1 << 3);
#[must_use]
pub const fn from_bits_truncate(bits: u8) -> Self {
Self(bits & 0b1111)
}
#[must_use]
#[allow(clippy::fn_params_excessive_bools)]
pub const fn from_parts(shift: bool, control: bool, alt: bool, super_: bool) -> Self {
Self((shift as u8) | (control as u8) << 1 | (alt as u8) << 2 | (super_ as u8) << 3)
}
#[must_use]
pub const fn bits(self) -> u8 {
self.0
}
#[must_use]
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl BitOr for KeyModifiers {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for KeyModifiers {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl BitAnd for KeyModifiers {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
impl BitAndAssign for KeyModifiers {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl Not for KeyModifiers {
type Output = Self;
fn not(self) -> Self {
Self(!self.0 & 0b1111)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ModifierKey {
Shift,
Control,
Alt,
Super,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum KeyCode {
Char(char),
F(u8),
Backspace,
Enter,
Left,
Right,
Up,
Down,
Home,
End,
PageUp,
PageDown,
Tab,
BackTab,
Delete,
Insert,
Escape,
Modifier(ModifierKey),
CapsLock,
ScrollLock,
NumLock,
PrintScreen,
Pause,
Menu,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum KeyEventKind {
#[default]
Press,
Repeat,
Release,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum KeyLocation {
#[default]
Standard,
Left,
Right,
Numpad,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct KeyEvent {
pub code: KeyCode,
pub modifiers: KeyModifiers,
pub kind: KeyEventKind,
pub location: KeyLocation,
}
impl KeyEvent {
#[must_use]
pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> Self {
Self {
code,
modifiers,
kind: KeyEventKind::Press,
location: KeyLocation::Standard,
}
}
#[must_use]
pub const fn with_kind(code: KeyCode, modifiers: KeyModifiers, kind: KeyEventKind) -> Self {
Self {
code,
modifiers,
kind,
location: KeyLocation::Standard,
}
}
#[must_use]
pub const fn with_location(
code: KeyCode,
modifiers: KeyModifiers,
kind: KeyEventKind,
location: KeyLocation,
) -> Self {
Self {
code,
modifiers,
kind,
location,
}
}
#[must_use]
pub const fn is_down(self) -> bool {
matches!(self.kind, KeyEventKind::Press | KeyEventKind::Repeat)
}
}
#[derive(Debug, Clone, Default)]
pub struct KeyState {
held: Vec<(KeyCode, KeyLocation)>,
}
impl KeyState {
#[must_use]
pub const fn new() -> Self {
Self { held: Vec::new() }
}
pub fn apply(&mut self, event: KeyEvent) {
let entry = (event.code, event.location);
match event.kind {
KeyEventKind::Press | KeyEventKind::Repeat => {
if !self.held.contains(&entry) {
self.held.push(entry);
}
}
KeyEventKind::Release => {
self.held.retain(|&e| e != entry);
}
}
}
pub fn apply_event(&mut self, event: &super::Event) {
match event {
super::Event::Key(key) => self.apply(*key),
super::Event::FocusLost => self.clear(),
_ => {}
}
}
#[must_use]
pub fn is_held(&self, code: KeyCode, location: KeyLocation) -> bool {
self.held.contains(&(code, location))
}
pub fn held(&self) -> impl Iterator<Item = (KeyCode, KeyLocation)> + '_ {
self.held.iter().copied()
}
pub fn clear(&mut self) {
self.held.clear();
}
}
#[cfg(test)]
mod tests {
use super::super::Event;
use super::*;
use alloc::vec;
#[test]
fn test_key_modifiers() {
let mods = KeyModifiers::SHIFT | KeyModifiers::CONTROL;
assert!(mods.contains(KeyModifiers::SHIFT));
assert!(mods.contains(KeyModifiers::CONTROL));
assert!(!mods.contains(KeyModifiers::ALT));
assert!(!mods.is_empty());
let inverse = !mods;
assert!(inverse.contains(KeyModifiers::ALT));
assert!(inverse.contains(KeyModifiers::SUPER));
assert!(!inverse.contains(KeyModifiers::SHIFT));
assert!(!inverse.contains(KeyModifiers::CONTROL));
}
#[derive(Default)]
struct TestHasher(u64);
impl core::hash::Hasher for TestHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for byte in bytes {
self.0 = self.0.wrapping_mul(31).wrapping_add(u64::from(*byte));
}
}
}
#[test]
fn test_key_modifiers_not_masks_unused_bits() {
use core::hash::Hash;
let all =
KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SUPER;
let inverse = !KeyModifiers::NONE;
assert_eq!(inverse, all, "NOT NONE should equal ALL");
let mut inverse_hasher = TestHasher::default();
inverse.hash(&mut inverse_hasher);
let mut all_hasher = TestHasher::default();
all.hash(&mut all_hasher);
assert_eq!(
core::hash::Hasher::finish(&inverse_hasher),
core::hash::Hasher::finish(&all_hasher),
"NOT NONE should hash the same as ALL"
);
}
#[test]
fn test_key_modifiers_super() {
let mods = KeyModifiers::SUPER;
assert!(mods.contains(KeyModifiers::SUPER));
assert!(!mods.contains(KeyModifiers::SHIFT));
assert!(!mods.contains(KeyModifiers::CONTROL));
assert!(!mods.contains(KeyModifiers::ALT));
let all =
KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SUPER;
assert!(all.contains(KeyModifiers::SUPER));
assert!(all.contains(KeyModifiers::SHIFT));
assert!(all.contains(KeyModifiers::CONTROL));
assert!(all.contains(KeyModifiers::ALT));
}
#[test]
fn test_key_modifiers_from_bits_truncate() {
assert_eq!(KeyModifiers::from_bits_truncate(0), KeyModifiers::NONE);
assert_eq!(KeyModifiers::from_bits_truncate(1), KeyModifiers::SHIFT);
assert_eq!(KeyModifiers::from_bits_truncate(2), KeyModifiers::CONTROL);
assert_eq!(KeyModifiers::from_bits_truncate(4), KeyModifiers::ALT);
assert_eq!(KeyModifiers::from_bits_truncate(8), KeyModifiers::SUPER);
assert_eq!(
KeyModifiers::from_bits_truncate(0b1111),
KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SUPER
);
assert_eq!(
KeyModifiers::from_bits_truncate(0xFF),
KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SUPER
);
}
#[test]
fn test_key_modifiers_bits_round_trip() {
for bits in 0..=u8::MAX {
assert_eq!(KeyModifiers::from_bits_truncate(bits).bits(), bits & 0b1111);
}
}
#[test]
fn test_key_modifiers_from_parts() {
assert_eq!(
KeyModifiers::from_parts(false, false, false, false),
KeyModifiers::NONE
);
assert_eq!(
KeyModifiers::from_parts(true, false, false, false),
KeyModifiers::SHIFT
);
assert_eq!(
KeyModifiers::from_parts(false, true, false, false),
KeyModifiers::CONTROL
);
assert_eq!(
KeyModifiers::from_parts(false, false, true, false),
KeyModifiers::ALT
);
assert_eq!(
KeyModifiers::from_parts(false, false, false, true),
KeyModifiers::SUPER
);
assert_eq!(
KeyModifiers::from_parts(true, true, true, true),
KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SUPER
);
}
#[test]
fn test_event_construction() {
let key_event = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::SHIFT);
let event = Event::Key(key_event);
if let Event::Key(ke) = event {
assert_eq!(ke.code, KeyCode::Char('a'));
assert!(ke.modifiers.contains(KeyModifiers::SHIFT));
assert_eq!(ke.kind, KeyEventKind::Press);
} else {
panic!("Expected Event::Key");
}
}
#[test]
fn test_key_event_kind_helpers() {
let press = KeyEvent::new(KeyCode::Char('x'), KeyModifiers::NONE);
assert_eq!(press.kind, KeyEventKind::Press);
assert!(press.is_down());
let repeat =
KeyEvent::with_kind(KeyCode::Char('x'), KeyModifiers::NONE, KeyEventKind::Repeat);
assert!(repeat.is_down());
let release = KeyEvent::with_kind(
KeyCode::Char('x'),
KeyModifiers::NONE,
KeyEventKind::Release,
);
assert!(!release.is_down());
}
#[test]
fn test_key_state_tracks_held_keys() {
let mut state = KeyState::new();
assert!(!state.is_held(KeyCode::Left, KeyLocation::Standard));
state.apply(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
assert!(state.is_held(KeyCode::Left, KeyLocation::Standard));
state.apply(KeyEvent::with_kind(
KeyCode::Left,
KeyModifiers::NONE,
KeyEventKind::Repeat,
));
assert!(state.is_held(KeyCode::Left, KeyLocation::Standard));
state.apply(KeyEvent::with_kind(
KeyCode::Left,
KeyModifiers::NONE,
KeyEventKind::Release,
));
assert!(!state.is_held(KeyCode::Left, KeyLocation::Standard));
}
#[test]
fn test_key_state_distinguishes_numpad_from_standard() {
let mut state = KeyState::new();
state.apply(KeyEvent::with_location(
KeyCode::Char('8'),
KeyModifiers::NONE,
KeyEventKind::Press,
KeyLocation::Numpad,
));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Numpad));
assert!(!state.is_held(KeyCode::Char('8'), KeyLocation::Standard));
state.apply(KeyEvent::new(KeyCode::Char('8'), KeyModifiers::NONE));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Standard));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Numpad));
state.apply(KeyEvent::with_kind(
KeyCode::Char('8'),
KeyModifiers::NONE,
KeyEventKind::Release,
));
assert!(!state.is_held(KeyCode::Char('8'), KeyLocation::Standard));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Numpad));
}
#[test]
fn test_key_state_apply_event_ignores_non_key() {
let mut state = KeyState::new();
state.apply_event(&Event::Resize(1, 1));
assert!(state.held().next().is_none());
state.apply_event(&Event::Key(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE)));
assert!(state.is_held(KeyCode::Up, KeyLocation::Standard));
}
#[test]
fn test_key_state_apply_event_clears_on_focus_lost() {
let mut state = KeyState::new();
state.apply_event(&Event::Key(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE)));
state.apply_event(&Event::Key(KeyEvent::new(
KeyCode::Left,
KeyModifiers::NONE,
)));
assert!(state.is_held(KeyCode::Up, KeyLocation::Standard));
assert!(state.is_held(KeyCode::Left, KeyLocation::Standard));
state.apply_event(&Event::FocusLost);
assert!(!state.is_held(KeyCode::Up, KeyLocation::Standard));
assert!(!state.is_held(KeyCode::Left, KeyLocation::Standard));
assert!(state.held().next().is_none());
}
#[test]
fn test_key_state_clear() {
let mut state = KeyState::new();
state.apply(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
state.apply(KeyEvent::new(KeyCode::Right, KeyModifiers::NONE));
assert_eq!(state.held().count(), 2);
state.clear();
assert!(state.held().next().is_none());
assert!(!state.is_held(KeyCode::Left, KeyLocation::Standard));
assert!(!state.is_held(KeyCode::Right, KeyLocation::Standard));
state.clear();
assert!(state.held().next().is_none());
}
#[test]
fn test_key_state_held_is_in_first_pressed_order() {
let mut state = KeyState::new();
state.apply(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
state.apply(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
state.apply(KeyEvent::new(KeyCode::Right, KeyModifiers::NONE));
assert_eq!(
state.held().collect::<Vec<_>>(),
vec![
(KeyCode::Left, KeyLocation::Standard),
(KeyCode::Up, KeyLocation::Standard),
(KeyCode::Right, KeyLocation::Standard),
]
);
state.apply(KeyEvent::with_kind(
KeyCode::Left,
KeyModifiers::NONE,
KeyEventKind::Release,
));
state.apply(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
assert_eq!(
state.held().collect::<Vec<_>>(),
vec![
(KeyCode::Up, KeyLocation::Standard),
(KeyCode::Right, KeyLocation::Standard),
(KeyCode::Left, KeyLocation::Standard),
]
);
}
#[test]
fn test_key_state_release_of_unpressed_key_is_a_no_op() {
let mut state = KeyState::new();
state.apply(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
state.apply(KeyEvent::with_kind(
KeyCode::Down,
KeyModifiers::NONE,
KeyEventKind::Release,
));
assert!(!state.is_held(KeyCode::Down, KeyLocation::Standard));
assert!(state.is_held(KeyCode::Up, KeyLocation::Standard));
assert_eq!(state.held().count(), 1);
}
#[test]
fn test_key_state_double_press_without_release_does_not_duplicate() {
let mut state = KeyState::new();
state.apply(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
state.apply(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
assert_eq!(state.held().count(), 1);
state.apply(KeyEvent::with_kind(
KeyCode::Up,
KeyModifiers::NONE,
KeyEventKind::Release,
));
assert!(!state.is_held(KeyCode::Up, KeyLocation::Standard));
assert!(state.held().next().is_none());
}
}