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

commit1a309e828ead8449165900b509432032cb6de91c
parente8eaa2db2c
authorLucas Galante <[email protected]>
date2026-07-15 10:24
refactor: extract policy layer to the cce-window-manager crate

src/server/policy/ and src/server/slotmap.rs move to the new sibling
repo cce-window-manager (initial commit 80b9f61 there); the Action enum
moves out of config.rs into that crate's api module. lib.rs aliases the
crate back as crate::policy / crate::tiling / crate::slotmap and
config.rs re-exports Action, so all historical paths keep working —
no mechanism code changed. Behavior-identical: 29 tests moved with the
crate, 6 remain here, 35 total as before.

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

 CLAUDE.md                    |   19 +-
 Cargo.toml                   |    1 +
 src/lib.rs                   |   11 +-
 src/server/config.rs         |   38 +-
 src/server/policy/api.rs     |  143 ----
 src/server/policy/arrange.rs | 1756 ------------------------------------------
 src/server/policy/mod.rs     |   20 -
 src/server/policy/state.rs   |   32 -
 src/server/policy/tiling.rs  |  113 ---
 src/server/slotmap.rs        |  195 -----
 10 files changed, 21 insertions(+), 2307 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 658d321..d13ddfc 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -14,8 +14,13 @@ protocol XML files you'll see throughout `src/server/` and `protocol/`.
 This crate lives inside a larger Cargo workspace (the workspace root is the **parent**
 directory `../Cargo.toml`, which lists ~20 `cce-*` sibling apps). This crate is the
 compositor; the siblings (`cce-status-interface`, `cce-system-settings`, etc.) are
-clients that talk to it over its sockets. The only intra-workspace dependency is
-`cce-ui` (`../cce-ui`).
+clients that talk to it over its sockets. Intra-workspace dependencies: `cce-ui`
+(`../cce-ui`, config helpers) and **`cce-window-manager`** (`../cce-window-manager`,
+its own repo) — the pure-Rust window-management **policy layer** (arrange pass,
+`TilingMode`, saved state, the `Policy`/`Compositor` trait boundary, slotmap). It was
+extracted from this crate's `src/server/policy/`; `src/lib.rs` re-exports it as
+`crate::policy` / `crate::tiling` / `crate::slotmap`, so mechanism code keeps using
+the historical paths.
 
 > Note: `README.md` is stale — it describes an old split `cce-server`/`cce-client`
 > architecture. The real architecture is monolithic (a single `cce-fx` server binary
@@ -55,8 +60,9 @@ Native libs via `pkg-config`: `wlroots-0.20`, `wayland-server`, `xkbcommon`,
 
 ## Tests
 
-Tests are sparse (unit tests in `slotmap.rs`, `config.rs`, `window_manager.rs`).
-The library crate name is `cce_fx` (underscored).
+Tests are sparse (unit tests in `config.rs`, `window_manager.rs`; the arrange/slotmap
+tests live in the sibling `cce-window-manager` crate — run them with
+`cargo test -p cce-window-manager`). The library crate name is `cce_fx` (underscored).
 
 ```sh
 cargo test --lib                  # all library tests
@@ -106,8 +112,9 @@ treats them as opaque.
   thread.
 - **`window.rs`** (~3300 lines) — per-window model and rendering (borders, blur,
   viewport transforms).
-- **`tiling.rs`** — `TilingMode` enum: `Floating`, `Cascade`, `Grid`, `Fullscreen`,
-  `Popup`, `Overlay`, `Maximized`. Modes apply per-window and per-viewport.
+- **`crate::tiling`** (from `cce-window-manager`) — `TilingMode` enum: `Floating`,
+  `Cascade`, `Grid`, `Fullscreen`, `Popup`, `Overlay`, `Maximized`. Modes apply
+  per-window and per-viewport.
 - Input stack: `input_manager.rs`, `seat.rs`, `cursor.rs`, `keyboard*.rs`,
   `xkb_*.rs`, `libinput_*.rs`, `pointer_*.rs`, `tablet*.rs`, `text_input.rs`,
   `input_relay.rs`/`input_popup.rs` (IME).
diff --git a/Cargo.toml b/Cargo.toml
index 64e944a..ad43ddc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,7 @@ doctest = false
 
 [dependencies]
 cce-ui = { path = "../cce-ui" }
+cce-window-manager = { path = "../cce-window-manager" }
 xkbcommon = "0.7"
 serde = { version = "1", features = ["derive"] }
 nix = { version = "0.29", features = ["signal", "process", "fs", "poll", "resource"] }
diff --git a/src/lib.rs b/src/lib.rs
index e2cb64d..85876af 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,8 +11,7 @@ pub mod server;
 pub mod process;
 #[path = "server/util.rs"]
 pub mod util;
-#[path = "server/slotmap.rs"]
-pub mod slotmap;
+pub use cce_window_manager::slotmap;
 #[path = "server/window_manager.rs"]
 pub mod window_manager;
 #[path = "server/xkb_bindings.rs"]
@@ -21,9 +20,11 @@ pub mod xkb_bindings;
 pub mod layer_shell;
 #[path = "server/scene.rs"]
 pub mod scene;
-#[path = "server/policy/mod.rs"]
-pub mod policy;
-pub use policy::tiling;
+// The window-management policy layer lives in the sibling crate
+// `cce-window-manager` (pure Rust, no FFI). The aliases keep the historical
+// `crate::policy::…` / `crate::tiling` / `crate::slotmap` paths working.
+pub use cce_window_manager as policy;
+pub use cce_window_manager::tiling;
 #[path = "server/config.rs"]
 pub mod config;
 #[path = "server/ipc_server.rs"]
diff --git a/src/server/config.rs b/src/server/config.rs
index 81e1070..96ac7e5 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -123,43 +123,7 @@ pub struct ModeRule {
     pub ssd: Option<bool>,
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum Action {
-    None,
-    Spawn,
-    Toggle,
-    Close,
-    FocusNext,
-    FocusPrev,
-    WindowSwitcher,
-    Move,
-    Resize,
-    Exit,
-    Reload,
-    Fullscreen,
-    LayoutNext,
-    ModeNext,
-    ModeNextShared,
-    View1,
-    View2,
-    View3,
-    View4,
-    SetViewport1,
-    SetViewport2,
-    SetViewport3,
-    SetViewport4,
-    Expose,
-    Minimize,
-    OverlayLeft,
-    OverlayRight,
-    ZoomIn,
-    ZoomOut,
-    ZoomReset,
-    PanLeft,
-    PanRight,
-    PanUp,
-    PanDown,
-}
+pub use cce_window_manager::api::Action;
 
 #[derive(Debug, Deserialize, Clone)]
 pub struct KeybindConfig {
diff --git a/src/server/policy/api.rs b/src/server/policy/api.rs
deleted file mode 100644
index 718428e..0000000
--- a/src/server/policy/api.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-// The Policy / Compositor trait boundary.
-//
-// `Policy` is implemented by the window-management side: it receives events
-// and decides placement, focus, decoration, and background — using only the
-// plain-data types in this file, never FFI. `Compositor` is implemented by
-// the mechanism side (`window_manager.rs` and friends): it executes those
-// decisions against the wlroots/scenefx scene graph.
-//
-// Skeleton status: nothing implements these traits yet. The migration plan is
-// to split `arrange_views()` into a policy half (compute placements) and a
-// mechanism half (apply to scene), then route window lifecycle, input actions,
-// and the animation tick through `Policy`. Effects are declarative on purpose:
-// new scenefx capabilities extend `EffectSpec` without changing either trait.
-
-use super::state::SavedState;
-
-/// Opaque handle to a window. Wraps the `SlotMap` key that the mechanism side
-/// uses internally; policy code never sees a pointer.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct WindowId(pub crate::slotmap::Key);
-
-/// What a surface is for. Assigned once at map time, this replaces scattered
-/// app_id string-matching (`"cce-wallpaper"`, `"cce-status*"`) in mechanism code.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum WindowRole {
-    Normal,
-    StatusBar,
-    Background,
-    Overlay,
-}
-
-impl WindowRole {
-    /// The single place the special app_id conventions are interpreted.
-    /// `Overlay` is never derived from an app_id — it comes from tiling mode.
-    pub fn from_app_id(app_id: Option<&str>) -> Self {
-        match app_id {
-            Some("cce-wallpaper") => WindowRole::Background,
-            Some(id) if id.starts_with("cce-status") => WindowRole::StatusBar,
-            _ => WindowRole::Normal,
-        }
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct WindowInfo {
-    pub app_id: String,
-    pub title: String,
-    pub role: WindowRole,
-    pub cmdline: String,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Rect {
-    pub x: i32,
-    pub y: i32,
-    pub width: i32,
-    pub height: i32,
-}
-
-/// Premultiplied-alpha RGBA, 0.0–1.0 per channel (scenefx convention).
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Rgba(pub [f32; 4]);
-
-/// Server-side decoration for one window: borders now, titlebars later.
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct DecorationSpec {
-    pub border_width: i32,
-    pub border_color: Rgba,
-    pub corner_radius: i32,
-}
-
-/// Declarative per-window scenefx effects.
-#[derive(Debug, Clone, PartialEq)]
-pub struct EffectSpec {
-    pub opacity: f32,
-    pub blur: bool,
-    pub shadow: Option<ShadowSpec>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct ShadowSpec {
-    pub color: Rgba,
-    pub blur_sigma: f32,
-}
-
-/// What the background layer shows. Absorbs cce-wallpaper (Solid) and the
-/// grid_tree drawing in `output.rs` (Grid).
-#[derive(Debug, Clone, PartialEq)]
-pub enum BackgroundSpec {
-    Solid(Rgba),
-    Grid(GridSpec),
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct GridSpec {
-    pub background: Rgba,
-    pub line_color: Rgba,
-    pub cell_size: i32,
-    pub line_width: i32,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct OutputInfo {
-    pub width: i32,
-    pub height: i32,
-    pub scale: f32,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum PointerEvent {
-    Press { window: Option<WindowId>, x: f64, y: f64, button: u32 },
-    Release { window: Option<WindowId>, x: f64, y: f64, button: u32 },
-    Motion { x: f64, y: f64 },
-}
-
-/// Commands from policy to mechanism. Implemented by the compositor side;
-/// every method maps onto existing `WindowManager` / scene operations.
-pub trait Compositor {
-    fn place(&mut self, window: WindowId, rect: Rect);
-    fn focus(&mut self, window: Option<WindowId>);
-    fn raise(&mut self, window: WindowId);
-    fn close(&mut self, window: WindowId);
-    /// Pans/zooms windows and the background in the same frame.
-    fn set_viewport(&mut self, pan_x: f64, pan_y: f64, zoom: f64);
-    fn set_decoration(&mut self, window: WindowId, spec: DecorationSpec);
-    fn set_effects(&mut self, window: WindowId, spec: EffectSpec);
-    fn set_background(&mut self, spec: BackgroundSpec);
-    fn spawn(&mut self, cmdline: &str);
-}
-
-/// Events from mechanism to policy. Implemented by the window-management side.
-pub trait Policy {
-    fn window_mapped(&mut self, c: &mut dyn Compositor, window: WindowId, info: &WindowInfo);
-    fn window_unmapped(&mut self, c: &mut dyn Compositor, window: WindowId);
-    fn window_meta_changed(&mut self, c: &mut dyn Compositor, window: WindowId, info: &WindowInfo);
-    fn action(&mut self, c: &mut dyn Compositor, action: &crate::config::Action, arg: Option<&str>);
-    fn pointer(&mut self, c: &mut dyn Compositor, event: PointerEvent);
-    fn output_changed(&mut self, c: &mut dyn Compositor, outputs: &[OutputInfo]);
-    /// Animation driver: easing for pan/zoom targets, effect transitions.
-    fn tick(&mut self, c: &mut dyn Compositor, dt: f64);
-    fn save_state(&self) -> SavedState;
-    fn restore_state(&mut self, c: &mut dyn Compositor, state: SavedState);
-}
diff --git a/src/server/policy/arrange.rs b/src/server/policy/arrange.rs
deleted file mode 100644
index 450ef49..0000000
--- a/src/server/policy/arrange.rs
+++ /dev/null
@@ -1,1756 +0,0 @@
-// Pure layout computation extracted from `WindowManager::arrange_views()`.
-//
-// Functions here take plain-data snapshots and return placement plans; the
-// mechanism side (`window_manager.rs`) builds the snapshots from FFI state and
-// applies the plans to the scene graph. `arrange()` is the whole frame in one
-// call — the future `Policy::arrange` entry point — composed from the
-// per-section functions below it.
-
-use super::api::{DecorationSpec, Rect, WindowRole};
-use super::tiling::TilingMode;
-
-/// Which screen edge/region a status-bar window docks to.
-/// Set from the app_id suffix or config; `Unspecified` resolves to `TopLeft`.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum StatusEdge {
-    Unspecified,
-    TopLeft,
-    TopCenter,
-    TopRight,
-    BottomLeft,
-    BottomCenter,
-    BottomRight,
-    Left,
-    Right,
-}
-
-/// Snapshot of one status-bar window, in `self.windows` iteration order.
-/// Windows being interactively dragged are excluded before layout.
-pub struct StatusBarItem {
-    pub app_id: String,
-    pub edge: StatusEdge,
-    /// max(box_geom.width, box_geom.height) — the bar's previous major length.
-    pub prev_len: i32,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct StatusBarPlacement {
-    pub x: i32,
-    pub y: i32,
-    pub width: u32,
-    pub height: u32,
-}
-
-pub struct StatusBarLayoutParams {
-    /// The output's layout box.
-    pub output: Rect,
-    pub bar_height: u32,
-    pub hide_mode: bool,
-    /// Pixels of bar left peeking when hide_mode pushes top bars offscreen.
-    pub hide_mode_preview: i32,
-}
-
-/// The usable (tileable) area of an output: the layout box, shrunk by the
-/// layer-shell non-exclusive area and by one bar-height per screen edge that
-/// has a status bar docked to it. Top bars reserve no space in hide mode.
-///
-/// `non_exclusive` is relative to the output box; a zero-sized rect means no
-/// layer-shell exclusion. `status_edges` holds the raw edge of every live
-/// status-bar window (`Unspecified` resolves to `TopLeft`).
-pub fn compute_usable_area(
-    output: Rect,
-    non_exclusive: Rect,
-    bar_height: i32,
-    status_hide_mode: bool,
-    status_edges: &[StatusEdge],
-) -> Rect {
-    let mut usable = output;
-
-    if non_exclusive.width > 0 && non_exclusive.height > 0 {
-        usable.x = output.x + non_exclusive.x;
-        usable.y = output.y + non_exclusive.y;
-        usable.width = non_exclusive.width;
-        usable.height = non_exclusive.height;
-    }
-
-    let mut has_top = false;
-    let mut has_bottom = false;
-    let mut has_left = false;
-    let mut has_right = false;
-
-    for &edge in status_edges {
-        let edge = if edge == StatusEdge::Unspecified { StatusEdge::TopLeft } else { edge };
-        match edge {
-            StatusEdge::Unspecified | StatusEdge::TopLeft | StatusEdge::TopCenter | StatusEdge::TopRight => {
-                if !status_hide_mode {
-                    has_top = true;
-                }
-            }
-            StatusEdge::BottomLeft | StatusEdge::BottomCenter | StatusEdge::BottomRight => {
-                has_bottom = true;
-            }
-            StatusEdge::Left => {
-                has_left = true;
-            }
-            StatusEdge::Right => {
-                has_right = true;
-            }
-        }
-    }
-
-    if has_top {
-        usable.y += bar_height;
-        usable.height -= bar_height;
-    }
-    if has_bottom {
-        usable.height -= bar_height;
-    }
-    if has_left {
-        usable.x += bar_height;
-        usable.width -= bar_height;
-    }
-    if has_right {
-        usable.width -= bar_height;
-    }
-
-    usable
-}
-
-/// How `arrange_views` treats a window this frame. `Background`/`StatusBar`
-/// windows get fixed geometry regardless of visibility; `Hidden` windows are
-/// disabled in the scene; `Overlay` windows get the overlay slot unless they
-/// are mid-drag (then they arrange as `Normal`).
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum WindowClass {
-    Background,
-    StatusBar,
-    Hidden,
-    Overlay,
-    Normal,
-}
-
-pub fn classify_window(
-    role: WindowRole,
-    minimized: bool,
-    closing_or_init: bool,
-    mode: TilingMode,
-    is_moving: bool,
-) -> WindowClass {
-    match role {
-        WindowRole::Background => WindowClass::Background,
-        WindowRole::StatusBar => WindowClass::StatusBar,
-        _ => {
-            if minimized || closing_or_init {
-                WindowClass::Hidden
-            } else if mode == TilingMode::Overlay && !is_moving {
-                WindowClass::Overlay
-            } else {
-                WindowClass::Normal
-            }
-        }
-    }
-}
-
-/// Overlay windows keep 16px clear above for decorations.
-const OVERLAY_DEC_H: i32 = 16;
-
-/// Windows further than this many pixels outside the output are culled.
-const OFFSCREEN_MARGIN: f64 = 50.0;
-
-pub const OVERLAY_UNFOCUSED_OPACITY: f32 = 0.85;
-pub const NORMAL_UNFOCUSED_OPACITY: f32 = 0.90;
-
-pub fn window_opacity(is_focused: bool, opacity_enabled: bool, unfocused: f32) -> f32 {
-    if is_focused || !opacity_enabled { 1.0 } else { unfocused }
-}
-
-/// Per-output placement context: the physical box, the usable area from
-/// `compute_usable_area`, and the desktop viewport (pan/zoom).
-pub struct PlacementCtx {
-    pub phys: Rect,
-    pub usable: Rect,
-    pub pan_x: f64,
-    pub pan_y: f64,
-    pub zoom: f64,
-}
-
-impl PlacementCtx {
-    fn virtual_to_screen(&self, vx: f64, vy: f64) -> (i32, i32) {
-        (
-            self.phys.x + ((vx - self.pan_x) * self.zoom) as i32,
-            self.phys.y + ((vy - self.pan_y) * self.zoom) as i32,
-        )
-    }
-
-    fn is_offscreen(&self, x: i32, y: i32, scaled_w: f64, scaled_h: f64) -> bool {
-        let viewport_w = self.phys.width as f64;
-        let viewport_h = self.phys.height as f64;
-        (x as f64 + scaled_w + OFFSCREEN_MARGIN) < self.phys.x as f64
-            || (x as f64 - OFFSCREEN_MARGIN) > (self.phys.x as f64 + viewport_w)
-            || (y as f64 + scaled_h + OFFSCREEN_MARGIN) < self.phys.y as f64
-            || (y as f64 - OFFSCREEN_MARGIN) > (self.phys.y as f64 + viewport_h)
-    }
-}
-
-pub struct OverlaySnapshot {
-    pub box_geom: Rect,
-    pub min_width: i32,
-    pub is_cloud: bool,
-    pub ssd: bool,
-    pub decorations_size: (i32, i32),
-}
-
-pub struct OverlayParams {
-    pub overlay_width: i32,
-    pub border_gap: i32,
-    /// Server-side border width; the fresh overlay slot is border-inclusive.
-    pub border_width: i32,
-    pub position_right: bool,
-    pub cloud_position_default: Option<[i32; 2]>,
-}
-
-pub struct OverlayPlacement {
-    pub pos: (i32, i32),
-    /// Persistent geometry to store back on the window, if placement chose it.
-    pub box_geom_write: Option<Rect>,
-    pub virtual_pos: (f64, f64),
-    pub size: (u32, u32),
-}
-
-/// Place the primary overlay window: fresh windows get the configured overlay
-/// slot (left or right edge, full usable height); cloud windows snap to their
-/// configured default position; anything else keeps its stored geometry.
-///
-/// The fresh slot is border-inclusive: since borders draw outside the content
-/// box, the content is inset by the border width so slot + border stays inside
-/// the configured gaps. Stored geometry is treated like a floating window (the
-/// border extends beyond it).
-pub fn place_overlay_window(
-    snap: &OverlaySnapshot,
-    p: &OverlayParams,
-    ctx: &PlacementCtx,
-) -> OverlayPlacement {
-    let bw = p.border_width.max(0);
-    let g = p.border_gap;
-    let dec_h = std::cmp::max(bw, OVERLAY_DEC_H);
-
-    let mut sp_x = snap.box_geom.x;
-    let mut sp_y = snap.box_geom.y;
-    let mut sp_w = snap.box_geom.width;
-    let mut sp_h = snap.box_geom.height;
-    let mut box_geom_write = None;
-
-    if sp_w == 0 || sp_h == 0 {
-        sp_w = if snap.min_width > 32 {
-            std::cmp::max(p.overlay_width, snap.min_width)
-        } else {
-            p.overlay_width
-        };
-        sp_h = (ctx.usable.height - dec_h - 2 * g - 2 * bw).max(1);
-
-        sp_x = if p.position_right {
-            ctx.usable.x + ctx.usable.width - sp_w - g - bw
-        } else {
-            ctx.usable.x + g + bw
-        };
-        sp_y = ctx.usable.y + dec_h + g + bw;
-
-        box_geom_write = Some(Rect { x: sp_x, y: sp_y, width: sp_w, height: sp_h });
-    } else if snap.is_cloud {
-        if let Some(pos) = p.cloud_position_default {
-            sp_x = ctx.usable.x + pos[0];
-            sp_y = ctx.usable.y + pos[1];
-            box_geom_write = Some(Rect { x: sp_x, y: sp_y, width: sp_w, height: sp_h });
-        }
-    }
-
-    let vx = ctx.pan_x + (sp_x - ctx.phys.x) as f64 / ctx.zoom;
-    let vy = ctx.pan_y + (sp_y - ctx.phys.y) as f64 / ctx.zoom;
-
-    let mut target_w = sp_w;
-    let mut target_h = sp_h;
-    if !snap.ssd {
-        let (dec_w, dec_h) = snap.decorations_size;
-        target_w = (sp_w - dec_w).max(1);
-        target_h = (sp_h - dec_h).max(1);
-    }
-
-    OverlayPlacement {
-        pos: (sp_x, sp_y),
-        box_geom_write,
-        virtual_pos: (vx, vy),
-        size: (target_w as u32, target_h as u32),
-    }
-}
-
-/// State-machine step for entering/leaving Maximized mode. `Enter` tells the
-/// mechanism to save the given restore size (plus the window's current virtual
-/// position) and set `was_maximized`; `Exit` restores the saved geometry.
-/// Leaving with an invalid saved size does nothing (`was_maximized` stays set).
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum MaximizedTransition {
-    Enter { width: i32, height: i32 },
-    Exit { width: i32, height: i32, virtual_x: f64, virtual_y: f64 },
-}
-
-pub fn maximized_transition(
-    is_maximized_mode: bool,
-    was_maximized: bool,
-    box_size: (i32, i32),
-    min_size: (i32, i32),
-    saved_size: (i32, i32),
-    saved_virtual: (f64, f64),
-) -> Option<MaximizedTransition> {
-    if is_maximized_mode && !was_maximized {
-        let mut w = box_size.0;
-        let mut h = box_size.1;
-        if w <= 0 {
-            w = if min_size.0 > 32 { min_size.0 } else { 800 };
-        }
-        if h <= 0 {
-            h = if min_size.1 > 32 { min_size.1 } else { 600 };
-        }
-        Some(MaximizedTransition::Enter { width: w, height: h })
-    } else if !is_maximized_mode && was_maximized {
-        if saved_size.0 > 0 && saved_size.1 > 0 {
-            Some(MaximizedTransition::Exit {
-                width: saved_size.0,
-                height: saved_size.1,
-                virtual_x: saved_virtual.0,
-                virtual_y: saved_virtual.1,
-            })
-        } else {
-            None
-        }
-    } else {
-        None
-    }
-}
-
-pub struct NormalSnapshot {
-    pub mode: TilingMode,
-    pub box_geom: Rect,
-    pub min_size: (i32, i32),
-    pub virtual_pos: (f64, f64),
-    /// Live interactive-resize dimensions, if a resize op is in progress.
-    pub active_resize: Option<(u32, u32)>,
-    pub is_cloud: bool,
-    pub saved_maximized_size: (i32, i32),
-    pub saved_maximized_virtual: (f64, f64),
-}
-
-pub struct NormalParams {
-    pub gap_right: i32,
-    pub gap_top: i32,
-    /// Server-side border width; docked and grid-snapped placements are
-    /// border-inclusive.
-    pub border_width: i32,
-    pub cloud_position_default: Option<[i32; 2]>,
-    pub desktop_grid_scale: f64,
-}
-
-pub struct NormalPlacement {
-    pub pos: (i32, i32),
-    pub scale: f64,
-    pub size: (u32, u32),
-    /// Fullscreen windows report all edges tiled to the client.
-    pub tiled_all_edges: bool,
-    /// Offscreen-culling result; `None` leaves the window's flag untouched.
-    pub hidden: Option<bool>,
-    /// Maximized grid-snap moves the window's virtual position.
-    pub virtual_write: Option<(f64, f64)>,
-}
-
-/// Place a non-overlay window according to its tiling mode: `Popup` docks to
-/// the usable area's top-right (or the cloud default position), `Fullscreen`
-/// covers the physical output, `Maximized` snaps to cover every desktop-grid
-/// cell its saved geometry touches, and everything else pans on the virtual
-/// surface under the current viewport.
-pub fn place_normal_window(
-    snap: &NormalSnapshot,
-    p: &NormalParams,
-    ctx: &PlacementCtx,
-) -> NormalPlacement {
-    match snap.mode {
-        TilingMode::Popup => {
-            let fw = if snap.box_geom.width > 0 {
-                snap.box_geom.width
-            } else if snap.min_size.0 > 32 {
-                snap.min_size.0
-            } else {
-                360
-            };
-            let fh = if snap.box_geom.height > 0 {
-                snap.box_geom.height
-            } else if snap.min_size.1 > 32 {
-                snap.min_size.1
-            } else {
-                100
-            };
-
-            // The dock slot is border-inclusive: inset so the border stays
-            // within the configured gaps. Explicit cloud positions are taken
-            // as content positions verbatim.
-            let bw = p.border_width.max(0);
-            let (fx, fy) = if snap.is_cloud && p.cloud_position_default.is_some() {
-                let pos = p.cloud_position_default.unwrap();
-                (ctx.usable.x + pos[0], ctx.usable.y + pos[1])
-            } else {
-                (ctx.usable.x + ctx.usable.width - fw - p.gap_right - bw, ctx.usable.y + p.gap_top + bw)
-            };
-
-            NormalPlacement {
-                pos: (fx, fy),
-                scale: 1.0,
-                size: (fw as u32, fh as u32),
-                tiled_all_edges: false,
-                hidden: None,
-                virtual_write: None,
-            }
-        }
-        TilingMode::Fullscreen => NormalPlacement {
-            pos: (ctx.phys.x, ctx.phys.y),
-            scale: 1.0,
-            size: (ctx.phys.width as u32, ctx.phys.height as u32),
-            tiled_all_edges: true,
-            hidden: None,
-            virtual_write: None,
-        },
-        TilingMode::Maximized => {
-            // Resize to fully fill all desktop-grid cells the saved geometry
-            // is fully/partially inside of.
-            let scale = p.desktop_grid_scale;
-
-            let x1 = snap.saved_maximized_virtual.0;
-            let y1 = snap.saved_maximized_virtual.1;
-            let w = snap.saved_maximized_size.0 as f64;
-            let h = snap.saved_maximized_size.1 as f64;
-            let x2 = x1 + w;
-            let y2 = y1 + h;
-
-            let col_min = (x1 / scale).floor() as i32;
-            let col_max = ((x2 / scale).ceil() as i32 - 1).max(col_min);
-            let row_min = (y1 / scale).floor() as i32;
-            let row_max = ((y2 / scale).ceil() as i32 - 1).max(row_min);
-
-            let snapped_x1 = col_min as f64 * scale;
-            let snapped_x2 = (col_max + 1) as f64 * scale;
-            let snapped_y1 = row_min as f64 * scale;
-            let snapped_y2 = (row_max + 1) as f64 * scale;
-
-            // The snapped cell span is border-inclusive: the content is inset
-            // so the border stays inside the covered cells. Idempotent across
-            // frames because it re-derives from the saved geometry.
-            let bw = p.border_width.max(0) as f64;
-            let content_x = snapped_x1 + bw;
-            let content_y = snapped_y1 + bw;
-            let fw = (snapped_x2 - snapped_x1 - 2.0 * bw).max(1.0);
-            let fh = (snapped_y2 - snapped_y1 - 2.0 * bw).max(1.0);
-
-            let (final_x, final_y) = ctx.virtual_to_screen(content_x, content_y);
-
-            NormalPlacement {
-                pos: (final_x, final_y),
-                scale: ctx.zoom,
-                size: (fw as u32, fh as u32),
-                tiled_all_edges: false,
-                hidden: Some(ctx.is_offscreen(final_x, final_y, fw * ctx.zoom, fh * ctx.zoom)),
-                virtual_write: Some((content_x, content_y)),
-            }
-        }
-        _ => {
-            // Regular pannable window on the virtual surface.
-            let fw = if let Some(resize_size) = snap.active_resize {
-                resize_size.0 as i32
-            } else if snap.box_geom.width > 0 {
-                snap.box_geom.width
-            } else if snap.min_size.0 > 32 {
-                snap.min_size.0
-            } else {
-                800
-            };
-            let fh = if let Some(resize_size) = snap.active_resize {
-                resize_size.1 as i32
-            } else if snap.box_geom.height > 0 {
-                snap.box_geom.height
-            } else if snap.min_size.1 > 32 {
-                snap.min_size.1
-            } else {
-                600
-            };
-
-            let (final_x, final_y) = ctx.virtual_to_screen(snap.virtual_pos.0, snap.virtual_pos.1);
-
-            NormalPlacement {
-                pos: (final_x, final_y),
-                scale: ctx.zoom,
-                size: (fw as u32, fh as u32),
-                tiled_all_edges: false,
-                hidden: Some(ctx.is_offscreen(final_x, final_y, fw as f64 * ctx.zoom, fh as f64 * ctx.zoom)),
-                virtual_write: None,
-            }
-        }
-    }
-}
-
-const SPACING: i32 = 12;
-const MARGIN: i32 = 12;
-
-const LEFT_ORDER: &[&str] = &["viewport", "window"];
-const RIGHT_ORDER: &[&str] = &["tray", "cpu", "memory", "brightness", "volume", "battery", "clock"];
-
-fn left_sort_key(app_id: &str) -> usize {
-    let name = app_id.strip_prefix("cce-status-interface-left-")
-        .or_else(|| app_id.strip_prefix("cce-status-left-"))
-        .unwrap_or(app_id);
-    LEFT_ORDER.iter().position(|&m| m == name).unwrap_or(99)
-}
-
-fn right_sort_key(app_id: &str) -> usize {
-    let name = app_id.strip_prefix("cce-status-interface-right-")
-        .or_else(|| app_id.strip_prefix("cce-status-right-"))
-        .unwrap_or(app_id);
-    RIGHT_ORDER.iter().position(|&m| m == name).unwrap_or(99)
-}
-
-/// Bar length to use: previous major length, or 100 for a fresh bar.
-fn bar_len(prev_len: i32) -> u32 {
-    if prev_len > 0 { prev_len as u32 } else { 100 }
-}
-
-/// Lay out status-bar windows on one output: horizontal groups on the top and
-/// bottom edges (left/center/right within each), vertical stacks on the left
-/// and right edges, and full-width bars across the top.
-///
-/// Returns one placement per item, index-aligned with `items`.
-pub fn layout_status_bars(
-    items: &[StatusBarItem],
-    p: &StatusBarLayoutParams,
-) -> Vec<Option<StatusBarPlacement>> {
-    let wlr_box = p.output;
-    let bar_h = p.bar_height;
-    let spacing = SPACING;
-    let margin = MARGIN;
-
-    let mut top_left: Vec<usize> = Vec::new();
-    let mut top_center: Vec<usize> = Vec::new();
-    let mut top_right: Vec<usize> = Vec::new();
-    let mut bottom_left: Vec<usize> = Vec::new();
-    let mut bottom_center: Vec<usize> = Vec::new();
-    let mut bottom_right: Vec<usize> = Vec::new();
-    let mut left_side: Vec<usize> = Vec::new();
-    let mut right_side: Vec<usize> = Vec::new();
-    let mut full_top: Vec<usize> = Vec::new();
-
-    for (idx, item) in items.iter().enumerate() {
-        let edge = if item.edge == StatusEdge::Unspecified {
-            StatusEdge::TopLeft
-        } else {
-            item.edge
-        };
-        match edge {
-            StatusEdge::TopLeft => top_left.push(idx),
-            StatusEdge::TopCenter => top_center.push(idx),
-            StatusEdge::TopRight => top_right.push(idx),
-            StatusEdge::BottomLeft => bottom_left.push(idx),
-            StatusEdge::BottomCenter => bottom_center.push(idx),
-            StatusEdge::BottomRight => bottom_right.push(idx),
-            StatusEdge::Left => left_side.push(idx),
-            StatusEdge::Right => right_side.push(idx),
-            _ => full_top.push(idx),
-        }
-    }
-
-    log::info!("[ArrangeStatus] top_left_len={}, top_center_len={}, top_right_len={}, left_side_len={}", top_left.len(), top_center.len(), top_right.len(), left_side.len());
-
-    let sort_left = |list: &mut Vec<usize>| {
-        list.sort_by_key(|&i| left_sort_key(&items[i].app_id));
-    };
-    let sort_right = |list: &mut Vec<usize>| {
-        list.sort_by_key(|&i| right_sort_key(&items[i].app_id));
-    };
-
-    sort_left(&mut top_left);
-    sort_left(&mut top_center);
-    sort_right(&mut top_right);
-    sort_left(&mut bottom_left);
-    sort_left(&mut bottom_center);
-    sort_right(&mut bottom_right);
-
-    let mut placements: Vec<Option<StatusBarPlacement>> = vec![None; items.len()];
-
-    // 1. Top Edge
-    let status_y_top = if p.hide_mode {
-        wlr_box.y - (bar_h as i32 - p.hide_mode_preview)
-    } else {
-        wlr_box.y
-    };
-
-    let mut top_right_width_needed = 0;
-    for &i in &top_right {
-        let w = bar_len(items[i].prev_len);
-        top_right_width_needed += w as i32 + spacing;
-    }
-    let top_right_boundary = wlr_box.x + wlr_box.width - margin - top_right_width_needed;
-
-    // 1a. Left Group (TopLeft / nw)
-    let mut cur_left_x = wlr_box.x + margin;
-    for &i in &top_left {
-        let mut w = bar_len(items[i].prev_len);
-        let max_allowed_w = top_right_boundary - cur_left_x - spacing;
-        if w as i32 > max_allowed_w {
-            w = std::cmp::max(max_allowed_w, 20) as u32;
-        }
-        log::info!("[TopLeftLayout] app_id={} x={}, w={}", items[i].app_id, cur_left_x, w);
-        placements[i] = Some(StatusBarPlacement { x: cur_left_x, y: status_y_top, width: w, height: bar_h });
-        cur_left_x += w as i32 + spacing;
-    }
-
-    // 1b. Center Group (TopCenter / n)
-    let mut top_center_width_needed = 0;
-    for &i in &top_center {
-        let w = bar_len(items[i].prev_len);
-        top_center_width_needed += w as i32 + spacing;
-    }
-    if top_center_width_needed > 0 {
-        top_center_width_needed -= spacing;
-    }
-    let center_start_x = wlr_box.x + (wlr_box.width - top_center_width_needed) / 2;
-    let mut cur_center_x = std::cmp::max(center_start_x, cur_left_x + spacing);
-
-    for &i in &top_center {
-        let mut w = bar_len(items[i].prev_len);
-        let max_allowed_w = top_right_boundary - cur_center_x - spacing;
-        if w as i32 > max_allowed_w {
-            w = std::cmp::max(max_allowed_w, 20) as u32;
-        }
-        log::info!("[TopCenterLayout] app_id={} x={}, w={}", items[i].app_id, cur_center_x, w);
-        placements[i] = Some(StatusBarPlacement { x: cur_center_x, y: status_y_top, width: w, height: bar_h });
-        cur_center_x += w as i32 + spacing;
-    }
-
-    // 1c. Right Group (TopRight / ne)
-    let mut cur_right_x = wlr_box.x + wlr_box.width - margin;
-    for &i in top_right.iter().rev() {
-        let w = bar_len(items[i].prev_len);
-        let x = cur_right_x - w as i32;
-        placements[i] = Some(StatusBarPlacement { x, y: status_y_top, width: w, height: bar_h });
-        cur_right_x = x - spacing;
-    }
-
-    for &i in &full_top {
-        placements[i] = Some(StatusBarPlacement { x: wlr_box.x, y: status_y_top, width: wlr_box.width as u32, height: bar_h });
-    }
-
-    // 2. Bottom Edge
-    let status_y_bottom = wlr_box.y + wlr_box.height - bar_h as i32;
-
-    let mut bottom_right_width_needed = 0;
-    for &i in &bottom_right {
-        let w = bar_len(items[i].prev_len);
-        bottom_right_width_needed += w as i32 + spacing;
-    }
-    let bottom_right_boundary = wlr_box.x + wlr_box.width - margin - bottom_right_width_needed;
-
-    // 2a. Left Group (BottomLeft / sw)
-    let mut cur_left_x = wlr_box.x + margin;
-    for &i in &bottom_left {
-        let mut w = bar_len(items[i].prev_len);
-        let max_allowed_w = bottom_right_boundary - cur_left_x - spacing;
-        if w as i32 > max_allowed_w {
-            w = std::cmp::max(max_allowed_w, 20) as u32;
-        }
-        placements[i] = Some(StatusBarPlacement { x: cur_left_x, y: status_y_bottom, width: w, height: bar_h });
-        cur_left_x += w as i32 + spacing;
-    }
-
-    // 2b. Center Group (BottomCenter / s)
-    let mut bottom_center_width_needed = 0;
-    for &i in &bottom_center {
-        let w = bar_len(items[i].prev_len);
-        bottom_center_width_needed += w as i32 + spacing;
-    }
-    if bottom_center_width_needed > 0 {
-        bottom_center_width_needed -= spacing;
-    }
-    let center_start_x = wlr_box.x + (wlr_box.width - bottom_center_width_needed) / 2;
-    let mut cur_center_x = std::cmp::max(center_start_x, cur_left_x + spacing);
-
-    for &i in &bottom_center {
-        let mut w = bar_len(items[i].prev_len);
-        let max_allowed_w = bottom_right_boundary - cur_center_x - spacing;
-        if w as i32 > max_allowed_w {
-            w = std::cmp::max(max_allowed_w, 20) as u32;
-        }
-        placements[i] = Some(StatusBarPlacement { x: cur_center_x, y: status_y_bottom, width: w, height: bar_h });
-        cur_center_x += w as i32 + spacing;
-    }
-
-    // 2c. Right Group (BottomRight / se)
-    let mut cur_right_x = wlr_box.x + wlr_box.width - margin;
-    for &i in bottom_right.iter().rev() {
-        let w = bar_len(items[i].prev_len);
-        let x = cur_right_x - w as i32;
-        placements[i] = Some(StatusBarPlacement { x, y: status_y_bottom, width: w, height: bar_h });
-        cur_right_x = x - spacing;
-    }
-
-    // 3. Left Edge (Vertical stacking)
-    let mut left_total_height = 0;
-    for &i in &left_side {
-        let actual_h = bar_len(items[i].prev_len);
-        left_total_height += actual_h as i32;
-    }
-    if !left_side.is_empty() {
-        left_total_height += (left_side.len() as i32 - 1) * spacing;
-    }
-    let mut cur_left_y = wlr_box.y + (wlr_box.height - left_total_height) / 2;
-
-    for &i in &left_side {
-        let actual_h = bar_len(items[i].prev_len);
-        placements[i] = Some(StatusBarPlacement { x: wlr_box.x, y: cur_left_y, width: bar_h, height: actual_h });
-        cur_left_y += actual_h as i32 + spacing;
-    }
-
-    // 4. Right Edge (Vertical stacking)
-    let mut right_total_height = 0;
-    for &i in &right_side {
-        let actual_h = bar_len(items[i].prev_len);
-        right_total_height += actual_h as i32;
-    }
-    if !right_side.is_empty() {
-        right_total_height += (right_side.len() as i32 - 1) * spacing;
-    }
-    let mut cur_right_y = wlr_box.y + (wlr_box.height - right_total_height) / 2;
-
-    for &i in &right_side {
-        let actual_h = bar_len(items[i].prev_len);
-        placements[i] = Some(StatusBarPlacement { x: wlr_box.x + wlr_box.width - bar_h as i32, y: cur_right_y, width: bar_h, height: actual_h });
-        cur_right_y += actual_h as i32 + spacing;
-    }
-
-    placements
-}
-
-/// One enabled output, as `arrange` needs it.
-#[derive(Debug, Clone, Copy)]
-pub struct OutputSnapshot {
-    /// The output's layout box.
-    pub layout_box: Rect,
-    /// Layer-shell non-exclusive area, relative to the layout box; a
-    /// zero-sized rect means no exclusion.
-    pub non_exclusive: Rect,
-}
-
-/// Everything `arrange` reads about one window. Built once per frame by the
-/// mechanism side; seat-dependent answers (`being_moved`, `active_resize`)
-/// are captured here so the pure pass never queries mid-computation.
-#[derive(Debug, Clone)]
-pub struct WindowSnapshot {
-    pub app_id: Option<String>,
-    /// Only used verbatim in log lines.
-    pub title: Option<String>,
-    pub role: WindowRole,
-    pub minimized: bool,
-    /// Window state is Closing or Init.
-    pub closing_or_init: bool,
-    /// Resolved tiling mode (`get_mode_for_window`).
-    pub mode: TilingMode,
-    /// Mode-rule SSD override; pre-gated on `!mode_locked`.
-    pub rule_ssd: Option<bool>,
-    /// A seat is interactively moving this window.
-    pub being_moved: bool,
-    pub status_edge: StatusEdge,
-    pub is_focused: bool,
-    pub box_geom: Rect,
-    /// (min_width, min_height) size hints.
-    pub min_size: (i32, i32),
-    pub virtual_pos: (f64, f64),
-    pub active_resize: Option<(u32, u32)>,
-    /// Current `wm_requested.ssd`.
-    pub ssd: bool,
-    /// Raw decoration measurement (`measure_decorations`), not gated on
-    /// `ssd` — placement applies it only when the effective SSD is off.
-    pub decorations_size: (i32, i32),
-    pub was_maximized: bool,
-    pub saved_maximized_size: (i32, i32),
-    pub saved_maximized_virtual: (f64, f64),
-}
-
-/// Frame-wide inputs: config knobs plus the desktop viewport.
-pub struct ArrangeParams {
-    pub bar_height: i32,
-    pub status_hide_mode: bool,
-    pub hide_mode_preview: i32,
-    /// `status_background_blur > 0.001`.
-    pub status_blur: bool,
-    pub window_blur: bool,
-    /// `layout.window_opacity` — unfocused windows dim when set.
-    pub opacity_enabled: bool,
-    /// The configured server-side decoration, applied to every placed window.
-    pub decoration: DecorationSpec,
-    /// Border color for the focused window (falls back to the unfocused
-    /// color in config when unset).
-    pub border_color_focused: crate::policy::api::Rgba,
-    pub overlay: OverlayParams,
-    pub normal: NormalParams,
-    pub pan_x: f64,
-    pub pan_y: f64,
-    pub zoom: f64,
-}
-
-/// Write instructions for one window. `None` leaves the field untouched, so
-/// the mechanism apply loop is a flat sequence of `if let Some` writes.
-#[derive(Debug, Clone, Default, PartialEq)]
-pub struct WindowPlan {
-    /// Enable/disable the window's scene tree.
-    pub scene_enabled: Option<bool>,
-    pub hidden: Option<bool>,
-    pub tiling_mode: Option<TilingMode>,
-    /// `wm_requested.tiled` edge bitmask.
-    pub tiled: Option<u32>,
-    pub ssd: Option<bool>,
-    pub scale: Option<f64>,
-    /// `rendering_requested.{x,y}`.
-    pub pos: Option<(i32, i32)>,
-    /// `wm_requested.dimensions` and `.bounds`.
-    pub size: Option<(u32, u32)>,
-    /// Persistent geometry write-back.
-    pub box_geom: Option<Rect>,
-    pub virtual_pos: Option<(f64, f64)>,
-    pub blur: Option<bool>,
-    pub decoration: Option<DecorationSpec>,
-    pub opacity: Option<f32>,
-    pub was_maximized: Option<bool>,
-    /// Maximized-enter save: (restore size, restore virtual position).
-    pub saved_maximized: Option<((i32, i32), (f64, f64))>,
-}
-
-pub struct ArrangePlan {
-    /// Index-aligned with the input snapshots.
-    pub windows: Vec<WindowPlan>,
-    /// Whether each output's fallback background rect should be shown
-    /// (false while a wallpaper window exists).
-    pub background_rect_enabled: bool,
-}
-
-fn is_cloud_app(app_id: Option<&str>) -> bool {
-    app_id.map_or(false, |id| id.starts_with("cce-cloud"))
-}
-
-/// The decoration one placed window gets: the configured spec with the
-/// focused border color swapped in, and no border at all when fullscreen.
-fn decoration_for(p: &ArrangeParams, is_focused: bool, mode: TilingMode) -> DecorationSpec {
-    if mode == TilingMode::Fullscreen {
-        return DecorationSpec {
-            border_width: 0,
-            corner_radius: 0,
-            ..p.decoration
-        };
-    }
-    DecorationSpec {
-        border_color: if is_focused {
-            p.border_color_focused
-        } else {
-            p.decoration.border_color
-        },
-        ..p.decoration
-    }
-}
-
-/// The whole arrange pass as one pure function: classify every window, place
-/// overlays/normals/status bars per output, and return per-window write
-/// instructions.
-///
-/// The output loop is last-wins, like the mechanism loop it replaces: every
-/// output pass re-plans every window, so with several outputs the final plan
-/// reflects the last one. State that arranging itself evolves (box geometry,
-/// virtual position, SSD overrides, the maximized save/restore machine) is
-/// tracked on a working copy of the snapshots so later sections and later
-/// output passes read what earlier ones wrote — exactly as the mutating
-/// original did.
-pub fn arrange(
-    windows: &[WindowSnapshot],
-    outputs: &[OutputSnapshot],
-    p: &ArrangeParams,
-) -> ArrangePlan {
-    let mut state: Vec<WindowSnapshot> = windows.to_vec();
-    let mut plan: Vec<WindowPlan> = vec![WindowPlan::default(); windows.len()];
-
-    let has_wallpaper = state.iter().any(|w| w.role == WindowRole::Background);
-
-    for out in outputs {
-        let phys = out.layout_box;
-
-        let status_edges: Vec<StatusEdge> = state
-            .iter()
-            .filter(|w| w.role == WindowRole::StatusBar)
-            .map(|w| w.status_edge)
-            .collect();
-        let usable = compute_usable_area(
-            phys,
-            out.non_exclusive,
-            p.bar_height,
-            p.status_hide_mode,
-            &status_edges,
-        );
-        let ctx = PlacementCtx {
-            phys,
-            usable,
-            pan_x: p.pan_x,
-            pan_y: p.pan_y,
-            zoom: p.zoom,
-        };
-
-        let mut overlay_windows: Vec<usize> = Vec::new();
-        let mut normal_windows: Vec<usize> = Vec::new();
-
-        for (i, w) in state.iter_mut().enumerate() {
-            let class = classify_window(w.role, w.minimized, w.closing_or_init, w.mode, w.being_moved);
-            let wp = &mut plan[i];
-            match class {
-                WindowClass::Background => {
-                    wp.tiling_mode = Some(TilingMode::Status);
-                    wp.tiled = Some(0);
-                    wp.ssd = Some(false);
-                    w.ssd = false;
-                    wp.scale = Some(1.0);
-                    wp.scene_enabled = Some(true);
-                    wp.hidden = Some(false);
-                    wp.blur = Some(false);
-                    wp.pos = Some((phys.x, phys.y));
-                    wp.size = Some((phys.width as u32, phys.height as u32));
-                }
-                WindowClass::StatusBar => {
-                    wp.tiling_mode = Some(TilingMode::Status);
-                    wp.tiled = Some(0);
-                    wp.ssd = Some(false);
-                    w.ssd = false;
-                    wp.scale = Some(1.0);
-                    wp.scene_enabled = Some(true);
-                    wp.hidden = Some(false);
-                    wp.blur = Some(p.status_blur);
-                }
-                WindowClass::Hidden => {
-                    wp.scene_enabled = Some(false);
-                    wp.hidden = Some(true);
-                }
-                WindowClass::Overlay | WindowClass::Normal => {
-                    wp.scene_enabled = Some(true);
-                    wp.hidden = Some(false);
-                    wp.tiling_mode = Some(w.mode);
-                    if let Some(rule_ssd) = w.rule_ssd {
-                        wp.ssd = Some(rule_ssd);
-                        w.ssd = rule_ssd;
-                    }
-                    if class == WindowClass::Overlay {
-                        overlay_windows.push(i);
-                    } else {
-                        normal_windows.push(i);
-                    }
-                }
-            }
-        }
-
-        for (sp_idx, &i) in overlay_windows.iter().enumerate() {
-            if sp_idx == 0 {
-                let w = &state[i];
-                let placement = place_overlay_window(
-                    &OverlaySnapshot {
-                        box_geom: w.box_geom,
-                        min_width: w.min_size.0,
-                        is_cloud: is_cloud_app(w.app_id.as_deref()),
-                        ssd: w.ssd,
-                        decorations_size: w.decorations_size,
-                    },
-                    &p.overlay,
-                    &ctx,
-                );
-
-                if let Some(bg) = placement.box_geom_write {
-                    state[i].box_geom = bg;
-                }
-                state[i].virtual_pos = placement.virtual_pos;
-
-                let wp = &mut plan[i];
-                if let Some(bg) = placement.box_geom_write {
-                    wp.box_geom = Some(bg);
-                }
-                wp.pos = Some(placement.pos);
-                wp.scale = Some(1.0);
-                wp.virtual_pos = Some(placement.virtual_pos);
-                wp.size = Some(placement.size);
-                wp.tiled = Some(1 | 2 | 4 | 8);
-                wp.decoration = Some(decoration_for(p, state[i].is_focused, state[i].mode));
-                wp.blur = Some(p.window_blur);
-                wp.opacity = Some(window_opacity(
-                    state[i].is_focused,
-                    p.opacity_enabled,
-                    OVERLAY_UNFOCUSED_OPACITY,
-                ));
-            } else {
-                normal_windows.push(i);
-            }
-        }
-
-        // Manage entering/exiting Maximized state for normal windows.
-        for &i in &normal_windows {
-            let w = &state[i];
-            let transition = maximized_transition(
-                w.mode == TilingMode::Maximized,
-                w.was_maximized,
-                (w.box_geom.width, w.box_geom.height),
-                w.min_size,
-                w.saved_maximized_size,
-                w.saved_maximized_virtual,
-            );
-            match transition {
-                Some(MaximizedTransition::Enter { width, height }) => {
-                    let w = &mut state[i];
-                    w.saved_maximized_size = (width, height);
-                    w.saved_maximized_virtual = w.virtual_pos;
-                    w.was_maximized = true;
-                    let wp = &mut plan[i];
-                    wp.saved_maximized = Some(((width, height), w.saved_maximized_virtual));
-                    wp.was_maximized = Some(true);
-                    log::info!("[Maximized] Saved window {:?} geometry: {}x{} at ({}, {})",
-                        w.title.as_deref().unwrap_or(""),
-                        width, height,
-                        w.saved_maximized_virtual.0, w.saved_maximized_virtual.1
-                    );
-                }
-                Some(MaximizedTransition::Exit { width, height, virtual_x, virtual_y }) => {
-                    let w = &mut state[i];
-                    w.box_geom.width = width;
-                    w.box_geom.height = height;
-                    w.virtual_pos = (virtual_x, virtual_y);
-                    w.was_maximized = false;
-                    let wp = &mut plan[i];
-                    wp.box_geom = Some(w.box_geom);
-                    wp.virtual_pos = Some((virtual_x, virtual_y));
-                    wp.was_maximized = Some(false);
-                    wp.size = Some((width as u32, height as u32));
-                    log::info!("[Maximized] Restored window {:?} geometry: {}x{} at ({}, {})",
-                        w.title.as_deref().unwrap_or(""),
-                        width, height, virtual_x, virtual_y
-                    );
-                }
-                None => {}
-            }
-        }
-
-        // Arrange normal windows on the virtual surface.
-        for &i in &normal_windows {
-            let w = &state[i];
-            let mode = w.mode;
-            let placement = place_normal_window(
-                &NormalSnapshot {
-                    mode: w.mode,
-                    box_geom: w.box_geom,
-                    min_size: w.min_size,
-                    virtual_pos: w.virtual_pos,
-                    active_resize: w.active_resize,
-                    is_cloud: is_cloud_app(w.app_id.as_deref()),
-                    saved_maximized_size: w.saved_maximized_size,
-                    saved_maximized_virtual: w.saved_maximized_virtual,
-                },
-                &p.normal,
-                &ctx,
-            );
-
-            let is_focused = w.is_focused;
-            if let Some(v) = placement.virtual_write {
-                state[i].virtual_pos = v;
-            }
-
-            let wp = &mut plan[i];
-            wp.pos = Some(placement.pos);
-            wp.scale = Some(placement.scale);
-            if let Some(v) = placement.virtual_write {
-                wp.virtual_pos = Some(v);
-            }
-            if let Some(hidden) = placement.hidden {
-                wp.hidden = Some(hidden);
-            }
-            wp.size = Some(placement.size);
-            if placement.tiled_all_edges {
-                wp.tiled = Some(1 | 2 | 4 | 8);
-            }
-            wp.decoration = Some(decoration_for(p, is_focused, mode));
-            wp.blur = Some(p.window_blur);
-            wp.opacity = Some(window_opacity(is_focused, p.opacity_enabled, NORMAL_UNFOCUSED_OPACITY));
-        }
-
-        // Position status bar windows on this output, excluding any being
-        // interactively dragged.
-        let mut status_items: Vec<StatusBarItem> = Vec::new();
-        let mut status_idxs: Vec<usize> = Vec::new();
-        for (i, w) in state.iter().enumerate() {
-            if w.closing_or_init {
-                continue;
-            }
-            if let Some(app_id) = &w.app_id {
-                if app_id.starts_with("cce-status") {
-                    if w.being_moved {
-                        continue;
-                    }
-                    log::info!("[ArrangeStatus] app_id={} status_edge={:?}", app_id, w.status_edge);
-                    status_items.push(StatusBarItem {
-                        app_id: app_id.clone(),
-                        edge: w.status_edge,
-                        prev_len: std::cmp::max(w.box_geom.width, w.box_geom.height),
-                    });
-                    status_idxs.push(i);
-                }
-            }
-        }
-
-        let placements = layout_status_bars(
-            &status_items,
-            &StatusBarLayoutParams {
-                output: phys,
-                bar_height: p.bar_height as u32,
-                hide_mode: p.status_hide_mode,
-                hide_mode_preview: p.hide_mode_preview,
-            },
-        );
-
-        for (&i, placement) in status_idxs.iter().zip(placements.iter()) {
-            if let Some(pl) = placement {
-                plan[i].pos = Some((pl.x, pl.y));
-                plan[i].size = Some((pl.width, pl.height));
-            }
-        }
-    }
-
-    ArrangePlan {
-        windows: plan,
-        background_rect_enabled: !has_wallpaper,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn params() -> StatusBarLayoutParams {
-        StatusBarLayoutParams {
-            output: Rect { x: 0, y: 0, width: 1920, height: 1080 },
-            bar_height: 30,
-            hide_mode: false,
-            hide_mode_preview: 5,
-        }
-    }
-
-    fn item(app_id: &str, edge: StatusEdge, prev_len: i32) -> StatusBarItem {
-        StatusBarItem { app_id: app_id.to_string(), edge, prev_len }
-    }
-
-    #[test]
-    fn top_groups_flow_from_edges() {
-        let items = vec![
-            item("cce-status-left-viewport", StatusEdge::TopLeft, 0),
-            item("cce-status-left-window", StatusEdge::TopLeft, 0),
-            item("cce-status-right-clock", StatusEdge::TopRight, 200),
-        ];
-        let p = layout_status_bars(&items, &params());
-        // Left group flows right from the margin; fresh bars default to width 100.
-        assert_eq!(p[0], Some(StatusBarPlacement { x: 12, y: 0, width: 100, height: 30 }));
-        assert_eq!(p[1], Some(StatusBarPlacement { x: 124, y: 0, width: 100, height: 30 }));
-        // Right group is placed from the right edge inward.
-        assert_eq!(p[2], Some(StatusBarPlacement { x: 1908 - 200, y: 0, width: 200, height: 30 }));
-    }
-
-    #[test]
-    fn right_group_sorted_by_module_order() {
-        let items = vec![
-            item("cce-status-right-clock", StatusEdge::TopRight, 100),
-            item("cce-status-right-battery", StatusEdge::TopRight, 100),
-        ];
-        let p = layout_status_bars(&items, &params());
-        // RIGHT_ORDER puts battery before clock left-to-right, so clock hugs the edge.
-        assert_eq!(p[0].unwrap().x, 1808);
-        assert_eq!(p[1].unwrap().x, 1808 - 12 - 100);
-    }
-
-    #[test]
-    fn hide_mode_pushes_top_bars_offscreen_with_preview() {
-        let mut prm = params();
-        prm.hide_mode = true;
-        let items = vec![item("cce-status-left-viewport", StatusEdge::Unspecified, 0)];
-        let p = layout_status_bars(&items, &prm);
-        // Unspecified resolves to TopLeft; y = 0 - (30 - 5).
-        assert_eq!(p[0].unwrap().y, -25);
-    }
-
-    #[test]
-    fn usable_area_reserves_bar_edges() {
-        let output = Rect { x: 0, y: 0, width: 1920, height: 1080 };
-        let no_excl = Rect { x: 0, y: 0, width: 0, height: 0 };
-
-        // No bars, no exclusion: the full output.
-        assert_eq!(compute_usable_area(output, no_excl, 30, false, &[]), output);
-
-        // Top + left bars each reserve one bar-height.
-        let edges = [StatusEdge::TopLeft, StatusEdge::Left];
-        assert_eq!(
-            compute_usable_area(output, no_excl, 30, false, &edges),
-            Rect { x: 30, y: 30, width: 1890, height: 1050 }
-        );
-
-        // Hide mode releases the top reservation but not the others.
-        assert_eq!(
-            compute_usable_area(output, no_excl, 30, true, &edges),
-            Rect { x: 30, y: 0, width: 1890, height: 1080 }
-        );
-
-        // Layer-shell non-exclusive area applies before bar reservations.
-        let excl = Rect { x: 10, y: 20, width: 1900, height: 1040 };
-        assert_eq!(
-            compute_usable_area(output, excl, 30, false, &[StatusEdge::BottomCenter]),
-            Rect { x: 10, y: 20, width: 1900, height: 1010 }
-        );
-    }
-
-    #[test]
-    fn window_roles_from_app_id() {
-        assert_eq!(WindowRole::from_app_id(Some("cce-wallpaper")), WindowRole::Background);
-        assert_eq!(WindowRole::from_app_id(Some("cce-status-interface-right-clock")), WindowRole::StatusBar);
-        assert_eq!(WindowRole::from_app_id(Some("firefox")), WindowRole::Normal);
-        assert_eq!(WindowRole::from_app_id(None), WindowRole::Normal);
-    }
-
-    #[test]
-    fn classification_precedence() {
-        // Role wins over visibility: a minimized wallpaper still arranges as background.
-        assert_eq!(
-            classify_window(WindowRole::Background, true, true, TilingMode::Floating, false),
-            WindowClass::Background
-        );
-        assert_eq!(
-            classify_window(WindowRole::StatusBar, false, false, TilingMode::Floating, false),
-            WindowClass::StatusBar
-        );
-        // Minimized or closing/init normal windows are hidden.
-        assert_eq!(
-            classify_window(WindowRole::Normal, true, false, TilingMode::Grid, false),
-            WindowClass::Hidden
-        );
-        // Overlay mode gets the overlay slot — unless mid-drag.
-        assert_eq!(
-            classify_window(WindowRole::Normal, false, false, TilingMode::Overlay, false),
-            WindowClass::Overlay
-        );
-        assert_eq!(
-            classify_window(WindowRole::Normal, false, false, TilingMode::Overlay, true),
-            WindowClass::Normal
-        );
-        assert_eq!(
-            classify_window(WindowRole::Normal, false, false, TilingMode::Cascade, false),
-            WindowClass::Normal
-        );
-    }
-
-    fn ctx() -> PlacementCtx {
-        PlacementCtx {
-            phys: Rect { x: 0, y: 0, width: 1920, height: 1080 },
-            usable: Rect { x: 0, y: 30, width: 1920, height: 1050 },
-            pan_x: 0.0,
-            pan_y: 0.0,
-            zoom: 1.0,
-        }
-    }
-
-    #[test]
-    fn fresh_overlay_gets_configured_slot() {
-        let placement = place_overlay_window(
-            &OverlaySnapshot {
-                box_geom: Rect { x: 0, y: 0, width: 0, height: 0 },
-                min_width: 0,
-                is_cloud: false,
-                ssd: true,
-                decorations_size: (0, 16),
-            },
-            &OverlayParams {
-                overlay_width: 400,
-                border_gap: 8,
-                border_width: 0,
-                position_right: true,
-                cloud_position_default: None,
-            },
-            &ctx(),
-        );
-        // Right slot: x = usable right edge - width - gap; height fills the
-        // usable area minus the 16px decoration strip and both gaps.
-        assert_eq!(placement.pos, (1512, 54));
-        assert_eq!(placement.size, (400, 1018));
-        assert_eq!(placement.box_geom_write, Some(Rect { x: 1512, y: 54, width: 400, height: 1018 }));
-        assert_eq!(placement.virtual_pos, (1512.0, 54.0));
-    }
-
-    #[test]
-    fn fresh_overlay_slot_insets_by_border_width() {
-        let placement = place_overlay_window(
-            &OverlaySnapshot {
-                box_geom: Rect { x: 0, y: 0, width: 0, height: 0 },
-                min_width: 0,
-                is_cloud: false,
-                ssd: true,
-                decorations_size: (0, 16),
-            },
-            &OverlayParams {
-                overlay_width: 400,
-                border_gap: 8,
-                border_width: 4,
-                position_right: true,
-                cloud_position_default: None,
-            },
-            &ctx(),
-        );
-        // Content sits one border width inside the zero-width slot on every
-        // side, so the border lands within the gaps.
-        assert_eq!(placement.pos, (1512 - 4, 54 + 4));
-        assert_eq!(placement.size, (400, 1018 - 8));
-    }
-
-    #[test]
-    fn overlay_without_ssd_shrinks_by_decorations() {
-        let placement = place_overlay_window(
-            &OverlaySnapshot {
-                box_geom: Rect { x: 100, y: 100, width: 400, height: 500 },
-                min_width: 0,
-                is_cloud: false,
-                ssd: false,
-                decorations_size: (2, 18),
-            },
-            &OverlayParams { overlay_width: 400, border_gap: 8, border_width: 0, position_right: false, cloud_position_default: None },
-            &ctx(),
-        );
-        // Existing geometry is kept; the client is sized minus decorations.
-        assert_eq!(placement.pos, (100, 100));
-        assert_eq!(placement.size, (398, 482));
-        assert_eq!(placement.box_geom_write, None);
-    }
-
-    #[test]
-    fn maximized_transitions() {
-        // Entering with no usable geometry falls back to 800x600.
-        assert_eq!(
-            maximized_transition(true, false, (0, 0), (0, 0), (0, 0), (0.0, 0.0)),
-            Some(MaximizedTransition::Enter { width: 800, height: 600 })
-        );
-        // Entering keeps real geometry.
-        assert_eq!(
-            maximized_transition(true, false, (640, 480), (0, 0), (0, 0), (0.0, 0.0)),
-            Some(MaximizedTransition::Enter { width: 640, height: 480 })
-        );
-        // Steady states do nothing.
-        assert_eq!(maximized_transition(true, true, (640, 480), (0, 0), (640, 480), (0.0, 0.0)), None);
-        assert_eq!(maximized_transition(false, false, (640, 480), (0, 0), (0, 0), (0.0, 0.0)), None);
-        // Exit restores the saved geometry; invalid saved size is a no-op.
-        assert_eq!(
-            maximized_transition(false, true, (0, 0), (0, 0), (640, 480), (10.0, 20.0)),
-            Some(MaximizedTransition::Exit { width: 640, height: 480, virtual_x: 10.0, virtual_y: 20.0 })
-        );
-        assert_eq!(maximized_transition(false, true, (0, 0), (0, 0), (0, 480), (10.0, 20.0)), None);
-    }
-
-    #[test]
-    fn maximized_snaps_to_grid_cells() {
-        let snap = NormalSnapshot {
-            mode: TilingMode::Maximized,
-            box_geom: Rect { x: 0, y: 0, width: 100, height: 50 },
-            min_size: (0, 0),
-            virtual_pos: (150.0, 120.0),
-            active_resize: None,
-            is_cloud: false,
-            saved_maximized_size: (100, 50),
-            saved_maximized_virtual: (150.0, 120.0),
-        };
-        let p = NormalParams { gap_right: 10, gap_top: 6, border_width: 0, cloud_position_default: None, desktop_grid_scale: 100.0 };
-        let placement = place_normal_window(&snap, &p, &ctx());
-        // Saved geometry spans grid columns 1-2 and row 1 → snapped to
-        // (100,100) with size 200x100.
-        assert_eq!(placement.virtual_write, Some((100.0, 100.0)));
-        assert_eq!(placement.pos, (100, 100));
-        assert_eq!(placement.size, (200, 100));
-        assert_eq!(placement.hidden, Some(false));
-        assert_eq!(placement.scale, 1.0);
-    }
-
-    #[test]
-    fn maximized_insets_by_border_width() {
-        let snap = NormalSnapshot {
-            mode: TilingMode::Maximized,
-            box_geom: Rect { x: 0, y: 0, width: 100, height: 50 },
-            min_size: (0, 0),
-            virtual_pos: (150.0, 120.0),
-            active_resize: None,
-            is_cloud: false,
-            saved_maximized_size: (100, 50),
-            saved_maximized_virtual: (150.0, 120.0),
-        };
-        let p = NormalParams { gap_right: 10, gap_top: 6, border_width: 4, cloud_position_default: None, desktop_grid_scale: 100.0 };
-        let placement = place_normal_window(&snap, &p, &ctx());
-        // Same cell span as without borders (100,100)+200x100, with the
-        // content inset so the border stays inside the covered cells.
-        assert_eq!(placement.virtual_write, Some((104.0, 104.0)));
-        assert_eq!(placement.pos, (104, 104));
-        assert_eq!(placement.size, (192, 92));
-    }
-
-    #[test]
-    fn popup_docks_top_right_of_usable_area() {
-        let snap = NormalSnapshot {
-            mode: TilingMode::Popup,
-            box_geom: Rect { x: 0, y: 0, width: 0, height: 0 },
-            min_size: (0, 0),
-            virtual_pos: (0.0, 0.0),
-            active_resize: None,
-            is_cloud: false,
-            saved_maximized_size: (0, 0),
-            saved_maximized_virtual: (0.0, 0.0),
-        };
-        let p = NormalParams { gap_right: 10, gap_top: 6, border_width: 0, cloud_position_default: None, desktop_grid_scale: 100.0 };
-        let placement = place_normal_window(&snap, &p, &ctx());
-        // Defaults to 360x100, docked inside the usable area (below the bar).
-        assert_eq!(placement.pos, (1920 - 360 - 10, 30 + 6));
-        assert_eq!(placement.size, (360, 100));
-        assert_eq!(placement.hidden, None);
-
-        // With a border, the dock position insets so the border stays inside
-        // the gaps; the popup keeps its size.
-        let p = NormalParams { border_width: 4, ..p };
-        let placement = place_normal_window(&snap, &p, &ctx());
-        assert_eq!(placement.pos, (1920 - 360 - 10 - 4, 30 + 6 + 4));
-        assert_eq!(placement.size, (360, 100));
-    }
-
-    #[test]
-    fn pannable_window_follows_viewport() {
-        let snap = NormalSnapshot {
-            mode: TilingMode::Cascade,
-            box_geom: Rect { x: 0, y: 0, width: 640, height: 480 },
-            min_size: (0, 0),
-            virtual_pos: (100.0, 200.0),
-            active_resize: None,
-            is_cloud: false,
-            saved_maximized_size: (0, 0),
-            saved_maximized_virtual: (0.0, 0.0),
-        };
-        let p = NormalParams { gap_right: 10, gap_top: 6, border_width: 0, cloud_position_default: None, desktop_grid_scale: 100.0 };
-        let mut c = ctx();
-        c.pan_x = 50.0;
-        c.pan_y = 100.0;
-        c.zoom = 2.0;
-        let placement = place_normal_window(&snap, &p, &c);
-        assert_eq!(placement.pos, (100, 200));
-        assert_eq!(placement.scale, 2.0);
-        assert_eq!(placement.size, (640, 480));
-        assert_eq!(placement.hidden, Some(false));
-
-        // Pan far enough away and the window is culled.
-        c.pan_x = 5000.0;
-        let placement = place_normal_window(&snap, &p, &c);
-        assert_eq!(placement.hidden, Some(true));
-
-        // Interactive resize dimensions override stored geometry.
-        let resizing = NormalSnapshot { active_resize: Some((800, 600)), ..snap };
-        c.pan_x = 50.0;
-        let placement = place_normal_window(&resizing, &p, &c);
-        assert_eq!(placement.size, (800, 600));
-    }
-
-    #[test]
-    fn opacity_policy() {
-        assert_eq!(window_opacity(true, true, OVERLAY_UNFOCUSED_OPACITY), 1.0);
-        assert_eq!(window_opacity(false, false, OVERLAY_UNFOCUSED_OPACITY), 1.0);
-        assert_eq!(window_opacity(false, true, OVERLAY_UNFOCUSED_OPACITY), 0.85);
-        assert_eq!(window_opacity(false, true, NORMAL_UNFOCUSED_OPACITY), 0.90);
-    }
-
-    fn snap(app_id: &str) -> WindowSnapshot {
-        WindowSnapshot {
-            app_id: Some(app_id.to_string()),
-            title: None,
-            role: WindowRole::from_app_id(Some(app_id)),
-            minimized: false,
-            closing_or_init: false,
-            mode: TilingMode::Floating,
-            rule_ssd: None,
-            being_moved: false,
-            status_edge: StatusEdge::Unspecified,
-            is_focused: false,
-            box_geom: Rect { x: 0, y: 0, width: 640, height: 480 },
-            min_size: (0, 0),
-            virtual_pos: (100.0, 200.0),
-            active_resize: None,
-            ssd: true,
-            decorations_size: (0, 16),
-            was_maximized: false,
-            saved_maximized_size: (0, 0),
-            saved_maximized_virtual: (0.0, 0.0),
-        }
-    }
-
-    fn arrange_params() -> ArrangeParams {
-        ArrangeParams {
-            bar_height: 30,
-            status_hide_mode: false,
-            hide_mode_preview: 5,
-            status_blur: true,
-            window_blur: true,
-            opacity_enabled: true,
-            decoration: DecorationSpec {
-                border_width: 0,
-                border_color: crate::policy::api::Rgba([0.1, 0.2, 0.3, 0.4]),
-                corner_radius: 0,
-            },
-            border_color_focused: crate::policy::api::Rgba([0.9, 0.1, 0.1, 1.0]),
-            overlay: OverlayParams {
-                overlay_width: 400,
-                border_gap: 8,
-                border_width: 0,
-                position_right: true,
-                cloud_position_default: None,
-            },
-            normal: NormalParams {
-                gap_right: 10,
-                gap_top: 6,
-                border_width: 0,
-                cloud_position_default: None,
-                desktop_grid_scale: 100.0,
-            },
-            pan_x: 0.0,
-            pan_y: 0.0,
-            zoom: 1.0,
-        }
-    }
-
-    fn one_output() -> Vec<OutputSnapshot> {
-        vec![OutputSnapshot {
-            layout_box: Rect { x: 0, y: 0, width: 1920, height: 1080 },
-            non_exclusive: Rect { x: 0, y: 0, width: 0, height: 0 },
-        }]
-    }
-
-    #[test]
-    fn arrange_background_covers_output_and_disables_fallback_rect() {
-        let windows = vec![snap("cce-wallpaper"), snap("firefox")];
-        let plan = arrange(&windows, &one_output(), &arrange_params());
-
-        assert!(!plan.background_rect_enabled);
-        let wp = &plan.windows[0];
-        assert_eq!(wp.tiling_mode, Some(TilingMode::Status));
-        assert_eq!(wp.tiled, Some(0));
-        assert_eq!(wp.ssd, Some(false));
-        assert_eq!(wp.scene_enabled, Some(true));
-        assert_eq!(wp.hidden, Some(false));
-        assert_eq!(wp.blur, Some(false));
-        assert_eq!(wp.pos, Some((0, 0)));
-        assert_eq!(wp.size, Some((1920, 1080)));
-
-        // Without a wallpaper window, the fallback rect stays on.
-        let plan = arrange(&windows[1..], &one_output(), &arrange_params());
-        assert!(plan.background_rect_enabled);
-    }
-
-    #[test]
-    fn arrange_normal_window_pans_with_border_blur_opacity() {
-        let mut w = snap("firefox");
-        w.is_focused = false;
-        let plan = arrange(&[w], &one_output(), &arrange_params());
-
-        let wp = &plan.windows[0];
-        assert_eq!(wp.scene_enabled, Some(true));
-        assert_eq!(wp.tiling_mode, Some(TilingMode::Floating));
-        assert_eq!(wp.pos, Some((100, 200)));
-        assert_eq!(wp.scale, Some(1.0));
-        assert_eq!(wp.size, Some((640, 480)));
-        assert_eq!(wp.hidden, Some(false));
-        assert_eq!(wp.tiled, None);
-        assert_eq!(wp.decoration, Some(arrange_params().decoration));
-        assert_eq!(wp.blur, Some(true));
-        assert_eq!(wp.opacity, Some(NORMAL_UNFOCUSED_OPACITY));
-    }
-
-    #[test]
-    fn arrange_focused_window_gets_focused_border_color() {
-        let mut focused = snap("firefox");
-        focused.is_focused = true;
-        let unfocused = snap("terminal");
-        let p = arrange_params();
-        let plan = arrange(&[focused, unfocused], &one_output(), &p);
-
-        assert_eq!(plan.windows[0].decoration.unwrap().border_color, p.border_color_focused);
-        assert_eq!(plan.windows[1].decoration.unwrap().border_color, p.decoration.border_color);
-    }
-
-    #[test]
-    fn arrange_fullscreen_window_gets_no_border() {
-        let mut w = snap("mpv");
-        w.mode = TilingMode::Fullscreen;
-        let mut p = arrange_params();
-        p.decoration.border_width = 4;
-        p.decoration.corner_radius = 12;
-        let plan = arrange(&[w], &one_output(), &p);
-
-        let dec = plan.windows[0].decoration.unwrap();
-        assert_eq!(dec.border_width, 0);
-        assert_eq!(dec.corner_radius, 0);
-    }
-
-    #[test]
-    fn arrange_hidden_window_disabled_in_scene() {
-        let mut w = snap("firefox");
-        w.minimized = true;
-        let plan = arrange(&[w], &one_output(), &arrange_params());
-
-        let wp = &plan.windows[0];
-        assert_eq!(wp.scene_enabled, Some(false));
-        assert_eq!(wp.hidden, Some(true));
-        assert_eq!(wp.pos, None);
-        assert_eq!(wp.size, None);
-    }
-
-    #[test]
-    fn arrange_first_overlay_gets_slot_second_demotes_to_normal() {
-        let mut first = snap("scratchpad");
-        first.mode = TilingMode::Overlay;
-        first.box_geom = Rect { x: 0, y: 0, width: 0, height: 0 };
-        let mut second = snap("other-overlay");
-        second.mode = TilingMode::Overlay;
-
-        let plan = arrange(&[first, second], &one_output(), &arrange_params());
-
-        // Fresh overlay: right slot (no bars, so the full output is usable),
-        // box_geom written back.
-        let wp = &plan.windows[0];
-        assert_eq!(wp.pos, Some((1512, 24)));
-        assert_eq!(wp.size, Some((400, 1048)));
-        assert_eq!(wp.box_geom, Some(Rect { x: 1512, y: 24, width: 400, height: 1048 }));
-        assert_eq!(wp.tiled, Some(15));
-        assert_eq!(wp.opacity, Some(OVERLAY_UNFOCUSED_OPACITY));
-
-        // Second overlay arranges as a pannable normal window.
-        let wp = &plan.windows[1];
-        assert_eq!(wp.tiling_mode, Some(TilingMode::Overlay));
-        assert_eq!(wp.pos, Some((100, 200)));
-        assert_eq!(wp.size, Some((640, 480)));
-        assert_eq!(wp.opacity, Some(NORMAL_UNFOCUSED_OPACITY));
-    }
-
-    #[test]
-    fn arrange_rule_ssd_reaches_overlay_sizing() {
-        let mut w = snap("scratchpad");
-        w.mode = TilingMode::Overlay;
-        w.ssd = true;
-        w.rule_ssd = Some(false);
-        w.decorations_size = (2, 18);
-
-        let plan = arrange(&[w], &one_output(), &arrange_params());
-        let wp = &plan.windows[0];
-        // The rule override is planned and the client size shrinks by the
-        // decorations, proving the override was visible to placement.
-        assert_eq!(wp.ssd, Some(false));
-        assert_eq!(wp.size, Some((638, 462)));
-    }
-
-    #[test]
-    fn arrange_maximized_enter_saves_geometry() {
-        let mut w = snap("firefox");
-        w.mode = TilingMode::Maximized;
-        w.box_geom = Rect { x: 0, y: 0, width: 150, height: 50 };
-        w.virtual_pos = (150.0, 120.0);
-
-        let plan = arrange(&[w], &one_output(), &arrange_params());
-        let wp = &plan.windows[0];
-        assert_eq!(wp.was_maximized, Some(true));
-        assert_eq!(wp.saved_maximized, Some(((150, 50), (150.0, 120.0))));
-        // Grid snap: spans columns 1-2, row 1 of the 100px grid.
-        assert_eq!(wp.virtual_pos, Some((100.0, 100.0)));
-        assert_eq!(wp.pos, Some((100, 100)));
-        assert_eq!(wp.size, Some((200, 100)));
-    }
-
-    #[test]
-    fn arrange_maximized_exit_restores_saved_geometry() {
-        let mut w = snap("firefox");
-        w.mode = TilingMode::Floating;
-        w.was_maximized = true;
-        w.saved_maximized_size = (500, 400);
-        w.saved_maximized_virtual = (10.0, 20.0);
-
-        let plan = arrange(&[w], &one_output(), &arrange_params());
-        let wp = &plan.windows[0];
-        assert_eq!(wp.was_maximized, Some(false));
-        assert_eq!(wp.box_geom, Some(Rect { x: 0, y: 0, width: 500, height: 400 }));
-        // The restored geometry flows into the pannable placement.
-        assert_eq!(wp.virtual_pos, Some((10.0, 20.0)));
-        assert_eq!(wp.pos, Some((10, 20)));
-        assert_eq!(wp.size, Some((500, 400)));
-    }
-
-    #[test]
-    fn arrange_status_bar_placed_unless_dragged() {
-        let mut bar = snap("cce-status-left-viewport");
-        bar.status_edge = StatusEdge::TopLeft;
-        bar.box_geom = Rect { x: 0, y: 0, width: 200, height: 30 };
-
-        let plan = arrange(&[bar.clone()], &one_output(), &arrange_params());
-        let wp = &plan.windows[0];
-        assert_eq!(wp.tiling_mode, Some(TilingMode::Status));
-        assert_eq!(wp.blur, Some(true));
-        assert_eq!(wp.pos, Some((12, 0)));
-        assert_eq!(wp.size, Some((200, 30)));
-
-        // A dragged bar keeps whatever geometry it has: no placement writes.
-        bar.being_moved = true;
-        let plan = arrange(&[bar], &one_output(), &arrange_params());
-        let wp = &plan.windows[0];
-        assert_eq!(wp.pos, None);
-        assert_eq!(wp.size, None);
-    }
-
-    #[test]
-    fn arrange_multi_output_is_last_wins() {
-        let outputs = vec![
-            OutputSnapshot {
-                layout_box: Rect { x: 0, y: 0, width: 1920, height: 1080 },
-                non_exclusive: Rect { x: 0, y: 0, width: 0, height: 0 },
-            },
-            OutputSnapshot {
-                layout_box: Rect { x: 1920, y: 0, width: 1280, height: 720 },
-                non_exclusive: Rect { x: 0, y: 0, width: 0, height: 0 },
-            },
-        ];
-        let windows = vec![snap("cce-wallpaper"), snap("firefox")];
-        let plan = arrange(&windows, &outputs, &arrange_params());
-
-        // The background covers the second output — the last pass wins.
-        assert_eq!(plan.windows[0].pos, Some((1920, 0)));
-        assert_eq!(plan.windows[0].size, Some((1280, 720)));
-
-        // Maximize state machine only fires once across passes: entering on
-        // pass one must not re-enter (and re-save) on pass two.
-        let mut w = snap("firefox");
-        w.mode = TilingMode::Maximized;
-        w.box_geom = Rect { x: 0, y: 0, width: 150, height: 50 };
-        w.virtual_pos = (150.0, 120.0);
-        let plan = arrange(&[w], &outputs, &arrange_params());
-        // Saved from the original geometry, not the pass-one grid snap.
-        assert_eq!(plan.windows[0].saved_maximized, Some(((150, 50), (150.0, 120.0))));
-    }
-
-    #[test]
-    fn side_stacks_center_vertically() {
-        let items = vec![
-            item("cce-status-a", StatusEdge::Left, 200),
-            item("cce-status-b", StatusEdge::Left, 100),
-        ];
-        let p = layout_status_bars(&items, &params());
-        // Total stack: 200 + 12 + 100 = 312, centered in 1080 → starts at 384.
-        assert_eq!(p[0], Some(StatusBarPlacement { x: 0, y: 384, width: 30, height: 200 }));
-        assert_eq!(p[1], Some(StatusBarPlacement { x: 0, y: 596, width: 30, height: 100 }));
-    }
-}
diff --git a/src/server/policy/mod.rs b/src/server/policy/mod.rs
deleted file mode 100644
index 68e2e5e..0000000
--- a/src/server/policy/mod.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-// The window-management policy layer.
-//
-// This module is the seam for the planned compositor / window-manager split:
-// everything under `policy/` is pure Rust — no FFI, no raw wlroots pointers —
-// and is intended to eventually move to a separate `cce-window-manager` crate.
-// The mechanism side (scene graph, seats, shells, sockets) stays in this crate
-// and talks to policy code only through the types in `api`.
-//
-// Migration status:
-//   - `tiling`: pure layout formulas (moved here from `src/server/tiling.rs`).
-//   - `state`: persisted session state (moved here from `window_manager.rs`).
-//   - `arrange`: pure pieces of `arrange_views()`; currently the status-bar
-//     layout engine. `StatusEdge` lives here (re-exported via `window.rs`).
-//   - `api`: the `Policy` / `Compositor` trait boundary. Skeleton — defined but
-//     not yet driven by `window_manager.rs`.
-
-pub mod api;
-pub mod arrange;
-pub mod state;
-pub mod tiling;
diff --git a/src/server/policy/state.rs b/src/server/policy/state.rs
deleted file mode 100644
index 7f677cd..0000000
--- a/src/server/policy/state.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Persisted session state, saved to $XDG_STATE_HOME/cce/state.json on shutdown
-// and restored on startup. Pure data — serialization and matching logic only;
-// the save/load I/O lives in `window_manager.rs`.
-
-use super::tiling::TilingMode;
-
-#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
-pub struct SavedWindowState {
-    pub app_id: String,
-    pub title: String,
-    pub tiling_mode: TilingMode,
-    pub minimized: bool,
-    pub virtual_x: f64,
-    pub virtual_y: f64,
-    pub scale: f64,
-    pub width: u32,
-    pub height: u32,
-    pub cmdline: String,
-    #[serde(default)]
-    pub focused: bool,
-}
-
-#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
-pub struct SavedState {
-    pub desk_pan_x: f64,
-    pub desk_pan_y: f64,
-    pub desk_zoom: f64,
-    pub global_layout: TilingMode,
-    pub windows: Vec<SavedWindowState>,
-    #[serde(default)]
-    pub last_window_states: Vec<SavedWindowState>,
-}
diff --git a/src/server/policy/tiling.rs b/src/server/policy/tiling.rs
deleted file mode 100644
index 043e6f8..0000000
--- a/src/server/policy/tiling.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-// Tiling formulas ported from cce-client
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
-pub enum TilingMode {
-    Floating,
-    Cascade,
-    Grid,
-    Fullscreen,
-    Popup,
-    Overlay,
-    Status,
-    Maximized,
-}
-
-impl TilingMode {
-    pub fn as_str(&self) -> &'static str {
-        match self {
-            TilingMode::Floating => "Floating",
-            TilingMode::Cascade => "Cascade",
-            TilingMode::Grid => "Grid",
-            TilingMode::Fullscreen => "Fullscreen",
-            TilingMode::Popup => "Popup",
-            TilingMode::Overlay => "Overlay",
-            TilingMode::Status => "Status",
-            TilingMode::Maximized => "Maximized",
-        }
-    }
-}
-
-/// Cascade depth factor: each depth step multiplies channels by this
-pub const CASCADE_DEPTH_FACTOR: f64 = 0.80;
-
-/// Full alpha, byte-replicated for River's color format
-pub const CASCADE_ALPHA: u32 = 0xFFFFFFFFu32;
-
-/// Tile a window in cascade mode.
-pub fn tile_cascade(
-    screen_w: i32,
-    screen_h: i32,
-    _gap: i32,
-    gap_top: i32,
-    gap_left: i32,
-    gap_right: i32,
-    gap_bottom: i32,
-    bw: i32,
-    dec_h: i32,
-    cascade_offset: i32,
-    bar_height: i32,
-    n_cascade: i32,
-    idx: i32,
-) -> (i32, i32, i32, i32) {
-    let max_offsets = 5;
-    let eff_cascade = n_cascade.min(max_offsets);
-    let width = screen_w - gap_left - gap_right - bw * 2 - cascade_offset * (eff_cascade - 1);
-    let height = screen_h - bar_height - gap_top - gap_bottom - (dec_h + bw) - cascade_offset * (eff_cascade - 1);
-    let width = if width < 1 { 1 } else { width };
-    let height = if height < 1 { 1 } else { height };
-    let pos_idx = idx.min(max_offsets - 1);
-    let x = gap_left + bw + pos_idx * cascade_offset;
-    let y = bar_height + gap_top + dec_h + pos_idx * cascade_offset;
-    (x, y, width, height)
-}
-
-/// Tile a window in grid mode.
-pub fn tile_grid(
-    screen_w: i32,
-    screen_h: i32,
-    gap: i32,
-    gap_top: i32,
-    gap_left: i32,
-    gap_right: i32,
-    gap_bottom: i32,
-    bw: i32,
-    dec_h: i32,
-    bar_height: i32,
-    n_grid: i32,
-    idx: i32,
-) -> (i32, i32, i32, i32) {
-    let cols = if n_grid == 1 { 1i32 } else { 2i32 };
-    let row = idx / cols;
-    let col = idx % cols;
-    let rows = (n_grid + cols - 1) / cols;
-    let width = (screen_w - gap_left - gap_right - (cols - 1) * gap) / cols - 2 * bw;
-    let height = (screen_h - bar_height - gap_top - gap_bottom - (rows - 1) * gap) / rows - (dec_h + bw);
-    let width = if width < 1 { 1 } else { width };
-    let height = if height < 1 { 1 } else { height };
-    let x = gap_left + bw + col * (width + 2 * bw + gap);
-    let y = bar_height + gap_top + dec_h + row * (height + (dec_h + bw) + gap);
-    (x, y, width, height)
-}
-
-/// Tile a window in fullscreen mode.
-pub fn tile_fullscreen(
-    screen_w: i32,
-    screen_h: i32,
-    _gap_top: i32,
-    _gap_left: i32,
-    _gap_right: i32,
-    _gap_bottom: i32,
-    _bw: i32,
-    _bar_height: i32,
-) -> (i32, i32, i32, i32) {
-    (0, 0, screen_w, screen_h)
-}
-
-/// Interpolate a byte-replicated 32-bit channel (0xVVVVVVVV) by factor^depth.
-pub fn interp_channel(fp_channel: u32, factor: f64, depth: i32) -> u32 {
-    let base = (fp_channel & 0xFF) as u8;
-    let f = factor.powi(depth);
-    let val = ((base as f64) * f) as u8;
-    val as u32 * 0x01010101
-}
-
diff --git a/src/server/slotmap.rs b/src/server/slotmap.rs
deleted file mode 100644
index 5bab508..0000000
--- a/src/server/slotmap.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-// SPDX-FileCopyrightText: © 2025 Isaac Freund
-// SPDX-License-Identifier: 0BSD
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Key {
-    pub generation: u32,
-    pub index: u32,
-}
-
-enum SlotData<T> {
-    Value(T),
-    NextFree(u32),
-}
-
-struct Slot<T> {
-    generation: u32,
-    data: SlotData<T>,
-}
-
-pub struct SlotMap<T> {
-    slots: Vec<Slot<T>>,
-    count: u32,
-    first_free: u32,
-}
-
-impl<T> SlotMap<T> {
-    pub fn new() -> Self {
-        Self {
-            slots: Vec::new(),
-            count: 0,
-            first_free: 1,
-        }
-    }
-}
-
-impl<T> Default for SlotMap<T> {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl<T> SlotMap<T> {
-
-    pub fn put(&mut self, value: T) -> Key {
-        let index = self.first_free as usize;
-        if index < self.slots.len() {
-            let slot = &mut self.slots[index];
-            if let SlotData::NextFree(next) = slot.data {
-                self.first_free = next;
-            } else {
-                panic!("SlotMap state corrupted: expected NextFree slot");
-            }
-            slot.data = SlotData::Value(value);
-            self.count += 1;
-            Key {
-                generation: slot.generation,
-                index: index as u32,
-            }
-        } else {
-            self.slots.push(Slot {
-                generation: 0,
-                data: SlotData::Value(value),
-            });
-            let new_index = self.slots.len() - 1;
-            self.count += 1;
-            self.first_free += 1;
-            Key {
-                generation: 0,
-                index: new_index as u32,
-            }
-        }
-    }
-
-    pub fn get(&self, key: Key) -> Option<&T> {
-        let idx = key.index as usize;
-        if idx < self.slots.len() {
-            let slot = &self.slots[idx];
-            if slot.generation == key.generation {
-                if let SlotData::Value(ref val) = slot.data {
-                    return Some(val);
-                }
-            }
-        }
-        None
-    }
-
-    pub fn get_mut(&mut self, key: Key) -> Option<&mut T> {
-        let idx = key.index as usize;
-        if idx < self.slots.len() {
-            let slot = &mut self.slots[idx];
-            if slot.generation == key.generation {
-                if let SlotData::Value(ref mut val) = slot.data {
-                    return Some(val);
-                }
-            }
-        }
-        None
-    }
-
-    pub fn remove(&mut self, key: Key) -> Option<T> {
-        let idx = key.index as usize;
-        if idx < self.slots.len() {
-            let slot = &mut self.slots[idx];
-            if slot.generation == key.generation {
-                if let SlotData::Value(_) = slot.data {
-                    let old_data = std::mem::replace(&mut slot.data, SlotData::NextFree(self.first_free));
-                    slot.generation = slot.generation.wrapping_add(1);
-                    self.count -= 1;
-                    self.first_free = key.index;
-                    if let SlotData::Value(val) = old_data {
-                        return Some(val);
-                    }
-                }
-            }
-        }
-        None
-    }
-
-    pub fn count(&self) -> u32 {
-        self.count
-    }
-
-    pub fn iter(&self) -> Iter<'_, T> {
-        Iter {
-            slots: &self.slots,
-            index: 0,
-        }
-    }
-}
-
-pub struct Iter<'a, T> {
-    slots: &'a [Slot<T>],
-    index: usize,
-}
-
-impl<'a, T> Iterator for Iter<'a, T> {
-    type Item = &'a T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        while self.index < self.slots.len() {
-            let slot = &self.slots[self.index];
-            self.index += 1;
-            if let SlotData::Value(ref val) = slot.data {
-                return Some(val);
-            }
-        }
-        None
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_basic() {
-        let mut map = SlotMap::new();
-
-        let key5 = map.put(5);
-        assert_eq!(map.get(key5), Some(&5));
-
-        assert_eq!(map.remove(key5), Some(5));
-        assert_eq!(map.get(key5), None);
-
-        let key6 = map.put(6);
-        assert_eq!(map.get(key6), Some(&6));
-        assert_eq!(map.get(key5), None);
-
-        let key7 = map.put(7);
-        let key8 = map.put(8);
-        let key9 = map.put(9);
-
-        assert_eq!(map.get(key6), Some(&6));
-        assert_eq!(map.get(key7), Some(&7));
-        assert_eq!(map.get(key8), Some(&8));
-        assert_eq!(map.get(key9), Some(&9));
-
-        map.remove(key8);
-        assert_eq!(map.get(key8), None);
-        assert_eq!(map.get(key9), Some(&9));
-    }
-
-    #[test]
-    fn test_iteration() {
-        let mut map = SlotMap::new();
-        map.put(5);
-        map.put(6);
-        map.put(7);
-        map.put(8);
-        map.put(9);
-
-        let vals: Vec<&i32> = map.iter().collect();
-        assert_eq!(vals, vec![&5, &6, &7, &8, &9]);
-    }
-}