diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-01 15:48:22 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-01 15:48:22 +0300 |
| commit | 207be3c771d281baae6e5fa0fb74be9a056e97a2 (patch) | |
| tree | d7b037ecdf629224a048c841d11659efdc23507d /src/pane/kitty_keyboard.rs | |
| parent | 8a6d697308162874a236c84aac0b0f8c7ac01f83 (diff) | |
refactor: render the shell in the client (#3487)
* feat: add opt-in client-rendered shell
* feat: add client-owned shell mouse controls
* feat: complete client-owned shell chrome
* feat: add client-owned shell settings and notifications
* feat: run client shell custom commands
* feat: render popup terminals in client shell
* feat: add client-owned scrollback and copy mode
* fix: preserve client worktree trust defaults
* feat: render startup diagnostics in client shell
* feat: render onboarding in client shell
* feat: render product announcements in client shell
* feat: render release notes in client shell
* feat: render mobile switcher in client shell
* feat: render kitty graphics in client shell
* fix: preserve client shell lifecycle coherence
* refactor: route client shell commands through endpoint connections
* refactor: make the client-rendered shell the default
* refactor: remove monolithic launch mode
* refactor: complete client-rendered shell cutover
* perf: retain client pane surface updates
* perf: scale retained pane surface updates
* perf: remove remote pane input latency
* test: make client shell checks platform-independent
* fix: prevent client timer starvation
* test: fix client shell platform gates
* test: accept retained surfaces in cross-area checks
* test: make client mode assertions platform-neutral
* fix: badge only outdated integrations
Diffstat (limited to 'src/pane/kitty_keyboard.rs')
| -rw-r--r-- | src/pane/kitty_keyboard.rs | 75 |
1 files changed, 55 insertions, 20 deletions
diff --git a/src/pane/kitty_keyboard.rs b/src/pane/kitty_keyboard.rs index 30ab62ba..74de2538 100644 --- a/src/pane/kitty_keyboard.rs +++ b/src/pane/kitty_keyboard.rs @@ -3,8 +3,7 @@ pub(crate) struct KittyKeyboardTracker { pending: Vec<u8>, stack: Vec<u16>, flags: u16, - #[cfg(windows)] - modify_other_keys: bool, + modify_other_keys_level: u8, } impl KittyKeyboardTracker { @@ -32,9 +31,10 @@ impl KittyKeyboardTracker { self.store_pending(&bytes[index..]); break; } - #[cfg(windows)] if bytes[index + 1] == b'c' { - self.modify_other_keys = false; + self.stack.clear(); + self.flags = 0; + self.modify_other_keys_level = 0; } if bytes[index + 1] != b'[' { index += 1; @@ -52,7 +52,6 @@ impl KittyKeyboardTracker { 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] @@ -61,7 +60,7 @@ impl KittyKeyboardTracker { !params.contains(&b';') && parse_kitty_keyboard_flags(params) == 4 }) => { - self.modify_other_keys = false; + self.modify_other_keys_level = 0; } _ => {} } @@ -69,18 +68,21 @@ impl KittyKeyboardTracker { } } + pub(crate) fn modify_other_keys_level(&self) -> u8 { + self.modify_other_keys_level + } + #[cfg(windows)] pub(crate) fn modify_other_keys_enabled(&self) -> bool { - self.modify_other_keys + self.modify_other_keys_level > 0 } - #[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; + self.modify_other_keys_level = 0; return; } @@ -91,8 +93,8 @@ impl KittyKeyboardTracker { return; } if parse_kitty_keyboard_flags(resource) == 4 { - self.modify_other_keys = - value.is_some_and(|value| parse_kitty_keyboard_flags(value) != 0); + self.modify_other_keys_level = + value.map(parse_kitty_keyboard_flags).unwrap_or(0).min(2) as u8; } } @@ -128,17 +130,22 @@ impl KittyKeyboardTracker { #[cfg(unix)] pub(crate) fn replay_ansi(&self) -> Option<String> { - if self.stack.is_empty() { - return (self.flags != 0).then(|| format!("\x1b[={}u", self.flags)); - } - let mut ansi = String::new(); - let baseline = self.stack[0]; - if baseline != 0 { - ansi.push_str(&format!("\x1b[={baseline}u")); + if self.stack.is_empty() { + if self.flags != 0 { + ansi.push_str(&format!("\x1b[={}u", self.flags)); + } + } else { + let baseline = self.stack[0]; + if baseline != 0 { + ansi.push_str(&format!("\x1b[={baseline}u")); + } + for flags in self.stack.iter().skip(1).copied().chain([self.flags]) { + ansi.push_str(&format!("\x1b[>{flags}u")); + } } - for flags in self.stack.iter().skip(1).copied().chain([self.flags]) { - ansi.push_str(&format!("\x1b[>{flags}u")); + if self.modify_other_keys_level > 0 { + ansi.push_str(&format!("\x1b[>4;{}m", self.modify_other_keys_level)); } (!ansi.is_empty()).then_some(ansi) } @@ -166,6 +173,7 @@ mod tests { assert_eq!(tracker.flags, 1); assert_eq!(tracker.stack, vec![0]); + assert_eq!(tracker.modify_other_keys_level(), 1); #[cfg(windows)] { assert!(tracker.modify_other_keys_enabled()); @@ -175,4 +183,31 @@ mod tests { assert!(!tracker.modify_other_keys_enabled()); } } + + #[test] + fn ris_clears_kitty_flags_stack_and_modify_other_keys() { + let mut tracker = KittyKeyboardTracker::default(); + tracker.observe(b"\x1b[>1u\x1b[>5u\x1b[>4;2m\x1bc"); + + assert_eq!(tracker.flags, 0); + assert!(tracker.stack.is_empty()); + assert_eq!(tracker.modify_other_keys_level(), 0); + #[cfg(unix)] + assert_eq!(tracker.replay_ansi(), None); + } + + #[test] + fn tracks_and_replays_exact_modify_other_keys_level() { + let mut tracker = KittyKeyboardTracker::default(); + + tracker.observe(b"\x1b[>4;1m"); + assert_eq!(tracker.modify_other_keys_level(), 1); + #[cfg(unix)] + assert_eq!(tracker.replay_ansi().as_deref(), Some("\x1b[>4;1m")); + + tracker.observe(b"\x1b[>4;2m"); + assert_eq!(tracker.modify_other_keys_level(), 2); + tracker.observe(b"\x1b[>4;0m"); + assert_eq!(tracker.modify_other_keys_level(), 0); + } } |
