mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-03 12:48:44 +03:00
gtk: apply all window appearance changes in syncAppearance
The GTK side of appearance code is kind of a mess with several different functions all having the responsibility of interacting with each other and setting the appropriate window appearance. It should solely be the responsibility of the `syncAppearance` function to apply appearance changes, with other callbacks/functions calling it instead: much like what we already do for the macOS apprt.
This commit is contained in:
@ -31,6 +31,8 @@ const log = std.log.scoped(.gtk);
|
||||
|
||||
app: *App,
|
||||
|
||||
config: DerivedConfig,
|
||||
|
||||
/// Our window
|
||||
window: *c.GtkWindow,
|
||||
|
||||
@ -55,6 +57,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 {
|
||||
background_opacity: f64,
|
||||
background_blur: configpkg.Config.BackgroundBlur,
|
||||
window_theme: configpkg.Config.WindowTheme,
|
||||
gtk_titlebar: bool,
|
||||
gtk_titlebar_hide_when_maximized: bool,
|
||||
gtk_tabs_location: configpkg.Config.GtkTabsLocation,
|
||||
gtk_wide_tabs: bool,
|
||||
gtk_toolbar_style: configpkg.Config.GtkToolbarStyle,
|
||||
|
||||
maximize: bool,
|
||||
fullscreen: bool,
|
||||
window_decoration: configpkg.Config.WindowDecoration,
|
||||
|
||||
pub fn init(config: *const configpkg.Config) DerivedConfig {
|
||||
return .{
|
||||
.background_opacity = config.@"background-opacity",
|
||||
.background_blur = config.@"background-blur",
|
||||
.window_theme = config.@"window-theme",
|
||||
.gtk_titlebar = config.@"gtk-titlebar",
|
||||
.gtk_titlebar_hide_when_maximized = config.@"gtk-titlebar-hide-when-maximized",
|
||||
.gtk_tabs_location = config.@"gtk-tabs-location",
|
||||
.gtk_wide_tabs = config.@"gtk-wide-tabs",
|
||||
.gtk_toolbar_style = config.@"gtk-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
|
||||
@ -73,6 +107,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,
|
||||
@ -99,13 +134,6 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
|
||||
c.gtk_window_set_icon_name(self.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(gtk_widget, "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);
|
||||
|
||||
@ -153,7 +181,7 @@ 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 (!adwaita.versionAtLeast(1, 4, 0)) unreachable;
|
||||
const btn = switch (app.config.@"gtk-tabs-location") {
|
||||
const btn = switch (self.config.gtk_tabs_location) {
|
||||
.top, .bottom => btn: {
|
||||
const btn = c.gtk_toggle_button_new();
|
||||
c.gtk_widget_set_tooltip_text(btn, "View Open Tabs");
|
||||
@ -188,7 +216,6 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
self.headerbar.packStart(btn);
|
||||
}
|
||||
|
||||
_ = c.g_signal_connect_data(self.window, "notify::decorated", c.G_CALLBACK(>kWindowNotifyDecorated), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(self.window, "notify::maximized", c.G_CALLBACK(>kWindowNotifyMaximized), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(self.window, "notify::fullscreened", c.G_CALLBACK(>kWindowNotifyFullscreened), self, null, c.G_CONNECT_DEFAULT);
|
||||
|
||||
@ -237,12 +264,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).
|
||||
@ -265,14 +286,14 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
|
||||
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, @ptrCast(@alignCast(self.notebook.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) {
|
||||
.top => 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),
|
||||
.hidden => unreachable,
|
||||
@ -280,7 +301,7 @@ 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.@"gtk-toolbar-style") {
|
||||
const toolbar_style: c.AdwToolbarStyle = switch (self.config.gtk_toolbar_style) {
|
||||
.flat => c.ADW_TOOLBAR_FLAT,
|
||||
.raised => c.ADW_TOOLBAR_RAISED,
|
||||
.@"raised-border" => c.ADW_TOOLBAR_RAISED_BORDER,
|
||||
@ -298,12 +319,12 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
@ptrCast(@alignCast(self.tab_overview)),
|
||||
);
|
||||
} else tab_bar: {
|
||||
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 => c.gtk_box_insert_child_after(
|
||||
@ptrCast(box),
|
||||
@ptrCast(@alignCast(tab_bar)),
|
||||
@ -317,9 +338,15 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
}
|
||||
c.adw_tab_bar_set_view(tab_bar, @ptrCast(@alignCast(self.notebook.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);
|
||||
}
|
||||
|
||||
// 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(gtk_widget);
|
||||
}
|
||||
@ -328,22 +355,80 @@ pub fn updateConfig(
|
||||
self: *Window,
|
||||
config: *const configpkg.Config,
|
||||
) !void {
|
||||
self.winproto.updateConfigEvent(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});
|
||||
};
|
||||
self.config = DerivedConfig.init(config);
|
||||
|
||||
// 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 {
|
||||
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.config.gtk_titlebar_hide_when_maximized)
|
||||
break :visible false;
|
||||
|
||||
break :visible self.config.gtk_titlebar;
|
||||
});
|
||||
|
||||
toggleCssClass(
|
||||
@ptrCast(self.window),
|
||||
"background",
|
||||
self.config.background_opacity >= 1,
|
||||
);
|
||||
|
||||
// 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 (self.tab_overview) |tab_overview| {
|
||||
if (!adwaita.versionAtLeast(1, 4, 0)) unreachable;
|
||||
|
||||
// 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(@ptrCast(tab_overview), @intFromBool(csd_enabled));
|
||||
c.adw_tab_overview_set_show_end_title_buttons(@ptrCast(tab_overview), @intFromBool(csd_enabled));
|
||||
|
||||
// Update toolbar view style
|
||||
const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_tab_overview_get_child(@ptrCast(tab_overview)));
|
||||
const toolbar_style: c.AdwToolbarStyle = switch (self.config.gtk_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});
|
||||
};
|
||||
@ -351,30 +436,8 @@ pub fn syncAppearance(self: *Window, config: *const configpkg.Config) !void {
|
||||
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));
|
||||
|
||||
// If we are not decorated then we hide the titlebar.
|
||||
self.headerbar.setVisible(config.@"gtk-titlebar" and csd_enabled);
|
||||
|
||||
// Disable the title buttons (close, maximize, minimize, ...)
|
||||
// *inside* the tab overview if CSDs are disabled.
|
||||
// We do spare the search button, though.
|
||||
if (self.tab_overview) |tab_overview| {
|
||||
if (!adwaita.versionAtLeast(1, 4, 0)) unreachable;
|
||||
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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn toggleCssClass(
|
||||
@ -518,30 +581,42 @@ 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 |err| {
|
||||
log.err("failed to sync appearance={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
/// Grabs focus on the currently selected tab.
|
||||
@ -562,23 +637,22 @@ pub fn sendToast(self: *Window, title: [:0]const u8) void {
|
||||
c.adw_toast_overlay_add_toast(@ptrCast(self.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});
|
||||
};
|
||||
|
||||
@ -591,61 +665,22 @@ 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), "csd", is_decorated);
|
||||
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 |err| {
|
||||
log.err("failed to sync appearance={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
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 |err| {
|
||||
log.err("failed to sync appearance={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
// Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -111,15 +110,6 @@ pub const Window = union(Protocol) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateConfigEvent(
|
||||
self: *Window,
|
||||
config: *const Config,
|
||||
) !void {
|
||||
switch (self.*) {
|
||||
inline else => |*v| try v.updateConfigEvent(config),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn syncAppearance(self: *Window) !void {
|
||||
switch (self.*) {
|
||||
inline else => |*v| try v.syncAppearance(),
|
||||
|
@ -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 {}
|
||||
|
@ -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;
|
||||
@ -155,7 +156,7 @@ pub const App = struct {
|
||||
|
||||
/// Per-window (wl_surface) state for the Wayland protocol.
|
||||
pub const Window = struct {
|
||||
config: DerivedConfig,
|
||||
config: *const ApprtWindow.DerivedConfig,
|
||||
|
||||
/// The Wayland surface for this window.
|
||||
surface: *wl.Surface,
|
||||
@ -170,28 +171,15 @@ pub const Window = struct {
|
||||
/// of the window.
|
||||
decoration: ?*org.KdeKwinServerDecoration,
|
||||
|
||||
const DerivedConfig = struct {
|
||||
blur: bool,
|
||||
window_decoration: Config.WindowDecoration,
|
||||
|
||||
pub fn init(config: *const Config) DerivedConfig {
|
||||
return .{
|
||||
.blur = config.@"background-blur".enabled(),
|
||||
.window_decoration = config.@"window-decoration",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
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 +210,7 @@ pub const Window = struct {
|
||||
};
|
||||
|
||||
return .{
|
||||
.config = DerivedConfig.init(config),
|
||||
.config = &apprt_window.config,
|
||||
.surface = wl_surface,
|
||||
.app_context = app.context,
|
||||
.blur_token = null,
|
||||
@ -236,18 +224,15 @@ pub const Window = struct {
|
||||
if (self.decoration) |deco| deco.release();
|
||||
}
|
||||
|
||||
pub fn updateConfigEvent(
|
||||
self: *Window,
|
||||
config: *const Config,
|
||||
) !void {
|
||||
self.config = DerivedConfig.init(config);
|
||||
}
|
||||
|
||||
pub fn resizeEvent(_: *Window) !void {}
|
||||
|
||||
pub fn syncAppearance(self: *Window) !void {
|
||||
try self.syncBlur();
|
||||
try self.syncDecoration();
|
||||
self.syncBlur() catch |err| {
|
||||
log.err("failed to sync blur={}", .{err});
|
||||
};
|
||||
self.syncDecoration() catch |err| {
|
||||
log.err("failed to sync blur={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
pub fn clientSideDecorationEnabled(self: Window) bool {
|
||||
@ -270,18 +255,18 @@ pub const Window = struct {
|
||||
/// Update the blur state of the window.
|
||||
fn syncBlur(self: *Window) !void {
|
||||
const manager = self.app_context.kde_blur_manager orelse return;
|
||||
const blur = self.config.blur;
|
||||
const blur = self.config.background_blur;
|
||||
|
||||
if (self.blur_token) |tok| {
|
||||
// Only release token when transitioning from blurred -> not blurred
|
||||
if (!blur) {
|
||||
if (!blur.enabled()) {
|
||||
manager.unset(self.surface);
|
||||
tok.release();
|
||||
self.blur_token = null;
|
||||
}
|
||||
} else {
|
||||
// Only acquire token when transitioning from not blurred -> blurred
|
||||
if (blur) {
|
||||
if (blur.enabled()) {
|
||||
const tok = try manager.create(self.surface);
|
||||
tok.commit();
|
||||
self.blur_token = tok;
|
||||
|
@ -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,33 +152,21 @@ pub const App = struct {
|
||||
|
||||
pub const Window = struct {
|
||||
app: *App,
|
||||
alloc: Allocator,
|
||||
config: DerivedConfig,
|
||||
config: *const ApprtWindow.DerivedConfig,
|
||||
window: c.Window,
|
||||
gtk_window: *c.GtkWindow,
|
||||
|
||||
blur_region: Region = .{},
|
||||
|
||||
const DerivedConfig = struct {
|
||||
blur: bool,
|
||||
window_decoration: Config.WindowDecoration,
|
||||
|
||||
pub fn init(config: *const Config) DerivedConfig {
|
||||
return .{
|
||||
.blur = config.@"background-blur".enabled(),
|
||||
.window_decoration = config.@"window-decoration",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
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 +177,9 @@ pub const Window = struct {
|
||||
|
||||
return .{
|
||||
.app = app,
|
||||
.alloc = alloc,
|
||||
.config = DerivedConfig.init(config),
|
||||
.config = &apprt_window.config,
|
||||
.window = c.gdk_x11_surface_get_xid(surface),
|
||||
.gtk_window = gtk_window,
|
||||
.gtk_window = apprt_window.window,
|
||||
};
|
||||
}
|
||||
|
||||
@ -200,13 +188,6 @@ pub const Window = struct {
|
||||
_ = alloc;
|
||||
}
|
||||
|
||||
pub fn updateConfigEvent(
|
||||
self: *Window,
|
||||
config: *const Config,
|
||||
) !void {
|
||||
self.config = DerivedConfig.init(config);
|
||||
}
|
||||
|
||||
pub fn resizeEvent(self: *Window) !void {
|
||||
// The blur region must update with window resizes
|
||||
self.blur_region.width = c.gtk_widget_get_width(@ptrCast(self.gtk_window));
|
||||
@ -257,14 +238,14 @@ pub const Window = struct {
|
||||
// and I think it's not really noticeable enough to justify the effort.
|
||||
// (Wayland also has this visual artifact anyway...)
|
||||
|
||||
const blur = self.config.blur;
|
||||
const blur = self.config.background_blur;
|
||||
log.debug("set blur={}, window xid={}, region={}", .{
|
||||
blur,
|
||||
self.window,
|
||||
self.blur_region,
|
||||
});
|
||||
|
||||
if (blur) {
|
||||
if (blur.enabled()) {
|
||||
try self.changeProperty(
|
||||
Region,
|
||||
self.app.atoms.kde_blur,
|
||||
|
Reference in New Issue
Block a user