diff options
| author | JJ Liebig <jonathan.liebig@gmail.com> | 2026-07-27 01:16:57 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-27 02:16:57 +0300 |
| commit | 04205ac070f1b5673b7afec930a561900a3bb31e (patch) | |
| tree | 68488c31661d37678afe80e4a57025729c1efe38 /src/pane | |
| parent | bb29eedb7209a0d5e91052458ce76bc7e4259d18 (diff) | |
fix: preserve shift-enter in windows panes (#1909)
refs #1743
Diffstat (limited to 'src/pane')
| -rw-r--r-- | src/pane/kitty_keyboard.rs | 61 | ||||
| -rw-r--r-- | src/pane/terminal.rs | 30 |
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~"); |
