mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
gtk/x11: link directly to libX11, no more dlopen (#3857)
As a follow-up to #3477 and #3748, this eliminates the use of dlopen to access `libX11` functions by directly linking `libX11` if X11 is enabled. This should also fix problems with systems like NixOS and Void Linux that have reported problems using Ghostty on X11 when using the distribution packages.
This commit is contained in:
@ -1427,6 +1427,7 @@ fn addDeps(
|
|||||||
.gtk => {
|
.gtk => {
|
||||||
step.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
step.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
||||||
if (config.adwaita) step.linkSystemLibrary2("adwaita-1", 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");
|
const gresource = @import("src/apprt/gtk/gresource.zig");
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
pandoc,
|
pandoc,
|
||||||
revision ? "dirty",
|
revision ? "dirty",
|
||||||
optimize ? "Debug",
|
optimize ? "Debug",
|
||||||
x11 ? false,
|
x11 ? true,
|
||||||
}: let
|
}: let
|
||||||
# The Zig hook has no way to select the release type without actual
|
# The Zig hook has no way to select the release type without actual
|
||||||
# overriding of the default flags.
|
# overriding of the default flags.
|
||||||
@ -151,7 +151,7 @@ in
|
|||||||
|
|
||||||
dontConfigure = true;
|
dontConfigure = true;
|
||||||
|
|
||||||
zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix";
|
zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix -Dgtk-x11=${lib.boolToString x11}";
|
||||||
|
|
||||||
preBuild = ''
|
preBuild = ''
|
||||||
rm -rf $ZIG_GLOBAL_CACHE_DIR
|
rm -rf $ZIG_GLOBAL_CACHE_DIR
|
||||||
@ -190,10 +190,6 @@ in
|
|||||||
echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
|
echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postFixup = lib.optionalString x11 ''
|
|
||||||
patchelf --add-rpath "${lib.makeLibraryPath [libX11]}" "$out/bin/.ghostty-wrapped"
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
homepage = "https://github.com/ghostty-org/ghostty";
|
homepage = "https://github.com/ghostty-org/ghostty";
|
||||||
license = lib.licenses.mit;
|
license = lib.licenses.mit;
|
||||||
|
@ -22,13 +22,14 @@ pub fn is_current_display_server() bool {
|
|||||||
return is_display(display);
|
return is_display(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Xkb = if (build_options.x11) struct {
|
pub const Xkb = struct {
|
||||||
base_event_code: c_int,
|
base_event_code: c_int,
|
||||||
funcs: Funcs,
|
|
||||||
|
|
||||||
/// Initialize an Xkb struct, for the given GDK display. If the display
|
/// Initialize an Xkb struct for the given GDK display. If the display isn't
|
||||||
/// isn't backed by X then this will return null.
|
/// backed by X then this will return null.
|
||||||
pub fn init(display_: ?*c.GdkDisplay) !?Xkb {
|
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 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.
|
// display so that the caller can just ignore it and not unwrap it.
|
||||||
const display = display_ orelse return null;
|
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);
|
const xdisplay = c.gdk_x11_display_get_xdisplay(display);
|
||||||
var result: Xkb = .{
|
var result: Xkb = .{
|
||||||
.base_event_code = 0,
|
.base_event_code = 0,
|
||||||
.funcs = try Funcs.init(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
log.debug("Xkb.init: running XkbQueryExtension", .{});
|
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 base_error_code: c_int = 0;
|
||||||
var major = c.XkbMajorVersion;
|
var major = c.XkbMajorVersion;
|
||||||
var minor = c.XkbMinorVersion;
|
var minor = c.XkbMinorVersion;
|
||||||
if (result.funcs.XkbQueryExtension(
|
if (c.XkbQueryExtension(
|
||||||
xdisplay,
|
xdisplay,
|
||||||
&opcode,
|
&opcode,
|
||||||
&result.base_event_code,
|
&result.base_event_code,
|
||||||
@ -61,7 +61,7 @@ pub const Xkb = if (build_options.x11) struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Xkb.init: running XkbSelectEventDetails", .{});
|
log.debug("Xkb.init: running XkbSelectEventDetails", .{});
|
||||||
if (result.funcs.XkbSelectEventDetails(
|
if (c.XkbSelectEventDetails(
|
||||||
xdisplay,
|
xdisplay,
|
||||||
c.XkbUseCoreKbd,
|
c.XkbUseCoreKbd,
|
||||||
c.XkbStateNotify,
|
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
|
/// back to the standard GDK modifier state (this likely means the key
|
||||||
/// event did not result in a modifier change).
|
/// event did not result in a modifier change).
|
||||||
pub fn modifier_state_from_notify(self: Xkb, display_: ?*c.GdkDisplay) ?input.Mods {
|
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;
|
const display = display_ orelse return null;
|
||||||
|
|
||||||
// Shoutout to Mozilla for figuring out a clean way to do this, this is
|
// Shoutout to Mozilla for figuring out a clean way to do this, this is
|
||||||
// paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp.
|
// paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp.
|
||||||
const xdisplay = c.gdk_x11_display_get_xdisplay(display);
|
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;
|
var nextEvent: c.XEvent = undefined;
|
||||||
_ = self.funcs.XPeekEvent(xdisplay, &nextEvent);
|
_ = c.XPeekEvent(xdisplay, &nextEvent);
|
||||||
if (nextEvent.type != self.base_event_code) return null;
|
if (nextEvent.type != self.base_event_code) return null;
|
||||||
|
|
||||||
const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent);
|
const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent);
|
||||||
@ -114,40 +116,4 @@ pub const Xkb = if (build_options.x11) struct {
|
|||||||
|
|
||||||
return mods;
|
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user