mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
core: add keyEventIsBinding
This API can be used to determine if the next key event, if given as-is, would result in a key binding being triggered.
This commit is contained in:
@ -714,6 +714,7 @@ 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);
|
||||
void ghostty_surface_key(ghostty_surface_t, ghostty_input_key_s);
|
||||
bool ghostty_surface_key_is_binding(ghostty_surface_t, ghostty_input_key_s);
|
||||
void ghostty_surface_text(ghostty_surface_t, const char*, uintptr_t);
|
||||
bool ghostty_surface_mouse_captured(ghostty_surface_t);
|
||||
bool ghostty_surface_mouse_button(ghostty_surface_t,
|
||||
|
@ -1637,13 +1637,38 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void {
|
||||
try self.queueRender();
|
||||
}
|
||||
|
||||
/// Returns true if the given key event would trigger a keybinding
|
||||
/// if it were to be processed. This is useful for determining if
|
||||
/// a key event should be sent to the terminal or not.
|
||||
///
|
||||
/// Note that this function does not check if the binding itself
|
||||
/// is performable, only if the key event would trigger a binding.
|
||||
/// If a performable binding is found and the event is not performable,
|
||||
/// then Ghosty will act as though the binding does not exist.
|
||||
pub fn keyEventIsBinding(
|
||||
self: *Surface,
|
||||
event: input.KeyEvent,
|
||||
) bool {
|
||||
switch (event.action) {
|
||||
.release => return false,
|
||||
.press, .repeat => {},
|
||||
}
|
||||
|
||||
// Our keybinding set is either our current nested set (for
|
||||
// sequences) or the root set.
|
||||
const set = self.keyboard.bindings orelse &self.config.keybind.set;
|
||||
|
||||
// If we have a keybinding for this event then we return true.
|
||||
return set.getEvent(event) != null;
|
||||
}
|
||||
|
||||
/// Called for any key events. This handles keybindings, encoding and
|
||||
/// sending to the terminal, etc.
|
||||
pub fn keyCallback(
|
||||
self: *Surface,
|
||||
event: input.KeyEvent,
|
||||
) !InputEffect {
|
||||
// log.debug("text keyCallback event={}", .{event});
|
||||
log.debug("text keyCallback event={}", .{event});
|
||||
|
||||
// Crash metadata in case we crash in here
|
||||
crash.sentry.thread_state = self.crashThreadState();
|
||||
|
@ -147,12 +147,12 @@ pub const App = struct {
|
||||
self.core_app.focusEvent(focused);
|
||||
}
|
||||
|
||||
/// See CoreApp.keyEvent.
|
||||
pub fn keyEvent(
|
||||
/// Convert a C key event into a Zig key event.
|
||||
fn coreKeyEvent(
|
||||
self: *App,
|
||||
target: KeyTarget,
|
||||
event: KeyEvent,
|
||||
) !bool {
|
||||
) !?input.KeyEvent {
|
||||
const action = event.action;
|
||||
const keycode = event.keycode;
|
||||
const mods = event.mods;
|
||||
@ -243,7 +243,7 @@ pub const App = struct {
|
||||
result.text,
|
||||
) catch |err| {
|
||||
log.err("error in preedit callback err={}", .{err});
|
||||
return false;
|
||||
return null;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
@ -251,7 +251,7 @@ pub const App = struct {
|
||||
.app => {},
|
||||
.surface => |surface| surface.core_surface.preeditCallback(null) catch |err| {
|
||||
log.err("error in preedit callback err={}", .{err});
|
||||
return false;
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
@ -335,7 +335,7 @@ pub const App = struct {
|
||||
} else .invalid;
|
||||
|
||||
// Build our final key event
|
||||
const input_event: input.KeyEvent = .{
|
||||
return .{
|
||||
.action = action,
|
||||
.key = key,
|
||||
.physical_key = physical_key,
|
||||
@ -345,24 +345,39 @@ pub const App = struct {
|
||||
.utf8 = result.text,
|
||||
.unshifted_codepoint = unshifted_codepoint,
|
||||
};
|
||||
}
|
||||
|
||||
/// See CoreApp.keyEvent.
|
||||
pub fn keyEvent(
|
||||
self: *App,
|
||||
target: KeyTarget,
|
||||
event: KeyEvent,
|
||||
) !bool {
|
||||
// Convert our C key event into a Zig one.
|
||||
const input_event: input.KeyEvent = (try self.coreKeyEvent(
|
||||
target,
|
||||
event,
|
||||
)) orelse return false;
|
||||
|
||||
// Invoke the core Ghostty logic to handle this input.
|
||||
const effect: CoreSurface.InputEffect = switch (target) {
|
||||
.app => if (self.core_app.keyEvent(
|
||||
self,
|
||||
input_event,
|
||||
))
|
||||
.consumed
|
||||
else
|
||||
.ignored,
|
||||
)) .consumed else .ignored,
|
||||
|
||||
.surface => |surface| try surface.core_surface.keyCallback(input_event),
|
||||
.surface => |surface| try surface.core_surface.keyCallback(
|
||||
input_event,
|
||||
),
|
||||
};
|
||||
|
||||
return switch (effect) {
|
||||
.closed => true,
|
||||
.ignored => false,
|
||||
.consumed => consumed: {
|
||||
const is_down = input_event.action == .press or
|
||||
input_event.action == .repeat;
|
||||
|
||||
if (is_down) {
|
||||
// If we consume the key then we want to reset the dead
|
||||
// key state.
|
||||
@ -1601,6 +1616,28 @@ pub const CAPI = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if the given key event would trigger a binding
|
||||
/// if it were sent to the surface right now. The "right now"
|
||||
/// is important because things like trigger sequences are only
|
||||
/// valid until the next key event.
|
||||
export fn ghostty_surface_key_is_binding(
|
||||
surface: *Surface,
|
||||
event: KeyEvent,
|
||||
) bool {
|
||||
const core_event = surface.app.coreKeyEvent(
|
||||
.{ .surface = surface },
|
||||
event.keyEvent(),
|
||||
) catch |err| {
|
||||
log.warn("error processing key event err={}", .{err});
|
||||
return false;
|
||||
} orelse {
|
||||
log.warn("error processing key event", .{});
|
||||
return false;
|
||||
};
|
||||
|
||||
return surface.core_surface.keyEventIsBinding(core_event);
|
||||
}
|
||||
|
||||
/// Send raw text to the terminal. This is treated like a paste
|
||||
/// so this isn't useful for sending escape sequences. For that,
|
||||
/// individual key input should be used.
|
||||
|
Reference in New Issue
Block a user