input: change the key_is_binding to return some information

This commit is contained in:
Mitchell Hashimoto
2026-01-09 06:32:37 -08:00
parent 18535f04d1
commit 856ef1fc1b
4 changed files with 62 additions and 18 deletions

View File

@@ -102,6 +102,13 @@ typedef enum {
GHOSTTY_MODS_SUPER_RIGHT = 1 << 9,
} ghostty_input_mods_e;
typedef enum {
GHOSTTY_BINDING_FLAGS_CONSUMED = 1 << 0,
GHOSTTY_BINDING_FLAGS_ALL = 1 << 1,
GHOSTTY_BINDING_FLAGS_GLOBAL = 1 << 2,
GHOSTTY_BINDING_FLAGS_PERFORMABLE = 1 << 3,
} ghostty_binding_flags_e;
typedef enum {
GHOSTTY_ACTION_RELEASE,
GHOSTTY_ACTION_PRESS,
@@ -1058,7 +1065,9 @@ void ghostty_surface_set_color_scheme(ghostty_surface_t,
ghostty_input_mods_e ghostty_surface_key_translation_mods(ghostty_surface_t,
ghostty_input_mods_e);
bool ghostty_surface_key(ghostty_surface_t, ghostty_input_key_s);
bool ghostty_surface_key_is_binding(ghostty_surface_t, ghostty_input_key_s);
bool ghostty_surface_key_is_binding(ghostty_surface_t,
ghostty_input_key_s,
ghostty_binding_flags_e*);
void ghostty_surface_text(ghostty_surface_t, const char*, uintptr_t);
void ghostty_surface_preedit(ghostty_surface_t, const char*, uintptr_t);
bool ghostty_surface_mouse_captured(ghostty_surface_t);

View File

@@ -2579,7 +2579,7 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void {
pub fn keyEventIsBinding(
self: *Surface,
event_orig: input.KeyEvent,
) bool {
) ?input.Binding.Flags {
// Apply key remappings for consistency with keyCallback
var event = event_orig;
if (self.config.key_remaps.isRemapped(event_orig.mods)) {
@@ -2587,26 +2587,35 @@ pub fn keyEventIsBinding(
}
switch (event.action) {
.release => return false,
.release => return null,
.press, .repeat => {},
}
// If we're in a sequence, check the sequence set
if (self.keyboard.sequence_set) |set| {
return set.getEvent(event) != null;
}
// Check active key tables (inner-most to outer-most)
const table_items = self.keyboard.table_stack.items;
for (0..table_items.len) |i| {
const rev_i: usize = table_items.len - 1 - i;
if (table_items[rev_i].set.getEvent(event) != null) {
return true;
// Look up our entry
const entry: input.Binding.Set.Entry = entry: {
// If we're in a sequence, check the sequence set
if (self.keyboard.sequence_set) |set| {
break :entry set.getEvent(event) orelse return null;
}
}
// Check the root set
return self.config.keybind.set.getEvent(event) != null;
// Check active key tables (inner-most to outer-most)
const table_items = self.keyboard.table_stack.items;
for (0..table_items.len) |i| {
const rev_i: usize = table_items.len - 1 - i;
if (table_items[rev_i].set.getEvent(event)) |entry| {
break :entry entry;
}
}
// Check the root set
break :entry self.config.keybind.set.getEvent(event) orelse return null;
};
// Return flags based on the
return switch (entry.value_ptr.*) {
.leader => .{},
inline .leaf, .leaf_chained => |v| v.flags,
};
}
/// Called for any key events. This handles keybindings, encoding and

View File

@@ -1751,13 +1751,18 @@ pub const CAPI = struct {
export fn ghostty_surface_key_is_binding(
surface: *Surface,
event: KeyEvent,
c_flags: ?*input.Binding.Flags.C,
) bool {
const core_event = event.keyEvent().core() orelse {
log.warn("error processing key event", .{});
return false;
};
return surface.core_surface.keyEventIsBinding(core_event);
const flags = surface.core_surface.keyEventIsBinding(
core_event,
) orelse return false;
if (c_flags) |ptr| ptr.* = flags.cval();
return true;
}
/// Send raw text to the terminal. This is treated like a paste

View File

@@ -45,6 +45,27 @@ pub const Flags = packed struct {
/// performed. If the action can't be performed then the binding acts as
/// if it doesn't exist.
performable: bool = false,
/// C type
pub const C = u8;
/// Converts this to a C-compatible value.
///
/// Sync with ghostty.h for enums.
pub fn cval(self: Flags) C {
const Backing = @typeInfo(Flags).@"struct".backing_integer.?;
return @as(Backing, @bitCast(self));
}
test "cval" {
const testing = std.testing;
try testing.expectEqual(@as(u8, 0b0001), (Flags{}).cval());
try testing.expectEqual(@as(u8, 0b0000), (Flags{ .consumed = false }).cval());
try testing.expectEqual(@as(u8, 0b0011), (Flags{ .all = true }).cval());
try testing.expectEqual(@as(u8, 0b0101), (Flags{ .global = true }).cval());
try testing.expectEqual(@as(u8, 0b1001), (Flags{ .performable = true }).cval());
try testing.expectEqual(@as(u8, 0b1111), (Flags{ .consumed = true, .all = true, .global = true, .performable = true }).cval());
}
};
/// Full binding parser. The binding parser is implemented as an iterator