use std::fmt;
use tracing::trace;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
pub enum Arrow {
Up,
Down,
Left,
Right,
}
impl Arrow {
pub fn flip(&self) -> Self {
match self {
Self::Up => Self::Down,
Self::Down => Self::Up,
Self::Left => Self::Right,
Self::Right => Self::Left,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
pub enum Input {
Char(char),
Ctrl(char),
Alt(char),
CtrlAlt(char),
Tab,
BackTab,
AltReturn,
Return,
Backspace,
Arrow(Arrow),
Del,
Home,
End,
PageUp,
PageDown,
Esc,
Mouse(MouseEvent),
}
impl fmt::Display for Input {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Char(c) if *c == ' ' => write!(f, "<space>"),
Self::Char(c) => write!(f, "{c}"),
Self::Ctrl(c) if *c == ' ' => write!(f, "C-<space>"),
Self::Ctrl(c) => write!(f, "C-{c}"),
Self::Alt(c) if *c == ' ' => write!(f, "A-<space>"),
Self::Alt(c) => write!(f, "A-{c}"),
Self::CtrlAlt(c) if *c == ' ' => write!(f, "C-A-<space>"),
Self::CtrlAlt(c) => write!(f, "C-A-{c}"),
Self::Tab => write!(f, "<tab>"),
Self::BackTab => write!(f, "S-<tab>"),
Self::AltReturn => write!(f, "A-<return>"),
Self::Return => write!(f, "<return>"),
Self::Backspace => write!(f, "<backspace>"),
Self::Arrow(a) => write!(f, "<{a:?}>"),
Self::Del => write!(f, "<delete>"),
Self::Home => write!(f, "<home>"),
Self::End => write!(f, "<end>"),
Self::PageUp => write!(f, "<page-up>"),
Self::PageDown => write!(f, "<page-down>"),
Self::Esc => write!(f, "<escape>"),
Self::Mouse(m) => write!(f, "<{m:?}>"), }
}
}
impl Input {
pub fn from_char(c: char) -> Self {
match c {
'\x1b' => Input::Esc,
'\n' | '\r' => Input::Return,
'\x7f' => Input::Backspace,
'\t' => Input::Tab,
c @ '\x01'..='\x1A' => Input::Ctrl((c as u8 - 0x1 + b'a') as char),
c @ '\x1C'..='\x1F' => Input::Ctrl((c as u8 - 0x1C + b'4') as char),
_ => Input::Char(c),
}
}
pub fn try_from_seq2(c1: char, c2: char) -> Option<Self> {
match (c1, c2) {
('O', 'H') => Some(Input::Home),
('O', 'F') => Some(Input::End),
('[', 'A') => Some(Input::Arrow(Arrow::Up)),
('[', 'B') => Some(Input::Arrow(Arrow::Down)),
('[', 'C') => Some(Input::Arrow(Arrow::Right)),
('[', 'D') => Some(Input::Arrow(Arrow::Left)),
('[', 'H') => Some(Input::Home),
('[', 'Z') => Some(Input::BackTab),
('\x1b', c) if c.is_ascii() => match Self::from_char(c) {
Input::Char(c) => Some(Input::Alt(c)),
Input::Ctrl(c) => Some(Input::CtrlAlt(c)),
Input::Return => Some(Input::AltReturn),
_ => None,
},
_ => None,
}
}
pub fn try_from_bracket_tilde(c: char) -> Option<Self> {
match c {
'1' | '7' => Some(Input::Home),
'4' | '8' => Some(Input::End),
'3' => Some(Input::Del),
'5' => Some(Input::PageUp),
'6' => Some(Input::PageDown),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
pub enum MouseButton {
Left,
Middle,
Right,
WheelUp,
WheelDown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
pub enum MouseMod {
NoMod,
Alt,
Ctrl,
AltCtrl,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
pub enum MouseEventKind {
Press,
Hold,
Release,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
pub struct MouseEvent {
pub k: MouseEventKind,
pub m: MouseMod,
pub b: MouseButton,
pub x: usize,
pub y: usize,
}
impl MouseEvent {
pub(crate) fn try_from_raw(b: usize, x: usize, y: usize, c: char) -> Option<Self> {
use MouseButton::*;
use MouseEventKind::*;
use MouseMod::*;
let (k, m, b) = match (b, c) {
(0, 'M') => (Press, NoMod, Left),
(1, 'M') => (Press, NoMod, Middle),
(2, 'M') => (Press, NoMod, Right),
(64, 'M') => (Press, NoMod, WheelUp),
(65, 'M') => (Press, NoMod, WheelDown),
(0, 'm') | (3, _) => (Release, NoMod, Left),
(1, 'm') => (Release, NoMod, Middle),
(2, 'm') => (Release, NoMod, Right),
(64, 'm') => (Release, NoMod, WheelUp),
(65, 'm') => (Release, NoMod, WheelDown),
(32, _) => (Hold, NoMod, Left),
(33, _) => (Hold, NoMod, Middle),
(34, _) => (Hold, NoMod, Right),
(8, 'M') => (Press, Alt, Left),
(9, 'M') => (Press, Alt, Middle),
(10, 'M') => (Press, Alt, Right),
(72, 'M') => (Press, Alt, WheelUp),
(73, 'M') => (Press, Alt, WheelDown),
(8, 'm') => (Release, Alt, Left),
(9, 'm') => (Release, Alt, Middle),
(10, 'm') => (Release, Alt, Right),
(72, 'm') => (Release, Alt, WheelUp),
(73, 'm') => (Release, Alt, WheelDown),
(40, _) => (Hold, Alt, Left),
(41, _) => (Hold, Alt, Middle),
(42, _) => (Hold, Alt, Right),
(16, 'M') => (Press, Ctrl, Left),
(17, 'M') => (Press, Ctrl, Middle),
(18, 'M') => (Press, Ctrl, Right),
(80, 'M') => (Press, Ctrl, WheelUp),
(81, 'M') => (Press, Ctrl, WheelDown),
(16, 'm') => (Release, Ctrl, Left),
(17, 'm') => (Release, Ctrl, Middle),
(18, 'm') => (Release, Ctrl, Right),
(80, 'm') => (Release, Ctrl, WheelUp),
(81, 'm') => (Release, Ctrl, WheelDown),
(48, _) => (Hold, Ctrl, Left),
(49, _) => (Hold, Ctrl, Middle),
(50, _) => (Hold, Ctrl, Right),
(24, 'M') => (Press, AltCtrl, Left),
(25, 'M') => (Press, AltCtrl, Middle),
(26, 'M') => (Press, AltCtrl, Right),
(88, 'M') => (Press, AltCtrl, WheelUp),
(89, 'M') => (Press, AltCtrl, WheelDown),
(24, 'm') => (Release, AltCtrl, Left),
(25, 'm') => (Release, AltCtrl, Middle),
(26, 'm') => (Release, AltCtrl, Right),
(88, 'm') => (Release, AltCtrl, WheelUp),
(89, 'm') => (Release, AltCtrl, WheelDown),
(56, _) => (Hold, AltCtrl, Left),
(57, _) => (Hold, AltCtrl, Middle),
(58, _) => (Hold, AltCtrl, Right),
_ => {
trace!("unmapped mouse input: b=b m=m");
return None;
}
};
Some(MouseEvent { k, m, b, x, y })
}
}