gtk(x11): fix blur regions when using >200% scaling (#6978)

See #6957

We were not considering GTK's internal scale factor that converts
between "surface coordinates" and actual device coordinates, and that
worked fine until the scale factor reached 2x (200%).

Since the code is now dependent on the scale factor (which could change
at any given moment), we also listen to scale factor changes and then
unconditionally call `winproto.syncAppearance`. Even though it's
somewhat overkill, I don't expect people to change their scale factor
dramatically all the time anyway...
This commit is contained in:
Leah Amelia Chen
2025-04-02 17:38:27 +02:00
committed by GitHub
2 changed files with 41 additions and 3 deletions

View File

@ -281,6 +281,15 @@ pub fn init(self: *Window, app: *App) !void {
.detail = "is-active",
},
);
_ = gobject.Object.signals.notify.connect(
self.window,
*Window,
gtkWindowUpdateScaleFactor,
self,
.{
.detail = "scale-factor",
},
);
// 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.
@ -784,6 +793,24 @@ fn gtkWindowNotifyIsActive(
}
}
fn gtkWindowUpdateScaleFactor(
_: *adw.ApplicationWindow,
_: *gobject.ParamSpec,
self: *Window,
) callconv(.c) void {
// On some platforms (namely X11) we need to refresh our appearance when
// the scale factor changes. In theory this could be more fine-grained as
// a full refresh could be expensive, but a) this *should* be rare, and
// b) quite noticeable visual bugs would occur if this is not present.
self.winproto.syncAppearance() catch |err| {
log.err(
"failed to sync appearance after scale factor has been updated={}",
.{err},
);
return;
};
}
// Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab
// sends an undefined value.
fn gtkTabNewClick(_: *gtk.Button, self: *Window) callconv(.c) void {

View File

@ -219,13 +219,12 @@ pub const Window = struct {
pub fn resizeEvent(self: *Window) !void {
// The blur region must update with window resizes
const gtk_widget = self.gtk_window.as(gtk.Widget);
self.blur_region.width = gtk_widget.getWidth();
self.blur_region.height = gtk_widget.getHeight();
try self.syncBlur();
}
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
@ -236,6 +235,11 @@ pub const Window = struct {
self.gtk_window.as(gtk.Native).getSurfaceTransform(&x, &y);
// Transform surface coordinates to device coordinates.
const scale: f64 = @floatFromInt(self.gtk_window.as(gtk.Widget).getScaleFactor());
x *= scale;
y *= scale;
break :blur .{
.x = @intFromFloat(x),
.y = @intFromFloat(y),
@ -265,6 +269,13 @@ 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 gtk_widget = self.gtk_window.as(gtk.Widget);
// Transform surface coordinates to device coordinates.
const scale = self.gtk_window.as(gtk.Widget).getScaleFactor();
self.blur_region.width = gtk_widget.getWidth() * scale;
self.blur_region.height = gtk_widget.getHeight() * scale;
const blur = self.config.background_blur;
log.debug("set blur={}, window xid={}, region={}", .{
blur,