mirror of
https://github.com/wez/wezterm.git
synced 2024-11-26 08:25:50 +03:00
kitty keeb: move encoding logic to wezterm-input-types
We need access to the underlying raw/physical key in order to correctly encode in some modes, so we need the full KeyEvent struct for that. Move the encoder up so it sits alongside the win32 input mode encoder. This should give us better results for both shifted/unshifted and the "base layout" (US english) representations of a number of keys. Note that this is still not 100% technically correct: the unshifted keys require knowledge of the keyboard layout that we don't have at this OS-independent layer. Right now we're assuming a US layout to unshift punctuation, which is not right if you're not using that layout. To resolve that, more work is needed on each OS to be able to extract that information and then to store it in the KeyEvent. refs: https://github.com/wez/wezterm/issues/3479 refs: https://github.com/wez/wezterm/issues/2546
This commit is contained in:
parent
54a65a5401
commit
e241ea58be
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5092,6 +5092,7 @@ dependencies = [
|
||||
"wezterm-blob-leases",
|
||||
"wezterm-color-types",
|
||||
"wezterm-dynamic",
|
||||
"wezterm-input-types",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -46,6 +46,7 @@ vtparse = { version="0.6.2", path="../vtparse" }
|
||||
wezterm-bidi = { path = "../bidi", version="0.2.1" }
|
||||
wezterm-blob-leases = { path = "../wezterm-blob-leases", version="0.1" }
|
||||
wezterm-color-types = { path = "../color-types", version="0.2" }
|
||||
wezterm-input-types = { path = "../wezterm-input-types", version="0.1" }
|
||||
wezterm-dynamic = { path = "../wezterm-dynamic", version="0.1" }
|
||||
|
||||
[features]
|
||||
|
@ -52,17 +52,7 @@ fn csi_size() {
|
||||
assert_eq!(std::mem::size_of::<CSI>(), 32);
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct KittyKeyboardFlags: u16 {
|
||||
const NONE = 0;
|
||||
const DISAMBIGUATE_ESCAPE_CODES = 1;
|
||||
const REPORT_EVENT_TYPES = 2;
|
||||
const REPORT_ALTERNATE_KEYS = 4;
|
||||
const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 8;
|
||||
const REPORT_ASSOCIATED_TEXT = 16;
|
||||
}
|
||||
}
|
||||
pub use wezterm_input_types::KittyKeyboardFlags;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(u16)]
|
||||
|
@ -272,257 +272,6 @@ impl KeyCode {
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions>
|
||||
fn kitty_function_code(self) -> Option<u32> {
|
||||
use KeyCode::*;
|
||||
Some(match self {
|
||||
Escape => 27,
|
||||
Enter => 13,
|
||||
Tab => 9,
|
||||
Backspace => 127,
|
||||
CapsLock => 57358,
|
||||
ScrollLock => 57359,
|
||||
NumLock => 57360,
|
||||
PrintScreen => 57361,
|
||||
Pause => 57362,
|
||||
Menu => 57363,
|
||||
Function(n) if n >= 13 && n <= 35 => 57376 + n as u32 - 13,
|
||||
Numpad0 => 57399,
|
||||
Numpad1 => 57400,
|
||||
Numpad2 => 57401,
|
||||
Numpad3 => 57402,
|
||||
Numpad4 => 57403,
|
||||
Numpad5 => 57404,
|
||||
Numpad6 => 57405,
|
||||
Numpad7 => 57406,
|
||||
Numpad8 => 57407,
|
||||
Numpad9 => 57408,
|
||||
Decimal => 57409,
|
||||
Divide => 57410,
|
||||
Multiply => 57411,
|
||||
Subtract => 57412,
|
||||
Add => 57413,
|
||||
// KeypadEnter => 57414,
|
||||
// KeypadEquals => 57415,
|
||||
Separator => 57416,
|
||||
ApplicationLeftArrow => 57417,
|
||||
ApplicationRightArrow => 57418,
|
||||
ApplicationUpArrow => 57419,
|
||||
ApplicationDownArrow => 57420,
|
||||
KeyPadHome => 57423,
|
||||
KeyPadEnd => 57424,
|
||||
KeyPadBegin => 57427,
|
||||
KeyPadPageUp => 57421,
|
||||
KeyPadPageDown => 57422,
|
||||
Insert => 57425,
|
||||
// KeypadDelete => 57426,
|
||||
MediaPlayPause => 57430,
|
||||
MediaStop => 57432,
|
||||
MediaNextTrack => 57435,
|
||||
MediaPrevTrack => 57436,
|
||||
VolumeDown => 57436,
|
||||
VolumeUp => 57439,
|
||||
VolumeMute => 57440,
|
||||
LeftShift => 57441,
|
||||
LeftControl => 57442,
|
||||
LeftAlt => 57443,
|
||||
LeftWindows => 57444,
|
||||
RightShift => 57447,
|
||||
RightControl => 57448,
|
||||
RightAlt => 57449,
|
||||
RightWindows => 57450,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode_kitty(
|
||||
&self,
|
||||
mods: Modifiers,
|
||||
is_down: bool,
|
||||
flags: KittyKeyboardFlags,
|
||||
) -> Result<String> {
|
||||
use KeyCode::*;
|
||||
|
||||
if !flags.contains(KittyKeyboardFlags::REPORT_EVENT_TYPES) && !is_down {
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
// Normalize
|
||||
let key = match self {
|
||||
Char('\r') => Enter,
|
||||
Char('\t') => Tab,
|
||||
Char('\x7f') => Delete,
|
||||
Char('\x08') => Backspace,
|
||||
c => *c,
|
||||
};
|
||||
|
||||
if mods.is_empty()
|
||||
&& !flags.contains(KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES)
|
||||
&& is_down
|
||||
{
|
||||
// Check for simple text generating keys
|
||||
match key {
|
||||
Enter => return Ok("\r".to_string()),
|
||||
Tab => return Ok("\t".to_string()),
|
||||
Backspace => return Ok("\x7f".to_string()),
|
||||
Char(c) => return Ok(c.to_string()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut modifiers = 0;
|
||||
if mods.contains(Modifiers::SHIFT) {
|
||||
modifiers |= 1;
|
||||
}
|
||||
if mods.contains(Modifiers::ALT) {
|
||||
modifiers |= 2;
|
||||
}
|
||||
if mods.contains(Modifiers::CTRL) {
|
||||
modifiers |= 4;
|
||||
}
|
||||
if mods.contains(Modifiers::SUPER) {
|
||||
modifiers |= 8;
|
||||
}
|
||||
if mods.contains(Modifiers::CAPS_LOCK) {
|
||||
modifiers |= 64;
|
||||
}
|
||||
if mods.contains(Modifiers::NUM_LOCK) {
|
||||
modifiers |= 128;
|
||||
}
|
||||
modifiers += 1;
|
||||
|
||||
let event_type = if flags.contains(KittyKeyboardFlags::REPORT_EVENT_TYPES) && !is_down {
|
||||
":3"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let is_legacy_key = match key {
|
||||
Char(c) => c.is_ascii_alphanumeric() || c.is_ascii_punctuation(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
match key {
|
||||
Char(shifted_key) => {
|
||||
let mut use_legacy = false;
|
||||
|
||||
if !flags.contains(KittyKeyboardFlags::REPORT_ALTERNATE_KEYS)
|
||||
&& event_type.is_empty()
|
||||
&& is_legacy_key
|
||||
&& !(flags.contains(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES)
|
||||
&& (mods.contains(Modifiers::CTRL) || mods.contains(Modifiers::ALT)))
|
||||
{
|
||||
use_legacy = true;
|
||||
}
|
||||
|
||||
if use_legacy {
|
||||
// Legacy text key
|
||||
let mut output = String::new();
|
||||
if mods.contains(Modifiers::ALT) {
|
||||
output.push('\x1b');
|
||||
}
|
||||
if mods.contains(Modifiers::CTRL) {
|
||||
csi_u_encode(
|
||||
&mut output,
|
||||
shifted_key.to_ascii_uppercase(),
|
||||
mods,
|
||||
&KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Xterm,
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
},
|
||||
)?;
|
||||
} else {
|
||||
output.push(shifted_key);
|
||||
}
|
||||
return Ok(output);
|
||||
}
|
||||
|
||||
let c = shifted_key.to_ascii_lowercase();
|
||||
|
||||
let key_code = if flags.contains(KittyKeyboardFlags::REPORT_ALTERNATE_KEYS)
|
||||
&& c != shifted_key
|
||||
{
|
||||
// Note: we don't have enough information here to know what the base-layout key
|
||||
// should really be.
|
||||
let base_layout = c;
|
||||
format!(
|
||||
"{}:{}:{}",
|
||||
(c as u32),
|
||||
(shifted_key as u32),
|
||||
(base_layout as u32)
|
||||
)
|
||||
} else {
|
||||
(c as u32).to_string()
|
||||
};
|
||||
|
||||
Ok(format!("\x1b[{key_code};{modifiers}{event_type}u"))
|
||||
}
|
||||
LeftArrow | RightArrow | UpArrow | DownArrow | Home | End => {
|
||||
let c = match key {
|
||||
UpArrow => 'A',
|
||||
DownArrow => 'B',
|
||||
RightArrow => 'C',
|
||||
LeftArrow => 'D',
|
||||
Home => 'H',
|
||||
End => 'F',
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(format!("\x1b[1;{modifiers}{event_type}{c}"))
|
||||
}
|
||||
PageUp | PageDown | Insert | Delete => {
|
||||
let c = match key {
|
||||
Insert => 2,
|
||||
Delete => 3,
|
||||
PageUp => 5,
|
||||
PageDown => 6,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(format!("\x1b[{c};{modifiers}{event_type}~"))
|
||||
}
|
||||
Function(n) if n < 13 => {
|
||||
// The spec says that kitty prefers an SS3 form for F1-F4,
|
||||
// but then has some variance in the encoding and cites a
|
||||
// compatibility issue with a cursor position report.
|
||||
// Since it allows reporting these all unambiguously with
|
||||
// the same general scheme, that is what we're using here.
|
||||
let intro = match n {
|
||||
1 => "\x1b[11",
|
||||
2 => "\x1b[12",
|
||||
3 => "\x1b[13",
|
||||
4 => "\x1b[14",
|
||||
5 => "\x1b[15",
|
||||
6 => "\x1b[17",
|
||||
7 => "\x1b[18",
|
||||
8 => "\x1b[19",
|
||||
9 => "\x1b[20",
|
||||
10 => "\x1b[21",
|
||||
11 => "\x1b[23",
|
||||
12 => "\x1b[24",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(format!("{intro};{modifiers}{event_type}~"))
|
||||
}
|
||||
|
||||
_ => {
|
||||
if key.is_modifier()
|
||||
&& !flags.contains(KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES)
|
||||
{
|
||||
// Don't report bare modifier only key events unless
|
||||
// we're reporting all keys with escape codes
|
||||
Ok(String::new())
|
||||
} else if let Some(code) = key.kitty_function_code() {
|
||||
Ok(format!("\x1b[{code};{modifiers}{event_type}u"))
|
||||
} else {
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the byte sequence that represents this KeyCode and Modifier combination,
|
||||
pub fn encode(
|
||||
&self,
|
||||
@ -530,12 +279,6 @@ impl KeyCode {
|
||||
modes: KeyCodeEncodeModes,
|
||||
is_down: bool,
|
||||
) -> Result<String> {
|
||||
match &modes.encoding {
|
||||
KeyboardEncoding::Kitty(flags) if *flags != KittyKeyboardFlags::NONE => {
|
||||
return self.encode_kitty(mods, is_down, *flags);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if !is_down {
|
||||
// We only want down events
|
||||
return Ok(String::new());
|
||||
@ -2194,151 +1937,6 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3220() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Kitty(
|
||||
KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES,
|
||||
),
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Char('o')
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"o".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::Char('o')
|
||||
.encode(Modifiers::NONE, mode, false)
|
||||
.unwrap(),
|
||||
"\x1b[111;1:3u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3473() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Kitty(
|
||||
KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
|
||||
),
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Function(1)
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"\x1b[11;1~".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::Function(1)
|
||||
.encode(Modifiers::NONE, mode, false)
|
||||
.unwrap(),
|
||||
"\x1b[11;1:3~".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_2546() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Kitty(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES),
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Char('i')
|
||||
.encode(Modifiers::ALT | Modifiers::SHIFT, mode, true)
|
||||
.unwrap(),
|
||||
"\x1b[105;4u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::Char('1')
|
||||
.encode(Modifiers::ALT | Modifiers::SHIFT, mode, true)
|
||||
.unwrap(),
|
||||
"\x1b[49;4u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3474() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Kitty(
|
||||
KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
|
||||
),
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Char('A')
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"\u{1b}[97:65:97;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::Char('A')
|
||||
.encode(Modifiers::NONE, mode, false)
|
||||
.unwrap(),
|
||||
"\u{1b}[97:65:97;1:3u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3476() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Kitty(
|
||||
KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
|
||||
),
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::LeftShift
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"\u{1b}[57441;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::LeftShift
|
||||
.encode(Modifiers::NONE, mode, false)
|
||||
.unwrap(),
|
||||
"\u{1b}[57441;1:3u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::LeftControl
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"\u{1b}[57442;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::LeftControl
|
||||
.encode(Modifiers::NONE, mode, false)
|
||||
.unwrap(),
|
||||
"\u{1b}[57442;1:3u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3478_xterm() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
@ -2374,83 +1972,4 @@ mod test {
|
||||
"\u{1b}[1;2F".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3478_kitty() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Kitty(
|
||||
KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
|
||||
),
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Numpad0
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"\u{1b}[57399;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::Numpad0
|
||||
.encode(Modifiers::SHIFT, mode, true)
|
||||
.unwrap(),
|
||||
"\u{1b}[57399;2u".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Numpad1
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"\u{1b}[57400;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyCode::Numpad1
|
||||
.encode(Modifiers::NONE | Modifiers::SHIFT, mode, true)
|
||||
.unwrap(),
|
||||
"\u{1b}[57400;2u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3315() {
|
||||
let mode = KeyCodeEncodeModes {
|
||||
encoding: KeyboardEncoding::Kitty(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES),
|
||||
newline_mode: false,
|
||||
application_cursor_keys: false,
|
||||
modify_other_keys: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Char('"')
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"\"".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Char('"')
|
||||
.encode(Modifiers::SHIFT, mode, true)
|
||||
.unwrap(),
|
||||
"\"".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::Char('!')
|
||||
.encode(Modifiers::SHIFT, mode, true)
|
||||
.unwrap(),
|
||||
"!".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyCode::LeftShift
|
||||
.encode(Modifiers::NONE, mode, true)
|
||||
.unwrap(),
|
||||
"".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -256,6 +256,17 @@ impl super::TermWindow {
|
||||
key.encode_win32_input_mode()
|
||||
}
|
||||
|
||||
fn encode_kitty_input(&self, pane: &Arc<dyn Pane>, key: &KeyEvent) -> Option<String> {
|
||||
if !self.config.enable_kitty_keyboard {
|
||||
return None;
|
||||
}
|
||||
if let KeyboardEncoding::Kitty(flags) = pane.get_keyboard_encoding() {
|
||||
Some(key.encode_kitty(flags))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_key(
|
||||
&mut self,
|
||||
pane: &Arc<dyn Pane>,
|
||||
@ -294,6 +305,7 @@ impl super::TermWindow {
|
||||
leader_mod: Modifiers,
|
||||
only_key_bindings: OnlyKeyBindings,
|
||||
is_down: bool,
|
||||
key_event: Option<&KeyEvent>,
|
||||
) -> bool {
|
||||
if is_down && !leader_active {
|
||||
// Check to see if this key-press is the leader activating
|
||||
@ -406,23 +418,49 @@ impl super::TermWindow {
|
||||
if bypass_compose {
|
||||
if let Key::Code(term_key) = self.win_key_code_to_termwiz_key_code(keycode) {
|
||||
let tw_raw_modifiers = window_mods_to_termwiz_mods(raw_modifiers);
|
||||
if self.config.debug_key_events {
|
||||
log::info!(
|
||||
"{:?} {:?} -> send to pane {:?} {:?}",
|
||||
keycode,
|
||||
raw_modifiers,
|
||||
term_key,
|
||||
tw_raw_modifiers
|
||||
);
|
||||
}
|
||||
|
||||
let res = if is_down {
|
||||
pane.key_down(term_key, tw_raw_modifiers)
|
||||
} else {
|
||||
pane.key_up(term_key, tw_raw_modifiers)
|
||||
let mut did_encode = false;
|
||||
if let Some(key_event) = key_event {
|
||||
if let Some(encoded) = self.encode_win32_input(&pane, &key_event) {
|
||||
if self.config.debug_key_events {
|
||||
log::info!("win32: Encoded input as {:?}", encoded);
|
||||
}
|
||||
pane.writer()
|
||||
.write_all(encoded.as_bytes())
|
||||
.context("sending win32-input-mode encoded data")
|
||||
.ok();
|
||||
did_encode = true;
|
||||
} else if let Some(encoded) = self.encode_kitty_input(&pane, &key_event) {
|
||||
if self.config.debug_key_events {
|
||||
log::info!("kitty: Encoded input as {:?}", encoded);
|
||||
}
|
||||
pane.writer()
|
||||
.write_all(encoded.as_bytes())
|
||||
.context("sending kitty encoded data")
|
||||
.ok();
|
||||
did_encode = true;
|
||||
}
|
||||
};
|
||||
if !did_encode {
|
||||
if self.config.debug_key_events {
|
||||
log::info!(
|
||||
"{:?} {:?} -> send to pane {:?} {:?}",
|
||||
keycode,
|
||||
raw_modifiers,
|
||||
term_key,
|
||||
tw_raw_modifiers
|
||||
);
|
||||
}
|
||||
|
||||
did_encode = if is_down {
|
||||
pane.key_down(term_key, tw_raw_modifiers)
|
||||
} else {
|
||||
pane.key_up(term_key, tw_raw_modifiers)
|
||||
}
|
||||
.is_ok();
|
||||
};
|
||||
|
||||
if res.is_ok() {
|
||||
if did_encode {
|
||||
if is_down
|
||||
&& !keycode.is_modifier()
|
||||
&& *keycode != KeyCode::CapsLock
|
||||
@ -492,6 +530,7 @@ impl super::TermWindow {
|
||||
leader_mod,
|
||||
OnlyKeyBindings::Yes,
|
||||
key.key_is_down,
|
||||
None,
|
||||
) {
|
||||
key.set_handled();
|
||||
return;
|
||||
@ -512,6 +551,7 @@ impl super::TermWindow {
|
||||
leader_mod,
|
||||
OnlyKeyBindings::Yes,
|
||||
key.key_is_down,
|
||||
None,
|
||||
) {
|
||||
key.set_handled();
|
||||
return;
|
||||
@ -532,6 +572,7 @@ impl super::TermWindow {
|
||||
leader_mod,
|
||||
OnlyKeyBindings::Yes,
|
||||
key.key_is_down,
|
||||
None,
|
||||
) {
|
||||
key.set_handled();
|
||||
}
|
||||
@ -643,6 +684,7 @@ impl super::TermWindow {
|
||||
leader_mod,
|
||||
OnlyKeyBindings::No,
|
||||
window_key.key_is_down,
|
||||
Some(&window_key),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -669,15 +711,6 @@ impl super::TermWindow {
|
||||
self.key_table_state.did_process_key();
|
||||
}
|
||||
|
||||
if self.config.debug_key_events {
|
||||
log::info!(
|
||||
"send to pane {} key={:?} mods={:?}",
|
||||
if window_key.key_is_down { "DOWN" } else { "UP" },
|
||||
key,
|
||||
modifiers
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(modal) = self.get_modal() {
|
||||
if window_key.key_is_down {
|
||||
modal.key_down(key, modifiers, self).ok();
|
||||
@ -687,15 +720,33 @@ impl super::TermWindow {
|
||||
|
||||
let res = if let Some(encoded) = self.encode_win32_input(&pane, &window_key) {
|
||||
if self.config.debug_key_events {
|
||||
log::info!("Encoded input as {:?}", encoded);
|
||||
log::info!("win32: Encoded input as {:?}", encoded);
|
||||
}
|
||||
pane.writer()
|
||||
.write_all(encoded.as_bytes())
|
||||
.context("sending win32-input-mode encoded data")
|
||||
} else if window_key.key_is_down {
|
||||
pane.key_down(key, modifiers)
|
||||
} else if let Some(encoded) = self.encode_kitty_input(&pane, &window_key) {
|
||||
if self.config.debug_key_events {
|
||||
log::info!("kitty: Encoded input as {:?}", encoded);
|
||||
}
|
||||
pane.writer()
|
||||
.write_all(encoded.as_bytes())
|
||||
.context("sending kitty encoded data")
|
||||
} else {
|
||||
pane.key_up(key, modifiers)
|
||||
if self.config.debug_key_events {
|
||||
log::info!(
|
||||
"send to pane {} key={:?} mods={:?}",
|
||||
if window_key.key_is_down { "DOWN" } else { "UP" },
|
||||
key,
|
||||
modifiers
|
||||
);
|
||||
}
|
||||
|
||||
if window_key.key_is_down {
|
||||
pane.key_down(key, modifiers)
|
||||
} else {
|
||||
pane.key_up(key, modifiers)
|
||||
}
|
||||
};
|
||||
|
||||
if res.is_ok() {
|
||||
|
@ -2,6 +2,7 @@ use bitflags::*;
|
||||
use serde::*;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Write;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
use wezterm_dynamic::{FromDynamic, ToDynamic};
|
||||
@ -1234,6 +1235,145 @@ impl RawKeyEvent {
|
||||
pub fn set_handled(&self) {
|
||||
self.handled.set_handled();
|
||||
}
|
||||
|
||||
/// <https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions>
|
||||
fn kitty_function_code(&self) -> Option<u32> {
|
||||
use KeyCode::*;
|
||||
Some(match self.key {
|
||||
// Tab => 9,
|
||||
// Backspace => 127,
|
||||
// CapsLock => 57358,
|
||||
// ScrollLock => 57359,
|
||||
// NumLock => 57360,
|
||||
// PrintScreen => 57361,
|
||||
// Pause => 57362,
|
||||
// Menu => 57363,
|
||||
Function(n) if n >= 13 && n <= 35 => 57376 + n as u32 - 13,
|
||||
Numpad(n) => n as u32 + 57399,
|
||||
Decimal => 57409,
|
||||
Divide => 57410,
|
||||
Multiply => 57411,
|
||||
Subtract => 57412,
|
||||
Add => 57413,
|
||||
// KeypadEnter => 57414,
|
||||
// KeypadEquals => 57415,
|
||||
Separator => 57416,
|
||||
ApplicationLeftArrow => 57417,
|
||||
ApplicationRightArrow => 57418,
|
||||
ApplicationUpArrow => 57419,
|
||||
ApplicationDownArrow => 57420,
|
||||
KeyPadHome => 57423,
|
||||
KeyPadEnd => 57424,
|
||||
KeyPadBegin => 57427,
|
||||
KeyPadPageUp => 57421,
|
||||
KeyPadPageDown => 57422,
|
||||
Insert => 57425,
|
||||
// KeypadDelete => 57426,
|
||||
MediaPlayPause => 57430,
|
||||
MediaStop => 57432,
|
||||
MediaNextTrack => 57435,
|
||||
MediaPrevTrack => 57436,
|
||||
VolumeDown => 57436,
|
||||
VolumeUp => 57439,
|
||||
VolumeMute => 57440,
|
||||
LeftShift => 57441,
|
||||
LeftControl => 57442,
|
||||
LeftAlt => 57443,
|
||||
LeftWindows => 57444,
|
||||
RightShift => 57447,
|
||||
RightControl => 57448,
|
||||
RightAlt => 57449,
|
||||
RightWindows => 57450,
|
||||
_ => match &self.phys_code {
|
||||
Some(phys) => {
|
||||
use PhysKeyCode::*;
|
||||
|
||||
match *phys {
|
||||
Escape => 27,
|
||||
Return => 13,
|
||||
Tab => 9,
|
||||
Backspace => 127,
|
||||
CapsLock => 57358,
|
||||
// ScrollLock => 57359,
|
||||
NumLock => 57360,
|
||||
// PrintScreen => 57361,
|
||||
// Pause => 57362,
|
||||
// Menu => 57363,
|
||||
F13 => 57376,
|
||||
F14 => 57377,
|
||||
F15 => 57378,
|
||||
F16 => 57379,
|
||||
F17 => 57380,
|
||||
F18 => 57381,
|
||||
F19 => 57382,
|
||||
F20 => 57383,
|
||||
/*
|
||||
F21 => 57384,
|
||||
F22 => 57385,
|
||||
F23 => 57386,
|
||||
F24 => 57387,
|
||||
F25 => 57388,
|
||||
F26 => 57389,
|
||||
F27 => 57390,
|
||||
F28 => 57391,
|
||||
F29 => 57392,
|
||||
F30 => 57393,
|
||||
F31 => 57394,
|
||||
F32 => 57395,
|
||||
F33 => 57396,
|
||||
F34 => 57397,
|
||||
*/
|
||||
Keypad0 => 57399,
|
||||
Keypad1 => 57400,
|
||||
Keypad2 => 57401,
|
||||
Keypad3 => 57402,
|
||||
Keypad4 => 57403,
|
||||
Keypad5 => 57404,
|
||||
Keypad6 => 57405,
|
||||
Keypad7 => 57406,
|
||||
Keypad8 => 57407,
|
||||
Keypad9 => 57408,
|
||||
KeypadDecimal => 57409,
|
||||
KeypadDivide => 57410,
|
||||
KeypadMultiply => 57411,
|
||||
KeypadSubtract => 57412,
|
||||
KeypadAdd => 57413,
|
||||
KeypadEnter => 57414,
|
||||
KeypadEquals => 57415,
|
||||
// KeypadSeparator => 57416,
|
||||
// ApplicationLeftArrow => 57417,
|
||||
// ApplicationRightArrow => 57418,
|
||||
// ApplicationUpArrow => 57419,
|
||||
// ApplicationDownArrow => 57420,
|
||||
// KeyPadHome => 57423,
|
||||
// KeyPadEnd => 57424,
|
||||
// KeyPadBegin => 57427,
|
||||
// KeyPadPageUp => 57421,
|
||||
// KeyPadPageDown => 57422,
|
||||
Insert => 57425,
|
||||
// KeypadDelete => 57426,
|
||||
// MediaPlayPause => 57430,
|
||||
// MediaStop => 57432,
|
||||
// MediaNextTrack => 57435,
|
||||
// MediaPrevTrack => 57436,
|
||||
VolumeDown => 57436,
|
||||
VolumeUp => 57439,
|
||||
VolumeMute => 57440,
|
||||
LeftShift => 57441,
|
||||
LeftControl => 57442,
|
||||
LeftAlt => 57443,
|
||||
LeftWindows => 57444,
|
||||
RightShift => 57447,
|
||||
RightControl => 57448,
|
||||
RightAlt => 57449,
|
||||
RightWindows => 57450,
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -1489,6 +1629,208 @@ impl KeyEvent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_kitty(&self, flags: KittyKeyboardFlags) -> String {
|
||||
use KeyCode::*;
|
||||
|
||||
if !flags.contains(KittyKeyboardFlags::REPORT_EVENT_TYPES) && !self.key_is_down {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
if self.modifiers.is_empty()
|
||||
&& !flags.contains(KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES)
|
||||
&& self.key_is_down
|
||||
{
|
||||
// Check for simple text generating keys
|
||||
match &self.key {
|
||||
Char(c) => return c.to_string(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut modifiers = 0;
|
||||
if self.modifiers.contains(Modifiers::SHIFT) {
|
||||
modifiers |= 1;
|
||||
}
|
||||
if self.modifiers.contains(Modifiers::ALT) {
|
||||
modifiers |= 2;
|
||||
}
|
||||
if self.modifiers.contains(Modifiers::CTRL) {
|
||||
modifiers |= 4;
|
||||
}
|
||||
if self.modifiers.contains(Modifiers::SUPER) {
|
||||
modifiers |= 8;
|
||||
}
|
||||
if self.modifiers.contains(Modifiers::CAPS_LOCK) {
|
||||
modifiers |= 64;
|
||||
}
|
||||
if self.modifiers.contains(Modifiers::NUM_LOCK) {
|
||||
modifiers |= 128;
|
||||
}
|
||||
modifiers += 1;
|
||||
|
||||
let event_type =
|
||||
if flags.contains(KittyKeyboardFlags::REPORT_EVENT_TYPES) && !self.key_is_down {
|
||||
":3"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let is_legacy_key = match &self.key {
|
||||
Char(c) => c.is_ascii_alphanumeric() || c.is_ascii_punctuation(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
match &self.key {
|
||||
PageUp | PageDown | Insert | Char('\x7f') => {
|
||||
let c = match &self.key {
|
||||
Insert => 2,
|
||||
Char('\x7f') => 3, // Delete
|
||||
PageUp => 5,
|
||||
PageDown => 6,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
format!("\x1b[{c};{modifiers}{event_type}~")
|
||||
}
|
||||
Char(shifted_key) => {
|
||||
let mut use_legacy = false;
|
||||
|
||||
if !flags.contains(KittyKeyboardFlags::REPORT_ALTERNATE_KEYS)
|
||||
&& event_type.is_empty()
|
||||
&& is_legacy_key
|
||||
&& !(flags.contains(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES)
|
||||
&& (self.modifiers.contains(Modifiers::CTRL)
|
||||
|| self.modifiers.contains(Modifiers::ALT)))
|
||||
{
|
||||
use_legacy = true;
|
||||
}
|
||||
|
||||
if use_legacy {
|
||||
// Legacy text key
|
||||
let mut output = String::new();
|
||||
if self.modifiers.contains(Modifiers::ALT) {
|
||||
output.push('\x1b');
|
||||
}
|
||||
if self.modifiers.contains(Modifiers::CTRL) {
|
||||
csi_u_encode(
|
||||
&mut output,
|
||||
shifted_key.to_ascii_uppercase(),
|
||||
self.modifiers,
|
||||
);
|
||||
} else {
|
||||
output.push(*shifted_key);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// FIXME: ideally we'd get the correct unshifted key from
|
||||
// the OS based on the current keyboard layout. That needs
|
||||
// more plumbing, so for now, we're assuming the US layout.
|
||||
let c = us_layout_unshift(*shifted_key);
|
||||
|
||||
let base_layout = self
|
||||
.raw
|
||||
.as_ref()
|
||||
.and_then(|raw| raw.phys_code.as_ref())
|
||||
.and_then(|phys| match phys.to_key_code() {
|
||||
KeyCode::Char(base) if base != c => Some(base),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let mut key_code = format!("{}", (c as u32));
|
||||
|
||||
if flags.contains(KittyKeyboardFlags::REPORT_ALTERNATE_KEYS)
|
||||
&& (c != *shifted_key || base_layout.is_some())
|
||||
{
|
||||
key_code.push(':');
|
||||
if c != *shifted_key {
|
||||
key_code.push_str(&format!("{}", (*shifted_key as u32)));
|
||||
}
|
||||
if let Some(base) = base_layout {
|
||||
key_code.push_str(&format!(":{}", (base as u32)));
|
||||
}
|
||||
}
|
||||
|
||||
format!("\x1b[{key_code};{modifiers}{event_type}u")
|
||||
}
|
||||
LeftArrow | RightArrow | UpArrow | DownArrow | Home | End => {
|
||||
let c = match &self.key {
|
||||
UpArrow => 'A',
|
||||
DownArrow => 'B',
|
||||
RightArrow => 'C',
|
||||
LeftArrow => 'D',
|
||||
Home => 'H',
|
||||
End => 'F',
|
||||
_ => unreachable!(),
|
||||
};
|
||||
format!("\x1b[1;{modifiers}{event_type}{c}")
|
||||
}
|
||||
Function(n) if *n < 13 => {
|
||||
// The spec says that kitty prefers an SS3 form for F1-F4,
|
||||
// but then has some variance in the encoding and cites a
|
||||
// compatibility issue with a cursor position report.
|
||||
// Since it allows reporting these all unambiguously with
|
||||
// the same general scheme, that is what we're using here.
|
||||
let intro = match *n {
|
||||
1 => "\x1b[11",
|
||||
2 => "\x1b[12",
|
||||
3 => "\x1b[13",
|
||||
4 => "\x1b[14",
|
||||
5 => "\x1b[15",
|
||||
6 => "\x1b[17",
|
||||
7 => "\x1b[18",
|
||||
8 => "\x1b[19",
|
||||
9 => "\x1b[20",
|
||||
10 => "\x1b[21",
|
||||
11 => "\x1b[23",
|
||||
12 => "\x1b[24",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
format!("{intro};{modifiers}{event_type}~")
|
||||
}
|
||||
|
||||
_ => {
|
||||
if self.key.is_modifier()
|
||||
&& !flags.contains(KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES)
|
||||
{
|
||||
// Don't report bare modifier only key events unless
|
||||
// we're reporting all keys with escape codes
|
||||
String::new()
|
||||
} else if let Some(code) =
|
||||
self.raw.as_ref().and_then(|raw| raw.kitty_function_code())
|
||||
{
|
||||
format!("\x1b[{code};{modifiers}{event_type}u")
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn csi_u_encode(buf: &mut String, c: char, mods: Modifiers) {
|
||||
let c = if mods.contains(Modifiers::CTRL) && ctrl_mapping(c).is_some() {
|
||||
ctrl_mapping(c).unwrap()
|
||||
} else {
|
||||
c
|
||||
};
|
||||
if mods.contains(Modifiers::ALT) {
|
||||
buf.push(0x1b as char);
|
||||
}
|
||||
write!(buf, "{}", c).ok();
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct KittyKeyboardFlags: u16 {
|
||||
const NONE = 0;
|
||||
const DISAMBIGUATE_ESCAPE_CODES = 1;
|
||||
const REPORT_EVENT_TYPES = 2;
|
||||
const REPORT_ALTERNATE_KEYS = 4;
|
||||
const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 8;
|
||||
const REPORT_ASSOCIATED_TEXT = 16;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@ -1629,6 +1971,45 @@ impl FromDynamic for IntegratedTitleButtonStyle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Kitty wants us to report the un-shifted version of a key.
|
||||
/// It's a PITA to obtain that from the OS-dependent keyboard
|
||||
/// layout stuff. For the moment, we'll do the slightly gross
|
||||
/// thing and make an assumption that a US ANSI layout is in
|
||||
/// use; this function encodes that mapping.
|
||||
fn us_layout_unshift(c: char) -> char {
|
||||
match c {
|
||||
'!' => '1',
|
||||
'@' => '2',
|
||||
'#' => '3',
|
||||
'$' => '4',
|
||||
'%' => '5',
|
||||
'^' => '6',
|
||||
'&' => '7',
|
||||
'*' => '8',
|
||||
'(' => '9',
|
||||
')' => '0',
|
||||
'_' => '-',
|
||||
'+' => '=',
|
||||
'~' => '`',
|
||||
'{' => '[',
|
||||
'}' => ']',
|
||||
'|' => '\\',
|
||||
':' => ';',
|
||||
'"' => '\'',
|
||||
'<' => ',',
|
||||
'>' => '.',
|
||||
'?' => '/',
|
||||
c => {
|
||||
let s: Vec<char> = c.to_lowercase().collect();
|
||||
if s.len() == 1 {
|
||||
s[0]
|
||||
} else {
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Map c to its Ctrl equivalent.
|
||||
/// In theory, this mapping is simply translating alpha characters
|
||||
/// to upper case and then masking them by 0x1f, but xterm inherits
|
||||
@ -1637,7 +2018,6 @@ impl FromDynamic for IntegratedTitleButtonStyle {
|
||||
/// to US keyboard layout (particularly the punctuation characters
|
||||
/// produced in combination with SHIFT) that may not be 100%
|
||||
/// the right thing to do here for users with non-US layouts.
|
||||
#[cfg(windows)]
|
||||
fn ctrl_mapping(c: char) -> Option<char> {
|
||||
// Please also sync with the copy of this function that
|
||||
// lives in termwiz :-/
|
||||
@ -1704,3 +2084,397 @@ impl Default for UIKeyCapRendering {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3220() {
|
||||
let flags =
|
||||
KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES | KittyKeyboardFlags::REPORT_EVENT_TYPES;
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('o'),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"o".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('o'),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: false,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\x1b[111;1:3u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3473() {
|
||||
let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Function(1),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\x1b[11;1~".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Function(1),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: false,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\x1b[11;1:3~".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_2546() {
|
||||
let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES;
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('i'),
|
||||
modifiers: Modifiers::ALT | Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\x1b[105;4u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('I'),
|
||||
modifiers: Modifiers::ALT | Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\x1b[105;4u".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('1'),
|
||||
modifiers: Modifiers::ALT | Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\x1b[49;4u".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('!'),
|
||||
modifiers: Modifiers::ALT | Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
Some(PhysKeyCode::K1)
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\x1b[49;4u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3474() {
|
||||
let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('A'),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[97:65;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('A'),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: false,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[97:65;1:3u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
fn make_event_with_raw(mut event: KeyEvent, phys: Option<PhysKeyCode>) -> KeyEvent {
|
||||
let phys = match phys {
|
||||
Some(phys) => Some(phys),
|
||||
None => event.key.to_phys(),
|
||||
};
|
||||
|
||||
event.raw = Some(RawKeyEvent {
|
||||
key: event.key.clone(),
|
||||
modifiers: event.modifiers,
|
||||
phys_code: phys,
|
||||
raw_code: 0,
|
||||
#[cfg(windows)]
|
||||
scan_code: 0,
|
||||
repeat_count: 1,
|
||||
key_is_down: event.key_is_down,
|
||||
handled: Handled::new(),
|
||||
});
|
||||
|
||||
event
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3476() {
|
||||
let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
|
||||
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::LeftShift,
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57441;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::LeftShift,
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: false,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57441;1:3u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::LeftControl,
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57442;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::LeftControl,
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: false,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57442;1:3u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3478() {
|
||||
let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
|
||||
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::Numpad(0),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57399;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::Numpad(0),
|
||||
modifiers: Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57399;2u".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::Numpad(1),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57400;1u".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::Numpad(1),
|
||||
modifiers: Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
None
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\u{1b}[57400;2u".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3315() {
|
||||
let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES;
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('"'),
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\"".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('"'),
|
||||
modifiers: Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"\"".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('!'),
|
||||
modifiers: Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"!".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
KeyEvent {
|
||||
key: KeyCode::LeftShift,
|
||||
modifiers: Modifiers::NONE,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
}
|
||||
.encode_kitty(flags),
|
||||
"".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_issue_3479() {
|
||||
let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KittyKeyboardFlags::REPORT_EVENT_TYPES
|
||||
| KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
|
||||
| KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
|
||||
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('ф'),
|
||||
modifiers: Modifiers::CTRL,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
Some(PhysKeyCode::A)
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\x1b[1092::97;5u".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
make_event_with_raw(
|
||||
KeyEvent {
|
||||
key: KeyCode::Char('Ф'),
|
||||
modifiers: Modifiers::CTRL | Modifiers::SHIFT,
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None
|
||||
},
|
||||
Some(PhysKeyCode::A)
|
||||
)
|
||||
.encode_kitty(flags),
|
||||
"\x1b[1092:1060:97;6u".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user