aboutsummaryrefslogtreecommitdiff
path: root/src/pane/kitty_keyboard.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-01 15:48:22 +0300
committerGitHub <noreply@github.com>2026-09-01 15:48:22 +0300
commit207be3c771d281baae6e5fa0fb74be9a056e97a2 (patch)
treed7b037ecdf629224a048c841d11659efdc23507d /src/pane/kitty_keyboard.rs
parent8a6d697308162874a236c84aac0b0f8c7ac01f83 (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.rs75
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);
+ }
}