Commit 54b212d

Umputun  ·  2026-09-25 15:52:53 -0400 EDT
parent 878a8b0
fix: kitty keyboard status reply should not take leadership (#266)

isUserInput accepted any CSI ending in u, so the terminal's CSI ? flags u reply to a keyboard status query promoted a follower. Skip that sequence and keep scanning the payload.
2 files changed,  +20, -1
+4, -0
......@@ -8,6 +8,10 @@ Use spec: https://common-changelog.org/
88
99 - Completions for [`yash`](https://magicant.github.io/yash/)
1010
11+### Fixed
12+
13+- Stop classifying complete kitty keyboard status replies (`CSI ? flags u`) as user input for leader selection
14+
1115 ## v0.8.1 - 2026-09-05
1216
1317 ### Fixed
+16, -1
......@@ -706,7 +706,10 @@ pub fn isUserInput(payload: []const u8) bool {
706706 // kitty keyboard: CSI ... u or CSI ... ~
707707 // legacy modified keys: CSI 27 ; ... ~
708708 // arrow/function keys with modifiers: CSI 1 ; <mod> A-D
709- if (csi.final == 'u' or csi.final == '~') return true;
709+ // CSI ? flags u is the terminal's reply to a kitty keyboard status
710+ // query, not a key press; skip it and keep scanning the payload
711+ const status_reply = csi.final == 'u' and csi.intermediates.len > 0 and csi.intermediates[0] == '?';
712+ if (!status_reply and (csi.final == 'u' or csi.final == '~')) return true;
710713 // modified arrow keys (e.g., Ctrl+F sends CSI 1;5C in legacy mode)
711714 if (csi.final >= 'A' and csi.final <= 'D' and csi.params.len > 1) return true;
712715 // mouse events: CSI M (basic) or CSI < (SGR extended) - EXCLUDE these
......@@ -2191,3 +2194,15 @@ test "stripAnsi: only escape sequences" {
21912194 defer alloc.free(result);
21922195 try testing.expectEqualStrings("", result);
21932196 }
2197+
2198+test "isUserInput: kitty keyboard status reply is a terminal response" {
2199+ // CSI ? flags u answers the program's CSI ? u query; no key was pressed
2200+ try testing.expect(!isUserInput("\x1b[?1u"));
2201+ try testing.expect(!isUserInput("\x1b[?0u"));
2202+ try testing.expect(!isUserInput("\x1b[?31u"));
2203+ // a real key coalesced with the reply still counts
2204+ try testing.expect(isUserInput("\x1b[?1ua"));
2205+ try testing.expect(isUserInput("\x1b[?1u\r"));
2206+ try testing.expect(isUserInput("\x1b[?1u\x1b[97;1u"));
2207+ try testing.expect(!isUserInput("\x1b[?1u\x1b[97;1:3u"));
2208+}