use super::types::*;
pub fn parse(buf: &[u8]) -> Option<(Event, usize)> {
if buf.is_empty() {
return None;
}
match buf[0] {
b'\x1b' => parse_esc(buf),
b'\r' | b'\n' => Some((key_event(KeyCode::Enter, KeyModifiers::NONE), 1)),
b'\t' => Some((key_event(KeyCode::Tab, KeyModifiers::NONE), 1)),
b'\x7f' => Some((key_event(KeyCode::Backspace, KeyModifiers::NONE), 1)),
b'\x08' => Some((key_event(KeyCode::Backspace, KeyModifiers::NONE), 1)),
b'\0' => Some((key_event(KeyCode::Null, KeyModifiers::NONE), 1)),
c @ 1..=26 => {
let ch = (c + b'a' - 1) as char;
Some((key_event(KeyCode::Char(ch), KeyModifiers::CONTROL), 1))
}
c if c >= 0x20 => parse_utf8_char(buf),
_ => Some((key_event(KeyCode::Null, KeyModifiers::NONE), 1)),
}
}
fn key_event(code: KeyCode, modifiers: KeyModifiers) -> Event {
Event::Key(KeyEvent::new(code, modifiers))
}
fn parse_utf8_char(buf: &[u8]) -> Option<(Event, usize)> {
let s = std::str::from_utf8(buf).ok().or_else(|| {
for len in (1..=buf.len().min(4)).rev() {
if let Ok(s) = std::str::from_utf8(&buf[..len]) {
return Some(s);
}
}
None
})?;
let ch = s.chars().next()?;
let len = ch.len_utf8();
if buf.len() < len {
return None; }
Some((key_event(KeyCode::Char(ch), KeyModifiers::NONE), len))
}
fn parse_esc(buf: &[u8]) -> Option<(Event, usize)> {
if buf.len() == 1 {
return None; }
match buf[1] {
b'[' => parse_csi(buf),
b'O' => parse_ss3(buf),
c if (0x20..0x7f).contains(&c) => {
let ch = c as char;
Some((key_event(KeyCode::Char(ch), KeyModifiers::ALT), 2))
}
_ => Some((key_event(KeyCode::Esc, KeyModifiers::NONE), 1)),
}
}
fn parse_csi(buf: &[u8]) -> Option<(Event, usize)> {
if buf.len() < 3 {
return None;
}
if buf[2] == b'<' {
return parse_sgr_mouse(buf);
}
let mut i = 2;
while i < buf.len() {
if buf[i] >= 0x40 && buf[i] <= 0x7E {
break;
}
i += 1;
}
if i >= buf.len() {
return None; }
let final_byte = buf[i];
let params_raw = &buf[2..i];
let consumed = i + 1;
let params = parse_params(params_raw);
match final_byte {
b'A' => Some((modified_key(KeyCode::Up, ¶ms), consumed)),
b'B' => Some((modified_key(KeyCode::Down, ¶ms), consumed)),
b'C' => Some((modified_key(KeyCode::Right, ¶ms), consumed)),
b'D' => Some((modified_key(KeyCode::Left, ¶ms), consumed)),
b'H' => Some((modified_key(KeyCode::Home, ¶ms), consumed)),
b'F' => Some((modified_key(KeyCode::End, ¶ms), consumed)),
b'Z' => Some((key_event(KeyCode::BackTab, KeyModifiers::SHIFT), consumed)),
b'~' => {
let code = params.first().copied().unwrap_or(0);
let key = match code {
1 => KeyCode::Home,
2 => KeyCode::Insert,
3 => KeyCode::Delete,
4 => KeyCode::End,
5 => KeyCode::PageUp,
6 => KeyCode::PageDown,
11 => KeyCode::F(1),
12 => KeyCode::F(2),
13 => KeyCode::F(3),
14 => KeyCode::F(4),
15 => KeyCode::F(5),
17 => KeyCode::F(6),
18 => KeyCode::F(7),
19 => KeyCode::F(8),
20 => KeyCode::F(9),
21 => KeyCode::F(10),
23 => KeyCode::F(11),
24 => KeyCode::F(12),
_ => return Some((key_event(KeyCode::Null, KeyModifiers::NONE), consumed)),
};
let mods = if params.len() >= 2 {
modifier_from_param(params[1])
} else {
KeyModifiers::NONE
};
Some((key_event(key, mods), consumed))
}
b'I' => Some((Event::FocusGained, consumed)),
b'O' => Some((Event::FocusLost, consumed)),
b't' => {
if params.first() == Some(&8) && params.len() >= 3 {
Some((Event::Resize(params[2] as u16, params[1] as u16), consumed))
} else {
Some((key_event(KeyCode::Null, KeyModifiers::NONE), consumed))
}
}
_ => Some((key_event(KeyCode::Null, KeyModifiers::NONE), consumed)),
}
}
fn parse_ss3(buf: &[u8]) -> Option<(Event, usize)> {
if buf.len() < 3 {
return None;
}
let key = match buf[2] {
b'P' => KeyCode::F(1),
b'Q' => KeyCode::F(2),
b'R' => KeyCode::F(3),
b'S' => KeyCode::F(4),
b'A' => KeyCode::Up,
b'B' => KeyCode::Down,
b'C' => KeyCode::Right,
b'D' => KeyCode::Left,
b'H' => KeyCode::Home,
b'F' => KeyCode::End,
_ => return Some((key_event(KeyCode::Esc, KeyModifiers::NONE), 1)),
};
Some((key_event(key, KeyModifiers::NONE), 3))
}
fn parse_sgr_mouse(buf: &[u8]) -> Option<(Event, usize)> {
let mut i = 3;
while i < buf.len() {
if buf[i] == b'M' || buf[i] == b'm' {
break;
}
i += 1;
}
if i >= buf.len() {
return None; }
let is_release = buf[i] == b'm';
let consumed = i + 1;
let params = parse_params(&buf[3..i]);
if params.len() < 3 {
return Some((key_event(KeyCode::Null, KeyModifiers::NONE), consumed));
}
let cb = params[0] as u16;
let col = params[1].saturating_sub(1) as u16; let row = params[2].saturating_sub(1) as u16;
let mut modifiers = KeyModifiers::NONE;
if cb & 4 != 0 {
modifiers |= KeyModifiers::SHIFT;
}
if cb & 8 != 0 {
modifiers |= KeyModifiers::ALT;
}
if cb & 16 != 0 {
modifiers |= KeyModifiers::CONTROL;
}
let button_bits = cb & 0b1100_0011;
let is_motion = cb & 32 != 0;
let kind = match button_bits {
64 => MouseEventKind::ScrollUp,
65 => MouseEventKind::ScrollDown,
66 => MouseEventKind::ScrollLeft,
67 => MouseEventKind::ScrollRight,
b => {
let button = match b & 3 {
0 => MouseButton::Left,
1 => MouseButton::Middle,
2 => MouseButton::Right,
_ => MouseButton::Left,
};
if is_release {
MouseEventKind::Up(button)
} else if is_motion {
MouseEventKind::Drag(button)
} else {
MouseEventKind::Down(button)
}
}
};
Some((
Event::Mouse(MouseEvent {
kind,
column: col,
row,
modifiers,
}),
consumed,
))
}
fn parse_params(raw: &[u8]) -> Vec<u32> {
if raw.is_empty() {
return vec![];
}
let s = std::str::from_utf8(raw).unwrap_or("");
s.split(';')
.map(|p| p.parse::<u32>().unwrap_or(0))
.collect()
}
fn modifier_from_param(param: u32) -> KeyModifiers {
let bits = param.saturating_sub(1) as u8;
let mut mods = KeyModifiers::NONE;
if bits & 1 != 0 {
mods |= KeyModifiers::SHIFT;
}
if bits & 2 != 0 {
mods |= KeyModifiers::ALT;
}
if bits & 4 != 0 {
mods |= KeyModifiers::CONTROL;
}
if bits & 8 != 0 {
mods |= KeyModifiers::SUPER;
}
if bits & 16 != 0 {
mods |= KeyModifiers::HYPER;
}
if bits & 32 != 0 {
mods |= KeyModifiers::META;
}
mods
}
fn modified_key(code: KeyCode, params: &[u32]) -> Event {
let mods = if params.len() >= 2 {
modifier_from_param(params[1])
} else {
KeyModifiers::NONE
};
key_event(code, mods)
}
pub fn esc_event() -> Event {
Event::Key(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE))
}
#[cfg(test)]
mod tests {
use super::*;
fn key(code: KeyCode) -> Event {
Event::Key(KeyEvent::new(code, KeyModifiers::NONE))
}
fn key_mod(code: KeyCode, mods: KeyModifiers) -> Event {
Event::Key(KeyEvent::new(code, mods))
}
#[test]
fn ascii_char() {
assert_eq!(parse(b"a"), Some((key(KeyCode::Char('a')), 1)));
assert_eq!(parse(b"Z"), Some((key(KeyCode::Char('Z')), 1)));
assert_eq!(parse(b" "), Some((key(KeyCode::Char(' ')), 1)));
}
#[test]
fn utf8_multibyte() {
let s = "é";
assert_eq!(
parse(s.as_bytes()),
Some((key(KeyCode::Char('é')), s.len()))
);
}
#[test]
fn enter() {
assert_eq!(parse(b"\r"), Some((key(KeyCode::Enter), 1)));
assert_eq!(parse(b"\n"), Some((key(KeyCode::Enter), 1)));
}
#[test]
fn tab() {
assert_eq!(parse(b"\t"), Some((key(KeyCode::Tab), 1)));
}
#[test]
fn backspace() {
assert_eq!(parse(b"\x7f"), Some((key(KeyCode::Backspace), 1)));
assert_eq!(parse(b"\x08"), Some((key(KeyCode::Backspace), 1)));
}
#[test]
fn null() {
assert_eq!(parse(b"\0"), Some((key(KeyCode::Null), 1)));
}
#[test]
fn ctrl_letter() {
assert_eq!(
parse(b"\x01"),
Some((key_mod(KeyCode::Char('a'), KeyModifiers::CONTROL), 1))
);
assert_eq!(
parse(b"\x03"),
Some((key_mod(KeyCode::Char('c'), KeyModifiers::CONTROL), 1))
);
}
#[test]
fn esc_alone_incomplete() {
assert_eq!(parse(b"\x1b"), None);
}
#[test]
fn alt_char() {
assert_eq!(
parse(b"\x1ba"),
Some((key_mod(KeyCode::Char('a'), KeyModifiers::ALT), 2))
);
}
#[test]
fn arrow_keys() {
assert_eq!(parse(b"\x1b[A"), Some((key(KeyCode::Up), 3)));
assert_eq!(parse(b"\x1b[B"), Some((key(KeyCode::Down), 3)));
assert_eq!(parse(b"\x1b[C"), Some((key(KeyCode::Right), 3)));
assert_eq!(parse(b"\x1b[D"), Some((key(KeyCode::Left), 3)));
}
#[test]
fn modified_arrow() {
assert_eq!(
parse(b"\x1b[1;5A"),
Some((key_mod(KeyCode::Up, KeyModifiers::CONTROL), 6))
);
assert_eq!(
parse(b"\x1b[1;2B"),
Some((key_mod(KeyCode::Down, KeyModifiers::SHIFT), 6))
);
}
#[test]
fn home_end() {
assert_eq!(parse(b"\x1b[H"), Some((key(KeyCode::Home), 3)));
assert_eq!(parse(b"\x1b[F"), Some((key(KeyCode::End), 3)));
}
#[test]
fn function_keys_ss3() {
assert_eq!(parse(b"\x1bOP"), Some((key(KeyCode::F(1)), 3)));
assert_eq!(parse(b"\x1bOQ"), Some((key(KeyCode::F(2)), 3)));
assert_eq!(parse(b"\x1bOR"), Some((key(KeyCode::F(3)), 3)));
assert_eq!(parse(b"\x1bOS"), Some((key(KeyCode::F(4)), 3)));
}
#[test]
fn function_keys_csi() {
assert_eq!(parse(b"\x1b[15~"), Some((key(KeyCode::F(5)), 5)));
assert_eq!(parse(b"\x1b[24~"), Some((key(KeyCode::F(12)), 5)));
}
#[test]
fn insert_delete_page() {
assert_eq!(parse(b"\x1b[2~"), Some((key(KeyCode::Insert), 4)));
assert_eq!(parse(b"\x1b[3~"), Some((key(KeyCode::Delete), 4)));
assert_eq!(parse(b"\x1b[5~"), Some((key(KeyCode::PageUp), 4)));
assert_eq!(parse(b"\x1b[6~"), Some((key(KeyCode::PageDown), 4)));
}
#[test]
fn backtab() {
assert_eq!(
parse(b"\x1b[Z"),
Some((key_mod(KeyCode::BackTab, KeyModifiers::SHIFT), 3))
);
}
#[test]
fn focus() {
assert_eq!(parse(b"\x1b[I"), Some((Event::FocusGained, 3)));
assert_eq!(parse(b"\x1b[O"), Some((Event::FocusLost, 3)));
}
#[test]
fn resize() {
assert_eq!(parse(b"\x1b[8;24;80t"), Some((Event::Resize(80, 24), 10)));
}
#[test]
fn mouse_click() {
assert_eq!(
parse(b"\x1b[<0;11;21M"),
Some((
Event::Mouse(MouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
column: 10,
row: 20,
modifiers: KeyModifiers::NONE,
}),
11
))
);
}
#[test]
fn mouse_release() {
assert_eq!(
parse(b"\x1b[<0;1;1m"),
Some((
Event::Mouse(MouseEvent {
kind: MouseEventKind::Up(MouseButton::Left),
column: 0,
row: 0,
modifiers: KeyModifiers::NONE,
}),
9
))
);
}
#[test]
fn mouse_scroll() {
assert_eq!(
parse(b"\x1b[<64;1;1M"),
Some((
Event::Mouse(MouseEvent {
kind: MouseEventKind::ScrollUp,
column: 0,
row: 0,
modifiers: KeyModifiers::NONE,
}),
10
))
);
}
#[test]
fn incomplete() {
assert_eq!(parse(b""), None);
assert_eq!(parse(b"\x1b"), None);
assert_eq!(parse(b"\x1b["), None);
assert_eq!(parse(b"\x1b[1"), None);
assert_eq!(parse(b"\x1b[<0;1"), None);
}
}