From 2520e27aefddfb681c7e306ffac26c470c494fa6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 23 Oct 2025 20:49:50 -0700 Subject: [PATCH] terminal: kitty keyboard actions --- src/terminal/stream.zig | 66 +++++++++++++++++++++++------------ src/termio/stream_handler.zig | 42 +++++++++++----------- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index e85176dbd..b74764fb4 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -86,6 +86,11 @@ pub const Action = union(Key) { protected_mode_dec, xtversion, kitty_keyboard_query, + kitty_keyboard_push: KittyKeyboardFlags, + kitty_keyboard_pop: u16, + kitty_keyboard_set: KittyKeyboardFlags, + kitty_keyboard_set_or: KittyKeyboardFlags, + kitty_keyboard_set_not: KittyKeyboardFlags, prompt_end, end_of_input, end_hyperlink, @@ -150,6 +155,11 @@ pub const Action = union(Key) { "protected_mode_dec", "xtversion", "kitty_keyboard_query", + "kitty_keyboard_push", + "kitty_keyboard_pop", + "kitty_keyboard_set", + "kitty_keyboard_set_or", + "kitty_keyboard_set_not", "prompt_end", "end_of_input", "end_hyperlink", @@ -222,6 +232,16 @@ pub const Action = union(Key) { top_left: u16, bottom_right: u16, }; + + pub const KittyKeyboardFlags = struct { + flags: kitty.KeyFlags, + + pub const C = u8; + + pub fn cval(self: KittyKeyboardFlags) KittyKeyboardFlags.C { + return @intCast(self.flags.int()); + } + }; }; /// Returns a type that can process a stream of tty control characters. @@ -1564,7 +1584,7 @@ pub fn Stream(comptime Handler: type) type { 1 => switch (input.intermediates[0]) { '?' => try self.handler.vt(.kitty_keyboard_query, {}), - '>' => if (@hasDecl(T, "pushKittyKeyboard")) push: { + '>' => push: { const flags: u5 = if (input.params.len == 1) std.math.cast(u5, input.params[0]) orelse { log.warn("invalid pushKittyKeyboard command: {f}", .{input}); @@ -1573,19 +1593,19 @@ pub fn Stream(comptime Handler: type) type { else 0; - try self.handler.pushKittyKeyboard(@bitCast(flags)); + try self.handler.vt(.kitty_keyboard_push, .{ .flags = @as(kitty.KeyFlags, @bitCast(flags)) }); }, - '<' => if (@hasDecl(T, "popKittyKeyboard")) { + '<' => { const number: u16 = if (input.params.len == 1) input.params[0] else 1; - try self.handler.popKittyKeyboard(number); + try self.handler.vt(.kitty_keyboard_pop, number); }, - '=' => if (@hasDecl(T, "setKittyKeyboard")) set: { + '=' => set: { const flags: u5 = if (input.params.len >= 1) std.math.cast(u5, input.params[0]) orelse { log.warn("invalid setKittyKeyboard command: {f}", .{input}); @@ -1599,20 +1619,23 @@ pub fn Stream(comptime Handler: type) type { else 1; - const mode: kitty.KeySetMode = switch (number) { - 1 => .set, - 2 => .@"or", - 3 => .not, + const action_tag: streampkg.Action.Tag = switch (number) { + 1 => .kitty_keyboard_set, + 2 => .kitty_keyboard_set_or, + 3 => .kitty_keyboard_set_not, else => { log.warn("invalid setKittyKeyboard command: {f}", .{input}); break :set; }, }; - try self.handler.setKittyKeyboard( - mode, - @bitCast(flags), - ); + const kitty_flags: streampkg.Action.KittyKeyboardFlags = .{ .flags = @as(kitty.KeyFlags, @bitCast(flags)) }; + switch (action_tag) { + .kitty_keyboard_set => try self.handler.vt(.kitty_keyboard_set, kitty_flags), + .kitty_keyboard_set_or => try self.handler.vt(.kitty_keyboard_set_or, kitty_flags), + .kitty_keyboard_set_not => try self.handler.vt(.kitty_keyboard_set_not, kitty_flags), + else => unreachable, + } }, else => log.warn( @@ -2254,18 +2277,15 @@ test "stream: pop kitty keyboard with no params defaults to 1" { const Self = @This(); n: u16 = 0, - pub fn popKittyKeyboard(self: *Self, n: u16) !void { - self.n = n; - } - pub fn vt( - self: *@This(), - comptime action: anytype, - value: anytype, + self: *Self, + comptime action: streampkg.Action.Tag, + value: streampkg.Action.Value(action), ) !void { - _ = self; - _ = action; - _ = value; + switch (action) { + .kitty_keyboard_pop => self.n = value, + else => {}, + } } }; diff --git a/src/termio/stream_handler.zig b/src/termio/stream_handler.zig index 68c7d00b7..ba9746ca2 100644 --- a/src/termio/stream_handler.zig +++ b/src/termio/stream_handler.zig @@ -272,6 +272,26 @@ pub const StreamHandler = struct { .protected_mode_dec => self.terminal.setProtectedMode(.dec), .xtversion => try self.reportXtversion(), .kitty_keyboard_query => try self.queryKittyKeyboard(), + .kitty_keyboard_push => { + log.debug("pushing kitty keyboard mode: {}", .{value.flags}); + self.terminal.screen.kitty_keyboard.push(value.flags); + }, + .kitty_keyboard_pop => { + log.debug("popping kitty keyboard mode n={}", .{value}); + self.terminal.screen.kitty_keyboard.pop(@intCast(value)); + }, + .kitty_keyboard_set => { + log.debug("setting kitty keyboard mode: set {}", .{value.flags}); + self.terminal.screen.kitty_keyboard.set(.set, value.flags); + }, + .kitty_keyboard_set_or => { + log.debug("setting kitty keyboard mode: or {}", .{value.flags}); + self.terminal.screen.kitty_keyboard.set(.@"or", value.flags); + }, + .kitty_keyboard_set_not => { + log.debug("setting kitty keyboard mode: not {}", .{value.flags}); + self.terminal.screen.kitty_keyboard.set(.not, value.flags); + }, .prompt_end => try self.promptEnd(), .end_of_input => try self.endOfInput(), .end_hyperlink => try self.endHyperlink(), @@ -865,28 +885,6 @@ pub const StreamHandler = struct { }); } - pub fn pushKittyKeyboard( - self: *StreamHandler, - flags: terminal.kitty.KeyFlags, - ) !void { - log.debug("pushing kitty keyboard mode: {}", .{flags}); - self.terminal.screen.kitty_keyboard.push(flags); - } - - pub fn popKittyKeyboard(self: *StreamHandler, n: u16) !void { - log.debug("popping kitty keyboard mode n={}", .{n}); - self.terminal.screen.kitty_keyboard.pop(@intCast(n)); - } - - pub fn setKittyKeyboard( - self: *StreamHandler, - mode: terminal.kitty.KeySetMode, - flags: terminal.kitty.KeyFlags, - ) !void { - log.debug("setting kitty keyboard mode: {} {}", .{ mode, flags }); - self.terminal.screen.kitty_keyboard.set(mode, flags); - } - pub fn reportXtversion( self: *StreamHandler, ) !void {