Skip to content

Commit 5f637d2

Browse files
committed
gtk/winproto: calculate rounded corners for blur
1 parent a9f4c28 commit 5f637d2

File tree

2 files changed

+91
-37
lines changed

2 files changed

+91
-37
lines changed

src/apprt/gtk/winproto.zig

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,7 @@ pub const Window = struct {
158158
if (!blur_config.enabled()) {
159159
self.blur_region.clear();
160160
} else {
161-
var new_region: blur.Region = try .calcForWindow(
162-
self.alloc,
163-
self.apprt_window.as(gtk.Window),
164-
);
161+
var new_region: blur.Region = try .calcForWindow(self);
165162
errdefer new_region.deinit(self.alloc);
166163

167164
if (!new_region.eql(self.blur_region)) {

src/apprt/gtk/winproto/blur.zig

Lines changed: 90 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@ const std = @import("std");
33
const Allocator = std.mem.Allocator;
44

55
const gtk = @import("gtk");
6+
const Window = @import("../winproto.zig").Window;
67

78
pub const Region = struct {
89
slices: std.ArrayList(Slice),
910

1011
/// A rectangular slice of the blur region.
1112
// Marked `extern` since we want to be able to use this in X11 directly,
12-
// and we use `c_long`s as, while XLib *says* they should be 32 bit integers,
13-
// in actuality they are architecture-dependent. I love legacy cruft
1413
pub const Slice = extern struct {
15-
x: c_long,
16-
y: c_long,
17-
width: c_long,
18-
height: c_long,
14+
x: Pos,
15+
y: Pos,
16+
width: Pos,
17+
height: Pos,
1918
};
2019

20+
// X11 compatibility. Ideally this should just be an `i32` like Wayland,
21+
// but XLib sucks
22+
const Pos = c_long;
23+
2124
pub const empty: Region = .{
2225
.slices = .empty,
2326
};
@@ -30,48 +33,45 @@ pub const Region = struct {
3033
self.slices.deinit(alloc);
3134
}
3235

33-
// Calculate the blur regions for a window.
36+
// Calculate the blur region for a window.
3437
//
3538
// Since we have rounded corners by default, we need to carve out the
3639
// pixels on each corner to avoid the "korners bug".
37-
// (cf. https://github.com/cutefishos/fishui/blob/41d4ba194063a3c7fff4675619b57e6ac0504f06/src/platforms/linux/blurhelper/windowblur.cpp#L134)
38-
pub fn calcForWindow(alloc: Allocator, window: *gtk.Window) Allocator.Error!Region {
39-
const native = window.as(gtk.Native);
40+
pub fn calcForWindow(window: *Window) Allocator.Error!Region {
41+
const native = window.apprt_window.as(gtk.Native);
4042
const surface = native.getSurface() orelse return .empty;
4143

42-
var slices: std.ArrayList(Slice) = .empty;
43-
errdefer slices.deinit(alloc);
44-
45-
// Calculate the primary blur region
46-
// (the one that covers most of the screen).
47-
// It's easier to do this inside a vector since we have to scale
48-
// everything by the scale factor anyways.
49-
5044
// NOTE(pluiedev): CSDs are a f--king mistake.
5145
// Please, GNOME, stop this nonsense of making a window ~30% bigger
5246
// internally than how they really are just for your shadows and
5347
// rounded corners and all that fluff. Please. I beg of you.
54-
var x: f64 = 0;
55-
var y: f64 = 0;
56-
native.getSurfaceTransform(&x, &y);
48+
const x: Pos, const y: Pos = off: {
49+
var x: f64 = 0;
50+
var y: f64 = 0;
51+
native.getSurfaceTransform(&x, &y);
52+
break :off .{ @intFromFloat(x), @intFromFloat(y) };
53+
};
5754

58-
var width: f64 = @floatFromInt(surface.getWidth());
59-
var height: f64 = @floatFromInt(surface.getHeight());
55+
var width = @as(Pos, surface.getWidth());
56+
var height = @as(Pos, surface.getHeight());
6057

6158
// Trim off the offsets. Be careful not to get negative.
62-
width = @max(0, width - x * 2);
63-
height = @max(0, height - y * 2);
59+
width -= x * 2;
60+
height -= y * 2;
61+
if (width <= 0 or height <= 0) return .empty;
6462

65-
// TODO: Add more regions to mitigate the "korners bug".
66-
try slices.append(alloc, .{
67-
.x = @intFromFloat(x),
68-
.y = @intFromFloat(y),
69-
.width = @intFromFloat(width),
70-
.height = @intFromFloat(height),
71-
});
63+
// Empirically determined.
64+
const radius: Pos = if (window.clientSideDecorationEnabled()) 12 else 0;
7265

7366
return .{
74-
.slices = slices,
67+
.slices = try approxRoundedRect(
68+
window.alloc,
69+
x,
70+
y,
71+
width,
72+
height,
73+
radius,
74+
),
7575
};
7676
}
7777

@@ -83,4 +83,61 @@ pub const Region = struct {
8383
}
8484
return true;
8585
}
86+
87+
/// Approximate a rounded rectangle with many smaller rectangles.
88+
fn approxRoundedRect(
89+
alloc: Allocator,
90+
x: Pos,
91+
y: Pos,
92+
width: Pos,
93+
height: Pos,
94+
radius: Pos,
95+
) Allocator.Error!std.ArrayList(Slice) {
96+
const r_f: f32 = @floatFromInt(radius);
97+
98+
var slices: std.ArrayList(Slice) = .empty;
99+
errdefer slices.deinit(alloc);
100+
101+
// Add the central rectangle
102+
try slices.append(alloc, .{
103+
.x = x,
104+
.y = y + radius,
105+
.width = width,
106+
.height = height - 2 * radius,
107+
});
108+
109+
// Add the corner rows. This is honestly quite cursed.
110+
var row: Pos = 0;
111+
while (row < radius) : (row += 1) {
112+
// y distance from this row to the center corner circle
113+
const dy = @as(f32, @floatFromInt(radius - row)) - 0.5;
114+
115+
// x distance - as given by the definition of a circle
116+
const dx = @sqrt(r_f * r_f - dy * dy);
117+
118+
// How much each row should be offset, rounded to an integer
119+
const row_x: Pos = @intFromFloat(r_f - dx + 0.5);
120+
121+
// Remove the offset from both ends
122+
const row_w = width - 2 * row_x;
123+
124+
// Top slice
125+
try slices.append(alloc, .{
126+
.x = x + row_x,
127+
.y = y + row,
128+
.width = row_w,
129+
.height = 1,
130+
});
131+
132+
// Bottom slice
133+
try slices.append(alloc, .{
134+
.x = x + row_x,
135+
.y = y + height - 1 - row,
136+
.width = row_w,
137+
.height = 1,
138+
});
139+
}
140+
141+
return slices;
142+
}
86143
};

0 commit comments

Comments
 (0)