macos: handle overridden system bindings with no focused window

This commit is contained in:
Mitchell Hashimoto
2025-01-04 14:07:33 -08:00
parent 1bcfff3b79
commit 40bdea7335
4 changed files with 51 additions and 0 deletions

View File

@ -686,6 +686,7 @@ void ghostty_app_tick(ghostty_app_t);
void* ghostty_app_userdata(ghostty_app_t);
void ghostty_app_set_focus(ghostty_app_t, bool);
bool ghostty_app_key(ghostty_app_t, ghostty_input_key_s);
bool ghostty_app_key_is_binding(ghostty_app_t, ghostty_input_key_s);
void ghostty_app_keyboard_changed(ghostty_app_t);
void ghostty_app_open_config(ghostty_app_t);
void ghostty_app_update_config(ghostty_app_t, ghostty_config_t);

View File

@ -425,6 +425,15 @@ class AppDelegate: NSObject,
// because we let it capture and propagate.
guard NSApp.mainWindow == nil else { return event }
// If this event as-is would result in a key binding then we send it.
if let app = ghostty.app,
ghostty_app_key_is_binding(
app,
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) {
ghostty_app_key(app, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))
return nil
}
// If this event would be handled by our menu then we do nothing.
if let mainMenu = NSApp.mainMenu,
mainMenu.performKeyEquivalent(with: event) {

View File

@ -313,6 +313,25 @@ pub fn focusEvent(self: *App, focused: bool) void {
self.focused = focused;
}
/// 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.
pub fn keyEventIsBinding(
self: *App,
rt_app: *apprt.App,
event: input.KeyEvent,
) bool {
_ = self;
switch (event.action) {
.release => return false,
.press, .repeat => {},
}
// If we have a keybinding for this event then we return true.
return rt_app.config.keybind.set.getEvent(event) != null;
}
/// Handle a key event at the app-scope. If this key event is used,
/// this will return true and the caller shouldn't continue processing
/// the event. If the event is not used, this will return false.

View File

@ -1386,6 +1386,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_app_key_is_binding(
app: *App,
event: KeyEvent,
) bool {
const core_event = app.coreKeyEvent(
.app,
event.keyEvent(),
) catch |err| {
log.warn("error processing key event err={}", .{err});
return false;
} orelse {
log.warn("error processing key event", .{});
return false;
};
return app.core_app.keyEventIsBinding(app, core_event);
}
/// Notify the app that the keyboard was changed. This causes the
/// keyboard layout to be reloaded from the OS.
export fn ghostty_app_keyboard_changed(v: *App) void {