From f2de3ebc61cc3f5d4c4081e9aa22fb1b6623f24d Mon Sep 17 00:00:00 2001
From: natepayn3 <284325035+natepayn3@users.noreply.github.com>
Date: Fri, 6 Nov 2026 08:42:58 -0700
Subject: [PATCH] Glass: a capture pleamar-wm took without our surface is the
background as it is
pleamar-wm marks a screencopy frame with flag 1<<16 when it left out the
surfaces of the program that asked. Nothing of ours is then unmixed from it
(opacity 0 in the unmix), so the holes under our opaque parts are gone.
---
src/platform/wayland.rs | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/src/platform/wayland.rs b/src/platform/wayland.rs
index d88516c..2e0f197 100644
--- a/src/platform/wayland.rs
+++ b/src/platform/wayland.rs
@@ -514,8 +514,16 @@ struct CaptureFor {
dmabuf: Mutex>,
/// The buffer on the card it was asked to copy into.
on_card: Mutex >>,
+ /// The compositor says the picture is of what is behind our surface, without it (pleamar-wm's
+ /// `WITHOUT_YOU` flag): it is the background as it is, and nothing of ours is unmixed from it.
+ without_us: std::sync::atomic::AtomicBool,
}
+/// pleamar-wm's flag on a screencopy frame (after `y_invert`, 1): the picture leaves out the surfaces of the
+/// program that asked. Under our opaque parts —a grid of pictures— the background cannot be unmixed, and
+/// left holes a dome magnified into view; with it there is nothing to unmix.
+const WITHOUT_YOU: u32 = 1 << 16;
+
/// A capture made of several small ones: the glass of a shell is a frame round the screen, and the one box
/// that holds it all is the whole screen, of which the compositor would read back (and we would upload) twenty
/// times what is needed. Each part is photographed on its own and copied to where it goes in one buffer
@@ -613,6 +621,13 @@ impl Dispatch2 for CaptureFor {
*self.dmabuf.lock().unwrap() = Some((format, width, height));
CARD_OFFERED.store(true, Ordering::Relaxed);
}
+ E::Flags { flags } => {
+ let bits = match flags {
+ wayland_client::WEnum::Value(f) => f.bits(),
+ wayland_client::WEnum::Unknown(v) => v,
+ };
+ self.without_us.store(bits & WITHOUT_YOU != 0, Ordering::Relaxed);
+ }
E::BufferDone => {
let id = self.target.id;
// On the card, if the render has made a buffer of the very size and format offered:
@@ -672,7 +687,8 @@ impl Dispatch2 for CaptureFor {
}
E::Ready { .. } => {
let id = self.target.id;
- let opacity = *self.target.opacity.lock().unwrap();
+ // Without us in it, there is nothing of ours to take out of it: as if ours were not there.
+ let opacity = if self.without_us.load(Ordering::Relaxed) { 0.0 } else { *self.target.opacity.lock().unwrap() };
let Some((parts, k)) = &self.part else {
if let Some(card) = self.on_card.lock().unwrap().take() {
let _ = e.to_render.send(ToRender::Backdrop(Box::new(super::Backdrop { sheet: id, width: card.size.0, height: card.size.1, stride: 0, opacity, data: None, rects: Vec::new(), card: Some(card), offer: None })));
@@ -929,10 +945,10 @@ impl PlatformWindow for WaylandWindow {
if rects.len() >= 2 && !PARTS_BROKEN.load(Ordering::Relaxed) && !on_card {
let parts = Arc::new(Mutex::new(Parts { hull: bounds, rects: rects.clone(), valid: Vec::new(), over: false }));
for (k, r) in rects.iter().enumerate() {
- frames.push(manager.capture_output_region(0, &output, x + r[0], y + r[1], r[2], r[3], &qh, CaptureFor { target: d.clone(), on_change, format: Mutex::new(None), part: Some((parts.clone(), k)), dmabuf: Mutex::new(None), on_card: Mutex::new(None) }));
+ frames.push(manager.capture_output_region(0, &output, x + r[0], y + r[1], r[2], r[3], &qh, CaptureFor { target: d.clone(), on_change, format: Mutex::new(None), part: Some((parts.clone(), k)), dmabuf: Mutex::new(None), on_card: Mutex::new(None), without_us: std::sync::atomic::AtomicBool::new(false) }));
}
} else {
- frames.push(manager.capture_output_region(0, &output, x + bounds[0], y + bounds[1], bounds[2], bounds[3], &qh, CaptureFor { target: d.clone(), on_change, format: Mutex::new(None), part: None, dmabuf: Mutex::new(None), on_card: Mutex::new(None) }));
+ frames.push(manager.capture_output_region(0, &output, x + bounds[0], y + bounds[1], bounds[2], bounds[3], &qh, CaptureFor { target: d.clone(), on_change, format: Mutex::new(None), part: None, dmabuf: Mutex::new(None), on_card: Mutex::new(None), without_us: std::sync::atomic::AtomicBool::new(false) }));
}
};
// With frames of its own, after the frame just handed over, from the thread that hands