Skip to content

Commit

Permalink
gtk: add option to not link against libX11 (#3748)
Browse files Browse the repository at this point in the history
Possible fix for #3477. Needs testing.
  • Loading branch information
mitchellh authored Dec 28, 2024
2 parents 531a225 + a55bea3 commit 5293fc9
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 28 deletions.
52 changes: 49 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -329,16 +329,62 @@ jobs:
- name: Test GTK Build
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true -Demit-docs

- name: Test GTK Build (No Libadwaita)
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=false -Demit-docs

- name: Test GLFW Build
run: nix develop -c zig build -Dapp-runtime=glfw

# This relies on the cache being populated by the commands above.
- name: Test System Build
run: nix develop -c zig build --system ${ZIG_GLOBAL_CACHE_DIR}/p

test-gtk:
strategy:
fail-fast: false
matrix:
adwaita: ["true", "false"]
x11: ["true", "false"]
name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }}
runs-on: namespace-profile-ghostty-sm
needs: test
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Cache
uses: namespacelabs/[email protected]
with:
path: |
/nix
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

- name: Test GTK Build
run: |
nix develop -c \
zig build \
-Dapp-runtime=gtk \
-Dgtk-adwaita=${{ matrix.adwaita }} \
-Dgtk-x11=${{ matrix.x11 }}
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

test-macos:
runs-on: namespace-profile-ghostty-macos
needs: test
Expand Down
47 changes: 47 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,53 @@ pub fn build(b: *std.Build) !void {
"Enables the use of Adwaita when using the GTK rendering backend.",
) orelse true;

config.x11 = b.option(
bool,
"gtk-x11",
"Enables linking against X11 libraries when using the GTK rendering backend.",
) orelse x11: {
if (target.result.os.tag != .linux) break :x11 false;

var pkgconfig = std.process.Child.init(&.{ "pkg-config", "--variable=targets", "gtk4" }, b.allocator);

pkgconfig.stdout_behavior = .Pipe;
pkgconfig.stderr_behavior = .Pipe;

try pkgconfig.spawn();

const output_max_size = 50 * 1024;

var stdout = std.ArrayList(u8).init(b.allocator);
var stderr = std.ArrayList(u8).init(b.allocator);
defer {
stdout.deinit();
stderr.deinit();
}

try pkgconfig.collectOutput(&stdout, &stderr, output_max_size);

const term = try pkgconfig.wait();

if (stderr.items.len > 0) {
std.log.warn("pkg-config had errors:\n{s}", .{stderr.items});
}

switch (term) {
.Exited => |code| {
if (code == 0) {
if (std.mem.indexOf(u8, stdout.items, "x11")) |_| break :x11 true;
break :x11 false;
}
std.log.warn("pkg-config: {s} with code {d}", .{ @tagName(term), code });
return error.Unexpected;
},
inline else => |code| {
std.log.warn("pkg-config: {s} with code {d}", .{ @tagName(term), code });
return error.Unexpected;
},
}
};

const pie = b.option(
bool,
"pie",
Expand Down
26 changes: 18 additions & 8 deletions nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
pandoc,
revision ? "dirty",
optimize ? "Debug",
x11 ? false,
}: let
# The Zig hook has no way to select the release type without actual
# overriding of the default flags.
Expand Down Expand Up @@ -136,15 +137,16 @@ in
oniguruma
zlib

libX11
libXcursor
libXi
libXrandr

libadwaita
gtk4
glib
gsettings-desktop-schemas
]
++ lib.optionals x11 [
libX11
libXcursor
libXi
libXrandr
];

dontConfigure = true;
Expand All @@ -157,7 +159,12 @@ in
chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR
'';

outputs = ["out" "terminfo" "shell_integration" "vim"];
outputs = [
"out"
"terminfo"
"shell_integration"
"vim"
];

postInstall = ''
terminfo_src=${
Expand All @@ -183,14 +190,17 @@ in
echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
'';

postFixup = ''
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;
platforms = ["x86_64-linux" "aarch64-linux"];
platforms = [
"x86_64-linux"
"aarch64-linux"
];
mainProgram = "ghostty";
};
})
2 changes: 2 additions & 0 deletions src/apprt/gtk/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const builtin = @import("builtin");
const build_config = @import("../../build_config.zig");
const build_options = @import("build_options");
const apprt = @import("../../apprt.zig");
const configpkg = @import("../../config.zig");
const input = @import("../../input.zig");
Expand Down Expand Up @@ -360,6 +361,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
// keyboard state but the block does more than that (i.e. setting up
// WM_CLASS).
const x11_xkb: ?x11.Xkb = x11_xkb: {
if (comptime !build_options.x11) break :x11_xkb null;
if (!x11.is_display(display)) break :x11_xkb null;

// Set the X11 window class property (WM_CLASS) if are are on an X11
Expand Down
5 changes: 3 additions & 2 deletions src/apprt/gtk/Surface.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const Surface = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const build_config = @import("../../build_config.zig");
const build_options = @import("build_options");
const configpkg = @import("../../config.zig");
const apprt = @import("../../apprt.zig");
const font = @import("../../font/main.zig");
Expand Down Expand Up @@ -1183,7 +1184,7 @@ fn showContextMenu(self: *Surface, x: f32, y: f32) void {
@ptrCast(window.window),
&c.GRAPHENE_POINT_INIT(point.x, point.y),
@ptrCast(&point),
) == c.False) {
) == 0) {
log.warn("failed computing point for context menu", .{});
return;
}
Expand Down Expand Up @@ -1899,7 +1900,7 @@ pub fn dimSurface(self: *Surface) void {
// Don't dim surface if context menu is open.
// This means we got unfocused due to it opening.
const context_menu_open = c.gtk_widget_get_visible(window.context_menu);
if (context_menu_open == c.True) return;
if (context_menu_open == 1) return;

if (self.unfocused_widget != null) return;
self.unfocused_widget = c.gtk_drawing_area_new();
Expand Down
2 changes: 1 addition & 1 deletion src/apprt/gtk/Window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ pub fn init(self: *Window, app: *App) !void {

self.context_menu = c.gtk_popover_menu_new_from_model(@ptrCast(@alignCast(self.app.context_menu)));
c.gtk_widget_set_parent(self.context_menu, window);
c.gtk_popover_set_has_arrow(@ptrCast(@alignCast(self.context_menu)), c.False);
c.gtk_popover_set_has_arrow(@ptrCast(@alignCast(self.context_menu)), 0);
c.gtk_widget_set_halign(self.context_menu, c.GTK_ALIGN_START);

// If we are in fullscreen mode, new windows start fullscreen.
Expand Down
16 changes: 10 additions & 6 deletions src/apprt/gtk/c.zig
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
const build_options = @import("build_options");

/// Imported C API directly from header files
pub const c = @cImport({
@cInclude("gtk/gtk.h");
if (@import("build_options").adwaita) {
if (build_options.adwaita) {
@cInclude("libadwaita-1/adwaita.h");
}

// Add in X11-specific GDK backend which we use for specific things
// (e.g. X11 window class).
@cInclude("gdk/x11/gdkx.h");
// Xkb for X11 state handling
@cInclude("X11/XKBlib.h");
if (build_options.x11) {
// Add in X11-specific GDK backend which we use for specific things
// (e.g. X11 window class).
@cInclude("gdk/x11/gdkx.h");
// Xkb for X11 state handling
@cInclude("X11/XKBlib.h");
}

// generated header files
@cInclude("ghostty_resources.h");
Expand Down
18 changes: 12 additions & 6 deletions src/apprt/gtk/key.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const build_options = @import("build_options");
const input = @import("../../input.zig");
const c = @import("c.zig").c;
const x11 = @import("x11.zig");
Expand Down Expand Up @@ -111,21 +112,26 @@ pub fn eventMods(
x11_xkb: ?*x11.Xkb,
) input.Mods {
const device = c.gdk_event_get_device(event);
const display = c.gtk_widget_get_display(widget);

var mods = if (x11_xkb) |xkb|
var mods = mods: {
// Add any modifier state events from Xkb if we have them (X11
// only). 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.
xkb.modifier_state_from_notify(display) orelse
translateMods(gtk_mods)
else
if (comptime build_options.x11) {
const display = c.gtk_widget_get_display(widget);
if (x11_xkb) |xkb| {
if (xkb.modifier_state_from_notify(display)) |x11_mods| break :mods x11_mods;
break :mods translateMods(gtk_mods);
}
}

// On Wayland, we have to use the GDK device because the mods sent
// to this event do not have the modifier key applied if it was
// presssed (i.e. left control).
translateMods(c.gdk_device_get_modifier_state(device));
break :mods translateMods(c.gdk_device_get_modifier_state(device));
};

mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1;

Expand Down
7 changes: 5 additions & 2 deletions src/apprt/gtk/x11.zig
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/// Utility functions for X11 handling.
const std = @import("std");
const build_options = @import("build_options");
const c = @import("c.zig").c;
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 is_display(display: ?*c.GdkDisplay) bool {
if (comptime !build_options.x11) return false;
return c.g_type_check_instance_is_a(
@ptrCast(@alignCast(display orelse return false)),
c.gdk_x11_display_get_type(),
Expand All @@ -15,11 +17,12 @@ pub fn is_display(display: ?*c.GdkDisplay) bool {

/// Returns true if the app is running on X11
pub fn is_current_display_server() bool {
if (comptime !build_options.x11) return false;
const display = c.gdk_display_get_default();
return is_display(display);
}

pub const Xkb = struct {
pub const Xkb = if (build_options.x11) struct {
base_event_code: c_int,
funcs: Funcs,

Expand Down Expand Up @@ -111,7 +114,7 @@ pub const Xkb = struct {

return mods;
}
};
} else struct {};

/// The functions that we load dynamically from libX11.so.
const Funcs = struct {
Expand Down
2 changes: 2 additions & 0 deletions src/build_config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub const BuildConfig = struct {
version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 },
flatpak: bool = false,
adwaita: bool = false,
x11: bool = false,
app_runtime: apprt.Runtime = .none,
renderer: rendererpkg.Impl = .opengl,
font_backend: font.Backend = .freetype,
Expand All @@ -41,6 +42,7 @@ pub const BuildConfig = struct {
// support all types.
step.addOption(bool, "flatpak", self.flatpak);
step.addOption(bool, "adwaita", self.adwaita);
step.addOption(bool, "x11", self.x11);
step.addOption(apprt.Runtime, "app_runtime", self.app_runtime);
step.addOption(font.Backend, "font_backend", self.font_backend);
step.addOption(rendererpkg.Impl, "renderer", self.renderer);
Expand Down
5 changes: 5 additions & 0 deletions src/cli/version.zig
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ pub fn run(alloc: Allocator) !u8 {
} else {
try stdout.print(" - libadwaita : disabled\n", .{});
}
if (comptime build_options.x11) {
try stdout.print(" - libX11 : enabled\n", .{});
} else {
try stdout.print(" - libX11 : disabled\n", .{});
}
}
return 0;
}

0 comments on commit 5293fc9

Please sign in to comment.