mirror of
https://github.com/herdrdev/herdr.git
synced 2026-11-06 00:06:18 +00:00
fix: send ctrl+shift+letter to plain shells as its control byte
This commit is contained in:
@@ -252,6 +252,19 @@ fn legacy_shell_exception(pane_mode: &[u8], key: &str, mods: u16) -> Option<Vec<
|
||||
("m", ghostty::MOD_CTRL) => b"\r",
|
||||
("i", ghostty::MOD_CTRL) | ("tab", ghostty::MOD_CTRL) => b"\t",
|
||||
("[", ghostty::MOD_CTRL) => b"\x1b",
|
||||
// Real terminals (lab: Alacritty, WezTerm) send the control byte.
|
||||
(letter, m)
|
||||
if m == ghostty::MOD_CTRL | ghostty::MOD_SHIFT
|
||||
&& letter.len() == 1
|
||||
&& letter.as_bytes()[0].is_ascii_lowercase() =>
|
||||
{
|
||||
let control = [letter.as_bytes()[0] & 0x1f];
|
||||
return Some(if alt {
|
||||
[b"\x1b".as_slice(), &control].concat()
|
||||
} else {
|
||||
control.to_vec()
|
||||
});
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(if alt {
|
||||
|
||||
@@ -118,6 +118,24 @@ pub(super) fn legacy_shell_key(key: crate::input::TerminalKey) -> crate::input::
|
||||
KeyCode::Tab
|
||||
}
|
||||
(KeyCode::Char('['), KeyModifiers::CONTROL) => KeyCode::Esc,
|
||||
// Ghostty reports Ctrl+Shift+letter as CSI u; every terminal sends the
|
||||
// plain control byte, which is all a shell can read.
|
||||
// WezTerm names it Ctrl plus an uppercase letter, without Shift.
|
||||
(KeyCode::Char(c), chord)
|
||||
if chord - KeyModifiers::SHIFT == KeyModifiers::CONTROL
|
||||
&& c.is_ascii_alphabetic()
|
||||
&& (chord.contains(KeyModifiers::SHIFT) || c.is_ascii_uppercase()) =>
|
||||
{
|
||||
// Ctrl+I and Ctrl+M then follow the rows above (Tab, Enter).
|
||||
return legacy_shell_key(
|
||||
crate::input::TerminalKey::new(
|
||||
KeyCode::Char(c.to_ascii_lowercase()),
|
||||
KeyModifiers::CONTROL | alt,
|
||||
)
|
||||
.with_kind(key.kind)
|
||||
.with_repeat_count(key.repeat_count),
|
||||
);
|
||||
}
|
||||
_ => return key,
|
||||
};
|
||||
crate::input::TerminalKey::new(classic, alt)
|
||||
|
||||
@@ -5001,6 +5001,29 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_shift_letters_reach_plain_shells_as_control_bytes() {
|
||||
// Lab capture: Alacritty and WezTerm (with or without Kitty keyboard)
|
||||
// send 0x01 for Ctrl+Shift+A; Ghostty's CSI u prints as garbage in a shell.
|
||||
for (host_bytes, plain, kitty) in [
|
||||
("\x1b[97;6u", &b"\x01"[..], &b"\x1b[97;6u"[..]),
|
||||
("\x1b[65;5u", b"\x01", b"\x1b[97;6u"),
|
||||
("\x1b[97;8u", b"\x1b\x01", b"\x1b[97;8u"),
|
||||
] {
|
||||
let key = crate::input::parse_terminal_key_sequence(host_bytes).expect("kitty key");
|
||||
assert_eq!(
|
||||
test_encode_key_for_app(b"", key.clone()),
|
||||
plain,
|
||||
"{host_bytes:?} in a plain shell"
|
||||
);
|
||||
assert_eq!(
|
||||
test_encode_key_for_app(b"\x1b[>1u", key),
|
||||
kitty,
|
||||
"{host_bytes:?} in a Kitty pane"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_alt_prefixes_the_produced_non_ascii_text() {
|
||||
// A legacy host's Alt+Shift+\u{f6}. libghostty on macOS would prefix the
|
||||
|
||||
Reference in New Issue
Block a user