diff --git a/build.zig.zon b/build.zig.zon index 7a669a4a1..b0b66a052 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -91,8 +91,8 @@ .lazy = true, }, .wayland_protocols = .{ - .url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz", - .hash = "N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S", + .url = "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz", + .hash = "N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA", .lazy = true, }, .plasma_wayland_protocols = .{ diff --git a/build.zig.zon.json b/build.zig.zon.json index 4a88e2017..b4e9dc39c 100644 --- a/build.zig.zon.json +++ b/build.zig.zon.json @@ -139,6 +139,11 @@ "url": "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz", "hash": "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=" }, + "N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA": { + "name": "wayland_protocols", + "url": "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz", + "hash": "sha256-3S3xSrX0EDgleq7cxLX7msDuAY8/D5SvkJcCjmDTMiM=" + }, "N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs": { "name": "wuffs", "url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz", diff --git a/build.zig.zon.nix b/build.zig.zon.nix index 53e1b6c02..a3f18c692 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -306,6 +306,14 @@ in hash = "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg="; }; } + { + name = "N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA"; + path = fetchZigArtifact { + name = "wayland_protocols"; + url = "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz"; + hash = "sha256-3S3xSrX0EDgleq7cxLX7msDuAY8/D5SvkJcCjmDTMiM="; + }; + } { name = "N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs"; path = fetchZigArtifact { diff --git a/build.zig.zon.txt b/build.zig.zon.txt index 4ac9e6592..75585dcb8 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -34,3 +34,4 @@ https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz https://github.com/ocornut/imgui/archive/refs/tags/v1.92.5-docking.tar.gz +https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz diff --git a/flatpak/zig-packages.json b/flatpak/zig-packages.json index e58ecd448..b971163b4 100644 --- a/flatpak/zig-packages.json +++ b/flatpak/zig-packages.json @@ -167,6 +167,12 @@ "dest": "vendor/p/N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S", "sha256": "5cedcadde81b75e60f23e5e83b5dd2b8eb4efb9f8f79bd7a347d148aeb0530f8" }, + { + "type": "archive", + "url": "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz", + "dest": "vendor/p/N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA", + "sha256": "dd2df14ab5f41038257aaedcc4b5fb9ac0ee018f3f0f94af9097028e60d33223" + }, { "type": "archive", "url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz", diff --git a/src/apprt/gtk/class/window.zig b/src/apprt/gtk/class/window.zig index c01cad618..8cef7b765 100644 --- a/src/apprt/gtk/class/window.zig +++ b/src/apprt/gtk/class/window.zig @@ -1071,21 +1071,6 @@ pub const Window = extern struct { self.syncAppearance(); } - fn propGdkSurfaceHeight( - _: *gdk.Surface, - _: *gobject.ParamSpec, - self: *Self, - ) callconv(.c) void { - // X11 needs to fix blurring on resize, but winproto implementations - // could do anything. - self.private().winproto.resizeEvent() catch |err| { - log.warn( - "winproto resize event failed error={}", - .{err}, - ); - }; - } - fn propIsActive( _: *gtk.Window, _: *gobject.ParamSpec, @@ -1111,7 +1096,7 @@ pub const Window = extern struct { }; } - fn propGdkSurfaceWidth( + fn propGdkSurfaceDims( _: *gdk.Surface, _: *gobject.ParamSpec, self: *Self, @@ -1282,14 +1267,14 @@ pub const Window = extern struct { _ = gobject.Object.signals.notify.connect( gdk_surface, *Self, - propGdkSurfaceWidth, + propGdkSurfaceDims, self, .{ .detail = "width" }, ); _ = gobject.Object.signals.notify.connect( gdk_surface, *Self, - propGdkSurfaceHeight, + propGdkSurfaceDims, self, .{ .detail = "height" }, ); diff --git a/src/apprt/gtk/winproto/wayland.zig b/src/apprt/gtk/winproto/wayland.zig index b90e9d115..b7207e545 100644 --- a/src/apprt/gtk/winproto/wayland.zig +++ b/src/apprt/gtk/winproto/wayland.zig @@ -10,6 +10,7 @@ const layer_shell = @import("gtk4-layer-shell"); const wayland = @import("wayland"); const wl = wayland.client.wl; +const ext = wayland.client.ext; const kde = wayland.client.kde; const org = wayland.client.org; const xdg = wayland.client.xdg; @@ -96,8 +97,8 @@ pub const Window = struct { /// The context from the app where we can load our Wayland interfaces. globals: *Globals, - /// A token that, when present, indicates that the window is blurred. - blur_token: ?*org.KdeKwinBlur = null, + /// Object that controls background effects like background blur. + bg_effect: ?*ext.BackgroundEffectSurfaceV1 = null, /// Object that controls the decoration mode (client/server/auto) /// of the window. @@ -148,6 +149,20 @@ pub const Window = struct { break :deco deco; }; + const bg_effect: ?*ext.BackgroundEffectSurfaceV1 = bg: { + const mgr = app.globals.get(.ext_background_effect) orelse + break :bg null; + + const bg_effect: *ext.BackgroundEffectSurfaceV1 = mgr.getBackgroundEffect( + wl_surface, + ) catch |err| { + log.warn("could not create background effect object={}", .{err}); + break :bg null; + }; + + break :bg bg_effect; + }; + if (apprt_window.isQuickTerminal()) { _ = gdk.Surface.signals.enter_monitor.connect( gdk_surface, @@ -163,17 +178,22 @@ pub const Window = struct { .surface = wl_surface, .globals = app.globals, .decoration = deco, + .bg_effect = bg_effect, }; } pub fn deinit(self: Window, alloc: Allocator) void { _ = alloc; - if (self.blur_token) |blur| blur.release(); + if (self.bg_effect) |bg| bg.destroy(); if (self.decoration) |deco| deco.release(); if (self.slide) |slide| slide.release(); } - pub fn resizeEvent(_: *Window) !void {} + pub fn resizeEvent(self: *Window) !void { + self.syncBlur() catch |err| { + log.err("failed to sync blur={}", .{err}); + }; + } pub fn syncAppearance(self: *Window) !void { self.syncBlur() catch |err| { @@ -224,28 +244,53 @@ pub const Window = struct { /// Update the blur state of the window. fn syncBlur(self: *Window) !void { - const manager = self.globals.get(.kde_blur_manager) orelse return; + const compositor = self.globals.get(.compositor) orelse return; + const bg = self.bg_effect orelse return; + if (!self.globals.state.bg_effect_capabilities.blur) return; + const config = if (self.apprt_window.getConfig()) |v| v.get() else return; const blur = config.@"background-blur"; - if (self.blur_token) |tok| { - // Only release token when transitioning from blurred -> not blurred - 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.enabled()) { - const tok = try manager.create(self.surface); - tok.commit(); - self.blur_token = tok; - } - } + const region = region: { + if (!blur.enabled()) break :region null; + + // 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. + + const native = self.apprt_window.as(gtk.Native); + const surface = native.getSurface() orelse break :region null; + const region = try compositor.createRegion(); + + var x: f64 = 0; + var y: f64 = 0; + native.getSurfaceTransform(&x, &y); + // Slightly inset the blur region + x += 1; + y += 1; + + var width: f64 = @floatFromInt(surface.getWidth()); + var height: f64 = @floatFromInt(surface.getHeight()); + width -= x * 2; + height -= y * 2; + if (width <= 0 or height <= 0) break :region null; + + // FIXME: Add rounded corners + region.add( + @intFromFloat(x), + @intFromFloat(y), + @intFromFloat(width), + @intFromFloat(height), + ); + break :region region; + }; + errdefer if (region) |r| r.destroy(); + + bg.setBlurRegion(region); } fn syncDecoration(self: *Window) !void { diff --git a/src/apprt/gtk/winproto/wayland/Globals.zig b/src/apprt/gtk/winproto/wayland/Globals.zig index 369d6a473..83052cbeb 100644 --- a/src/apprt/gtk/winproto/wayland/Globals.zig +++ b/src/apprt/gtk/winproto/wayland/Globals.zig @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; const wayland = @import("wayland"); const wl = wayland.client.wl; +const ext = wayland.client.ext; const kde = wayland.client.kde; const org = wayland.client.org; const xdg = wayland.client.xdg; @@ -26,7 +27,8 @@ const Binding = struct { }; pub const Tag = enum { - kde_blur_manager, + compositor, + ext_background_effect, kde_decoration_manager, kde_slide_manager, kde_output_order, @@ -34,7 +36,8 @@ pub const Tag = enum { fn Type(comptime self: Tag) type { return switch (self) { - .kde_blur_manager => org.KdeKwinBlurManager, + .compositor => wl.Compositor, + .ext_background_effect => ext.BackgroundEffectManagerV1, .kde_decoration_manager => org.KdeKwinServerDecorationManager, .kde_slide_manager => org.KdeKwinSlideManager, .kde_output_order => kde.OutputOrderV1, @@ -56,6 +59,8 @@ pub const State = struct { default_deco_mode: ?org.KdeKwinServerDecorationManager.Mode = null, + bg_effect_capabilities: ext.BackgroundEffectManagerV1.Capability = .{}, + /// Reset cached state derived from kde_output_order_v1. fn resetOutputOrder(self: *State, alloc: Allocator) void { if (self.primary_output_name) |name| alloc.free(name); @@ -102,6 +107,11 @@ fn onGlobalAttached(self: *Globals, comptime tag: Tag) void { // keeps listener setup and object lifetime in one // place and also supports globals that appear later. switch (tag) { + .ext_background_effect => { + const v = self.get(tag) orelse return; + v.setListener(*Globals, bgEffectListener, self); + self.needs_roundtrip = true; + }, .kde_decoration_manager => { const v = self.get(tag) orelse return; v.setListener(*Globals, decoManagerListener, self); @@ -179,6 +189,18 @@ fn registryListener( } } +fn bgEffectListener( + _: *ext.BackgroundEffectManagerV1, + event: ext.BackgroundEffectManagerV1.Event, + self: *Globals, +) void { + switch (event) { + .capabilities => |cap| { + self.state.bg_effect_capabilities = cap.flags; + }, + } +} + fn decoManagerListener( _: *org.KdeKwinServerDecorationManager, event: org.KdeKwinServerDecorationManager.Event, diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index 4b1ea936d..9dc4162ae 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -626,9 +626,6 @@ fn addGtkNg( .wayland_protocols = wayland_protocols_dep.path(""), }); - scanner.addCustomProtocol( - plasma_wayland_protocols_dep.path("src/protocols/blur.xml"), - ); // FIXME: replace with `zxdg_decoration_v1` once GTK merges https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6398 scanner.addCustomProtocol( plasma_wayland_protocols_dep.path("src/protocols/server-decoration.xml"), @@ -640,13 +637,14 @@ fn addGtkNg( plasma_wayland_protocols_dep.path("src/protocols/kde-output-order-v1.xml"), ); scanner.addSystemProtocol("staging/xdg-activation/xdg-activation-v1.xml"); + scanner.addSystemProtocol("staging/ext-background-effect/ext-background-effect-v1.xml"); scanner.generate("wl_compositor", 1); - scanner.generate("org_kde_kwin_blur_manager", 1); scanner.generate("org_kde_kwin_server_decoration_manager", 1); scanner.generate("org_kde_kwin_slide_manager", 1); scanner.generate("kde_output_order_v1", 1); scanner.generate("xdg_activation_v1", 1); + scanner.generate("ext_background_effect_manager_v1", 1); step.root_module.addImport("wayland", b.createModule(.{ .root_source_file = scanner.result,