1367 lines
48 KiB
Rust
1367 lines
48 KiB
Rust
use std::io;
|
|
|
|
use crate::{
|
|
event::{
|
|
Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, MediaKeyCode,
|
|
ModifierKeyCode, MouseButton, MouseEvent, MouseEventKind,
|
|
},
|
|
ErrorKind, Result,
|
|
};
|
|
|
|
use super::super::super::InternalEvent;
|
|
|
|
// Event parsing
|
|
//
|
|
// This code (& previous one) are kind of ugly. We have to think about this,
|
|
// because it's really not maintainable, no tests, etc.
|
|
//
|
|
// Every fn returns Result<Option<InputEvent>>
|
|
//
|
|
// Ok(None) -> wait for more bytes
|
|
// Err(_) -> failed to parse event, clear the buffer
|
|
// Ok(Some(event)) -> we have event, clear the buffer
|
|
//
|
|
|
|
fn could_not_parse_event_error() -> ErrorKind {
|
|
io::Error::new(io::ErrorKind::Other, "Could not parse an event.")
|
|
}
|
|
|
|
pub(crate) fn parse_event(buffer: &[u8], input_available: bool) -> Result<Option<InternalEvent>> {
|
|
if buffer.is_empty() {
|
|
return Ok(None);
|
|
}
|
|
|
|
match buffer[0] {
|
|
b'\x1B' => {
|
|
if buffer.len() == 1 {
|
|
if input_available {
|
|
// Possible Esc sequence
|
|
Ok(None)
|
|
} else {
|
|
Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into()))))
|
|
}
|
|
} else {
|
|
match buffer[1] {
|
|
b'O' => {
|
|
if buffer.len() == 2 {
|
|
Ok(None)
|
|
} else {
|
|
match buffer[2] {
|
|
b'D' => {
|
|
Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))))
|
|
}
|
|
b'C' => Ok(Some(InternalEvent::Event(Event::Key(
|
|
KeyCode::Right.into(),
|
|
)))),
|
|
b'A' => {
|
|
Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Up.into()))))
|
|
}
|
|
b'B' => {
|
|
Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Down.into()))))
|
|
}
|
|
b'H' => {
|
|
Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Home.into()))))
|
|
}
|
|
b'F' => {
|
|
Ok(Some(InternalEvent::Event(Event::Key(KeyCode::End.into()))))
|
|
}
|
|
// F1-F4
|
|
val @ b'P'..=b'S' => Ok(Some(InternalEvent::Event(Event::Key(
|
|
KeyCode::F(1 + val - b'P').into(),
|
|
)))),
|
|
_ => Err(could_not_parse_event_error()),
|
|
}
|
|
}
|
|
}
|
|
b'[' => parse_csi(buffer),
|
|
b'\x1B' => Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into())))),
|
|
_ => parse_event(&buffer[1..], input_available).map(|event_option| {
|
|
event_option.map(|event| {
|
|
if let InternalEvent::Event(Event::Key(key_event)) = event {
|
|
let mut alt_key_event = key_event;
|
|
alt_key_event.modifiers |= KeyModifiers::ALT;
|
|
InternalEvent::Event(Event::Key(alt_key_event))
|
|
} else {
|
|
event
|
|
}
|
|
})
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
b'\r' => Ok(Some(InternalEvent::Event(Event::Key(
|
|
KeyCode::Enter.into(),
|
|
)))),
|
|
// Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get
|
|
// newlines as input is because the terminal converts \r into \n for us. When we
|
|
// enter raw mode, we disable that, so \n no longer has any meaning - it's better to
|
|
// use Ctrl+J. Waiting to handle it here means it gets picked up later
|
|
b'\n' if !crate::terminal::sys::is_raw_mode_enabled() => Ok(Some(InternalEvent::Event(
|
|
Event::Key(KeyCode::Enter.into()),
|
|
))),
|
|
b'\t' => Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into())))),
|
|
b'\x7F' => Ok(Some(InternalEvent::Event(Event::Key(
|
|
KeyCode::Backspace.into(),
|
|
)))),
|
|
c @ b'\x01'..=b'\x1A' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char((c as u8 - 0x1 + b'a') as char),
|
|
KeyModifiers::CONTROL,
|
|
))))),
|
|
c @ b'\x1C'..=b'\x1F' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char((c as u8 - 0x1C + b'4') as char),
|
|
KeyModifiers::CONTROL,
|
|
))))),
|
|
b'\0' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char(' '),
|
|
KeyModifiers::CONTROL,
|
|
))))),
|
|
_ => parse_utf8_char(buffer).map(|maybe_char| {
|
|
maybe_char
|
|
.map(KeyCode::Char)
|
|
.map(char_code_to_event)
|
|
.map(Event::Key)
|
|
.map(InternalEvent::Event)
|
|
}),
|
|
}
|
|
}
|
|
|
|
// converts KeyCode to KeyEvent (adds shift modifier in case of uppercase characters)
|
|
fn char_code_to_event(code: KeyCode) -> KeyEvent {
|
|
let modifiers = match code {
|
|
KeyCode::Char(c) if c.is_uppercase() => KeyModifiers::SHIFT,
|
|
_ => KeyModifiers::empty(),
|
|
};
|
|
KeyEvent::new(code, modifiers)
|
|
}
|
|
|
|
pub(crate) fn parse_csi(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
|
|
|
if buffer.len() == 2 {
|
|
return Ok(None);
|
|
}
|
|
|
|
let input_event = match buffer[2] {
|
|
b'[' => {
|
|
if buffer.len() == 3 {
|
|
None
|
|
} else {
|
|
match buffer[3] {
|
|
// NOTE (@imdaveho): cannot find when this occurs;
|
|
// having another '[' after ESC[ not a likely scenario
|
|
val @ b'A'..=b'E' => Some(Event::Key(KeyCode::F(1 + val - b'A').into())),
|
|
_ => return Err(could_not_parse_event_error()),
|
|
}
|
|
}
|
|
}
|
|
b'D' => Some(Event::Key(KeyCode::Left.into())),
|
|
b'C' => Some(Event::Key(KeyCode::Right.into())),
|
|
b'A' => Some(Event::Key(KeyCode::Up.into())),
|
|
b'B' => Some(Event::Key(KeyCode::Down.into())),
|
|
b'H' => Some(Event::Key(KeyCode::Home.into())),
|
|
b'F' => Some(Event::Key(KeyCode::End.into())),
|
|
b'Z' => Some(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::BackTab,
|
|
KeyModifiers::SHIFT,
|
|
KeyEventKind::Press,
|
|
))),
|
|
b'M' => return parse_csi_normal_mouse(buffer),
|
|
b'<' => return parse_csi_sgr_mouse(buffer),
|
|
b'I' => Some(Event::FocusGained),
|
|
b'O' => Some(Event::FocusLost),
|
|
b';' => return parse_csi_modifier_key_code(buffer),
|
|
b'0'..=b'9' => {
|
|
// Numbered escape code.
|
|
if buffer.len() == 3 {
|
|
None
|
|
} else {
|
|
// The final byte of a CSI sequence can be in the range 64-126, so
|
|
// let's keep reading anything else.
|
|
let last_byte = buffer[buffer.len() - 1];
|
|
if !(64..=126).contains(&last_byte) {
|
|
None
|
|
} else {
|
|
#[cfg(feature = "bracketed-paste")]
|
|
if buffer.starts_with(b"\x1B[200~") {
|
|
return parse_csi_bracketed_paste(buffer);
|
|
}
|
|
match last_byte {
|
|
b'M' => return parse_csi_rxvt_mouse(buffer),
|
|
b'~' => return parse_csi_special_key_code(buffer),
|
|
b'u' => return parse_csi_u_encoded_key_code(buffer),
|
|
b'R' => return parse_csi_cursor_position(buffer),
|
|
_ => return parse_csi_modifier_key_code(buffer),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => return Err(could_not_parse_event_error()),
|
|
};
|
|
|
|
Ok(input_event.map(InternalEvent::Event))
|
|
}
|
|
|
|
pub(crate) fn next_parsed<T>(iter: &mut dyn Iterator<Item = &str>) -> Result<T>
|
|
where
|
|
T: std::str::FromStr,
|
|
{
|
|
iter.next()
|
|
.ok_or_else(could_not_parse_event_error)?
|
|
.parse::<T>()
|
|
.map_err(|_| could_not_parse_event_error())
|
|
}
|
|
|
|
fn modifier_and_kind_parsed(iter: &mut dyn Iterator<Item = &str>) -> Result<(u8, u8)> {
|
|
let mut sub_split = iter
|
|
.next()
|
|
.ok_or_else(could_not_parse_event_error)?
|
|
.split(':');
|
|
|
|
let modifier_mask = next_parsed::<u8>(&mut sub_split)?;
|
|
|
|
if let Ok(kind_code) = next_parsed::<u8>(&mut sub_split) {
|
|
Ok((modifier_mask, kind_code))
|
|
} else {
|
|
Ok((modifier_mask, 1))
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_csi_cursor_position(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
// ESC [ Cy ; Cx R
|
|
// Cy - cursor row number (starting from 1)
|
|
// Cx - cursor column number (starting from 1)
|
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
|
assert!(buffer.ends_with(&[b'R']));
|
|
|
|
let s = std::str::from_utf8(&buffer[2..buffer.len() - 1])
|
|
.map_err(|_| could_not_parse_event_error())?;
|
|
|
|
let mut split = s.split(';');
|
|
|
|
let y = next_parsed::<u16>(&mut split)? - 1;
|
|
let x = next_parsed::<u16>(&mut split)? - 1;
|
|
|
|
Ok(Some(InternalEvent::CursorPosition(x, y)))
|
|
}
|
|
|
|
fn parse_modifiers(mask: u8) -> KeyModifiers {
|
|
let modifier_mask = mask.saturating_sub(1);
|
|
let mut modifiers = KeyModifiers::empty();
|
|
if modifier_mask & 1 != 0 {
|
|
modifiers |= KeyModifiers::SHIFT;
|
|
}
|
|
if modifier_mask & 2 != 0 {
|
|
modifiers |= KeyModifiers::ALT;
|
|
}
|
|
if modifier_mask & 4 != 0 {
|
|
modifiers |= KeyModifiers::CONTROL;
|
|
}
|
|
if modifier_mask & 8 != 0 {
|
|
modifiers |= KeyModifiers::SUPER;
|
|
}
|
|
if modifier_mask & 16 != 0 {
|
|
modifiers |= KeyModifiers::HYPER;
|
|
}
|
|
if modifier_mask & 32 != 0 {
|
|
modifiers |= KeyModifiers::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,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
|
//
|
|
let s = std::str::from_utf8(&buffer[2..buffer.len() - 1])
|
|
.map_err(|_| could_not_parse_event_error())?;
|
|
let mut split = s.split(';');
|
|
|
|
split.next();
|
|
|
|
let (modifiers, kind) =
|
|
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
|
|
(
|
|
parse_modifiers(modifier_mask),
|
|
parse_key_event_kind(kind_code),
|
|
)
|
|
} else if buffer.len() > 3 {
|
|
(
|
|
parse_modifiers(
|
|
(buffer[buffer.len() - 2] as char)
|
|
.to_digit(10)
|
|
.ok_or_else(could_not_parse_event_error)? as u8,
|
|
),
|
|
KeyEventKind::Press,
|
|
)
|
|
} else {
|
|
(KeyModifiers::NONE, KeyEventKind::Press)
|
|
};
|
|
let key = buffer[buffer.len() - 1];
|
|
|
|
let keycode = match key {
|
|
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'R' => KeyCode::F(3),
|
|
b'S' => KeyCode::F(4),
|
|
_ => return Err(could_not_parse_event_error()),
|
|
};
|
|
|
|
let input_event = Event::Key(KeyEvent::new_with_kind(keycode, modifiers, kind));
|
|
|
|
Ok(Some(InternalEvent::Event(input_event)))
|
|
}
|
|
|
|
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::TrackNext)),
|
|
57436 => Some(KeyCode::Media(MediaKeyCode::TrackPrevious)),
|
|
57437 => Some(KeyCode::Media(MediaKeyCode::Record)),
|
|
57438 => Some(KeyCode::Media(MediaKeyCode::LowerVolume)),
|
|
57439 => Some(KeyCode::Media(MediaKeyCode::RaiseVolume)),
|
|
57440 => Some(KeyCode::Media(MediaKeyCode::MuteVolume)),
|
|
57441 => Some(KeyCode::Modifier(ModifierKeyCode::LeftShift)),
|
|
57442 => Some(KeyCode::Modifier(ModifierKeyCode::LeftControl)),
|
|
57443 => Some(KeyCode::Modifier(ModifierKeyCode::LeftAlt)),
|
|
57444 => Some(KeyCode::Modifier(ModifierKeyCode::LeftSuper)),
|
|
57445 => Some(KeyCode::Modifier(ModifierKeyCode::LeftHyper)),
|
|
57446 => Some(KeyCode::Modifier(ModifierKeyCode::LeftMeta)),
|
|
57447 => Some(KeyCode::Modifier(ModifierKeyCode::RightShift)),
|
|
57448 => Some(KeyCode::Modifier(ModifierKeyCode::RightControl)),
|
|
57449 => Some(KeyCode::Modifier(ModifierKeyCode::RightAlt)),
|
|
57450 => Some(KeyCode::Modifier(ModifierKeyCode::RightSuper)),
|
|
57451 => Some(KeyCode::Modifier(ModifierKeyCode::RightHyper)),
|
|
57452 => Some(KeyCode::Modifier(ModifierKeyCode::RightMeta)),
|
|
57453 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift)),
|
|
57454 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift)),
|
|
_ => None,
|
|
} {
|
|
return Some((keycode, KeyEventState::empty()));
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
|
assert!(buffer.ends_with(&[b'u']));
|
|
|
|
let s = std::str::from_utf8(&buffer[2..buffer.len() - 1])
|
|
.map_err(|_| could_not_parse_event_error())?;
|
|
let mut split = s.split(';');
|
|
|
|
// This CSI sequence a tuple of semicolon-separated numbers.
|
|
// CSI [codepoint];[modifiers] u
|
|
// codepoint: ASCII Dec value
|
|
let codepoint = next_parsed::<u32>(&mut split)?;
|
|
|
|
let (mut modifiers, kind, state_from_modifiers) =
|
|
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
|
|
(
|
|
parse_modifiers(modifier_mask),
|
|
parse_key_event_kind(kind_code),
|
|
parse_modifiers_to_state(modifier_mask),
|
|
)
|
|
} else {
|
|
(KeyModifiers::NONE, KeyEventKind::Press, KeyEventState::NONE)
|
|
};
|
|
|
|
let (keycode, state_from_keycode) = {
|
|
if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) {
|
|
(special_key_code, state)
|
|
} else if let Some(c) = char::from_u32(codepoint) {
|
|
(
|
|
match c {
|
|
'\x1B' => KeyCode::Esc,
|
|
'\r' => KeyCode::Enter,
|
|
// Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get
|
|
// newlines as input is because the terminal converts \r into \n for us. When we
|
|
// enter raw mode, we disable that, so \n no longer has any meaning - it's better to
|
|
// use Ctrl+J. Waiting to handle it here means it gets picked up later
|
|
'\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter,
|
|
'\t' => {
|
|
if modifiers.contains(KeyModifiers::SHIFT) {
|
|
KeyCode::BackTab
|
|
} else {
|
|
KeyCode::Tab
|
|
}
|
|
}
|
|
'\x7F' => KeyCode::Backspace,
|
|
_ => KeyCode::Char(c),
|
|
},
|
|
KeyEventState::empty(),
|
|
)
|
|
} else {
|
|
return Err(could_not_parse_event_error());
|
|
}
|
|
};
|
|
|
|
if let KeyCode::Modifier(modifier_keycode) = keycode {
|
|
match modifier_keycode {
|
|
ModifierKeyCode::LeftAlt | ModifierKeyCode::RightAlt => {
|
|
modifiers.set(KeyModifiers::ALT, true)
|
|
}
|
|
ModifierKeyCode::LeftControl | ModifierKeyCode::RightControl => {
|
|
modifiers.set(KeyModifiers::CONTROL, true)
|
|
}
|
|
ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => {
|
|
modifiers.set(KeyModifiers::SHIFT, true)
|
|
}
|
|
ModifierKeyCode::LeftSuper | ModifierKeyCode::RightSuper => {
|
|
modifiers.set(KeyModifiers::SUPER, true)
|
|
}
|
|
ModifierKeyCode::LeftHyper | ModifierKeyCode::RightHyper => {
|
|
modifiers.set(KeyModifiers::HYPER, true)
|
|
}
|
|
ModifierKeyCode::LeftMeta | ModifierKeyCode::RightMeta => {
|
|
modifiers.set(KeyModifiers::META, true)
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
let input_event = Event::Key(KeyEvent::new_with_kind_and_state(
|
|
keycode,
|
|
modifiers,
|
|
kind,
|
|
state_from_keycode | state_from_modifiers,
|
|
));
|
|
|
|
Ok(Some(InternalEvent::Event(input_event)))
|
|
}
|
|
|
|
pub(crate) fn parse_csi_special_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
|
assert!(buffer.ends_with(&[b'~']));
|
|
|
|
let s = std::str::from_utf8(&buffer[2..buffer.len() - 1])
|
|
.map_err(|_| could_not_parse_event_error())?;
|
|
let mut split = s.split(';');
|
|
|
|
// This CSI sequence can be a list of semicolon-separated numbers.
|
|
let first = next_parsed::<u8>(&mut split)?;
|
|
|
|
let modifiers = if let Ok(modifier_mask) = next_parsed::<u8>(&mut split) {
|
|
parse_modifiers(modifier_mask)
|
|
} else {
|
|
KeyModifiers::NONE
|
|
};
|
|
|
|
let keycode = match first {
|
|
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),
|
|
_ => return Err(could_not_parse_event_error()),
|
|
};
|
|
|
|
let input_event = Event::Key(KeyEvent::new(keycode, modifiers));
|
|
|
|
Ok(Some(InternalEvent::Event(input_event)))
|
|
}
|
|
|
|
pub(crate) fn parse_csi_rxvt_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
// rxvt mouse encoding:
|
|
// ESC [ Cb ; Cx ; Cy ; M
|
|
|
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
|
assert!(buffer.ends_with(&[b'M']));
|
|
|
|
let s = std::str::from_utf8(&buffer[2..buffer.len() - 1])
|
|
.map_err(|_| could_not_parse_event_error())?;
|
|
let mut split = s.split(';');
|
|
|
|
let cb = next_parsed::<u8>(&mut split)?
|
|
.checked_sub(32)
|
|
.ok_or_else(could_not_parse_event_error)?;
|
|
let (kind, modifiers) = parse_cb(cb)?;
|
|
|
|
let cx = next_parsed::<u16>(&mut split)? - 1;
|
|
let cy = next_parsed::<u16>(&mut split)? - 1;
|
|
|
|
Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind,
|
|
column: cx,
|
|
row: cy,
|
|
modifiers,
|
|
}))))
|
|
}
|
|
|
|
pub(crate) fn parse_csi_normal_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
// Normal mouse encoding: ESC [ M CB Cx Cy (6 characters only).
|
|
|
|
assert!(buffer.starts_with(&[b'\x1B', b'[', b'M'])); // ESC [ M
|
|
|
|
if buffer.len() < 6 {
|
|
return Ok(None);
|
|
}
|
|
|
|
let cb = buffer[3]
|
|
.checked_sub(32)
|
|
.ok_or_else(could_not_parse_event_error)?;
|
|
let (kind, modifiers) = parse_cb(cb)?;
|
|
|
|
// See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
|
// The upper left character position on the terminal is denoted as 1,1.
|
|
// Subtract 1 to keep it synced with cursor
|
|
let cx = u16::from(buffer[4].saturating_sub(32)) - 1;
|
|
let cy = u16::from(buffer[5].saturating_sub(32)) - 1;
|
|
|
|
Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind,
|
|
column: cx,
|
|
row: cy,
|
|
modifiers,
|
|
}))))
|
|
}
|
|
|
|
pub(crate) fn parse_csi_sgr_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
// ESC [ < Cb ; Cx ; Cy (;) (M or m)
|
|
|
|
assert!(buffer.starts_with(&[b'\x1B', b'[', b'<'])); // ESC [ <
|
|
|
|
if !buffer.ends_with(&[b'm']) && !buffer.ends_with(&[b'M']) {
|
|
return Ok(None);
|
|
}
|
|
|
|
let s = std::str::from_utf8(&buffer[3..buffer.len() - 1])
|
|
.map_err(|_| could_not_parse_event_error())?;
|
|
let mut split = s.split(';');
|
|
|
|
let cb = next_parsed::<u8>(&mut split)?;
|
|
let (kind, modifiers) = parse_cb(cb)?;
|
|
|
|
// See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
|
// The upper left character position on the terminal is denoted as 1,1.
|
|
// Subtract 1 to keep it synced with cursor
|
|
let cx = next_parsed::<u16>(&mut split)? - 1;
|
|
let cy = next_parsed::<u16>(&mut split)? - 1;
|
|
|
|
// When button 3 in Cb is used to represent mouse release, you can't tell which button was
|
|
// released. SGR mode solves this by having the sequence end with a lowercase m if it's a
|
|
// button release and an uppercase M if it's a button press.
|
|
//
|
|
// We've already checked that the last character is a lowercase or uppercase M at the start of
|
|
// this function, so we just need one if.
|
|
let kind = if buffer.last() == Some(&b'm') {
|
|
match kind {
|
|
MouseEventKind::Down(button) => MouseEventKind::Up(button),
|
|
other => other,
|
|
}
|
|
} else {
|
|
kind
|
|
};
|
|
|
|
Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind,
|
|
column: cx,
|
|
row: cy,
|
|
modifiers,
|
|
}))))
|
|
}
|
|
|
|
/// Cb is the byte of a mouse input that contains the button being used, the key modifiers being
|
|
/// held and whether the mouse is dragging or not.
|
|
///
|
|
/// Bit layout of cb, from low to high:
|
|
///
|
|
/// - button number
|
|
/// - button number
|
|
/// - shift
|
|
/// - meta (alt)
|
|
/// - control
|
|
/// - mouse is dragging
|
|
/// - button number
|
|
/// - button number
|
|
fn parse_cb(cb: u8) -> Result<(MouseEventKind, KeyModifiers)> {
|
|
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,
|
|
// We do not support other buttons.
|
|
_ => return Err(could_not_parse_event_error()),
|
|
};
|
|
|
|
let mut modifiers = KeyModifiers::empty();
|
|
|
|
if cb & 0b0000_0100 == 0b0000_0100 {
|
|
modifiers |= KeyModifiers::SHIFT;
|
|
}
|
|
if cb & 0b0000_1000 == 0b0000_1000 {
|
|
modifiers |= KeyModifiers::ALT;
|
|
}
|
|
if cb & 0b0001_0000 == 0b0001_0000 {
|
|
modifiers |= KeyModifiers::CONTROL;
|
|
}
|
|
|
|
Ok((kind, modifiers))
|
|
}
|
|
|
|
#[cfg(feature = "bracketed-paste")]
|
|
pub(crate) fn parse_csi_bracketed_paste(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|
// ESC [ 2 0 0 ~ pasted text ESC 2 0 1 ~
|
|
assert!(buffer.starts_with(b"\x1B[200~"));
|
|
|
|
if !buffer.ends_with(b"\x1b[201~") {
|
|
Ok(None)
|
|
} else {
|
|
let paste = String::from_utf8_lossy(&buffer[6..buffer.len() - 6]).to_string();
|
|
Ok(Some(InternalEvent::Event(Event::Paste(paste))))
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_utf8_char(buffer: &[u8]) -> Result<Option<char>> {
|
|
match std::str::from_utf8(buffer) {
|
|
Ok(s) => {
|
|
let ch = s.chars().next().ok_or_else(could_not_parse_event_error)?;
|
|
|
|
Ok(Some(ch))
|
|
}
|
|
Err(_) => {
|
|
// from_utf8 failed, but we have to check if we need more bytes for code point
|
|
// and if all the bytes we have no are valid
|
|
|
|
let required_bytes = match buffer[0] {
|
|
// https://en.wikipedia.org/wiki/UTF-8#Description
|
|
(0x00..=0x7F) => 1, // 0xxxxxxx
|
|
(0xC0..=0xDF) => 2, // 110xxxxx 10xxxxxx
|
|
(0xE0..=0xEF) => 3, // 1110xxxx 10xxxxxx 10xxxxxx
|
|
(0xF0..=0xF7) => 4, // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
|
(0x80..=0xBF) | (0xF8..=0xFF) => return Err(could_not_parse_event_error()),
|
|
};
|
|
|
|
// More than 1 byte, check them for 10xxxxxx pattern
|
|
if required_bytes > 1 && buffer.len() > 1 {
|
|
for byte in &buffer[1..] {
|
|
if byte & !0b0011_1111 != 0b1000_0000 {
|
|
return Err(could_not_parse_event_error());
|
|
}
|
|
}
|
|
}
|
|
|
|
if buffer.len() < required_bytes {
|
|
// All bytes looks good so far, but we need more of them
|
|
Ok(None)
|
|
} else {
|
|
Err(could_not_parse_event_error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::event::{KeyEventState, KeyModifiers, MouseButton, MouseEvent};
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_esc_key() {
|
|
assert_eq!(
|
|
parse_event(b"\x1B", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into()))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_possible_esc_sequence() {
|
|
assert_eq!(parse_event(b"\x1B", true).unwrap(), None,);
|
|
}
|
|
|
|
#[test]
|
|
fn test_alt_key() {
|
|
assert_eq!(
|
|
parse_event(b"\x1Bc", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('c'),
|
|
KeyModifiers::ALT
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_alt_shift() {
|
|
assert_eq!(
|
|
parse_event(b"\x1BH", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('H'),
|
|
KeyModifiers::ALT | KeyModifiers::SHIFT
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_alt_ctrl() {
|
|
assert_eq!(
|
|
parse_event(b"\x1B\x14", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('t'),
|
|
KeyModifiers::ALT | KeyModifiers::CONTROL
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_event_subsequent_calls() {
|
|
// The main purpose of this test is to check if we're passing
|
|
// correct slice to other parse_ functions.
|
|
|
|
// parse_csi_cursor_position
|
|
assert_eq!(
|
|
parse_event(b"\x1B[20;10R", false).unwrap(),
|
|
Some(InternalEvent::CursorPosition(9, 19))
|
|
);
|
|
|
|
// parse_csi
|
|
assert_eq!(
|
|
parse_event(b"\x1B[D", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))),
|
|
);
|
|
|
|
// parse_csi_modifier_key_code
|
|
assert_eq!(
|
|
parse_event(b"\x1B[2D", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Left,
|
|
KeyModifiers::SHIFT
|
|
))))
|
|
);
|
|
|
|
// parse_csi_special_key_code
|
|
assert_eq!(
|
|
parse_event(b"\x1B[3~", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))),
|
|
);
|
|
|
|
// parse_csi_bracketed_paste
|
|
#[cfg(feature = "bracketed-paste")]
|
|
assert_eq!(
|
|
parse_event(b"\x1B[200~on and on and on\x1B[201~", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Paste(
|
|
"on and on and on".to_string()
|
|
))),
|
|
);
|
|
|
|
// parse_csi_rxvt_mouse
|
|
assert_eq!(
|
|
parse_event(b"\x1B[32;30;40;M", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Down(MouseButton::Left),
|
|
column: 29,
|
|
row: 39,
|
|
modifiers: KeyModifiers::empty(),
|
|
})))
|
|
);
|
|
|
|
// parse_csi_normal_mouse
|
|
assert_eq!(
|
|
parse_event(b"\x1B[M0\x60\x70", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Down(MouseButton::Left),
|
|
column: 63,
|
|
row: 79,
|
|
modifiers: KeyModifiers::CONTROL,
|
|
})))
|
|
);
|
|
|
|
// parse_csi_sgr_mouse
|
|
assert_eq!(
|
|
parse_event(b"\x1B[<0;20;10;M", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Down(MouseButton::Left),
|
|
column: 19,
|
|
row: 9,
|
|
modifiers: KeyModifiers::empty(),
|
|
})))
|
|
);
|
|
|
|
// parse_utf8_char
|
|
assert_eq!(
|
|
parse_event("Ž".as_bytes(), false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('Ž'),
|
|
KeyModifiers::SHIFT
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_event() {
|
|
assert_eq!(
|
|
parse_event(b"\t", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into()))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_cursor_position() {
|
|
assert_eq!(
|
|
parse_csi_cursor_position(b"\x1B[20;10R").unwrap(),
|
|
Some(InternalEvent::CursorPosition(9, 19))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi() {
|
|
assert_eq!(
|
|
parse_csi(b"\x1B[D").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_modifier_key_code() {
|
|
assert_eq!(
|
|
parse_csi_modifier_key_code(b"\x1B[2D").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Left,
|
|
KeyModifiers::SHIFT
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_special_key_code() {
|
|
assert_eq!(
|
|
parse_csi_special_key_code(b"\x1B[3~").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_special_key_code_multiple_values_not_supported() {
|
|
assert_eq!(
|
|
parse_csi_special_key_code(b"\x1B[3;2~").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Delete,
|
|
KeyModifiers::SHIFT
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[cfg(feature = "bracketed-paste")]
|
|
#[test]
|
|
fn test_parse_csi_bracketed_paste() {
|
|
//
|
|
assert_eq!(
|
|
parse_event(b"\x1B[200~o", false).unwrap(),
|
|
None,
|
|
"A partial bracketed paste isn't parsed"
|
|
);
|
|
assert_eq!(
|
|
parse_event(b"\x1B[200~o\x1B[2D", false).unwrap(),
|
|
None,
|
|
"A partial bracketed paste containing another escape code isn't parsed"
|
|
);
|
|
assert_eq!(
|
|
parse_event(b"\x1B[200~o\x1B[2D\x1B[201~", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Paste("o\x1B[2D".to_string())))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_focus() {
|
|
assert_eq!(
|
|
parse_csi(b"\x1B[O").unwrap(),
|
|
Some(InternalEvent::Event(Event::FocusLost))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_rxvt_mouse() {
|
|
assert_eq!(
|
|
parse_csi_rxvt_mouse(b"\x1B[32;30;40;M").unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Down(MouseButton::Left),
|
|
column: 29,
|
|
row: 39,
|
|
modifiers: KeyModifiers::empty(),
|
|
})))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_normal_mouse() {
|
|
assert_eq!(
|
|
parse_csi_normal_mouse(b"\x1B[M0\x60\x70").unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Down(MouseButton::Left),
|
|
column: 63,
|
|
row: 79,
|
|
modifiers: KeyModifiers::CONTROL,
|
|
})))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_sgr_mouse() {
|
|
assert_eq!(
|
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10;M").unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Down(MouseButton::Left),
|
|
column: 19,
|
|
row: 9,
|
|
modifiers: KeyModifiers::empty(),
|
|
})))
|
|
);
|
|
assert_eq!(
|
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10M").unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Down(MouseButton::Left),
|
|
column: 19,
|
|
row: 9,
|
|
modifiers: KeyModifiers::empty(),
|
|
})))
|
|
);
|
|
assert_eq!(
|
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10;m").unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Up(MouseButton::Left),
|
|
column: 19,
|
|
row: 9,
|
|
modifiers: KeyModifiers::empty(),
|
|
})))
|
|
);
|
|
assert_eq!(
|
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10m").unwrap(),
|
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
|
kind: MouseEventKind::Up(MouseButton::Left),
|
|
column: 19,
|
|
row: 9,
|
|
modifiers: KeyModifiers::empty(),
|
|
})))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_utf8() {
|
|
// https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805
|
|
|
|
// 'Valid ASCII' => "a",
|
|
assert_eq!(parse_utf8_char(b"a").unwrap(), Some('a'),);
|
|
|
|
// 'Valid 2 Octet Sequence' => "\xc3\xb1",
|
|
assert_eq!(parse_utf8_char(&[0xC3, 0xB1]).unwrap(), Some('ñ'),);
|
|
|
|
// 'Invalid 2 Octet Sequence' => "\xc3\x28",
|
|
assert!(parse_utf8_char(&[0xC3, 0x28]).is_err());
|
|
|
|
// 'Invalid Sequence Identifier' => "\xa0\xa1",
|
|
assert!(parse_utf8_char(&[0xA0, 0xA1]).is_err());
|
|
|
|
// 'Valid 3 Octet Sequence' => "\xe2\x82\xa1",
|
|
assert_eq!(
|
|
parse_utf8_char(&[0xE2, 0x81, 0xA1]).unwrap(),
|
|
Some('\u{2061}'),
|
|
);
|
|
|
|
// 'Invalid 3 Octet Sequence (in 2nd Octet)' => "\xe2\x28\xa1",
|
|
assert!(parse_utf8_char(&[0xE2, 0x28, 0xA1]).is_err());
|
|
|
|
// 'Invalid 3 Octet Sequence (in 3rd Octet)' => "\xe2\x82\x28",
|
|
assert!(parse_utf8_char(&[0xE2, 0x82, 0x28]).is_err());
|
|
|
|
// 'Valid 4 Octet Sequence' => "\xf0\x90\x8c\xbc",
|
|
assert_eq!(
|
|
parse_utf8_char(&[0xF0, 0x90, 0x8C, 0xBC]).unwrap(),
|
|
Some('𐌼'),
|
|
);
|
|
|
|
// 'Invalid 4 Octet Sequence (in 2nd Octet)' => "\xf0\x28\x8c\xbc",
|
|
assert!(parse_utf8_char(&[0xF0, 0x28, 0x8C, 0xBC]).is_err());
|
|
|
|
// 'Invalid 4 Octet Sequence (in 3rd Octet)' => "\xf0\x90\x28\xbc",
|
|
assert!(parse_utf8_char(&[0xF0, 0x90, 0x28, 0xBC]).is_err());
|
|
|
|
// 'Invalid 4 Octet Sequence (in 4th Octet)' => "\xf0\x28\x8c\x28",
|
|
assert!(parse_utf8_char(&[0xF0, 0x28, 0x8C, 0x28]).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_char_event_lowercase() {
|
|
assert_eq!(
|
|
parse_event(b"c", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('c'),
|
|
KeyModifiers::empty()
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_char_event_uppercase() {
|
|
assert_eq!(
|
|
parse_event(b"C", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('C'),
|
|
KeyModifiers::SHIFT
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_basic_csi_u_encoded_key_code() {
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::empty()
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;2u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('A'),
|
|
KeyModifiers::SHIFT
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;7u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::ALT | KeyModifiers::CONTROL
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_basic_csi_u_encoded_key_code_special_keys() {
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[13u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Enter,
|
|
KeyModifiers::empty()
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[27u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Esc,
|
|
KeyModifiers::empty()
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57358u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::CapsLock,
|
|
KeyModifiers::empty()
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57376u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::F(13),
|
|
KeyModifiers::empty()
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57428u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Media(MediaKeyCode::Play),
|
|
KeyModifiers::empty()
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57441u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Modifier(ModifierKeyCode::LeftShift),
|
|
KeyModifiers::SHIFT,
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_u_encoded_keypad_code() {
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57399u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(
|
|
KeyEvent::new_with_kind_and_state(
|
|
KeyCode::Char('0'),
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Press,
|
|
KeyEventState::KEYPAD,
|
|
)
|
|
))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57419u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(
|
|
KeyEvent::new_with_kind_and_state(
|
|
KeyCode::Up,
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Press,
|
|
KeyEventState::KEYPAD,
|
|
)
|
|
))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_u_encoded_key_code_with_types() {
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;1u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Press,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;1:1u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Press,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;5:1u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::CONTROL,
|
|
KeyEventKind::Press,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;1:2u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Repeat,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;1:3u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Release,
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_u_encoded_key_code_has_modifier_on_modifier_press() {
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57449u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Modifier(ModifierKeyCode::RightAlt),
|
|
KeyModifiers::ALT,
|
|
KeyEventKind::Press,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57449;3:3u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Modifier(ModifierKeyCode::RightAlt),
|
|
KeyModifiers::ALT,
|
|
KeyEventKind::Release,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57450u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Modifier(ModifierKeyCode::RightSuper),
|
|
KeyModifiers::SUPER,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57451u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Modifier(ModifierKeyCode::RightHyper),
|
|
KeyModifiers::HYPER,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[57452u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Modifier(ModifierKeyCode::RightMeta),
|
|
KeyModifiers::META,
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_u_encoded_key_code_with_extra_modifiers() {
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;9u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::SUPER
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;17u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::HYPER,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;33u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::META,
|
|
)))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_u_encoded_key_code_with_extra_state() {
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[97;65u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(
|
|
KeyEvent::new_with_kind_and_state(
|
|
KeyCode::Char('a'),
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Press,
|
|
KeyEventState::CAPS_LOCK,
|
|
)
|
|
))),
|
|
);
|
|
assert_eq!(
|
|
parse_csi_u_encoded_key_code(b"\x1B[49;129u").unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(
|
|
KeyEvent::new_with_kind_and_state(
|
|
KeyCode::Char('1'),
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Press,
|
|
KeyEventState::NUM_LOCK,
|
|
)
|
|
))),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_csi_special_key_code_with_types() {
|
|
assert_eq!(
|
|
parse_event(b"\x1B[;1:3B", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Down,
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Release,
|
|
)))),
|
|
);
|
|
assert_eq!(
|
|
parse_event(b"\x1B[1;1:3B", false).unwrap(),
|
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
|
KeyCode::Down,
|
|
KeyModifiers::empty(),
|
|
KeyEventKind::Release,
|
|
)))),
|
|
);
|
|
}
|
|
}
|