Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix(overview): launching an app keeps overview, panning only to show it
A world window mapping during overview flew the camera to zoom 1 on it
and left overview. It now keeps the zoom and pans just far enough to
show the whole window (pan_to_virtual_rect), whatever center_on_spawn
says, and not while an overview flight still owns the camera.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/server/window.rs | 17 +++++++++-------
src/server/window_manager.rs | 46 +++++++++-----------------------------------
2 files changed, 19 insertions(+), 44 deletions(-)
diff --git a/src/server/window.rs b/src/server/window.rs
index 76a6fafc..8a45ad9c 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -2269,12 +2269,15 @@ 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.
+ // A WORLD window spawning during overview stays in overview:
+ // the camera keeps its zoom and only pans, as little as it
+ // must, to show the whole new window. Until 2026-10-05 it
+ // flew out to zoom 1 on the window, so launching from the
+ // overview left it. Chrome (Popup/Overlay), status, wallpaper
+ // and the grid spawn without touching the camera. Here rather
+ // than left to the focus loop's focus-follow pan, which skips
+ // a first focus unless `center_on_spawn` allows it — the exit
+ // this replaced always moved the camera.
if should_focus
&& (*self.server).wm.mode == crate::window_manager::WindowManagerMode::Overview
&& !self.is_grid()
@@ -2286,7 +2289,7 @@ impl Window {
resolved,
crate::tiling::TilingMode::Popup | crate::tiling::TilingMode::Overlay
) {
- (*self.server).wm.exit_overview_to_window(self as *mut Window);
+ (*self.server).wm.pan_overview_to_window(self as *mut Window);
}
}
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index f8892d7d..dc519745 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -3365,47 +3365,19 @@ 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() {
+ /// Pan the overview camera, at its current zoom, just far enough to
+ /// show all of `win` — the map-time treatment for a window SPAWNED
+ /// during overview, which leaves the mode alone. A camera ramp (an
+ /// overview enter still flying) owns the camera, and its mid-flight
+ /// sample would give a stale target, so the window is left to land
+ /// wherever the flight shows it.
+ pub unsafe fn pan_overview_to_window(&mut self, win: *mut Window) {
+ if win.is_null() || self.camera_ramp_anim.is_some() {
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;
- // Same animated flight as the Overview toggle's exit: SetCamera
- // owns the ramp/target bookkeeping and flips the mode by fiat.
- self.stop_panning_animation();
- crate::policy::api::Compositor::apply(
- self,
- &crate::policy::api::Command::SetCamera {
- camera: crate::policy::camera::Camera {
- pan_x: center_x - viewport_w / 2.0,
- pan_y: center_y - viewport_h / 2.0,
- zoom: 1.0,
- },
- overview: Some(false),
- animate: true,
- },
- );
- crate::policy::api::Compositor::apply(self, &crate::policy::api::Command::RefreshCamera);
+ self.pan_to_virtual_rect((*win).virtual_x, (*win).virtual_y, win_w, win_h);
}
/// Issue grid_patch events to grid clients whose current patch no