Increase support for kitty enhanced keyboard protocol (#688)
This commit is contained in:
parent
4dcc6fc058
commit
60e51be726
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! cargo run --example event-match-modifiers
|
//! cargo run --example event-match-modifiers
|
||||||
|
|
||||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||||
|
|
||||||
fn match_event(read_event: Event) {
|
fn match_event(read_event: Event) {
|
||||||
match read_event {
|
match read_event {
|
||||||
@ -10,24 +10,29 @@ fn match_event(read_event: Event) {
|
|||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
code,
|
code,
|
||||||
|
..
|
||||||
}) => {
|
}) => {
|
||||||
println!("Control + {:?}", code);
|
println!("Control + {:?}", code);
|
||||||
}
|
}
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::SHIFT,
|
modifiers: KeyModifiers::SHIFT,
|
||||||
code,
|
code,
|
||||||
|
..
|
||||||
}) => {
|
}) => {
|
||||||
println!("Shift + {:?}", code);
|
println!("Shift + {:?}", code);
|
||||||
}
|
}
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
code,
|
code,
|
||||||
|
..
|
||||||
}) => {
|
}) => {
|
||||||
println!("Alt + {:?}", code);
|
println!("Alt + {:?}", code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match on multiple modifiers:
|
// Match on multiple modifiers:
|
||||||
Event::Key(KeyEvent { code, modifiers }) => {
|
Event::Key(KeyEvent {
|
||||||
|
code, modifiers, ..
|
||||||
|
}) => {
|
||||||
if modifiers == (KeyModifiers::ALT | KeyModifiers::SHIFT) {
|
if modifiers == (KeyModifiers::ALT | KeyModifiers::SHIFT) {
|
||||||
println!("Alt + Shift {:?}", code);
|
println!("Alt + Shift {:?}", code);
|
||||||
} else {
|
} else {
|
||||||
@ -43,21 +48,26 @@ fn main() {
|
|||||||
match_event(Event::Key(KeyEvent {
|
match_event(Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
code: KeyCode::Char('z'),
|
code: KeyCode::Char('z'),
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
}));
|
}));
|
||||||
match_event(Event::Key(KeyEvent {
|
match_event(Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::SHIFT,
|
modifiers: KeyModifiers::SHIFT,
|
||||||
code: KeyCode::Left,
|
code: KeyCode::Left,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
}));
|
}));
|
||||||
match_event(Event::Key(KeyEvent {
|
match_event(Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
code: KeyCode::Delete,
|
code: KeyCode::Delete,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
}));
|
}));
|
||||||
match_event(Event::Key(KeyEvent {
|
match_event(Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::ALT | KeyModifiers::SHIFT,
|
modifiers: KeyModifiers::ALT | KeyModifiers::SHIFT,
|
||||||
code: KeyCode::Right,
|
code: KeyCode::Right,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
}));
|
}));
|
||||||
match_event(Event::Key(KeyEvent {
|
match_event(Event::Key(KeyEvent {
|
||||||
modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL,
|
||||||
code: KeyCode::Home,
|
code: KeyCode::Home,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
155
src/event.rs
155
src/event.rs
@ -294,6 +294,120 @@ impl Command for DisableMouseCapture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Represents special flags that tell compatible terminals to add extra information to keyboard events.
|
||||||
|
///
|
||||||
|
/// See <https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement> for more information.
|
||||||
|
///
|
||||||
|
/// Alternate keys and Unicode codepoints are not yet supported by crossterm.
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct KeyboardEnhancementFlags: u8 {
|
||||||
|
/// Represent Escape and modified keys using CSI-u sequences, so they can be unambiguously
|
||||||
|
/// read.
|
||||||
|
const DISAMBIGUATE_ESCAPE_CODES = 0b0000_0001;
|
||||||
|
/// Add extra events with [`KeyEvent.kind`] set to [`KeyEventKind::Repeat`] or
|
||||||
|
/// [`KeyEventKind::Release`] when keys are autorepeated or released.
|
||||||
|
const REPORT_EVENT_TYPES = 0b0000_0010;
|
||||||
|
// Send [alternate keycodes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-codes)
|
||||||
|
// in addition to the base keycode.
|
||||||
|
//
|
||||||
|
// *Note*: these are not yet supported by crossterm.
|
||||||
|
// const REPORT_ALTERNATE_KEYS = 0b0000_0100;
|
||||||
|
/// Represent all keyboard events as CSI-u sequences. This is required to get repeat/release
|
||||||
|
/// events for plain-text keys.
|
||||||
|
const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 0b0000_1000;
|
||||||
|
// Send the Unicode codepoint as well as the keycode.
|
||||||
|
//
|
||||||
|
// *Note*: this is not yet supported by crossterm.
|
||||||
|
// const REPORT_ASSOCIATED_TEXT = 0b0001_0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A command that enables the [kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/), which adds extra information to keyboard events and removes ambiguity for modifier keys.
|
||||||
|
///
|
||||||
|
/// It should be paired with [`PopKeyboardEnhancementFlags`] at the end of execution.
|
||||||
|
///
|
||||||
|
/// Example usage:
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::{Write, stdout};
|
||||||
|
/// use crossterm::execute;
|
||||||
|
/// use crossterm::event::{
|
||||||
|
/// KeyboardEnhancementFlags,
|
||||||
|
/// PushKeyboardEnhancementFlags,
|
||||||
|
/// PopKeyboardEnhancementFlags
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// let mut stdout = stdout();
|
||||||
|
///
|
||||||
|
/// execute!(
|
||||||
|
/// stdout,
|
||||||
|
/// PushKeyboardEnhancementFlags(
|
||||||
|
/// KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||||
|
/// )
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // ...
|
||||||
|
///
|
||||||
|
/// execute!(stdout, PopKeyboardEnhancementFlags);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note that, currently, only the following support this protocol:
|
||||||
|
/// * [kitty terminal](https://sw.kovidgoyal.net/kitty/)
|
||||||
|
/// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319)
|
||||||
|
/// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html)
|
||||||
|
/// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131)
|
||||||
|
/// * [neovim text editor](https://github.com/neovim/neovim/pull/18181)
|
||||||
|
/// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103)
|
||||||
|
/// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138)
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct PushKeyboardEnhancementFlags(pub KeyboardEnhancementFlags);
|
||||||
|
|
||||||
|
impl Command for PushKeyboardEnhancementFlags {
|
||||||
|
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
||||||
|
write!(f, "{}{}u", csi!(">"), self.0.bits())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn execute_winapi(&self) -> Result<()> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Unsupported,
|
||||||
|
"Keyboard progressive enhancement not implemented on Windows.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn is_ansi_code_supported(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A command that disables extra kinds of keyboard events.
|
||||||
|
///
|
||||||
|
/// Specifically, it pops one level of keyboard enhancement flags.
|
||||||
|
///
|
||||||
|
/// See [`PushKeyboardEnhancementFlags`] and <https://sw.kovidgoyal.net/kitty/keyboard-protocol/> for more information.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct PopKeyboardEnhancementFlags;
|
||||||
|
|
||||||
|
impl Command for PopKeyboardEnhancementFlags {
|
||||||
|
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
||||||
|
f.write_str(csi!("<1u"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn execute_winapi(&self) -> Result<()> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Unsupported,
|
||||||
|
"Keyboard progressive enhancement not implemented on Windows.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn is_ansi_code_supported(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents an event.
|
/// Represents an event.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
@ -384,6 +498,15 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a keyboard event kind.
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
pub enum KeyEventKind {
|
||||||
|
Press,
|
||||||
|
Repeat,
|
||||||
|
Release,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a key event.
|
/// Represents a key event.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, PartialOrd, Clone, Copy)]
|
#[derive(Debug, PartialOrd, Clone, Copy)]
|
||||||
@ -392,11 +515,29 @@ pub struct KeyEvent {
|
|||||||
pub code: KeyCode,
|
pub code: KeyCode,
|
||||||
/// Additional key modifiers.
|
/// Additional key modifiers.
|
||||||
pub modifiers: KeyModifiers,
|
pub modifiers: KeyModifiers,
|
||||||
|
/// Kind of event.
|
||||||
|
pub kind: KeyEventKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyEvent {
|
impl KeyEvent {
|
||||||
pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent {
|
pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent {
|
||||||
KeyEvent { code, modifiers }
|
KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn new_with_kind(
|
||||||
|
code: KeyCode,
|
||||||
|
modifiers: KeyModifiers,
|
||||||
|
kind: KeyEventKind,
|
||||||
|
) -> KeyEvent {
|
||||||
|
KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// modifies the KeyEvent,
|
// modifies the KeyEvent,
|
||||||
@ -422,6 +563,7 @@ impl From<KeyCode> for KeyEvent {
|
|||||||
KeyEvent {
|
KeyEvent {
|
||||||
code,
|
code,
|
||||||
modifiers: KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,12 +573,14 @@ impl PartialEq for KeyEvent {
|
|||||||
let KeyEvent {
|
let KeyEvent {
|
||||||
code: lhs_code,
|
code: lhs_code,
|
||||||
modifiers: lhs_modifiers,
|
modifiers: lhs_modifiers,
|
||||||
|
kind: lhs_kind,
|
||||||
} = self.normalize_case();
|
} = self.normalize_case();
|
||||||
let KeyEvent {
|
let KeyEvent {
|
||||||
code: rhs_code,
|
code: rhs_code,
|
||||||
modifiers: rhs_modifiers,
|
modifiers: rhs_modifiers,
|
||||||
|
kind: rhs_kind,
|
||||||
} = other.normalize_case();
|
} = other.normalize_case();
|
||||||
(lhs_code == rhs_code) && (lhs_modifiers == rhs_modifiers)
|
(lhs_code == rhs_code) && (lhs_modifiers == rhs_modifiers) && (lhs_kind == rhs_kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,9 +588,14 @@ impl Eq for KeyEvent {}
|
|||||||
|
|
||||||
impl Hash for KeyEvent {
|
impl Hash for KeyEvent {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
let KeyEvent { code, modifiers } = self.normalize_case();
|
let KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind,
|
||||||
|
} = self.normalize_case();
|
||||||
code.hash(state);
|
code.hash(state);
|
||||||
modifiers.hash(state);
|
modifiers.hash(state);
|
||||||
|
kind.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind},
|
event::{
|
||||||
|
Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEvent,
|
||||||
|
MouseEventKind,
|
||||||
|
},
|
||||||
ErrorKind, Result,
|
ErrorKind, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,6 +163,7 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|||||||
b'Z' => Some(Event::Key(KeyEvent {
|
b'Z' => Some(Event::Key(KeyEvent {
|
||||||
code: KeyCode::BackTab,
|
code: KeyCode::BackTab,
|
||||||
modifiers: KeyModifiers::SHIFT,
|
modifiers: KeyModifiers::SHIFT,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
})),
|
})),
|
||||||
b'M' => return parse_csi_normal_mouse(buffer),
|
b'M' => return parse_csi_normal_mouse(buffer),
|
||||||
b'<' => return parse_csi_sgr_mouse(buffer),
|
b'<' => return parse_csi_sgr_mouse(buffer),
|
||||||
@ -200,6 +204,21 @@ where
|
|||||||
.map_err(|_| could_not_parse_event_error())
|
.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>> {
|
pub(crate) fn parse_csi_cursor_position(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
||||||
// ESC [ Cy ; Cx R
|
// ESC [ Cy ; Cx R
|
||||||
// Cy - cursor row number (starting from 1)
|
// Cy - cursor row number (starting from 1)
|
||||||
@ -233,6 +252,15 @@ fn parse_modifiers(mask: u8) -> KeyModifiers {
|
|||||||
modifiers
|
modifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>> {
|
pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
||||||
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
||||||
|
|
||||||
@ -273,10 +301,14 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
|
|||||||
// codepoint: ASCII Dec value
|
// codepoint: ASCII Dec value
|
||||||
let codepoint = next_parsed::<u32>(&mut split)?;
|
let codepoint = next_parsed::<u32>(&mut split)?;
|
||||||
|
|
||||||
let modifiers = if let Ok(modifier_mask) = next_parsed::<u8>(&mut split) {
|
let (modifiers, kind) =
|
||||||
parse_modifiers(modifier_mask)
|
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
|
||||||
|
(
|
||||||
|
parse_modifiers(modifier_mask),
|
||||||
|
parse_key_event_kind(kind_code),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
KeyModifiers::NONE
|
(KeyModifiers::NONE, KeyEventKind::Press)
|
||||||
};
|
};
|
||||||
|
|
||||||
let keycode = {
|
let keycode = {
|
||||||
@ -304,7 +336,7 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let input_event = Event::Key(KeyEvent::new(keycode, modifiers));
|
let input_event = Event::Key(KeyEvent::new_with_kind(keycode, modifiers, kind));
|
||||||
|
|
||||||
Ok(Some(InternalEvent::Event(input_event)))
|
Ok(Some(InternalEvent::Event(input_event)))
|
||||||
}
|
}
|
||||||
@ -844,4 +876,91 @@ mod tests {
|
|||||||
)))),
|
)))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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()
|
||||||
|
)))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
)))),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,9 @@
|
|||||||
//! - Shape -
|
//! - Shape -
|
||||||
//! [`SetCursorShape`](cursor/struct.SetCursorShape.html)
|
//! [`SetCursorShape`](cursor/struct.SetCursorShape.html)
|
||||||
//! - Module [`event`](event/index.html)
|
//! - Module [`event`](event/index.html)
|
||||||
|
//! - Keyboard events -
|
||||||
|
//! [`PushKeyboardEnhancementFlags`](event/struct.PushKeyboardEnhancementFlags.html),
|
||||||
|
//! [`PopKeyboardEnhancementFlags`](event/struct.PopKeyboardEnhancementFlags.html)
|
||||||
//! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html),
|
//! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html),
|
||||||
//! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html)
|
//! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html)
|
||||||
//! - Module [`style`](style/index.html)
|
//! - Module [`style`](style/index.html)
|
||||||
|
Loading…
Reference in New Issue
Block a user