mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-20 10:46:07 +03:00
gtk: add option to not link against libX11 (#3748)
Possible fix for #3477. Needs testing.
This commit is contained in:
52
.github/workflows/test.yml
vendored
52
.github/workflows/test.yml
vendored
@ -329,9 +329,6 @@ jobs:
|
|||||||
- name: Test GTK Build
|
- name: Test GTK Build
|
||||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true -Demit-docs
|
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
|
- name: Test GLFW Build
|
||||||
run: nix develop -c zig build -Dapp-runtime=glfw
|
run: nix develop -c zig build -Dapp-runtime=glfw
|
||||||
|
|
||||||
@ -339,6 +336,55 @@ jobs:
|
|||||||
- name: Test System Build
|
- name: Test System Build
|
||||||
run: nix develop -c zig build --system ${ZIG_GLOBAL_CACHE_DIR}/p
|
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/nscloud-cache-action@v1.2.0
|
||||||
|
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:
|
test-macos:
|
||||||
runs-on: namespace-profile-ghostty-macos
|
runs-on: namespace-profile-ghostty-macos
|
||||||
needs: test
|
needs: test
|
||||||
|
47
build.zig
47
build.zig
@ -105,6 +105,53 @@ pub fn build(b: *std.Build) !void {
|
|||||||
"Enables the use of Adwaita when using the GTK rendering backend.",
|
"Enables the use of Adwaita when using the GTK rendering backend.",
|
||||||
) orelse true;
|
) 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(
|
const pie = b.option(
|
||||||
bool,
|
bool,
|
||||||
"pie",
|
"pie",
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
pandoc,
|
pandoc,
|
||||||
revision ? "dirty",
|
revision ? "dirty",
|
||||||
optimize ? "Debug",
|
optimize ? "Debug",
|
||||||
|
x11 ? false,
|
||||||
}: 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.
|
||||||
@ -136,15 +137,16 @@ in
|
|||||||
oniguruma
|
oniguruma
|
||||||
zlib
|
zlib
|
||||||
|
|
||||||
libX11
|
|
||||||
libXcursor
|
|
||||||
libXi
|
|
||||||
libXrandr
|
|
||||||
|
|
||||||
libadwaita
|
libadwaita
|
||||||
gtk4
|
gtk4
|
||||||
glib
|
glib
|
||||||
gsettings-desktop-schemas
|
gsettings-desktop-schemas
|
||||||
|
]
|
||||||
|
++ lib.optionals x11 [
|
||||||
|
libX11
|
||||||
|
libXcursor
|
||||||
|
libXi
|
||||||
|
libXrandr
|
||||||
];
|
];
|
||||||
|
|
||||||
dontConfigure = true;
|
dontConfigure = true;
|
||||||
@ -157,7 +159,12 @@ in
|
|||||||
chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR
|
chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR
|
||||||
'';
|
'';
|
||||||
|
|
||||||
outputs = ["out" "terminfo" "shell_integration" "vim"];
|
outputs = [
|
||||||
|
"out"
|
||||||
|
"terminfo"
|
||||||
|
"shell_integration"
|
||||||
|
"vim"
|
||||||
|
];
|
||||||
|
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
terminfo_src=${
|
terminfo_src=${
|
||||||
@ -183,14 +190,17 @@ in
|
|||||||
echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
|
echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postFixup = ''
|
postFixup = lib.optionalString x11 ''
|
||||||
patchelf --add-rpath "${lib.makeLibraryPath [libX11]}" "$out/bin/.ghostty-wrapped"
|
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;
|
||||||
platforms = ["x86_64-linux" "aarch64-linux"];
|
platforms = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
mainProgram = "ghostty";
|
mainProgram = "ghostty";
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -15,6 +15,7 @@ const assert = std.debug.assert;
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const build_config = @import("../../build_config.zig");
|
const build_config = @import("../../build_config.zig");
|
||||||
|
const build_options = @import("build_options");
|
||||||
const apprt = @import("../../apprt.zig");
|
const apprt = @import("../../apprt.zig");
|
||||||
const configpkg = @import("../../config.zig");
|
const configpkg = @import("../../config.zig");
|
||||||
const input = @import("../../input.zig");
|
const input = @import("../../input.zig");
|
||||||
@ -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
|
// keyboard state but the block does more than that (i.e. setting up
|
||||||
// WM_CLASS).
|
// WM_CLASS).
|
||||||
const x11_xkb: ?x11.Xkb = x11_xkb: {
|
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;
|
if (!x11.is_display(display)) break :x11_xkb null;
|
||||||
|
|
||||||
// Set the X11 window class property (WM_CLASS) if are are on an X11
|
// Set the X11 window class property (WM_CLASS) if are are on an X11
|
||||||
|
@ -6,6 +6,7 @@ const Surface = @This();
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const build_config = @import("../../build_config.zig");
|
const build_config = @import("../../build_config.zig");
|
||||||
|
const build_options = @import("build_options");
|
||||||
const configpkg = @import("../../config.zig");
|
const configpkg = @import("../../config.zig");
|
||||||
const apprt = @import("../../apprt.zig");
|
const apprt = @import("../../apprt.zig");
|
||||||
const font = @import("../../font/main.zig");
|
const font = @import("../../font/main.zig");
|
||||||
@ -1183,7 +1184,7 @@ fn showContextMenu(self: *Surface, x: f32, y: f32) void {
|
|||||||
@ptrCast(window.window),
|
@ptrCast(window.window),
|
||||||
&c.GRAPHENE_POINT_INIT(point.x, point.y),
|
&c.GRAPHENE_POINT_INIT(point.x, point.y),
|
||||||
@ptrCast(&point),
|
@ptrCast(&point),
|
||||||
) == c.False) {
|
) == 0) {
|
||||||
log.warn("failed computing point for context menu", .{});
|
log.warn("failed computing point for context menu", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1899,7 +1900,7 @@ pub fn dimSurface(self: *Surface) void {
|
|||||||
// Don't dim surface if context menu is open.
|
// Don't dim surface if context menu is open.
|
||||||
// This means we got unfocused due to it opening.
|
// This means we got unfocused due to it opening.
|
||||||
const context_menu_open = c.gtk_widget_get_visible(window.context_menu);
|
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;
|
if (self.unfocused_widget != null) return;
|
||||||
self.unfocused_widget = c.gtk_drawing_area_new();
|
self.unfocused_widget = c.gtk_drawing_area_new();
|
||||||
|
@ -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)));
|
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_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);
|
c.gtk_widget_set_halign(self.context_menu, c.GTK_ALIGN_START);
|
||||||
|
|
||||||
// If we are in fullscreen mode, new windows start fullscreen.
|
// If we are in fullscreen mode, new windows start fullscreen.
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
/// Imported C API directly from header files
|
/// Imported C API directly from header files
|
||||||
pub const c = @cImport({
|
pub const c = @cImport({
|
||||||
@cInclude("gtk/gtk.h");
|
@cInclude("gtk/gtk.h");
|
||||||
if (@import("build_options").adwaita) {
|
if (build_options.adwaita) {
|
||||||
@cInclude("libadwaita-1/adwaita.h");
|
@cInclude("libadwaita-1/adwaita.h");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add in X11-specific GDK backend which we use for specific things
|
if (build_options.x11) {
|
||||||
// (e.g. X11 window class).
|
// Add in X11-specific GDK backend which we use for specific things
|
||||||
@cInclude("gdk/x11/gdkx.h");
|
// (e.g. X11 window class).
|
||||||
// Xkb for X11 state handling
|
@cInclude("gdk/x11/gdkx.h");
|
||||||
@cInclude("X11/XKBlib.h");
|
// Xkb for X11 state handling
|
||||||
|
@cInclude("X11/XKBlib.h");
|
||||||
|
}
|
||||||
|
|
||||||
// generated header files
|
// generated header files
|
||||||
@cInclude("ghostty_resources.h");
|
@cInclude("ghostty_resources.h");
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const build_options = @import("build_options");
|
||||||
const input = @import("../../input.zig");
|
const input = @import("../../input.zig");
|
||||||
const c = @import("c.zig").c;
|
const c = @import("c.zig").c;
|
||||||
const x11 = @import("x11.zig");
|
const x11 = @import("x11.zig");
|
||||||
@ -111,21 +112,26 @@ pub fn eventMods(
|
|||||||
x11_xkb: ?*x11.Xkb,
|
x11_xkb: ?*x11.Xkb,
|
||||||
) input.Mods {
|
) input.Mods {
|
||||||
const device = c.gdk_event_get_device(event);
|
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
|
// Add any modifier state events from Xkb if we have them (X11
|
||||||
// only). Null back from the Xkb call means there was no modifier
|
// only). Null back from the Xkb call means there was no modifier
|
||||||
// event to read. This likely means that the key event did not
|
// 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
|
// result in a modifier change and we can safely rely on the GDK
|
||||||
// state.
|
// state.
|
||||||
xkb.modifier_state_from_notify(display) orelse
|
if (comptime build_options.x11) {
|
||||||
translateMods(gtk_mods)
|
const display = c.gtk_widget_get_display(widget);
|
||||||
else
|
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
|
// 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
|
// to this event do not have the modifier key applied if it was
|
||||||
// presssed (i.e. left control).
|
// 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;
|
mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/// Utility functions for X11 handling.
|
/// Utility functions for X11 handling.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const build_options = @import("build_options");
|
||||||
const c = @import("c.zig").c;
|
const c = @import("c.zig").c;
|
||||||
const input = @import("../../input.zig");
|
const input = @import("../../input.zig");
|
||||||
|
|
||||||
@ -7,6 +8,7 @@ const log = std.log.scoped(.gtk_x11);
|
|||||||
|
|
||||||
/// Returns true if the passed in display is an X11 display.
|
/// Returns true if the passed in display is an X11 display.
|
||||||
pub fn is_display(display: ?*c.GdkDisplay) bool {
|
pub fn is_display(display: ?*c.GdkDisplay) bool {
|
||||||
|
if (comptime !build_options.x11) return false;
|
||||||
return c.g_type_check_instance_is_a(
|
return c.g_type_check_instance_is_a(
|
||||||
@ptrCast(@alignCast(display orelse return false)),
|
@ptrCast(@alignCast(display orelse return false)),
|
||||||
c.gdk_x11_display_get_type(),
|
c.gdk_x11_display_get_type(),
|
||||||
@ -15,11 +17,12 @@ pub fn is_display(display: ?*c.GdkDisplay) bool {
|
|||||||
|
|
||||||
/// Returns true if the app is running on X11
|
/// Returns true if the app is running on X11
|
||||||
pub fn is_current_display_server() bool {
|
pub fn is_current_display_server() bool {
|
||||||
|
if (comptime !build_options.x11) return false;
|
||||||
const display = c.gdk_display_get_default();
|
const display = c.gdk_display_get_default();
|
||||||
return is_display(display);
|
return is_display(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Xkb = struct {
|
pub const Xkb = if (build_options.x11) struct {
|
||||||
base_event_code: c_int,
|
base_event_code: c_int,
|
||||||
funcs: Funcs,
|
funcs: Funcs,
|
||||||
|
|
||||||
@ -111,7 +114,7 @@ pub const Xkb = struct {
|
|||||||
|
|
||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
};
|
} else struct {};
|
||||||
|
|
||||||
/// The functions that we load dynamically from libX11.so.
|
/// The functions that we load dynamically from libX11.so.
|
||||||
const Funcs = struct {
|
const Funcs = struct {
|
||||||
|
@ -22,6 +22,7 @@ pub const BuildConfig = struct {
|
|||||||
version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 },
|
version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 },
|
||||||
flatpak: bool = false,
|
flatpak: bool = false,
|
||||||
adwaita: bool = false,
|
adwaita: bool = false,
|
||||||
|
x11: bool = false,
|
||||||
app_runtime: apprt.Runtime = .none,
|
app_runtime: apprt.Runtime = .none,
|
||||||
renderer: rendererpkg.Impl = .opengl,
|
renderer: rendererpkg.Impl = .opengl,
|
||||||
font_backend: font.Backend = .freetype,
|
font_backend: font.Backend = .freetype,
|
||||||
@ -41,6 +42,7 @@ pub const BuildConfig = struct {
|
|||||||
// support all types.
|
// support all types.
|
||||||
step.addOption(bool, "flatpak", self.flatpak);
|
step.addOption(bool, "flatpak", self.flatpak);
|
||||||
step.addOption(bool, "adwaita", self.adwaita);
|
step.addOption(bool, "adwaita", self.adwaita);
|
||||||
|
step.addOption(bool, "x11", self.x11);
|
||||||
step.addOption(apprt.Runtime, "app_runtime", self.app_runtime);
|
step.addOption(apprt.Runtime, "app_runtime", self.app_runtime);
|
||||||
step.addOption(font.Backend, "font_backend", self.font_backend);
|
step.addOption(font.Backend, "font_backend", self.font_backend);
|
||||||
step.addOption(rendererpkg.Impl, "renderer", self.renderer);
|
step.addOption(rendererpkg.Impl, "renderer", self.renderer);
|
||||||
|
@ -61,6 +61,11 @@ pub fn run(alloc: Allocator) !u8 {
|
|||||||
} else {
|
} else {
|
||||||
try stdout.print(" - libadwaita : disabled\n", .{});
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user