diff --git a/src/terminal/kitty.zig b/src/terminal/kitty.zig index cfba9c405..35f92da02 100644 --- a/src/terminal/kitty.zig +++ b/src/terminal/kitty.zig @@ -19,6 +19,24 @@ pub const KeyFlagStack = struct { return self.flags[self.idx]; } + /// Perform the "set" operation as described in the spec for + /// the CSI = u sequence. + pub fn set( + self: *KeyFlagStack, + mode: KeySetMode, + v: KeyFlags, + ) void { + switch (mode) { + .set => self.flags[self.idx] = v, + .@"or" => self.flags[self.idx] = @bitCast( + self.flags[self.idx].int() | v.int(), + ), + .not => self.flags[self.idx] = @bitCast( + self.flags[self.idx].int() & ~v.int(), + ), + } + } + /// Push a new set of flags onto the stack. If the stack is full /// then the oldest entry is evicted. pub fn push(self: *KeyFlagStack, flags: KeyFlags) void { @@ -87,6 +105,9 @@ pub const KeyFlags = packed struct(u5) { } }; +/// The possible modes for setting the key flags. +pub const KeySetMode = enum { set, @"or", not }; + test "KeyFlagStack: push pop" { const testing = std.testing; var stack: KeyFlagStack = .{}; @@ -106,3 +127,28 @@ test "KeyFlagStack: pop big number" { stack.pop(100); try testing.expectEqual(KeyFlags{}, stack.current()); } + +test "KeyFlagStack: set" { + const testing = std.testing; + var stack: KeyFlagStack = .{}; + stack.set(.set, .{ .disambiguate = true }); + try testing.expectEqual( + KeyFlags{ .disambiguate = true }, + stack.current(), + ); + + stack.set(.@"or", .{ .report_events = true }); + try testing.expectEqual( + KeyFlags{ + .disambiguate = true, + .report_events = true, + }, + stack.current(), + ); + + stack.set(.not, .{ .report_events = true }); + try testing.expectEqual( + KeyFlags{ .disambiguate = true }, + stack.current(), + ); +} diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index a152b1bc9..7dd75513f 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -673,7 +673,35 @@ pub fn Stream(comptime Handler: type) type { try self.handler.popKittyKeyboard(number); }, - '=' => @panic("TODO! DO NOT MERGE"), + '=' => if (@hasDecl(T, "setKittyKeyboard")) set: { + const flags: u5 = if (action.params.len >= 1) + std.math.cast(u5, action.params[0]) orelse { + log.warn("invalid setKittyKeyboard command: {}", .{action}); + break :set; + } + else + 0; + + const number: u16 = if (action.params.len >= 2) + action.params[1] + else + 1; + + const mode: kitty.KeySetMode = switch (number) { + 0 => .set, + 1 => .@"or", + 2 => .not, + else => { + log.warn("invalid setKittyKeyboard command: {}", .{action}); + break :set; + }, + }; + + try self.handler.setKittyKeyboard( + mode, + @bitCast(flags), + ); + }, else => log.warn( "unknown CSI s with intermediate: {}", diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 91b955753..cbb4f0fd4 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -1435,6 +1435,15 @@ const StreamHandler = struct { 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); + } + //------------------------------------------------------------------------- // OSC