mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
gtk/wayland: replace KDE blur with ext-background-effect-v1
The venerable KDE blur protocol has been replaced with the compositor- agnostic ext-background-effect-v1 protocol, to be implemented by Niri and others. The new protocol is much easier to use overall, though we do need to calculate the blur region manually like X11.
This commit is contained in:
@@ -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 = .{
|
||||
|
||||
5
build.zig.zon.json
generated
5
build.zig.zon.json
generated
@@ -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",
|
||||
|
||||
8
build.zig.zon.nix
generated
8
build.zig.zon.nix
generated
@@ -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 {
|
||||
|
||||
1
build.zig.zon.txt
generated
1
build.zig.zon.txt
generated
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" },
|
||||
);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user