diff --git a/build.zig b/build.zig index 0e183efd10..7902bae48b 100644 --- a/build.zig +++ b/build.zig @@ -105,6 +105,12 @@ 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 the linking against X11 libraries when using the GTK rendering backend.", + ) orelse true; + const pie = b.option( bool, "pie", diff --git a/nix/package.nix b/nix/package.nix index bfc1e47dee..55205f4591 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -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. @@ -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; @@ -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=${ @@ -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"; }; }) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 47ef512546..9128c8b108 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -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"); @@ -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 diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 3d80d92595..5ccf0473f8 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -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"); diff --git a/src/apprt/gtk/c.zig b/src/apprt/gtk/c.zig index 63801250e2..e58aff1abc 100644 --- a/src/apprt/gtk/c.zig +++ b/src/apprt/gtk/c.zig @@ -1,15 +1,21 @@ +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"); + if (build_options.x11) { + @cInclude("gdk/x11/gdkx.h"); + } // Xkb for X11 state handling - @cInclude("X11/XKBlib.h"); + if (build_options.x11) { + @cInclude("X11/XKBlib.h"); + } // generated header files @cInclude("ghostty_resources.h"); diff --git a/src/apprt/gtk/ghostty_gtk_compat.h b/src/apprt/gtk/ghostty_gtk_compat.h index c0dc819c2e..3b4393fdba 100644 --- a/src/apprt/gtk/ghostty_gtk_compat.h +++ b/src/apprt/gtk/ghostty_gtk_compat.h @@ -12,3 +12,11 @@ #ifndef G_APPLICATION_DEFAULT_FLAGS #define G_APPLICATION_DEFAULT_FLAGS G_APPLICATION_FLAGS_NONE #endif + +#ifndef False +#define False 0 +#endif + +#ifndef True +#define True 1 +#endif diff --git a/src/apprt/gtk/key.zig b/src/apprt/gtk/key.zig index f8458bd3bc..e30d6911df 100644 --- a/src/apprt/gtk/key.zig +++ b/src/apprt/gtk/key.zig @@ -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"); @@ -111,21 +112,25 @@ 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| - // 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 + var mods = mods: { + if (build_options.x11) { + const display = c.gtk_widget_get_display(widget); + if (x11_xkb) |xkb| { + // 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. + 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; diff --git a/src/apprt/gtk/x11.zig b/src/apprt/gtk/x11.zig index 0b116c59f6..078305904c 100644 --- a/src/apprt/gtk/x11.zig +++ b/src/apprt/gtk/x11.zig @@ -1,5 +1,6 @@ /// 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"); @@ -7,6 +8,7 @@ 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(), @@ -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, @@ -111,7 +114,7 @@ pub const Xkb = struct { return mods; } -}; +} else struct {}; /// The functions that we load dynamically from libX11.so. const Funcs = struct { diff --git a/src/build_config.zig b/src/build_config.zig index 1f3b35e037..35c4295640 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -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, @@ -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);