git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit1e1958b2f759da2c1b5c0f10e4210ab71da963ea
parent8c99a9686c
authorLucas Galante <[email protected]>
date2026-08-21 13:00
grid: quantize patches from the flight's target zoom; strict drift at rest

Two fixes for the grid 'readjusting' at the end of the overview exit
animation:

- The mid-flight repatch quantized its resolution from the interpolated
  zoom at issue time (the drift ceiling trips near zoom 0.5 on exit →
  a q=1 patch), which then RESTED at display factor 2.0 — just inside
  the permissive drift window — so the grid stayed 2x-magnified after
  every overview round trip, and the latch landing at the animation's
  end read as a visible readjust. Patches now quantize from the camera
  flight's DESTINATION zoom (ramp anim target, else the exponential
  target), so the one mid-flight swap lands the final content: exit
  re-patches once at @native, enter reaches the overview resolution in
  one hop instead of two.

- covers() keeps the permissive (0.5, 2.01) drift window only while the
  viewport is moving; at rest it demands the pow2 half-step band
  (0.70, 1.42) — the safety net that re-patches any path that settles
  mis-resolved. The band includes 1.414, so a boundary zoom re-quantizes
  to the same q and a settled repatch can never loop.

Shadow-verified (scale 2, deep overview via a far window): enter sends
@0.500 directly, exit sends a single @2.000, the settled grid rests at
native resolution, and the only post-settle frame delta is the standard
backdrop-blur re-enable on windows.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/server/window_manager.rs | 35 ++++++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 2a7c761..7bdbefc 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -2080,9 +2080,38 @@ impl WindowManager {
         // scale so a buffer px is a NATIVE px at the quantized zoom — at
         // scale 2 an unscaled patch is magnified 2x on screen, which turns
         // every cell edge into 2px staircase blocks.
-        let q = (2f64).powf(zoom.log2().round()).clamp(0.125, 2.0) * out_scale;
+        //
+        // Quantized from the DESTINATION zoom while a camera flight is in
+        // progress: quantizing from the interpolated zoom issued the
+        // mid-flight patch at the flight's own resolution (overview exit
+        // crossed the drift ceiling near zoom 0.5 → a q=1 patch) which then
+        // RESTED at display factor 2.0 — just inside the permissive drift
+        // window — so the grid stayed 2x-magnified after every overview
+        // round trip, and the latch landing at the animation's end read as
+        // the grid "readjusting". A target-quantized patch still swaps
+        // mid-flight, but lands the final content once, and enter flights
+        // reach the overview resolution in one hop instead of two.
+        let q_zoom = crate::policy::background::sanitized_zoom(
+            self.camera_ramp_anim
+                .as_ref()
+                .map(|a| a.target.zoom)
+                .or(self.target_desk_zoom)
+                .unwrap_or(self.desk_zoom),
+        );
+        let q = (2f64).powf(q_zoom.log2().round()).clamp(0.125, 2.0) * out_scale;
         let period = self.layout.desktop_grid_scale
             + (self.layout.desktop_gap_width as f64).max(0.0);
+        // Resolution drift tolerance: permissive while the camera is moving
+        // (a giant re-render per animation frame would be worse than a bit
+        // of scaling), but at REST the patch must sit within the pow2
+        // half-step band around exact — the safety net that re-patches any
+        // path that settles mis-resolved. The band includes 1.414 (a zoom
+        // exactly on a half-step boundary re-quantizes to the same q), so
+        // a settled repatch can never loop.
+        let in_flight = self.viewport_is_active
+            || self.camera_ramp_anim.is_some()
+            || self.target_desk_zoom.is_some();
+        let (disp_lo, disp_hi) = if in_flight { (0.5, 2.01) } else { (0.70, 1.42) };
         let covers = |p: &crate::policy::api::GridPatch| -> bool {
             let mx = vw * 0.15;
             let my = vh * 0.15;
@@ -2095,8 +2124,8 @@ impl WindowManager {
                 && p.y <= vy - my
                 && p.x + p.w >= vx + vw + mx
                 && p.y + p.h >= vy + vh + my
-                && disp > 0.5
-                && disp < 2.01
+                && disp > disp_lo
+                && disp < disp_hi
         };
         for &w in self.windows.iter() {
             if w.is_null() || (*w).closed || !(*w).is_grid() {