diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 7142db995..e9b377ec7 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -56,6 +56,9 @@ clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null, /// This is set to false when the main loop should exit. running: bool = true, +/// Xkb state (X11 only). Will be null on Wayland. +x11_xkb: ?x11.X11Xkb = null, + pub fn init(core_app: *CoreApp, opts: Options) !App { _ = opts; @@ -170,8 +173,9 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { return error.GtkApplicationRegisterFailed; } + var x11_xkb: ?x11.X11Xkb = null; const display = c.gdk_display_get_default(); - if (x11.x11_is_display(display)) { + if (x11.is_display(display)) { // Set the X11 window class property (WM_CLASS) if are are on an X11 // display. // @@ -197,6 +201,9 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { "ghostty"; c.g_set_prgname(prgname); c.gdk_x11_display_set_program_class(display, app_id); + + // Set up Xkb + x11_xkb = try x11.X11Xkb.init(c.gdk_display_get_default()); } // This just calls the "activate" signal but its part of the normal @@ -210,6 +217,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { .config = config, .ctx = ctx, .cursor_none = cursor_none, + .x11_xkb = x11_xkb, // If we are NOT the primary instance, then we never want to run. // This means that another instance of the GTK app is running and // our "activate" call above will open a window. @@ -574,3 +582,9 @@ test "isValidAppId" { try testing.expect(!isValidAppId("")); try testing.expect(!isValidAppId("foo" ** 86)); } + +/// Loads keyboard state from Xkb if there is an event pending and Xkb is +/// loaded (X11 only). Returns null otherwise. +pub fn modifier_state_from_xkb(self: *App, display_: ?*c.GdkDisplay) ?input.Mods { + return (self.x11_xkb orelse return null).modifier_state_from_notify(display_); +} diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 2ab1a0051..a0f2091bb 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -19,7 +19,6 @@ const Window = @import("Window.zig"); const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig"); const inspector = @import("inspector.zig"); const gtk_key = @import("key.zig"); -const x11 = @import("x11.zig"); const c = @import("c.zig"); const log = std.log.scoped(.gtk_surface); @@ -254,9 +253,6 @@ im_composing: bool = false, im_buf: [128]u8 = undefined, im_len: u7 = 0, -/// Xkb state (X11 only). Will be null on Wayland. -x11_xkb: ?x11.X11Xkb = null, - pub fn create(alloc: Allocator, app: *App, opts: Options) !*Surface { var surface = try alloc.create(Surface); errdefer alloc.destroy(surface); @@ -359,9 +355,6 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { }; errdefer self.* = undefined; - // Set up Xkb if we are running under X11. - self.x11_xkb = try x11.X11Xkb.init(c.gdk_display_get_default()); - // Set our default mouse shape try self.setMouseShape(.text); @@ -1343,18 +1336,16 @@ fn keyEvent( const device = c.gdk_event_get_device(event); var mods = init_mods: { - if (self.x11_xkb) |x11_xkb| { - // Add any modifier state events from Xkb if we have them (X11 only). - if (x11_xkb.modifier_state_from_notify(display)) |xkb_mods| { - break :init_mods xkb_mods; - } - - // Null back from the Xkb call means there was no modifier - // event to read. This likely means that the key event did not - // result in a modifier change and we can safely rely on the - // GDK state. + // Add any modifier state events from Xkb if we have them (X11 only). + if (self.app.modifier_state_from_xkb(display)) |xkb_mods| { + break :init_mods xkb_mods; } + // Null back from the Xkb call means there was no modifier + // event to read. This likely means that the key event did not + // result in a modifier change and we can safely rely on the + // GDK state. + break :init_mods translateMods(c.gdk_device_get_modifier_state(device)); }; diff --git a/src/apprt/gtk/x11.zig b/src/apprt/gtk/x11.zig index c9fd270b0..a188df8bb 100644 --- a/src/apprt/gtk/x11.zig +++ b/src/apprt/gtk/x11.zig @@ -6,7 +6,7 @@ const input = @import("../../input.zig"); const log = std.log.scoped(.gtk_x11); /// Returns true if the passed in display is an X11 display. -pub fn x11_is_display(display: ?*c.GdkDisplay) bool { +pub fn is_display(display: ?*c.GdkDisplay) bool { return c.g_type_check_instance_is_a( @ptrCast(@alignCast(display orelse return false)), c.gdk_x11_display_get_type(), @@ -14,9 +14,7 @@ pub fn x11_is_display(display: ?*c.GdkDisplay) bool { } pub const X11Xkb = struct { - opcode: c_int, base_event_code: c_int, - base_error_code: c_int, funcs: Funcs, /// Initialize an X11Xkb struct, for the given GDK display. If the display @@ -27,25 +25,25 @@ pub const X11Xkb = struct { const display = display_ orelse return null; // If the display isn't X11, then we don't need to do anything. - if (!x11_is_display(display)) return null; + if (!is_display(display)) return null; log.debug("X11Xkb.init: initializing Xkb", .{}); const xdisplay = c.gdk_x11_display_get_xdisplay(display); var result: X11Xkb = .{ - .opcode = 0, .base_event_code = 0, - .base_error_code = 0, .funcs = try Funcs.init(), }; log.debug("X11Xkb.init: running XkbQueryExtension", .{}); + var opcode: c_int = 0; + var base_error_code: c_int = 0; var major = c.XkbMajorVersion; var minor = c.XkbMinorVersion; if (result.funcs.XkbQueryExtension( xdisplay, - &result.opcode, + &opcode, &result.base_event_code, - &result.base_error_code, + &base_error_code, &major, &minor, ) == 0) {