Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gtk: add option to not link against libX11 #3748

Merged
merged 5 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -329,16 +329,53 @@ 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"]
runs-on: namespace-profile-ghostty-md
Copy link
Collaborator

@tristan957 tristan957 Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest using enum like strings. Otherwise you won't know which job failed because it will look like test-gtk(true, false) or something.

adwaita: ["with-adwaita", "without-adwaita"]
x11: ["with-x11", "without-x11"]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just named them using the name: key. Is there some other way? That's what I'm used to doing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is just a UX problem in my opinion of GitHub that have to be worked around. No biggie

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

- 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;
}
Loading