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

commitc3b61efb443bfaf0c80fa5982968a042a65a10ac
parentd1e60d707a
authorLucas Galante <[email protected]>
date2026-09-09 08:11
fix(restore): a floating window remembered off-view reopens centered in view

try_restore replayed a saved floating origin verbatim, so Inkscape's start
screen restored a full viewport above the desk every login — at the cell
its previous incarnation was saved in, with nothing on screen to say it
existed. After the mode latching and geometric promotion, a window that is
still Floating (and not minimized) now asks policy::camera::recalled_origin
whether its remembered position would show at least a quarter of it under
the camera as it stands at restore; if not, it takes the origin that
centers it in the current view. Tiled windows are untouched: they are the
grid's, and a saved Tiled window off the current view stays exactly where
the grid has it.

Verified in a scale-2 shadow with a session save and --restore: a floating
GTK3 window parked eight grid periods below the view came back centered
(pan + (viewport - size) / 2 on both axes, to the pixel), while a tiled
window parked eight periods to the right came back at its far cell. The
same scenario on the previous binary restored the floating window
off-view.

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

 CLAUDE.md            |  6 ++++++
 src/server/window.rs | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)

diff --git a/CLAUDE.md b/CLAUDE.md
index a9e6ee6..4052cd7 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -468,6 +468,12 @@ the **bare name** whenever the name's first `PATH` hit is a different file
 from the one running (`path_shadowed_name`), and the restore's `sh -c`
 resolves it the way the launcher did. The absolute path is kept when PATH
 agrees with it.
+A restored **floating** window is recalled into the current view
+(`policy::camera::recalled_origin`, applied at the end of `try_restore`)
+when its remembered position would show less than a quarter of it: the
+camera at restore is wherever the session left it, and a floating window a
+screen away from that is lost, not remembered. Tiled windows stay where the
+grid has them.
 
 ### IPC & status sockets
 
diff --git a/src/server/window.rs b/src/server/window.rs
index 24eb570..630dafc 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -1179,6 +1179,40 @@ impl Window {
                 }
             }
 
+            // A remembered FLOATING position is only worth keeping if it is
+            // where the user can see it. The camera at restore is wherever
+            // the session left it (or wherever the user has panned since a
+            // relaunch), and a floating window a screen away from that is
+            // lost, not remembered: Inkscape's start screen came back a full
+            // viewport above the desk every login, at the cell its previous
+            // incarnation had been saved in, with nothing on screen to say
+            // it existed. Tiled windows are the grid's and stay put.
+            if self.tiling_mode == crate::tiling::TilingMode::Floating && !self.minimized {
+                let (_, _, vp_w, vp_h) = self.first_enabled_output_box();
+                let wm = &(*self.server).wm;
+                let cam = crate::policy::camera::Camera {
+                    pan_x: wm.desk_pan_x,
+                    pan_y: wm.desk_pan_y,
+                    zoom: wm.desk_zoom,
+                };
+                if let Some((nx, ny)) = crate::policy::camera::recalled_origin(
+                    self.virtual_x,
+                    self.virtual_y,
+                    saved.width as f64,
+                    saved.height as f64,
+                    cam,
+                    vp_w,
+                    vp_h,
+                ) {
+                    log::info!(
+                        "Recalling off-view floating window into view: app_id={} remembered=({:.0},{:.0}) -> ({:.0},{:.0})",
+                        app_id_str, self.virtual_x, self.virtual_y, nx, ny
+                    );
+                    self.virtual_x = nx;
+                    self.virtual_y = ny;
+                }
+            }
+
             self.restored = true;
             self.session_restored = from_session;
             // The saved `focused` flag only means something for the startup