diff --git a/build.zig b/build.zig index 63ac1a2cf..0f31ca16f 100644 --- a/build.zig +++ b/build.zig @@ -1273,13 +1273,7 @@ fn addDeps( const gresource = @import("src/apprt/gtk/gresource.zig"); const wf = b.addWriteFiles(); - const gresource_xml = wf.add( - "gresource.xml", - if (config.libadwaita) - gresource.gresource_xml_libadwaita - else - gresource.gresource_xml_gtk, - ); + const gresource_xml = wf.add("gresource.xml", gresource.gresource_xml); const generate_resources_c = b.addSystemCommand(&.{ "glib-compile-resources", @@ -1290,7 +1284,7 @@ fn addDeps( }); const ghostty_resources_c = generate_resources_c.addOutputFileArg("ghostty_resources.c"); generate_resources_c.addFileArg(gresource_xml); - generate_resources_c.extra_file_dependencies = if (config.libadwaita) &gresource.dependencies_libadwaita else &gresource.dependencies_gtk; + generate_resources_c.extra_file_dependencies = &gresource.dependencies; step.addCSourceFile(.{ .file = ghostty_resources_c, .flags = &.{} }); const generate_resources_h = b.addSystemCommand(&.{ @@ -1302,7 +1296,7 @@ fn addDeps( }); const ghostty_resources_h = generate_resources_h.addOutputFileArg("ghostty_resources.h"); generate_resources_h.addFileArg(gresource_xml); - generate_resources_h.extra_file_dependencies = if (config.libadwaita) &gresource.dependencies_libadwaita else &gresource.dependencies_gtk; + generate_resources_h.extra_file_dependencies = &gresource.dependencies; step.addIncludePath(ghostty_resources_h.dirname()); } }, diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index b18753344..155702296 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -128,6 +128,9 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { } } + c.gtk_init(); + const display = c.gdk_display_get_default(); + // If we're using libadwaita, log the version if (adwaita.enabled(&config)) { log.info("libadwaita version build={s} runtime={}.{}.{}", .{ @@ -183,6 +186,51 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { if ((comptime !adwaita.versionAtLeast(0, 0, 0)) or !adwaita.enabled(&config)) { + { + const provider = c.gtk_css_provider_new(); + defer c.g_object_unref(provider); + switch (config.@"window-theme") { + .system, .light => {}, + .dark => { + c.gtk_css_provider_load_from_resource( + provider, + "/com/mitchellh/ghostty/style-dark.css", + ); + c.gtk_style_context_add_provider_for_display( + display, + @ptrCast(provider), + c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 2, + ); + }, + .auto, .ghostty => { + const lum = config.background.toTerminalRGB().perceivedLuminance(); + if (lum <= 0.5) { + c.gtk_css_provider_load_from_resource( + provider, + "/com/mitchellh/ghostty/style-dark.css", + ); + c.gtk_style_context_add_provider_for_display( + display, + @ptrCast(provider), + c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 2, + ); + } + }, + } + } + + { + const provider = c.gtk_css_provider_new(); + defer c.g_object_unref(provider); + + c.gtk_css_provider_load_from_resource(provider, "/com/mitchellh/ghostty/style.css"); + c.gtk_style_context_add_provider_for_display( + display, + @ptrCast(provider), + c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1, + ); + } + break :app @as(?*c.GtkApplication, @ptrCast(c.gtk_application_new( app_id.ptr, app_flags, @@ -277,7 +325,6 @@ 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: { - const display = c.gdk_display_get_default(); if (!x11.is_display(display)) break :x11_xkb null; // Set the X11 window class property (WM_CLASS) if are are on an X11 @@ -335,7 +382,14 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { ); } + // Internally, GTK ensures that only one instance of this provider exists in the provider list + // for the display. const css_provider = c.gtk_css_provider_new(); + c.gtk_style_context_add_provider_for_display( + display, + @ptrCast(css_provider), + c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 3, + ); try loadRuntimeCss(&config, css_provider); return .{ diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index a3d74c77f..a9130ea26 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -80,10 +80,17 @@ pub fn init(self: *Window, app: *App) !void { }; // Create the window - const window: *c.GtkWidget = if (self.isAdwWindow()) - c.adw_application_window_new(app.app) - else - c.gtk_application_window_new(app.app); + const window: *c.GtkWidget = window: { + if (self.isAdwWindow()) { + const window = c.adw_application_window_new(app.app); + c.gtk_widget_add_css_class(@ptrCast(window), "adw"); + break :window window; + } else { + const window = c.gtk_application_window_new(app.app); + c.gtk_widget_add_css_class(@ptrCast(window), "gtk"); + break :window window; + } + }; const gtk_window: *c.GtkWindow = @ptrCast(window); errdefer if (self.isAdwWindow()) { @@ -111,11 +118,6 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_widget_remove_css_class(@ptrCast(window), "background"); } - // Internally, GTK ensures that only one instance of this provider exists in the provider list - // for the display. - const display = c.gdk_display_get_default(); - c.gtk_style_context_add_provider_for_display(display, @ptrCast(app.css_provider), c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - // Create our box which will hold our widgets in the main content area. const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0); diff --git a/src/apprt/gtk/gresource.zig b/src/apprt/gtk/gresource.zig index 5705e96da..db987cbea 100644 --- a/src/apprt/gtk/gresource.zig +++ b/src/apprt/gtk/gresource.zig @@ -49,45 +49,42 @@ const icons = [_]struct { }, }; -pub const gresource_xml_gtk = comptimeGenerateGResourceXML(false); -pub const gresource_xml_libadwaita = comptimeGenerateGResourceXML(true); +pub const gresource_xml = comptimeGenerateGResourceXML(); -fn comptimeGenerateGResourceXML(comptime libadwaita: bool) []const u8 { +fn comptimeGenerateGResourceXML() []const u8 { comptime { @setEvalBranchQuota(13000); var counter = std.io.countingWriter(std.io.null_writer); - try writeGResourceXML(libadwaita, &counter.writer()); + try writeGResourceXML(&counter.writer()); var buf: [counter.bytes_written]u8 = undefined; var stream = std.io.fixedBufferStream(&buf); - try writeGResourceXML(libadwaita, stream.writer()); + try writeGResourceXML(stream.writer()); const final = buf; return final[0..stream.getWritten().len]; } } -fn writeGResourceXML(libadwaita: bool, writer: anytype) !void { +fn writeGResourceXML(writer: anytype) !void { try writer.writeAll( \\ \\ \\ ); - if (libadwaita) { - try writer.writeAll( - \\ - \\ - ); - for (css_files) |css_file| { - try writer.print( - " src/apprt/gtk/{s}\n", - .{ css_file, css_file }, - ); - } - try writer.writeAll( - \\ - \\ + try writer.writeAll( + \\ + \\ + ); + for (css_files) |css_file| { + try writer.print( + " src/apprt/gtk/{s}\n", + .{ css_file, css_file }, ); } + try writer.writeAll( + \\ + \\ + ); try writer.writeAll( \\ \\ @@ -105,15 +102,7 @@ fn writeGResourceXML(libadwaita: bool, writer: anytype) !void { ); } -pub const dependencies_gtk = deps: { - var deps: [icons.len][]const u8 = undefined; - for (icons, 0..) |icon, i| { - deps[i] = std.fmt.comptimePrint("images/icons/icon_{s}.png", .{icon.source}); - } - break :deps deps; -}; - -pub const dependencies_libadwaita = deps: { +pub const dependencies = deps: { var deps: [css_files.len + icons.len][]const u8 = undefined; for (css_files, 0..) |css_file, i| { deps[i] = std.fmt.comptimePrint("src/apprt/gtk/{s}", .{css_file});