aboutsummaryrefslogtreecommitdiff
path: root/src/pane
diff options
context:
space:
mode:
authorJJ Liebig <jonathan.liebig@gmail.com>2026-07-27 01:16:57 +0200
committerGitHub <noreply@github.com>2026-07-27 02:16:57 +0300
commit04205ac070f1b5673b7afec930a561900a3bb31e (patch)
tree68488c31661d37678afe80e4a57025729c1efe38 /src/pane
parentbb29eedb7209a0d5e91052458ce76bc7e4259d18 (diff)
fix: preserve shift-enter in windows panes (#1909)
refs #1743
Diffstat (limited to 'src/pane')
-rw-r--r--src/pane/kitty_keyboard.rs61
-rw-r--r--src/pane/terminal.rs30
2 files changed, 83 insertions, 8 deletions
diff --git a/src/pane/kitty_keyboard.rs b/src/pane/kitty_keyboard.rs
index 89824ee3..30ab62ba 100644
--- a/src/pane/kitty_keyboard.rs
+++ b/src/pane/kitty_keyboard.rs
@@ -3,6 +3,8 @@ pub(crate) struct KittyKeyboardTracker {
pending: Vec<u8>,
stack: Vec<u16>,
flags: u16,
+ #[cfg(windows)]
+ modify_other_keys: bool,
}
impl KittyKeyboardTracker {
@@ -30,6 +32,10 @@ impl KittyKeyboardTracker {
self.store_pending(&bytes[index..]);
break;
}
+ #[cfg(windows)]
+ if bytes[index + 1] == b'c' {
+ self.modify_other_keys = false;
+ }
if bytes[index + 1] != b'[' {
index += 1;
continue;
@@ -44,13 +50,52 @@ impl KittyKeyboardTracker {
break;
}
- if bytes[end] == b'u' {
- self.observe_csi_u(&bytes[index + 2..end]);
+ match bytes[end] {
+ b'u' => self.observe_csi_u(&bytes[index + 2..end]),
+ #[cfg(windows)]
+ b'm' => self.observe_modify_other_keys(&bytes[index + 2..end]),
+ #[cfg(windows)]
+ b'n' if bytes[index + 2..end]
+ .strip_prefix(b">")
+ .is_some_and(|params| {
+ !params.contains(&b';') && parse_kitty_keyboard_flags(params) == 4
+ }) =>
+ {
+ self.modify_other_keys = false;
+ }
+ _ => {}
}
index = end + 1;
}
}
+ #[cfg(windows)]
+ pub(crate) fn modify_other_keys_enabled(&self) -> bool {
+ self.modify_other_keys
+ }
+
+ #[cfg(windows)]
+ fn observe_modify_other_keys(&mut self, params: &[u8]) {
+ let Some(params) = params.strip_prefix(b">") else {
+ return;
+ };
+ if params.is_empty() {
+ self.modify_other_keys = false;
+ return;
+ }
+
+ let mut parts = params.split(|byte| *byte == b';');
+ let resource = parts.next().unwrap_or_default();
+ let value = parts.next();
+ if parts.next().is_some() {
+ return;
+ }
+ if parse_kitty_keyboard_flags(resource) == 4 {
+ self.modify_other_keys =
+ value.is_some_and(|value| parse_kitty_keyboard_flags(value) != 0);
+ }
+ }
+
fn store_pending(&mut self, bytes: &[u8]) {
self.pending.clear();
if bytes.len() <= 64 {
@@ -115,11 +160,19 @@ mod tests {
fn buffers_split_csi_sequences() {
let mut tracker = KittyKeyboardTracker::default();
- tracker.observe(b"\x1b[>1u\x1b[>5");
- tracker.observe(b"u\x1b[<");
+ tracker.observe(b"\x1b[>1u\x1b[>4;");
+ tracker.observe(b"01m\x1b[>5u\x1b[<");
tracker.observe(b"u");
assert_eq!(tracker.flags, 1);
assert_eq!(tracker.stack, vec![0]);
+ #[cfg(windows)]
+ {
+ assert!(tracker.modify_other_keys_enabled());
+ tracker.observe(b"\x1b[>1m");
+ assert!(tracker.modify_other_keys_enabled());
+ tracker.observe(b"\x1b[>04n");
+ assert!(!tracker.modify_other_keys_enabled());
+ }
}
}
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs
index d5070b9b..76ab20d3 100644
--- a/src/pane/terminal.rs
+++ b/src/pane/terminal.rs
@@ -1286,6 +1286,8 @@ impl GhosttyPaneTerminal {
let Ok(mut core) = self.core.lock() else {
return;
};
+ #[cfg(windows)]
+ core.kitty_keyboard.observe(ansi.as_bytes());
core.terminal.write(ansi.as_bytes());
#[cfg(windows)]
windows_recent_fallback::update(&mut core);
@@ -1565,6 +1567,9 @@ impl GhosttyPaneTerminal {
mouse_protocol_mode,
mouse_protocol_encoding,
mouse_alternate_scroll,
+ #[cfg(windows)]
+ modify_other_keys: core.kitty_keyboard.modify_other_keys_enabled(),
+ #[cfg(not(windows))]
modify_other_keys: core
.terminal
.keyboard_state_ansi()
@@ -1619,6 +1624,18 @@ impl GhosttyPaneTerminal {
key: crate::input::TerminalKey,
protocol: crate::input::KeyboardProtocol,
) -> Vec<u8> {
+ #[cfg(windows)]
+ if let Some(bytes) = crate::platform::encode_windows_conpty_shift_enter(key) {
+ if self.core.lock().is_ok_and(|core| {
+ core.terminal
+ .kitty_keyboard_flags()
+ .is_ok_and(|flags| flags == 0)
+ && !core.kitty_keyboard.modify_other_keys_enabled()
+ }) {
+ return bytes;
+ }
+ }
+
if ghostty_prefers_herdr_text_encoding(key) {
return crate::input::encode_terminal_key(key, protocol);
}
@@ -3967,11 +3984,16 @@ mod tests {
fn ghostty_modify_other_keys_mode_one_preserves_shift_enter() {
let (tx, _rx) = mpsc::channel(4);
let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap();
- let pane = GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap();
- let pane_id = PaneId::from_raw(1);
- pane.process_pty_bytes(pane_id, 0, b"\x1b[>4;1m", &tx);
-
+ let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
let key = crate::input::parse_terminal_key_sequence("\x1b[13;2u").unwrap();
+
+ #[cfg(windows)]
+ assert_eq!(
+ pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy),
+ b"\x1b[13;28;13;1;16;1_"
+ );
+
+ pane.seed_history_ansi("\x1b[>4;1m");
let encoded = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy);
assert_eq!(encoded, b"\x1b[27;2;13~");