use super::command::CommandId;
use super::geometry::Point;
use crossterm::event::{KeyCode as CKC, KeyEvent, KeyModifiers};
use std::fmt;
use std::time::{Duration, Instant};
pub type KeyCode = u16;
pub const KB_ESC: KeyCode = 0x011B;
pub const KB_ENTER: KeyCode = 0x1C0D;
pub const KB_BACKSPACE: KeyCode = 0x0E08;
pub const KB_TAB: KeyCode = 0x0F09;
pub const KB_SHIFT_TAB: KeyCode = 0x0F00;
pub const KB_F1: KeyCode = 0x3B00;
pub const KB_F2: KeyCode = 0x3C00;
pub const KB_F3: KeyCode = 0x3D00;
pub const KB_F4: KeyCode = 0x3E00;
pub const KB_F5: KeyCode = 0x3F00;
pub const KB_F6: KeyCode = 0x4000;
pub const KB_F7: KeyCode = 0x4100;
pub const KB_F8: KeyCode = 0x4200;
pub const KB_F9: KeyCode = 0x4300;
pub const KB_F10: KeyCode = 0x4400;
pub const KB_F11: KeyCode = 0x8500;
pub const KB_F12: KeyCode = 0x8600;
pub const KB_SHIFT_F12: KeyCode = 0x8601;
pub const KB_UP: KeyCode = 0x4800;
pub const KB_DOWN: KeyCode = 0x5000;
pub const KB_LEFT: KeyCode = 0x4B00;
pub const KB_RIGHT: KeyCode = 0x4D00;
pub const KB_HOME: KeyCode = 0x4700;
pub const KB_END: KeyCode = 0x4F00;
pub const KB_PGUP: KeyCode = 0x4900;
pub const KB_PGDN: KeyCode = 0x5100;
pub const KB_INS: KeyCode = 0x5200;
pub const KB_DEL: KeyCode = 0x5300;
pub const KB_ALT_A: KeyCode = 0x1E00;
pub const KB_ALT_B: KeyCode = 0x3000;
pub const KB_ALT_C: KeyCode = 0x2E00;
pub const KB_ALT_D: KeyCode = 0x2000;
pub const KB_ALT_E: KeyCode = 0x1200;
pub const KB_ALT_F: KeyCode = 0x2100;
pub const KB_ALT_G: KeyCode = 0x2200;
pub const KB_ALT_H: KeyCode = 0x2300;
pub const KB_ALT_I: KeyCode = 0x1700;
pub const KB_ALT_J: KeyCode = 0x2400;
pub const KB_ALT_K: KeyCode = 0x2500;
pub const KB_ALT_L: KeyCode = 0x2600;
pub const KB_ALT_M: KeyCode = 0x3200;
pub const KB_ALT_N: KeyCode = 0x3100;
pub const KB_ALT_O: KeyCode = 0x1800;
pub const KB_ALT_P: KeyCode = 0x1900;
pub const KB_ALT_Q: KeyCode = 0x1000;
pub const KB_ALT_R: KeyCode = 0x1300;
pub const KB_ALT_S: KeyCode = 0x1F00;
pub const KB_ALT_T: KeyCode = 0x1400;
pub const KB_ALT_U: KeyCode = 0x1600;
pub const KB_ALT_V: KeyCode = 0x2F00;
pub const KB_ALT_W: KeyCode = 0x1100;
pub const KB_ALT_X: KeyCode = 0x2D00;
pub const KB_ALT_Y: KeyCode = 0x1500;
pub const KB_ALT_Z: KeyCode = 0x2C00;
pub const KB_ALT_F1: KeyCode = 0x6800; pub const KB_ALT_F3: KeyCode = 0x6A00;
pub const KB_ESC_F: KeyCode = 0x2101; pub const KB_ESC_H: KeyCode = 0x2301; pub const KB_ESC_X: KeyCode = 0x2D01; pub const KB_ESC_A: KeyCode = 0x1E01; pub const KB_ESC_O: KeyCode = 0x1801; pub const KB_ESC_E: KeyCode = 0x1201; pub const KB_ESC_S: KeyCode = 0x1F01; pub const KB_ESC_V: KeyCode = 0x2F01;
pub const KB_CTRL_A: KeyCode = 0x0001; pub const KB_CTRL_B: KeyCode = 0x0002; pub const KB_CTRL_C: KeyCode = 0x0003; pub const KB_CTRL_D: KeyCode = 0x0004; pub const KB_CTRL_E: KeyCode = 0x0005; pub const KB_CTRL_F: KeyCode = 0x0006; pub const KB_CTRL_G: KeyCode = 0x0007; pub const KB_CTRL_H: KeyCode = 0x0008; pub const KB_CTRL_I: KeyCode = 0x0009; pub const KB_CTRL_J: KeyCode = 0x000a; pub const KB_CTRL_K: KeyCode = 0x000b; pub const KB_CTRL_L: KeyCode = 0x000c; pub const KB_CTRL_M: KeyCode = 0x000d; pub const KB_CTRL_N: KeyCode = 0x000e; pub const KB_CTRL_O: KeyCode = 0x000f; pub const KB_CTRL_P: KeyCode = 0x0010; pub const KB_CTRL_Q: KeyCode = 0x0011; pub const KB_CTRL_R: KeyCode = 0x0012; pub const KB_CTRL_S: KeyCode = 0x0013; pub const KB_CTRL_T: KeyCode = 0x0014; pub const KB_CTRL_U: KeyCode = 0x0015; pub const KB_CTRL_V: KeyCode = 0x0016; pub const KB_CTRL_W: KeyCode = 0x0017; pub const KB_CTRL_X: KeyCode = 0x0018; pub const KB_CTRL_Y: KeyCode = 0x0019; pub const KB_CTRL_Z: KeyCode = 0x001a;
pub const KB_ESC_ESC: KeyCode = 0x011C;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventType {
Nothing,
Keyboard,
MouseDown,
MouseUp,
MouseMove,
MouseAuto,
MouseWheelUp, MouseWheelDown, Command,
Broadcast,
}
pub const EV_NOTHING: u16 = 0x0000;
pub const EV_MOUSE_DOWN: u16 = 0x0001;
pub const EV_MOUSE_UP: u16 = 0x0002;
pub const EV_MOUSE_MOVE: u16 = 0x0004;
pub const EV_MOUSE_AUTO: u16 = 0x0008;
pub const EV_MOUSE_WHEEL_UP: u16 = 0x0010;
pub const EV_MOUSE_WHEEL_DOWN: u16 = 0x0020;
pub const EV_MOUSE: u16 = 0x003F; pub const EV_KEYBOARD: u16 = 0x0040;
pub const EV_COMMAND: u16 = 0x0100;
pub const EV_BROADCAST: u16 = 0x0200;
pub const EV_MESSAGE: u16 = 0xFF00;
pub const MB_LEFT_BUTTON: u8 = 0x01;
pub const MB_MIDDLE_BUTTON: u8 = 0x02;
pub const MB_RIGHT_BUTTON: u8 = 0x04;
#[derive(Debug, Clone, Copy)]
pub struct MouseEvent {
pub pos: Point,
pub buttons: u8, pub double_click: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct Event {
pub what: EventType,
pub key_code: KeyCode,
pub key_modifiers: KeyModifiers,
pub mouse: MouseEvent,
pub command: CommandId,
}
impl Event {
pub fn nothing() -> Self {
Self {
what: EventType::Nothing,
key_code: 0,
key_modifiers: KeyModifiers::empty(),
mouse: MouseEvent {
pos: Point::zero(),
buttons: 0,
double_click: false,
},
command: 0,
}
}
pub fn keyboard(key_code: KeyCode) -> Self {
Self {
what: EventType::Keyboard,
key_code,
key_modifiers: KeyModifiers::empty(),
..Self::nothing()
}
}
pub fn command(cmd: CommandId) -> Self {
Self {
what: EventType::Command,
command: cmd,
..Self::nothing()
}
}
pub fn broadcast(cmd: CommandId) -> Self {
Self {
what: EventType::Broadcast,
command: cmd,
..Self::nothing()
}
}
pub fn mouse(event_type: EventType, pos: Point, buttons: u8, double_click: bool) -> Self {
Self {
what: event_type,
mouse: MouseEvent { pos, buttons, double_click },
..Self::nothing()
}
}
pub fn from_crossterm_key(key_event: KeyEvent) -> Self {
let key_code = crossterm_to_keycode(key_event);
Self {
what: EventType::Keyboard,
key_code,
key_modifiers: key_event.modifiers,
..Self::nothing()
}
}
pub fn clear(&mut self) {
self.what = EventType::Nothing;
}
}
impl Default for Event {
fn default() -> Self {
Self::nothing()
}
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.what {
EventType::Nothing => write!(f, "Event::Nothing"),
EventType::Keyboard => {
write!(f, "Event::Keyboard(key_code={:#06x}", self.key_code)?;
if !self.key_modifiers.is_empty() {
write!(f, ", modifiers={:?}", self.key_modifiers)?;
}
write!(f, ")")
}
EventType::MouseDown => write!(
f,
"Event::MouseDown({}, buttons={:#04x}{})",
self.mouse.pos,
self.mouse.buttons,
if self.mouse.double_click { ", double_click" } else { "" }
),
EventType::MouseUp => write!(f, "Event::MouseUp({}, buttons={:#04x})", self.mouse.pos, self.mouse.buttons),
EventType::MouseMove => write!(f, "Event::MouseMove({}, buttons={:#04x})", self.mouse.pos, self.mouse.buttons),
EventType::MouseAuto => write!(f, "Event::MouseAuto({}, buttons={:#04x})", self.mouse.pos, self.mouse.buttons),
EventType::MouseWheelUp => write!(f, "Event::MouseWheelUp({})", self.mouse.pos),
EventType::MouseWheelDown => write!(f, "Event::MouseWheelDown({})", self.mouse.pos),
EventType::Command => write!(f, "Event::Command({:#06x})", self.command),
EventType::Broadcast => write!(f, "Event::Broadcast({:#06x})", self.command),
}
}
}
fn char_to_alt_code(c: char) -> Option<KeyCode> {
match c {
'a' => Some(KB_ALT_A),
'b' => Some(KB_ALT_B),
'c' => Some(KB_ALT_C),
'd' => Some(KB_ALT_D),
'e' => Some(KB_ALT_E),
'f' => Some(KB_ALT_F),
'g' => Some(KB_ALT_G),
'h' => Some(KB_ALT_H),
'i' => Some(KB_ALT_I),
'j' => Some(KB_ALT_J),
'k' => Some(KB_ALT_K),
'l' => Some(KB_ALT_L),
'm' => Some(KB_ALT_M),
'n' => Some(KB_ALT_N),
'o' => Some(KB_ALT_O),
'p' => Some(KB_ALT_P),
'q' => Some(KB_ALT_Q),
'r' => Some(KB_ALT_R),
's' => Some(KB_ALT_S),
't' => Some(KB_ALT_T),
'u' => Some(KB_ALT_U),
'v' => Some(KB_ALT_V),
'w' => Some(KB_ALT_W),
'x' => Some(KB_ALT_X),
'y' => Some(KB_ALT_Y),
'z' => Some(KB_ALT_Z),
_ => None,
}
}
pub struct EscSequenceTracker {
last_esc_time: Option<Instant>,
waiting_for_char: bool,
timeout_ms: u64,
}
impl EscSequenceTracker {
pub fn new() -> Self {
Self::with_timeout(500)
}
pub fn with_timeout(timeout_ms: u64) -> Self {
Self {
last_esc_time: None,
waiting_for_char: false,
timeout_ms,
}
}
pub fn set_timeout(&mut self, timeout_ms: u64) {
self.timeout_ms = timeout_ms;
}
pub fn process_key(&mut self, key: KeyEvent) -> KeyCode {
if matches!(key.code, CKC::Esc) {
let now = Instant::now();
if let Some(last_time) = self.last_esc_time {
if now.duration_since(last_time) < Duration::from_millis(self.timeout_ms) {
self.last_esc_time = None;
self.waiting_for_char = false;
return KB_ESC_ESC;
}
}
self.last_esc_time = Some(now);
self.waiting_for_char = true;
return 0; }
if self.waiting_for_char {
self.waiting_for_char = false;
let esc_time = self.last_esc_time;
self.last_esc_time = None;
if let Some(last_time) = esc_time {
if Instant::now().duration_since(last_time) <= Duration::from_millis(self.timeout_ms) {
if let CKC::Char(c) = key.code {
if let Some(alt_code) = char_to_alt_code(c.to_ascii_lowercase()) {
return alt_code;
}
}
}
}
return crossterm_to_keycode(key);
}
if let Some(last_time) = self.last_esc_time {
if Instant::now().duration_since(last_time) > Duration::from_millis(self.timeout_ms) {
self.last_esc_time = None;
self.waiting_for_char = false;
if matches!(key.code, CKC::Char(_)) {
return crossterm_to_keycode(key);
}
}
}
crossterm_to_keycode(key)
}
}
fn crossterm_to_keycode(key: KeyEvent) -> KeyCode {
match key.code {
CKC::Char(c) => {
if key.modifiers.contains(KeyModifiers::CONTROL) {
let c_lower = c.to_ascii_lowercase();
if c_lower >= 'a' && c_lower <= 'z' {
return (c_lower as u16) - ('a' as u16) + 1; }
}
if key.modifiers.contains(KeyModifiers::ALT) {
if let Some(alt_code) = char_to_alt_code(c.to_ascii_lowercase()) {
return alt_code;
}
}
c as u16
}
CKC::Enter => KB_ENTER,
CKC::Backspace => KB_BACKSPACE,
CKC::Tab => {
if key.modifiers.contains(KeyModifiers::SHIFT) {
KB_SHIFT_TAB
} else {
KB_TAB
}
}
CKC::BackTab => KB_SHIFT_TAB, CKC::Esc => KB_ESC,
CKC::Up => KB_UP,
CKC::Down => KB_DOWN,
CKC::Left => KB_LEFT,
CKC::Right => KB_RIGHT,
CKC::Home => KB_HOME,
CKC::End => KB_END,
CKC::PageUp => KB_PGUP,
CKC::PageDown => KB_PGDN,
CKC::Insert => KB_INS,
CKC::Delete => KB_DEL,
CKC::F(1) => {
if key.modifiers.contains(KeyModifiers::ALT) {
KB_ALT_F1
} else {
KB_F1
}
}
CKC::F(2) => KB_F2,
CKC::F(3) => {
if key.modifiers.contains(KeyModifiers::ALT) {
KB_ALT_F3
} else {
KB_F3
}
}
CKC::F(4) => KB_F4,
CKC::F(5) => KB_F5,
CKC::F(6) => KB_F6,
CKC::F(7) => KB_F7,
CKC::F(8) => KB_F8,
CKC::F(9) => KB_F9,
CKC::F(10) => KB_F10,
CKC::F(11) => KB_F11,
CKC::F(12) => {
if key.modifiers.contains(KeyModifiers::SHIFT) {
KB_SHIFT_F12
} else {
KB_F12
}
}
_ => 0,
}
}