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 ghostty_surface_key_translation_mods(ghostty_surface_t,
|
||||||
ghostty_input_mods_e);
|
ghostty_input_mods_e);
|
||||||
void ghostty_surface_key(ghostty_surface_t, ghostty_input_key_s);
|
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);
|
void ghostty_surface_text(ghostty_surface_t, const char*, uintptr_t);
|
||||||
bool ghostty_surface_mouse_captured(ghostty_surface_t);
|
bool ghostty_surface_mouse_captured(ghostty_surface_t);
|
||||||
bool ghostty_surface_mouse_button(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();
|
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
|
/// Called for any key events. This handles keybindings, encoding and
|
||||||
/// sending to the terminal, etc.
|
/// sending to the terminal, etc.
|
||||||
pub fn keyCallback(
|
pub fn keyCallback(
|
||||||
self: *Surface,
|
self: *Surface,
|
||||||
event: input.KeyEvent,
|
event: input.KeyEvent,
|
||||||
) !InputEffect {
|
) !InputEffect {
|
||||||
// log.debug("text keyCallback event={}", .{event});
|
log.debug("text keyCallback event={}", .{event});
|
||||||
|
|
||||||
// Crash metadata in case we crash in here
|
// Crash metadata in case we crash in here
|
||||||
crash.sentry.thread_state = self.crashThreadState();
|
crash.sentry.thread_state = self.crashThreadState();
|
||||||
|
@ -147,12 +147,12 @@ pub const App = struct {
|
|||||||
self.core_app.focusEvent(focused);
|
self.core_app.focusEvent(focused);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See CoreApp.keyEvent.
|
/// Convert a C key event into a Zig key event.
|
||||||
pub fn keyEvent(
|
fn coreKeyEvent(
|
||||||
self: *App,
|
self: *App,
|
||||||
target: KeyTarget,
|
target: KeyTarget,
|
||||||
event: KeyEvent,
|
event: KeyEvent,
|
||||||
) !bool {
|
) !?input.KeyEvent {
|
||||||
const action = event.action;
|
const action = event.action;
|
||||||
const keycode = event.keycode;
|
const keycode = event.keycode;
|
||||||
const mods = event.mods;
|
const mods = event.mods;
|
||||||
@ -243,7 +243,7 @@ pub const App = struct {
|
|||||||
result.text,
|
result.text,
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.err("error in preedit callback err={}", .{err});
|
log.err("error in preedit callback err={}", .{err});
|
||||||
return false;
|
return null;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -251,7 +251,7 @@ pub const App = struct {
|
|||||||
.app => {},
|
.app => {},
|
||||||
.surface => |surface| surface.core_surface.preeditCallback(null) catch |err| {
|
.surface => |surface| surface.core_surface.preeditCallback(null) catch |err| {
|
||||||
log.err("error in preedit callback err={}", .{err});
|
log.err("error in preedit callback err={}", .{err});
|
||||||
return false;
|
return null;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ pub const App = struct {
|
|||||||
} else .invalid;
|
} else .invalid;
|
||||||
|
|
||||||
// Build our final key event
|
// Build our final key event
|
||||||
const input_event: input.KeyEvent = .{
|
return .{
|
||||||
.action = action,
|
.action = action,
|
||||||
.key = key,
|
.key = key,
|
||||||
.physical_key = physical_key,
|
.physical_key = physical_key,
|
||||||
@ -345,24 +345,39 @@ pub const App = struct {
|
|||||||
.utf8 = result.text,
|
.utf8 = result.text,
|
||||||
.unshifted_codepoint = unshifted_codepoint,
|
.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.
|
// Invoke the core Ghostty logic to handle this input.
|
||||||
const effect: CoreSurface.InputEffect = switch (target) {
|
const effect: CoreSurface.InputEffect = switch (target) {
|
||||||
.app => if (self.core_app.keyEvent(
|
.app => if (self.core_app.keyEvent(
|
||||||
self,
|
self,
|
||||||
input_event,
|
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) {
|
return switch (effect) {
|
||||||
.closed => true,
|
.closed => true,
|
||||||
.ignored => false,
|
.ignored => false,
|
||||||
.consumed => consumed: {
|
.consumed => consumed: {
|
||||||
|
const is_down = input_event.action == .press or
|
||||||
|
input_event.action == .repeat;
|
||||||
|
|
||||||
if (is_down) {
|
if (is_down) {
|
||||||
// If we consume the key then we want to reset the dead
|
// If we consume the key then we want to reset the dead
|
||||||
// key state.
|
// 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
|
/// Send raw text to the terminal. This is treated like a paste
|
||||||
/// so this isn't useful for sending escape sequences. For that,
|
/// so this isn't useful for sending escape sequences. For that,
|
||||||
/// individual key input should be used.
|
/// individual key input should be used.
|
||||||
|
Reference in New Issue
Block a user