use crate::error::Result;
use crate::mapper::ScancodeMapper;
use std::collections::HashSet;
use std::time::Instant;
use tracing::debug;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct KeyModifiers {
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
pub meta: bool,
pub caps_lock: bool,
pub num_lock: bool,
pub scroll_lock: bool,
}
#[derive(Debug, Clone)]
pub enum KeyboardEvent {
KeyDown {
keycode: u32,
scancode: u16,
modifiers: KeyModifiers,
timestamp: Instant,
},
KeyUp {
keycode: u32,
scancode: u16,
modifiers: KeyModifiers,
timestamp: Instant,
},
KeyRepeat {
keycode: u32,
scancode: u16,
modifiers: KeyModifiers,
timestamp: Instant,
},
}
pub struct KeyboardHandler {
mapper: ScancodeMapper,
pressed_keys: HashSet<u32>,
modifiers: KeyModifiers,
last_key_times: std::collections::HashMap<u32, Instant>,
repeat_delay_ms: u64,
repeat_rate_ms: u64,
}
impl KeyboardHandler {
pub fn new() -> Self {
Self {
mapper: ScancodeMapper::new(),
pressed_keys: HashSet::new(),
modifiers: KeyModifiers::default(),
last_key_times: std::collections::HashMap::new(),
repeat_delay_ms: 500,
repeat_rate_ms: 33,
}
}
pub fn handle_key_down(&mut self, scancode: u16, extended: bool, e1_prefix: bool) -> Result<KeyboardEvent> {
let keycode = self.mapper.translate_scancode(scancode as u32, extended, e1_prefix)?;
let timestamp = Instant::now();
let is_repeat = self.pressed_keys.contains(&keycode);
if is_repeat {
if let Some(last_time) = self.last_key_times.get(&keycode) {
let elapsed = timestamp.duration_since(*last_time).as_millis() as u64;
if elapsed < self.repeat_rate_ms {
debug!("Key repeat within rate limit: keycode {}", keycode);
return Ok(KeyboardEvent::KeyRepeat {
keycode,
scancode,
modifiers: self.modifiers,
timestamp,
});
}
}
}
self.pressed_keys.insert(keycode);
self.last_key_times.insert(keycode, timestamp);
self.update_modifiers(keycode, true);
debug!(
"Key down: scancode=0x{:04X}, keycode={}, modifiers={:?}",
scancode, keycode, self.modifiers
);
if is_repeat {
Ok(KeyboardEvent::KeyRepeat {
keycode,
scancode,
modifiers: self.modifiers,
timestamp,
})
} else {
Ok(KeyboardEvent::KeyDown {
keycode,
scancode,
modifiers: self.modifiers,
timestamp,
})
}
}
pub fn handle_key_up(&mut self, scancode: u16, extended: bool, e1_prefix: bool) -> Result<KeyboardEvent> {
let keycode = self.mapper.translate_scancode(scancode as u32, extended, e1_prefix)?;
let timestamp = Instant::now();
self.pressed_keys.remove(&keycode);
self.last_key_times.remove(&keycode);
self.update_modifiers(keycode, false);
debug!(
"Key up: scancode=0x{:04X}, keycode={}, modifiers={:?}",
scancode, keycode, self.modifiers
);
Ok(KeyboardEvent::KeyUp {
keycode,
scancode,
modifiers: self.modifiers,
timestamp,
})
}
fn update_modifiers(&mut self, keycode: u32, pressed: bool) {
#[allow(clippy::wildcard_imports)]
use crate::mapper::keycodes::*;
match keycode {
KEY_LEFTSHIFT | KEY_RIGHTSHIFT => {
if pressed {
self.modifiers.shift = true;
} else {
self.modifiers.shift = self.is_key_pressed(KEY_LEFTSHIFT) || self.is_key_pressed(KEY_RIGHTSHIFT);
}
}
KEY_LEFTCTRL | KEY_RIGHTCTRL => {
if pressed {
self.modifiers.ctrl = true;
} else {
self.modifiers.ctrl = self.is_key_pressed(KEY_LEFTCTRL) || self.is_key_pressed(KEY_RIGHTCTRL);
}
}
KEY_LEFTALT | KEY_RIGHTALT => {
if pressed {
self.modifiers.alt = true;
} else {
self.modifiers.alt = self.is_key_pressed(KEY_LEFTALT) || self.is_key_pressed(KEY_RIGHTALT);
}
}
KEY_LEFTMETA | KEY_RIGHTMETA => {
if pressed {
self.modifiers.meta = true;
} else {
self.modifiers.meta = self.is_key_pressed(KEY_LEFTMETA) || self.is_key_pressed(KEY_RIGHTMETA);
}
}
KEY_CAPSLOCK if pressed => {
self.modifiers.caps_lock = !self.modifiers.caps_lock;
}
KEY_NUMLOCK if pressed => {
self.modifiers.num_lock = !self.modifiers.num_lock;
}
KEY_SCROLLLOCK if pressed => {
self.modifiers.scroll_lock = !self.modifiers.scroll_lock;
}
_ => {}
}
}
pub fn is_key_pressed(&self, keycode: u32) -> bool {
self.pressed_keys.contains(&keycode)
}
pub fn modifiers(&self) -> KeyModifiers {
self.modifiers
}
pub fn set_layout(&mut self, layout: &str) {
self.mapper.set_layout(layout);
debug!("Keyboard layout changed to: {}", layout);
}
pub fn layout(&self) -> &str {
self.mapper.layout()
}
pub fn set_repeat_delay(&mut self, delay_ms: u64) {
self.repeat_delay_ms = delay_ms;
}
pub fn set_repeat_rate(&mut self, rate_ms: u64) {
self.repeat_rate_ms = rate_ms;
}
pub fn reset(&mut self) {
self.pressed_keys.clear();
self.last_key_times.clear();
self.modifiers = KeyModifiers::default();
debug!("Keyboard state reset");
}
pub fn pressed_key_count(&self) -> usize {
self.pressed_keys.len()
}
pub fn get_pressed_keys(&self) -> Vec<u32> {
self.pressed_keys.iter().copied().collect()
}
}
impl Default for KeyboardHandler {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keyboard_handler_creation() {
let handler = KeyboardHandler::new();
assert_eq!(handler.pressed_key_count(), 0);
assert!(!handler.modifiers().shift);
}
#[test]
fn test_key_press_release() {
let mut handler = KeyboardHandler::new();
let event = handler.handle_key_down(0x1E, false, false).unwrap();
match event {
KeyboardEvent::KeyDown { keycode, .. } => {
assert!(keycode > 0);
assert!(handler.is_key_pressed(keycode));
}
_ => panic!("Expected KeyDown event"),
}
assert_eq!(handler.pressed_key_count(), 1);
let event = handler.handle_key_up(0x1E, false, false).unwrap();
match event {
KeyboardEvent::KeyUp { keycode, .. } => {
assert!(!handler.is_key_pressed(keycode));
}
_ => panic!("Expected KeyUp event"),
}
assert_eq!(handler.pressed_key_count(), 0);
}
#[test]
fn test_modifier_tracking() {
let mut handler = KeyboardHandler::new();
handler.handle_key_down(0x2A, false, false).unwrap();
assert!(handler.modifiers().shift);
handler.handle_key_down(0x1D, false, false).unwrap();
assert!(handler.modifiers().ctrl);
handler.handle_key_up(0x2A, false, false).unwrap();
assert!(!handler.modifiers().shift);
assert!(handler.modifiers().ctrl);
handler.handle_key_up(0x1D, false, false).unwrap();
assert!(!handler.modifiers().ctrl);
}
#[test]
fn test_caps_lock_toggle() {
let mut handler = KeyboardHandler::new();
assert!(!handler.modifiers().caps_lock);
handler.handle_key_down(0x3A, false, false).unwrap();
assert!(handler.modifiers().caps_lock);
handler.handle_key_up(0x3A, false, false).unwrap();
assert!(handler.modifiers().caps_lock);
handler.handle_key_down(0x3A, false, false).unwrap();
assert!(!handler.modifiers().caps_lock);
}
#[test]
fn test_multiple_modifiers() {
let mut handler = KeyboardHandler::new();
handler.handle_key_down(0x2A, false, false).unwrap(); handler.handle_key_down(0x1D, false, false).unwrap(); handler.handle_key_down(0x38, false, false).unwrap();
let mods = handler.modifiers();
assert!(mods.shift);
assert!(mods.ctrl);
assert!(mods.alt);
}
#[test]
fn test_both_shifts() {
let mut handler = KeyboardHandler::new();
handler.handle_key_down(0x2A, false, false).unwrap();
assert!(handler.modifiers().shift);
handler.handle_key_down(0x36, false, false).unwrap();
assert!(handler.modifiers().shift);
handler.handle_key_up(0x2A, false, false).unwrap();
assert!(handler.modifiers().shift);
handler.handle_key_up(0x36, false, false).unwrap();
assert!(!handler.modifiers().shift);
}
#[test]
fn test_extended_key() {
let mut handler = KeyboardHandler::new();
let event = handler.handle_key_down(0x1D, true, false).unwrap();
match event {
KeyboardEvent::KeyDown { keycode, .. } => {
assert!(keycode > 0);
}
_ => panic!("Expected KeyDown event"),
}
assert!(handler.modifiers().ctrl);
}
#[test]
fn test_layout_change() {
let mut handler = KeyboardHandler::new();
assert_eq!(handler.layout(), "us");
handler.set_layout("de");
assert_eq!(handler.layout(), "de");
}
#[test]
fn test_reset() {
let mut handler = KeyboardHandler::new();
handler.handle_key_down(0x1E, false, false).unwrap(); handler.handle_key_down(0x2A, false, false).unwrap(); handler.handle_key_down(0x1D, false, false).unwrap();
assert!(handler.pressed_key_count() > 0);
assert!(handler.modifiers().shift);
handler.reset();
assert_eq!(handler.pressed_key_count(), 0);
assert!(!handler.modifiers().shift);
assert!(!handler.modifiers().ctrl);
}
#[test]
fn test_get_pressed_keys() {
let mut handler = KeyboardHandler::new();
handler.handle_key_down(0x1E, false, false).unwrap(); handler.handle_key_down(0x1F, false, false).unwrap();
let pressed = handler.get_pressed_keys();
assert_eq!(pressed.len(), 2);
}
#[test]
fn test_repeat_rate() {
let mut handler = KeyboardHandler::new();
handler.set_repeat_delay(100);
handler.set_repeat_rate(50);
assert_eq!(handler.repeat_delay_ms, 100);
assert_eq!(handler.repeat_rate_ms, 50);
}
#[test]
fn test_unknown_scancode() {
let mut handler = KeyboardHandler::new();
let result = handler.handle_key_down(0xFF, false, false);
assert!(result.is_err());
}
#[test]
fn test_function_keys() {
let mut handler = KeyboardHandler::new();
let event = handler.handle_key_down(0x3B, false, false).unwrap();
match event {
KeyboardEvent::KeyDown { keycode, .. } => {
assert!(keycode > 0);
}
_ => panic!("Expected KeyDown event"),
}
let event = handler.handle_key_down(0x58, false, false).unwrap();
match event {
KeyboardEvent::KeyDown { keycode, .. } => {
assert!(keycode > 0);
}
_ => panic!("Expected KeyDown event"),
}
}
}