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

commitd15f78adf31676fadffcef98b67bc4fb478aecff
parent91da44bbe2
authorLucas Galante <[email protected]>
date2026-08-04 21:31
fix: user clicks pan to restored windows on first focus

A restored window maps unfocused and keeps is_new until its first focus
— which, right after a session restart, is the user's first click on it.
The focus-follow gate suppressed that pan (it only meant to stop the
RESTORE pipeline's automatic focuses from yanking the camera), so after
every login each window's first click appeared to ignore focus-follow
entirely. Once startup_input_seen is set the settling phase is over: a
first focus is user intent and pans like any other, except placement-
hinted spawns whose pickers must open at their control without moving
the camera.

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

 src/server/seat.rs | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/server/seat.rs b/src/server/seat.rs
index ff2cc0f..d945b54 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -450,7 +450,16 @@ impl Seat {
                     let spawn_pan = !(*window).session_restored
                         && !(*window).hint_placed
                         && (*self.server).wm.center_on_spawn;
-                    let should_pan = (!is_new || spawn_pan) && !is_cce_cloud;
+                    // A restored window maps unfocused and keeps is_new until
+                    // its first focus — which, after a session restart, is
+                    // the user's first CLICK on it. Suppressing that pan made
+                    // every window seem to ignore focus-follow right after
+                    // login. Once real input has been seen the settling phase
+                    // is over: a first focus is user intent and pans like any
+                    // other, except for placement-hinted spawns (pickers that
+                    // open at their control and must not yank the camera).
+                    let user_focus = (*self.server).wm.startup_input_seen && !(*window).hint_placed;
+                    let should_pan = (!is_new || spawn_pan || user_focus) && !is_cce_cloud;
                     if should_pan {
                         let outputs_list = &mut (*self.server).om.outputs as *mut ffi::wl_list as *mut WlList;
                         let mut curr_out = (*outputs_list).next;