srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/udev
diff options
context:
space:
mode:
Diffstat (limited to 'crates/wayland/src/udev')
-rw-r--r--crates/wayland/src/udev/mod.rs7
-rw-r--r--crates/wayland/src/udev/platform.rs1
-rw-r--r--crates/wayland/src/udev/render.rs44
3 files changed, 51 insertions, 1 deletions
diff --git a/crates/wayland/src/udev/mod.rs b/crates/wayland/src/udev/mod.rs
index 83ffa87..4762992 100644
--- a/crates/wayland/src/udev/mod.rs
+++ b/crates/wayland/src/udev/mod.rs
@@ -231,6 +231,13 @@ pub(crate) struct UdevState {
/// regardless (every head starts with `ages: [0, 0]` - see
/// `UdevHead`'s own field).
pub(crate) last_rendered_workspace: Option<srdwm_core::WorkspaceId>,
+ /// Order-sensitive hash of every visible window's id and rect, compared
+ /// each frame in `render_udev_frame` to force `ages` back to `[0, 0]`
+ /// on any move/resize/open/close/restack - see that comparison's own
+ /// doc comment for the live-reproduced ghost-content bug this catches.
+ /// `None` before the first frame, same reasoning as `last_rendered_
+ /// workspace` above (renders fully regardless).
+ pub(crate) last_rendered_layout: Option<u64>,
}
impl UdevState {
diff --git a/crates/wayland/src/udev/platform.rs b/crates/wayland/src/udev/platform.rs
index 1d2ef0e..2868f15 100644
--- a/crates/wayland/src/udev/platform.rs
+++ b/crates/wayland/src/udev/platform.rs
@@ -130,6 +130,7 @@ impl UdevPlatform {
session: session.clone(),
disabled_connectors: std::collections::HashSet::new(),
last_rendered_workspace: None,
+ last_rendered_layout: None,
};
let mut state = CompState {
diff --git a/crates/wayland/src/udev/render.rs b/crates/wayland/src/udev/render.rs
index 398f06e..d8f0003 100644
--- a/crates/wayland/src/udev/render.rs
+++ b/crates/wayland/src/udev/render.rs
@@ -46,7 +46,40 @@ impl CompState {
// happen per head below without needing `self` itself mutably) --
// see the per-head loop for why decoration/border buffers still get
// looked up fresh per head (head-local `origin` translation).
- let ids: Vec<srdwm_core::WindowId> = if locked { Vec::new() } else { self.wm.borrow().visible_windows_front_to_back().map(|w| w.id).collect() };
+ // `layout_signature` folds in every visible window's id and rect
+ // (order-sensitive, so a restack counts as a change too) - compared
+ // against `udev.last_rendered_layout` below to force `ages = [0, 0]`
+ // whenever a window moves, resizes, opens, closes, or restacks
+ // between one frame and the next. Without this, a window vacating
+ // part of the screen (closing, moving away, un-maximizing) can leave
+ // stale content from its old position baked into whichever DRM
+ // buffer isn't due to be written again for a while - confirmed
+ // live: maximizing a window over a second one, then un-maximizing,
+ // left a persistent ghost of the second window's old titlebar/status
+ // text sitting in the vacated corner, unchanged across multiple
+ // otherwise-idle frames, because `OutputDamageTracker`'s own element-
+ // level diffing (relied on by the workspace-switch reset's own doc
+ // comment above) evidently doesn't always catch a vacated region on
+ // its own - most visible right where a rounded corner's mask should
+ // have revealed the desktop underneath but instead revealed this.
+ // Same "defensive, not a fix for a proven bug in the diffing itself"
+ // reasoning as the workspace-switch reset already documents; this is
+ // just a second, complementary trigger for the same reset.
+ let (ids, layout_signature): (Vec<srdwm_core::WindowId>, u64) = if locked {
+ (Vec::new(), 0)
+ } else {
+ let wm = self.wm.borrow();
+ let mut ids = Vec::new();
+ let mut sig: u64 = 0xcbf29ce484222325;
+ for w in wm.visible_windows_front_to_back() {
+ ids.push(w.id);
+ for part in [w.id, w.geometry.x as u64, w.geometry.y as u64, w.geometry.width as u64, w.geometry.height as u64] {
+ sig ^= part;
+ sig = sig.wrapping_mul(0x100000001b3);
+ }
+ }
+ (ids, sig)
+ };
let focused = self.wm.borrow().focused_id();
// Stays default `false` here, unlike winit's `unwrap_or(true)` --
// see `rounded_corners_pixman`'s module doc comment for the real
@@ -106,6 +139,15 @@ impl CompState {
head.ages = [0, 0];
}
}
+ // See `layout_signature`'s own doc comment above for what this
+ // catches that the workspace-switch reset above doesn't: any move,
+ // resize, open, close, or restack within the *same* workspace.
+ if !locked && udev.last_rendered_layout != Some(layout_signature) {
+ udev.last_rendered_layout = Some(layout_signature);
+ for head in &mut udev.heads {
+ head.ages = [0, 0];
+ }
+ }
// A head whose page-flip event never arrives (kernel-dropped, or a
// DRM event this driver never sends for reasons this backend has no
// visibility into) would otherwise sit in `flip_pending` forever: