Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
overview: a world window spawning during overview exits onto it
Mapping a Floating/Tiled/Fullscreen window while overview is up snaps
the camera out of overview (zoom 1, centered on the new window, mode
Normal) before the map-time focus runs — the user asked for the window
(launcher pick, spawn keybind), so the session lands on it. Chrome
(Popup/Overlay via resolved mode), status, wallpaper and the grid
spawn without disturbing the overview; settle-phase/blocked-focus
spawns are excluded by the existing should_focus gates.
exit_overview_to_window is the factored map-time variant of the camera
dance the overview click-release path does inline in cursor.rs.
Shadow-verified: overview -> launcher spawn stays in overview ->
cce-files spawn exits to normal with the new window focused.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/server/window.rs | 21 +++++++++++++++++++++
src/server/window_manager.rs | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/src/server/window.rs b/src/server/window.rs
index 496b44e..42484e8 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -1259,6 +1259,27 @@ impl Window {
}
}
+ // A WORLD window spawning during overview pulls the session
+ // out of it, landing at zoom 1 on the new window — the user
+ // asked for it (launcher pick, spawn keybind). Chrome
+ // (Popup/Overlay), status, wallpaper and the grid spawn without
+ // disturbing the overview. Before the focus loop, so the
+ // focus-follow pan sees the settled zoom-1 camera and no-ops.
+ if should_focus
+ && (*self.server).wm.mode == crate::window_manager::WindowManagerMode::Overview
+ && !self.is_grid()
+ && !self.is_status_bar()
+ && !self.is_wallpaper()
+ {
+ let resolved = (*self.server).wm.get_mode_for_window(self as *mut Window);
+ if !matches!(
+ resolved,
+ crate::tiling::TilingMode::Popup | crate::tiling::TilingMode::Overlay
+ ) {
+ (*self.server).wm.exit_overview_to_window(self as *mut Window);
+ }
+ }
+
// The grid layer never takes focus — it is desktop furniture,
// not a window (it is also input-transparent, so focus here
// would be unreachable-by-click and unswitchable-away for
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index a2a65ed..c1ceb53 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -2010,6 +2010,43 @@ impl WindowManager {
+ /// Snap the camera out of overview and onto `win`: zoom 1, centered,
+ /// mode Normal. The overview click-release path in cursor.rs does the
+ /// same dance inline (plus its focus/seat-event bookkeeping); this is
+ /// the map-time variant for windows SPAWNED during overview.
+ pub unsafe fn exit_overview_to_window(&mut self, win: *mut Window) {
+ if win.is_null() {
+ return;
+ }
+ let (mut viewport_w, mut viewport_h) = (1920.0_f64, 1080.0_f64);
+ let outputs_list = &(*self.server).om.outputs as *const ffi::wl_list as *mut WlList;
+ let mut curr_out = (*outputs_list).next;
+ while curr_out != outputs_list {
+ let output = crate::container_of!(curr_out, crate::output::Output, link);
+ if (*output).sent.state == crate::output::OutputStateValue::Enabled {
+ let wlr_box = (*output).sent.box_layout();
+ viewport_w = wlr_box.width as f64;
+ viewport_h = wlr_box.height as f64;
+ break;
+ }
+ curr_out = (*curr_out).next;
+ }
+ let win_w = if (*win).box_geom.width > 0 { (*win).box_geom.width as f64 } else { 800.0 };
+ let win_h = if (*win).box_geom.height > 0 { (*win).box_geom.height as f64 } else { 600.0 };
+ let center_x = (*win).virtual_x + win_w / 2.0;
+ let center_y = (*win).virtual_y + win_h / 2.0;
+ self.desk_zoom = 1.0;
+ self.mode = WindowManagerMode::Normal;
+ self.desk_pan_x = center_x - viewport_w / 2.0;
+ self.desk_pan_y = center_y - viewport_h / 2.0;
+ self.stop_panning_animation();
+ if matches!(self.state, WindowManagerState::Idle) {
+ self.update_viewport_local();
+ } else {
+ self.dirty_windowing();
+ }
+ }
+
/// Issue grid_patch events to grid clients whose current patch no
/// longer comfortably covers the viewport (or whose buffer resolution
/// has drifted more than 2x from the zoom). One patch in flight per