apprt: add action for key table activation/deactivation

This commit is contained in:
Mitchell Hashimoto
2025-12-20 19:47:51 -08:00
parent 028691766d
commit 44972198ae
4 changed files with 126 additions and 14 deletions

View File

@@ -691,6 +691,27 @@ typedef struct {
ghostty_input_trigger_s trigger;
} ghostty_action_key_sequence_s;
// apprt.action.KeyTable.Tag
typedef enum {
GHOSTTY_KEY_TABLE_ACTIVATE,
GHOSTTY_KEY_TABLE_DEACTIVATE,
GHOSTTY_KEY_TABLE_DEACTIVATE_ALL,
} ghostty_action_key_table_tag_e;
// apprt.action.KeyTable.CValue
typedef union {
struct {
const char *name;
size_t len;
} activate;
} ghostty_action_key_table_u;
// apprt.action.KeyTable.C
typedef struct {
ghostty_action_key_table_tag_e tag;
ghostty_action_key_table_u value;
} ghostty_action_key_table_s;
// apprt.action.ColorKind
typedef enum {
GHOSTTY_ACTION_COLOR_KIND_FOREGROUND = -1,
@@ -836,6 +857,7 @@ typedef enum {
GHOSTTY_ACTION_FLOAT_WINDOW,
GHOSTTY_ACTION_SECURE_INPUT,
GHOSTTY_ACTION_KEY_SEQUENCE,
GHOSTTY_ACTION_KEY_TABLE,
GHOSTTY_ACTION_COLOR_CHANGE,
GHOSTTY_ACTION_RELOAD_CONFIG,
GHOSTTY_ACTION_CONFIG_CHANGE,
@@ -881,6 +903,7 @@ typedef union {
ghostty_action_float_window_e float_window;
ghostty_action_secure_input_e secure_input;
ghostty_action_key_sequence_s key_sequence;
ghostty_action_key_table_s key_table;
ghostty_action_color_change_s color_change;
ghostty_action_reload_config_s reload_config;
ghostty_action_config_change_s config_change;

View File

@@ -5631,28 +5631,68 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
.once = tag == .activate_key_table_once,
});
// Notify the UI.
_ = self.rt_app.performAction(
.{ .surface = self },
.key_table,
.{ .activate = name },
) catch |err| {
log.warn(
"failed to notify app of key table err={}",
.{err},
);
};
log.debug("key table activated: {s}", .{name});
},
.deactivate_key_table => switch (self.keyboard.table_stack.items.len) {
// No key table active. This does nothing.
0 => return false,
.deactivate_key_table => {
switch (self.keyboard.table_stack.items.len) {
// No key table active. This does nothing.
0 => return false,
// Final key table active, clear our state.
1 => self.keyboard.table_stack.clearAndFree(self.alloc),
// Final key table active, clear our state.
1 => self.keyboard.table_stack.clearAndFree(self.alloc),
// Restore the prior key table. We don't free any memory in
// this case because we assume it will be freed later when
// we finish our key table.
else => _ = self.keyboard.table_stack.pop(),
// Restore the prior key table. We don't free any memory in
// this case because we assume it will be freed later when
// we finish our key table.
else => _ = self.keyboard.table_stack.pop(),
}
// Notify the UI.
_ = self.rt_app.performAction(
.{ .surface = self },
.key_table,
.deactivate,
) catch |err| {
log.warn(
"failed to notify app of key table err={}",
.{err},
);
};
},
.deactivate_all_key_tables => switch (self.keyboard.table_stack.items.len) {
// No key table active. This does nothing.
0 => return false,
.deactivate_all_key_tables => {
switch (self.keyboard.table_stack.items.len) {
// No key table active. This does nothing.
0 => return false,
// Clear the entire table stack.
else => self.keyboard.table_stack.clearAndFree(self.alloc),
// Clear the entire table stack.
else => self.keyboard.table_stack.clearAndFree(self.alloc),
}
// Notify the UI.
_ = self.rt_app.performAction(
.{ .surface = self },
.key_table,
.deactivate_all,
) catch |err| {
log.warn(
"failed to notify app of key table err={}",
.{err},
);
};
},
.crash => |location| switch (location) {

View File

@@ -250,6 +250,9 @@ pub const Action = union(Key) {
/// key mode because other input may be ignored.
key_sequence: KeySequence,
/// A key table has been activated or deactivated.
key_table: KeyTable,
/// A terminal color was changed programmatically through things
/// such as OSC 10/11.
color_change: ColorChange,
@@ -371,6 +374,7 @@ pub const Action = union(Key) {
float_window,
secure_input,
key_sequence,
key_table,
color_change,
reload_config,
config_change,
@@ -711,6 +715,50 @@ pub const KeySequence = union(enum) {
}
};
pub const KeyTable = union(enum) {
activate: []const u8,
deactivate,
deactivate_all,
// Sync with: ghostty_action_key_table_tag_e
pub const Tag = enum(c_int) {
activate,
deactivate,
deactivate_all,
};
// Sync with: ghostty_action_key_table_u
pub const CValue = extern union {
activate: extern struct {
name: [*]const u8,
len: usize,
},
};
// Sync with: ghostty_action_key_table_s
pub const C = extern struct {
tag: Tag,
value: CValue,
};
pub fn cval(self: KeyTable) C {
return switch (self) {
.activate => |name| .{
.tag = .activate,
.value = .{ .activate = .{ .name = name.ptr, .len = name.len } },
},
.deactivate => .{
.tag = .deactivate,
.value = undefined,
},
.deactivate_all => .{
.tag = .deactivate_all,
.value = undefined,
},
};
}
};
pub const ColorChange = extern struct {
kind: ColorKind,
r: u8,

View File

@@ -744,6 +744,7 @@ pub const Application = extern struct {
.toggle_background_opacity,
.cell_size,
.key_sequence,
.key_table,
.render_inspector,
.renderer_health,
.color_change,