use ::termion::event::{Event as T, Key as K, MouseButton as B, MouseEvent as M};
use super::{Button, Event, HeldButton, Key, KeyCode, Modifiers, Mouse, MouseKind, char_key};
pub fn from_termion(event: T, held: &mut HeldButton) -> Option<Event> {
match event {
T::Key(key) => self::key(key).map(Event::Key),
T::Mouse(mouse) => {
let (kind, column, row) = match mouse {
M::Press(button, column, row) => {
let kind = match button {
B::Left => MouseKind::Down(Button::Left),
B::Right => MouseKind::Down(Button::Right),
B::Middle => MouseKind::Down(Button::Middle),
B::WheelUp => MouseKind::ScrollUp,
B::WheelDown => MouseKind::ScrollDown,
B::WheelLeft | B::WheelRight => return None,
};
if let MouseKind::Down(button) = kind {
held.0 = Some(button);
}
(kind, column, row)
}
M::Release(column, row) => (
MouseKind::Up(held.0.take().unwrap_or(Button::Left)),
column,
row,
),
M::Hold(column, row) => {
(MouseKind::Drag(held.0.unwrap_or(Button::Left)), column, row)
}
};
Some(Event::Mouse(Mouse::new(
kind,
column.saturating_sub(1),
row.saturating_sub(1),
)))
}
T::Unsupported(_) => None,
}
}
fn key(key: K) -> Option<Key> {
const SHIFT: Modifiers = Modifiers {
shift: true,
ctrl: false,
alt: false,
};
const ALT: Modifiers = Modifiers {
shift: false,
ctrl: false,
alt: true,
};
const CTRL: Modifiers = Modifiers::CTRL;
let with = |code, modifiers| Some(Key::with(code, modifiers));
let plain = |code| Some(Key::new(code));
match key {
K::Backspace => plain(KeyCode::Backspace),
K::Left => plain(KeyCode::Left),
K::ShiftLeft => with(KeyCode::Left, SHIFT),
K::AltLeft => with(KeyCode::Left, ALT),
K::CtrlLeft => with(KeyCode::Left, CTRL),
K::Right => plain(KeyCode::Right),
K::ShiftRight => with(KeyCode::Right, SHIFT),
K::AltRight => with(KeyCode::Right, ALT),
K::CtrlRight => with(KeyCode::Right, CTRL),
K::Up => plain(KeyCode::Up),
K::ShiftUp => with(KeyCode::Up, SHIFT),
K::AltUp => with(KeyCode::Up, ALT),
K::CtrlUp => with(KeyCode::Up, CTRL),
K::Down => plain(KeyCode::Down),
K::ShiftDown => with(KeyCode::Down, SHIFT),
K::AltDown => with(KeyCode::Down, ALT),
K::CtrlDown => with(KeyCode::Down, CTRL),
K::Home => plain(KeyCode::Home),
K::CtrlHome => with(KeyCode::Home, CTRL),
K::End => plain(KeyCode::End),
K::CtrlEnd => with(KeyCode::End, CTRL),
K::PageUp => plain(KeyCode::PageUp),
K::PageDown => plain(KeyCode::PageDown),
K::BackTab => plain(KeyCode::BackTab),
K::Delete => plain(KeyCode::Delete),
K::Insert => plain(KeyCode::Insert),
K::F(n) => plain(KeyCode::F(n)),
K::Char(c) => Some(char_key(c, Modifiers::NONE)),
K::Alt(c) => Some(char_key(c, ALT)),
K::Ctrl(c) => with(KeyCode::Char(c), CTRL),
K::Null => Some(Key::ctrl(' ')),
K::Esc => plain(KeyCode::Escape),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn read(bytes: &[u8]) -> Option<Event> {
let mut rest = bytes[1..].iter().map(|&b| Ok(b));
let event = ::termion::event::parse_event(bytes[0], &mut rest).ok()?;
from_termion(event, &mut HeldButton::default())
}
fn key(name: &str) -> Option<Event> {
Some(Event::Key(Key::parse(name).unwrap()))
}
#[test]
fn keys_read_as_a_legacy_terminal_sends_them() {
for (bytes, name) in [
(&b"a"[..], "a"),
(b"A", "A"),
(b"\x01", "ctrl+a"),
(b"\t", "tab"),
(b"\r", "enter"),
(b"\x7f", "backspace"),
(b"\x1bx", "alt+x"),
(b"\x1b\x01", "alt+ctrl+a"),
(b"\x00", "ctrl+space"),
(b"\x1c", "ctrl+4"),
(b"\x1b[Z", "shift+tab"),
(b"\x1b[A", "up"),
(b"\x1b[1;5C", "ctrl+right"),
(b"\x1b[1;2D", "shift+left"),
(b"\x1b[3~", "delete"),
(b"\x1b[6~", "pagedown"),
(b"\x1bOP", "f1"),
(b"\x1b[15~", "f5"),
] {
let read = self::read(bytes);
assert_eq!(read, key(name), "{bytes:?}");
assert!(!read.unwrap().key().unwrap().is_exact(), "{bytes:?}");
}
let Some(Event::Key(tab)) = read(b"\t") else {
panic!("a key");
};
assert!(tab.matches(&Key::parse("ctrl+i").unwrap()));
}
#[test]
fn the_mouse_remembers_the_button_held() {
let mut held = HeldButton::default();
let mut read = |bytes: &[u8]| {
let mut rest = bytes[1..].iter().map(|&b| Ok(b));
let event = ::termion::event::parse_event(bytes[0], &mut rest).ok()?;
from_termion(event, &mut held)
};
let at = |kind, column, row| Some(Event::Mouse(Mouse::new(kind, column, row)));
assert_eq!(
read(b"\x1b[<2;5;3M"),
at(MouseKind::Down(Button::Right), 4, 2)
);
assert_eq!(
read(b"\x1b[<2;6;3m"),
at(MouseKind::Up(Button::Right), 5, 2)
);
assert_eq!(
read(b"\x1b[<0;1;1M"),
at(MouseKind::Down(Button::Left), 0, 0)
);
assert_eq!(
read(b"\x1b[<32;2;1M"),
at(MouseKind::Drag(Button::Left), 1, 0)
);
assert_eq!(read(b"\x1b[<0;2;1m"), at(MouseKind::Up(Button::Left), 1, 0));
assert_eq!(read(b"\x1b[<64;1;1M"), at(MouseKind::ScrollUp, 0, 0));
assert_eq!(read(b"\x1b[<65;1;1M"), at(MouseKind::ScrollDown, 0, 0));
assert_eq!(read(b"\x1b[200~"), None);
assert_eq!(read(b"\x1b[<4;1;1M"), None);
assert_eq!(
from_termion(T::Unsupported(b"\x1b[200~".to_vec()), &mut held),
None
);
}
}