aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/input/encode.rs61
-rw-r--r--src/pane/terminal.rs2
2 files changed, 58 insertions, 5 deletions
diff --git a/src/input/encode.rs b/src/input/encode.rs
index 58543232..91b28fc8 100644
--- a/src/input/encode.rs
+++ b/src/input/encode.rs
@@ -23,12 +23,12 @@ pub fn encode_terminal_key(key: TerminalKey, protocol: KeyboardProtocol) -> Vec<
return Vec::new();
}
- // Super has no legacy character encoding. Preserve the chord with CSI-u
- // instead of leaking the unmodified character into the pane.
+ // Super has no legacy character encoding, and Ctrl+Shift+letter would
+ // collapse into the same C0 byte as Ctrl+letter. Preserve both chords with
+ // CSI-u, matching Ghostty's legacy encoder.
if matches!(protocol, KeyboardProtocol::Legacy)
&& key.kind != crossterm::event::KeyEventKind::Release
- && matches!(key.code, KeyCode::Char(_))
- && key.modifiers.contains(KeyModifiers::SUPER)
+ && legacy_chord_needs_csi_u(&key)
{
if let Some(bytes) = try_encode_csi_u(&key, 0) {
return bytes;
@@ -81,6 +81,17 @@ pub fn encode_terminal_key(key: TerminalKey, protocol: KeyboardProtocol) -> Vec<
encode_legacy(key)
}
+fn legacy_chord_needs_csi_u(key: &TerminalKey) -> bool {
+ let KeyCode::Char(ch) = key.code else {
+ return false;
+ };
+ key.modifiers.contains(KeyModifiers::SUPER)
+ || (ch.is_ascii_alphabetic()
+ && key
+ .modifiers
+ .contains(KeyModifiers::CONTROL | KeyModifiers::SHIFT))
+}
+
#[allow(dead_code)] // exercised in input unit tests; production uses TerminalRuntime helpers
pub fn encode_cursor_key(code: KeyCode, application_cursor: bool) -> Vec<u8> {
match (code, application_cursor) {
@@ -626,6 +637,48 @@ mod tests {
}
#[test]
+ fn legacy_ctrl_shift_letter_preserves_shift_with_csi_u() {
+ for ch in ['c', 'C'] {
+ let key = KeyEvent::new(
+ KeyCode::Char(ch),
+ KeyModifiers::CONTROL | KeyModifiers::SHIFT,
+ );
+ assert_eq!(
+ encode_key(key, KeyboardProtocol::Legacy),
+ b"\x1b[99;6u",
+ "ch={ch}"
+ );
+ }
+ }
+
+ #[test]
+ fn legacy_ctrl_alt_shift_letter_uses_csi_u() {
+ let key = KeyEvent::new(
+ KeyCode::Char('c'),
+ KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT,
+ );
+ assert_eq!(encode_key(key, KeyboardProtocol::Legacy), b"\x1b[99;8u");
+ }
+
+ #[test]
+ fn legacy_ctrl_shift_letter_from_kitty_host_input_keeps_shift() {
+ let key = parse_terminal_key_sequence("\x1b[99:67;6:1u").expect("Ctrl+Shift+C");
+ assert_eq!(
+ encode_terminal_key(key, KeyboardProtocol::Legacy),
+ b"\x1b[99;6u"
+ );
+ }
+
+ #[test]
+ fn legacy_ctrl_shift_punctuation_keeps_c0_byte() {
+ let key = KeyEvent::new(
+ KeyCode::Char('_'),
+ KeyModifiers::CONTROL | KeyModifiers::SHIFT,
+ );
+ assert_eq!(encode_key(key, KeyboardProtocol::Legacy), vec![31]);
+ }
+
+ #[test]
fn legacy_ctrl_slash_aliases_ctrl_underscore() {
let key = KeyEvent::new(KeyCode::Char('/'), KeyModifiers::CONTROL);
assert_eq!(encode_key(key, KeyboardProtocol::Legacy), vec![31]);
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs
index 53d8e32d..d0b9d3ca 100644
--- a/src/pane/terminal.rs
+++ b/src/pane/terminal.rs
@@ -4959,7 +4959,7 @@ mod tests {
let encoded = pane.encode_terminal_key(
crate::input::TerminalKey::new(
crossterm::event::KeyCode::Char('a'),
- crossterm::event::KeyModifiers::CONTROL | crossterm::event::KeyModifiers::SHIFT,
+ crossterm::event::KeyModifiers::CONTROL,
),
crate::input::KeyboardProtocol::Legacy,
);