From 9c547dd8cd63e611f0828299d67314cf83e6ac4a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 7 Oct 2024 13:06:25 -0700 Subject: [PATCH] apprt/gtk: forward window key events to the focused surface --- src/apprt/gtk/Surface.zig | 23 +++++++--- src/apprt/gtk/Window.zig | 90 +++------------------------------------ 2 files changed, 25 insertions(+), 88 deletions(-) diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 43b36a9eb..e00304491 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1395,7 +1395,14 @@ fn gtkKeyPressed( gtk_mods: c.GdkModifierType, ud: ?*anyopaque, ) callconv(.C) c.gboolean { - return if (keyEvent(.press, ec_key, keyval, keycode, gtk_mods, ud)) 1 else 0; + const self = userdataSelf(ud.?); + return if (self.keyEvent( + .press, + ec_key, + keyval, + keycode, + gtk_mods, + )) 1 else 0; } fn gtkKeyReleased( @@ -1405,7 +1412,14 @@ fn gtkKeyReleased( state: c.GdkModifierType, ud: ?*anyopaque, ) callconv(.C) c.gboolean { - return if (keyEvent(.release, ec_key, keyval, keycode, state, ud)) 1 else 0; + const self = userdataSelf(ud.?); + return if (self.keyEvent( + .release, + ec_key, + keyval, + keycode, + state, + )) 1 else 0; } /// Key press event. This is where we do ALL of our key handling, @@ -1432,15 +1446,14 @@ fn gtkKeyReleased( /// Note we ALSO have an IMContext attached directly to the widget /// which can emit preedit and commit callbacks. But, if we're not /// in a keypress, we let those automatically work. -fn keyEvent( +pub fn keyEvent( + self: *Surface, action: input.Action, ec_key: *c.GtkEventControllerKey, keyval: c.guint, keycode: c.guint, gtk_mods: c.GdkModifierType, - ud: ?*anyopaque, ) bool { - const self = userdataSelf(ud.?); const keyval_unicode = c.gdk_keyval_to_unicode(keyval); const event = c.gtk_event_controller_get_current_event( @ptrCast(ec_key), diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 2931456b7..d24542a63 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -679,90 +679,14 @@ fn gtkKeyPressed( ud: ?*anyopaque, ) callconv(.C) c.gboolean { const self = userdataSelf(ud.?); - const keyval_unicode = c.gdk_keyval_to_unicode(keyval); - const event = c.gtk_event_controller_get_current_event(@ptrCast(ec_key)) orelse return 0; - - // We want to get the physical unmapped key to process physical keybinds. - // (These are keybinds explicitly marked as requesting physical mapping). - const physical_key = keycode: for (input.keycodes.entries) |entry| { - if (entry.native == keycode) break :keycode entry.key; - } else .invalid; - - // Get our modifier for the event - const mods: input.Mods = gtk_key.eventMods( - @ptrCast(self.window), - event, - physical_key, - gtk_mods, - if (self.app.x11_xkb) |*xkb| xkb else null, - ); - - // Get our consumed modifiers - const consumed_mods: input.Mods = consumed: { - const raw = c.gdk_key_event_get_consumed_modifiers(event); - const masked = raw & c.GDK_MODIFIER_MASK; - break :consumed gtk_key.translateMods(masked); - }; - - // Get the unshifted unicode value of the keyval. - const keyval_unicode_unshifted: u21 = gtk_key.keyvalUnicodeUnshifted( - @ptrCast(self.window), - event, + const surface = self.app.core_app.focusedSurface() orelse return 0; + return if (surface.rt_surface.keyEvent( + .press, + ec_key, + keyval, keycode, - ); - - // If we're not in a dead key state, we want to translate our text - // to some input.Key. - const key: input.Key = key: { - // First, try to convert the keyval directly to a key. This allows the - // use of key remapping and identification of keypad numerics (as - // opposed to their ASCII counterparts) - if (gtk_key.keyFromKeyval(keyval)) |key| { - break :key key; - } - - // If that doesn't work then we try to translate the kevval.. - if (keyval_unicode != 0) { - if (std.math.cast(u8, keyval_unicode)) |byte| { - if (input.Key.fromASCII(byte)) |key| { - break :key key; - } - } - } - - // If that doesn't work we use the unshifted value... - if (std.math.cast(u8, keyval_unicode_unshifted)) |ascii| { - if (input.Key.fromASCII(ascii)) |key| { - break :key key; - } - } - - if (keyval_unicode_unshifted != 0) break :key .invalid; - break :key physical_key; - }; - - // Build our final key event - const core_event: input.KeyEvent = .{ - .action = .press, - .key = key, - .physical_key = physical_key, - .mods = mods, - .consumed_mods = consumed_mods, - .composing = false, - .utf8 = "", - .unshifted_codepoint = keyval_unicode_unshifted, - }; - - // log.debug("attempting app-scoped key event={}", .{core_event}); - - // Invoke the core Ghostty logic to handle this input. - const consumed = self.app.core_app.keyEvent(self.app, core_event); - if (consumed) { - log.info("app-scoped key consumed event={}", .{core_event}); - return 1; - } - - return 0; + gtk_mods, + )) 1 else 0; } fn gtkActionAbout(