diff --git a/src/apprt/gtk/winproto/x11.zig b/src/apprt/gtk/winproto/x11.zig index 675434fd1..92ecf2700 100644 --- a/src/apprt/gtk/winproto/x11.zig +++ b/src/apprt/gtk/winproto/x11.zig @@ -19,6 +19,7 @@ pub const c = @cImport({ const input = @import("../../../input.zig"); const Config = @import("../../../config.zig").Config; const ApprtWindow = @import("../class/window.zig").Window; +const BlurRegion = @import("BlurRegion.zig"); const log = std.log.scoped(.gtk_x11); @@ -169,13 +170,13 @@ pub const Window = struct { app: *App, apprt_window: *ApprtWindow, x11_surface: *gdk_x11.X11Surface, + alloc: Allocator, - blur_region: Region = .{}, + blur_region: BlurRegion = .empty, // Cache last applied values to avoid redundant X11 property updates. // Redundant property updates seem to cause some visual glitches // with some window managers: https://github.com/ghostty-org/ghostty/pull/8075 - last_applied_blur_region: ?Region = null, last_applied_decoration_hints: ?MotifWMHints = null, pub fn init( @@ -183,8 +184,6 @@ pub const Window = struct { app: *App, apprt_window: *ApprtWindow, ) !Window { - _ = alloc; - const surface = apprt_window.as(gtk.Native).getSurface() orelse return error.NotX11Surface; @@ -195,6 +194,7 @@ pub const Window = struct { return .{ .app = app, + .alloc = alloc, .apprt_window = apprt_window, .x11_surface = x11_surface, }; @@ -213,26 +213,6 @@ pub const Window = struct { pub fn syncAppearance(self: *Window) !void { // The user could have toggled between CSDs and SSDs, // therefore we need to recalculate the blur region offset. - self.blur_region = blur: { - // NOTE(pluiedev): CSDs are a f--king mistake. - // Please, GNOME, stop this nonsense of making a window ~30% bigger - // internally than how they really are just for your shadows and - // rounded corners and all that fluff. Please. I beg of you. - var x: f64 = 0; - var y: f64 = 0; - - self.apprt_window.as(gtk.Native).getSurfaceTransform(&x, &y); - - // Transform surface coordinates to device coordinates. - const scale: f64 = @floatFromInt(self.apprt_window.as(gtk.Widget).getScaleFactor()); - x *= scale; - y *= scale; - - break :blur .{ - .x = @intFromFloat(x), - .y = @intFromFloat(y), - }; - }; self.syncBlur() catch |err| { log.err("failed to synchronize blur={}", .{err}); }; @@ -249,36 +229,29 @@ pub const Window = struct { } fn syncBlur(self: *Window) !void { - // FIXME: This doesn't currently factor in rounded corners on Adwaita, - // which means that the blur region will grow slightly outside of the - // window borders. Unfortunately, actually calculating the rounded - // region can be quite complex without having access to existing APIs - // (cf. https://github.com/cutefishos/fishui/blob/41d4ba194063a3c7fff4675619b57e6ac0504f06/src/platforms/linux/blurhelper/windowblur.cpp#L134) - // and I think it's not really noticeable enough to justify the effort. - // (Wayland also has this visual artifact anyway...) - - const gtk_widget = self.apprt_window.as(gtk.Widget); const config = if (self.apprt_window.getConfig()) |v| v.get() else return; // When blur is disabled, remove the property if it was previously set const blur = config.@"background-blur"; if (!blur.enabled()) { - if (self.last_applied_blur_region != null) { - try self.deleteProperty(self.app.atoms.kde_blur); - self.last_applied_blur_region = null; - } - + self.blur_region.deinit(self.alloc); + self.blur_region = .empty; + try self.deleteProperty(self.app.atoms.kde_blur); return; } - // Transform surface coordinates to device coordinates. - const scale = gtk_widget.getScaleFactor(); - self.blur_region.width = gtk_widget.getWidth() * scale; - self.blur_region.height = gtk_widget.getHeight() * scale; + var region: BlurRegion = try .calcForWindow( + self.alloc, + self.apprt_window, + self.clientSideDecorationEnabled(), + true, + ); + errdefer region.deinit(self.alloc); // Only update X11 properties when the blur region actually changes - if (self.last_applied_blur_region) |last| { - if (std.meta.eql(self.blur_region, last)) return; + if (region.eql(self.blur_region)) { + region.deinit(self.alloc); + return; } log.debug("set blur={}, window xid={}, region={}", .{ @@ -288,14 +261,14 @@ pub const Window = struct { }); try self.changeProperty( - Region, + BlurRegion.Slice, self.app.atoms.kde_blur, c.XA_CARDINAL, ._32, .{ .mode = .replace }, - &self.blur_region, + self.blur_region.slices.items, ); - self.last_applied_blur_region = self.blur_region; + self.blur_region = region; } fn syncDecorations(self: *Window) !void { @@ -335,7 +308,7 @@ pub const Window = struct { self.app.atoms.motif_wm_hints, ._32, .{ .mode = .replace }, - &hints, + &.{hints}, ); self.last_applied_decoration_hints = hints; } @@ -410,9 +383,11 @@ pub const Window = struct { options: struct { mode: PropertyChangeMode, }, - value: *T, + values: []const T, ) X11Error!void { - const data: format.bufferType() = @ptrCast(value); + const data: format.bufferType() = @ptrCast(@constCast(values)); + // The number of "words" that each element `T` occupies. + const words_per_elem = @divExact(@sizeOf(T), @sizeOf(format.elemType())); const status = c.XChangeProperty( @ptrCast(@alignCast(self.app.display)), @@ -422,7 +397,7 @@ pub const Window = struct { @intFromEnum(format), @intFromEnum(options.mode), data, - @divExact(@sizeOf(T), @sizeOf(format.elemType())), + @intCast(words_per_elem * values.len), ); // For some godforsaken reason Xlib alternates between @@ -498,13 +473,6 @@ const PropertyFormat = enum(c_int) { } }; -const Region = extern struct { - x: c_long = 0, - y: c_long = 0, - width: c_long = 0, - height: c_long = 0, -}; - // See Xm/MwmUtil.h, packaged with the Motif Window Manager const MotifWMHints = extern struct { flags: packed struct(c_ulong) {