Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix: blur transparency mask samples 1:1 over the blur box
The standard blur node stretched its transparency-mask texture (the
window surface, via ignore_transparent) across the blur rect. A mask
buffer LARGER than the blur box — a client overflow rim, where the
surface extends transparently past the xdg geometry the blur is sized
to — squeezed its transparent rim INTO the box, and ignore_transparent
then killed the backdrop blur in a rim-proportioned band at the
window right/bottom: the data-editor backplate visibly lost its frost
along the bottom/right exactly while a File dropdown (and its overflow
rim) was expanded. Diagnosed at low diff thresholds — through an 81%-
opaque plate the sharp-vs-blurred backdrop difference is under 20/255,
invisible to coarse pixel diffs.
The mask src_box now maps the blur box through the mask buffer own
geometry (1:1 in layout space, using its node coords + dest size);
congruent boxes — every mask before overflow rims existed — skip the
remap and keep the previous full-texture mapping byte-for-byte.
Verified nested A/B with a rim-forcing File menu over a grid gap line:
old binary loses the frost across the bottom band (63% of band pixels
change, the gap line pierces the plate sharply); fixed binary shows
only the expected app-state diffs. build.rs now watches the scenefx
sources — editing them previously shipped a stale static lib.
Co-Authored-By: Claude Fable 5 <[email protected]>
build.rs | 6 ++++++
scenefx/types/scene/wlr_scene.c | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/build.rs b/build.rs
index 09eadd4..3280521 100644
--- a/build.rs
+++ b/build.rs
@@ -4,6 +4,12 @@ use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=src/server/wlroots_log_wrapper.c");
println!("cargo:rerun-if-changed=wrapper.h");
+ // Vendored scenefx sources: without these, editing a scenefx .c/.h
+ // silently ships a stale static lib (meson only reruns when build.rs
+ // does; meson compile is a fast no-op when nothing changed).
+ println!("cargo:rerun-if-changed=scenefx/types");
+ println!("cargo:rerun-if-changed=scenefx/render");
+ println!("cargo:rerun-if-changed=scenefx/include");
println!("cargo:rerun-if-changed=protocol/river-xkb-bindings-v1.xml");
println!("cargo:rerun-if-changed=protocol/river-layer-shell-v1.xml");
println!("cargo:rerun-if-changed=protocol/river-input-management-v1.xml");
diff --git a/scenefx/types/scene/wlr_scene.c b/scenefx/types/scene/wlr_scene.c
index 6aed74d..fc0f487 100644
--- a/scenefx/types/scene/wlr_scene.c
+++ b/scenefx/types/scene/wlr_scene.c
@@ -2296,6 +2296,46 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
mask_transform = wlr_output_transform_invert(mask->transform);
mask_transform = wlr_output_transform_compose(mask_transform, data->transform);
mask_src_box = mask->src_box;
+
+ // Map the blur box through the mask buffer's own geometry instead
+ // of stretching the whole mask texture over the blur rect: a mask
+ // buffer LARGER than the blur box (a client overflow rim — the
+ // surface extends transparently past the xdg geometry the blur is
+ // sized to) would otherwise squeeze its transparent rim INTO the
+ // box, and ignore_transparent then kills the blur in a rim-scaled
+ // band at the window's right/bottom. Sample exactly the sub-rect
+ // of the mask that overlaps the blur box, 1:1 in layout space.
+ // Congruent boxes (the common case) skip this and keep the
+ // previous full-texture mapping byte-for-byte.
+ if (tex != NULL) {
+ int mask_x, mask_y, blur_x, blur_y;
+ if (wlr_scene_node_coords(&mask->node, &mask_x, &mask_y) &&
+ wlr_scene_node_coords(node, &blur_x, &blur_y)) {
+ int mask_w = 0, mask_h = 0;
+ scene_node_get_size(&mask->node, &mask_w, &mask_h);
+ if (mask_w > 0 && mask_h > 0 &&
+ (mask_w != blur->width || mask_h != blur->height ||
+ mask_x != blur_x || mask_y != blur_y)) {
+ struct wlr_fbox full = mask_src_box;
+ if (full.width <= 0 || full.height <= 0) {
+ full = (struct wlr_fbox){
+ .x = 0,
+ .y = 0,
+ .width = tex->width,
+ .height = tex->height,
+ };
+ }
+ double sx = full.width / mask_w;
+ double sy = full.height / mask_h;
+ mask_src_box = (struct wlr_fbox){
+ .x = full.x + (blur_x - mask_x) * sx,
+ .y = full.y + (blur_y - mask_y) * sy,
+ .width = blur->width * sx,
+ .height = blur->height * sy,
+ };
+ }
+ }
+ }
}
struct fx_corner_radii blur_corners = blur->corners;