terminal: kitty keyboard actions

pull/9342/head
Mitchell Hashimoto 2025-10-23 20:49:50 -07:00
parent b7ea979f38
commit 2520e27aef
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
2 changed files with 63 additions and 45 deletions

View File

@ -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 => {},
}
}
};

View File

@ -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 {