diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 3a72e17526..6addd6794d 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -31,6 +31,8 @@ const log = std.log.scoped(.gtk); app: *App, +config: DerivedConfig, + /// Our window window: *c.GtkWindow, @@ -59,6 +61,38 @@ adw_tab_overview_focus_timer: ?c.guint = null, /// State and logic for windowing protocol for a window. winproto: winproto.Window, +pub const DerivedConfig = struct { + adw_enabled: bool, + background_opacity: f64, + background_blur: configpkg.Config.BackgroundBlur, + window_theme: configpkg.Config.WindowTheme, + gtk_titlebar: bool, + gtk_tabs_location: configpkg.Config.GtkTabsLocation, + gtk_wide_tabs: bool, + adw_toolbar_style: configpkg.Config.AdwToolbarStyle, + + maximize: bool, + fullscreen: bool, + window_decoration: configpkg.Config.WindowDecoration, + + pub fn init(config: *const configpkg.Config) DerivedConfig { + return .{ + .adw_enabled = adwaita.enabled(config), + .background_opacity = config.@"background-opacity", + .background_blur = config.@"background-blur", + .window_theme = config.@"window-theme", + .gtk_titlebar = config.@"gtk-titlebar", + .gtk_tabs_location = config.@"gtk-tabs-location", + .gtk_wide_tabs = config.@"gtk-wide-tabs", + .adw_toolbar_style = config.@"adw-toolbar-style", + + .maximize = config.maximize, + .fullscreen = config.fullscreen, + .window_decoration = config.@"window-decoration", + }; + } +}; + pub fn create(alloc: Allocator, app: *App) !*Window { // Allocate a fixed pointer for our window. We try to minimize // allocations but windows and other GUI requirements are so minimal @@ -77,6 +111,7 @@ pub fn init(self: *Window, app: *App) !void { // Set up our own state self.* = .{ .app = app, + .config = DerivedConfig.init(&app.config), .window = undefined, .headerbar = undefined, .tab_overview = null, @@ -88,7 +123,7 @@ pub fn init(self: *Window, app: *App) !void { // Create the window const window: *c.GtkWidget = window: { - if ((comptime adwaita.versionAtLeast(0, 0, 0)) and adwaita.enabled(&self.app.config)) { + if ((comptime adwaita.versionAtLeast(0, 0, 0)) and self.config.adw_enabled) { const window = c.adw_application_window_new(app.app); c.gtk_widget_add_css_class(@ptrCast(window), "adw"); break :window window; @@ -113,13 +148,6 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_window_set_icon_name(gtk_window, build_config.bundle_id); - // Apply class to color headerbar if window-theme is set to `ghostty` and - // GTK version is before 4.16. The conditional is because above 4.16 - // we use GTK CSS color variables. - if (!version.atLeast(4, 16, 0) and app.config.@"window-theme" == .ghostty) { - c.gtk_widget_add_css_class(@ptrCast(gtk_window), "window-theme-ghostty"); - } - // Create our box which will hold our widgets in the main content area. const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0); @@ -127,7 +155,7 @@ pub fn init(self: *Window, app: *App) !void { self.notebook.init(); // If we are using Adwaita, then we can support the tab overview. - self.tab_overview = if ((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.enabled(&self.app.config) and adwaita.versionAtLeast(1, 4, 0)) overview: { + self.tab_overview = if ((comptime adwaita.versionAtLeast(1, 4, 0)) and self.config.adw_enabled and adwaita.versionAtLeast(1, 4, 0)) overview: { const tab_overview = c.adw_tab_overview_new(); c.adw_tab_overview_set_view(@ptrCast(tab_overview), self.notebook.adw.tab_view); c.adw_tab_overview_set_enable_new_tab(@ptrCast(tab_overview), 1); @@ -167,8 +195,9 @@ pub fn init(self: *Window, app: *App) !void { // If we're using an AdwWindow then we can support the tab overview. if (self.tab_overview) |tab_overview| { if (comptime !adwaita.versionAtLeast(1, 4, 0)) unreachable; - assert(self.app.config.@"gtk-adwaita" and adwaita.versionAtLeast(1, 4, 0)); - const btn = switch (app.config.@"gtk-tabs-location") { + assert(self.config.adw_enabled and adwaita.versionAtLeast(1, 4, 0)); + + const btn = switch (self.config.gtk_tabs_location) { .top, .bottom, .left, .right => btn: { const btn = c.gtk_toggle_button_new(); c.gtk_widget_set_tooltip_text(btn, "View Open Tabs"); @@ -203,13 +232,12 @@ pub fn init(self: *Window, app: *App) !void { self.headerbar.packStart(btn); } - _ = c.g_signal_connect_data(gtk_window, "notify::decorated", c.G_CALLBACK(>kWindowNotifyDecorated), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(gtk_window, "notify::maximized", c.G_CALLBACK(>kWindowNotifyMaximized), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(gtk_window, "notify::fullscreened", c.G_CALLBACK(>kWindowNotifyFullscreened), self, null, c.G_CONNECT_DEFAULT); // If Adwaita is enabled and is older than 1.4.0 we don't have the tab overview and so we // need to stick the headerbar into the content box. - if (!adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)) { + if (!adwaita.versionAtLeast(1, 4, 0) and self.config.adw_enabled) { c.gtk_box_append(@ptrCast(box), self.headerbar.asWidget()); } @@ -219,7 +247,7 @@ pub fn init(self: *Window, app: *App) !void { const warning_box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0); const warning_text = "⚠️ You're running a debug build of Ghostty! Performance will be degraded."; if ((comptime adwaita.versionAtLeast(1, 3, 0)) and - adwaita.enabled(&app.config) and + self.config.adw_enabled and adwaita.versionAtLeast(1, 3, 0)) { const banner = c.adw_banner_new(warning_text); @@ -237,7 +265,10 @@ pub fn init(self: *Window, app: *App) !void { } // Setup our toast overlay if we have one - self.toast_overlay = if (adwaita.enabled(&self.app.config)) toast: { + self.toast_overlay = toast: { + if ((comptime !adwaita.versionAtLeast(0, 0, 0)) or !self.config.adw_enabled) + break :toast null; + const toast_overlay = c.adw_toast_overlay_new(); c.adw_toast_overlay_set_child( @ptrCast(toast_overlay), @@ -245,10 +276,8 @@ pub fn init(self: *Window, app: *App) !void { ); c.gtk_box_append(@ptrCast(box), toast_overlay); break :toast toast_overlay; - } else toast: { - c.gtk_box_append(@ptrCast(box), self.notebook.asWidget()); - break :toast null; }; + c.gtk_box_append(@ptrCast(box), self.toast_overlay orelse self.notebook.asWidget()); // If we have a tab overview then we can set it on our notebook. if (self.tab_overview) |tab_overview| { @@ -262,12 +291,6 @@ pub fn init(self: *Window, app: *App) !void { 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 want the window to be maximized, we do that here. - if (app.config.maximize) c.gtk_window_maximize(self.window); - - // If we are in fullscreen mode, new windows start fullscreen. - if (app.config.fullscreen) c.gtk_window_fullscreen(self.window); - // We register a key event controller with the window so // we can catch key events when our surface may not be // focused (i.e. when the libadw tab overview is shown). @@ -285,19 +308,19 @@ pub fn init(self: *Window, app: *App) !void { // Our actions for the menu initActions(self); - if ((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)) { + if ((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0) and self.config.adw_enabled) { const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_toolbar_view_new()); c.adw_toolbar_view_add_top_bar(toolbar_view, self.headerbar.asWidget()); - if (self.app.config.@"gtk-tabs-location" != .hidden) { + if (self.config.gtk_tabs_location != .hidden) { const tab_bar = c.adw_tab_bar_new(); c.adw_tab_bar_set_view(tab_bar, self.notebook.adw.tab_view); - if (!app.config.@"gtk-wide-tabs") c.adw_tab_bar_set_expand_tabs(tab_bar, 0); + if (!self.config.gtk_wide_tabs) c.adw_tab_bar_set_expand_tabs(tab_bar, 0); const tab_bar_widget: *c.GtkWidget = @ptrCast(@alignCast(tab_bar)); - switch (self.app.config.@"gtk-tabs-location") { + switch (self.config.gtk_tabs_location) { // left and right are not supported in libadwaita. .top, .left, .right => c.adw_toolbar_view_add_top_bar(toolbar_view, tab_bar_widget), .bottom => c.adw_toolbar_view_add_bottom_bar(toolbar_view, tab_bar_widget), @@ -306,14 +329,6 @@ pub fn init(self: *Window, app: *App) !void { } c.adw_toolbar_view_set_content(toolbar_view, box); - const toolbar_style: c.AdwToolbarStyle = switch (self.app.config.@"adw-toolbar-style") { - .flat => c.ADW_TOOLBAR_FLAT, - .raised => c.ADW_TOOLBAR_RAISED, - .@"raised-border" => c.ADW_TOOLBAR_RAISED_BORDER, - }; - c.adw_toolbar_view_set_top_bar_style(toolbar_view, toolbar_style); - c.adw_toolbar_view_set_bottom_bar_style(toolbar_view, toolbar_style); - // Set our application window content. c.adw_tab_overview_set_child( @ptrCast(self.tab_overview), @@ -326,12 +341,13 @@ pub fn init(self: *Window, app: *App) !void { } else tab_bar: { switch (self.notebook) { .adw => |*adw| if (comptime adwaita.versionAtLeast(0, 0, 0)) { - if (app.config.@"gtk-tabs-location" == .hidden) break :tab_bar; + if (self.config.gtk_tabs_location == .hidden) break :tab_bar; + // In earlier adwaita versions, we need to add the tabbar manually since we do not use // an AdwToolbarView. const tab_bar: *c.AdwTabBar = c.adw_tab_bar_new().?; c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_bar)), "inline"); - switch (app.config.@"gtk-tabs-location") { + switch (self.config.gtk_tabs_location) { .top, .left, .right, @@ -345,14 +361,14 @@ pub fn init(self: *Window, app: *App) !void { } c.adw_tab_bar_set_view(tab_bar, adw.tab_view); - if (!app.config.@"gtk-wide-tabs") c.adw_tab_bar_set_expand_tabs(tab_bar, 0); + if (!self.config.gtk_wide_tabs) c.adw_tab_bar_set_expand_tabs(tab_bar, 0); }, .gtk => {}, } // The box is our main child - if (!adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)) { + if ((comptime adwaita.versionAtLeast(0, 0, 0)) and !adwaita.versionAtLeast(1, 4, 0) and self.config.adw_enabled) { c.adw_application_window_set_content( @ptrCast(gtk_window), box, @@ -363,6 +379,12 @@ pub fn init(self: *Window, app: *App) !void { } } + // If we want the window to be maximized, we do that here. + if (self.config.maximize) c.gtk_window_maximize(self.window); + + // If we are in fullscreen mode, new windows start fullscreen. + if (self.config.fullscreen) c.gtk_window_fullscreen(self.window); + // Show the window c.gtk_widget_show(window); } @@ -371,56 +393,89 @@ pub fn updateConfig( self: *Window, config: *const configpkg.Config, ) !void { - self.winproto.updateConfigEvent(config) catch |err| { + self.config = DerivedConfig.init(config); + + self.winproto.updateConfigEvent(&self.config) catch |err| { // We want to continue attempting to make the other config // changes necessary so we just log the error and continue. log.warn("failed to update window protocol config error={}", .{err}); }; // We always resync our appearance whenever the config changes. - try self.syncAppearance(config); + try self.syncAppearance(); } /// Updates appearance based on config settings. Will be called once upon window -/// realization, and every time the config is reloaded. +/// realization, every time the config is reloaded, and every time a window state +/// is toggled (un-/maximized, un-/fullscreened, window decorations toggled, etc.) /// /// TODO: Many of the initial style settings in `create` could possibly be made /// reactive by moving them here. -pub fn syncAppearance(self: *Window, config: *const configpkg.Config) !void { - self.winproto.syncAppearance() catch |err| { - log.warn("failed to sync winproto appearance error={}", .{err}); - }; +pub fn syncAppearance(self: *Window) !void { + const csd_enabled = self.winproto.clientSideDecorationEnabled(); + c.gtk_window_set_decorated(self.window, @intFromBool(csd_enabled)); + + // Fix any artifacting that may occur in window corners. The .ssd CSS + // class is defined in the GtkWindow documentation: + // https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition + // for .ssd is provided by GTK and Adwaita. + toggleCssClass(@ptrCast(self.window), "csd", csd_enabled); + toggleCssClass(@ptrCast(self.window), "ssd", !csd_enabled); + toggleCssClass(@ptrCast(self.window), "no-border-radius", !csd_enabled); + + self.headerbar.setVisible(visible: { + // Never display the header bar when CSDs are disabled. + if (!csd_enabled) break :visible false; + + // Unconditionally disable the header bar when fullscreened. + if (self.config.fullscreen) break :visible false; + + // *Conditionally* disable the header bar when maximized, + // and gtk-titlebar-hide-when-maximized is set + if (self.config.maximize and self.app.config.@"gtk-titlebar-hide-when-maximized") + break :visible false; + + break :visible self.config.gtk_titlebar; + }); toggleCssClass( @ptrCast(self.window), "background", - config.@"background-opacity" >= 1, + self.config.background_opacity >= 1, ); - // If we are disabling CSDs then disable them right away. - const csd_enabled = self.winproto.clientSideDecorationEnabled(); - c.gtk_window_set_decorated(self.window, @intFromBool(csd_enabled)); + // Apply class to color headerbar if window-theme is set to `ghostty` and + // GTK version is before 4.16. The conditional is because above 4.16 + // we use GTK CSS color variables. + toggleCssClass( + @ptrCast(self.window), + "window-theme-ghostty", + !version.atLeast(4, 16, 0) and self.config.window_theme == .ghostty, + ); - // If we are not decorated then we hide the titlebar. - self.headerbar.setVisible(config.@"gtk-titlebar" and csd_enabled); + if ((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0) and self.config.adw_enabled) tab_overview: { + const tab_overview: *c.AdwTabOverview = @ptrCast(self.tab_overview orelse break :tab_overview); - // Disable the title buttons (close, maximize, minimize, ...) - // *inside* the tab overview if CSDs are disabled. - // We do spare the search button, though. - if ((comptime adwaita.versionAtLeast(1, 4, 0)) and - adwaita.enabled(&self.app.config)) - { - if (self.tab_overview) |tab_overview| { - c.adw_tab_overview_set_show_start_title_buttons( - @ptrCast(tab_overview), - @intFromBool(csd_enabled), - ); - c.adw_tab_overview_set_show_end_title_buttons( - @ptrCast(tab_overview), - @intFromBool(csd_enabled), - ); - } + // Disable the title buttons (close, maximize, minimize, ...) + // *inside* the tab overview if CSDs are disabled. + // We do spare the search button, though. + c.adw_tab_overview_set_show_start_title_buttons(tab_overview, @intFromBool(csd_enabled)); + c.adw_tab_overview_set_show_end_title_buttons(tab_overview, @intFromBool(csd_enabled)); + + // Update toolbar view style + const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_tab_overview_get_child(tab_overview)); + const toolbar_style: c.AdwToolbarStyle = switch (self.config.adw_toolbar_style) { + .flat => c.ADW_TOOLBAR_FLAT, + .raised => c.ADW_TOOLBAR_RAISED, + .@"raised-border" => c.ADW_TOOLBAR_RAISED_BORDER, + }; + c.adw_toolbar_view_set_top_bar_style(toolbar_view, toolbar_style); + c.adw_toolbar_view_set_bottom_bar_style(toolbar_view, toolbar_style); } + + self.winproto.syncAppearance() catch |err| { + log.warn("failed to sync winproto appearance error={}", .{err}); + }; } fn toggleCssClass( @@ -561,30 +616,40 @@ pub fn toggleTabOverview(self: *Window) void { /// Toggle the maximized state for this window. pub fn toggleMaximize(self: *Window) void { - if (c.gtk_window_is_maximized(self.window) == 0) { - c.gtk_window_maximize(self.window); - } else { + if (self.config.maximize) { c.gtk_window_unmaximize(self.window); + } else { + c.gtk_window_maximize(self.window); } + // We update the config and call syncAppearance + // in the gtkWindowNotifyMaximized callback } /// Toggle fullscreen for this window. pub fn toggleFullscreen(self: *Window) void { - const is_fullscreen = c.gtk_window_is_fullscreen(self.window); - if (is_fullscreen == 0) { - c.gtk_window_fullscreen(self.window); - } else { + if (self.config.fullscreen) { c.gtk_window_unfullscreen(self.window); + } else { + c.gtk_window_fullscreen(self.window); } + // We update the config and call syncAppearance + // in the gtkWindowNotifyFullscreened callback } /// Toggle the window decorations for this window. pub fn toggleWindowDecorations(self: *Window) void { - self.app.config.@"window-decoration" = switch (self.app.config.@"window-decoration") { + self.config.window_decoration = switch (self.config.window_decoration) { + .none => switch (self.app.config.@"window-decoration") { + // If we started as none, then we switch to auto + .none => .auto, + // Switch back + .auto, .client, .server => |v| v, + }, + // Always set to none .auto, .client, .server => .none, - .none => .client, }; - self.updateConfig(&self.app.config) catch {}; + + self.syncAppearance() catch {}; } /// Grabs focus on the currently selected tab. @@ -607,23 +672,22 @@ pub fn sendToast(self: *Window, title: [:0]const u8) void { c.adw_toast_overlay_add_toast(@ptrCast(toast_overlay), toast); } -fn gtkRealize(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool { +fn gtkRealize(_: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool { const self = userdataSelf(ud.?); // Initialize our window protocol logic if (winproto.Window.init( self.app.core_app.alloc, &self.app.winproto, - v, - &self.app.config, - )) |winproto_win| { - self.winproto = winproto_win; + self, + )) |wp| { + self.winproto = wp; } else |err| { log.warn("failed to initialize window protocol error={}", .{err}); } // When we are realized we always setup our appearance - self.syncAppearance(&self.app.config) catch |err| { + self.syncAppearance() catch |err| { log.err("failed to initialize appearance={}", .{err}); }; @@ -636,60 +700,18 @@ fn gtkWindowNotifyMaximized( ud: ?*anyopaque, ) callconv(.C) void { const self = userdataSelf(ud orelse return); - - // Only toggle visibility of the header bar when we're using CSDs, - // and actually intend on displaying the header bar - if (!self.winproto.clientSideDecorationEnabled()) return; - - // If we aren't maximized, we should show the headerbar again - // if it was originally visible. - const maximized = c.gtk_window_is_maximized(self.window) != 0; - if (!maximized) { - self.headerbar.setVisible(self.app.config.@"gtk-titlebar"); - return; - } - - // If we are maximized, we should hide the headerbar if requested. - if (self.app.config.@"gtk-titlebar-hide-when-maximized") { - self.headerbar.setVisible(false); - } -} - -fn gtkWindowNotifyDecorated( - object: *c.GObject, - _: *c.GParamSpec, - ud: ?*anyopaque, -) callconv(.C) void { - const self = userdataSelf(ud orelse return); - const is_decorated = c.gtk_window_get_decorated(@ptrCast(object)) == 1; - - // Fix any artifacting that may occur in window corners. The .ssd CSS - // class is defined in the GtkWindow documentation: - // https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition - // for .ssd is provided by GTK and Adwaita. - toggleCssClass(@ptrCast(object), "ssd", !is_decorated); - toggleCssClass(@ptrCast(object), "no-border-radius", !is_decorated); - - // FIXME: This is to update the blur region offset on X11. - // Remove this when we move everything related to window appearance - // to `syncAppearance` for Ghostty 1.2. - self.winproto.syncAppearance() catch {}; + self.config.maximize = c.gtk_window_is_maximized(self.window) != 0; + self.syncAppearance() catch {}; } fn gtkWindowNotifyFullscreened( - object: *c.GObject, + _: *c.GObject, _: *c.GParamSpec, ud: ?*anyopaque, ) callconv(.C) void { const self = userdataSelf(ud orelse return); - const fullscreened = c.gtk_window_is_fullscreen(@ptrCast(object)) != 0; - if (!fullscreened) { - const csd_enabled = self.winproto.clientSideDecorationEnabled(); - self.headerbar.setVisible(self.app.config.@"gtk-titlebar" and csd_enabled); - return; - } - - self.headerbar.setVisible(false); + self.config.fullscreen = c.gtk_window_is_fullscreen(self.window) != 0; + self.syncAppearance() catch {}; } // Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab @@ -707,7 +729,7 @@ fn gtkTabNewClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { /// because we need to return an AdwTabPage from this function. fn gtkNewTabFromOverview(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) ?*c.AdwTabPage { const self: *Window = userdataSelf(ud.?); - assert((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)); + assert((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0) and self.config.adw_enabled); const alloc = self.app.core_app.alloc; const surface = self.actionSurface(); @@ -889,7 +911,7 @@ fn gtkActionAbout( if ((comptime adwaita.versionAtLeast(1, 5, 0)) and adwaita.versionAtLeast(1, 5, 0) and - adwaita.enabled(&self.app.config)) + self.config.adw_enabled) { c.adw_show_about_dialog( @ptrCast(self.window), diff --git a/src/apprt/gtk/winproto.zig b/src/apprt/gtk/winproto.zig index e6020f49e8..ebdaa9396e 100644 --- a/src/apprt/gtk/winproto.zig +++ b/src/apprt/gtk/winproto.zig @@ -5,6 +5,7 @@ const c = @import("c.zig").c; const Config = @import("../../config.zig").Config; const input = @import("../../input.zig"); const key = @import("key.zig"); +const ApprtWindow = @import("Window.zig"); pub const noop = @import("winproto/noop.zig"); pub const x11 = @import("winproto/x11.zig"); @@ -74,8 +75,7 @@ pub const Window = union(Protocol) { pub fn init( alloc: Allocator, app: *App, - window: *c.GtkWindow, - config: *const Config, + apprt_window: *ApprtWindow, ) !Window { return switch (app.*) { inline else => |*v, tag| { @@ -90,8 +90,7 @@ pub const Window = union(Protocol) { try field.type.init( alloc, v, - window, - config, + apprt_window, ), ); } @@ -113,7 +112,7 @@ pub const Window = union(Protocol) { pub fn updateConfigEvent( self: *Window, - config: *const Config, + config: *const ApprtWindow.DerivedConfig, ) !void { switch (self.*) { inline else => |*v| try v.updateConfigEvent(config), diff --git a/src/apprt/gtk/winproto/noop.zig b/src/apprt/gtk/winproto/noop.zig index 38703aecbe..c10e768dc1 100644 --- a/src/apprt/gtk/winproto/noop.zig +++ b/src/apprt/gtk/winproto/noop.zig @@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator; const c = @import("../c.zig").c; const Config = @import("../../../config.zig").Config; const input = @import("../../../input.zig"); +const ApprtWindow = @import("../Window.zig"); const log = std.log.scoped(.winproto_noop); @@ -34,8 +35,7 @@ pub const Window = struct { pub fn init( _: Allocator, _: *App, - _: *c.GtkWindow, - _: *const Config, + _: *ApprtWindow, ) !Window { return .{}; } @@ -47,7 +47,7 @@ pub const Window = struct { pub fn updateConfigEvent( _: *Window, - _: *const Config, + _: *const ApprtWindow.DerivedConfig, ) !void {} pub fn resizeEvent(_: *Window) !void {} diff --git a/src/apprt/gtk/winproto/wayland.zig b/src/apprt/gtk/winproto/wayland.zig index 3e239eb296..0c6f7b68c2 100644 --- a/src/apprt/gtk/winproto/wayland.zig +++ b/src/apprt/gtk/winproto/wayland.zig @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; const c = @import("../c.zig").c; const Config = @import("../../../config.zig").Config; const input = @import("../../../input.zig"); +const ApprtWindow = @import("../Window.zig"); const wl = wayland.client.wl; const org = wayland.client.org; @@ -174,10 +175,10 @@ pub const Window = struct { blur: bool, window_decoration: Config.WindowDecoration, - pub fn init(config: *const Config) DerivedConfig { + pub fn init(config: *const ApprtWindow.DerivedConfig) DerivedConfig { return .{ - .blur = config.@"background-blur".enabled(), - .window_decoration = config.@"window-decoration", + .blur = config.background_blur.enabled(), + .window_decoration = config.window_decoration, }; } }; @@ -185,13 +186,12 @@ pub const Window = struct { pub fn init( alloc: Allocator, app: *App, - gtk_window: *c.GtkWindow, - config: *const Config, + apprt_window: *ApprtWindow, ) !Window { _ = alloc; const gdk_surface = c.gtk_native_get_surface( - @ptrCast(gtk_window), + @ptrCast(apprt_window.window), ) orelse return error.NotWaylandSurface; // This should never fail, because if we're being called at this point @@ -222,7 +222,7 @@ pub const Window = struct { }; return .{ - .config = DerivedConfig.init(config), + .config = DerivedConfig.init(&apprt_window.config), .surface = wl_surface, .app_context = app.context, .blur_token = null, @@ -236,10 +236,7 @@ pub const Window = struct { if (self.decoration) |deco| deco.release(); } - pub fn updateConfigEvent( - self: *Window, - config: *const Config, - ) !void { + pub fn updateConfigEvent(self: *Window, config: *const ApprtWindow.DerivedConfig) !void { self.config = DerivedConfig.init(config); } diff --git a/src/apprt/gtk/winproto/x11.zig b/src/apprt/gtk/winproto/x11.zig index c58df6dea4..eada2728c4 100644 --- a/src/apprt/gtk/winproto/x11.zig +++ b/src/apprt/gtk/winproto/x11.zig @@ -7,6 +7,7 @@ const c = @import("../c.zig").c; const input = @import("../../../input.zig"); const Config = @import("../../../config.zig").Config; const adwaita = @import("../adwaita.zig"); +const ApprtWindow = @import("../Window.zig"); const log = std.log.scoped(.gtk_x11); @@ -151,7 +152,6 @@ pub const App = struct { pub const Window = struct { app: *App, - alloc: Allocator, config: DerivedConfig, window: c.Window, gtk_window: *c.GtkWindow, @@ -162,10 +162,10 @@ pub const Window = struct { blur: bool, window_decoration: Config.WindowDecoration, - pub fn init(config: *const Config) DerivedConfig { + pub fn init(config: *const ApprtWindow.DerivedConfig) DerivedConfig { return .{ - .blur = config.@"background-blur".enabled(), - .window_decoration = config.@"window-decoration", + .blur = config.background_blur.enabled(), + .window_decoration = config.window_decoration, }; } }; @@ -173,11 +173,12 @@ pub const Window = struct { pub fn init( alloc: Allocator, app: *App, - gtk_window: *c.GtkWindow, - config: *const Config, + apprt_window: *ApprtWindow, ) !Window { + _ = alloc; + const surface = c.gtk_native_get_surface( - @ptrCast(gtk_window), + @ptrCast(apprt_window.window), ) orelse return error.NotX11Surface; // Check if we're actually on X11 @@ -188,10 +189,9 @@ pub const Window = struct { return .{ .app = app, - .alloc = alloc, - .config = DerivedConfig.init(config), + .config = DerivedConfig.init(&apprt_window.config), .window = c.gdk_x11_surface_get_xid(surface), - .gtk_window = gtk_window, + .gtk_window = apprt_window.window, }; } @@ -202,7 +202,7 @@ pub const Window = struct { pub fn updateConfigEvent( self: *Window, - config: *const Config, + config: *const ApprtWindow.DerivedConfig, ) !void { self.config = DerivedConfig.init(config); } diff --git a/src/config/Config.zig b/src/config/Config.zig index 3010b87d1e..fe42adfe29 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1174,12 +1174,7 @@ keybind: Keybinds = .{}, /// Windows). /// /// The "toggle_window_decorations" keybind action can be used to create -/// a keybinding to toggle this setting at runtime. This will always toggle -/// back to "auto" if the current value is "none" (this is an issue -/// that will be fixed in the future). -/// -/// Changing this configuration in your configuration and reloading will -/// only affect new windows. Existing windows will not be affected. +/// a keybinding to toggle this setting at runtime. /// /// macOS: To hide the titlebar without removing the native window borders /// or rounded corners, use `macos-titlebar-style = hidden` instead. @@ -1286,8 +1281,8 @@ keybind: Keybinds = .{}, /// window will be placed below the menu bar. /// /// Note: this is only supported on macOS and Linux GLFW builds. The GTK -/// runtime does not support setting the window position (this is a limitation -/// of GTK 4.0). +/// runtime does not support setting the window position, as windows are +/// only allowed position themselves in X11 and not Wayland. @"window-position-x": ?i16 = null, @"window-position-y": ?i16 = null, @@ -2115,9 +2110,6 @@ keybind: Keybinds = .{}, /// /// This option does nothing when `window-decoration` is false or when running /// under macOS. -/// -/// Changing this value at runtime and reloading the configuration will only -/// affect new windows. @"gtk-titlebar": bool = true, /// Determines the side of the screen that the GTK tab bar will stick to. @@ -2148,8 +2140,6 @@ keybind: Keybinds = .{}, /// * `raised` - Top and bottom bars cast a shadow on the terminal area. /// * `raised-border` - Similar to `raised` but the shadow is replaced with a /// more subtle border. -/// -/// Changing this value at runtime will only affect new windows. @"adw-toolbar-style": AdwToolbarStyle = .raised, /// If `true` (default), then the Ghostty GTK tabs will be "wide." Wide tabs