diff options
| author | srdusr <[email protected]> | 2025-03-07 22:50:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-03-07 22:50:00 +0200 |
| commit | ee972222588bfcbdad76a30eee46166891ffc45d (patch) | |
| tree | bb54ab25e9a3e0b8ca7bcc512ab472df1d78a99f /crates/wayland/src/state/geometry.rs | |
| parent | 9667308e6def22e9b1fa352357a788749ad92c74 (diff) | |
| download | srdwm-ee972222588bfcbdad76a30eee46166891ffc45d.tar.gz srdwm-ee972222588bfcbdad76a30eee46166891ffc45d.zip | |
Fix border/decoration stuck at a stale size after a passive tiling reflow
effective_frame_of sized a window's border/shadow/titlebar from
dwindow.geometry() - xdg_surface::set_window_geometry. Per smithay's
own implementation that value is the client's cached hint intersected
with bbox(), falling back to bbox() only if never set. Nothing in the
protocol obliges a client to resend the hint on every resize, and
intersection() can never return something larger than its smaller
operand - so once a client's cached hint is smaller than its current
real buffer, geometry() stays clamped there permanently.
Confirmed live: after a passive tiling reflow (this window resized
only as a side effect of a sibling window moving, no direct action on
this window itself), Firefox's real content filled the correct, much
larger area immediately, but its border/decoration stayed rendered at
a small fraction of that - unchanged for several seconds, well past
both animation settling and any reasonable commit-throttle window --
until an unrelated maximize/restore cycle happened to prompt Firefox
into resending a fresh hint and self-correcting.
Switched to bbox(): the real bounding box of the window's current
surface tree, which updates on every commit unconditionally. This
gives up excluding a CSD client's own invisible drop-shadow margin,
but sync_geometry already unconditionally sends all four tiled state
bits specifically so a compliant client (GTK4/Firefox) reserves no
such margin at all, so a compliant client loses nothing. Both the
decoration-bitmap sizing (redraw_decoration_buffer) and the render
loops' own border/shadow/occlusion positioning funnel through this one
function, so they stay consistent with each other - avoiding the
out-of-bounds texture-crop regression a previous, different attempt at
this same lag hit (see this function's own doc comment history).
Diffstat (limited to 'crates/wayland/src/state/geometry.rs')
| -rw-r--r-- | crates/wayland/src/state/geometry.rs | 45 |
1 files changed, 40 insertions, 5 deletions
diff --git a/crates/wayland/src/state/geometry.rs b/crates/wayland/src/state/geometry.rs index 715881b..774eabc 100644 --- a/crates/wayland/src/state/geometry.rs +++ b/crates/wayland/src/state/geometry.rs @@ -64,7 +64,41 @@ impl CompState { // yet done. let Some(w) = wm.borrow().window(id).cloned() else { return geom }; let Some(dwindow) = id_to_window.get(&id) else { return geom }; - let content = dwindow.geometry(); + // `dwindow.geometry()` - `xdg_surface::set_window_geometry` - is, + // per smithay's own implementation, that cached hint *intersected* + // with `bbox()`, falling back to `bbox()` only if the client never + // set one. Nothing in the protocol obliges a client to resend the + // hint on every resize (only when the visible-content-vs-buffer + // relationship itself changes), and `intersection()` can never + // return something larger than its smaller operand - so once a + // client's cached hint is smaller than its current real buffer, + // `geometry()` stays clamped there permanently, no matter how much + // larger the buffer grows afterward. Confirmed live: after a + // *passive* tiling reflow (this window resized only as a side + // effect of a sibling moving, no direct action on this window + // itself), Firefox's real content filled the correct, much larger + // area immediately, but its border/decoration - both driven by + // this function, see `redraw_decoration_buffer`'s and the render + // loops' own call sites - stayed rendered at a small fraction of + // that, unchanged for several seconds (long past both animation + // settling and any reasonable commit-throttle window), until an + // unrelated maximize/restore cycle on the same window happened to + // prompt Firefox into resending a fresh hint and self-correcting. + // `bbox()` - the real bounding box of the window's current surface + // tree - updates on every commit unconditionally, independent of + // whether the client also chose to resend the optional hint, so it + // has no equivalent staleness window. The one thing this gives up + // is excluding a CSD client's own invisible drop-shadow margin from + // the border/content area - but `sync_geometry` already + // unconditionally tells every window it is tiled on all four sides + // specifically so a compliant client (GTK4/Firefox concretely, see + // that function's own doc comment) reserves no such margin at all, + // so a compliant client loses nothing here. A client that ignores + // the tiled hint and reserves one anyway ends up with a border a + // few pixels larger than its real visible chrome - a real but + // strictly smaller, more cosmetic problem than one stuck at the + // wrong size and position indefinitely. + let content = dwindow.bbox(); if content.size.w <= 0 || content.size.h <= 0 { // No real committed content yet - racing the first commit // right after creation, most likely. Nothing to correct @@ -72,10 +106,11 @@ impl CompState { // collapsing every dimension down to (near) zero. return geom; } - // `content` is `xdg_surface::set_window_geometry` - specified to - // carry *logical* points, same as `sync_geometry`'s own `size` - // going the other direction (see that function's matching doc - // comment). Every caller of this method (border, shadow, occlusion, + // `content` (`bbox()`) is in the same *logical* points as + // `xdg_surface::set_window_geometry` would have been, same as + // `sync_geometry`'s own `size` going the other direction (see that + // function's matching doc comment). Every caller of this method + // (border, shadow, occlusion, // resize-margin hit-test) works in this compositor's own physical // convention, same as `geom` - so `content.size` needs converting // back to physical here, the same `* scale` `sync_geometry` divides |