diff --git a/build.zig b/build.zig index 614026572..c9ab5dd8f 100644 --- a/build.zig +++ b/build.zig @@ -1427,6 +1427,7 @@ fn addDeps( .gtk => { step.linkSystemLibrary2("gtk4", dynamic_link_opts); if (config.adwaita) step.linkSystemLibrary2("adwaita-1", dynamic_link_opts); + if (config.x11) step.linkSystemLibrary2("X11", dynamic_link_opts); { const gresource = @import("src/apprt/gtk/gresource.zig"); diff --git a/nix/package.nix b/nix/package.nix index 55205f459..3c36661bf 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -26,7 +26,7 @@ pandoc, revision ? "dirty", optimize ? "Debug", - x11 ? false, + x11 ? true, }: let # The Zig hook has no way to select the release type without actual # overriding of the default flags. @@ -151,7 +151,7 @@ in dontConfigure = true; - zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix"; + zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix -Dgtk-x11=${lib.boolToString x11}"; preBuild = '' rm -rf $ZIG_GLOBAL_CACHE_DIR @@ -190,10 +190,6 @@ in echo "$vim" >> "$out/nix-support/propagated-user-env-packages" ''; - postFixup = lib.optionalString x11 '' - patchelf --add-rpath "${lib.makeLibraryPath [libX11]}" "$out/bin/.ghostty-wrapped" - ''; - meta = { homepage = "https://github.com/ghostty-org/ghostty"; license = lib.licenses.mit; diff --git a/src/apprt/gtk/x11.zig b/src/apprt/gtk/x11.zig index 078305904..21ff87b34 100644 --- a/src/apprt/gtk/x11.zig +++ b/src/apprt/gtk/x11.zig @@ -22,13 +22,14 @@ pub fn is_current_display_server() bool { return is_display(display); } -pub const Xkb = if (build_options.x11) struct { +pub const Xkb = struct { base_event_code: c_int, - funcs: Funcs, - /// Initialize an Xkb struct, for the given GDK display. If the display - /// isn't backed by X then this will return null. + /// Initialize an Xkb struct for the given GDK display. If the display isn't + /// backed by X then this will return null. pub fn init(display_: ?*c.GdkDisplay) !?Xkb { + if (comptime !build_options.x11) return null; + // Display should never be null but we just treat that as a non-X11 // display so that the caller can just ignore it and not unwrap it. const display = display_ orelse return null; @@ -40,7 +41,6 @@ pub const Xkb = if (build_options.x11) struct { const xdisplay = c.gdk_x11_display_get_xdisplay(display); var result: Xkb = .{ .base_event_code = 0, - .funcs = try Funcs.init(), }; log.debug("Xkb.init: running XkbQueryExtension", .{}); @@ -48,7 +48,7 @@ pub const Xkb = if (build_options.x11) struct { var base_error_code: c_int = 0; var major = c.XkbMajorVersion; var minor = c.XkbMinorVersion; - if (result.funcs.XkbQueryExtension( + if (c.XkbQueryExtension( xdisplay, &opcode, &result.base_event_code, @@ -61,7 +61,7 @@ pub const Xkb = if (build_options.x11) struct { } log.debug("Xkb.init: running XkbSelectEventDetails", .{}); - if (result.funcs.XkbSelectEventDetails( + if (c.XkbSelectEventDetails( xdisplay, c.XkbUseCoreKbd, c.XkbStateNotify, @@ -86,15 +86,17 @@ pub const Xkb = if (build_options.x11) struct { /// back to the standard GDK modifier state (this likely means the key /// event did not result in a modifier change). pub fn modifier_state_from_notify(self: Xkb, display_: ?*c.GdkDisplay) ?input.Mods { + if (comptime !build_options.x11) return null; + const display = display_ orelse return null; // Shoutout to Mozilla for figuring out a clean way to do this, this is // paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp. const xdisplay = c.gdk_x11_display_get_xdisplay(display); - if (self.funcs.XEventsQueued(xdisplay, c.QueuedAfterReading) == 0) return null; + if (c.XEventsQueued(xdisplay, c.QueuedAfterReading) == 0) return null; var nextEvent: c.XEvent = undefined; - _ = self.funcs.XPeekEvent(xdisplay, &nextEvent); + _ = c.XPeekEvent(xdisplay, &nextEvent); if (nextEvent.type != self.base_event_code) return null; const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent); @@ -114,40 +116,4 @@ pub const Xkb = if (build_options.x11) struct { return mods; } -} else struct {}; - -/// The functions that we load dynamically from libX11.so. -const Funcs = struct { - XkbQueryExtension: XkbQueryExtensionType, - XkbSelectEventDetails: XkbSelectEventDetailsType, - XEventsQueued: XEventsQueuedType, - XPeekEvent: XPeekEventType, - - const XkbQueryExtensionType = *const fn (?*c.struct__XDisplay, [*c]c_int, [*c]c_int, [*c]c_int, [*c]c_int, [*c]c_int) callconv(.C) c_int; - const XkbSelectEventDetailsType = *const fn (?*c.struct__XDisplay, c_uint, c_uint, c_ulong, c_ulong) callconv(.C) c_int; - const XEventsQueuedType = *const fn (?*c.struct__XDisplay, c_int) callconv(.C) c_int; - const XPeekEventType = *const fn (?*c.struct__XDisplay, [*c]c.union__XEvent) callconv(.C) c_int; - - pub fn init() !Funcs { - var libX11 = try std.DynLib.open("libX11.so"); - defer libX11.close(); - - var result: Funcs = undefined; - inline for (@typeInfo(Funcs).Struct.fields) |field| { - const name = comptime name: { - const null_term = field.name ++ .{0}; - break :name null_term[0..field.name.len :0]; - }; - - @field(result, field.name) = libX11.lookup( - field.type, - name, - ) orelse { - log.err(" error dynamic loading libX11: missing symbol {s}", .{field.name}); - return error.XkbInitializationError; - }; - } - - return result; - } };