use anyhow::{anyhow, bail, Context};
use crate::{
key::{
Key, KeyCode, KeyEventKind, KeyEventState, KeyMods, MediaKeyCode,
ModKeyCode,
},
mouse::{MouseButton, MouseEvent, MouseEventKind},
term::internal::InternalTermEvent as E,
};
pub struct InputParser {
buf: Vec<u8>,
#[cfg(windows)]
pub windows_mouse_buttons: u32,
}
impl InputParser {
pub fn new() -> Self {
Self {
buf: Vec::new(),
#[cfg(windows)]
windows_mouse_buttons: 0,
}
}
pub fn parse_input<F>(
&mut self,
input: &[u8],
is_raw_mode: bool,
more: bool,
mut f: F,
) where
F: FnMut(E),
{
let mut i = 0;
let mut consumed = 0;
use crate::key::KeyMods as Mods;
self.buf.extend_from_slice(input);
let buf = &self.buf;
while i < buf.len() {
let next_char = buf[i];
i += 1;
match next_char {
b'\x1B' => {
if i >= buf.len() {
if more {
break;
} else {
f(E::Key(Key::new(KeyCode::Esc, Mods::NONE)));
consumed = i;
continue;
}
}
let next_char = buf[i];
i += 1;
match next_char {
b'O' => {
if i >= buf.len() {
break;
}
let next_char = buf[i];
i += 1;
match next_char {
b'A' => {
consumed = i;
f(E::Key(Key::new(KeyCode::Up, Mods::NONE)));
}
b'B' => {
consumed = i;
f(E::Key(Key::new(KeyCode::Down, Mods::NONE)));
}
b'C' => {
consumed = i;
f(E::Key(Key::new(KeyCode::Right, Mods::NONE)));
}
b'D' => {
consumed = i;
f(E::Key(Key::new(KeyCode::Left, Mods::NONE)));
}
b'F' => {
consumed = i;
f(E::Key(Key::new(KeyCode::End, Mods::NONE)));
}
b'H' => {
consumed = i;
f(E::Key(Key::new(KeyCode::Home, Mods::NONE)));
}
val @ b'P'..=b'S' => {
consumed = i;
f(E::Key(Key::new(KeyCode::F(1 + val - b'P'), Mods::NONE)));
}
_ => {
consumed = i;
log::error!("Input parsing error.");
}
}
}
b'[' => {
if let Some(b'M') = buf.get(i) {
i += 1; if i + 3 >= buf.len() {
break;
}
let b = buf[i].saturating_sub(32);
let x = u16::from(buf[i + 1].saturating_sub(32)) - 1;
let y = u16::from(buf[i + 2].saturating_sub(32)) - 1;
i += 3;
consumed = i;
match parse_cb(b) {
Ok((kind, mods)) => {
f(E::Mouse(MouseEvent {
kind,
mods,
x: x as i32,
y: y as i32,
}));
}
Err(err) => {
log::error!("'CSI M bxy' error: {}", err);
}
}
} else {
let len = if let Some(len) =
parse_csi(&buf[i..], is_raw_mode, &mut f)
{
len
} else {
break;
};
i += len;
consumed = i;
}
}
b'\x1B' => {
f(E::Key(Key::new(KeyCode::Esc, Mods::NONE)));
consumed = i;
}
c => {
match parse_char_key(c, &buf[i - 1..]) {
Ok((used, key)) => {
i += used;
if let Some(mut key) = key {
key.mods |= Mods::ALT;
f(E::Key(key));
}
}
Err(err) => {
log::error!("Parse esc-char key error: {}", err);
}
}
consumed = i;
}
}
}
c => {
match parse_char_key(c, &buf[i - 1..]) {
Ok((used, key)) => {
i += used;
if let Some(key) = key {
f(E::Key(key));
}
}
Err(err) => {
log::error!("Parse char key error: {}", err);
}
}
consumed = i;
}
}
}
let buf_len = self.buf.len();
self.buf.copy_within(consumed..buf_len, 0);
self.buf.truncate(buf_len - consumed);
}
}
fn parse_char_key(c: u8, buf: &[u8]) -> anyhow::Result<(usize, Option<Key>)> {
use crate::key::KeyMods as Mods;
let mut i = 0;
let key = match c {
b'\r' => Key::new(KeyCode::Enter, Mods::NONE),
b'\n' => Key::new(KeyCode::Enter, Mods::NONE),
b'\t' => Key::new(KeyCode::Tab, Mods::NONE),
b'\x7F' => Key::new(KeyCode::Backspace, Mods::NONE),
c @ b'\x01'..=b'\x1A' => {
Key::new(KeyCode::Char((c - 0x1 + b'a') as char), Mods::CONTROL)
}
c @ b'\x1C'..=b'\x1F' => {
Key::new(KeyCode::Char((c - 0x1C + b'4') as char), Mods::CONTROL)
}
b'\0' => Key::new(KeyCode::Char(' '), Mods::CONTROL),
first_byte => {
let char_len = utf8_char_len(first_byte);
if char_len == 0 {
return Ok((0, None));
} else if char_len - 1 <= buf.len() {
let char = str::from_utf8(&buf[..char_len]);
i = char_len - 1;
match char {
Ok(s) => {
let char = s.chars().next().unwrap();
Key::new(KeyCode::Char(char), Mods::NONE)
}
Err(_) => {
bail!("Invalid utf-8 char");
}
}
} else {
bail!("Not enough bytes");
}
}
};
Ok((i, Some(key)))
}
fn parse_csi<F>(buf: &[u8], is_raw_mode: bool, f: F) -> Option<usize>
where
F: FnMut(E),
{
let mut i = 0;
while i < buf.len() && (0x30..=0x3f).contains(&buf[i]) {
i += 1;
}
let params = &buf[..i];
while i < buf.len() && (0x20..=0x2f).contains(&buf[i]) {
i += 1;
}
let intermediates = &buf[params.len()..i];
let final_ = if i < buf.len() {
if (0x40..=0x7E).contains(&buf[i]) {
i += 1;
buf[i - 1]
} else {
log::error!("CSI is incomplete. Ignoring.");
return Some(i);
}
} else {
return None;
};
match parse_csi_impl(buf, params, intermediates, final_, is_raw_mode, f) {
Ok(()) => (),
Err(err) => log::error!("CSI error: {}", err),
}
Some(i)
}
fn parse_csi_impl<F>(
buf: &[u8],
params: &[u8],
intermediates: &[u8],
final_: u8,
is_raw_mode: bool,
mut f: F,
) -> anyhow::Result<()>
where
F: FnMut(E),
{
match final_ {
b'A' | b'B' | b'C' | b'D' | b'F' | b'H' | b'P' | b'Q' | b'S' => {
let code = match final_ {
b'A' => KeyCode::Up,
b'B' => KeyCode::Down,
b'C' => KeyCode::Right,
b'D' => KeyCode::Left,
b'F' => KeyCode::End,
b'H' => KeyCode::Home,
b'P' => KeyCode::F(1),
b'Q' => KeyCode::F(2),
b'S' => KeyCode::F(4),
_ => unreachable!("Unhanled CSI [A|B|C|...] key"),
};
let params = str::from_utf8(params)?;
let mut params = params.split(';');
params.next();
let (mods, kind) = if let Some(mods_param) = params.next() {
let mut mods_param = mods_param.split(':');
let mods = parse_modifiers(
mods_param.next().unwrap_or("0").parse::<u8>().unwrap_or(0),
);
let kind = parse_key_event_kind(
mods_param.next().unwrap_or("").parse().unwrap_or(0),
);
(mods, kind)
} else {
(KeyMods::NONE, KeyEventKind::Press)
};
f(E::Key(Key::new_with_kind(code, mods, kind)));
}
b'c' if params.starts_with(b"?") => {
f(E::PrimaryDeviceAttributes);
}
b'Z' => {
f(E::Key(Key::new(KeyCode::Tab, KeyMods::SHIFT)));
}
b'I' => {
f(E::FocusGained);
}
b'O' => {
f(E::FocusLost);
}
b'u' => {
if params.starts_with(b"?") {
if let Some(flags_char) = params.get(1) {
let flags = flags_char.wrapping_sub(b'0');
f(E::ReplyKittyKeyboard(flags));
}
} else {
let (code, alt_code, mods_param, kind) =
parse_csi_u(params).context("parse_csi_u")?;
let mut mods = parse_modifiers(mods_param);
let kind = parse_key_event_kind(kind);
let state_from_mods = parse_modifiers_to_state(mods_param);
let (mut keycode, state_from_keycode) = {
if let Some((special_key_code, state)) =
translate_functional_key_code(code)
{
(special_key_code, state)
} else if let Some(c) = char::from_u32(code) {
(
match c {
'\x1B' => KeyCode::Esc,
'\r' => KeyCode::Enter,
'\n' if !is_raw_mode => KeyCode::Enter,
'\t' => KeyCode::Tab,
'\x7F' => KeyCode::Backspace,
_ => KeyCode::Char(c),
},
KeyEventState::empty(),
)
} else {
bail!(
"Unhandled CSI-u {:?} {:?} {}",
params,
intermediates,
final_
);
}
};
if let KeyCode::Modifier(modifier_keycode) = keycode {
match modifier_keycode {
ModKeyCode::LeftAlt | ModKeyCode::RightAlt => {
mods.set(KeyMods::ALT, true)
}
ModKeyCode::LeftControl | ModKeyCode::RightControl => {
mods.set(KeyMods::CONTROL, true)
}
ModKeyCode::LeftShift | ModKeyCode::RightShift => {
mods.set(KeyMods::SHIFT, true)
}
ModKeyCode::LeftSuper | ModKeyCode::RightSuper => {
mods.set(KeyMods::SUPER, true)
}
ModKeyCode::LeftHyper | ModKeyCode::RightHyper => {
mods.set(KeyMods::HYPER, true)
}
ModKeyCode::LeftMeta | ModKeyCode::RightMeta => {
mods.set(KeyMods::META, true)
}
_ => {}
}
}
if mods.contains(KeyMods::SHIFT) {
if let Some(shifted_c) = alt_code.and_then(char::from_u32) {
keycode = KeyCode::Char(shifted_c);
mods.set(KeyMods::SHIFT, false);
}
}
let key_event = Key {
code: keycode,
mods,
kind,
state: state_from_keycode | state_from_mods,
};
f(E::Key(key_event));
}
}
b'm' | b'M' if params.starts_with(b"<") => {
let params_str = str::from_utf8(params)?;
let mut params = params_str[1..].split(';');
let b = params.next().ok_or_else(|| anyhow!("No Cb param"))?;
let b = b.parse::<u8>()?;
let (kind, mods) = parse_cb(b)?;
let kind = if final_ == b'm' {
match kind {
MouseEventKind::Down(button) => MouseEventKind::Up(button),
other => other,
}
} else {
kind
};
let x = params.next().ok_or_else(|| anyhow!("No Cx param"))?;
let x = x.parse::<u16>()? - 1;
let y = params.next().ok_or_else(|| anyhow!("No Cy param"))?;
let y = y.parse::<u16>()? - 1;
f(E::Mouse(MouseEvent {
kind,
mods,
x: x as i32,
y: y as i32,
}));
}
b'M' => {
let params_str = str::from_utf8(params)?;
let mut params = params_str.split(';');
let b = params.next().ok_or_else(|| anyhow!("No rxvt Cb param"))?;
let b = b.parse::<u8>()?.saturating_sub(32);
let (kind, mods) = parse_cb(b)?;
let x = params.next().ok_or_else(|| anyhow!("No rxvt Cx param"))?;
let x = x.parse::<u16>()? - 1;
let y = params.next().ok_or_else(|| anyhow!("No rxvt Cy param"))?;
let y = y.parse::<u16>()? - 1;
f(E::Mouse(MouseEvent {
kind,
mods,
x: x as i32,
y: y as i32,
}));
}
b'R' => {
let params_str = str::from_utf8(params)?;
let mut params = params_str.split(';');
let y = params.next().ok_or_else(|| anyhow!("No CSI-R Cy param"))?;
let y = y.parse::<u16>()?.saturating_sub(1);
let x = params.next().ok_or_else(|| anyhow!("No CSI-R Cx param"))?;
let x = x.parse::<u16>()?.saturating_sub(1);
f(E::CursorPos(x, y));
}
b'~' => {
let params_str = str::from_utf8(params)?;
let mut params = params_str.split(';');
let first = params
.next()
.ok_or_else(|| anyhow!("No key param in CSI ~"))?
.parse::<u8>()?;
let mods_param = params.next();
let (mods, state) = if let Some(mods_param) = mods_param {
let mask = mods_param.parse::<u8>()?;
(parse_modifiers(mask), parse_modifiers_to_state(mask))
} else {
(KeyMods::NONE, KeyEventState::NONE)
};
if first == 27 {
let code_param = params.next();
let code = if let Some(code_param) = code_param {
let code = code_param.parse::<u8>()?;
match code {
8 | 0x7f => KeyCode::Backspace,
0x1b => KeyCode::Esc,
9 => KeyCode::Tab,
10 | 13 => KeyCode::Enter,
_ => KeyCode::Char(code as char),
}
} else {
bail!("Empty code in modifyOtherKeys")
};
f(E::Key(Key {
code,
mods,
kind: KeyEventKind::Press,
state,
}));
} else {
let code = first;
let code = match code {
1 | 7 => KeyCode::Home,
2 => KeyCode::Insert,
3 => KeyCode::Delete,
4 | 8 => KeyCode::End,
5 => KeyCode::PageUp,
6 => KeyCode::PageDown,
v @ 11..=15 => KeyCode::F(v - 10),
v @ 17..=21 => KeyCode::F(v - 11),
v @ 23..=26 => KeyCode::F(v - 12),
v @ 28..=29 => KeyCode::F(v - 15),
v @ 31..=34 => KeyCode::F(v - 17),
v => bail!("Wrong key param ({}) in CSI ~", v),
};
f(E::Key(Key {
code,
mods,
kind: KeyEventKind::Press,
state,
}));
}
}
_ => {
log::debug!(
"Unknown CSI: {} ({:?}, {:?}, {})",
str::from_utf8(buf).unwrap_or("???"),
params,
intermediates,
final_
);
}
}
Ok(())
}
fn parse_csi_u(params: &[u8]) -> anyhow::Result<(u32, Option<u32>, u8, u8)> {
let params = str::from_utf8(params)?;
let mut params = params.split(';');
let code_param = params.next().unwrap_or("");
let mut code_param = code_param.split(':');
let code = code_param.next().unwrap_or("0");
let code = code.parse::<u32>()?;
let alt_code = code_param.next().map(|c| c.parse::<u32>().ok()).flatten();
let mods_param = params.next().unwrap_or("0");
let mut mods_param = mods_param.split(':');
let mods = mods_param.next().unwrap_or("0");
let mods = mods.parse::<u8>().unwrap_or(0);
let kind = mods_param.next().unwrap_or("1").parse::<u8>().unwrap_or(1);
Ok((code, alt_code, mods, kind))
}
fn parse_modifiers(mask: u8) -> KeyMods {
let modifier_mask = mask.saturating_sub(1);
let mut modifiers = KeyMods::empty();
if modifier_mask & 1 != 0 {
modifiers |= KeyMods::SHIFT;
}
if modifier_mask & 2 != 0 {
modifiers |= KeyMods::ALT;
}
if modifier_mask & 4 != 0 {
modifiers |= KeyMods::CONTROL;
}
if modifier_mask & 8 != 0 {
modifiers |= KeyMods::SUPER;
}
if modifier_mask & 16 != 0 {
modifiers |= KeyMods::HYPER;
}
if modifier_mask & 32 != 0 {
modifiers |= KeyMods::META;
}
modifiers
}
fn parse_modifiers_to_state(mask: u8) -> KeyEventState {
let modifier_mask = mask.saturating_sub(1);
let mut state = KeyEventState::empty();
if modifier_mask & 64 != 0 {
state |= KeyEventState::CAPS_LOCK;
}
if modifier_mask & 128 != 0 {
state |= KeyEventState::NUM_LOCK;
}
state
}
fn parse_key_event_kind(kind: u8) -> KeyEventKind {
match kind {
1 => KeyEventKind::Press,
2 => KeyEventKind::Repeat,
3 => KeyEventKind::Release,
_ => KeyEventKind::Press,
}
}
fn translate_functional_key_code(
codepoint: u32,
) -> Option<(KeyCode, KeyEventState)> {
if let Some(keycode) = match codepoint {
57399 => Some(KeyCode::Char('0')),
57400 => Some(KeyCode::Char('1')),
57401 => Some(KeyCode::Char('2')),
57402 => Some(KeyCode::Char('3')),
57403 => Some(KeyCode::Char('4')),
57404 => Some(KeyCode::Char('5')),
57405 => Some(KeyCode::Char('6')),
57406 => Some(KeyCode::Char('7')),
57407 => Some(KeyCode::Char('8')),
57408 => Some(KeyCode::Char('9')),
57409 => Some(KeyCode::Char('.')),
57410 => Some(KeyCode::Char('/')),
57411 => Some(KeyCode::Char('*')),
57412 => Some(KeyCode::Char('-')),
57413 => Some(KeyCode::Char('+')),
57414 => Some(KeyCode::Enter),
57415 => Some(KeyCode::Char('=')),
57416 => Some(KeyCode::Char(',')),
57417 => Some(KeyCode::Left),
57418 => Some(KeyCode::Right),
57419 => Some(KeyCode::Up),
57420 => Some(KeyCode::Down),
57421 => Some(KeyCode::PageUp),
57422 => Some(KeyCode::PageDown),
57423 => Some(KeyCode::Home),
57424 => Some(KeyCode::End),
57425 => Some(KeyCode::Insert),
57426 => Some(KeyCode::Delete),
57427 => Some(KeyCode::KeypadBegin),
_ => None,
} {
return Some((keycode, KeyEventState::KEYPAD));
}
if let Some(keycode) = match codepoint {
57358 => Some(KeyCode::CapsLock),
57359 => Some(KeyCode::ScrollLock),
57360 => Some(KeyCode::NumLock),
57361 => Some(KeyCode::PrintScreen),
57362 => Some(KeyCode::Pause),
57363 => Some(KeyCode::Menu),
57376 => Some(KeyCode::F(13)),
57377 => Some(KeyCode::F(14)),
57378 => Some(KeyCode::F(15)),
57379 => Some(KeyCode::F(16)),
57380 => Some(KeyCode::F(17)),
57381 => Some(KeyCode::F(18)),
57382 => Some(KeyCode::F(19)),
57383 => Some(KeyCode::F(20)),
57384 => Some(KeyCode::F(21)),
57385 => Some(KeyCode::F(22)),
57386 => Some(KeyCode::F(23)),
57387 => Some(KeyCode::F(24)),
57388 => Some(KeyCode::F(25)),
57389 => Some(KeyCode::F(26)),
57390 => Some(KeyCode::F(27)),
57391 => Some(KeyCode::F(28)),
57392 => Some(KeyCode::F(29)),
57393 => Some(KeyCode::F(30)),
57394 => Some(KeyCode::F(31)),
57395 => Some(KeyCode::F(32)),
57396 => Some(KeyCode::F(33)),
57397 => Some(KeyCode::F(34)),
57398 => Some(KeyCode::F(35)),
57428 => Some(KeyCode::Media(MediaKeyCode::Play)),
57429 => Some(KeyCode::Media(MediaKeyCode::Pause)),
57430 => Some(KeyCode::Media(MediaKeyCode::PlayPause)),
57431 => Some(KeyCode::Media(MediaKeyCode::Reverse)),
57432 => Some(KeyCode::Media(MediaKeyCode::Stop)),
57433 => Some(KeyCode::Media(MediaKeyCode::FastForward)),
57434 => Some(KeyCode::Media(MediaKeyCode::Rewind)),
57435 => Some(KeyCode::Media(MediaKeyCode::Next)),
57436 => Some(KeyCode::Media(MediaKeyCode::Prev)),
57437 => Some(KeyCode::Media(MediaKeyCode::Record)),
57438 => Some(KeyCode::Media(MediaKeyCode::VolumeDown)),
57439 => Some(KeyCode::Media(MediaKeyCode::VolumeUp)),
57440 => Some(KeyCode::Media(MediaKeyCode::VolumeMute)),
57441 => Some(KeyCode::Modifier(ModKeyCode::LeftShift)),
57442 => Some(KeyCode::Modifier(ModKeyCode::LeftControl)),
57443 => Some(KeyCode::Modifier(ModKeyCode::LeftAlt)),
57444 => Some(KeyCode::Modifier(ModKeyCode::LeftSuper)),
57445 => Some(KeyCode::Modifier(ModKeyCode::LeftHyper)),
57446 => Some(KeyCode::Modifier(ModKeyCode::LeftMeta)),
57447 => Some(KeyCode::Modifier(ModKeyCode::RightShift)),
57448 => Some(KeyCode::Modifier(ModKeyCode::RightControl)),
57449 => Some(KeyCode::Modifier(ModKeyCode::RightAlt)),
57450 => Some(KeyCode::Modifier(ModKeyCode::RightSuper)),
57451 => Some(KeyCode::Modifier(ModKeyCode::RightHyper)),
57452 => Some(KeyCode::Modifier(ModKeyCode::RightMeta)),
57453 => Some(KeyCode::Modifier(ModKeyCode::IsoLevel3Shift)),
57454 => Some(KeyCode::Modifier(ModKeyCode::IsoLevel5Shift)),
_ => None,
} {
return Some((keycode, KeyEventState::empty()));
}
None
}
fn parse_cb(cb: u8) -> anyhow::Result<(MouseEventKind, KeyMods)> {
let button_number = (cb & 0b0000_0011) | ((cb & 0b1100_0000) >> 4);
let dragging = cb & 0b0010_0000 == 0b0010_0000;
let kind = match (button_number, dragging) {
(0, false) => MouseEventKind::Down(MouseButton::Left),
(1, false) => MouseEventKind::Down(MouseButton::Middle),
(2, false) => MouseEventKind::Down(MouseButton::Right),
(0, true) => MouseEventKind::Drag(MouseButton::Left),
(1, true) => MouseEventKind::Drag(MouseButton::Middle),
(2, true) => MouseEventKind::Drag(MouseButton::Right),
(3, false) => MouseEventKind::Up(MouseButton::Left),
(3, true) | (4, true) | (5, true) => MouseEventKind::Moved,
(4, false) => MouseEventKind::ScrollUp,
(5, false) => MouseEventKind::ScrollDown,
(6, false) => MouseEventKind::ScrollLeft,
(7, false) => MouseEventKind::ScrollRight,
_ => bail!("Failed to parse Cb param button"),
};
let mut modifiers = KeyMods::empty();
if cb & 0b0000_0100 == 0b0000_0100 {
modifiers |= KeyMods::SHIFT;
}
if cb & 0b0000_1000 == 0b0000_1000 {
modifiers |= KeyMods::ALT;
}
if cb & 0b0001_0000 == 0b0001_0000 {
modifiers |= KeyMods::CONTROL;
}
Ok((kind, modifiers))
}
fn utf8_char_len(first_byte: u8) -> usize {
match first_byte {
(0x00..=0x7F) => 1, (0xC0..=0xDF) => 2, (0xE0..=0xEF) => 3, (0xF0..=0xF7) => 4, (0x80..=0xBF) | (0xF8..=0xFF) => 0,
}
}