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

commitc0d151fd19c6deacba41746ad028cd68f7997855
parent502b548074
authorLucas Galante <[email protected]>
date2026-06-14 21:56
Fix monolithic window mapping, layer shell support, and rendering lifecycle assertions

 Cargo.lock                              |   99 +-
 Cargo.toml                              |    4 -
 src/bin/cce.rs                          |   12 +-
 src/{client => }/clearctl.rs            |   26 +-
 src/client/borders.rs                   |  210 --
 src/client/clear_inspector.rs           |  371 ---
 src/client/config.rs                    |  909 -------
 src/client/decorations.rs               | 1276 ---------
 src/client/input.rs                     | 1063 --------
 src/client/ipc.rs                       | 1571 -----------
 src/client/ipc_server.rs                |  131 -
 src/client/lib.rs                       |   22 -
 src/client/paths.rs                     |   97 -
 src/client/protocol.rs                  |   65 -
 src/client/restart.rs                   |  279 --
 src/client/run.rs                       |  328 ---
 src/client/state.rs                     |  475 ----
 src/client/status.rs                    |  100 -
 src/client/tiling.rs                    |  311 ---
 src/client/types.rs                     |  781 ------
 src/client/wayland.rs                   | 4436 -------------------------------
 src/client/wm.rs                        | 1538 -----------
 src/lib.rs                              |   54 +-
 src/server/config.rs                    |  693 +++++
 src/server/cursor.rs                    |   67 +
 src/server/ipc_server.rs                |   84 +
 src/server/keyboard_group.rs            |   68 +-
 src/server/run_server.rs                |   61 +-
 src/server/seat.rs                      |   51 +
 src/server/server.rs                    |    8 +-
 src/{client => server}/status_server.rs |  209 +-
 src/server/tiling.rs                    |  117 +
 src/server/window.rs                    |   28 +
 src/server/window_manager.rs            |  872 +++++-
 34 files changed, 2123 insertions(+), 14293 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 2378892..655f83d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -130,10 +130,6 @@ dependencies = [
  "serde_json",
  "tokio",
  "toml",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-scanner",
  "xkbcommon",
 ]
 
@@ -215,12 +211,6 @@ version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
 
-[[package]]
-name = "downcast-rs"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
-
 [[package]]
 name = "either"
 version = "1.16.0"
@@ -415,12 +405,6 @@ version = "0.4.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
 
-[[package]]
-name = "linux-raw-sys"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
-
 [[package]]
 name = "lock_api"
 version = "0.4.14"
@@ -571,15 +555,6 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "quick-xml"
-version = "0.39.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
-dependencies = [
- "memchr",
-]
-
 [[package]]
 name = "quote"
 version = "1.0.45"
@@ -642,23 +617,10 @@ dependencies = [
  "bitflags",
  "errno",
  "libc",
- "linux-raw-sys 0.4.15",
+ "linux-raw-sys",
  "windows-sys 0.59.0",
 ]
 
-[[package]]
-name = "rustix"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys 0.12.1",
- "windows-sys 0.61.2",
-]
-
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
@@ -865,63 +827,6 @@ version = "0.11.1+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 
-[[package]]
-name = "wayland-backend"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
-dependencies = [
- "cc",
- "downcast-rs",
- "rustix 1.1.4",
- "smallvec",
- "wayland-sys",
-]
-
-[[package]]
-name = "wayland-client"
-version = "0.31.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
-dependencies = [
- "bitflags",
- "rustix 1.1.4",
- "wayland-backend",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-protocols"
-version = "0.32.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f"
-dependencies = [
- "bitflags",
- "wayland-backend",
- "wayland-client",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-scanner"
-version = "0.31.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
-dependencies = [
- "proc-macro2",
- "quick-xml",
- "quote",
-]
-
-[[package]]
-name = "wayland-sys"
-version = "0.31.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
-dependencies = [
- "pkg-config",
-]
-
 [[package]]
 name = "which"
 version = "4.4.2"
@@ -931,7 +836,7 @@ dependencies = [
  "either",
  "home",
  "once_cell",
- "rustix 0.38.44",
+ "rustix",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 62cd018..6172850 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,10 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-wayland-client = "0.31"
-wayland-backend = "0.3"
-wayland-protocols = { version = "0.32", features = ["client", "unstable", "staging"] }
-wayland-scanner = "0.31"
 xkbcommon = "0.7"
 toml = "0.8"
 serde = { version = "1", features = ["derive"] }
diff --git a/src/bin/cce.rs b/src/bin/cce.rs
index 64cda9a..f49f0e3 100644
--- a/src/bin/cce.rs
+++ b/src/bin/cce.rs
@@ -5,18 +5,14 @@ fn main() {
     if args.len() > 1 {
         match args[1].as_str() {
             "client" => {
-                cce::run_client();
+                println!("cce: standalone client mode is deprecated in the monolithic architecture");
+                std::process::exit(0);
             }
             "control" => {
                 let mut control_args = vec!["clearctl".to_string()];
                 control_args.extend(args.iter().skip(2).cloned());
                 cce::run_clearctl(control_args);
             }
-            "inspect" | "inspector" => {
-                let mut inspect_args = vec!["clear-inspector".to_string()];
-                inspect_args.extend(args.iter().skip(2).cloned());
-                cce::run_inspector(inspect_args);
-            }
             "--help" | "-h" | "help" => {
                 print_help();
             }
@@ -33,8 +29,6 @@ fn print_help() {
     println!("usage: cce <subcommand> [options]");
     println!();
     println!("subcommands:");
-    println!("  (default)          Start the compositor server and embedded client");
-    println!("  client             Start the standalone Wayland window manager client");
+    println!("  (default)          Start the monolithic compositor and window manager");
     println!("  control            Run IPC control commands (e.g. cce control layout gap 10)");
-    println!("  inspect            Run the widget tree inspector");
 }
diff --git a/src/client/clearctl.rs b/src/clearctl.rs
similarity index 94%
rename from src/client/clearctl.rs
rename to src/clearctl.rs
index 992598c..a8b811a 100644
--- a/src/client/clearctl.rs
+++ b/src/clearctl.rs
@@ -1,25 +1,25 @@
-// clearctl — IPC client for cce-client
-
+// clearctl — IPC client for cce
+ 
 use std::env;
 use std::fs;
 use std::io::{Read, Write};
 use std::os::unix::net::UnixStream;
 use std::process;
-
+ 
 fn get_socket_path() -> String {
     match env::var("WAYLAND_DISPLAY") {
         Ok(display) => format!("/tmp/cce-client-{}.sock", display),
         Err(_) => "/tmp/cce-client.sock".to_string(),
     }
 }
-
+ 
 fn get_windows_path() -> String {
     match env::var("WAYLAND_DISPLAY") {
         Ok(display) => format!("/tmp/cce-client-windows-{}", display),
         Err(_) => "/tmp/cce-client-windows".to_string(),
     }
 }
-
+ 
 fn usage(name: &str, to_stderr: bool) {
     let print = |s: &str| {
         if to_stderr {
@@ -37,7 +37,7 @@ fn usage(name: &str, to_stderr: bool) {
     print("  close");
     print("  minimize");
     print("  focus-next");
-  print("  focus-window <app_id|title>");
+    print("  focus-window <app_id|title>");
     print("  expose");
     print("  windows");
     print("  exit");
@@ -64,27 +64,27 @@ fn usage(name: &str, to_stderr: bool) {
     print("  key-press <key>");
     print("  key-release <key>");
 }
-
+ 
 pub fn run_clearctl(args: Vec<String>) {
     if args.len() < 2 {
         usage(&args[0], true);
         process::exit(1);
     }
-
+ 
     if args[1] == "--help" || args[1] == "-h" || args[1] == "help" {
         usage(&args[0], false);
         return;
     }
-
+ 
     // Special case: "windows" reads the status file directly
     if args[1] == "windows" {
         match fs::read_to_string(get_windows_path()) {
             Ok(content) => print!("{}", content),
-            Err(_) => eprintln!("No windows info (cce-client may not be running)"),
+            Err(_) => eprintln!("No windows info (cce may not be running)"),
         }
         return;
     }
-
+ 
     // Connect to IPC socket
     let stream = match UnixStream::connect(get_socket_path()) {
         Ok(s) => s,
@@ -93,7 +93,7 @@ pub fn run_clearctl(args: Vec<String>) {
             process::exit(1);
         }
     };
-
+ 
     let mut stream = stream;
     // Build command string from args
     let cmd = args[1..].join(" ") + "\n";
@@ -101,7 +101,7 @@ pub fn run_clearctl(args: Vec<String>) {
         eprintln!("write: {}", e);
         process::exit(1);
     }
-
+ 
     // Read response
     let mut buf = [0u8; 4096];
     loop {
diff --git a/src/client/borders.rs b/src/client/borders.rs
deleted file mode 100644
index 5fbc3f7..0000000
--- a/src/client/borders.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-// Border color computation for cascade depth gradient and normal windows
-
-use crate::types::{TilingMode, WindowManager};
-
-/// Interpolate a single 8-bit channel toward black by (factor ^ depth).
-/// Returns a 32-bit value with the byte replicated across all 4 bytes,
-/// which is the format River expects (it divides by maxInt(u32) to get a float).
-pub fn interp_channel(fp_channel: u32, factor: f64, depth: i32) -> u32 {
-    let base = (fp_channel & 0xFF) as u8;
-    let mut f = 1.0_f64;
-    for _ in 0..depth {
-        f *= factor;
-    }
-    let val = (base as f64 * f) as u8;
-    // Replicate byte across all 4 bytes: 0xVV -> 0xVVVVVVVV
-    val as u32 * 0x01010101
-}
-
-/// Blend between two byte-replicated 32-bit channel values.
-/// factor=0.0 → pure bg, factor=1.0 → pure fg.
-fn blend_channel(bg_channel: u32, fg_channel: u32, factor: f64) -> u32 {
-    let bg = (bg_channel & 0xFF) as u8 as f64;
-    let fg = (fg_channel & 0xFF) as u8 as f64;
-    let val = (bg + (fg - bg) * factor) as u8;
-    val as u32 * 0x01010101
-}
-
-/// Alpha for borders: fully opaque, byte-replicated
-pub const ALPHA: u32 = 0xFFFFFFFF;
-
-/// Unfocused depth factor: each step away from the focused window reduces
-/// the blend factor by this multiplier, making the border color approach
-/// the background color.
-pub const UNFOCUSED_DEPTH_FACTOR: f64 = 0.70;
-
-/// Result of border color computation for a single window
-#[derive(Debug, Clone)]
-pub struct WindowBorders {
-    pub window_idx: usize,
-    pub edges: u32, // all edges = top | bottom | left | right
-    pub width: i32,
-    pub r: u32,
-    pub g: u32,
-    pub b: u32,
-    pub a: u32,
-}
-
-/// Compute border colors for all visible windows.
-/// The focused window gets the configured border_color.
-/// Unfocused windows get a color interpolated between the desktop
-/// background_color and border_color based on stack depth.
-pub fn compute_border_colors(state: &WindowManager) -> Vec<WindowBorders> {
-    let mut results = Vec::new();
-
-    // Find the focused window ID from the first non-removed seat
-    let focused_id = state
-        .seats
-        .iter()
-        .find(|s| !s.removed)
-        .and_then(|s| s.focused_window_id);
-
-    // Collect visible window indices (ordered from bottom to top of stack)
-    let visible: Vec<usize> = state
-        .windows
-        .iter()
-        .enumerate()
-        .filter(|(_, w)| (w.tags & state.active_tags) != 0 && !w.closed)
-        .map(|(i, _)| i)
-        .collect();
-
-    for (idx, win) in state.windows.iter().enumerate() {
-        if (win.tags & state.active_tags) == 0 {
-            continue;
-        }
-
-        let is_focused = focused_id.map_or(false, |fid| win.id == fid);
-
-        /*
-        eprintln!(
-            "[borders] win={} app_id={:?} is_focused={} border_r=#{:08x} border_g=#{:08x} border_b=#{:08x}",
-            win.id, win.app_id, is_focused,
-            state.layout.border_r,
-            state.layout.border_g,
-            state.layout.border_b,
-        );
-        */
-
-        let (r, g, b, mut a) = if win.tiling_mode == TilingMode::Popup {
-            // Popup windows have a transparent border
-            (0, 0, 0, 0)
-        } else if is_focused {
-            // Focused window: pure border color
-            (
-                state.layout.border_r,
-                state.layout.border_g,
-                state.layout.border_b,
-                state.layout.border_a,
-            )
-        } else {
-            // Unfocused window: blend background → border based on depth
-            let visible_mode: Vec<usize> = visible
-                .iter()
-                .cloned()
-                .filter(|&i| state.expose_visual_active || state.windows[i].tiling_mode == win.tiling_mode)
-                .collect();
-            let n_visible_mode = visible_mode.len();
-            let pos = visible_mode.iter().position(|&i| i == idx).unwrap_or(0);
-            let depth = (n_visible_mode - 1 - pos) as i32;
-            let mut factor = 1.0_f64;
-            for _ in 0..depth {
-                factor *= UNFOCUSED_DEPTH_FACTOR;
-            }
-            let r = blend_channel(state.layout.background_r, state.layout.border_r, factor);
-            let g = blend_channel(state.layout.background_g, state.layout.border_g, factor);
-            let b = blend_channel(state.layout.background_b, state.layout.border_b, factor);
-            let a = blend_channel(state.layout.background_a, state.layout.border_a, factor);
-            (r, g, b, a)
-        };
-
-        if win.tiling_mode == TilingMode::SidePanel {
-            let opacity_factor = state.layout.side_panel_border_opacity as f64 / 100.0;
-            let base_alpha = (a & 0xFF) as f64 * opacity_factor;
-            let val = base_alpha.round().clamp(0.0, 255.0) as u8;
-            a = val as u32 * 0x01010101;
-        }
-
-        let edges = 0b0000u32;
-        let width = 0;
-
-        /*
-        eprintln!(
-            "[borders]   -> r=#{:08x} g=#{:08x} b=#{:08x} a=#{:08x} width={}",
-            r, g, b, a, width,
-        );
-        */
-
-        results.push(WindowBorders {
-            window_idx: idx,
-            edges,
-            width,
-            r,
-            g,
-            b,
-            a,
-        });
-    }
-
-    results
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::types::{Window, ModeRule};
-
-    #[test]
-    fn test_interp_channel_depth0() {
-        // Depth 0 should return the base color unchanged
-        let result = interp_channel(0x5C5C5C5C, 0.80, 0);
-        assert_eq!(result, 0x5C5C5C5C);
-    }
-
-    #[test]
-    fn test_interp_channel_depth1() {
-        let result = interp_channel(0x90909090, 0.80, 1);
-        let expected = ((0x90 as f64 * 0.80) as u8) as u32 * 0x01010101;
-        assert_eq!(result, expected);
-    }
-
-    #[test]
-    fn test_border_edges_with_decorations() {
-        let mut wm = WindowManager::default();
-        wm.active_tags = 1;
-
-        // Window with client decorations OK (should have edges = 0b0000)
-        wm.windows.push(Window {
-            id: 1,
-            tiling_mode: TilingMode::Cascade,
-            tags: 1,
-            ..Default::default()
-        });
-
-        // Window with client decorations disabled via mode rule
-        wm.mode_rules.push(ModeRule {
-            mode: TilingMode::Cascade,
-            app_id_pattern: "no-decorations".to_string(),
-            title_pattern: None,
-            single_instance: false,
-            tag: 0,
-            circular: false,
-            ssd: Some(false), // Disable CSD
-        });
-        wm.windows.push(Window {
-            id: 2,
-            tiling_mode: TilingMode::Cascade,
-            app_id: Some("no-decorations".to_string()),
-            tags: 1,
-            ..Default::default()
-        });
-
-        let results = compute_border_colors(&wm);
-        assert_eq!(results.len(), 2);
-
-        // Window 1: no compositor-drawn borders
-        assert_eq!(results[0].edges, 0);
-
-        // Window 2: no compositor-drawn borders either (all borders are handled in client)
-        assert_eq!(results[1].edges, 0);
-    }
-}
diff --git a/src/client/clear_inspector.rs b/src/client/clear_inspector.rs
deleted file mode 100644
index 2e2becc..0000000
--- a/src/client/clear_inspector.rs
+++ /dev/null
@@ -1,371 +0,0 @@
-use wayland_client::{
-    globals::{registry_queue_init, GlobalListContents},
-    protocol::wl_registry,
-    Connection, Dispatch, QueueHandle,
-};
-use serde_json::Value;
-
-// Import generated client protocols from cce library
-use crate::protocol::clear_inspector::client::zclear_inspector_v1::{self, ZclearInspectorV1};
-
-struct InspectorState {
-    inspector: Option<ZclearInspectorV1>,
-    done: bool,
-    surfaces: Vec<InspectedSurface>,
-}
-
-struct InspectedSurface {
-    title: String,
-    app_id: String,
-    x: i32,
-    y: i32,
-    width: i32,
-    height: i32,
-    state: String,
-}
-
-impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for InspectorState {
-    fn event(
-        state: &mut Self,
-        registry: &wl_registry::WlRegistry,
-        event: wl_registry::Event,
-        _data: &GlobalListContents,
-        _conn: &Connection,
-        qh: &QueueHandle<Self>,
-    ) {
-        if let wl_registry::Event::Global { name, interface, version } = event {
-            println!("Debug Global: interface='{}', version={}, name={}", interface, version, name);
-            if interface == "zclear_inspector_v1" {
-                state.inspector = Some(registry.bind::<ZclearInspectorV1, _, _>(name, version, qh, ()));
-            }
-        }
-    }
-}
-
-impl Dispatch<ZclearInspectorV1, ()> for InspectorState {
-    fn event(
-        state: &mut Self,
-        _proxy: &ZclearInspectorV1,
-        event: zclear_inspector_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-    ) {
-        #[allow(unreachable_patterns)]
-        match event {
-            zclear_inspector_v1::Event::InspectedSurface { title, app_id, x, y, width, height, fd, len } => {
-                let mut surface_state = String::new();
-                if len > 0 {
-                    use std::io::Read;
-                    let file = std::fs::File::from(fd);
-                    let _ = file.take(len as u64).read_to_string(&mut surface_state);
-                }
-                state.surfaces.push(InspectedSurface {
-                    title,
-                    app_id,
-                    x,
-                    y,
-                    width,
-                    height,
-                    state: surface_state,
-                });
-            }
-            zclear_inspector_v1::Event::InspectedSurfaceDone => {
-                state.done = true;
-            }
-            _ => {}
-        }
-    }
-}
-
-fn get_socket_path() -> String {
-    match std::env::var("WAYLAND_DISPLAY") {
-        Ok(display) => format!("/tmp/cce-client-{}.sock", display),
-        Err(_) => "/tmp/cce-client.sock".to_string(),
-    }
-}
-
-fn send_ipc_commands(commands: &[String]) -> Result<Vec<String>, std::io::Error> {
-    use std::io::{Read, Write};
-    use std::os::unix::net::UnixStream;
-    let mut stream = UnixStream::connect(get_socket_path())?;
-    
-    let mut combined_cmd = String::new();
-    for cmd in commands {
-        combined_cmd.push_str(cmd);
-    }
-    
-    stream.write_all(combined_cmd.as_bytes())?;
-    
-    let mut buf = [0u8; 4096];
-    let mut response = String::new();
-    loop {
-        match stream.read(&mut buf) {
-            Ok(0) => break,
-            Ok(n) => {
-                response.push_str(&String::from_utf8_lossy(&buf[..n]));
-            }
-            Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
-            Err(e) => return Err(e),
-        }
-    }
-    
-    let replies = response.lines().map(|line| format!("{}\n", line)).collect();
-    Ok(replies)
-}
-
-fn find_widget_recursive(val: &Value, target_type: &str, target_label: Option<&str>) -> Option<(f64, f64, f64, f64)> {
-    if let Some(obj) = val.as_object() {
-        let type_match = obj.get("type")
-            .and_then(|t| t.as_str())
-            .map(|t| t == target_type)
-            .unwrap_or(false);
-            
-        let label_match = match target_label {
-            Some(lbl) => obj.get("label")
-                .and_then(|l| l.as_str())
-                .map(|l| l.eq_ignore_ascii_case(lbl) || l.to_lowercase().contains(&lbl.to_lowercase()))
-                .unwrap_or(false),
-            None => true,
-        };
-        
-        if type_match && label_match {
-            if let Some(rect_arr) = obj.get("rect").and_then(|r| r.as_array()) {
-                if rect_arr.len() == 4 {
-                    let rx = rect_arr[0].as_f64().unwrap_or(0.0);
-                    let ry = rect_arr[1].as_f64().unwrap_or(0.0);
-                    let rw = rect_arr[2].as_f64().unwrap_or(0.0);
-                    let rh = rect_arr[3].as_f64().unwrap_or(0.0);
-                    return Some((rx, ry, rw, rh));
-                }
-            }
-        }
-        
-        // Search children
-        if let Some(children) = obj.get("children").and_then(|c| c.as_array()) {
-            for child in children {
-                if let Some(coords) = find_widget_recursive(child, target_type, target_label) {
-                    return Some(coords);
-                }
-            }
-        }
-    } else if let Some(arr) = val.as_array() {
-        for item in arr {
-            if let Some(coords) = find_widget_recursive(item, target_type, target_label) {
-                return Some(coords);
-            }
-        }
-    }
-    None
-}
-
-fn print_usage(bin_name: &str) {
-    println!("Usage:");
-    println!("  {}                       List all inspected surfaces and their widget trees", bin_name);
-    println!("  {} click-widget --app-id <app_id> --type <widget_type> [--label <widget_label>]", bin_name);
-    println!();
-    println!("Options:");
-    println!("  -a, --app-id <app_id>       The Wayland application ID (e.g. clear-design-interface)");
-    println!("  -t, --type <widget_type>    The widget type (e.g. MenuItem, Button)");
-    println!("  -l, --label <widget_label>  Optional widget label to filter by");
-}
-
-pub fn run_inspector(args: Vec<String>) {
-    let bin_name = args.get(0).map(|s| s.as_str()).unwrap_or("clear-inspector");
-    
-    let mut app_id = None;
-    let mut widget_type = None;
-    let mut widget_label = None;
-    let mut is_click_widget = false;
-
-    if args.len() > 1 {
-        if args[1] == "click-widget" {
-            is_click_widget = true;
-            let mut i = 2;
-            while i < args.len() {
-                match args[i].as_str() {
-                    "--app-id" | "-a" => {
-                        if i + 1 < args.len() {
-                            app_id = Some(args[i+1].clone());
-                            i += 2;
-                        } else {
-                            eprintln!("Error: missing value for --app-id");
-                            std::process::exit(1);
-                        }
-                    }
-                    "--type" | "-t" => {
-                        if i + 1 < args.len() {
-                            widget_type = Some(args[i+1].clone());
-                            i += 2;
-                        } else {
-                            eprintln!("Error: missing value for --type");
-                            std::process::exit(1);
-                        }
-                    }
-                    "--label" | "-l" => {
-                        if i + 1 < args.len() {
-                            widget_label = Some(args[i+1].clone());
-                            i += 2;
-                        } else {
-                            eprintln!("Error: missing value for --label");
-                            std::process::exit(1);
-                        }
-                    }
-                    "-h" | "--help" | "help" => {
-                        print_usage(bin_name);
-                        std::process::exit(0);
-                    }
-                    _ => {
-                        eprintln!("Error: unknown argument '{}'", args[i]);
-                        print_usage(bin_name);
-                        std::process::exit(1);
-                    }
-                }
-            }
-            if app_id.is_none() || widget_type.is_none() {
-                eprintln!("Error: --app-id and --type are required for click-widget command");
-                print_usage(bin_name);
-                std::process::exit(1);
-            }
-        } else if args[1] == "-h" || args[1] == "--help" || args[1] == "help" {
-            print_usage(bin_name);
-            std::process::exit(0);
-        } else {
-            eprintln!("Error: unknown command '{}'", args[1]);
-            print_usage(bin_name);
-            std::process::exit(1);
-        }
-    }
-
-    let conn = match Connection::connect_to_env() {
-        Ok(c) => c,
-        Err(e) => {
-            eprintln!("Failed to connect to Wayland display socket: {:?}", e);
-            std::process::exit(1);
-        }
-    };
-
-    let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
-    let qh = event_queue.handle();
-
-    let mut state = InspectorState {
-        inspector: None,
-        done: false,
-        surfaces: Vec::new(),
-    };
-
-    let inspector = match globals.bind::<ZclearInspectorV1, _, _>(&qh, 1..=1, ()) {
-        Ok(ins) => ins,
-        Err(e) => {
-            eprintln!("Error: zclear_inspector_v1 global protocol not found on Wayland registry: {:?}", e);
-            eprintln!("Ensure clear-river is running and supports the inspector protocol.");
-            std::process::exit(1);
-        }
-    };
-
-    // Request active inspected surfaces
-    inspector.get_inspected_surfaces();
-
-    // Dispatch until completed
-    while !state.done {
-        event_queue.blocking_dispatch(&mut state).unwrap();
-    }
-
-    if is_click_widget {
-        let target_app_id = app_id.as_deref().unwrap();
-        let target_type = widget_type.as_deref().unwrap();
-        let matched_surface = state.surfaces.iter().find(|s| s.app_id.eq_ignore_ascii_case(target_app_id));
-        if let Some(surface) = matched_surface {
-            if let Ok(json_val) = serde_json::from_str::<Value>(&surface.state) {
-                let mut max_logical_w = 0.0;
-                if let Some(arr) = json_val.as_array() {
-                    for item in arr {
-                        if let Some(obj) = item.as_object() {
-                            if let Some(rect_arr) = obj.get("rect").and_then(|r| r.as_array()) {
-                                if rect_arr.len() == 4 {
-                                    let rx_val = rect_arr[0].as_f64().unwrap_or(0.0);
-                                    let rw_val = rect_arr[2].as_f64().unwrap_or(0.0);
-                                    if rx_val == 0.0 && rw_val > max_logical_w {
-                                        max_logical_w = rw_val;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-                let scale = if max_logical_w > 0.0 {
-                    (surface.width as f64) / max_logical_w
-                } else {
-                    1.0
-                };
-
-                if let Some((rx, ry, rw, rh)) = find_widget_recursive(&json_val, target_type, widget_label.as_deref()) {
-                    let target_x = (surface.x as f64 + (rx + rw / 2.0) * scale) as i32;
-                    let target_y = (surface.y as f64 + (ry + rh / 2.0) * scale) as i32;
-                    println!("Found matching widget! Coordinate local bounds: [{}, {}, {}, {}], global target: ({}, {}), scale: {}", rx, ry, rw, rh, target_x, target_y, scale);
-                    
-                    let commands = vec![
-                        format!("focus-window {}\n", target_app_id),
-                        format!("pointer-move-to {} {}\n", target_x, target_y),
-                        format!("pointer-click left\n"),
-                    ];
-                    match send_ipc_commands(&commands) {
-                        Ok(replies) => {
-                            for r in replies {
-                                print!("{}", r);
-                            }
-                        }
-                        Err(e) => {
-                            eprintln!("Error sending IPC commands to cce-client socket: {:?}", e);
-                            std::process::exit(1);
-                        }
-                    }
-                } else {
-                    eprintln!("Error: Element of type '{}'{} not found in surface '{}'", 
-                        target_type, 
-                        widget_label.as_ref().map(|l| format!(" with label '{}'", l)).unwrap_or_default(),
-                        target_app_id
-                    );
-                    std::process::exit(1);
-                }
-            } else {
-                eprintln!("Error: Failed to parse widget state JSON for surface '{}'", target_app_id);
-                std::process::exit(1);
-            }
-        } else {
-            eprintln!("Error: Surface with app_id '{}' not found", target_app_id);
-            std::process::exit(1);
-        }
-        return;
-    }
-
-    // Print results
-    println!("Found {} active inspected surface(s):", state.surfaces.len());
-    println!("{}", "=".repeat(60));
-
-    for (idx, surface) in state.surfaces.iter().enumerate() {
-        println!("Surface #{}:", idx + 1);
-        println!("  Title:       {}", surface.title);
-        println!("  App ID:      {}", surface.app_id);
-        println!("  Position:    (x: {}, y: {})", surface.x, surface.y);
-        println!("  Dimensions:  {}x{} px", surface.width, surface.height);
-        
-        // Pretty print widget state if it's JSON
-        print!("  Element Tree: ");
-        if let Ok(json_val) = serde_json::from_str::<Value>(&surface.state) {
-            if let Ok(pretty_json) = serde_json::to_string_pretty(&json_val) {
-                // Indent pretty JSON for clean display
-                let indented = pretty_json.lines()
-                    .map(|line| format!("    {}", line))
-                    .collect::<Vec<String>>()
-                    .join("\n");
-                println!("\n{}", indented);
-            } else {
-                println!("{}", surface.state);
-            }
-        } else {
-            println!("{}", surface.state);
-        }
-        println!("{}", "=".repeat(60));
-    }
-}
diff --git a/src/client/config.rs b/src/client/config.rs
deleted file mode 100644
index a5d118d..0000000
--- a/src/client/config.rs
+++ /dev/null
@@ -1,909 +0,0 @@
-// TOML config parsing for cce-client
-
-use serde::Deserialize;
-use std::collections::HashMap;
-use std::fs;
-
-use crate::types::{
-    parse_action, parse_button, parse_hex_color, parse_modifiers, parse_tiling_mode, ModeRule,
-    PendingPointerBinding, PendingXkbBinding, WindowManager, NUM_TAGS,
-};
-
-#[derive(Debug, Deserialize)]
-pub struct Config {
-    #[serde(default)]
-    pub layout: LayoutConfig,
-    #[serde(default)]
-    pub output: OutputConfig,
-    #[serde(default)]
-    pub input: InputConfig,
-    #[serde(default)]
-    pub repeat: RepeatConfig,
-    #[serde(default)]
-    pub startup: Vec<StartupEntryConfig>,
-    #[serde(default)]
-    pub env: HashMap<String, String>,
-    #[serde(default)]
-    pub keybind: Vec<KeybindConfig>,
-    #[serde(default)]
-    pub pointer_bind: Vec<PointerBindConfig>,
-    #[serde(default)]
-    pub mode_rule: Vec<ModeRuleConfig>,
-    #[serde(default)]
-    pub tag_layout: Vec<TagLayoutConfig>,
-    #[serde(default)]
-    pub notifications: NotificationsConfig,
-    #[serde(default)]
-    pub reload: Vec<ReloadEntryConfig>,
-    #[serde(default)]
-    pub inertial: Option<InertialConfig>,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ReloadEntryConfig {
-    pub exec: String,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct LayoutConfig {
-    #[serde(default = "default_gap")]
-    pub gap: i64,
-    #[serde(default = "default_gap_top")]
-    pub gap_top: i64,
-    #[serde(default = "default_gap_left")]
-    pub gap_left: i64,
-    #[serde(default = "default_gap_right")]
-    pub gap_right: i64,
-    #[serde(default = "default_gap_bottom")]
-    pub gap_bottom: i64,
-    #[serde(default = "default_cascade_offset")]
-    pub cascade_offset: i64,
-    #[serde(default = "default_bar_height")]
-    pub bar_height: i64,
-    #[serde(default = "default_border_width")]
-    pub border_width: i64,
-    #[serde(default = "default_fullscreen_border_width")]
-    pub fullscreen_border_width: i64,
-    #[serde(default = "default_cascade_border_width")]
-    pub cascade_border_width: i64,
-    #[serde(default = "default_grid_border_width")]
-    pub grid_border_width: i64,
-    #[serde(default = "default_floating_border_width")]
-    pub floating_border_width: i64,
-    #[serde(default = "default_border_color", alias = "high_color")]
-    pub border_color: String,
-    #[serde(default = "default_background_color", alias = "low_color")]
-    pub background_color: String,
-    #[serde(default = "default_border_font_size")]
-    pub border_font_size: i64,
-    #[serde(default = "default_transition_duration")]
-    pub transition_duration: i64,
-    #[serde(default = "default_grid_gap")]
-    pub grid_gap: i64,
-    #[serde(default = "default_border_blur")]
-    pub border_blur: bool,
-    #[serde(default = "default_window_blur")]
-    pub window_blur: bool,
-    #[serde(default = "default_side_panel_behavior")]
-    pub side_panel_behavior: String,
-    #[serde(default = "default_side_panel_width")]
-    pub side_panel_width: i64,
-    #[serde(default = "default_side_panel_position")]
-    pub side_panel_position: String,
-    #[serde(default = "default_side_panel_border_gap")]
-    pub side_panel_border_gap: i64,
-    #[serde(default = "default_side_panel_border_opacity")]
-    pub side_panel_border_opacity: i64,
-}
-
-impl Default for LayoutConfig {
-    fn default() -> Self {
-        LayoutConfig {
-            gap: default_gap(),
-            gap_top: default_gap_top(),
-            gap_left: default_gap_left(),
-            gap_right: default_gap_right(),
-            gap_bottom: default_gap_bottom(),
-            cascade_offset: default_cascade_offset(),
-            bar_height: default_bar_height(),
-            border_width: default_border_width(),
-            fullscreen_border_width: default_fullscreen_border_width(),
-            cascade_border_width: default_cascade_border_width(),
-            grid_border_width: default_grid_border_width(),
-            floating_border_width: default_floating_border_width(),
-            border_color: default_border_color(),
-            background_color: default_background_color(),
-            border_font_size: default_border_font_size(),
-            transition_duration: default_transition_duration(),
-            grid_gap: default_grid_gap(),
-            border_blur: default_border_blur(),
-            window_blur: default_window_blur(),
-            side_panel_behavior: default_side_panel_behavior(),
-            side_panel_width: default_side_panel_width(),
-            side_panel_position: default_side_panel_position(),
-            side_panel_border_gap: default_side_panel_border_gap(),
-            side_panel_border_opacity: default_side_panel_border_opacity(),
-        }
-    }
-}
-
-fn default_grid_gap() -> i64 {
-    18
-}
-
-fn default_border_blur() -> bool {
-    false
-}
-
-fn default_window_blur() -> bool {
-    false
-}
-
-fn default_side_panel_behavior() -> String {
-    "inline".to_string()
-}
-
-fn default_side_panel_width() -> i64 {
-    360
-}
-
-fn default_side_panel_position() -> String {
-    "left".to_string()
-}
-
-fn default_side_panel_border_gap() -> i64 {
-    0
-}
-
-fn default_side_panel_border_opacity() -> i64 {
-    100
-}
-
-fn default_gap() -> i64 {
-    48
-}
-fn default_gap_top() -> i64 {
-    48
-}
-fn default_gap_left() -> i64 {
-    48
-}
-fn default_gap_right() -> i64 {
-    48
-}
-fn default_gap_bottom() -> i64 {
-    48
-}
-fn default_cascade_offset() -> i64 {
-    20
-}
-fn default_bar_height() -> i64 {
-    24
-}
-fn default_border_width() -> i64 {
-    6
-}
-fn default_fullscreen_border_width() -> i64 {
-    0
-}
-fn default_cascade_border_width() -> i64 {
-    6
-}
-fn default_grid_border_width() -> i64 {
-    6
-}
-fn default_floating_border_width() -> i64 {
-    6
-}
-fn default_border_color() -> String {
-    "#3e3e3e".to_string()
-}
-fn default_background_color() -> String {
-    "#0a1a0e".to_string()
-}
-fn default_border_font_size() -> i64 {
-    11
-}
-fn default_transition_duration() -> i64 {
-    300
-}
-
-#[derive(Debug, Deserialize, Default)]
-pub struct OutputConfig {
-    #[serde(default)]
-    pub scale: f64,
-}
-
-#[derive(Debug, Deserialize, Default)]
-pub struct RepeatConfig {
-    #[serde(default)]
-    pub rate: i64,
-    #[serde(default)]
-    pub delay: i64,
-}
-
-#[derive(Debug, Deserialize, Default)]
-pub struct InputConfig {
-    #[serde(default)]
-    pub tap_to_click: bool,
-    pub accel_speed: Option<f64>,
-    pub accel_profile: Option<String>,
-    pub natural_scroll: Option<bool>,
-    pub dwt: Option<bool>,
-    pub dwtp: Option<bool>,
-    pub trackpoint_accel_speed: Option<f64>,
-    pub trackpoint_accel_profile: Option<String>,
-    pub cursor_theme: Option<String>,
-    pub cursor_size: Option<u32>,
-}
-
-#[derive(Debug, Deserialize, Clone)]
-pub struct InertialConfig {
-    #[serde(default = "default_true")]
-    pub inertial_scroll: bool,
-    #[serde(default = "default_scroll_friction")]
-    pub scroll_friction: u16,
-    #[serde(default = "default_false")]
-    pub inertial_pointer: bool,
-    #[serde(default = "default_pointer_friction")]
-    pub pointer_friction: u16,
-    #[serde(default = "default_false")]
-    pub inertial_trackpad: bool,
-    #[serde(default = "default_trackpad_friction")]
-    pub trackpad_friction: u16,
-    #[serde(default = "default_speed")]
-    pub pointer_speed: f64,
-    #[serde(default = "default_speed")]
-    pub scroll_speed: f64,
-    #[serde(default = "default_speed")]
-    pub trackpad_speed: f64,
-}
-
-fn default_scroll_friction() -> u16 { 90 }
-fn default_pointer_friction() -> u16 { 95 }
-fn default_trackpad_friction() -> u16 { 95 }
-fn default_speed() -> f64 { 1.0 }
-fn default_false() -> bool { false }
-
-#[derive(Debug, Deserialize)]
-pub struct NotificationsConfig {
-    #[serde(default = "default_true")]
-    pub enable: bool,
-}
-
-impl Default for NotificationsConfig {
-    fn default() -> Self {
-        Self { enable: true }
-    }
-}
-
-fn default_true() -> bool {
-    true
-}
-
-#[derive(Debug, Deserialize)]
-pub struct StartupEntryConfig {
-    pub exec: String,
-    #[serde(default)]
-    pub once: bool,
-    pub restart: Option<bool>,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct KeybindConfig {
-    pub mods: String,
-    pub key: String,
-    pub action: String,
-    pub command: Option<String>,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct PointerBindConfig {
-    pub mods: String,
-    pub button: String,
-    pub action: String,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ModeRuleConfig {
-    pub mode: String,
-    pub app_id: String,
-    pub title: Option<String>,
-    pub single: Option<bool>,
-    pub tag: Option<i64>,
-    pub circular: Option<bool>,
-    pub ssd: Option<bool>,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct TagLayoutConfig {
-    pub tag: i64,
-    pub mode: String,
-}
-
-/// Parse the TOML config file and apply it to the WindowManager state.
-/// `cold_start` controls whether `once = true` startup entries are spawned.
-pub fn parse_config(path: &str, cold_start: bool, state: &mut WindowManager) -> Result<(), String> {
-    let content = match fs::read_to_string(path) {
-        Ok(c) => c,
-        Err(e) => {
-            let err_msg = format!("cannot open {}: {}", path, e);
-            eprintln!("parse_config: {}", err_msg);
-            return Err(err_msg);
-        }
-    };
-
-    let config: Config = match toml::from_str(&content) {
-        Ok(c) => c,
-        Err(e) => {
-            let err_msg = format!("TOML parse error: {}", e);
-            eprintln!("parse_config: {}", err_msg);
-            return Err(err_msg);
-        }
-    };
-
-    // [layout] section
-    eprintln!("[config] applying layout section...");
-    state.layout.gap = config.layout.gap as i32;
-    state.layout.gap_top = config.layout.gap_top as i32;
-    state.layout.gap_left = config.layout.gap_left as i32;
-    state.layout.gap_right = config.layout.gap_right as i32;
-    state.layout.gap_bottom = config.layout.gap_bottom as i32;
-    state.layout.cascade_offset = config.layout.cascade_offset as i32;
-    state.layout.bar_height = config.layout.bar_height as i32;
-    state.layout.border_width = config.layout.border_width as i32;
-    state.layout.fullscreen_border_width = config.layout.fullscreen_border_width as i32;
-    state.layout.cascade_border_width = config.layout.cascade_border_width as i32;
-    state.layout.grid_border_width = config.layout.grid_border_width as i32;
-    state.layout.floating_border_width = config.layout.floating_border_width as i32;
-    state.layout.border_font_size = config.layout.border_font_size as i32;
-    state.layout.transition_duration = config.layout.transition_duration as i32;
-    state.layout.grid_gap = config.layout.grid_gap as i32;
-    state.layout.border_blur = config.layout.border_blur;
-    state.layout.window_blur = config.layout.window_blur;
-    state.layout.side_panel_behavior = config.layout.side_panel_behavior.clone();
-    state.layout.side_panel_width = config.layout.side_panel_width as i32;
-    state.layout.side_panel_position = config.layout.side_panel_position.clone();
-    state.layout.side_panel_border_gap = config.layout.side_panel_border_gap as i32;
-    state.layout.side_panel_border_opacity = config.layout.side_panel_border_opacity as i32;
-    if let Some((r, g, b, a)) = parse_hex_color(&config.layout.border_color) {
-        state.layout.border_r = r;
-        state.layout.border_g = g;
-        state.layout.border_b = b;
-        state.layout.border_a = a;
-    }
-    if let Some((r, g, b, a)) = parse_hex_color(&config.layout.background_color) {
-        state.layout.background_r = r;
-        state.layout.background_g = g;
-        state.layout.background_b = b;
-        state.layout.background_a = a;
-    }
-
-    // [[keybind]] array
-    eprintln!("[config] processing {} keybinds...", config.keybind.len());
-    for kb in &config.keybind {
-        let mods = parse_modifiers(&kb.mods);
-        let keysym = parse_keysym(&kb.key);
-        let action = parse_action(&kb.action);
-        let command = if action == crate::types::Action::Spawn || action == crate::types::Action::Toggle {
-            kb.command.clone()
-        } else {
-            None
-        };
-
-        state.pending_bindings.push(PendingXkbBinding {
-            mods,
-            keysym,
-            action,
-            command,
-        });
-    }
-
-    // Register default super+left and super+right bindings for side panel position if not overridden
-    let super_mod = parse_modifiers("super");
-    let left_sym = parse_keysym("Left");
-    let right_sym = parse_keysym("Right");
-
-    let has_super_left = state.pending_bindings.iter().any(|b| b.mods == super_mod && b.keysym == left_sym);
-    if !has_super_left {
-        state.pending_bindings.push(PendingXkbBinding {
-            mods: super_mod,
-            keysym: left_sym,
-            action: crate::types::Action::SidePanelLeft,
-            command: None,
-        });
-    }
-
-    let has_super_right = state.pending_bindings.iter().any(|b| b.mods == super_mod && b.keysym == right_sym);
-    if !has_super_right {
-        state.pending_bindings.push(PendingXkbBinding {
-            mods: super_mod,
-            keysym: right_sym,
-            action: crate::types::Action::SidePanelRight,
-            command: None,
-        });
-    }
-
-    // [[pointer_bind]] array
-    for pb in &config.pointer_bind {
-        let mods = parse_modifiers(&pb.mods);
-        let button = parse_button(&pb.button);
-        let action = parse_action(&pb.action);
-
-        state.pending_pointer_bindings.push(PendingPointerBinding {
-            mods,
-            button,
-            action,
-        });
-    }
-
-    // [[mode_rule]] array
-    for mr in &config.mode_rule {
-        let mode = parse_tiling_mode(&mr.mode);
-        let tag = mr.tag.unwrap_or(0) as i32;
-        state.mode_rules.push(ModeRule {
-            mode,
-            app_id_pattern: mr.app_id.clone(),
-            title_pattern: mr.title.clone(),
-            single_instance: mr.single.unwrap_or(false),
-            tag,
-            circular: mr.circular.unwrap_or(false),
-            ssd: mr.ssd,
-        });
-    }
-
-    // [[tag_layout]] array
-    for tl in &config.tag_layout {
-        let tag = tl.tag as i32;
-        if tag >= 1 && tag <= NUM_TAGS as i32 {
-            let mode = parse_tiling_mode(&tl.mode);
-            state.tag_layouts[tag as usize - 1] = mode;
-            state.has_tag_layout[tag as usize - 1] = true;
-        }
-    }
-
-    // [env] — set environment variables BEFORE startup apps are processed,
-    // so that PATH and other vars are available when spawn_command_bg runs.
-    // Supports $VAR and ${VAR} expansion using the current environment.
-    for (key, value) in &config.env {
-        let expanded = expand_env_vars(value);
-        std::env::set_var(key, &expanded);
-        state.env_vars.insert(key.clone(), expanded);
-    }
-
-    // [[startup]] array — queue apps for spawning inside the render callback.
-    // Spawning between blocking_dispatch calls corrupts the Wayland connection
-    // because the fork inherits the socket fd, so we defer to render time.
-    state.pending_startup_apps.clear();
-    eprintln!(
-        "[config] processing {} startup entries (cold_start={})...",
-        config.startup.len(),
-        cold_start
-    );
-    for (i, entry) in config.startup.iter().enumerate() {
-        let name = extract_program_name(&entry.exec);
-        let should_restart = entry.restart.unwrap_or(false);
-
-        if entry.once && !cold_start && !should_restart {
-            eprintln!(
-                "[config] startup[{}]: exec=\"{}\" once=true → skipped (not cold start)",
-                i, entry.exec
-            );
-            continue;
-        }
-
-        let running = process_running(&name);
-        if should_restart && running {
-            eprintln!(
-                "[config] startup[{}]: exec=\"{}\" restart=true → killing existing process {}",
-                i, entry.exec, name
-            );
-            let mut pkill_cmd = std::process::Command::new("pkill");
-            if name.len() > 15 {
-                pkill_cmd.arg("-f").arg(&name);
-            } else {
-                pkill_cmd.arg("-x").arg(&name);
-            }
-            match pkill_cmd.status() {
-                Ok(status) => {
-                    eprintln!("[config] pkill status: {}", status);
-                    let start = std::time::Instant::now();
-                    while process_running(&name) && start.elapsed().as_secs_f64() < 1.0 {
-                        std::thread::sleep(std::time::Duration::from_millis(50));
-                    }
-                    eprintln!(
-                        "[config] process {} exited after pkill: {}",
-                        name,
-                        !process_running(&name)
-                    );
-                }
-                Err(e) => {
-                    eprintln!("[config] failed to execute pkill: {:?}", e);
-                }
-            }
-            state.pending_startup_apps.push(entry.exec.clone());
-        } else if running {
-            eprintln!("[config] startup[{}]: exec=\"{}\" once={} → skipped (already running, pgrep -x {})", i, entry.exec, entry.once, name);
-        } else {
-            eprintln!(
-                "[config] startup[{}]: exec=\"{}\" once={} → queued for spawn",
-                i, entry.exec, entry.once
-            );
-            state.pending_startup_apps.push(entry.exec.clone());
-        }
-    }
-
-    // [output] scale — applied via wlr-output-management protocol.
-    // After storing the scale, set pending_scale_apply so that the next
-    // output_manager done event triggers the configuration. This handles
-    // both initial startup and VT-switch-back (where wlroots resets scale to 1).
-    state.output_scale = if config.output.scale > 0.0 {
-        state.pending_scale_apply = true;
-        config.output.scale
-    } else {
-        0.0
-    };
-
-    // [input] tap_to_click — applied via river-libinput-config protocol.
-    // The tap config will be applied when libinput devices are discovered
-    // (in the RiverLibinputDeviceV1 TapSupport event handler).
-    state.tap_to_click = config.input.tap_to_click;
-    state.accel_speed = config.input.accel_speed;
-    state.accel_profile = config.input.accel_profile.clone();
-    state.natural_scroll = config.input.natural_scroll;
-    state.dwt = config.input.dwt;
-    state.dwtp = config.input.dwtp;
-    state.trackpoint_accel_speed = config.input.trackpoint_accel_speed;
-    state.trackpoint_accel_profile = config.input.trackpoint_accel_profile.clone();
-    state.tap_config_applied = false;
-
-    if state.cursor_theme != config.input.cursor_theme || state.cursor_size != config.input.cursor_size {
-        state.cursor_theme = config.input.cursor_theme.clone();
-        state.cursor_size = config.input.cursor_size;
-        state.cursor_theme_applied = false;
-    }
-
-    // Send InertialConfig and tap_to_click state to input subsystem daemon
-    let inertial_cfg = config.inertial.clone().unwrap_or_else(|| {
-        InertialConfig {
-            inertial_scroll: true,
-            scroll_friction: 90,
-            inertial_pointer: false,
-            pointer_friction: 95,
-            inertial_trackpad: false,
-            trackpad_friction: 95,
-            pointer_speed: 1.0,
-            scroll_speed: 1.0,
-            trackpad_speed: 1.0,
-        }
-    });
-    if let Some(ref controller) = state.input_controller {
-        let _ = controller.send(crate::input::InputDaemonMsg::UpdateConfig(inertial_cfg, config.input.tap_to_click));
-    }
-
-    // [notifications]
-    state.notifications_enable = config.notifications.enable;
-
-    // [[reload]] array
-    state.reload_commands.clear();
-    for entry in &config.reload {
-        state.reload_commands.push(entry.exec.clone());
-    }
-
-    // Signal config-done
-    state.config_done = true;
-    Ok(())
-}
-
-/// Expand $VAR and ${VAR} references in a string using the current environment.
-/// Unset variables expand to empty strings. $$ is not expanded (not a shell).
-fn expand_env_vars(s: &str) -> String {
-    let mut result = String::with_capacity(s.len());
-    let chars: Vec<char> = s.chars().collect();
-    let mut i = 0;
-    while i < chars.len() {
-        if chars[i] == '$' && i + 1 < chars.len() {
-            if chars[i + 1] == '{' {
-                // ${VAR} form
-                if let Some(end) = chars[i + 2..].iter().position(|c| *c == '}') {
-                    let var_name: String = chars[i + 2..i + 2 + end].iter().collect();
-                    let val = std::env::var(&var_name).unwrap_or_default();
-                    result.push_str(&val);
-                    i = i + 2 + end + 1; // skip ${VAR}
-                } else {
-                    result.push(chars[i]);
-                    i += 1;
-                }
-            } else if chars[i + 1].is_ascii_alphabetic() || chars[i + 1] == '_' {
-                // $VAR form — name is [A-Za-z_][A-Za-z0-9_]*
-                let start = i + 1;
-                let mut end = start;
-                while end < chars.len() && (chars[end].is_ascii_alphanumeric() || chars[end] == '_')
-                {
-                    end += 1;
-                }
-                let var_name: String = chars[start..end].iter().collect();
-                let val = std::env::var(&var_name).unwrap_or_default();
-                result.push_str(&val);
-                i = end;
-            } else {
-                // $ followed by non-identifier char (e.g. $$, $:, $@) — keep as-is
-                result.push(chars[i]);
-                i += 1;
-            }
-        } else {
-            result.push(chars[i]);
-            i += 1;
-        }
-    }
-    result
-}
-
-/// Extract the program name (first word, basename) from a command string
-pub fn extract_program_name(cmd: &str) -> String {
-    let cmd = cmd.trim_start();
-    let first_word: String = cmd.chars().take_while(|c| !c.is_whitespace()).collect();
-    if let Some(slash) = first_word.rfind('/') {
-        first_word[slash + 1..].to_string()
-    } else {
-        first_word
-    }
-}
-
-/// Parse a key name string into an xkb keysym value.
-/// Uses xkbcommon to resolve key names.
-pub fn parse_keysym(key_str: &str) -> u32 {
-    let name = if key_str.starts_with("XKB_KEY_") {
-        &key_str[8..]
-    } else {
-        key_str
-    };
-    // Use xkbcommon to parse the keysym
-    xkbcommon::xkb::keysym_from_name(name, xkbcommon::xkb::KEYSYM_CASE_INSENSITIVE).into()
-}
-
-/// Spawn a command in the background.
-///
-/// Closes all inherited FDs > 2 in the child via pre_exec so that
-/// spawned Wayland clients (fuzzel, foot, etc.) never accidentally
-/// read from cce-client's Wayland socket fd. Also redirects stdout/stderr
-/// to /dev/null so child output doesn't pollute cce-client's log, and
-/// calls setsid() to detach from cce-client's process group.
-pub fn spawn_command_bg(cmd: &str) {
-    use std::os::unix::process::CommandExt;
-    let cmd = cmd.to_string();
-    
-    let stdout_cfg = if let Ok(f) = std::fs::OpenOptions::new()
-        .create(true)
-        .append(true)
-        .open("/tmp/cce-client-spawned-apps.log")
-    {
-        std::process::Stdio::from(f)
-    } else {
-        std::process::Stdio::null()
-    };
-
-    let stderr_cfg = if let Ok(f) = std::fs::OpenOptions::new()
-        .create(true)
-        .append(true)
-        .open("/tmp/cce-client-spawned-apps.log")
-    {
-        std::process::Stdio::from(f)
-    } else {
-        std::process::Stdio::null()
-    };
-
-    let _ = unsafe {
-        std::process::Command::new("sh")
-            .arg("-c")
-            .arg(&cmd)
-            .env_remove("WAYLAND_DEBUG")
-            .stdout(stdout_cfg)
-            .stderr(stderr_cfg)
-            .pre_exec(|| {
-                // Close all inherited FDs > 2 to prevent the child from
-                // accidentally reading cce-client's Wayland socket or status
-                // socket FDs. close() and setsid() are async-signal-safe.
-                let max_fd = libc::sysconf(libc::_SC_OPEN_MAX) as libc::c_int;
-                for fd in 3..max_fd {
-                    libc::close(fd);
-                }
-                libc::setsid();
-                Ok(())
-            })
-            .spawn()
-    };
-}
-pub fn process_running(name: &str) -> bool {
-    let mut cmd = std::process::Command::new("pgrep");
-    if name.len() > 15 {
-        cmd.arg("-f").arg(name);
-    } else {
-        cmd.arg("-x").arg(name);
-    }
-    match cmd.output() {
-        Ok(output) => output.status.success(),
-        Err(_) => false,
-    }
-}
-
-/// Show a system desktop notification via notify-send.
-pub fn show_notification(title: &str, body: &str) {
-    let title_escaped = title.replace('\'', "'\\''");
-    let body_escaped = body.replace('\'', "'\\''");
-    let cmd = format!("notify-send -a cce-client '{}' '{}'", title_escaped, body_escaped);
-    spawn_command_bg(&cmd);
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_extract_program_name() {
-        assert_eq!(extract_program_name("ghostty"), "ghostty");
-        assert_eq!(extract_program_name("ghostty -e something"), "ghostty");
-        assert_eq!(
-            extract_program_name("/home/lsgalante/.local/bin/tmux-startup --recreate main"),
-            "tmux-startup"
-        );
-        assert_eq!(extract_program_name("  waybar"), "waybar");
-    }
-
-    #[test]
-    fn test_config_layout_defaults() {
-        let lc = LayoutConfig::default();
-        assert_eq!(lc.gap, 48);
-        assert_eq!(lc.gap_top, 48);
-        assert_eq!(lc.gap_left, 48);
-        assert_eq!(lc.gap_right, 48);
-        assert_eq!(lc.gap_bottom, 48);
-        assert_eq!(lc.cascade_offset, 20);
-        assert_eq!(lc.bar_height, 24);
-        assert_eq!(lc.border_width, 6);
-        assert_eq!(lc.fullscreen_border_width, 0);
-        assert_eq!(lc.border_color, "#3e3e3e");
-        assert_eq!(lc.border_font_size, 11);
-        assert_eq!(lc.grid_gap, 18);
-        assert_eq!(lc.side_panel_width, 360);
-        assert_eq!(lc.side_panel_behavior, "inline");
-        assert_eq!(lc.side_panel_position, "left");
-        assert_eq!(lc.side_panel_border_gap, 0);
-        assert_eq!(lc.side_panel_border_opacity, 100);
-    }
-
-    #[test]
-    fn test_expand_env_vars_simple() {
-        std::env::set_var("CCE_CLIENT_TEST_VAR_SIMPLE", "hello");
-        assert_eq!(expand_env_vars("$CCE_CLIENT_TEST_VAR_SIMPLE"), "hello");
-        std::env::remove_var("CCE_CLIENT_TEST_VAR_SIMPLE");
-    }
-
-    #[test]
-    fn test_expand_env_vars_braces() {
-        std::env::set_var("CCE_CLIENT_TEST_VAR_BRACES", "world");
-        assert_eq!(expand_env_vars("${CCE_CLIENT_TEST_VAR_BRACES}!"), "world!");
-        std::env::remove_var("CCE_CLIENT_TEST_VAR_BRACES");
-    }
-
-    #[test]
-    fn test_expand_env_vars_mid_string() {
-        std::env::set_var("CCE_CLIENT_TEST_HOME", "/home/user");
-        assert_eq!(
-            expand_env_vars("$CCE_CLIENT_TEST_HOME/.local/bin:$CCE_CLIENT_TEST_HOME/bin"),
-            "/home/user/.local/bin:/home/user/bin"
-        );
-        std::env::remove_var("CCE_CLIENT_TEST_HOME");
-    }
-
-    #[test]
-    fn test_expand_env_vars_unset() {
-        assert_eq!(expand_env_vars("$CCE_CLIENT_NONEXISTENT_VAR"), "");
-    }
-
-    #[test]
-    fn test_expand_env_vars_no_vars() {
-        assert_eq!(expand_env_vars("plain string"), "plain string");
-    }
-
-    #[test]
-    fn test_expand_env_vars_dollar_non_identifier() {
-        // $$ and $: should be kept as-is (not a shell)
-        assert_eq!(expand_env_vars("$$"), "$$");
-        assert_eq!(expand_env_vars("$:"), "$:");
-    }
-
-    #[test]
-    fn test_expand_env_vars_path_append() {
-        // Simulates the real use case: PATH = "$HOME/.local/bin:$PATH"
-        let orig_path = std::env::var("PATH").unwrap_or_default();
-        let home = std::env::var("HOME").unwrap_or_default();
-        let expanded = expand_env_vars("$HOME/.local/bin:$PATH");
-        assert!(expanded.starts_with(&format!("{}/.local/bin:", home)));
-        assert!(expanded.contains(&orig_path));
-    }
-
-    #[test]
-    fn test_notifications_config_parse() {
-        let toml_str = r#"
-[notifications]
-enable = false
-"#;
-        let config: Config = toml::from_str(toml_str).unwrap();
-        assert!(!config.notifications.enable);
-
-        let toml_str_empty = "";
-        let config_empty: Config = toml::from_str(toml_str_empty).unwrap();
-        assert!(config_empty.notifications.enable);
-    }
-
-    #[test]
-    fn test_parse_keysym_favorites() {
-        let sym = parse_keysym("XF86Favorites");
-        assert!(sym != 0, "XF86Favorites keysym must be resolved successfully");
-    }
-}
-
-#[cfg(test)]
-mod startup_format_tests {
-    use super::*;
-
-    #[test]
-    fn test_startup_entry_format() {
-        let toml_str = r#"
-[env]
-XDG_CURRENT_DESKTOP = "river"
-
-[[startup]]
-exec = "waybar"
-
-[[startup]]
-exec = "fuzzel"
-once = true
-
-[[startup]]
-exec = "cce-system-interface"
-once = true
-restart = true
-"#;
-        let config: Config = toml::from_str(toml_str).expect("TOML parse failed");
-        assert_eq!(config.startup.len(), 3);
-        assert_eq!(config.startup[0].exec, "waybar");
-        assert!(!config.startup[0].once);
-        assert_eq!(config.startup[1].exec, "fuzzel");
-        assert!(config.startup[1].once);
-        assert_eq!(config.startup[2].exec, "cce-system-interface");
-        assert!(config.startup[2].once);
-        assert_eq!(config.startup[2].restart, Some(true));
-        assert_eq!(config.env.get("XDG_CURRENT_DESKTOP").unwrap(), "river");
-    }
-
-    #[test]
-    fn test_reload_entry_format() {
-        let toml_str = r#"
-[[reload]]
-exec = "pkill clear-input-daemon"
-
-[[reload]]
-exec = "echo reloaded"
-"#;
-        let config: Config = toml::from_str(toml_str).expect("TOML parse failed");
-        assert_eq!(config.reload.len(), 2);
-        assert_eq!(config.reload[0].exec, "pkill clear-input-daemon");
-        assert_eq!(config.reload[1].exec, "echo reloaded");
-
-        // Also test integration via parse_config by writing to a temporary file
-        let temp_path_buf = std::env::temp_dir().join("scratch_config_test.toml");
-        let temp_path = temp_path_buf.to_str().unwrap();
-        std::fs::write(temp_path, toml_str).unwrap();
-
-        let mut wm = WindowManager::default();
-        let parse_res = parse_config(temp_path, false, &mut wm);
-        let _ = std::fs::remove_file(temp_path);
-
-        parse_res.unwrap();
-        assert_eq!(wm.reload_commands.len(), 2);
-        assert_eq!(wm.reload_commands[0], "pkill clear-input-daemon");
-        assert_eq!(wm.reload_commands[1], "echo reloaded");
-    }
-}
diff --git a/src/client/decorations.rs b/src/client/decorations.rs
deleted file mode 100644
index e7beb5d..0000000
--- a/src/client/decorations.rs
+++ /dev/null
@@ -1,1276 +0,0 @@
-// Wayland decoration surface drawing and management for cce-client
-
-use std::ffi::CString;
-use std::os::fd::RawFd;
-use std::ptr;
-use wayland_client::protocol::{wl_buffer, wl_shm, wl_shm_pool, wl_surface};
-use wayland_client::QueueHandle;
-
-use crate::protocol::river_window_management::client::river_decoration_v1::RiverDecorationV1;
-use crate::wayland::AppState;
-
-fn resolve_window_border_font_path() -> Option<String> {
-    use std::io::Read;
-    let mut child = std::process::Command::new("fc-match")
-        .args(&["-f", "%{file}", "window\\-borders"])
-        .stdout(std::process::Stdio::piped())
-        .stderr(std::process::Stdio::null())
-        .spawn()
-        .ok()?;
-
-    let mut stdout = child.stdout.take()?;
-    let mut output_str = String::new();
-    let _ = stdout.read_to_string(&mut output_str);
-    let _ = child.wait();
-
-    let path = output_str.trim().to_string();
-    if !path.is_empty() && std::path::Path::new(&path).exists() {
-        return Some(path);
-    }
-    None
-}
-
-/// Struct tracking the Wayland decoration resources for a window.
-pub struct WindowDecoration {
-    pub decoration: RiverDecorationV1,
-    pub surface: wl_surface::WlSurface,
-    pub buffer: Option<wl_buffer::WlBuffer>,
-    pub pool: Option<wl_shm_pool::WlShmPool>,
-    pub width: i32,
-    pub height: i32,
-    pub mapped_data: *mut u32,
-    pub mapped_size: usize,
-}
-
-impl Drop for WindowDecoration {
-    fn drop(&mut self) {
-        if !self.mapped_data.is_null() {
-            unsafe {
-                libc::munmap(self.mapped_data as *mut libc::c_void, self.mapped_size);
-            }
-        }
-    }
-}
-
-// Standard 8x8 bitmap font (IBM CP437 ASCII printable characters 32..=126)
-const FONT_DATA: &[u8; 760] = &[
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //   (0x20)
-    0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00, // ! (0x21)
-    0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, // " (0x22)
-    0x24, 0x24, 0x7e, 0x24, 0x7e, 0x24, 0x24, 0x00, // # (0x23)
-    0x08, 0x3e, 0x28, 0x3e, 0x0a, 0x3e, 0x08, 0x00, // $ (0x24)
-    0x00, 0x62, 0x64, 0x08, 0x13, 0x23, 0x00, 0x00, // % (0x25)
-    0x18, 0x24, 0x18, 0x2a, 0x24, 0x24, 0x1b, 0x00, // & (0x26)
-    0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ' (0x27)
-    0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // ( (0x28)
-    0x10, 0x08, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00, // ) (0x29)
-    0x00, 0x24, 0x18, 0x3c, 0x18, 0x24, 0x00, 0x00, // * (0x2a)
-    0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, // + (0x2b)
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, // , (0x2c)
-    0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, // - (0x2d)
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // . (0x2e)
-    0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, // / (0x2f)
-    0x3c, 0x46, 0x4a, 0x52, 0x62, 0x3c, 0x00, 0x00, // 0 (0x30)
-    0x18, 0x28, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, // 1 (0x31)
-    0x3c, 0x42, 0x02, 0x3c, 0x40, 0x7e, 0x00, 0x00, // 2 (0x32)
-    0x3c, 0x42, 0x1c, 0x02, 0x42, 0x3c, 0x00, 0x00, // 3 (0x33)
-    0x08, 0x18, 0x28, 0x48, 0x7e, 0x08, 0x00, 0x00, // 4 (0x34)
-    0x7e, 0x40, 0x7c, 0x02, 0x42, 0x3c, 0x00, 0x00, // 5 (0x35)
-    0x3c, 0x40, 0x7c, 0x42, 0x42, 0x3c, 0x00, 0x00, // 6 (0x36)
-    0x7e, 0x02, 0x04, 0x08, 0x10, 0x10, 0x00, 0x00, // 7 (0x37)
-    0x3c, 0x42, 0x3c, 0x42, 0x42, 0x3c, 0x00, 0x00, // 8 (0x38)
-    0x3c, 0x42, 0x3e, 0x02, 0x02, 0x3c, 0x00, 0x00, // 9 (0x39)
-    0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, // : (0x3a)
-    0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x10, 0x00, // ; (0x3b)
-    0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, // < (0x3c)
-    0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00, // = (0x3d)
-    0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, // > (0x3e)
-    0x3c, 0x42, 0x02, 0x0c, 0x00, 0x08, 0x08, 0x00, // ? (0x3f)
-    0x3c, 0x42, 0x5a, 0x5a, 0x5a, 0x3e, 0x00, 0x00, // @ (0x40)
-    0x18, 0x24, 0x42, 0x7e, 0x42, 0x42, 0x00, 0x00, // A (0x41)
-    0x7c, 0x42, 0x7c, 0x42, 0x42, 0x7c, 0x00, 0x00, // B (0x42)
-    0x3c, 0x42, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, // C (0x43)
-    0x78, 0x44, 0x42, 0x42, 0x44, 0x78, 0x00, 0x00, // D (0x44)
-    0x7e, 0x40, 0x7c, 0x40, 0x40, 0x7e, 0x00, 0x00, // E (0x45)
-    0x7e, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x00, 0x00, // F (0x46)
-    0x3c, 0x42, 0x40, 0x4e, 0x42, 0x3c, 0x00, 0x00, // G (0x47)
-    0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, // H (0x48)
-    0x3e, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, // I (0x49)
-    0x02, 0x02, 0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, // J (0x4a)
-    0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00, 0x00, // K (0x4b)
-    0x40, 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, // L (0x4c)
-    0x42, 0x66, 0x5a, 0x42, 0x42, 0x42, 0x00, 0x00, // M (0x4d)
-    0x42, 0x62, 0x52, 0x4a, 0x46, 0x42, 0x00, 0x00, // N (0x4e)
-    0x3c, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, // O (0x4f)
-    0x7c, 0x42, 0x7c, 0x40, 0x40, 0x40, 0x00, 0x00, // P (0x50)
-    0x3c, 0x42, 0x42, 0x42, 0x4a, 0x3c, 0x02, 0x00, // Q (0x51)
-    0x7c, 0x42, 0x7c, 0x48, 0x44, 0x42, 0x00, 0x00, // R (0x52)
-    0x3c, 0x40, 0x3c, 0x02, 0x02, 0x3c, 0x00, 0x00, // S (0x53)
-    0x7e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, // T (0x54)
-    0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, // U (0x55)
-    0x42, 0x42, 0x42, 0x24, 0x24, 0x18, 0x00, 0x00, // V (0x56)
-    0x42, 0x42, 0x42, 0x5a, 0x5a, 0x24, 0x00, 0x00, // W (0x57)
-    0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00, 0x00, // X (0x58)
-    0x42, 0x42, 0x24, 0x18, 0x08, 0x08, 0x00, 0x00, // Y (0x59)
-    0x7e, 0x02, 0x04, 0x08, 0x10, 0x7e, 0x00, 0x00, // Z (0x5a)
-    0x3c, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x00, 0x00, // [ (0x5b)
-    0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, // \ (0x5c)
-    0x3c, 0x02, 0x02, 0x02, 0x02, 0x3c, 0x00, 0x00, // ] (0x5d)
-    0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, // ^ (0x5e)
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, // _ (0x5f)
-    0x08, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ` (0x60)
-    0x00, 0x3c, 0x02, 0x3e, 0x42, 0x3e, 0x00, 0x00, // a (0x61)
-    0x40, 0x40, 0x7c, 0x42, 0x42, 0x7c, 0x00, 0x00, // b (0x62)
-    0x00, 0x3c, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, // c (0x63)
-    0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, // d (0x64)
-    0x00, 0x3c, 0x42, 0x7e, 0x40, 0x3c, 0x00, 0x00, // e (0x65)
-    0x1c, 0x20, 0x78, 0x20, 0x20, 0x20, 0x00, 0x00, // f (0x66)
-    0x00, 0x3e, 0x42, 0x3e, 0x02, 0x3c, 0x00, 0x00, // g (0x67)
-    0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x00, 0x00, // h (0x68)
-    0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, // i (0x69)
-    0x02, 0x00, 0x02, 0x02, 0x02, 0x42, 0x3c, 0x00, // j (0x6a)
-    0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x00, 0x00, // k (0x6b)
-    0x18, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, // l (0x6c)
-    0x00, 0x66, 0x5a, 0x42, 0x42, 0x42, 0x00, 0x00, // m (0x6d)
-    0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, // n (0x6e)
-    0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, // o (0x6f)
-    0x00, 0x7c, 0x42, 0x7c, 0x40, 0x40, 0x00, 0x00, // p (0x70)
-    0x00, 0x3e, 0x42, 0x3e, 0x02, 0x02, 0x00, 0x00, // q (0x71)
-    0x00, 0x7c, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, // r (0x72)
-    0x00, 0x3e, 0x40, 0x3c, 0x02, 0x3c, 0x00, 0x00, // s (0x73)
-    0x08, 0x08, 0x3e, 0x08, 0x08, 0x0a, 0x04, 0x00, // t (0x74)
-    0x00, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, // u (0x75)
-    0x00, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, // v (0x76)
-    0x00, 0x42, 0x42, 0x5a, 0x5a, 0x24, 0x00, 0x00, // w (0x77)
-    0x00, 0x42, 0x24, 0x18, 0x24, 0x42, 0x00, 0x00, // x (0x78)
-    0x00, 0x42, 0x42, 0x3e, 0x02, 0x3c, 0x00, 0x00, // y (0x79)
-    0x00, 0x7e, 0x04, 0x08, 0x10, 0x7e, 0x00, 0x00, // z (0x7a)
-    0x0c, 0x10, 0x10, 0x20, 0x10, 0x10, 0x0c, 0x00, // { (0x7b)
-    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, // | (0x7c)
-    0x30, 0x08, 0x08, 0x04, 0x08, 0x08, 0x30, 0x00, // } (0x7d)
-    0x00, 0x00, 0x00, 0x14, 0x28, 0x00, 0x00, 0x00, // ~ (0x7e)
-];
-
-/// Helper to draw a character at (x, y) with a given scale and BGRA color.
-fn draw_char(
-    buf: &mut [u32],
-    buf_w: i32,
-    buf_h: i32,
-    c: char,
-    x: i32,
-    y: i32,
-    scale: i32,
-    color: u32,
-) {
-    let code = (c as usize).saturating_sub(32);
-    if code >= 95 {
-        return;
-    }
-    let glyph = &FONT_DATA[code * 8..(code + 1) * 8];
-    for row in 0..8 {
-        let row_byte = glyph[row];
-        for col in 0..8 {
-            let bit = (row_byte >> (7 - col)) & 1;
-            if bit != 0 {
-                for dy in 0..scale {
-                    for dx in 0..scale {
-                        let px = x + (col as i32) * scale + dx;
-                        let py = y + (row as i32) * scale + dy;
-                        if px >= 0 && px < buf_w && py >= 0 && py < buf_h {
-                            buf[(py * buf_w + px) as usize] = color;
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-/// Create a temporary shared memory file descriptor.
-fn create_memfd(size: usize) -> Option<RawFd> {
-    let name = CString::new("cce-client-decoration").ok()?;
-    let fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
-    if fd < 0 {
-        return None;
-    }
-    if unsafe { libc::ftruncate(fd, size as libc::off_t) } < 0 {
-        unsafe { libc::close(fd); }
-        return None;
-    }
-    Some(fd)
-}
-
-#[derive(Clone, Copy, PartialEq, Eq)]
-enum CornerType {
-    TopLeft,
-    TopRight,
-    BottomLeft,
-    BottomRight,
-}
-
-fn round_corner(
-    buffer_slice: &mut [u32],
-    dec_width: i32,
-    dec_height: i32,
-    cx: i32,
-    cy: i32,
-    r: i32,
-    corner: CornerType,
-) {
-    if r <= 0 {
-        return;
-    }
-    let r_f = r as f32;
-
-    let (x_range, y_range) = match corner {
-        CornerType::TopLeft => (0..cx, 0..cy),
-        CornerType::TopRight => (cx..dec_width, 0..cy),
-        CornerType::BottomLeft => (0..cx, cy..dec_height),
-        CornerType::BottomRight => (cx..dec_width, cy..dec_height),
-    };
-
-    for py in y_range {
-        if py < 0 || py >= dec_height {
-            continue;
-        }
-        let row_offset = (py * dec_width) as usize;
-        for px in x_range.clone() {
-            if px < 0 || px >= dec_width {
-                continue;
-            }
-
-            let dx = (px - cx) as f32;
-            let dy = (py - cy) as f32;
-            let dist = (dx * dx + dy * dy).sqrt();
-
-            if dist > r_f + 0.5 {
-                buffer_slice[row_offset + px as usize] = 0x00000000;
-            } else if dist > r_f - 0.5 {
-                let alpha_scale = r_f + 0.5 - dist; // value between 0.0 and 1.0
-                let index = row_offset + px as usize;
-                let pixel = buffer_slice[index];
-                let a = ((pixel >> 24) & 0xFF) as f32 * alpha_scale;
-                let r_val = ((pixel >> 16) & 0xFF) as f32 * alpha_scale;
-                let g = ((pixel >> 8) & 0xFF) as f32 * alpha_scale;
-                let b = (pixel & 0xFF) as f32 * alpha_scale;
-
-                buffer_slice[index] = ((a as u32) << 24)
-                    | ((r_val as u32) << 16)
-                    | ((g as u32) << 8)
-                    | (b as u32);
-            }
-        }
-    }
-}
-
-fn update_border_decoration(
-    _wid: u64,
-    dec_opt: &mut Option<WindowDecoration>,
-    compositor: &wayland_client::protocol::wl_compositor::WlCompositor,
-    shm: &wayland_client::protocol::wl_shm::WlShm,
-    river_window: &crate::protocol::river_window_management::client::river_window_v1::RiverWindowV1,
-    qhandle: &QueueHandle<AppState>,
-    offset_x: i32,
-    offset_y: i32,
-    logical_width: i32,
-    logical_height: i32,
-    scale: i32,
-    border_blur: bool,
-    bg_color: u32,
-    rect_x: i32,
-    rect_y: i32,
-    rect_w: i32,
-    rect_h: i32,
-    border_width: i32,
-    round_bottom: bool,
-    side: &str,
-    hover_info: Option<(f64, f64)>,
-) {
-    let dec_width = logical_width * scale;
-    let dec_height = logical_height * scale;
-
-    let needs_new_buffer = match dec_opt {
-        Some(dec) => dec.width != dec_width || dec.height != dec_height,
-        None => true,
-    };
-
-    if dec_opt.is_none() {
-        let surface: wl_surface::WlSurface = compositor.create_surface(qhandle, ());
-        let decoration: RiverDecorationV1 = river_window.get_decoration_above(&surface, qhandle, ());
-
-        *dec_opt = Some(WindowDecoration {
-            decoration,
-            surface,
-            buffer: None,
-            pool: None,
-            width: 0,
-            height: 0,
-            mapped_data: ptr::null_mut(),
-            mapped_size: 0,
-        });
-    }
-
-    let dec = dec_opt.as_mut().unwrap();
-    dec.decoration.set_offset(offset_x, offset_y);
-    dec.decoration.set_blur(if border_blur { 1 } else { 0 });
-
-    if needs_new_buffer {
-        if !dec.mapped_data.is_null() {
-            unsafe {
-                libc::munmap(dec.mapped_data as *mut libc::c_void, dec.mapped_size);
-            }
-            dec.mapped_data = ptr::null_mut();
-        }
-
-        let stride = dec_width * 4;
-        let size = (stride * dec_height) as usize;
-
-        if let Some(fd) = create_memfd(size) {
-            let mapped_data = unsafe {
-                libc::mmap(
-                    ptr::null_mut(),
-                    size,
-                    libc::PROT_READ | libc::PROT_WRITE,
-                    libc::MAP_SHARED,
-                    fd,
-                    0,
-                ) as *mut u32
-            };
-
-            if mapped_data != libc::MAP_FAILED as *mut u32 {
-                let borrowed_fd = unsafe { std::os::fd::BorrowedFd::borrow_raw(fd) };
-                let pool = shm.create_pool(borrowed_fd, size as i32, qhandle, ());
-                let buffer = pool.create_buffer(
-                    0,
-                    dec_width,
-                    dec_height,
-                    stride,
-                    wl_shm::Format::Argb8888,
-                    qhandle,
-                    (),
-                );
-
-                dec.pool = Some(pool);
-                dec.buffer = Some(buffer);
-                dec.width = dec_width;
-                dec.height = dec_height;
-                dec.mapped_data = mapped_data;
-                dec.mapped_size = size;
-                dec.surface.set_buffer_scale(scale);
-            } else {
-                eprintln!("[decorations] failed to mmap border decoration buffer");
-                unsafe { libc::close(fd); }
-                return;
-            }
-            unsafe { libc::close(fd); }
-        } else {
-            eprintln!("[decorations] failed to create memfd for border decoration");
-            return;
-        }
-    }
-
-    if let Some(ref buffer) = dec.buffer {
-        if !dec.mapped_data.is_null() {
-            let buffer_slice = unsafe {
-                std::slice::from_raw_parts_mut(dec.mapped_data, (dec_width * dec_height) as usize)
-            };
-
-            // Clear to fully transparent
-            for pixel in buffer_slice.iter_mut() {
-                *pixel = 0x00000000;
-            }
-
-            let mut highlight_region: Option<HighlightRegion> = None;
-            if let Some((hx, hy)) = hover_info {
-                const CORNER_THRESHOLD: f64 = 16.0;
-                match side {
-                    "left" => {
-                        let mid_x = (logical_width as f64) / 2.0;
-                        if hx < mid_x {
-                            if hy < CORNER_THRESHOLD {
-                                highlight_region = Some(HighlightRegion::TopLeft);
-                            } else if hy > (logical_height as f64 - CORNER_THRESHOLD) {
-                                highlight_region = Some(HighlightRegion::BottomLeft);
-                            } else {
-                                highlight_region = Some(HighlightRegion::Left);
-                            }
-                        }
-                    }
-                    "right" => {
-                        let mid_x = (logical_width as f64) / 2.0;
-                        if hx >= mid_x {
-                            if hy < CORNER_THRESHOLD {
-                                highlight_region = Some(HighlightRegion::TopRight);
-                            } else if hy > (logical_height as f64 - CORNER_THRESHOLD) {
-                                highlight_region = Some(HighlightRegion::BottomRight);
-                            } else {
-                                highlight_region = Some(HighlightRegion::Right);
-                            }
-                        }
-                    }
-                    "bottom" => {
-                        let mid_y = (logical_height as f64) / 2.0;
-                        if hy >= mid_y {
-                            if hx < CORNER_THRESHOLD {
-                                highlight_region = Some(HighlightRegion::BottomLeft);
-                            } else if hx > (logical_width as f64 - CORNER_THRESHOLD) {
-                                highlight_region = Some(HighlightRegion::BottomRight);
-                            } else {
-                                highlight_region = Some(HighlightRegion::Bottom);
-                            }
-                        }
-                    }
-                    _ => {}
-                }
-            }
-
-            // Draw visual border color with 3D cylindrical shading and mitred joints
-            let rx_start = rect_x * scale;
-            let ry_start = rect_y * scale;
-            let rx_end = (rect_x + rect_w) * scale;
-            let ry_end = (rect_y + rect_h) * scale;
-
-            let border_w_scaled = border_width * scale;
-            let win_w_scaled = (rect_w - 2 * border_width) * scale;
-            let grab_w_scaled = logical_height * scale;
-
-            for py in ry_start..ry_end {
-                if py >= 0 && py < dec_height {
-                    let row_offset = (py * dec_width) as usize;
-                    for px in rx_start..rx_end {
-                        if px >= 0 && px < dec_width {
-                            let s = if rect_w > rect_h {
-                                // Horizontal border (bottom)
-                                let logical_px = px - grab_w_scaled;
-                                let logical_py = py;
-
-                                if logical_px < 0 && logical_py < -logical_px {
-                                    // Left border region (outer is at t = 0)
-                                    let t_val = ((logical_px + border_w_scaled) as f32) / (border_w_scaled as f32 - 1.0).max(1.0);
-                                    if t_val < 0.5 {
-                                        1.0 + 0.45 * (1.0 - 2.0 * t_val) * (1.0 - 2.0 * t_val)
-                                    } else {
-                                        1.0
-                                    }
-                                } else if logical_px > win_w_scaled && logical_py < (logical_px - win_w_scaled) {
-                                    // Right border region (outer is at t = 0)
-                                    let t_val = 1.0 - ((logical_px - win_w_scaled) as f32) / (border_w_scaled as f32 - 1.0).max(1.0);
-                                    if t_val < 0.5 {
-                                        1.0 - 0.50 * (1.0 - 2.0 * t_val) * (1.0 - 2.0 * t_val)
-                                    } else {
-                                        1.0
-                                    }
-                                } else {
-                                    // Bottom border region (outer is at t = 0)
-                                    let t_val = 1.0 - (logical_py as f32) / (border_w_scaled as f32 - 1.0).max(1.0);
-                                    if t_val < 0.5 {
-                                        1.0 - 0.50 * (1.0 - 2.0 * t_val) * (1.0 - 2.0 * t_val)
-                                    } else {
-                                        1.0
-                                    }
-                                }
-                            } else {
-                                // Vertical border (left/right)
-                                let t_val = ((px - rx_start) as f32) / (border_w_scaled as f32 - 1.0).max(1.0);
-                                if offset_x < 0 {
-                                    // Left border: outer is at t = 0 (highlight)
-                                    if t_val < 0.5 {
-                                        1.0 + 0.45 * (1.0 - 2.0 * t_val) * (1.0 - 2.0 * t_val)
-                                    } else {
-                                        1.0
-                                    }
-                                } else {
-                                    // Right border: outer is at t = 1 (shadow)
-                                    if t_val > 0.5 {
-                                        1.0 - 0.50 * (2.0 * t_val - 1.0) * (2.0 * t_val - 1.0)
-                                    } else {
-                                        1.0
-                                    }
-                                }
-                            };
-
-                            let a = ((bg_color >> 24) & 0xFF) as f32;
-                            let mut r_val = (((bg_color >> 16) & 0xFF) as f32 * s).round().min(255.0) as u32;
-                            let mut g = (((bg_color >> 8) & 0xFF) as f32 * s).round().min(255.0) as u32;
-                            let mut b = ((bg_color & 0xFF) as f32 * s).round().min(255.0) as u32;
-
-                            if let Some(region) = highlight_region {
-                                if is_pixel_in_highlight_region(side, region, px, py, logical_width, logical_height, scale) {
-                                    r_val = (r_val as f32 * 0.6 + 255.0 * 0.4).round() as u32;
-                                    g = (g as f32 * 0.6 + 255.0 * 0.4).round() as u32;
-                                    b = (b as f32 * 0.6 + 255.0 * 0.4).round() as u32;
-                                }
-                            }
-
-                            buffer_slice[row_offset + px as usize] = ((a as u32) << 24)
-                                | (r_val << 16)
-                                | (g << 8)
-                                | b;
-                        }
-                    }
-                }
-            }
-
-            if round_bottom {
-                let r_val = (16.min(border_width)) * scale;
-                let cy = ry_end - r_val;
-                
-                // Bottom Left
-                let cx_l = rx_start + r_val;
-                round_corner(buffer_slice, dec_width, dec_height, cx_l, cy, r_val, CornerType::BottomLeft);
-
-                // Bottom Right
-                let cx_r = rx_end - r_val;
-                round_corner(buffer_slice, dec_width, dec_height, cx_r, cy, r_val, CornerType::BottomRight);
-            }
-        }
-
-        dec.surface.attach(Some(buffer), 0, 0);
-        dec.surface.damage(0, 0, dec_width, dec_height);
-        dec.surface.commit();
-    }
-}
-
-/// Main entry point to create or update decoration surfaces during rendering.
-pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>) {
-    // Check if we need to load or reload the font
-    let current_path = resolve_window_border_font_path();
-    if current_path != state.border_font_path {
-        state.border_font_path = current_path.clone();
-        state.border_font = current_path
-            .and_then(|path| std::fs::read(&path).ok())
-            .and_then(|data| fontdue::Font::from_bytes(data, fontdue::FontSettings::default()).ok());
-        if state.border_font.is_some() {
-            eprintln!("[decorations] loaded border font: {:?}", state.border_font_path);
-        } else {
-            eprintln!("[decorations] failed to load border font, falling back to built-in bitmap font");
-        }
-    }
-
-    let active_tags = state.wm.active_tags;
-
-    // We can only create decoration surfaces if the compositor and shm globals are bound
-    let (compositor, shm) = match (&state.compositor, &state.shm) {
-        (Some(c), Some(s)) => (c, s),
-        _ => return,
-    };
-
-    // Clean up decorations for windows that should not be decorated,
-    // or side borders for windows that do not have them (Popup, Fullscreen).
-    for (wid, wp) in &mut state.window_proxies {
-        if let Some(w) = state.wm.windows.iter().find(|win| win.id == *wid) {
-            let is_minimized = w.minimized;
-            let should_not_decorate = w.closed
-                || w.app_id.as_deref() == Some("cce-status-interface")
-                || w.app_id.as_deref().map_or(false, |aid| aid.contains("noborder"))
-                || w.tiling_mode == crate::types::TilingMode::Popup
-                || w.tiling_mode == crate::types::TilingMode::Fullscreen
-                || w.circular;
-            let needs_sides = !should_not_decorate && !is_minimized;
-            // eprintln!("[decorations] window {} minimized={} needs_sides={} dec_right_exists={}", w.id, is_minimized, needs_sides, wp.dec_right.is_some());
-
-            if should_not_decorate {
-                if let Some(dec) = wp.decoration.take() {
-                    dec.decoration.destroy();
-                    dec.surface.destroy();
-                }
-            }
-
-            if !needs_sides {
-                if let Some(dec) = wp.dec_left.take() {
-                    dec.decoration.destroy();
-                    dec.surface.destroy();
-                }
-                if let Some(dec) = wp.dec_right.take() {
-                    dec.decoration.destroy();
-                    dec.surface.destroy();
-                }
-                if let Some(dec) = wp.dec_bottom.take() {
-                    dec.decoration.destroy();
-                    dec.surface.destroy();
-                }
-            }
-        }
-    }
-
-    struct DecorateInfo {
-        id: u64,
-        win_width: i32,
-        win_height: i32,
-        border_width: i32,
-        title: String,
-        bg_color: u32,
-        tiling_mode: crate::types::TilingMode,
-        is_minimized: bool,
-        minimized_idx: Option<usize>,
-        win_y: i32,
-    }
-
-    // Calculate dynamic border colors
-    let border_colors = crate::borders::compute_border_colors(&state.wm);
-    let text_color = 0xFFE0E0E0u32;
-
-    // Collect window IDs to modify so we don't violate the borrow checker
-    let windows_to_decorate: Vec<DecorateInfo> = state
-        .wm
-        .windows
-        .iter()
-        .enumerate()
-        .filter(|(_, w)| {
-            !w.closed && w.app_id.as_deref() != Some("cce-status-interface") && !w.circular && !w.app_id.as_deref().map_or(false, |aid| aid.contains("noborder")) && (w.minimized || (w.tiling_mode != crate::types::TilingMode::Popup && w.tiling_mode != crate::types::TilingMode::Fullscreen))
-        })
-        .filter(|(_, w)| (w.tags & active_tags) != 0)
-        .map(|(idx, w)| {
-            let is_minimized = w.minimized;
-            let minimized_idx = if is_minimized {
-                state.wm.windows
-                    .iter()
-                    .filter(|win| !win.closed && win.minimized && (win.tags & active_tags) != 0 && win.app_id.as_deref() != Some("cce-status-interface"))
-                    .position(|win| win.id == w.id)
-            } else {
-                None
-            };
-            let title = w.title.clone().unwrap_or_else(|| {
-                w.app_id.clone().unwrap_or_else(|| "Window".to_string())
-            });
-            let mode_idx = if state.wm.expose_visual_active && w.tiling_mode != crate::types::TilingMode::Popup {
-                let list: Vec<_> = state.wm.windows
-                    .iter()
-                    .filter(|win| !win.closed && win.app_id.as_deref() != Some("cce-status-interface") && win.tiling_mode != crate::types::TilingMode::Popup && (win.tags & active_tags) != 0)
-                    .collect();
-                let len = list.len();
-                let pos = list.iter().position(|win| win.id == w.id).unwrap_or(0);
-                if len > 0 { len - 1 - pos } else { 0 }
-            } else {
-                let list: Vec<_> = state.wm.windows
-                    .iter()
-                    .filter(|win| !win.closed && win.app_id.as_deref() != Some("cce-status-interface") && (win.tags & active_tags) != 0 && win.tiling_mode == w.tiling_mode)
-                    .collect();
-                let len = list.len();
-                let pos = list.iter().position(|win| win.id == w.id).unwrap_or(0);
-                if len > 0 { len - 1 - pos } else { 0 }
-            };
-            let indicator = if is_minimized {
-                "M"
-            } else if state.wm.expose_visual_active && w.tiling_mode != crate::types::TilingMode::Popup {
-                "EX"
-            } else {
-                match w.tiling_mode {
-                    crate::types::TilingMode::Floating => "F",
-                    crate::types::TilingMode::Cascade => "C",
-                    crate::types::TilingMode::Grid => "G",
-                    crate::types::TilingMode::Fullscreen => "S",
-                    crate::types::TilingMode::Popup => "P",
-                    crate::types::TilingMode::SidePanel => "SP",
-                }
-            };
-            let title_with_idx = if is_minimized {
-                format!("[{}] {}", indicator, title)
-            } else {
-                format!("[{}{}] {}", indicator, mode_idx, title)
-            };
-
-            // Find matching computed border color for this window
-            let bc = border_colors.iter().find(|b| b.window_idx == idx);
-            let (r, g, b, a) = match bc {
-                Some(b) => (b.r, b.g, b.b, b.a),
-                None => (
-                    state.wm.layout.border_r,
-                    state.wm.layout.border_g,
-                    state.wm.layout.border_b,
-                    state.wm.layout.border_a,
-                ),
-            };
-
-            let border_r = (r & 0xFF) as u8;
-            let border_g = (g & 0xFF) as u8;
-            let border_b = (b & 0xFF) as u8;
-            let border_a = (a & 0xFF) as u8;
-
-            let r_premult = ((border_r as u32 * border_a as u32) / 255) as u8;
-            let g_premult = ((border_g as u32 * border_a as u32) / 255) as u8;
-            let b_premult = ((border_b as u32 * border_a as u32) / 255) as u8;
-
-            let bg_color = ((border_a as u32) << 24)
-                | ((r_premult as u32) << 16)
-                | ((g_premult as u32) << 8)
-                | (b_premult as u32);
-
-            // Border width is mode-specific
-            let border_width = if is_minimized {
-                state.wm.layout.grid_border_width
-            } else if state.wm.expose_visual_active && w.tiling_mode != crate::types::TilingMode::Popup {
-                state.wm.layout.grid_border_width
-            } else {
-                match w.tiling_mode {
-                    crate::types::TilingMode::Cascade => state.wm.layout.cascade_border_width,
-                    crate::types::TilingMode::Fullscreen => state.wm.layout.fullscreen_border_width,
-                    crate::types::TilingMode::Grid => state.wm.layout.grid_border_width,
-                    crate::types::TilingMode::Floating => state.wm.layout.floating_border_width,
-                    crate::types::TilingMode::Popup => 0,
-                    crate::types::TilingMode::SidePanel => state.wm.layout.cascade_border_width,
-                }
-            };
-            DecorateInfo {
-                id: w.id,
-                win_width: if w.committed_width > 0 { w.committed_width } else { w.width },
-                win_height: if w.committed_height > 0 { w.committed_height } else { w.height },
-                border_width,
-                title: title_with_idx,
-                bg_color,
-                tiling_mode: w.tiling_mode,
-                is_minimized,
-                minimized_idx,
-                win_y: w.y,
-            }
-        })
-        .collect();
-
-    for info in windows_to_decorate {
-        let wid = info.id;
-        let win_width = info.win_width;
-        let win_height = info.win_height;
-        let border_width = info.border_width;
-        let title = info.title;
-        let bg_color = info.bg_color;
-        let tiling_mode = info.tiling_mode;
-        let is_minimized = info.is_minimized;
-        let minimized_idx = info.minimized_idx;
-        let win_y = info.win_y;
-
-        // Find or create decoration proxy
-        let wp_idx = match state.window_proxies.iter().position(|(id, _)| *id == wid) {
-            Some(idx) => idx,
-            None => continue,
-        };
-
-        let wp = &mut state.window_proxies[wp_idx].1;
-
-        let scale = (state.wm.output_scale.round() as i32).max(1);
-
-        if tiling_mode != crate::types::TilingMode::Popup && tiling_mode != crate::types::TilingMode::Fullscreen && !is_minimized {
-            let grab_w = if tiling_mode == crate::types::TilingMode::Floating {
-                border_width.max(10)
-            } else {
-                border_width.max(1)
-            };
-
-            let hover_left = state.pointer_hovered_surface.as_ref()
-                .filter(|surf| wp.dec_left.as_ref().map_or(false, |d| &d.surface == *surf))
-                .map(|_| (state.last_pointer_surface_x, state.last_pointer_surface_y));
-
-            update_border_decoration(
-                wid,
-                &mut wp.dec_left,
-                compositor,
-                shm,
-                &wp.river_window,
-                qhandle,
-                -grab_w,
-                0,
-                grab_w,
-                win_height,
-                scale,
-                state.wm.layout.border_blur,
-                bg_color,
-                grab_w - border_width,
-                0,
-                border_width,
-                win_height,
-                border_width,
-                false,
-                "left",
-                hover_left,
-            );
-
-            let hover_right = state.pointer_hovered_surface.as_ref()
-                .filter(|surf| wp.dec_right.as_ref().map_or(false, |d| &d.surface == *surf))
-                .map(|_| (state.last_pointer_surface_x, state.last_pointer_surface_y));
-
-            update_border_decoration(
-                wid,
-                &mut wp.dec_right,
-                compositor,
-                shm,
-                &wp.river_window,
-                qhandle,
-                win_width,
-                0,
-                grab_w,
-                win_height,
-                scale,
-                state.wm.layout.border_blur,
-                bg_color,
-                0,
-                0,
-                border_width,
-                win_height,
-                border_width,
-                false,
-                "right",
-                hover_right,
-            );
-
-            let hover_bottom = state.pointer_hovered_surface.as_ref()
-                .filter(|surf| wp.dec_bottom.as_ref().map_or(false, |d| &d.surface == *surf))
-                .map(|_| (state.last_pointer_surface_x, state.last_pointer_surface_y));
-
-            update_border_decoration(
-                wid,
-                &mut wp.dec_bottom,
-                compositor,
-                shm,
-                &wp.river_window,
-                qhandle,
-                -grab_w,
-                win_height,
-                win_width + 2 * grab_w,
-                grab_w,
-                scale,
-                state.wm.layout.border_blur,
-                bg_color,
-                grab_w - border_width,
-                0,
-                win_width + 2 * border_width,
-                border_width,
-                border_width,
-                true,
-                "bottom",
-                hover_bottom,
-            );
-        }
-
-        // Determine titlebar dimensions:
-        // Height equals border_width (or 16 if border_width is too small to display font)
-        let logical_height = std::cmp::max(border_width, 16);
-        let logical_width = if is_minimized {
-            160
-        } else {
-            win_width + 2 * border_width
-        };
-        let dec_height = logical_height * scale;
-        let dec_width = logical_width * scale;
-
-        let needs_new_buffer = match &wp.decoration {
-            Some(dec) => dec.width != dec_width || dec.height != dec_height,
-            None => true,
-        };
-
-        if wp.decoration.is_none() {
-            eprintln!("[decorations] creating decoration for window {}", wid);
-            let surface: wl_surface::WlSurface = compositor.create_surface(qhandle, ());
-            let decoration: RiverDecorationV1 = wp.river_window.get_decoration_above(&surface, qhandle, ());
-
-            wp.decoration = Some(WindowDecoration {
-                decoration,
-                surface,
-                buffer: None,
-                pool: None,
-                width: 0,
-                height: 0,
-                mapped_data: ptr::null_mut(),
-                mapped_size: 0,
-            });
-        }
-
-        let hover_top = state.pointer_hovered_surface.as_ref()
-            .filter(|surf| wp.decoration.as_ref().map_or(false, |d| &d.surface == *surf))
-            .map(|_| (state.last_pointer_surface_x, state.last_pointer_surface_y));
-
-        let dec = wp.decoration.as_mut().unwrap();
-
-        // Position decoration on top of the window top border
-        if is_minimized {
-            if let Some(idx) = minimized_idx {
-                let bar_height = state.wm.layout.bar_height;
-                let gap_top = state.wm.layout.gap_top;
-                let bubble_gap = 8;
-                let bubble_y = bar_height + gap_top + idx as i32 * (logical_height + bubble_gap);
-                dec.decoration.set_offset(0, bubble_y - win_y);
-            }
-        } else {
-            dec.decoration.set_offset(-border_width, -logical_height);
-        }
-
-        dec.decoration.set_blur(if state.wm.layout.border_blur { 1 } else { 0 });
-
-        if needs_new_buffer {
-            eprintln!(
-                "[decorations] allocating buffer {}x{} for window {}",
-                dec_width, dec_height, wid
-            );
-
-            // Clean up old mapping if it exists
-            if !dec.mapped_data.is_null() {
-                unsafe {
-                    libc::munmap(dec.mapped_data as *mut libc::c_void, dec.mapped_size);
-                }
-                dec.mapped_data = ptr::null_mut();
-            }
-
-            let stride = dec_width * 4;
-            let size = (stride * dec_height) as usize;
-
-            if let Some(fd) = create_memfd(size) {
-                let mapped_data = unsafe {
-                    libc::mmap(
-                        ptr::null_mut(),
-                        size,
-                        libc::PROT_READ | libc::PROT_WRITE,
-                        libc::MAP_SHARED,
-                        fd,
-                        0,
-                    ) as *mut u32
-                };
-
-                if mapped_data != libc::MAP_FAILED as *mut u32 {
-                    let borrowed_fd = unsafe { std::os::fd::BorrowedFd::borrow_raw(fd) };
-                    let pool = shm.create_pool(borrowed_fd, size as i32, qhandle, ());
-                    let buffer = pool.create_buffer(
-                        0,
-                        dec_width,
-                        dec_height,
-                        stride,
-                        wl_shm::Format::Argb8888,
-                        qhandle,
-                        (),
-                    );
-
-                    dec.pool = Some(pool);
-                    dec.buffer = Some(buffer);
-                    dec.width = dec_width;
-                    dec.height = dec_height;
-                    dec.mapped_data = mapped_data;
-                    dec.mapped_size = size;
-                    dec.surface.set_buffer_scale(scale);
-                } else {
-                    eprintln!("[decorations] failed to mmap decoration buffer");
-                    unsafe { libc::close(fd); }
-                    continue;
-                }
-                unsafe { libc::close(fd); }
-            } else {
-                eprintln!("[decorations] failed to create memfd");
-                continue;
-            }
-        }
-
-        // Draw titlebar background and text
-        if !dec.mapped_data.is_null() {
-            let buffer_slice = unsafe {
-                std::slice::from_raw_parts_mut(dec.mapped_data, (dec_width * dec_height) as usize)
-            };
-
-            // Clear background with 3D cylindrical shading and mitred corners (outer edge only)
-            let border_w_scaled = border_width * scale;
-            let titlebar_h_scaled = logical_height * scale;
-            let win_w_scaled = (if is_minimized { 160 - 2 * border_width } else { win_width }) * scale;
-
-            let mut highlight_region: Option<HighlightRegion> = None;
-            if let Some((hx, hy)) = hover_top {
-                let mid_y = (logical_height as f64) / 2.0;
-                if hy < mid_y {
-                    const CORNER_THRESHOLD: f64 = 16.0;
-                    if hx < CORNER_THRESHOLD {
-                        highlight_region = Some(HighlightRegion::TopLeft);
-                    } else if hx > (logical_width as f64 - CORNER_THRESHOLD) {
-                        highlight_region = Some(HighlightRegion::TopRight);
-                    } else {
-                        highlight_region = Some(HighlightRegion::Top);
-                    }
-                }
-            }
-
-            for py in 0..dec_height {
-                let row_offset = (py * dec_width) as usize;
-                for px in 0..dec_width {
-                    // Convert to coordinates relative to client area (0, 0)
-                    let logical_px = px - border_w_scaled;
-                    let logical_py = py - titlebar_h_scaled;
-
-                    // Determine which border region this pixel belongs to, using the exact diagonal slope:
-                    let slope = (titlebar_h_scaled as f32) / (border_w_scaled as f32).max(1.0);
-                    let s = if logical_px < 0 && logical_py > (logical_px as f32 * slope) as i32 {
-                        // Left border region (outer is at t = 0)
-                        let t_val = ((logical_px + border_w_scaled) as f32) / (border_w_scaled as f32 - 1.0).max(1.0);
-                        if t_val < 0.5 {
-                            1.0 + 0.45 * (1.0 - 2.0 * t_val) * (1.0 - 2.0 * t_val)
-                        } else {
-                            1.0
-                        }
-                    } else if logical_px > win_w_scaled && logical_py > (-((logical_px - win_w_scaled) as f32 * slope)) as i32 {
-                        // Right border region (outer is at t = 0)
-                        let t_val = 1.0 - ((logical_px - win_w_scaled) as f32) / (border_w_scaled as f32 - 1.0).max(1.0);
-                        if t_val < 0.5 {
-                            1.0 - 0.50 * (1.0 - 2.0 * t_val) * (1.0 - 2.0 * t_val)
-                        } else {
-                            1.0
-                        }
-                    } else {
-                        // Top border region (outer is at t = 0)
-                        let t_val = ((logical_py + titlebar_h_scaled) as f32) / (titlebar_h_scaled as f32 - 1.0).max(1.0);
-                        if t_val < 0.5 {
-                            1.0 + 0.45 * (1.0 - 2.0 * t_val) * (1.0 - 2.0 * t_val)
-                        } else {
-                            1.0
-                        }
-                    };
-
-                    let a = ((bg_color >> 24) & 0xFF) as f32;
-                    let mut r_val = (((bg_color >> 16) & 0xFF) as f32 * s).round().min(255.0) as u32;
-                    let mut g = (((bg_color >> 8) & 0xFF) as f32 * s).round().min(255.0) as u32;
-                    let mut b = ((bg_color & 0xFF) as f32 * s).round().min(255.0) as u32;
-
-                    if let Some(region) = highlight_region {
-                        if is_pixel_in_highlight_region("top", region, px, py, logical_width, logical_height, scale) {
-                            r_val = (r_val as f32 * 0.6 + 255.0 * 0.4).round() as u32;
-                            g = (g as f32 * 0.6 + 255.0 * 0.4).round() as u32;
-                            b = (b as f32 * 0.6 + 255.0 * 0.4).round() as u32;
-                        }
-                    }
-
-                    let base_pixel_color = ((a as u32) << 24) | (r_val << 16) | (g << 8) | b;
-                    let mut final_color = base_pixel_color;
-
-                    let lx = px as f32 / scale as f32;
-                    let ly = py as f32 / scale as f32;
-
-                    if !is_minimized && ly >= 0.0 && ly < logical_height as f32 {
-                        if lx >= (logical_width as f32 - 48.0) && lx < (logical_width as f32 - 32.0) {
-                            // Minimize button
-                            let is_hovered = hover_top.map_or(false, |(hx, hy)| {
-                                hx >= (logical_width as f64 - 48.0) && hx < (logical_width as f64 - 32.0)
-                                    && hy >= 0.0 && hy < logical_height as f64
-                            });
-                            final_color = if is_hovered {
-                                blend_colors(base_pixel_color, 0x30FFFFFF)
-                            } else {
-                                base_pixel_color
-                            };
-
-                            let cx = logical_width as f32 - 40.0;
-                            let cy = logical_height as f32 / 2.0;
-                            if lx >= (cx - 4.0) && lx <= (cx + 4.0) && ly >= (cy + 2.0) && ly < (cy + 3.0) {
-                                final_color = text_color;
-                            }
-                        } else if lx >= (logical_width as f32 - 32.0) && lx < (logical_width as f32 - 16.0) {
-                            // Maximize button
-                            let is_hovered = hover_top.map_or(false, |(hx, hy)| {
-                                hx >= (logical_width as f64 - 32.0) && hx < (logical_width as f64 - 16.0)
-                                    && hy >= 0.0 && hy < logical_height as f64
-                            });
-                            final_color = if is_hovered {
-                                blend_colors(base_pixel_color, 0x30FFFFFF)
-                            } else {
-                                base_pixel_color
-                            };
-
-                            let cx = logical_width as f32 - 24.0;
-                            let cy = logical_height as f32 / 2.0;
-                            let is_border_x = (lx >= cx - 4.0 && lx < cx - 3.0) || (lx > cx + 3.0 && lx <= cx + 4.0);
-                            let is_in_x = lx >= cx - 4.0 && lx <= cx + 4.0;
-                            let is_border_y = (ly >= cy - 4.0 && ly < cy - 3.0) || (ly > cy + 3.0 && ly <= cy + 4.0);
-                            let is_in_y = ly >= cy - 4.0 && ly <= cy + 4.0;
-                            if (is_border_x && is_in_y) || (is_border_y && is_in_x) {
-                                final_color = text_color;
-                            }
-                        } else if lx >= (logical_width as f32 - 16.0) && lx <= logical_width as f32 {
-                            // Close button
-                            let is_hovered = hover_top.map_or(false, |(hx, hy)| {
-                                hx >= (logical_width as f64 - 16.0) && hx <= logical_width as f64
-                                    && hy >= 0.0 && hy < logical_height as f64
-                            });
-                            final_color = if is_hovered {
-                                blend_colors(base_pixel_color, 0x90E53935)
-                            } else {
-                                base_pixel_color
-                            };
-
-                            let cx = logical_width as f32 - 8.0;
-                            let cy = logical_height as f32 / 2.0;
-                            let dx = (lx - cx).abs();
-                            let dy = (ly - cy).abs();
-                            if dx <= 4.0 && dy <= 4.0 && (dx - dy).abs() < 1.0 {
-                                final_color = text_color;
-                            }
-                        }
-                    }
-
-                    buffer_slice[row_offset + px as usize] = final_color;
-                }
-            }
-
-            // Draw window title text
-            if let Some(ref font) = state.border_font {
-                let font_size = if state.wm.layout.border_font_size > 0 {
-                    state.wm.layout.border_font_size as f32 * scale as f32
-                } else {
-                    if logical_height >= 32 {
-                        22.0 * scale as f32
-                    } else if logical_height >= 24 {
-                        16.0 * scale as f32
-                    } else if logical_height >= 16 {
-                        11.0 * scale as f32
-                    } else {
-                        (logical_height as f32 - 4.0).max(8.0) * scale as f32
-                    }
-                };
-
-                let line_metrics = font.horizontal_line_metrics(font_size).unwrap_or(fontdue::LineMetrics {
-                    ascent: font_size * 0.8,
-                    descent: -font_size * 0.2,
-                    line_gap: 0.0,
-                    new_line_size: font_size,
-                });
-                let baseline_y = (dec_height as f32 + line_metrics.ascent + line_metrics.descent) / 2.0;
-
-                let mut text_x = 24.0f32 * scale as f32; // Margin from left
-                for c in title.chars() {
-                    let (metrics, bitmap) = font.rasterize(c, font_size);
-                    if text_x + metrics.xmin as f32 + metrics.width as f32 > dec_width as f32 - 48.0 * scale as f32 {
-                        break;
-                    }
-
-                    let x_start = (text_x + metrics.xmin as f32).round() as i32;
-                    let y_start = (baseline_y - metrics.ymin as f32 - metrics.height as f32).round() as i32;
-
-                    for row in 0..metrics.height {
-                        for col in 0..metrics.width {
-                            let px = x_start + col as i32;
-                            let py = y_start + row as i32;
-
-                            if px >= 0 && px < dec_width && py >= 0 && py < dec_height {
-                                let alpha_coverage = bitmap[row * metrics.width + col] as u32;
-                                if alpha_coverage > 0 {
-                                    let index = (py * dec_width + px) as usize;
-                                    let dest_pixel = buffer_slice[index];
-
-                                    let src_r = (text_color >> 16) & 0xFF;
-                                    let src_g = (text_color >> 8) & 0xFF;
-                                    let src_b = text_color & 0xFF;
-                                    let src_a = (text_color >> 24) & 0xFF;
-
-                                    let dest_r = (dest_pixel >> 16) & 0xFF;
-                                    let dest_g = (dest_pixel >> 8) & 0xFF;
-                                    let dest_b = dest_pixel & 0xFF;
-                                    let dest_a = (dest_pixel >> 24) & 0xFF;
-
-                                    let alpha = (alpha_coverage * src_a) / 255;
-
-                                    let out_r = ((src_r * alpha) + (dest_r * (255 - alpha))) / 255;
-                                    let out_g = ((src_g * alpha) + (dest_g * (255 - alpha))) / 255;
-                                    let out_b = ((src_b * alpha) + (dest_b * (255 - alpha))) / 255;
-                                    let out_a = dest_a + ((255 - dest_a) * alpha) / 255;
-
-                                    buffer_slice[index] = (out_a << 24) | (out_r << 16) | (out_g << 8) | out_b;
-                                }
-                            }
-                        }
-                    }
-                    text_x += metrics.advance_width;
-                }
-            } else {
-                // Determine font scale (1x if height < 32, 2x if height >= 32)
-                let drawing_scale = if logical_height >= 32 { 2 } else { 1 };
-                let font_h = 8 * drawing_scale * scale;
-                
-                // Vertically center the text inside the titlebar
-                let text_y = (dec_height - font_h) / 2;
-                let mut text_x = 24 * scale; // Margin from left
-
-                for c in title.chars() {
-                    if text_x + 8 * drawing_scale * scale > dec_width - 48 * scale {
-                        break; // Out of bounds
-                    }
-                    draw_char(buffer_slice, dec_width, dec_height, c, text_x, text_y, drawing_scale * scale, text_color);
-                    text_x += 8 * drawing_scale * scale;
-                }
-            }
-
-            // Apply corner rounding to top-left and top-right of titlebar
-            let r_top = 16 * scale;
-            round_corner(buffer_slice, dec_width, dec_height, r_top, r_top, r_top, CornerType::TopLeft);
-            round_corner(buffer_slice, dec_width, dec_height, dec_width - r_top, r_top, r_top, CornerType::TopRight);
-
-            // Commit surface rendering
-            if !is_minimized {
-                dec.decoration.sync_next_commit();
-            }
-            if let Some(ref wl_buf) = dec.buffer {
-                dec.surface.attach(Some(wl_buf), 0, 0);
-            }
-            dec.surface.commit();
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum HighlightRegion {
-    TopLeft,
-    Top,
-    TopRight,
-    Left,
-    Right,
-    BottomLeft,
-    Bottom,
-    BottomRight,
-}
-
-fn is_pixel_in_highlight_region(
-    side: &str,
-    region: HighlightRegion,
-    px: i32,
-    py: i32,
-    logical_width: i32,
-    logical_height: i32,
-    scale: i32,
-) -> bool {
-    let px_logical = px as f32 / scale as f32;
-    let py_logical = py as f32 / scale as f32;
-    const CORNER_THRESHOLD: f32 = 16.0;
-
-    match (side, region) {
-        ("left", HighlightRegion::TopLeft) => py_logical < CORNER_THRESHOLD,
-        ("left", HighlightRegion::BottomLeft) => py_logical > (logical_height as f32 - CORNER_THRESHOLD),
-        ("left", HighlightRegion::Left) => py_logical >= CORNER_THRESHOLD && py_logical <= (logical_height as f32 - CORNER_THRESHOLD),
-
-        ("right", HighlightRegion::TopRight) => py_logical < CORNER_THRESHOLD,
-        ("right", HighlightRegion::BottomRight) => py_logical > (logical_height as f32 - CORNER_THRESHOLD),
-        ("right", HighlightRegion::Right) => py_logical >= CORNER_THRESHOLD && py_logical <= (logical_height as f32 - CORNER_THRESHOLD),
-
-        ("bottom", HighlightRegion::BottomLeft) => px_logical < CORNER_THRESHOLD,
-        ("bottom", HighlightRegion::BottomRight) => px_logical > (logical_width as f32 - CORNER_THRESHOLD),
-        ("bottom", HighlightRegion::Bottom) => px_logical >= CORNER_THRESHOLD && px_logical <= (logical_width as f32 - CORNER_THRESHOLD),
-
-        ("top", HighlightRegion::TopLeft) => px_logical < CORNER_THRESHOLD,
-        ("top", HighlightRegion::TopRight) => px_logical > (logical_width as f32 - CORNER_THRESHOLD),
-        ("top", HighlightRegion::Top) => px_logical >= CORNER_THRESHOLD && px_logical <= (logical_width as f32 - CORNER_THRESHOLD),
-
-        _ => false,
-    }
-}
-
-fn blend_colors(bg: u32, fg: u32) -> u32 {
-    let fg_a = (fg >> 24) & 0xFF;
-    if fg_a == 0 {
-        return bg;
-    }
-    if fg_a == 255 {
-        return fg;
-    }
-    let bg_a = (bg >> 24) & 0xFF;
-    let bg_r = (bg >> 16) & 0xFF;
-    let bg_g = (bg >> 8) & 0xFF;
-    let bg_b = bg & 0xFF;
-
-    let fg_r = (fg >> 16) & 0xFF;
-    let fg_g = (fg >> 8) & 0xFF;
-    let fg_b = fg & 0xFF;
-
-    let out_r = ((fg_r * fg_a) + (bg_r * (255 - fg_a))) / 255;
-    let out_g = ((fg_g * fg_a) + (bg_g * (255 - fg_a))) / 255;
-    let out_b = ((fg_b * fg_a) + (bg_b * (255 - fg_a))) / 255;
-    let out_a = bg_a + ((255 - bg_a) * fg_a) / 255;
-
-    (out_a << 24) | (out_r << 16) | (out_g << 8) | out_b
-}
diff --git a/src/client/input.rs b/src/client/input.rs
deleted file mode 100644
index 28b2401..0000000
--- a/src/client/input.rs
+++ /dev/null
@@ -1,1063 +0,0 @@
-use std::collections::HashSet;
-use std::fs::{self, OpenOptions};
-use std::os::unix::fs::OpenOptionsExt;
-use std::os::unix::io::AsRawFd;
-use std::path::PathBuf;
-use std::time::Instant;
-use std::sync::atomic::{AtomicI32, Ordering};
-use tokio::io::AsyncReadExt;
-use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
-use tokio::time::{sleep, Duration};
-use crate::config::InertialConfig;
-
-pub static POINTER_X: AtomicI32 = AtomicI32::new(0);
-pub static POINTER_Y: AtomicI32 = AtomicI32::new(0);
-pub static SCREEN_WIDTH: AtomicI32 = AtomicI32::new(1920);
-pub static SCREEN_HEIGHT: AtomicI32 = AtomicI32::new(1080);
-
-pub fn update_pointer_coords(dx: i32, dy: i32) {
-    let screen_w = SCREEN_WIDTH.load(Ordering::SeqCst);
-    let screen_h = SCREEN_HEIGHT.load(Ordering::SeqCst);
-    let mut current_x = POINTER_X.load(Ordering::SeqCst);
-    let mut current_y = POINTER_Y.load(Ordering::SeqCst);
-
-    current_x = (current_x + dx).clamp(0, screen_w);
-    current_y = (current_y + dy).clamp(0, screen_h);
-
-    POINTER_X.store(current_x, Ordering::SeqCst);
-    POINTER_Y.store(current_y, Ordering::SeqCst);
-}
-
-pub fn set_pointer_coords(x: i32, y: i32) {
-    let screen_w = SCREEN_WIDTH.load(Ordering::SeqCst);
-    let screen_h = SCREEN_HEIGHT.load(Ordering::SeqCst);
-    let current_x = x.clamp(0, screen_w);
-    let current_y = y.clamp(0, screen_h);
-
-    POINTER_X.store(current_x, Ordering::SeqCst);
-    POINTER_Y.store(current_y, Ordering::SeqCst);
-}
-
-// IOCTL and Event constants
-const UI_DEV_CREATE: libc::c_ulong = 0x5501;
-const UI_DEV_SETUP: libc::c_ulong = 0x405C5503;
-const UI_SET_EVBIT: libc::c_ulong = 0x40045564;
-const UI_SET_RELBIT: libc::c_ulong = 0x40045566;
-const UI_SET_KEYBIT: libc::c_ulong = 0x40045565;
-
-// Linux input event codes
-const EV_SYN: u16 = 0x00;
-const EV_KEY: u16 = 0x01;
-const EV_REL: u16 = 0x02;
-const EV_ABS: u16 = 0x03;
-
-const REL_X: u16 = 0x00;
-const REL_Y: u16 = 0x01;
-const REL_HWHEEL: u16 = 0x06;
-const REL_WHEEL: u16 = 0x08;
-
-const ABS_X: u16 = 0x00;
-const ABS_Y: u16 = 0x01;
-const ABS_MT_POSITION_X: u16 = 0x35;
-const ABS_MT_POSITION_Y: u16 = 0x36;
-const ABS_MT_TRACKING_ID: u16 = 0x39;
-
-const BTN_TOUCH: u16 = 0x14a;
-
-const SYN_REPORT: u16 = 0x00;
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-struct InputId {
-    bustype: u16,
-    vendor: u16,
-    product: u16,
-    version: u16,
-}
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-struct UinputSetup {
-    id: InputId,
-    name: [u8; 80],
-    ff_effects_max: u32,
-}
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-struct InputEvent {
-    tv_sec: i64,
-    tv_usec: i64,
-    type_: u16,
-    code: u16,
-    value: i32,
-}
-
-const UI_SET_ABSBIT: libc::c_ulong = 0x40045567;
-const UI_ABS_SETUP: libc::c_ulong = 1075598596; // 0x40185568
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-struct InputAbsinfo {
-    value: i32,
-    minimum: i32,
-    maximum: i32,
-    fuzz: i32,
-    flat: i32,
-    resolution: i32,
-}
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-struct UinputAbsSetup {
-    code: u16,
-    _padding: u16,
-    absinfo: InputAbsinfo,
-}
-
-#[derive(Debug, Clone, serde::Serialize)]
-pub struct FingerState {
-    pub slot: usize,
-    pub x: f32,
-    pub y: f32,
-}
-
-#[derive(Debug, Clone, Copy, Default)]
-struct SlotState {
-    active: bool,
-    x: f32,
-    y: f32,
-    raw_x: Option<i32>,
-    raw_y: Option<i32>,
-}
-
-fn eviocgabs(abs: u32) -> libc::c_ulong {
-    let size = std::mem::size_of::<libc::input_absinfo>() as u64;
-    ((2u64 << 30) | ((b'E' as u64) << 8) | ((0x40 + abs) as u64) | (size << 16)) as libc::c_ulong
-}
-
-const ABS_MT_SLOT: u16 = 0x2f;
-
-#[derive(Debug, Clone)]
-pub enum InputDaemonMsg {
-    UpdateConfig(InertialConfig, bool),
-    SimulateMoveTo { x: i32, y: i32 },
-    SimulateMoveBy { dx: i32, dy: i32 },
-    SimulateButton { button: u16, press: bool },
-    SimulateKey { keycode: u16, press: bool },
-    SimulateClick { button: u16 },
-    SimulateKeyPress { keycode: u16 },
-    SimulateScroll { dx: i32, dy: i32 },
-}
-
-#[derive(Debug)]
-enum CoordinatorMsg {
-    PhysicalMove { dx: i32, dy: i32, timestamp: Instant },
-    PhysicalTrackpadMove { dx: i32, dy: i32, timestamp: Instant },
-    PhysicalTrackpadLift { timestamp: Instant },
-    PhysicalScroll { dwx: i32, dwy: i32, timestamp: Instant },
-    FingersReport(Vec<FingerState>),
-    DaemonMsg(InputDaemonMsg),
-    InternalReleaseButton { button: u16 },
-    InternalReleaseKey { keycode: u16 },
-}
-
-fn setup_uinput_mouse() -> std::io::Result<std::fs::File> {
-    let file = OpenOptions::new()
-        .write(true)
-        .custom_flags(libc::O_NONBLOCK)
-        .open("/dev/uinput")?;
-
-    let fd = file.as_raw_fd();
-
-    unsafe {
-        if libc::ioctl(fd, UI_SET_EVBIT, EV_REL as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        if libc::ioctl(fd, UI_SET_RELBIT, REL_X as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        if libc::ioctl(fd, UI_SET_RELBIT, REL_Y as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        if libc::ioctl(fd, UI_SET_RELBIT, REL_WHEEL as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        if libc::ioctl(fd, UI_SET_RELBIT, REL_HWHEEL as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-
-        if libc::ioctl(fd, UI_SET_EVBIT, EV_KEY as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        for key in 1..=511 {
-            if libc::ioctl(fd, UI_SET_KEYBIT, key as libc::c_int) < 0 {
-                return Err(std::io::Error::last_os_error());
-            }
-        }
-        for btn in 272..=276 {
-            if libc::ioctl(fd, UI_SET_KEYBIT, btn as libc::c_int) < 0 {
-                return Err(std::io::Error::last_os_error());
-            }
-        }
-    }
-
-    let mut setup = UinputSetup {
-        id: InputId {
-            bustype: 0x0006, // BUS_VIRTUAL
-            vendor: 0x1234,
-            product: 0x5678,
-            version: 1,
-        },
-        name: [0; 80],
-        ff_effects_max: 0,
-    };
-
-    let name_bytes = b"Clear Virtual Mouse";
-    setup.name[..name_bytes.len()].copy_from_slice(name_bytes);
-
-    unsafe {
-        let setup_ptr = &setup as *const UinputSetup as *const libc::c_void;
-        if libc::ioctl(fd, UI_DEV_SETUP, setup_ptr) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        if libc::ioctl(fd, UI_DEV_CREATE) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-    }
-
-    println!("[input-subsystem] Successfully created virtual uinput mouse device.");
-    Ok(file)
-}
-
-fn setup_uinput_abs() -> std::io::Result<std::fs::File> {
-    let file = OpenOptions::new()
-        .write(true)
-        .custom_flags(libc::O_NONBLOCK)
-        .open("/dev/uinput")?;
-
-    let fd = file.as_raw_fd();
-
-    unsafe {
-        if libc::ioctl(fd, UI_SET_EVBIT, EV_ABS as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        if libc::ioctl(fd, UI_SET_ABSBIT, ABS_X as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        let abs_x_setup = UinputAbsSetup {
-            code: ABS_X,
-            _padding: 0,
-            absinfo: InputAbsinfo {
-                value: 0,
-                minimum: 0,
-                maximum: 1920,
-                fuzz: 0,
-                flat: 0,
-                resolution: 1,
-            },
-        };
-        if libc::ioctl(fd, UI_ABS_SETUP, &abs_x_setup as *const UinputAbsSetup as *const libc::c_void) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-
-        if libc::ioctl(fd, UI_SET_ABSBIT, ABS_Y as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        let abs_y_setup = UinputAbsSetup {
-            code: ABS_Y,
-            _padding: 0,
-            absinfo: InputAbsinfo {
-                value: 0,
-                minimum: 0,
-                maximum: 1200,
-                fuzz: 0,
-                flat: 0,
-                resolution: 1,
-            },
-        };
-        if libc::ioctl(fd, UI_ABS_SETUP, &abs_y_setup as *const UinputAbsSetup as *const libc::c_void) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-
-        if libc::ioctl(fd, UI_SET_EVBIT, EV_KEY as libc::c_int) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        for btn in 272..=276 {
-            if libc::ioctl(fd, UI_SET_KEYBIT, btn as libc::c_int) < 0 {
-                return Err(std::io::Error::last_os_error());
-            }
-        }
-    }
-
-    let mut setup = UinputSetup {
-        id: InputId {
-            bustype: 0x0006, // BUS_VIRTUAL
-            vendor: 0x1234,
-            product: 0x5679,
-            version: 1,
-        },
-        name: [0; 80],
-        ff_effects_max: 0,
-    };
-
-    let name_bytes = b"Clear Virtual Absolute Pointer";
-    setup.name[..name_bytes.len()].copy_from_slice(name_bytes);
-
-    unsafe {
-        let setup_ptr = &setup as *const UinputSetup as *const libc::c_void;
-        if libc::ioctl(fd, UI_DEV_SETUP, setup_ptr) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-        if libc::ioctl(fd, UI_DEV_CREATE) < 0 {
-            return Err(std::io::Error::last_os_error());
-        }
-    }
-
-    println!("[input-subsystem] Successfully created virtual uinput absolute pointer device.");
-    Ok(file)
-}
-
-fn write_raw_event(file: &mut std::fs::File, type_: u16, code: u16, value: i32) -> std::io::Result<()> {
-    let event = InputEvent {
-        tv_sec: 0,
-        tv_usec: 0,
-        type_,
-        code,
-        value,
-    };
-    let buf = unsafe {
-        std::slice::from_raw_parts(&event as *const InputEvent as *const u8, std::mem::size_of::<InputEvent>())
-    };
-    use std::io::Write;
-    file.write_all(buf)?;
-    file.flush()?;
-    Ok(())
-}
-
-
-fn write_mouse_absolute(file: &mut std::fs::File, x: i32, y: i32) -> std::io::Result<()> {
-    write_raw_event(file, EV_ABS, ABS_X, x)?;
-    write_raw_event(file, EV_ABS, ABS_Y, y)?;
-    write_raw_event(file, EV_SYN, SYN_REPORT, 0)?;
-    Ok(())
-}
-
-fn write_mouse_relative(file: &mut std::fs::File, dx: i32, dy: i32) -> std::io::Result<()> {
-    if dx == 0 && dy == 0 {
-        return Ok(());
-    }
-    if dx != 0 {
-        write_raw_event(file, EV_REL, REL_X, dx)?;
-    }
-    if dy != 0 {
-        write_raw_event(file, EV_REL, REL_Y, dy)?;
-    }
-    write_raw_event(file, EV_SYN, SYN_REPORT, 0)?;
-    Ok(())
-}
-
-fn write_scroll(file: &mut std::fs::File, dwx: i32, dwy: i32) -> std::io::Result<()> {
-    if dwx != 0 {
-        write_raw_event(file, EV_REL, REL_HWHEEL, dwx)?;
-    }
-    if dwy != 0 {
-        write_raw_event(file, EV_REL, REL_WHEEL, dwy)?;
-    }
-    write_raw_event(file, EV_SYN, SYN_REPORT, 0)?;
-    Ok(())
-}
-
-struct TouchTracker {
-    dx: i32,
-    dy: i32,
-    finger_down: bool,
-}
-
-async fn read_device_loop(
-    path: PathBuf,
-    tx: UnboundedSender<CoordinatorMsg>,
-) -> std::io::Result<()> {
-    println!("[input-subsystem] Started monitoring {:?}", path);
-    let mut file = tokio::fs::File::open(&path).await?;
-    let fd = file.as_raw_fd();
-
-    let mut min_x = 0;
-    let mut max_x = 3528;
-    let mut min_y = 0;
-    let mut max_y = 2006;
-
-    let mut absinfo = libc::input_absinfo {
-        value: 0, minimum: 0, maximum: 0, fuzz: 0, flat: 0, resolution: 0
-    };
-    unsafe {
-        if libc::ioctl(fd, eviocgabs(ABS_MT_POSITION_X as u32), &mut absinfo) >= 0 {
-            min_x = absinfo.minimum;
-            max_x = absinfo.maximum;
-        } else if libc::ioctl(fd, eviocgabs(ABS_X as u32), &mut absinfo) >= 0 {
-            min_x = absinfo.minimum;
-            max_x = absinfo.maximum;
-        }
-        if libc::ioctl(fd, eviocgabs(ABS_MT_POSITION_Y as u32), &mut absinfo) >= 0 {
-            min_y = absinfo.minimum;
-            max_y = absinfo.maximum;
-        } else if libc::ioctl(fd, eviocgabs(ABS_Y as u32), &mut absinfo) >= 0 {
-            min_y = absinfo.minimum;
-            max_y = absinfo.maximum;
-        }
-    }
-
-    let range_x = (max_x - min_x).max(1) as f32;
-    let range_y = (max_y - min_y).max(1) as f32;
-
-    let mut slots = [SlotState::default(); 16];
-    let mut current_slot = 0usize;
-    let mut buf = [0u8; 24];
-
-    let mut touch = TouchTracker {
-        dx: 0,
-        dy: 0,
-        finger_down: false,
-    };
-
-    loop {
-        file.read_exact(&mut buf).await?;
-        let event: InputEvent = unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const InputEvent) };
-        let now = Instant::now();
-
-        if event.type_ == EV_REL {
-            if event.code == REL_X {
-                let _ = tx.send(CoordinatorMsg::PhysicalMove { dx: event.value, dy: 0, timestamp: now });
-            } else if event.code == REL_Y {
-                let _ = tx.send(CoordinatorMsg::PhysicalMove { dx: 0, dy: event.value, timestamp: now });
-            } else if event.code == REL_WHEEL {
-                let _ = tx.send(CoordinatorMsg::PhysicalScroll { dwx: 0, dwy: event.value, timestamp: now });
-            } else if event.code == REL_HWHEEL {
-                let _ = tx.send(CoordinatorMsg::PhysicalScroll { dwx: event.value, dwy: 0, timestamp: now });
-            }
-        } else if event.type_ == EV_ABS {
-            if event.code == ABS_X || event.code == ABS_MT_POSITION_X {
-                let val = event.value;
-                let slot_idx = if event.code == ABS_X { 0 } else { current_slot };
-                let is_primary = slots.iter().position(|s| s.active) == Some(slot_idx);
-                let slot = &mut slots[slot_idx];
-                if is_primary {
-                    if let Some(lx) = slot.raw_x {
-                        touch.dx += val - lx;
-                    }
-                }
-                slot.raw_x = Some(val);
-                slot.x = (val - min_x) as f32 / range_x;
-            } else if event.code == ABS_Y || event.code == ABS_MT_POSITION_Y {
-                let val = event.value;
-                let slot_idx = if event.code == ABS_Y { 0 } else { current_slot };
-                let is_primary = slots.iter().position(|s| s.active) == Some(slot_idx);
-                let slot = &mut slots[slot_idx];
-                if is_primary {
-                    if let Some(ly) = slot.raw_y {
-                        touch.dy += val - ly;
-                    }
-                }
-                slot.raw_y = Some(val);
-                slot.y = (val - min_y) as f32 / range_y;
-            } else if event.code == ABS_MT_TRACKING_ID {
-                if event.value >= 0 {
-                    slots[current_slot].active = true;
-                    touch.finger_down = true;
-                } else {
-                    slots[current_slot].active = false;
-                    slots[current_slot].raw_x = None;
-                    slots[current_slot].raw_y = None;
-                    touch.finger_down = slots.iter().any(|s| s.active);
-                }
-            } else if event.code == ABS_MT_SLOT {
-                current_slot = (event.value as usize).min(15);
-            }
-        } else if event.type_ == EV_KEY {
-            if event.code == BTN_TOUCH {
-                if event.value == 1 {
-                    touch.finger_down = true;
-                    slots[0].active = true;
-                } else {
-                    touch.finger_down = false;
-                    for s in &mut slots {
-                        s.active = false;
-                        s.raw_x = None;
-                        s.raw_y = None;
-                    }
-                }
-            }
-        } else if event.type_ == EV_SYN {
-            if event.code == SYN_REPORT {
-                let active_fingers: Vec<FingerState> = slots.iter().enumerate()
-                    .filter(|(_, slot)| slot.active)
-                    .map(|(i, slot)| FingerState { slot: i, x: slot.x, y: slot.y })
-                    .collect();
-                let _ = tx.send(CoordinatorMsg::FingersReport(active_fingers));
-
-                if touch.finger_down {
-                    if touch.dx != 0 || touch.dy != 0 {
-                        let scaled_dx = (touch.dx as f32 * 0.15) as i32;
-                        let scaled_dy = (touch.dy as f32 * 0.15) as i32;
-                        if scaled_dx != 0 || scaled_dy != 0 {
-                            let _ = tx.send(CoordinatorMsg::PhysicalTrackpadMove { dx: scaled_dx, dy: scaled_dy, timestamp: now });
-                        }
-                        touch.dx = 0;
-                        touch.dy = 0;
-                    }
-                } else {
-                    let _ = tx.send(CoordinatorMsg::PhysicalTrackpadLift { timestamp: now });
-                    touch.dx = 0;
-                    touch.dy = 0;
-                    for s in &mut slots {
-                        s.raw_x = None;
-                        s.raw_y = None;
-                    }
-                }
-            }
-        }
-    }
-}
-
-struct PhysicsState {
-    config: InertialConfig,
-
-    last_move_time: Instant,
-    vel_x: f32,
-    vel_y: f32,
-    anim_vel_x: f32,
-    anim_vel_y: f32,
-    is_animating_pointer: bool,
-    accum_x: f32,
-    accum_y: f32,
-
-    last_trackpad_move_time: Instant,
-    trackpad_vel_x: f32,
-    trackpad_vel_y: f32,
-    anim_trackpad_vel_x: f32,
-    anim_trackpad_vel_y: f32,
-    is_animating_trackpad: bool,
-    accum_trackpad_x: f32,
-    accum_trackpad_y: f32,
-    last_physical_scroll_time: Instant,
-
-    last_scroll_time: Instant,
-    scroll_vel_x: f32,
-    scroll_vel_y: f32,
-    anim_scroll_vel_x: f32,
-    anim_scroll_vel_y: f32,
-    is_animating_scroll: bool,
-    accum_scroll_x: f32,
-    accum_scroll_y: f32,
-
-    tap_to_click: bool,
-    trackpad_disabled_by_scroll: bool,
-    three_finger_start_x: Option<f32>,
-    three_finger_start_y: Option<f32>,
-    three_finger_gesture_triggered: bool,
-}
-
-fn send_ipc_cmd(ipc_tx: &std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>, cmd: String) {
-    let (reply_tx, _) = std::sync::mpsc::channel();
-    let _ = ipc_tx.send(crate::ipc_server::IpcRequest { command: cmd, reply_tx });
-}
-
-fn trigger_tap_to_click_ipc(tap: bool, ipc_tx: &std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>, pipe_write: libc::c_int) {
-    let cmd = format!("input tap-to-click {}", tap);
-    send_ipc_cmd(ipc_tx, cmd);
-    unsafe {
-        libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
-    }
-}
-
-fn trigger_trackpad_disabled_ipc(disabled: bool, ipc_tx: &std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>, pipe_write: libc::c_int) {
-    let cmd = format!("input trackpad-disabled {}", disabled);
-    send_ipc_cmd(ipc_tx, cmd);
-    unsafe {
-        libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
-    }
-}
-
-fn trigger_expose_ipc(ipc_tx: &std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>, pipe_write: libc::c_int) {
-    let cmd = "expose".to_string();
-    send_ipc_cmd(ipc_tx, cmd);
-    unsafe {
-        libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
-    }
-}
-
-fn trigger_expose_exit_ipc(ipc_tx: &std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>, pipe_write: libc::c_int) {
-    let cmd = "expose-exit".to_string();
-    send_ipc_cmd(ipc_tx, cmd);
-    unsafe {
-        libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
-    }
-}
-
-fn trigger_view_next_ipc(ipc_tx: &std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>, pipe_write: libc::c_int) {
-    let cmd = "view-next".to_string();
-    send_ipc_cmd(ipc_tx, cmd);
-    unsafe {
-        libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
-    }
-}
-
-fn trigger_view_prev_ipc(ipc_tx: &std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>, pipe_write: libc::c_int) {
-    let cmd = "view-prev".to_string();
-    send_ipc_cmd(ipc_tx, cmd);
-    unsafe {
-        libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
-    }
-}
-
-pub fn run_input_daemon(
-    mut event_queue_rx: tokio::sync::mpsc::UnboundedReceiver<InputDaemonMsg>,
-    ipc_tx: std::sync::mpsc::Sender<crate::ipc_server::IpcRequest>,
-    pipe_write: libc::c_int,
-) -> Result<(), Box<dyn std::error::Error>> {
-    let rt = tokio::runtime::Builder::new_current_thread()
-        .enable_all()
-        .build()?;
-
-    let res: Result<(), Box<dyn std::error::Error>> = rt.block_on(async move {
-        println!("[input-subsystem] Starting Clear Input Subsystem...");
-
-        let mut uinput_mouse_file = match setup_uinput_mouse() {
-            Ok(f) => f,
-            Err(e) => {
-                eprintln!("[input-subsystem] FATAL: Could not initialize uinput mouse: {}.", e);
-                return Err(Box::new(e) as Box<dyn std::error::Error>);
-            }
-        };
-
-        let mut uinput_abs_file = match setup_uinput_abs() {
-            Ok(f) => f,
-            Err(e) => {
-                eprintln!("[input-subsystem] FATAL: Could not initialize uinput absolute pointer: {}.", e);
-                return Err(Box::new(e) as Box<dyn std::error::Error>);
-            }
-        };
-
-        let (tx, mut rx): (UnboundedSender<CoordinatorMsg>, UnboundedReceiver<CoordinatorMsg>) = unbounded_channel();
-
-        // Broadcast coordinate socket setup
-        let (broadcast_tx, _) = tokio::sync::broadcast::channel::<String>(32);
-        let socket_path = crate::paths::get_input_coords_socket_path();
-        let _ = fs::remove_file(&socket_path);
-        let listener = tokio::net::UnixListener::bind(&socket_path)?;
-        let b_tx = broadcast_tx.clone();
-        tokio::spawn(async move {
-            loop {
-                if let Ok((mut stream, _)) = listener.accept().await {
-                    let mut rx = b_tx.subscribe();
-                    tokio::spawn(async move {
-                        use tokio::io::AsyncWriteExt;
-                        while let Ok(msg) = rx.recv().await {
-                            if stream.write_all(format!("{}\n", msg).as_bytes()).await.is_err() {
-                                break;
-                            }
-                        }
-                    });
-                }
-            }
-        });
-
-        // Wait for initial config
-        let (initial_config, tap_to_click) = match event_queue_rx.recv().await {
-            Some(InputDaemonMsg::UpdateConfig(res, tap)) => (res, tap),
-            _ => {
-                return Err(Box::from("Failed to receive initial input configuration") as Box<dyn std::error::Error>);
-            }
-        };
-
-        // Config listener task
-        let tx_config = tx.clone();
-        tokio::spawn(async move {
-            while let Some(msg) = event_queue_rx.recv().await {
-                let _ = tx_config.send(CoordinatorMsg::DaemonMsg(msg));
-            }
-        });
-
-        // Event device discovery loop
-        let tx_devices = tx.clone();
-        tokio::spawn(async move {
-            let mut active_devices = HashSet::new();
-            loop {
-                if let Ok(entries) = fs::read_dir("/dev/input/by-path") {
-                    for entry in entries.flatten() {
-                        let path = entry.path();
-                        if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
-                            if name.ends_with("-event-mouse") {
-                                if let Ok(canonical) = fs::canonicalize(&path) {
-                                    if !active_devices.contains(&canonical) {
-                                        active_devices.insert(canonical.clone());
-                                        let tx_c = tx_devices.clone();
-                                        let device_path = canonical.clone();
-                                        tokio::spawn(async move {
-                                            if let Err(e) = read_device_loop(device_path.clone(), tx_c).await {
-                                                eprintln!("[input-subsystem] Reader loop exited for {:?}: {}", device_path, e);
-                                            }
-                                        });
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-                sleep(Duration::from_secs(5)).await;
-            }
-        });
-
-        // Central coordinator and physics ticks
-        trigger_tap_to_click_ipc(tap_to_click, &ipc_tx, pipe_write);
-
-        let mut state = PhysicsState {
-            config: initial_config,
-            last_move_time: Instant::now(),
-            vel_x: 0.0,
-            vel_y: 0.0,
-            anim_vel_x: 0.0,
-            anim_vel_y: 0.0,
-            is_animating_pointer: false,
-            accum_x: 0.0,
-            accum_y: 0.0,
-            last_trackpad_move_time: Instant::now(),
-            trackpad_vel_x: 0.0,
-            trackpad_vel_y: 0.0,
-            anim_trackpad_vel_x: 0.0,
-            anim_trackpad_vel_y: 0.0,
-            is_animating_trackpad: false,
-            accum_trackpad_x: 0.0,
-            accum_trackpad_y: 0.0,
-            last_physical_scroll_time: Instant::now(),
-            last_scroll_time: Instant::now(),
-            scroll_vel_x: 0.0,
-            scroll_vel_y: 0.0,
-            anim_scroll_vel_x: 0.0,
-            anim_scroll_vel_y: 0.0,
-            is_animating_scroll: false,
-            accum_scroll_x: 0.0,
-            accum_scroll_y: 0.0,
-            tap_to_click,
-            trackpad_disabled_by_scroll: false,
-            three_finger_start_x: None,
-            three_finger_start_y: None,
-            three_finger_gesture_triggered: false,
-        };
-
-        let mut tick_interval = tokio::time::interval(Duration::from_millis(16)); // ~60fps
-
-        loop {
-            tokio::select! {
-                Some(msg) = rx.recv() => {
-                    match msg {
-                        CoordinatorMsg::DaemonMsg(daemon_msg) => {
-                            match daemon_msg {
-                                InputDaemonMsg::UpdateConfig(cfg, tap) => {
-                                    state.config = cfg;
-                                    if state.tap_to_click != tap {
-                                        state.tap_to_click = tap;
-                                        trigger_tap_to_click_ipc(tap, &ipc_tx, pipe_write);
-                                    }
-                                }
-                                InputDaemonMsg::SimulateMoveTo { x, y } => {
-                                    set_pointer_coords(x, y);
-                                    let px = POINTER_X.load(Ordering::SeqCst);
-                                    let py = POINTER_Y.load(Ordering::SeqCst);
-                                    let _ = write_mouse_absolute(&mut uinput_abs_file, px, py);
-                                }
-                                InputDaemonMsg::SimulateMoveBy { dx, dy } => {
-                                    update_pointer_coords(dx, dy);
-                                    let _ = write_mouse_relative(&mut uinput_mouse_file, dx, dy);
-                                }
-                                InputDaemonMsg::SimulateButton { button, press } => {
-                                    let val = if press { 1 } else { 0 };
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_KEY, button, val);
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_SYN, SYN_REPORT, 0);
-                                }
-                                InputDaemonMsg::SimulateKey { keycode, press } => {
-                                    let val = if press { 1 } else { 0 };
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_KEY, keycode, val);
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_SYN, SYN_REPORT, 0);
-                                }
-                                InputDaemonMsg::SimulateClick { button } => {
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_KEY, button, 1);
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_SYN, SYN_REPORT, 0);
-                                    let tx_clone = tx.clone();
-                                    tokio::spawn(async move {
-                                        tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
-                                        let _ = tx_clone.send(CoordinatorMsg::InternalReleaseButton { button });
-                                    });
-                                }
-                                InputDaemonMsg::SimulateKeyPress { keycode } => {
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_KEY, keycode, 1);
-                                    let _ = write_raw_event(&mut uinput_mouse_file, EV_SYN, SYN_REPORT, 0);
-                                    let tx_clone = tx.clone();
-                                    tokio::spawn(async move {
-                                        tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
-                                        let _ = tx_clone.send(CoordinatorMsg::InternalReleaseKey { keycode });
-                                    });
-                                }
-                                InputDaemonMsg::SimulateScroll { dx, dy } => {
-                                    let _ = write_scroll(&mut uinput_mouse_file, dx, dy);
-                                }
-                            }
-                        }
-                        CoordinatorMsg::InternalReleaseButton { button } => {
-                            let _ = write_raw_event(&mut uinput_mouse_file, EV_KEY, button, 0);
-                            let _ = write_raw_event(&mut uinput_mouse_file, EV_SYN, SYN_REPORT, 0);
-                        }
-                        CoordinatorMsg::InternalReleaseKey { keycode } => {
-                            let _ = write_raw_event(&mut uinput_mouse_file, EV_KEY, keycode, 0);
-                            let _ = write_raw_event(&mut uinput_mouse_file, EV_SYN, SYN_REPORT, 0);
-                        }
-                        CoordinatorMsg::PhysicalMove { dx, dy, timestamp } => {
-                            update_pointer_coords(dx, dy);
-                            let dt = timestamp.duration_since(state.last_move_time).as_secs_f32();
-                            state.last_move_time = timestamp;
-
-                            state.is_animating_pointer = false;
-                            state.anim_vel_x = 0.0;
-                            state.anim_vel_y = 0.0;
-
-                            if dt > 0.1 {
-                                state.vel_x = 0.0;
-                                state.vel_y = 0.0;
-                            } else if dt > 0.001 {
-                                let inst_vx = dx as f32 / dt;
-                                let inst_vy = dy as f32 / dt;
-                                let alpha = 0.35;
-                                state.vel_x = alpha * inst_vx + (1.0 - alpha) * state.vel_x;
-                                state.vel_y = alpha * inst_vy + (1.0 - alpha) * state.vel_y;
-                            }
-                        }
-                        CoordinatorMsg::PhysicalTrackpadMove { dx, dy, timestamp } => {
-                            update_pointer_coords(dx, dy);
-                            let dt = timestamp.duration_since(state.last_trackpad_move_time).as_secs_f32();
-                            state.last_trackpad_move_time = timestamp;
-
-                            state.is_animating_trackpad = false;
-                            state.anim_trackpad_vel_x = 0.0;
-                            state.anim_trackpad_vel_y = 0.0;
-
-                            if dt > 0.1 {
-                                state.trackpad_vel_x = 0.0;
-                                state.trackpad_vel_y = 0.0;
-                            } else if dt > 0.001 {
-                                let inst_vx = dx as f32 / dt;
-                                let inst_vy = dy as f32 / dt;
-                                let alpha = 0.35;
-                                state.trackpad_vel_x = alpha * inst_vx + (1.0 - alpha) * state.trackpad_vel_x;
-                                state.trackpad_vel_y = alpha * inst_vy + (1.0 - alpha) * state.trackpad_vel_y;
-                            }
-                        }
-                        CoordinatorMsg::PhysicalTrackpadLift { timestamp } => {
-                            if state.trackpad_disabled_by_scroll {
-                                state.trackpad_disabled_by_scroll = false;
-                                trigger_trackpad_disabled_ipc(false, &ipc_tx, pipe_write);
-                            }
-                            let time_since_scroll = timestamp.duration_since(state.last_physical_scroll_time).as_secs_f32();
-                            if time_since_scroll > 0.15 {
-                                if state.config.inertial_trackpad && !state.is_animating_trackpad {
-                                    let time_since_last = timestamp.duration_since(state.last_trackpad_move_time).as_secs_f32();
-                                    if time_since_last < 0.05 {
-                                        let speed = (state.trackpad_vel_x * state.trackpad_vel_x + state.trackpad_vel_y * state.trackpad_vel_y).sqrt();
-                                        if speed > 150.0 {
-                                            state.is_animating_trackpad = true;
-                                            state.anim_trackpad_vel_x = state.trackpad_vel_x;
-                                            state.anim_trackpad_vel_y = state.trackpad_vel_y;
-                                            state.accum_trackpad_x = 0.0;
-                                            state.accum_trackpad_y = 0.0;
-                                        }
-                                    }
-                                }
-                            } else {
-                                state.trackpad_vel_x = 0.0;
-                                state.trackpad_vel_y = 0.0;
-                            }
-                        }
-                        CoordinatorMsg::PhysicalScroll { dwx, dwy, timestamp } => {
-                            if !state.trackpad_disabled_by_scroll {
-                                state.trackpad_disabled_by_scroll = true;
-                                trigger_trackpad_disabled_ipc(true, &ipc_tx, pipe_write);
-                            }
-                            let dt = timestamp.duration_since(state.last_scroll_time).as_secs_f32();
-                            state.last_scroll_time = timestamp;
-                            state.last_physical_scroll_time = timestamp;
-
-                            state.is_animating_scroll = false;
-                            state.anim_scroll_vel_x = 0.0;
-                            state.anim_scroll_vel_y = 0.0;
-
-                            if dt > 0.15 {
-                                state.scroll_vel_x = 0.0;
-                                state.scroll_vel_y = 0.0;
-                            } else if dt > 0.001 {
-                                let inst_vx = dwx as f32 / dt;
-                                let inst_vy = dwy as f32 / dt;
-                                let alpha = 0.45;
-                                state.scroll_vel_x = alpha * inst_vx + (1.0 - alpha) * state.scroll_vel_x;
-                                state.scroll_vel_y = alpha * inst_vy + (1.0 - alpha) * state.scroll_vel_y;
-                            }
-                        }
-                        CoordinatorMsg::FingersReport(fingers) => {
-                            if fingers.is_empty() && state.trackpad_disabled_by_scroll {
-                                state.trackpad_disabled_by_scroll = false;
-                                trigger_trackpad_disabled_ipc(false, &ipc_tx, pipe_write);
-                            }
-
-                            // Detect 3-finger gestures (swipe up, down, left, right)
-                            if fingers.len() == 3 {
-                                let avg_x = (fingers[0].x + fingers[1].x + fingers[2].x) / 3.0;
-                                let avg_y = (fingers[0].y + fingers[1].y + fingers[2].y) / 3.0;
-                                if let (Some(start_x), Some(start_y)) = (state.three_finger_start_x, state.three_finger_start_y) {
-                                    let dy_up = start_y - avg_y; // Y decreases as fingers move up
-                                    let dy_down = avg_y - start_y; // Y increases as fingers move down
-                                    let dx_right = avg_x - start_x; // X increases as fingers move right
-                                    let dx_left = start_x - avg_x; // X decreases as fingers move left
-
-                                    if dy_up > 0.15 && !state.three_finger_gesture_triggered {
-                                        state.three_finger_gesture_triggered = true;
-                                        println!("[input-subsystem] 3-finger swipe up gesture detected. Triggering Expose mode.");
-                                        trigger_expose_ipc(&ipc_tx, pipe_write);
-                                    } else if dy_down > 0.15 && !state.three_finger_gesture_triggered {
-                                        state.three_finger_gesture_triggered = true;
-                                        println!("[input-subsystem] 3-finger swipe down gesture detected. Triggering Expose exit.");
-                                        trigger_expose_exit_ipc(&ipc_tx, pipe_write);
-                                    } else if dx_right > 0.15 && !state.three_finger_gesture_triggered {
-                                        state.three_finger_gesture_triggered = true;
-                                        println!("[input-subsystem] 3-finger swipe right gesture detected. Switching to previous tag.");
-                                        trigger_view_prev_ipc(&ipc_tx, pipe_write);
-                                    } else if dx_left > 0.15 && !state.three_finger_gesture_triggered {
-                                        state.three_finger_gesture_triggered = true;
-                                        println!("[input-subsystem] 3-finger swipe left gesture detected. Switching to next tag.");
-                                        trigger_view_next_ipc(&ipc_tx, pipe_write);
-                                    }
-                                } else {
-                                    state.three_finger_start_x = Some(avg_x);
-                                    state.three_finger_start_y = Some(avg_y);
-                                }
-                            } else {
-                                state.three_finger_start_x = None;
-                                state.three_finger_start_y = None;
-                                state.three_finger_gesture_triggered = false;
-                            }
-
-                            if let Ok(serialized) = serde_json::to_string(&fingers) {
-                                let _ = broadcast_tx.send(serialized);
-                            }
-                        }
-                    }
-                }
-                _ = tick_interval.tick() => {
-                    let now = Instant::now();
-                    let tick_dt = 0.016;
-
-                    // --- Pointer physics tick ---
-                    if state.config.inertial_pointer {
-                        if !state.is_animating_pointer {
-                            let time_since_last = now.duration_since(state.last_move_time).as_secs_f32();
-                            if time_since_last >= 0.02 && time_since_last < 0.1 {
-                                let speed = (state.vel_x * state.vel_x + state.vel_y * state.vel_y).sqrt();
-                                if speed > 150.0 {
-                                    state.is_animating_pointer = true;
-                                    state.anim_vel_x = state.vel_x;
-                                    state.anim_vel_y = state.vel_y;
-                                    state.accum_x = 0.0;
-                                    state.accum_y = 0.0;
-                                }
-                            }
-                        } else {
-                            let friction_coef = state.config.pointer_friction as f32 / 100.0;
-                            state.anim_vel_x *= friction_coef;
-                            state.anim_vel_y *= friction_coef;
-
-                            let speed = (state.anim_vel_x * state.anim_vel_x + state.anim_vel_y * state.anim_vel_y).sqrt();
-                            if speed < 12.0 {
-                                state.is_animating_pointer = false;
-                            } else {
-                                state.accum_x += state.anim_vel_x * tick_dt * state.config.pointer_speed as f32;
-                                state.accum_y += state.anim_vel_y * tick_dt * state.config.pointer_speed as f32;
-
-                                let steps_x = state.accum_x.trunc() as i32;
-                                let steps_y = state.accum_y.trunc() as i32;
-                                state.accum_x -= steps_x as f32;
-                                state.accum_y -= steps_y as f32;
-
-                                if steps_x != 0 || steps_y != 0 {
-                                    update_pointer_coords(steps_x, steps_y);
-                                    let _ = write_mouse_relative(&mut uinput_mouse_file, steps_x, steps_y);
-                                }
-                            }
-                        }
-                    }
-
-                    // --- Trackpad physics tick ---
-                    if state.config.inertial_trackpad && state.is_animating_trackpad {
-                        let friction_coef = state.config.trackpad_friction as f32 / 100.0;
-                        state.anim_trackpad_vel_x *= friction_coef;
-                        state.anim_trackpad_vel_y *= friction_coef;
-
-                        let speed = (state.anim_trackpad_vel_x * state.anim_trackpad_vel_x + state.anim_trackpad_vel_y * state.anim_trackpad_vel_y).sqrt();
-                        if speed < 12.0 {
-                            state.is_animating_trackpad = false;
-                        } else {
-                            state.accum_trackpad_x += state.anim_trackpad_vel_x * tick_dt * state.config.trackpad_speed as f32;
-                            state.accum_trackpad_y += state.anim_trackpad_vel_y * tick_dt * state.config.trackpad_speed as f32;
-
-                            let steps_x = state.accum_trackpad_x.trunc() as i32;
-                            let steps_y = state.accum_trackpad_y.trunc() as i32;
-                            state.accum_trackpad_x -= steps_x as f32;
-                            state.accum_trackpad_y -= steps_y as f32;
-
-                            if steps_x != 0 || steps_y != 0 {
-                                update_pointer_coords(steps_x, steps_y);
-                                let _ = write_mouse_relative(&mut uinput_mouse_file, steps_x, steps_y);
-                            }
-                        }
-                    }
-
-                    // --- Scroll physics tick ---
-                    if state.config.inertial_scroll {
-                        if !state.is_animating_scroll {
-                            let time_since_last = now.duration_since(state.last_scroll_time).as_secs_f32();
-                            if time_since_last >= 0.03 && time_since_last < 0.15 {
-                                let speed = (state.scroll_vel_x * state.scroll_vel_x + state.scroll_vel_y * state.scroll_vel_y).sqrt();
-                                if speed > 5.0 {
-                                    state.is_animating_scroll = true;
-                                    state.anim_scroll_vel_x = state.scroll_vel_x;
-                                    state.anim_scroll_vel_y = state.scroll_vel_y;
-                                    state.accum_scroll_x = 0.0;
-                                    state.accum_scroll_y = 0.0;
-                                }
-                            }
-                        } else {
-                            let friction_coef = state.config.scroll_friction as f32 / 100.0;
-                            state.anim_scroll_vel_x *= friction_coef;
-                            state.anim_scroll_vel_y *= friction_coef;
-
-                            let speed = (state.anim_scroll_vel_x * state.anim_scroll_vel_x + state.anim_scroll_vel_y * state.anim_scroll_vel_y).sqrt();
-                            if speed < 0.5 {
-                                state.is_animating_scroll = false;
-                            } else {
-                                state.accum_scroll_x += state.anim_scroll_vel_x * tick_dt * state.config.scroll_speed as f32;
-                                state.accum_scroll_y += state.anim_scroll_vel_y * tick_dt * state.config.scroll_speed as f32;
-
-                                let steps_x = state.accum_scroll_x.trunc() as i32;
-                                let steps_y = state.accum_scroll_y.trunc() as i32;
-                                state.accum_scroll_x -= steps_x as f32;
-                                state.accum_scroll_y -= steps_y as f32;
-
-                                if steps_x != 0 || steps_y != 0 {
-                                    let _ = write_scroll(&mut uinput_mouse_file, steps_x, steps_y);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    });
-
-    res
-}
diff --git a/src/client/ipc.rs b/src/client/ipc.rs
deleted file mode 100644
index b09f6df..0000000
--- a/src/client/ipc.rs
+++ /dev/null
@@ -1,1571 +0,0 @@
-// IPC command parser for cce-client
-// Ported from handle_ipc_command in cce-client.c
-
-use crate::config::{parse_keysym, spawn_command_bg};
-use crate::types::{
-    parse_action, parse_button, parse_hex_color, parse_modifiers, parse_tiling_mode, Action,
-    ModeRule, PendingPointerBinding, PendingXkbBinding, WindowManager, NUM_TAGS, TilingMode,
-};
-
-fn get_window_under_pointer(state: &WindowManager) -> Option<u64> {
-    // 1. Check seat.hovered_window_id first
-    for seat in &state.seats {
-        if !seat.removed {
-            if let Some(wid) = seat.hovered_window_id {
-                return Some(wid);
-            }
-        }
-    }
-
-    // 2. Fallback: find it using POINTER_X/POINTER_Y and window geometries
-    let px = crate::input::POINTER_X.load(std::sync::atomic::Ordering::SeqCst) as f64;
-    let py = crate::input::POINTER_Y.load(std::sync::atomic::Ordering::SeqCst) as f64;
-
-    let active_tags = state.active_tags;
-    let mut best_wid = None;
-
-    for win in &state.windows {
-        if win.closed
-            || (win.tags & active_tags) == 0
-            || win.app_id.as_deref() == Some("cce-status-interface")
-            || win.tiling_mode == TilingMode::Popup
-        {
-            continue;
-        }
-        let wx = win.anim_x.unwrap_or(win.x as f64);
-        let wy = win.anim_y.unwrap_or(win.y as f64);
-        let ww = win.anim_w.unwrap_or(win.width as f64);
-        let wh = win.anim_h.unwrap_or(win.height as f64);
-
-        if px >= wx && px <= wx + ww && py >= wy && py <= wy + wh {
-            best_wid = Some(win.id);
-        }
-    }
-
-    best_wid
-}
-
-/// Handle an IPC command string, modifying the window manager state.
-/// This is called from the Unix socket listener when clearctl sends a command.
-pub fn handle_ipc_command(cmd: &str, state: &mut WindowManager) -> String {
-    // Strip trailing newlines/spaces
-    let cmd = cmd.trim_end_matches(|c| c == '\n' || c == '\r' || c == ' ');
-    if cmd.is_empty() {
-        return "\n".to_string();
-    }
-
-    // Split the command into tokens
-    let tokens: Vec<&str> = cmd.splitn(2, ' ').collect();
-    let tok = tokens[0];
-    let rest = if tokens.len() > 1 { tokens[1] } else { "" };
-
-    let mut reply = "ok\n".to_string();
-
-    match tok {
-        "spawn" => {
-            if !rest.is_empty() {
-                spawn_command_bg(rest);
-            }
-        }
-        "close" => {
-            // Close the focused window
-            if let Some(seat) = state.seats.first() {
-                if let Some(focused_id) = seat.focused_window_id {
-                    if let Some(window) = state.get_window_mut(focused_id) {
-                        window.closed = true;
-                    }
-                }
-            }
-            state.needs_render = true;
-        }
-        "minimize" => {
-            if let Some(seat) = state.seats.first() {
-                if let Some(focused_id) = seat.focused_window_id {
-                    if let Some(window) = state.get_window_mut(focused_id) {
-                        window.minimized = true;
-                    }
-                    // Shift focus to the next visible window
-                    let active_tags = state.active_tags;
-                    let visible_ids: Vec<u64> = state
-                        .windows
-                        .iter()
-                        .filter(|w| (w.tags & active_tags) != 0 && !w.closed && !w.minimized && w.app_id.as_deref() != Some("cce-status-interface"))
-                        .map(|w| w.id)
-                        .collect();
-                    let next_id = visible_ids.last().copied();
-                    for s in &mut state.seats {
-                        if !s.removed {
-                            s.focused_window_id = next_id;
-                        }
-                    }
-                }
-            }
-            state.needs_render = true;
-            state.needs_focus = true;
-            state.needs_status_update = true;
-        }
-        "focus-next" => {
-            // Focus the next visible window (wrapping) and move it to the
-            // front of the cascade stack (end of windows vector).
-            if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                let focused_id = seat.focused_window_id;
-                let active_tags = state.active_tags;
-                let visible_ids: Vec<u64> = state
-                    .windows
-                    .iter()
-                    .filter(|w| (w.tags & active_tags) != 0 && !w.closed && !w.minimized && w.app_id.as_deref() != Some("cce-status-interface"))
-                    .map(|w| w.id)
-                    .collect();
-                if visible_ids.len() > 1 {
-                    if let Some(fid) = focused_id {
-                        if let Some(idx) = visible_ids.iter().position(|id| *id == fid) {
-                            let next_idx = (idx + 1) % visible_ids.len();
-                            let next_id = visible_ids[next_idx];
-                            seat.focused_window_id = Some(next_id);
-                            // Move newly focused window to front of cascade stack
-                            state.move_window_to_end(next_id);
-                        }
-                    }
-                }
-            }
-            state.needs_render = true;
-            state.needs_focus = true;
-            state.needs_status_update = true;
-        }
-        "focus-prev" => {
-            // Focus the previous visible window (wrapping) and move it to the
-            // front of the cascade stack (end of windows vector).
-            if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                let focused_id = seat.focused_window_id;
-                let active_tags = state.active_tags;
-                let visible_ids: Vec<u64> = state
-                    .windows
-                    .iter()
-                    .filter(|w| (w.tags & active_tags) != 0 && !w.closed && !w.minimized && w.app_id.as_deref() != Some("cce-status-interface"))
-                    .map(|w| w.id)
-                    .collect();
-                if visible_ids.len() > 1 {
-                    if let Some(fid) = focused_id {
-                        if let Some(idx) = visible_ids.iter().position(|id| *id == fid) {
-                            let prev_idx = if idx == 0 { visible_ids.len() - 1 } else { idx - 1 };
-                            let prev_id = visible_ids[prev_idx];
-                            seat.focused_window_id = Some(prev_id);
-                            // Move newly focused window to front of cascade stack
-                            state.move_window_to_end(prev_id);
-                        }
-                    }
-                }
-            }
-            state.needs_render = true;
-            state.needs_focus = true;
-            state.needs_status_update = true;
-        }
-        "exit" => {
-            // Signal exit request
-        }
-        "restart" => {
-            crate::restart::wm_restart();
-        }
-        "reload" => {
-            crate::restart::wm_reload(state);
-        }
-        "expose" => {
-            let active = !state.expose_active;
-            if !active {
-                // Exiting expose mode! Focus the window under pointer.
-                if let Some(wid) = get_window_under_pointer(state) {
-                    for seat in &mut state.seats {
-                        if !seat.removed {
-                            seat.focused_window_id = Some(wid);
-                        }
-                    }
-                    state.move_window_to_end(wid);
-                    state.needs_focus = true;
-                }
-            }
-            crate::wm::set_expose_active(state, active);
-            state.needs_render = true;
-            state.needs_status_update = true;
-        }
-        "expose-exit" => {
-            if state.expose_active {
-                let hovered_id = get_window_under_pointer(state);
-                crate::wm::set_expose_active(state, false);
-                if let Some(wid) = hovered_id {
-                    for seat in &mut state.seats {
-                        if !seat.removed {
-                            seat.focused_window_id = Some(wid);
-                        }
-                    }
-                    state.move_window_to_end(wid);
-                    state.needs_focus = true;
-                }
-                state.needs_render = true;
-                state.needs_status_update = true;
-            } else {
-                spawn_command_bg("clear-cloud --apps");
-            }
-        }
-        "view-next" => {
-            let current_tag_idx = (0..NUM_TAGS).find(|&i| (state.active_tags & (1 << i)) != 0).unwrap_or(0);
-            let next_tag_idx = (current_tag_idx + 1) % NUM_TAGS;
-            state.active_tags = 1 << next_tag_idx;
-
-            if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                let visible_ids: Vec<u64> = state
-                    .windows
-                    .iter()
-                    .filter(|w| (w.tags & state.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                    .map(|w| w.id)
-                    .collect();
-                seat.focused_window_id = visible_ids.last().copied();
-            }
-            state.needs_render = true;
-            state.needs_focus = true;
-            state.needs_status_update = true;
-        }
-        "view-prev" => {
-            let current_tag_idx = (0..NUM_TAGS).find(|&i| (state.active_tags & (1 << i)) != 0).unwrap_or(0);
-            let prev_tag_idx = (current_tag_idx + NUM_TAGS - 1) % NUM_TAGS;
-            state.active_tags = 1 << prev_tag_idx;
-
-            if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                let visible_ids: Vec<u64> = state
-                    .windows
-                    .iter()
-                    .filter(|w| (w.tags & state.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                    .map(|w| w.id)
-                    .collect();
-                seat.focused_window_id = visible_ids.last().copied();
-            }
-            state.needs_render = true;
-            state.needs_focus = true;
-            state.needs_status_update = true;
-        }
-        "view" | _ if tok.starts_with("view") => {
-            let tag = parse_tag_from_command(tok, "view", rest);
-            if let Some(tag) = tag {
-                if tag >= 1 && tag <= NUM_TAGS as i32 {
-                    state.active_tags = 1 << (tag - 1);
-
-                    // Reassign focus to a visible window on the new tag
-                    if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                        let visible_ids: Vec<u64> = state
-                            .windows
-                            .iter()
-                            .filter(|w| (w.tags & state.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                            .map(|w| w.id)
-                            .collect();
-                        seat.focused_window_id = visible_ids.last().copied();
-                    }
-                    state.needs_render = true;
-                    state.needs_focus = true;
-                    state.needs_status_update = true;
-                }
-            }
-        }
-        "toggle" | _ if tok.starts_with("toggle") => {
-            let tag = parse_tag_from_command(tok, "toggle", rest);
-            if let Some(tag) = tag {
-                if tag >= 1 && tag <= NUM_TAGS as i32 {
-                    state.active_tags ^= 1 << (tag - 1);
-
-                    // If the focused window is no longer visible, reassign focus
-                    let focused_id = state
-                        .seats
-                        .iter()
-                        .find(|s| !s.removed)
-                        .and_then(|s| s.focused_window_id);
-                    let focused_still_visible = focused_id.map_or(false, |fid| {
-                        state
-                            .get_window(fid)
-                            .map_or(false, |w| (w.tags & state.active_tags) != 0 && !w.closed)
-                    });
-                    if !focused_still_visible {
-                        let visible_ids: Vec<u64> = state
-                            .windows
-                            .iter()
-                            .filter(|w| (w.tags & state.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                            .map(|w| w.id)
-                            .collect();
-                        if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                            seat.focused_window_id = visible_ids.last().copied();
-                        }
-                        state.needs_focus = true;
-                    }
-                    state.needs_render = true;
-                    state.needs_status_update = true;
-                }
-            }
-        }
-        "config-done" => {
-            state.config_done = true;
-        }
-        "layout" => {
-            handle_layout_command(rest, state);
-        }
-        "mode" => {
-            handle_mode_command(rest, state);
-        }
-        "set-mode" => {
-            handle_set_mode_command(rest, state);
-        }
-        "apply-mode-sharing" => {
-            handle_apply_mode_sharing_command(rest, state);
-        }
-        "mode-next-shared" => {
-            let cycle = [
-                TilingMode::Cascade,
-                TilingMode::Grid,
-                TilingMode::Fullscreen,
-                TilingMode::Floating,
-            ];
-            let focused_id = state
-                .seats
-                .iter()
-                .find(|s| !s.removed)
-                .and_then(|s| s.focused_window_id);
-            if let Some(fid) = focused_id {
-                let notifications_enable = state.notifications_enable;
-                let current_mode = state.get_window(fid).map(|w| w.tiling_mode);
-                if let Some(old_mode) = current_mode {
-                    let next = cycle
-                        .iter()
-                        .position(|m| *m == old_mode)
-                        .map(|i| cycle[(i + 1) % cycle.len()])
-                        .unwrap_or(TilingMode::Cascade);
-
-                    let active_tags = state.active_tags;
-
-                    let mut updated_count = 0;
-                    for win in &mut state.windows {
-                        if !win.closed
-                            && win.app_id.as_deref() != Some("cce-status-interface")
-                            && (win.tags & active_tags) != 0
-                            && win.tiling_mode == old_mode
-                        {
-                            win.tiling_mode = next;
-                            win.mode_locked = true;
-                            updated_count += 1;
-                        }
-                    }
-
-                    if notifications_enable && updated_count > 0 {
-                        crate::config::show_notification(
-                            "cce-client",
-                            &format!(
-                                "Tiling mode set to {} for all {} windows on active tag",
-                                next.as_str(),
-                                old_mode.as_str()
-                            ),
-                        );
-                    }
-                    state.needs_render = true;
-                    state.needs_status_update = true;
-                }
-            }
-        }
-        "bind" => {
-            handle_bind_command(rest, state);
-        }
-        "pbind" => {
-            handle_pbind_command(rest, state);
-        }
-        "retile" => {
-            // Retile is handled by the WM layer - just set a flag
-            // The actual tile_windows() call happens in wm.rs
-        }
-        "tag-layout" => {
-            handle_tag_layout_command(rest, state);
-        }
-        "set-tag" => {
-            handle_set_tag_command(rest, state);
-        }
-        "repeat" => {
-            handle_repeat_command(rest, state);
-        }
-        "input" => {
-            handle_input_command(rest, state);
-        }
-        "notify" => {
-            let parts: Vec<&str> = rest.splitn(2, ' ').collect();
-            if parts.len() == 2 {
-                if state.notifications_enable {
-                    crate::config::show_notification(parts[0], parts[1]);
-                }
-            } else if parts.len() == 1 && !parts[0].is_empty() {
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", parts[0]);
-                }
-            }
-        }
-        "focus-window" => {
-            let app_id_or_title = rest.trim();
-            if !app_id_or_title.is_empty() {
-                let mut found_id = None;
-                for win in &state.windows {
-                    if !win.closed && !win.minimized {
-                        if let Some(ref app_id) = win.app_id {
-                            if app_id.eq_ignore_ascii_case(app_id_or_title) {
-                                found_id = Some(win.id);
-                                break;
-                            }
-                        }
-                    }
-                }
-                if found_id.is_none() {
-                    for win in &state.windows {
-                        if !win.closed && !win.minimized {
-                            if let Some(ref title) = win.title {
-                                if title.to_lowercase().contains(&app_id_or_title.to_lowercase()) {
-                                    found_id = Some(win.id);
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                }
-                if let Some(wid) = found_id {
-                    if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                        seat.focused_window_id = Some(wid);
-                        state.move_window_to_end(wid);
-                        state.needs_focus = true;
-                        state.needs_render = true;
-                        state.needs_status_update = true;
-                        reply = format!("ok focused window {}\n", wid);
-                    } else {
-                        reply = "error: no active seat\n".to_string();
-                    }
-                } else {
-                    reply = "error: window not found\n".to_string();
-                }
-            } else {
-                reply = "error: usage: focus-window <app_id|title>\n".to_string();
-            }
-        }
-        "pointer-location" => {
-            let px = crate::input::POINTER_X.load(std::sync::atomic::Ordering::SeqCst);
-            let py = crate::input::POINTER_Y.load(std::sync::atomic::Ordering::SeqCst);
-            reply = format!("{} {}\n", px, py);
-        }
-        "pointer-move-to" => {
-            let parts: Vec<&str> = rest.split_whitespace().collect();
-            if parts.len() == 2 {
-                if let (Ok(target_x), Ok(target_y)) = (parts[0].parse::<i32>(), parts[1].parse::<i32>()) {
-                    if let Some(ref controller) = state.input_controller {
-                        let _ = controller.send(crate::input::InputDaemonMsg::SimulateMoveTo { x: target_x, y: target_y });
-                    }
-                } else {
-                    reply = "error: invalid coordinates\n".to_string();
-                }
-            } else {
-                reply = "error: usage: pointer-move-to <x> <y>\n".to_string();
-            }
-        }
-        "pointer-move-by" => {
-            let parts: Vec<&str> = rest.split_whitespace().collect();
-            if parts.len() == 2 {
-                if let (Ok(dx), Ok(dy)) = (parts[0].parse::<i32>(), parts[1].parse::<i32>()) {
-                    if let Some(ref controller) = state.input_controller {
-                        let _ = controller.send(crate::input::InputDaemonMsg::SimulateMoveBy { dx, dy });
-                    }
-                } else {
-                    reply = "error: invalid deltas\n".to_string();
-                }
-            } else {
-                reply = "error: usage: pointer-move-by <dx> <dy>\n".to_string();
-            }
-        }
-        "pointer-scroll" => {
-            let parts: Vec<&str> = rest.split_whitespace().collect();
-            if parts.len() == 2 {
-                if let (Ok(dx), Ok(dy)) = (parts[0].parse::<i32>(), parts[1].parse::<i32>()) {
-                    if let Some(ref controller) = state.input_controller {
-                        let _ = controller.send(crate::input::InputDaemonMsg::SimulateScroll { dx, dy });
-                    }
-                } else {
-                    reply = "error: invalid deltas\n".to_string();
-                }
-            } else {
-                reply = "error: usage: pointer-scroll <dx> <dy>\n".to_string();
-            }
-        }
-        "pointer-click" => {
-            let btn = parse_button(rest) as u16;
-            if btn != 0 {
-                if let Some(ref controller) = state.input_controller {
-                    let _ = controller.send(crate::input::InputDaemonMsg::SimulateClick { button: btn });
-                }
-            } else {
-                reply = "error: invalid button\n".to_string();
-            }
-        }
-        "pointer-press" => {
-            let btn = parse_button(rest) as u16;
-            if btn != 0 {
-                if let Some(ref controller) = state.input_controller {
-                    let _ = controller.send(crate::input::InputDaemonMsg::SimulateButton { button: btn, press: true });
-                }
-            } else {
-                reply = "error: invalid button\n".to_string();
-            }
-        }
-        "pointer-release" => {
-            let btn = parse_button(rest) as u16;
-            if btn != 0 {
-                if let Some(ref controller) = state.input_controller {
-                    let _ = controller.send(crate::input::InputDaemonMsg::SimulateButton { button: btn, press: false });
-                }
-            } else {
-                reply = "error: invalid button\n".to_string();
-            }
-        }
-        "keypress" => {
-            if let Some(key) = parse_keycode(rest) {
-                if let Some(ref controller) = state.input_controller {
-                    let _ = controller.send(crate::input::InputDaemonMsg::SimulateKeyPress { keycode: key });
-                }
-            } else {
-                reply = "error: invalid key\n".to_string();
-            }
-        }
-        "key-press" => {
-            if let Some(key) = parse_keycode(rest) {
-                if let Some(ref controller) = state.input_controller {
-                    let _ = controller.send(crate::input::InputDaemonMsg::SimulateKey { keycode: key, press: true });
-                }
-            } else {
-                reply = "error: invalid key\n".to_string();
-            }
-        }
-        "key-release" => {
-            if let Some(key) = parse_keycode(rest) {
-                if let Some(ref controller) = state.input_controller {
-                    let _ = controller.send(crate::input::InputDaemonMsg::SimulateKey { keycode: key, press: false });
-                }
-            } else {
-                reply = "error: invalid key\n".to_string();
-            }
-        }
-        _ => {
-            reply = "error: unknown command\n".to_string();
-        }
-    }
-
-    reply
-}
-
-/// Parse a tag number from a command like "view-1" or "view 1"
-fn parse_tag_from_command(tok: &str, prefix: &str, rest: &str) -> Option<i32> {
-    let after_prefix = &tok[prefix.len()..];
-    if after_prefix.starts_with('-') {
-        after_prefix[1..].parse::<i32>().ok()
-    } else if !rest.is_empty() {
-        rest.trim()
-            .split_whitespace()
-            .next()
-            .and_then(|s| s.parse::<i32>().ok())
-    } else {
-        None
-    }
-}
-
-/// Handle "layout <param> <value>" command
-fn handle_layout_command(rest: &str, state: &mut WindowManager) {
-    let parts: Vec<&str> = rest.split_whitespace().collect();
-    if parts.len() < 2 {
-        return;
-    }
-    let param = parts[0];
-    let value_str = parts[1];
-
-    match param {
-        "gap" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.gap = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Gap set to {}px", value));
-                }
-            }
-        }
-        "gap_top" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.gap_top = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Top gap set to {}px", value));
-                }
-            }
-        }
-        "gap_left" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.gap_left = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Left gap set to {}px", value));
-                }
-            }
-        }
-        "gap_right" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.gap_right = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Right gap set to {}px", value));
-                }
-            }
-        }
-        "gap_bottom" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.gap_bottom = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Bottom gap set to {}px", value));
-                }
-            }
-        }
-        "cascade_offset" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.cascade_offset = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Cascade offset set to {}px", value));
-                }
-            }
-        }
-        "bar_height" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.bar_height = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Bar height set to {}px", value));
-                }
-            }
-        }
-        "border_width" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.border_width = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Border width set to {}px", value));
-                }
-            }
-        }
-        "border_font_size" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.border_font_size = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Border font size set to {}px", value));
-                }
-            }
-        }
-        "transition_duration" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.transition_duration = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Transition duration set to {}ms", value));
-                }
-            }
-        }
-        "fullscreen_border_width" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.fullscreen_border_width = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Fullscreen border width set to {}px", value));
-                }
-            }
-        }
-        "cascade_border_width" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.cascade_border_width = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Cascade border width set to {}px", value));
-                }
-            }
-        }
-        "grid_gap" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.grid_gap = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Grid gap set to {}px", value));
-                }
-            }
-        }
-        "grid_border_width" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.grid_border_width = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Grid border width set to {}px", value));
-                }
-            }
-        }
-        "floating_border_width" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.floating_border_width = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Floating border width set to {}px", value));
-                }
-            }
-        }
-        "border_color" | "high_color" => {
-            if let Some((r, g, b, a)) = parse_hex_color(value_str) {
-                state.layout.border_r = r;
-                state.layout.border_g = g;
-                state.layout.border_b = b;
-                state.layout.border_a = a;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Border color set to {}", value_str));
-                }
-            }
-        }
-        "background_color" | "low_color" => {
-            if let Some((r, g, b, a)) = parse_hex_color(value_str) {
-                state.layout.background_r = r;
-                state.layout.background_g = g;
-                state.layout.background_b = b;
-                state.layout.background_a = a;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Background color set to {}", value_str));
-                }
-            }
-        }
-        "side_panel_behavior" | "side-panel-behavior" => {
-            state.layout.side_panel_behavior = value_str.to_string();
-            if state.notifications_enable {
-                crate::config::show_notification("cce-client", &format!("Side panel behavior set to {}", value_str));
-            }
-        }
-        "side_panel_width" | "side-panel-width" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.side_panel_width = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Side panel width set to {}px", value));
-                }
-            }
-        }
-        "side_panel_position" | "side-panel-position" => {
-            state.layout.side_panel_position = value_str.to_string();
-            if state.notifications_enable {
-                crate::config::show_notification("cce-client", &format!("Side panel position set to {}", value_str));
-            }
-        }
-        "side_panel_border_gap" | "side-panel-border-gap" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.side_panel_border_gap = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Side panel border gap set to {}px", value));
-                }
-            }
-        }
-        "side_panel_border_opacity" | "side-panel-border-opacity" => {
-            if let Ok(value) = value_str.parse::<i32>() {
-                state.layout.side_panel_border_opacity = value;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Side panel border opacity set to {}%", value));
-                }
-            }
-        }
-        _ => {}
-    }
-    state.needs_render = true;
-}
-
-/// Handle "mode <mode> <app_id_pattern> [--single] [--tag N] [title_pattern]" command
-fn handle_mode_command(rest: &str, state: &mut WindowManager) {
-    let mut parts: Vec<&str> = rest.split_whitespace().collect();
-    if parts.len() < 2 {
-        return;
-    }
-
-    let mode_str = parts[0];
-    let app_id_pattern = parts[1].to_string();
-
-    // Remove the first two tokens
-    parts.drain(0..2);
-
-    let mut title_pattern: Option<String> = None;
-    let mut single_instance = false;
-    let mut tag = 0i32;
-
-    let mut i = 0;
-    while i < parts.len() {
-        if parts[i] == "--single" {
-            single_instance = true;
-        } else if parts[i] == "--tag" {
-            if i + 1 < parts.len() {
-                tag = parts[i + 1].parse::<i32>().unwrap_or(0);
-                i += 1;
-            }
-        } else if title_pattern.is_none() {
-            title_pattern = Some(parts[i].to_string());
-        }
-        i += 1;
-    }
-
-    let mode = parse_tiling_mode(mode_str);
-    if let Some(existing) = state.mode_rules.iter_mut().find(|r| {
-        r.app_id_pattern == app_id_pattern && r.title_pattern == title_pattern
-    }) {
-        existing.mode = mode;
-        existing.single_instance = single_instance;
-        existing.tag = tag;
-    } else {
-        state.mode_rules.push(ModeRule {
-            mode,
-            app_id_pattern,
-            title_pattern,
-            single_instance,
-            tag,
-            circular: false,
-            ssd: None,
-        });
-    }
-}
-
-/// Handle "set-mode <mode>" command — set the focused window's tiling mode
-fn handle_set_mode_command(rest: &str, state: &mut WindowManager) {
-    let mode_str = rest.trim();
-    if mode_str.is_empty() {
-        return;
-    }
-    let mode = parse_tiling_mode(mode_str);
-    let notifications_enable = state.notifications_enable;
-    if let Some(window) = state.focused_window_mut() {
-        let old_mode = window.tiling_mode;
-        window.tiling_mode = mode;
-        window.mode_locked = true;
-        if (mode == TilingMode::Floating || mode == TilingMode::Popup)
-            && old_mode != TilingMode::Floating
-            && old_mode != TilingMode::Popup
-        {
-            window.width = 0;
-            window.height = 0;
-        }
-        if notifications_enable {
-            let win_title = window.title.as_deref().unwrap_or("Window");
-            crate::config::show_notification("cce-client", &format!("Tiling mode set to {} for: {}", mode.as_str(), win_title));
-        }
-        state.needs_render = true;
-        state.needs_status_update = true;
-    }
-}
-
-/// Handle "apply-mode-sharing <mode>" command — apply <mode> to all windows sharing a tiling mode with the focused window.
-fn handle_apply_mode_sharing_command(rest: &str, state: &mut WindowManager) {
-    let mode_str = rest.trim();
-    if mode_str.is_empty() {
-        return;
-    }
-    let new_mode = parse_tiling_mode(mode_str);
-
-    // Find the focused window's current tiling mode
-    let old_mode = if let Some(window) = state.focused_window() {
-        window.tiling_mode
-    } else {
-        return;
-    };
-
-    let notifications_enable = state.notifications_enable;
-
-    // Iterate over all windows and update tiling mode for matching windows
-    for window in &mut state.windows {
-        if !window.closed && window.tiling_mode == old_mode {
-            let win_old_mode = window.tiling_mode;
-            window.tiling_mode = new_mode;
-            window.mode_locked = true;
-            if (new_mode == TilingMode::Floating || new_mode == TilingMode::Popup)
-                && win_old_mode != TilingMode::Floating
-                && win_old_mode != TilingMode::Popup
-            {
-                window.width = 0;
-                window.height = 0;
-            }
-        }
-    }
-
-    if notifications_enable {
-        crate::config::show_notification("cce-client", &format!("Applied tiling mode {} to all windows sharing mode {}", new_mode.as_str(), old_mode.as_str()));
-    }
-    state.needs_render = true;
-    state.needs_status_update = true;
-}
-
-/// Handle "bind <mods> <key> <action> [command]" command
-fn handle_bind_command(rest: &str, state: &mut WindowManager) {
-    // Format: bind <mods> <key> <action> [command...]
-    let parts: Vec<&str> = rest.splitn(3, ' ').collect();
-    if parts.len() < 3 {
-        return;
-    }
-    let mod_str = parts[0];
-    let key_str = parts[1];
-    let action_and_cmd = parts[2];
-
-    let mods = parse_modifiers(mod_str);
-    let keysym = parse_keysym(key_str);
-
-    // Split action from command
-    let (action_str, command) = if let Some(space_pos) = action_and_cmd.find(' ') {
-        let (a, c) = action_and_cmd.split_at(space_pos);
-        (a, Some(c.trim_start().to_string()))
-    } else {
-        (action_and_cmd, None)
-    };
-
-    let action = parse_action(action_str);
-    let command = if action == Action::Spawn || action == Action::Toggle {
-        command
-    } else {
-        None
-    };
-
-    state.pending_bindings.push(PendingXkbBinding {
-        mods,
-        keysym,
-        action,
-        command,
-    });
-}
-
-/// Handle "pbind <mods> <button> <action>" command
-fn handle_pbind_command(rest: &str, state: &mut WindowManager) {
-    let parts: Vec<&str> = rest.split_whitespace().collect();
-    if parts.len() < 3 {
-        return;
-    }
-    let mod_str = parts[0];
-    let button_str = parts[1];
-    let action_str = parts[2];
-
-    let mods = parse_modifiers(mod_str);
-    let button = parse_button(button_str);
-    let action = parse_action(action_str);
-
-    state.pending_pointer_bindings.push(PendingPointerBinding {
-        mods,
-        button,
-        action,
-    });
-}
-
-/// Handle "tag-layout <tag> <mode>" command
-fn handle_tag_layout_command(rest: &str, state: &mut WindowManager) {
-    let parts: Vec<&str> = rest.split_whitespace().collect();
-    if parts.len() < 2 {
-        return;
-    }
-    if let Ok(tag) = parts[0].parse::<i32>() {
-        if tag >= 1 && tag <= NUM_TAGS as i32 {
-            let mode = parse_tiling_mode(parts[1]);
-            state.tag_layouts[tag as usize - 1] = mode;
-            state.has_tag_layout[tag as usize - 1] = true;
-            if state.notifications_enable {
-                crate::config::show_notification("cce-client", &format!("Tag {} layout set to {}", tag, mode.as_str()));
-            }
-            state.needs_render = true;
-            state.needs_status_update = true;
-        }
-    }
-}
-
-/// Handle "set-tag <tag>" command — set the focused window's tag
-fn handle_set_tag_command(rest: &str, state: &mut WindowManager) {
-    let tag_str = rest.trim();
-    if let Ok(tag) = tag_str.parse::<i32>() {
-        if tag >= 1 && tag <= NUM_TAGS as i32 {
-            // Read focused_id before any mutable borrow
-            let focused_id = state
-                .seats
-                .iter()
-                .find(|s| !s.removed)
-                .and_then(|s| s.focused_window_id);
-            if let Some(focused_id) = focused_id {
-                // Set the window's tag
-                let active_tags = state.active_tags;
-                let window_left_active_tag =
-                    state.get_window_mut(focused_id).map_or(false, |window| {
-                        window.tags = 1 << (tag - 1);
-                        (window.tags & active_tags) == 0
-                    });
-
-                // If the window is no longer on an active tag, shift focus
-                if window_left_active_tag {
-                    let visible_ids: Vec<u64> = state
-                        .windows
-                        .iter()
-                        .filter(|w| (w.tags & state.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                        .map(|w| w.id)
-                        .collect();
-                    if let Some(seat) = state.seats.iter_mut().find(|s| !s.removed) {
-                        seat.focused_window_id = visible_ids.last().copied();
-                    }
-                    state.needs_focus = true;
-                }
-                state.needs_render = true;
-                state.needs_status_update = true;
-            }
-        }
-    }
-}
-
-/// Handle "repeat <rate> <delay>" command
-fn handle_repeat_command(_rest: &str, _state: &mut WindowManager) {
-    // The actual repeat rate change is applied to input devices via the
-    // Wayland protocol; this is a placeholder for the pure-logic layer.
-    // The WM integration layer will read the rate/delay and call
-    // river_input_device_v1_set_repeat_info()
-}
-
-fn handle_input_command(rest: &str, state: &mut WindowManager) {
-    let tokens: Vec<&str> = rest.splitn(2, ' ').collect();
-    let param = tokens[0];
-    let value_str = if tokens.len() > 1 { tokens[1] } else { "" };
-
-    match param {
-        "tap-to-click" | "tap_to_click" => {
-            let old_val = state.tap_to_click;
-            match value_str {
-                "true" | "1" | "enabled" => {
-                    state.tap_to_click = true;
-                    state.tap_config_applied = false; // re-apply
-                }
-                "false" | "0" | "disabled" => {
-                    state.tap_to_click = false;
-                    state.tap_config_applied = false; // re-apply
-                }
-                "toggle" => {
-                    state.tap_to_click = !state.tap_to_click;
-                    state.tap_config_applied = false; // re-apply
-                }
-                _ => {}
-            }
-            if state.tap_to_click != old_val && state.notifications_enable {
-                crate::config::show_notification(
-                    "cce-client",
-                    &format!(
-                        "Tap-to-click {}",
-                        if state.tap_to_click { "enabled" } else { "disabled" }
-                    ),
-                );
-            }
-        }
-        "accel-speed" | "accel_speed" => {
-            if let Ok(val) = value_str.parse::<f64>() {
-                state.accel_speed = Some(val);
-                state.tap_config_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Acceleration speed set to {}", val));
-                }
-            }
-        }
-        "accel-profile" | "accel_profile" => {
-            let val = value_str.trim().to_string();
-            if !val.is_empty() {
-                state.accel_profile = Some(val.clone());
-                state.tap_config_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Acceleration profile set to {}", val));
-                }
-            }
-        }
-        "natural-scroll" | "natural_scroll" => {
-            let old_val = state.natural_scroll;
-            match value_str {
-                "true" | "1" | "enabled" => {
-                    state.natural_scroll = Some(true);
-                }
-                "false" | "0" | "disabled" => {
-                    state.natural_scroll = Some(false);
-                }
-                "toggle" => {
-                    state.natural_scroll = Some(!state.natural_scroll.unwrap_or(false));
-                }
-                _ => {}
-            }
-            if state.natural_scroll != old_val {
-                state.tap_config_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification(
-                        "cce-client",
-                        &format!(
-                            "Natural scroll {}",
-                            if state.natural_scroll.unwrap_or(false) { "enabled" } else { "disabled" }
-                        ),
-                    );
-                }
-            }
-        }
-        "dwt" => {
-            let old_val = state.dwt;
-            match value_str {
-                "true" | "1" | "enabled" => {
-                    state.dwt = Some(true);
-                }
-                "false" | "0" | "disabled" => {
-                    state.dwt = Some(false);
-                }
-                "toggle" => {
-                    state.dwt = Some(!state.dwt.unwrap_or(false));
-                }
-                _ => {}
-            }
-            if state.dwt != old_val {
-                state.tap_config_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification(
-                        "cce-client",
-                        &format!(
-                            "Disable-while-typing {}",
-                            if state.dwt.unwrap_or(false) { "enabled" } else { "disabled" }
-                        ),
-                    );
-                }
-            }
-        }
-        "dwtp" => {
-            let old_val = state.dwtp;
-            match value_str {
-                "true" | "1" | "enabled" => {
-                    state.dwtp = Some(true);
-                }
-                "false" | "0" | "disabled" => {
-                    state.dwtp = Some(false);
-                }
-                "toggle" => {
-                    state.dwtp = Some(!state.dwtp.unwrap_or(false));
-                }
-                _ => {}
-            }
-            if state.dwtp != old_val {
-                state.tap_config_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification(
-                        "cce-client",
-                        &format!(
-                            "Disable-while-trackpointing {}",
-                            if state.dwtp.unwrap_or(false) { "enabled" } else { "disabled" }
-                        ),
-                    );
-                }
-            }
-        }
-        "trackpad-disabled" | "trackpad_disabled" => {
-            let old_val = state.trackpad_disabled;
-            match value_str {
-                "true" | "1" | "enabled" => {
-                    state.trackpad_disabled = true;
-                }
-                "false" | "0" | "disabled" => {
-                    state.trackpad_disabled = false;
-                }
-                _ => {}
-            }
-            if state.trackpad_disabled != old_val {
-                state.tap_config_applied = false;
-            }
-        }
-        "trackpoint-accel-speed" | "trackpoint_accel_speed" => {
-            if let Ok(val) = value_str.parse::<f64>() {
-                state.trackpoint_accel_speed = Some(val);
-                state.tap_config_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Trackpoint acceleration speed set to {}", val));
-                }
-            }
-        }
-        "trackpoint-accel-profile" | "trackpoint_accel_profile" => {
-            let val = value_str.trim().to_string();
-            if !val.is_empty() {
-                state.trackpoint_accel_profile = Some(val.clone());
-                state.tap_config_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Trackpoint acceleration profile set to {}", val));
-                }
-            }
-        }
-        "cursor-theme" | "cursor_theme" => {
-            let val = value_str.trim().to_string();
-            if !val.is_empty() {
-                state.cursor_theme = Some(val.clone());
-                state.cursor_theme_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Cursor theme set to {}", val));
-                }
-            }
-        }
-        "cursor-size" | "cursor_size" => {
-            if let Ok(val) = value_str.parse::<u32>() {
-                state.cursor_size = Some(val);
-                state.cursor_theme_applied = false;
-                if state.notifications_enable {
-                    crate::config::show_notification("cce-client", &format!("Cursor size set to {}", val));
-                }
-            }
-        }
-        _ => {}
-    }
-}
-
-
-fn parse_keycode(key: &str) -> Option<u16> {
-    let key = key.trim();
-    if let Ok(val) = key.parse::<u16>() {
-        return Some(val);
-    }
-    match key.to_lowercase().as_str() {
-        "esc" | "escape" => Some(1),
-        "1" => Some(2), "2" => Some(3), "3" => Some(4), "4" => Some(5),
-        "5" => Some(6), "6" => Some(7), "7" => Some(8), "8" => Some(9),
-        "9" => Some(10), "0" => Some(11),
-        "minus" => Some(12), "equal" => Some(13), "backspace" => Some(14),
-        "tab" => Some(15),
-        "q" => Some(16), "w" => Some(17), "e" => Some(18), "r" => Some(19),
-        "t" => Some(20), "y" => Some(21), "u" => Some(22), "i" => Some(23),
-        "o" => Some(24), "p" => Some(25),
-        "leftbrace" | "[" => Some(26), "rightbrace" | "]" => Some(27),
-        "enter" | "return" => Some(28),
-        "ctrl" | "leftctrl" => Some(29),
-        "a" => Some(30), "s" => Some(31), "d" => Some(32), "f" => Some(33),
-        "g" => Some(34), "h" => Some(35), "j" => Some(36), "k" => Some(37),
-        "l" => Some(38), "semicolon" | ";" => Some(39), "apostrophe" | "'" => Some(40),
-        "grave" | "`" => Some(41), "shift" | "leftshift" => Some(42),
-        "backslash" | "\\" => Some(43),
-        "z" => Some(44), "x" => Some(45), "c" => Some(46), "v" => Some(47),
-        "b" => Some(48), "n" => Some(49), "m" => Some(50),
-        "comma" | "," => Some(51), "dot" | "." => Some(52), "slash" | "/" => Some(53),
-        "rightshift" => Some(54),
-        "alt" | "leftalt" => Some(56), "space" => Some(57), "capslock" => Some(58),
-        "f1" => Some(59), "f2" => Some(60), "f3" => Some(61), "f4" => Some(62),
-        "f5" => Some(63), "f6" => Some(64), "f7" => Some(65), "f8" => Some(66),
-        "f9" => Some(67), "f10" => Some(68),
-        "f11" => Some(87), "f12" => Some(88),
-        "rightctrl" => Some(97), "rightalt" => Some(100),
-        "home" => Some(102), "up" => Some(103), "pageup" => Some(104),
-        "left" => Some(105), "right" => Some(106), "end" => Some(107),
-        "down" => Some(108), "pagedown" => Some(109), "insert" => Some(110),
-        "delete" => Some(111),
-        "super" | "hyper" | "meta" | "logo" | "leftmeta" | "leftsuper" => Some(125),
-        "rightmeta" | "rightsuper" => Some(126),
-        _ => None,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::types::{TilingMode, WindowManager};
-
-    #[test]
-    fn test_ipc_view() {
-        let mut state = WindowManager::default();
-        assert_eq!(state.active_tags, 1);
-
-        handle_ipc_command("view-2", &mut state);
-        assert_eq!(state.active_tags, 2);
-
-        handle_ipc_command("view 3", &mut state);
-        assert_eq!(state.active_tags, 4);
-    }
-
-    #[test]
-    fn test_ipc_toggle() {
-        let mut state = WindowManager::default();
-        assert_eq!(state.active_tags, 1);
-
-        handle_ipc_command("toggle-2", &mut state);
-        assert_eq!(state.active_tags, 3); // tag 1 + tag 2
-
-        handle_ipc_command("toggle-1", &mut state);
-        assert_eq!(state.active_tags, 2); // tag 2 only
-    }
-
-    #[test]
-    fn test_ipc_config_done() {
-        let mut state = WindowManager::default();
-        assert!(!state.config_done);
-
-        handle_ipc_command("config-done", &mut state);
-        assert!(state.config_done);
-    }
-
-    #[test]
-    fn test_ipc_layout_gap() {
-        let mut state = WindowManager::default();
-        assert_eq!(state.layout.gap, 48);
-
-        handle_ipc_command("layout gap 18", &mut state);
-        assert_eq!(state.layout.gap, 18);
-    }
-
-    #[test]
-    fn test_ipc_layout_grid_gap() {
-        let mut state = WindowManager::default();
-        assert_eq!(state.layout.grid_gap, 18);
-
-        handle_ipc_command("layout grid_gap 24", &mut state);
-        assert_eq!(state.layout.grid_gap, 24);
-    }
-
-    #[test]
-    fn test_ipc_layout_gap_sides() {
-        let mut state = WindowManager::default();
-        assert_eq!(state.layout.gap_top, 48);
-        assert_eq!(state.layout.gap_left, 48);
-        assert_eq!(state.layout.gap_right, 48);
-        assert_eq!(state.layout.gap_bottom, 48);
-
-        handle_ipc_command("layout gap_top 10", &mut state);
-        handle_ipc_command("layout gap_left 20", &mut state);
-        handle_ipc_command("layout gap_right 30", &mut state);
-        handle_ipc_command("layout gap_bottom 40", &mut state);
-        assert_eq!(state.layout.gap_top, 10);
-        assert_eq!(state.layout.gap_left, 20);
-        assert_eq!(state.layout.gap_right, 30);
-        assert_eq!(state.layout.gap_bottom, 40);
-    }
-
-    #[test]
-    fn test_ipc_layout_border_color() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("layout border_color #5c9060", &mut state);
-        assert_eq!(state.layout.border_r, 0x5C5C5C5C);
-        assert_eq!(state.layout.border_g, 0x90909090);
-        assert_eq!(state.layout.border_b, 0x60606060);
-    }
-
-    #[test]
-    fn test_ipc_mode_rule() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("mode cascade ghostty", &mut state);
-        assert_eq!(state.mode_rules.len(), 1);
-        assert_eq!(state.mode_rules[0].mode, TilingMode::Cascade);
-        assert_eq!(state.mode_rules[0].app_id_pattern, "ghostty");
-    }
-
-    #[test]
-    fn test_ipc_mode_rule_update() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("mode fullscreen cce-system-interface", &mut state);
-        assert_eq!(state.mode_rules.len(), 1);
-        assert_eq!(state.mode_rules[0].mode, TilingMode::Fullscreen);
-
-        handle_ipc_command("mode cascade cce-system-interface", &mut state);
-        assert_eq!(state.mode_rules.len(), 1);
-        assert_eq!(state.mode_rules[0].mode, TilingMode::Cascade);
-    }
-
-    #[test]
-    fn test_ipc_mode_rule_with_single() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("mode cascade qutebrowser --single", &mut state);
-        assert!(state.mode_rules[0].single_instance);
-    }
-
-    #[test]
-    fn test_ipc_mode_rule_with_tag() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("mode cascade ghostty --tag 2", &mut state);
-        assert_eq!(state.mode_rules[0].tag, 2);
-    }
-
-    #[test]
-    fn test_ipc_bind() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("bind alt Return spawn ghostty", &mut state);
-        assert_eq!(state.pending_bindings.len(), 1);
-        assert_eq!(state.pending_bindings[0].action, Action::Spawn);
-        assert_eq!(
-            state.pending_bindings[0].command,
-            Some("ghostty".to_string())
-        );
-    }
-
-    #[test]
-    fn test_ipc_pbind() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("pbind alt left move", &mut state);
-        assert_eq!(state.pending_pointer_bindings.len(), 1);
-        assert_eq!(state.pending_pointer_bindings[0].action, Action::Move);
-        assert_eq!(state.pending_pointer_bindings[0].button, 0x110); // BTN_LEFT
-    }
-
-    #[test]
-    fn test_ipc_tag_layout() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("tag-layout 2 grid", &mut state);
-        assert!(state.has_tag_layout[1]);
-        assert_eq!(state.tag_layouts[1], TilingMode::Grid);
-    }
-
-    #[test]
-    fn test_ipc_set_tag() {
-        let mut state = WindowManager::default();
-        // Add a window and a seat with focus
-        state.windows.push(crate::types::Window {
-            id: 1,
-            tags: 1,
-            ..Default::default()
-        });
-        state.seats.push(crate::types::Seat {
-            id: 1,
-            focused_window_id: Some(1),
-            ..Default::default()
-        });
-
-        handle_ipc_command("set-tag 3", &mut state);
-        assert_eq!(state.windows[0].tags, 4); // 1 << 2
-    }
-
-    #[test]
-    fn test_ipc_empty_command() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("", &mut state);
-        handle_ipc_command("  \n", &mut state);
-        // Should not crash
-    }
-
-    #[test]
-    fn test_ipc_layout_cascade_offset() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("layout cascade_offset 32", &mut state);
-        assert_eq!(state.layout.cascade_offset, 32);
-    }
-
-    #[test]
-    fn test_ipc_layout_bar_height() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("layout bar_height 28", &mut state);
-        assert_eq!(state.layout.bar_height, 28);
-    }
-
-    #[test]
-    fn test_ipc_layout_border_width() {
-        let mut state = WindowManager::default();
-        handle_ipc_command("layout border_width 18", &mut state);
-        assert_eq!(state.layout.border_width, 18);
-    }
-
-    #[test]
-    fn test_ipc_expose() {
-        let mut state = WindowManager::default();
-        assert!(!state.expose_active);
-        handle_ipc_command("expose", &mut state);
-        assert!(state.expose_active);
-        handle_ipc_command("expose", &mut state);
-        assert!(!state.expose_active);
-    }
-
-    #[test]
-    fn test_ipc_expose_exit() {
-        let mut state = WindowManager::default();
-        state.seats.push(crate::types::Seat {
-            id: 1,
-            hovered_window_id: Some(42),
-            focused_window_id: Some(10),
-            ..Default::default()
-        });
-
-        // 1. When expose is not active, "expose-exit" should do nothing.
-        assert!(!state.expose_active);
-        handle_ipc_command("expose-exit", &mut state);
-        assert!(!state.expose_active);
-        assert_eq!(state.seats[0].focused_window_id, Some(10));
-
-        // 2. When expose is active, "expose-exit" should deactivate it and focus the hovered window.
-        state.expose_active = true;
-        handle_ipc_command("expose-exit", &mut state);
-        assert!(!state.expose_active);
-        assert_eq!(state.seats[0].focused_window_id, Some(42));
-        assert!(state.needs_focus);
-        assert!(state.needs_render);
-    }
-
-    #[test]
-    fn test_ipc_view_next_prev() {
-        let mut state = WindowManager::default();
-        state.active_tags = 1; // Tag 1 (1 << 0)
-
-        // view-next should go: Tag 1 -> Tag 2 -> Tag 3 -> Tag 4 -> Tag 1
-        handle_ipc_command("view-next", &mut state);
-        assert_eq!(state.active_tags, 2); // Tag 2
-
-        handle_ipc_command("view-next", &mut state);
-        assert_eq!(state.active_tags, 4); // Tag 3
-
-        handle_ipc_command("view-next", &mut state);
-        assert_eq!(state.active_tags, 8); // Tag 4
-
-        handle_ipc_command("view-next", &mut state);
-        assert_eq!(state.active_tags, 1); // Tag 1 (wrap around)
-
-        // view-prev should go: Tag 1 -> Tag 4 -> Tag 3 -> Tag 2 -> Tag 1
-        handle_ipc_command("view-prev", &mut state);
-        assert_eq!(state.active_tags, 8); // Tag 4 (wrap around)
-
-        handle_ipc_command("view-prev", &mut state);
-        assert_eq!(state.active_tags, 4); // Tag 3
-
-        handle_ipc_command("view-prev", &mut state);
-        assert_eq!(state.active_tags, 2); // Tag 2
-
-        handle_ipc_command("view-prev", &mut state);
-        assert_eq!(state.active_tags, 1); // Tag 1
-    }
-
-    #[test]
-    fn test_ipc_focus_next_prev() {
-        let mut state = WindowManager::default();
-        // Setup 3 windows
-        state.windows.push(crate::types::Window { id: 1, tags: 1, ..Default::default() });
-        state.windows.push(crate::types::Window { id: 2, tags: 1, ..Default::default() });
-        state.windows.push(crate::types::Window { id: 3, tags: 1, ..Default::default() });
-        state.seats.push(crate::types::Seat { id: 1, focused_window_id: Some(1), ..Default::default() });
-
-        handle_ipc_command("focus-next", &mut state);
-        assert_eq!(state.seats[0].focused_window_id, Some(2));
-
-        handle_ipc_command("focus-next", &mut state);
-        assert_eq!(state.seats[0].focused_window_id, Some(1));
-
-        handle_ipc_command("focus-next", &mut state);
-        assert_eq!(state.seats[0].focused_window_id, Some(3));
-
-        handle_ipc_command("focus-prev", &mut state);
-        assert_eq!(state.seats[0].focused_window_id, Some(1));
-
-        handle_ipc_command("focus-prev", &mut state);
-        assert_eq!(state.seats[0].focused_window_id, Some(3));
-    }
-
-    #[test]
-    fn test_ipc_pointer_and_keys() {
-        let mut state = WindowManager::default();
-        
-        // Test pointer location query
-        let location = handle_ipc_command("pointer-location", &mut state);
-        assert!(location.ends_with("\n"));
-        let coords: Vec<&str> = location.trim().split_whitespace().collect();
-        assert_eq!(coords.len(), 2);
-        assert_eq!(coords[0].parse::<i32>().is_ok(), true);
-        assert_eq!(coords[1].parse::<i32>().is_ok(), true);
-
-        // Test button parsing helpers
-        assert_eq!(parse_button("left"), 272);
-        assert_eq!(parse_button("right"), 273);
-        assert_eq!(parse_button("middle"), 274);
-        assert_eq!(parse_button("side"), 275);
-        assert_eq!(parse_button("extra"), 276);
-        assert_eq!(parse_button("280"), 280);
-        assert_eq!(parse_button("invalid"), 0);
-
-        // Test keycode parsing helpers
-        assert_eq!(parse_keycode("escape"), Some(1));
-        assert_eq!(parse_keycode("enter"), Some(28));
-        assert_eq!(parse_keycode("a"), Some(30));
-        assert_eq!(parse_keycode("30"), Some(30));
-        assert_eq!(parse_keycode("invalid"), None);
-
-        // Test some simulation commands return error if invalid or ok (without input controller it shouldn't crash)
-        let r1 = handle_ipc_command("pointer-move-to invalid", &mut state);
-        assert!(r1.starts_with("error:"));
-        let r2 = handle_ipc_command("pointer-move-by 10", &mut state);
-        assert!(r2.starts_with("error:"));
-        let r3 = handle_ipc_command("pointer-click invalid", &mut state);
-        assert!(r3.starts_with("error:"));
-        let r4 = handle_ipc_command("keypress invalid", &mut state);
-        assert!(r4.starts_with("error:"));
-    }
-}
diff --git a/src/client/ipc_server.rs b/src/client/ipc_server.rs
deleted file mode 100644
index 858d1f3..0000000
--- a/src/client/ipc_server.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-use std::io::{Read, Write};
-use std::os::unix::net::{UnixListener, UnixStream};
-use std::sync::mpsc;
-
-use crate::paths;
-
-pub struct IpcRequest {
-    pub command: String,
-    pub reply_tx: mpsc::Sender<String>,
-}
-
-pub struct IpcReceiver {
-    pub rx: mpsc::Receiver<IpcRequest>,
-    pub tx: mpsc::Sender<IpcRequest>,
-}
-
-pub fn spawn_ipc_server(pipe_write: libc::c_int) -> IpcReceiver {
-    let (tx, rx) = mpsc::channel::<IpcRequest>();
-    let tx_clone = tx.clone();
-
-    std::thread::Builder::new()
-        .name("cce-client-ipc".into())
-        .spawn(move || {
-            ipc_server_main(tx, pipe_write);
-        })
-        .expect("failed to spawn IPC server thread");
-
-    IpcReceiver { rx, tx: tx_clone }
-}
-
-fn ipc_server_main(tx: mpsc::Sender<IpcRequest>, pipe_write: libc::c_int) {
-    let socket_path = paths::get_socket_path();
-    let _ = std::fs::remove_file(&socket_path);
-
-    let listener = match UnixListener::bind(&socket_path) {
-        Ok(l) => l,
-        Err(e) => {
-            eprintln!("[ipc] failed to bind {}: {}", socket_path, e);
-            return;
-        }
-    };
-
-    if let Err(e) = listener.set_nonblocking(true) {
-        eprintln!("[ipc] failed to set non-blocking: {}", e);
-        return;
-    }
-
-    eprintln!("[ipc] listening on {}", socket_path);
-
-    let mut streams: Vec<UnixStream> = Vec::new();
-
-    loop {
-        let mut activity = false;
-        for _ in 0..5 {
-            match listener.accept() {
-                Ok((stream, _addr)) => {
-                    if let Err(e) = stream.set_nonblocking(true) {
-                        eprintln!("[ipc] failed to set non-blocking on client: {}", e);
-                        continue;
-                    }
-                    eprintln!("[ipc] new connection");
-                    streams.push(stream);
-                    activity = true;
-                }
-                Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
-                Err(e) => {
-                    eprintln!("[ipc] accept error: {}", e);
-                    break;
-                }
-            }
-        }
-
-        let mut dead = Vec::new();
-        for (i, stream) in streams.iter_mut().enumerate() {
-            let mut buf = [0u8; 4096];
-            match stream.read(&mut buf) {
-                Ok(0) => {
-                    dead.push(i);
-                }
-                Ok(n) => {
-                    let s = String::from_utf8_lossy(&buf[..n]);
-                    let mut sent = false;
-                    let (reply_tx, reply_rx) = mpsc::channel();
-                    let mut cmd_count = 0;
-                    for line in s.lines() {
-                        let cmd = line.trim().to_string();
-                        if !cmd.is_empty() {
-                            let _ = tx.send(IpcRequest {
-                                command: cmd,
-                                reply_tx: reply_tx.clone(),
-                            });
-                            sent = true;
-                            cmd_count += 1;
-                        }
-                    }
-                    if sent {
-                        // Wake up main thread
-                        unsafe {
-                            libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
-                        }
-                        
-                        let mut response = String::new();
-                        for _ in 0..cmd_count {
-                            if let Ok(res) = reply_rx.recv_timeout(std::time::Duration::from_millis(1000)) {
-                                response.push_str(&res);
-                            } else {
-                                response.push_str("error: timeout or no response\n");
-                            }
-                        }
-                        let _ = stream.write_all(response.as_bytes());
-                        dead.push(i);
-                    }
-                    activity = true;
-                }
-                Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
-                Err(e) => {
-                    eprintln!("[ipc] read error: {}", e);
-                    dead.push(i);
-                }
-            }
-        }
-
-        for i in dead.into_iter().rev() {
-            streams.remove(i);
-        }
-
-        if !activity {
-            std::thread::sleep(std::time::Duration::from_millis(20));
-        }
-    }
-}
diff --git a/src/client/lib.rs b/src/client/lib.rs
deleted file mode 100644
index ac40d31..0000000
--- a/src/client/lib.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-// cce-client — Wayland window manager for river, written in Rust
-
-pub mod borders;
-pub mod config;
-pub mod decorations;
-pub mod ipc;
-pub mod ipc_server;
-pub mod protocol;
-pub mod restart;
-pub mod state;
-pub mod status;
-pub mod status_server;
-pub mod tiling;
-pub mod types;
-#[allow(unreachable_patterns)] // wayland event match arms use _ => {} for forward-compat
-pub mod wayland;
-pub mod wm;
-pub mod input;
-pub mod paths;
-pub mod run;
-
-pub use run::run_client;
diff --git a/src/client/paths.rs b/src/client/paths.rs
deleted file mode 100644
index a74053a..0000000
--- a/src/client/paths.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-use std::env;
- 
-pub fn get_socket_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-{}.sock", display)
-    } else {
-        "/tmp/cce-client.sock".to_string()
-    }
-}
- 
-pub fn get_status_socket_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-status-{}.sock", display)
-    } else {
-        "/tmp/cce-client-status.sock".to_string()
-    }
-}
- 
-pub fn get_windows_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-windows-{}", display)
-    } else {
-        "/tmp/cce-client-windows".to_string()
-    }
-}
- 
-pub fn get_tags_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-tags-{}", display)
-    } else {
-        "/tmp/cce-client-tags".to_string()
-    }
-}
- 
-pub fn get_layout_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-layout-{}", display)
-    } else {
-        "/tmp/cce-client-layout".to_string()
-    }
-}
- 
-pub fn get_title_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-title-{}", display)
-    } else {
-        "/tmp/cce-client-title".to_string()
-    }
-}
- 
-pub fn get_death_log_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-death-{}.log", display)
-    } else {
-        "/tmp/cce-client-death.log".to_string()
-    }
-}
- 
-pub fn get_bt_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-bt-{}.txt", display)
-    } else {
-        "/tmp/cce-client-bt.txt".to_string()
-    }
-}
- 
-pub fn get_input_coords_socket_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/clear-input-coords-{}.sock", display)
-    } else {
-        "/tmp/clear-input-coords.sock".to_string()
-    }
-}
- 
-pub fn get_log_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-{}.log", display)
-    } else {
-        "/tmp/cce-client.log".to_string()
-    }
-}
- 
-pub fn get_prev_log_path() -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-{}-prev.log", display)
-    } else {
-        "/tmp/cce-client-prev.log".to_string()
-    }
-}
- 
-pub fn get_xprop_path(wid: u64) -> String {
-    if let Ok(display) = env::var("WAYLAND_DISPLAY") {
-        format!("/tmp/cce-client-xprop-{}-{}", wid, display)
-    } else {
-        format!("/tmp/cce-client-xprop-{}", wid)
-    }
-}
diff --git a/src/client/protocol.rs b/src/client/protocol.rs
deleted file mode 100644
index 8fd1d59..0000000
--- a/src/client/protocol.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-// River Wayland protocol bindings generated from XML
-
-macro_rules! river_protocol {
-    ($path:expr, [$($imports:path),*]) => {
-        #[allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)]
-        #[allow(non_upper_case_globals, non_snake_case, unused_imports, missing_docs, clippy::all)]
-        pub mod generated {
-            pub mod client {
-                use wayland_client;
-                use wayland_client::protocol::*;
-                $(use $imports::{client::*};)*
-
-                pub mod __interfaces {
-                    use wayland_client::protocol::__interfaces::*;
-                    $(use $imports::{client::__interfaces::*};)*
-                    wayland_scanner::generate_interfaces!($path);
-                }
-                use self::__interfaces::*;
-
-                wayland_scanner::generate_client_code!($path);
-            }
-        }
-        pub use self::generated::client;
-    };
-}
-
-pub mod river_window_management {
-    river_protocol!("protocol/river-window-management-v1.xml", []);
-}
-
-pub mod river_xkb_bindings {
-    river_protocol!(
-        "protocol/river-xkb-bindings-v1.xml",
-        [crate::protocol::river_window_management::generated]
-    );
-}
-
-pub mod river_layer_shell {
-    river_protocol!(
-        "protocol/river-layer-shell-v1.xml",
-        [crate::protocol::river_window_management::generated]
-    );
-}
-
-pub mod river_input_management {
-    river_protocol!(
-        "protocol/river-input-management-v1.xml",
-        [crate::protocol::river_window_management::generated]
-    );
-}
-
-pub mod river_libinput_config {
-    river_protocol!(
-        "protocol/river-libinput-config-v1.xml",
-        [crate::protocol::river_input_management::generated]
-    );
-}
-
-pub mod wlr_output_management {
-    river_protocol!("protocol/wlr-output-management-unstable-v1.xml", []);
-}
-
-pub mod clear_inspector {
-    river_protocol!("protocol/clear-inspector-v1.xml", []);
-}
diff --git a/src/client/restart.rs b/src/client/restart.rs
deleted file mode 100644
index 9cd1f2f..0000000
--- a/src/client/restart.rs
+++ /dev/null
@@ -1,279 +0,0 @@
-// Restart and reload logic for cce-client
-
-use crate::config::parse_config;
-use crate::types::WindowManager;
-
-/// Restart the window manager process.
-///
-/// This forks a child process that waits briefly for the parent to die
-/// (so River cleans up the old Wayland connection), then execs a fresh
-/// cce-client instance. The parent (current process) exits immediately.
-///
-/// The CCE_CLIENT_RESTARTING environment variable signals that this is a restart
-/// (not a cold start), so the new process skips cold_start_only apps.
-///
-/// ## Why setsid() is required
-///
-/// cce-client is typically a session leader (PID == SID, started by River's
-/// `-c` launch script). When a session leader exits, the kernel sends SIGHUP
-/// to all processes in that session — including the forked child. Without
-/// `setsid()`, the child dies from SIGHUP before it can exec, and cce-client
-/// never comes back.
-pub fn wm_restart() {
-    use std::time::Instant;
-
-    use crate::paths;
-
-    // Write to death log before fork — this is the last chance to capture
-    // why we're restarting, since fork+process::exit(0) silently kills the parent.
-    if let Ok(mut f) = std::fs::OpenOptions::new()
-        .create(true)
-        .append(true)
-        .open(paths::get_death_log_path())
-    {
-        use std::io::Write;
-        let _ = writeln!(f, "wm_restart() called — about to fork+exit");
-        let _ = f.sync_all(); // ensure it hits disk before process::exit(0)
-    }
-
-    static mut LAST_RESTART: Option<Instant> = None;
-
-    // Throttle restarts (minimum 2 seconds between restarts)
-    // Safety: single-threaded access; acceptable for this use case
-    let now = Instant::now();
-    let should_throttle = unsafe {
-        if let Some(last) = LAST_RESTART {
-            now.duration_since(last).as_secs_f64() < 2.0
-        } else {
-            false
-        }
-    };
-
-    if should_throttle {
-        std::thread::sleep(std::time::Duration::from_secs(2));
-    }
-
-    unsafe {
-        LAST_RESTART = Some(now);
-    }
-
-    // Signal that this is a restart, not a cold start
-    std::env::set_var("CCE_CLIENT_RESTARTING", "1");
-
-    // Persist state to ~/.cache/cce_client_state for the new instance to restore.
-    // Note: we can't pass &WindowManager here since wm_restart() has no access
-    // to it. The state file is kept fresh by RenderStart's needs_status_update
-    // path, so it should be reasonably up-to-date already.
-
-    // Save the current log before River's launch script truncates it on restart.
-    // This preserves the crash/reason for the restart.
-    let _ = std::fs::copy(paths::get_log_path(), paths::get_prev_log_path());
-
-    // Remove the IPC socket
-    let _ = std::fs::remove_file(paths::get_socket_path());
-
-    // Get the current executable path.
-    // CString is required because execl() needs a null-terminated C string;
-    // Rust's String::as_ptr() is NOT guaranteed to be null-terminated.
-    //
-    // We prefer the symlink path (~/.local/bin/cce-client) over current_exe()
-    // because current_exe() resolves through /proc/self/exe to the real path,
-    // which may be on a sync filesystem (Dropbox) that temporarily moves files.
-    // The symlink is on the root filesystem and always available.
-    let home = std::env::var("HOME").unwrap_or_default();
-    let exe_path = if !home.is_empty() {
-        let symlink = format!("{}/.local/bin/cce-client", home);
-        if std::path::Path::new(&symlink).exists() {
-            std::path::PathBuf::from(symlink)
-        } else {
-            std::env::current_exe().unwrap_or_else(|_| std::process::exit(1))
-        }
-    } else {
-        std::env::current_exe().unwrap_or_else(|_| std::process::exit(1))
-    };
-    let _ = std::io::Write::write_fmt(&mut std::io::stderr(), format_args!("wm_restart: exe_path={}\n", exe_path.display()));
-    let path_cstr = std::ffi::CString::new(exe_path.to_string_lossy().into_owned())
-        .unwrap_or_else(|_| std::process::exit(1));
-
-    // Prepare log path CString in the parent process before fork to be async-signal-safe.
-    let log_path = paths::get_log_path();
-    let log_path_cstr = std::ffi::CString::new(log_path)
-        .unwrap_or_else(|_| std::process::exit(1));
-
-    // Fork: child waits for parent to die, then execs fresh cce-client.
-    // Parent exits so River tears down the old Wayland connection.
-    let pid = unsafe { libc::fork() };
-    if pid < 0 {
-        // fork failed, just exit
-        std::process::exit(1);
-    } else if pid == 0 {
-        // ── Child process ──
-
-        // Create a new session IMMEDIATELY. Without this, the child stays
-        // in the parent's session. When the parent (session leader) calls
-        // process::exit(0), the kernel sends SIGHUP to every process in that
-        // session — killing the child before it can exec.
-        unsafe {
-            libc::setsid();
-            libc::signal(libc::SIGHUP, libc::SIG_IGN);
-        }
-
-        // Wait for parent to exit so River cleans up the old Wayland
-        // connection before we try to connect fresh.
-        let parent_pid = unsafe { libc::getppid() };
-        std::thread::sleep(std::time::Duration::from_millis(500));
-
-        // If parent is somehow still alive, wait a bit more
-        if unsafe { libc::kill(parent_pid, 0) == 0 } {
-            std::thread::sleep(std::time::Duration::from_millis(500));
-        }
-
-        // Close inherited Wayland FDs so the new cce-client instance doesn't
-        // confuse River with stale connections.
-        // (close everything except stdin/stdout/stderr)
-        let max_fd = unsafe { libc::sysconf(libc::_SC_OPEN_MAX) } as i32;
-        for fd in 3..max_fd {
-            unsafe {
-                libc::close(fd);
-            }
-        }
-
-        // Reopen standard input to /dev/null to ensure a clean stdin fd
-        let null_fd = unsafe { libc::open(b"/dev/null\0".as_ptr() as *const libc::c_char, libc::O_RDONLY) };
-        if null_fd >= 0 {
-            unsafe {
-                libc::dup2(null_fd, 0);
-                libc::close(null_fd);
-            }
-        }
-
-        // Reopen standard output and error to the log file to prevent EPIPE panics when parent shell/session exits
-        let log_fd = unsafe {
-            libc::open(
-                log_path_cstr.as_ptr(),
-                libc::O_WRONLY | libc::O_CREAT | libc::O_APPEND,
-                0o644,
-            )
-        };
-        if log_fd >= 0 {
-            unsafe {
-                libc::dup2(log_fd, 1);
-                libc::dup2(log_fd, 2);
-                libc::close(log_fd);
-            }
-        }
-
-        // Exec the same binary — replaces this process with a fresh cce-client.
-        // Retry up to 3 times with a short delay — the binary may be temporarily
-        // unavailable if cargo build is replacing it mid-write (atomic rename).
-        use std::io::Write;
-        for attempt in 0..3 {
-            let ret = unsafe {
-                libc::execl(
-                    path_cstr.as_ptr(),
-                    path_cstr.as_ptr(),
-                    std::ptr::null::<i8>(),
-                )
-            };
-            let errno = unsafe { *libc::__errno_location() };
-            if attempt < 2 {
-                let _ = writeln!(
-                    std::io::stderr(),
-                    "wm_restart: execl attempt {} failed (errno={} {}), retrying in 500ms...",
-                    attempt + 1,
-                    errno,
-                    std::io::Error::from_raw_os_error(errno)
-                );
-                std::thread::sleep(std::time::Duration::from_millis(500));
-                let _ = ret; // suppress unused
-            } else {
-                // Final attempt failed — log and exit
-                let _ = writeln!(
-                    std::io::stderr(),
-                    "wm_restart: execl failed after 3 attempts! errno={} ({})",
-                    errno,
-                    std::io::Error::from_raw_os_error(errno)
-                );
-                if let Ok(mut f) = std::fs::OpenOptions::new()
-                    .create(true)
-                    .append(true)
-                    .open(paths::get_death_log_path())
-                {
-                    let _ = writeln!(
-                        f,
-                        "child: execl failed after 3 attempts! errno={} ({}) path={}",
-                        errno,
-                        std::io::Error::from_raw_os_error(errno),
-                        exe_path.display()
-                    );
-                }
-                let _ = ret; // suppress unused
-                std::process::exit(1);
-            }
-        }
-    } else {
-        // ── Parent process ──
-        // Exit immediately so River sees the Wayland connection drop
-        // and tears down the old WM binding.
-        std::process::exit(0);
-    }
-}
-
-/// Reload the configuration file.
-///
-/// This clears mode rules, pending bindings, and seat bindings, then
-/// re-parses the config file.
-///
-/// Ported from C wm_reload().
-pub fn wm_reload(state: &mut WindowManager) {
-    // Unlock mode on all windows so the new rules apply to them
-    for window in &mut state.windows {
-        window.mode_locked = false;
-    }
-
-    // Clear per-tag layout defaults
-    state.tag_layouts = [crate::types::TilingMode::Cascade; crate::types::NUM_TAGS];
-    state.has_tag_layout = [true; crate::types::NUM_TAGS];
-
-    // Mark status for update after reload
-    state.needs_status_update = true;
-    state.tap_config_applied = false;
-    state.startup_spawned = false;
-
-    // Clear mode rules
-    state.mode_rules.clear();
-
-    // Clear pending bindings
-    state.pending_bindings.clear();
-    state.pending_pointer_bindings.clear();
-
-    // Try TOML config first
-    let home = std::env::var("HOME").unwrap_or_default();
-    if !home.is_empty() {
-        let config_path = format!("{}/.config/cce/config.toml", home);
-        if std::path::Path::new(&config_path).exists() {
-            match parse_config(&config_path, false, state) {
-                Ok(_) => {
-                    for cmd in &state.reload_commands {
-                        let _ = std::io::Write::write_fmt(&mut std::io::stderr(), format_args!("[reload] executing reload command: {}\n", cmd));
-                        spawn_command_bg(cmd);
-                    }
-                    if state.notifications_enable {
-                        crate::config::show_notification("cce-client", "Configuration reloaded successfully");
-                    }
-                }
-                Err(e) => {
-                    if state.notifications_enable {
-                        crate::config::show_notification("cce-client", &format!("Config reload failed:\n{}", e));
-                    }
-                }
-            }
-        }
-    }
-}
-
-/// Spawn a command in the background (re-export from config for convenience)
-pub use crate::config::spawn_command_bg;
-
-/// Check if a process with the given name is already running (re-export from config)
-pub use crate::config::process_running;
diff --git a/src/client/run.rs b/src/client/run.rs
deleted file mode 100644
index 2cee73f..0000000
--- a/src/client/run.rs
+++ /dev/null
@@ -1,328 +0,0 @@
-// cce-client — Wayland window manager run loop
-
-use crate::config::parse_config;
-use crate::ipc;
-use crate::ipc_server;
-use crate::restart;
-use crate::status_server;
-use crate::wayland::{wayland_init, AppState};
-use crate::paths;
-use std::env;
-use std::fs;
-use wayland_client::EventQueue;
-
-/// Write a crash/exit trace to /tmp/cce-client-death.log so we can diagnose
-/// why cce-client dies even when the normal log gets overwritten on restart.
-fn log_death(msg: &str) {
-    use std::io::Write;
-    if let Ok(mut f) = std::fs::OpenOptions::new()
-        .create(true)
-        .append(true)
-        .open(paths::get_death_log_path())
-    {
-        let _ = writeln!(f, "{}", msg);
-        let _ = f.sync_all();
-    }
-    let _ = writeln!(std::io::stderr(), "{}", msg);
-}
-
-pub fn run_client() {
-    // Install a panic hook that writes to a separate log file before aborting.
-    std::panic::set_hook(Box::new(|info| {
-        let msg = format!("[PANIC] {}", info);
-        use std::io::Write;
-        if let Ok(mut f) = std::fs::OpenOptions::new()
-            .create(true)
-            .append(true)
-            .open(paths::get_death_log_path())
-        {
-            let _ = writeln!(f, "{}", msg);
-            let _ = f.sync_all();
-        }
-        let _ = writeln!(std::io::stderr(), "{}", msg);
-    }));
-
-    eprintln!("cce-client starting...");
-
-    // Start the status socket server thread (for waybar integration)
-    let status_sender = status_server::spawn_status_server();
-
-    // Ignore SIGPIPE — when the Wayland compositor closes the connection
-    // (protocol error, unresponsive timeout, etc.), writes to the socket
-    // generate SIGPIPE. Without ignoring it, the process is killed before
-    // conn.flush() or blocking_dispatch() can return an error, masking the
-    // real cause of the disconnect.
-    unsafe {
-        nix::libc::signal(nix::libc::SIGPIPE, nix::libc::SIG_IGN);
-    }
-
-    // Set up SIGCHLD handler to reap child processes
-    let sa = nix::sys::signal::SigAction::new(
-        nix::sys::signal::SigHandler::Handler(sigchld_handler),
-        nix::sys::signal::SaFlags::SA_RESTART,
-        nix::sys::signal::SigSet::empty(),
-    );
-    unsafe {
-        nix::sys::signal::sigaction(nix::sys::signal::Signal::SIGCHLD, &sa)
-            .expect("failed to set SIGCHLD handler");
-    }
-
-    // Connect to Wayland display and get initial state.
-    // SIGUSR2 handler: dump backtrace to /tmp/cce-client-bt.txt for debugging busy loops
-    unsafe {
-        nix::sys::signal::sigaction(
-            nix::sys::signal::SIGUSR2,
-            &nix::sys::signal::SigAction::new(
-                nix::sys::signal::SigHandler::Handler(dump_backtrace),
-                nix::sys::signal::SaFlags::empty(),
-                nix::sys::signal::SigSet::empty(),
-            ),
-        )
-        .expect("failed to set SIGUSR2 handler");
-    }
-
-    let (_conn, mut event_queue, mut state) = match wayland_init() {
-        Ok(c) => c,
-        Err(e) => {
-            log_death(&format!("fatal: {}", e));
-            std::process::exit(1);
-        }
-    };
-
-    // Create a pipe to wake up the main loop when IPC commands arrive
-    let mut pipe_fds = [0; 2];
-    unsafe {
-        libc::pipe(pipe_fds.as_mut_ptr());
-    }
-    let pipe_read = pipe_fds[0];
-    let pipe_write = pipe_fds[1];
-
-    // Start the IPC server thread (for clearctl and cce-system-interface)
-    let ipc_server = ipc_server::spawn_ipc_server(pipe_write);
-    let ipc_rx = ipc_server.rx;
-    let ipc_tx = ipc_server.tx;
-
-    // Setup channel for configuration updates:
-    let (config_tx, config_rx) = tokio::sync::mpsc::unbounded_channel::<crate::input::InputDaemonMsg>();
-    state.wm.input_controller = Some(config_tx);
-
-    // Spawn the input subsystem background thread:
-    let pipe_write_clone = pipe_write;
-    let ipc_tx_clone = ipc_tx.clone();
-    std::thread::Builder::new()
-        .name("cce-client-input-subsystem".into())
-        .spawn(move || {
-            if let Err(e) = crate::input::run_input_daemon(config_rx, ipc_tx_clone, pipe_write_clone) {
-                eprintln!("[input-subsystem] Fatal error: {:?}", e);
-            }
-        })
-        .expect("failed to spawn input subsystem thread");
-
-    // Store the status sender in the app state so RenderStart can push updates
-    state.status_sender = Some(status_sender);
-
-    // Check if this is a restart
-    let cold_start = if env::var("CCE_CLIENT_RESTARTING").as_deref() == Ok("1") {
-        env::remove_var("CCE_CLIENT_RESTARTING");
-        false
-    } else {
-        true
-    };
-
-    // Don't remove WAYLAND_DEBUG — we need protocol debug logging to diagnose crashes
-    // env::remove_var("WAYLAND_DEBUG");
-
-    // Load config immediately, before entering the event loop.
-    // This ensures bindings are registered before any render_start arrives.
-    // Following the tinyrwm pattern: do setup, then simple blocking_dispatch loop.
-    eprintln!(
-        "[init] about to load config, render_count={}",
-        state.render_count
-    );
-    let config_start = std::time::Instant::now();
-    if let Ok(home) = env::var("HOME") {
-        let config_path = format!("{}/.config/cce/config.toml", home);
-        if fs::metadata(&config_path).is_ok() {
-            if let Err(e) = parse_config(&config_path, cold_start, &mut state.wm) {
-                eprintln!("[init] failed to load config: {}", e);
-            }
-        }
-    }
-    eprintln!(
-        "[init] config loaded in {:?}, render_count={}",
-        config_start.elapsed(),
-        state.render_count
-    );
-
-    // Apply output scale immediately if heads were discovered during init roundtrips.
-    if state.wm.pending_scale_apply && state.wm.output_scale > 0.0 && !state.output_heads.is_empty()
-    {
-        let qh = event_queue.handle();
-        crate::wayland::apply_output_scale(&mut state, &qh);
-    }
-
-    // Flush any queued requests from config loading (bindings, etc.)
-    eprintln!("[init] flushed, entering main loop");
-
-    // Main loop — using poll to block on both Wayland socket and IPC wake-up pipe.
-    // All work (including spawning) happens inside Dispatch callbacks.
-    let mut loop_count: u64 = 0;
-    loop {
-        loop_count += 1;
-        if loop_count % 10000 == 0 {
-            eprintln!("[main] loop iteration {}", loop_count);
-        }
-
-        // 1. Dispatch any already pending events in the queue
-        let _ = event_queue.dispatch_pending(&mut state);
-
-        // 2. Flush outgoing requests to the compositor
-        if let Err(e) = event_queue.flush() {
-            log_death(&format!("flush error: {:?}", e));
-            let _ = std::fs::copy(paths::get_log_path(), paths::get_prev_log_path());
-            if !state.wm.exit_requested {
-                restart::wm_restart();
-            }
-            break;
-        }
-
-        // 3. Prepare to read Wayland events
-        let read_guard = match event_queue.prepare_read() {
-            Some(g) => g,
-            None => {
-                // If None, events are already in the queue, dispatch them immediately
-                let _ = event_queue.dispatch_pending(&mut state);
-                continue;
-            }
-        };
-
-        // 4. Poll both the Wayland display FD and the IPC wake-up pipe
-        use std::os::fd::{AsFd, AsRawFd};
-        let wl_fd = event_queue.as_fd().as_raw_fd();
-        let mut poll_fds = [
-            nix::poll::PollFd::new(unsafe { std::os::fd::BorrowedFd::borrow_raw(wl_fd) }, nix::poll::PollFlags::POLLIN),
-            nix::poll::PollFd::new(unsafe { std::os::fd::BorrowedFd::borrow_raw(pipe_read) }, nix::poll::PollFlags::POLLIN),
-        ];
-
-        let timeout = if state.wm.animating {
-            let elapsed = state.wm.last_animation_tick.elapsed();
-            let target = std::time::Duration::from_millis(32);
-            let timeout_duration = if elapsed >= target {
-                std::time::Duration::ZERO
-            } else {
-                target - elapsed
-            };
-            nix::poll::PollTimeout::try_from(timeout_duration).unwrap()
-        } else {
-            nix::poll::PollTimeout::NONE
-        };
-
-        match nix::poll::poll(&mut poll_fds, timeout) {
-            Ok(num_events) => {
-                if num_events > 0 {
-                    // If Wayland FD is readable, read the events
-                    if poll_fds[0].revents().unwrap_or(nix::poll::PollFlags::empty()).contains(nix::poll::PollFlags::POLLIN) {
-                        let _ = read_guard.read();
-                    } else {
-                        // Otherwise drop the read guard to release the lock
-                        std::mem::drop(read_guard);
-                    }
-
-                    // If IPC pipe is readable, drain it
-                    if poll_fds[1].revents().unwrap_or(nix::poll::PollFlags::empty()).contains(nix::poll::PollFlags::POLLIN) {
-                        let mut buf = [0u8; 128];
-                        unsafe {
-                            libc::read(pipe_read, buf.as_mut_ptr() as *mut libc::c_void, buf.len());
-                        }
-                    }
-                } else {
-                    // Timeout (num_events == 0) — drop the read guard to release the lock
-                    std::mem::drop(read_guard);
-                }
-            }
-            Err(e) => {
-                std::mem::drop(read_guard);
-                if e != nix::errno::Errno::EINTR {
-                    log_death(&format!("poll error: {:?}", e));
-                    break;
-                }
-            }
-        }
-
-        // 5. Dispatch read events
-        let _ = event_queue.dispatch_pending(&mut state);
-
-        // 6. If animating and frame budget elapsed, request next frame (fallback only)
-        if state.wm.animating && state.wm.last_animation_tick.elapsed() >= std::time::Duration::from_millis(32) {
-            state.manage_dirty();
-        }
-
-        // 6. Process pending IPC commands from the socket channel
-        let mut ipc_commands = false;
-        loop {
-            match ipc_rx.try_recv() {
-                Ok(req) => {
-                    eprintln!("[main] IPC command: {}", req.command);
-                    let reply = ipc::handle_ipc_command(&req.command, &mut state.wm);
-                    let _ = req.reply_tx.send(reply);
-                    ipc_commands = true;
-                }
-                Err(std::sync::mpsc::TryRecvError::Empty) => break,
-                Err(std::sync::mpsc::TryRecvError::Disconnected) => {
-                    eprintln!("[main] IPC server disconnected");
-                    break;
-                }
-            }
-        }
-        // Force a render sequence if we processed IPC commands
-        if ipc_commands {
-            state.manage_dirty();
-            if !state.wm.tap_config_applied && !state.libinput_devices.is_empty() {
-                let qh = event_queue.handle();
-                crate::wayland::apply_input_config(&mut state, &qh);
-            }
-        }
-
-        if state.exit_requested || state.wm.exit_requested {
-            log_death(&format!(
-                "main loop exit: exit_requested={} wm.exit_requested={}",
-                state.exit_requested, state.wm.exit_requested
-            ));
-            break;
-        }
-    }
-    let _ = fs::remove_file(paths::get_socket_path());
-    if !state.wm.exit_requested {
-        // Save log before restart overwrites it
-        let _ = std::fs::copy(paths::get_log_path(), paths::get_prev_log_path());
-        restart::wm_restart();
-    }
-    log_death("main loop exited cleanly");
-}
-
-extern "C" fn sigchld_handler(_sig: nix::libc::c_int) {
-    use nix::sys::wait::WaitStatus;
-    while let Ok(status) = nix::sys::wait::waitpid(
-        nix::unistd::Pid::from_raw(-1),
-        Some(nix::sys::wait::WaitPidFlag::WNOHANG),
-    ) {
-        match status {
-            WaitStatus::StillAlive => break, // no more children to reap
-            _ => continue,                   // reaped a child, check for more
-        }
-    }
-}
-
-extern "C" fn dump_backtrace(_sig: nix::libc::c_int) {
-    use std::io::Write;
-    let bt = std::backtrace::Backtrace::capture();
-    if let Ok(mut f) = std::fs::OpenOptions::new()
-        .create(true)
-        .write(true)
-        .truncate(true)
-        .open(paths::get_bt_path())
-    {
-        let _ = writeln!(f, "SIGUSR2 backtrace:\n{:?}", bt);
-        let _ = f.sync_all();
-    }
-}
diff --git a/src/client/state.rs b/src/client/state.rs
deleted file mode 100644
index 94e52c2..0000000
--- a/src/client/state.rs
+++ /dev/null
@@ -1,475 +0,0 @@
-// Persistent state file for cce-client restart recovery.
-//
-// Writes window tag assignments and global tag/layout state to
-// ~/.cache/cce_client_state so that it survives restarts. On startup,
-// the state file is read and applied to re-advertised windows
-// matched by their River identifier (stable across WM restarts)
-// or app_id+title as a fallback.
-//
-// What is persisted:
-//   - active_tags bitmask
-//   - Per-tag layout overrides (tag_layouts + has_tag_layout)
-//   - Per-window: tags, tiling_mode (only if mode_locked), identifier, app_id, title
-//
-// What is NOT persisted:
-//   - Focus (seat.focused_window_id)
-//   - Window positions/dimensions (recomputed on startup)
-//   - Transient flags (is_new, needs_*, etc.)
-
-use crate::types::{TilingMode, WindowManager, NUM_TAGS};
-use std::fs;
-use std::io::{BufRead, Write};
-use std::path::PathBuf;
-
-/// Get the state file path: ~/.cache/cce_client_state
-fn state_file_path() -> PathBuf {
-    if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") {
-        let mut path = PathBuf::from(runtime_dir);
-        path.push("cce_client_state");
-        path
-    } else {
-        let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
-        let mut path = PathBuf::from(home);
-        path.push(".cache");
-        path.push("cce_client_state");
-        path
-    }
-}
-
-/// Persistent state snapshot (plain-text, line-oriented).
-///
-/// Format:
-///   active_tags=<u32>
-///   tag_layout=<tag_index> <mode_str> <has_layout_bool>
-///   window <identifier> <app_id> <title> <tags> <mode_str> <mode_locked>
-///
-/// Fields are tab-separated. App_id and title use URL-style percent-encoding
-/// for spaces, tabs, and newlines so they never break the line format.
-pub struct PersistentState {
-    pub active_tags: u32,
-    pub tag_layouts: Vec<(usize, TilingMode, bool)>,
-    pub windows: Vec<PersistentWindow>,
-}
-
-pub struct PersistentWindow {
-    pub identifier: Option<String>,
-    pub app_id: Option<String>,
-    pub title: Option<String>,
-    pub tags: u32,
-    pub tiling_mode: TilingMode,
-    pub mode_locked: bool,
-    pub minimized: bool,
-}
-
-/// Percent-encode spaces, tabs, newlines, and percent signs in a string.
-fn pct_encode(s: &str) -> String {
-    let mut out = String::with_capacity(s.len());
-    for c in s.chars() {
-        match c {
-            ' ' => out.push_str("%20"),
-            '\t' => out.push_str("%09"),
-            '\n' => out.push_str("%0A"),
-            '\r' => out.push_str("%0D"),
-            '%' => out.push_str("%25"),
-            _ => out.push(c),
-        }
-    }
-    out
-}
-
-/// Decode a percent-encoded string.
-fn pct_decode(s: &str) -> String {
-    let mut out = String::with_capacity(s.len());
-    let mut chars = s.chars();
-    while let Some(c) = chars.next() {
-        if c == '%' {
-            let hex: String = chars.by_ref().take(2).collect();
-            if let Ok(byte) = u8::from_str_radix(&hex, 16) {
-                out.push(byte as char);
-            } else {
-                out.push('%');
-                out.push_str(&hex);
-            }
-        } else {
-            out.push(c);
-        }
-    }
-    out
-}
-
-/// Write the current WM state to the state file.
-/// Safe to call inside Dispatch callbacks — just file I/O, no fork.
-pub fn write_state(wm: &WindowManager) {
-    let path = state_file_path();
-
-    // Ensure ~/.cache/ exists
-    if let Some(parent) = path.parent() {
-        let _ = fs::create_dir_all(parent);
-    }
-
-    if let Ok(mut f) = fs::File::create(&path) {
-        let _ = writeln!(f, "active_tags={}", wm.active_tags);
-
-        for tag_bit in 0..NUM_TAGS {
-            let mode_str = wm.tag_layouts[tag_bit].as_str();
-            let has = wm.has_tag_layout[tag_bit];
-            let _ = writeln!(f, "tag_layout\t{}\t{}\t{}", tag_bit, mode_str, has);
-        }
-
-        for win in &wm.windows {
-            if win.closed {
-                continue;
-            }
-            let ident = win
-                .identifier
-                .as_deref()
-                .map(pct_encode)
-                .unwrap_or_else(|| "-".to_string());
-            let app_id = win
-                .app_id
-                .as_deref()
-                .map(pct_encode)
-                .unwrap_or_else(|| "-".to_string());
-            let title = win
-                .title
-                .as_deref()
-                .map(pct_encode)
-                .unwrap_or_else(|| "-".to_string());
-            let mode_str = win.tiling_mode.as_str();
-            let _ = writeln!(
-                f,
-                "window\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
-                ident, app_id, title, win.tags, mode_str, win.mode_locked, win.minimized
-            );
-        }
-
-        let _ = f.sync_all();
-    }
-}
-
-/// Read the persisted state from disk. Returns None if the file doesn't
-/// exist or can't be parsed.
-pub fn read_state() -> Option<PersistentState> {
-    let path = state_file_path();
-    let file = fs::File::open(&path).ok()?;
-    let reader = std::io::BufReader::new(file);
-
-    let mut active_tags: Option<u32> = None;
-    let mut tag_layouts = Vec::new();
-    let mut windows = Vec::new();
-
-    for line in reader.lines() {
-        let line = line.ok()?;
-        let line = line.trim();
-        if line.is_empty() || line.starts_with('#') {
-            continue;
-        }
-
-        if let Some(rest) = line.strip_prefix("active_tags=") {
-            active_tags = rest.parse::<u32>().ok();
-            continue;
-        }
-
-        if let Some(rest) = line.strip_prefix("tag_layout") {
-            let rest = rest.trim_start();
-            let parts: Vec<&str> = rest.split('\t').collect();
-            if parts.len() >= 3 {
-                if let Ok(idx) = parts[0].parse::<usize>() {
-                    let mode = parse_tiling_mode_str(parts[1]);
-                    let has = parts[2] == "true";
-                    tag_layouts.push((idx, mode, has));
-                }
-            }
-            continue;
-        }
-
-        if let Some(rest) = line.strip_prefix("window") {
-            let rest = rest.trim_start();
-            let parts: Vec<&str> = rest.split('\t').collect();
-            if parts.len() >= 6 {
-                let identifier = if parts[0] == "-" {
-                    None
-                } else {
-                    Some(pct_decode(parts[0]))
-                };
-                let app_id = if parts[1] == "-" {
-                    None
-                } else {
-                    Some(pct_decode(parts[1]))
-                };
-                let title = if parts[2] == "-" {
-                    None
-                } else {
-                    Some(pct_decode(parts[2]))
-                };
-                let tags = parts[3].parse::<u32>().unwrap_or(1);
-                let tiling_mode = parse_tiling_mode_str(parts[4]);
-                let mode_locked = parts[5] == "true";
-                let minimized = parts.get(6).map(|&s| s == "true").unwrap_or(false);
-
-                windows.push(PersistentWindow {
-                    identifier,
-                    app_id,
-                    title,
-                    tags,
-                    tiling_mode,
-                    mode_locked,
-                    minimized,
-                });
-            }
-            continue;
-        }
-    }
-
-    Some(PersistentState {
-        active_tags: active_tags.unwrap_or(1),
-        tag_layouts,
-        windows,
-    })
-}
-
-/// Parse a tiling mode string (case-insensitive).
-fn parse_tiling_mode_str(s: &str) -> TilingMode {
-    match s {
-        "Cascade" => TilingMode::Cascade,
-        "Grid" => TilingMode::Grid,
-        "Fullscreen" => TilingMode::Fullscreen,
-        "Floating" => TilingMode::Floating,
-        "Popup" => TilingMode::Popup,
-        "Side Panel" | "SidePanel" | "side-panel" => TilingMode::SidePanel,
-        _ => TilingMode::Cascade,
-    }
-}
-
-/// Apply persisted state to the window manager.
-/// Called on startup after config is loaded but before the main loop.
-///
-/// - Restores active_tags (so the correct tag view is shown).
-/// - Restores per-tag layout overrides.
-/// - For each live window, looks up its persisted entry by identifier
-///   (preferred) or app_id+title (fallback) and restores:
-///     - tags (which tag the window is on)
-///     - tiling_mode + mode_locked (if the mode was user-set)
-pub fn apply_state(wm: &mut WindowManager, state: &PersistentState) {
-    // Restore active_tags
-    wm.active_tags = state.active_tags;
-
-    // Restore per-tag layouts
-    for (idx, mode, has) in &state.tag_layouts {
-        if *idx < NUM_TAGS {
-            wm.tag_layouts[*idx] = *mode;
-            wm.has_tag_layout[*idx] = *has;
-        }
-    }
-
-    // Apply per-window state from persisted entries.
-    // Build a lookup: identifier -> PersistentWindow, and (app_id, title) -> PersistentWindow.
-    // Identifier is the primary key (stable across restarts).
-    // App_id+title is a fallback for windows without identifiers.
-    let mut by_identifier: Vec<&PersistentWindow> = Vec::new();
-    let mut by_app_id_title: Vec<&PersistentWindow> = Vec::new();
-
-    for pw in &state.windows {
-        if pw.identifier.is_some() {
-            by_identifier.push(pw);
-        }
-        if pw.app_id.is_some() {
-            by_app_id_title.push(pw);
-        }
-    }
-
-    for win in &mut wm.windows {
-        if win.closed {
-            continue;
-        }
-
-        // Try identifier match first
-        let matched = if let Some(ref ident) = win.identifier {
-            by_identifier.iter().find(|pw| {
-                pw.identifier.as_deref() == Some(ident.as_str())
-            })
-        } else {
-            None
-        };
-
-        // Fallback: app_id + title match
-        let matched = matched.or_else(|| {
-            if let Some(ref aid) = win.app_id {
-                by_app_id_title.iter().find(|pw| {
-                    pw.app_id.as_deref() == Some(aid.as_str())
-                        && pw.title == win.title
-                })
-            } else {
-                None
-            }
-        });
-
-        if let Some(pw) = matched {
-            // Restore tag assignment
-            win.tags = pw.tags;
-
-            // Restore locked tiling mode (user explicitly set this)
-            if pw.mode_locked {
-                win.tiling_mode = pw.tiling_mode;
-                win.mode_locked = true;
-            }
-            win.minimized = pw.minimized;
-        }
-
-        // Post-restore normalization: Ensure blank steam_proton helper windows are untagged (tags = 0)
-        let is_proton = win.app_id.as_deref() == Some("steam_proton");
-        let is_blank = win.title.is_none() || win.title.as_deref().map_or(true, |t| t.is_empty());
-        if is_proton && is_blank {
-            win.tags = 0;
-        }
-    }
-
-    eprintln!(
-        "[state] restored: active_tags={}, tag_layouts={:?}, window_entries={}",
-        state.active_tags,
-        state
-            .tag_layouts
-            .iter()
-            .filter(|(_, _, has)| *has)
-            .count(),
-        state.windows.len()
-    );
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_pct_encode_decode_roundtrip() {
-        let cases = vec![
-            "",
-            "hello",
-            "hello world",
-            "foo\tbar\nbaz",
-            "100%",
-            "org.qutebrowser.qutebrowser",
-            "zed-industries/awesome-gpui: Awesome projects!",
-        ];
-        for case in cases {
-            assert_eq!(pct_decode(&pct_encode(case)), case);
-        }
-    }
-
-    #[test]
-    fn test_write_read_roundtrip() {
-        let dir = std::env::temp_dir().join("cce_client_state_test");
-        let _ = fs::create_dir_all(&dir);
-        let path = dir.join("cce_client_state");
-
-        let mut wm = WindowManager::default();
-        wm.active_tags = 0b1010; // tags 2 and 4
-        wm.tag_layouts[1] = TilingMode::Grid;
-        wm.has_tag_layout[1] = true;
-
-        // Add a window
-        let mut win = crate::types::Window::default();
-        win.id = 42;
-        win.identifier = Some("river-window-123".to_string());
-        win.app_id = Some("org.qutebrowser.qutebrowser".to_string());
-        win.title = Some("Test Page - qutebrowser".to_string());
-        win.tags = 0b100; // tag 3
-        win.tiling_mode = TilingMode::Fullscreen;
-        win.mode_locked = true;
-        wm.windows.push(win);
-
-        // Write
-        if let Some(parent) = path.parent() {
-            let _ = fs::create_dir_all(parent);
-        }
-        if let Ok(mut f) = fs::File::create(&path) {
-            // Manually write using the same format as write_state
-            let _ = writeln!(f, "active_tags={}", wm.active_tags);
-            for tag_bit in 0..NUM_TAGS {
-                let _ = writeln!(
-                    f,
-                    "tag_layout\t{}\t{}\t{}",
-                    tag_bit,
-                    wm.tag_layouts[tag_bit].as_str(),
-                    wm.has_tag_layout[tag_bit]
-                );
-            }
-            for w in &wm.windows {
-                let ident = w.identifier.as_deref().map(pct_encode).unwrap_or("-".to_string());
-                let app_id = w.app_id.as_deref().map(pct_encode).unwrap_or("-".to_string());
-                let title = w.title.as_deref().map(pct_encode).unwrap_or("-".to_string());
-                let _ = writeln!(
-                    f,
-                    "window\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
-                    ident, app_id, title, w.tags, w.tiling_mode.as_str(), w.mode_locked, w.minimized
-                );
-            }
-        }
-
-        // Read and verify via the public read_state() path.
-        // We set HOME to our temp dir so state_file_path() finds the test file.
-        // Instead, use a direct parse approach:
-        let file = fs::File::open(&path).unwrap();
-        let reader = std::io::BufReader::new(file);
-        let mut active_tags: Option<u32> = None;
-        let mut tag_layouts = Vec::new();
-        let mut windows = Vec::new();
-        for line_r in reader.lines() {
-            let line = line_r.unwrap();
-            let line = line.trim();
-            if line.is_empty() { continue; }
-            if let Some(rest) = line.strip_prefix("active_tags=") {
-                active_tags = rest.parse::<u32>().ok();
-            } else if let Some(rest) = line.strip_prefix("tag_layout") {
-                let rest = rest.trim_start();
-                let parts: Vec<&str> = rest.split('\t').collect();
-                if parts.len() >= 3 {
-                    if let Ok(idx) = parts[0].parse::<usize>() {
-                        let mode = parse_tiling_mode_str(parts[1]);
-                        let has = parts[2] == "true";
-                        tag_layouts.push((idx, mode, has));
-                    }
-                }
-            } else if let Some(rest) = line.strip_prefix("window") {
-                let rest = rest.trim_start();
-                let parts: Vec<&str> = rest.split('\t').collect();
-                if parts.len() >= 6 {
-                    let identifier = if parts[0] == "-" { None } else { Some(pct_decode(parts[0])) };
-                    let app_id = if parts[1] == "-" { None } else { Some(pct_decode(parts[1])) };
-                    let title = if parts[2] == "-" { None } else { Some(pct_decode(parts[2])) };
-                    let tags = parts[3].parse::<u32>().unwrap_or(1);
-                    let tiling_mode = parse_tiling_mode_str(parts[4]);
-                    let mode_locked = parts[5] == "true";
-                    let minimized = parts.get(6).map(|&s| s == "true").unwrap_or(false);
-                    windows.push(PersistentWindow { identifier, app_id, title, tags, tiling_mode, mode_locked, minimized });
-                }
-            }
-        }
-        let state = PersistentState {
-            active_tags: active_tags.unwrap_or(1),
-            tag_layouts,
-            windows,
-        };
-        assert_eq!(state.active_tags, 0b1010);
-        assert_eq!(state.tag_layouts.len(), NUM_TAGS);
-        // Tag layout at index 1 should be Grid + has=true
-        let tl1 = state.tag_layouts.iter().find(|(idx, _, _)| *idx == 1);
-        assert!(tl1.is_some());
-        let (_, mode, has) = tl1.unwrap();
-        assert_eq!(*mode, TilingMode::Grid);
-        assert!(has);
-
-        assert_eq!(state.windows.len(), 1);
-        let pw = &state.windows[0];
-        assert_eq!(pw.identifier.as_deref(), Some("river-window-123"));
-        assert_eq!(pw.app_id.as_deref(), Some("org.qutebrowser.qutebrowser"));
-        assert_eq!(pw.title.as_deref(), Some("Test Page - qutebrowser"));
-        assert_eq!(pw.tags, 0b100);
-        assert_eq!(pw.tiling_mode, TilingMode::Fullscreen);
-        assert!(pw.mode_locked);
-        assert!(!pw.minimized);
-
-        // Clean up
-        let _ = fs::remove_file(&path);
-    }
-}
diff --git a/src/client/status.rs b/src/client/status.rs
deleted file mode 100644
index df2fdc8..0000000
--- a/src/client/status.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-// Status file writing and waybar signaling
-
-use crate::types::{TilingMode, WindowManager};
-use std::fs;
-use std::process::Command;
-
-pub const NUM_TAGS: u32 = 4;
-
-/// Write status files only (no pkill signaling). Safe to call inside
-/// Dispatch callbacks — no fork, no blocking, just file I/O.
-pub fn write_status_files(state: &WindowManager) {
-    use crate::paths;
-    use std::io::Write;
-
-    // /tmp/cce-client-tags: active_tags focused_tags num_tags
-    if let Ok(mut f) = fs::File::create(paths::get_tags_path()) {
-        let _ = writeln!(
-            f,
-            "{} {} {}",
-            state.active_tags, state.focused_tags, NUM_TAGS
-        );
-    }
-
-    // /tmp/cce-client-layout: focused window's tiling mode
-    let mode_str = if state.expose_active {
-        "Expose"
-    } else {
-        state
-            .focused_window()
-            .map(|w| tiling_mode_str(w.tiling_mode))
-            .unwrap_or("none")
-    };
-
-    if let Ok(mut f) = fs::File::create(paths::get_layout_path()) {
-        let _ = writeln!(f, "{}", mode_str);
-    }
-
-    // /tmp/cce-client-windows: one line per window
-    if let Ok(mut f) = fs::File::create(paths::get_windows_path()) {
-        let focused_title = state.focused_window().map(|w| w.title.clone());
-
-        for win in &state.windows {
-            let mode_str = tiling_mode_str(win.tiling_mode);
-            let decoration_str = match win.decoration_hint {
-                0 => "only_csd",
-                1 => "prefers_csd",
-                2 => "prefers_ssd",
-                3 => "no_preference",
-                _ => "unknown",
-            };
-            let presentation_str = match win.presentation_hint {
-                0 => "vsync",
-                1 => "async",
-                _ => "unknown",
-            };
-            let _ = writeln!(
-                f,
-                "window app_id={} title={} mode={} decoration={} presentation={} tags={} x={} y={} w={} h={} has_parent={} minimized={}",
-                win.app_id.as_deref().unwrap_or("(null)"),
-                win.title.as_deref().unwrap_or("(null)"),
-                mode_str,
-                decoration_str,
-                presentation_str,
-                win.tags, win.x, win.y, win.width, win.height,
-                win.has_parent,
-                win.minimized,
-            );
-        }
-
-        // /tmp/cce-client-title
-        if let Some(title) = focused_title {
-            if let Ok(mut tf) = fs::File::create(paths::get_title_path()) {
-                let _ = writeln!(tf, "{}", title.as_deref().unwrap_or("(null)"));
-            }
-        }
-    }
-}
-
-/// Write all status files and signal waybar.
-/// WARNING: The pkill calls block the event loop. Do NOT call this
-/// inside a Dispatch callback. Use write_status_files() instead.
-pub fn update_status_files(state: &WindowManager) {
-    write_status_files(state);
-
-    // Signal waybar
-    let _ = Command::new("pkill").args(["-RTMIN+8", "waybar"]).output();
-    let _ = Command::new("pkill").args(["-RTMIN+9", "waybar"]).output();
-    let _ = Command::new("pkill").args(["-RTMIN+10", "waybar"]).output();
-}
-
-fn tiling_mode_str(mode: TilingMode) -> &'static str {
-    match mode {
-        TilingMode::Floating => "Floating",
-        TilingMode::Cascade => "Cascade",
-        TilingMode::Grid => "Grid",
-        TilingMode::Fullscreen => "Fullscreen",
-        TilingMode::Popup => "Popup",
-        TilingMode::SidePanel => "Side Panel",
-    }
-}
diff --git a/src/client/tiling.rs b/src/client/tiling.rs
deleted file mode 100644
index 52ecc93..0000000
--- a/src/client/tiling.rs
+++ /dev/null
@@ -1,311 +0,0 @@
-// Tiling formulas ported from cce-client.c
-
-/// 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;
-
-/// Cascade base green (focused window) in fixed-point format
-/// These come from the layout.border_r/g/b values, not hardcoded constants.
-/// The C code uses #5c9060 as the default cascade base but it's actually
-/// read from config. We just define the factor here.
-
-/// Tile a window in cascade mode.
-///
-/// Returns (x, y, width, height) for the window at the given cascade index.
-///
-/// Screen edges use per-side gaps; inter-window offset is cascade_offset.
-///   width  = screen_w - gap_left - gap_right - bw*2 - cascade_offset*(n_cascade - 1)
-///   height = screen_h - gap_top - gap_bottom - bw*2 - cascade_offset*(n_cascade - 1)
-///   x = gap_left + bw + idx * cascade_offset
-///   y = bar_height + gap_top + bw + idx * cascade_offset
-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,
-    cascade_offset: i32,
-    bar_height: i32,
-    n_cascade: i32,
-    idx: i32,
-) -> (i32, i32, i32, i32) {
-    let dec_h = std::cmp::max(bw, 16);
-    let width = screen_w - gap_left - gap_right - bw * 2 - cascade_offset * (n_cascade - 1);
-    let height = screen_h - bar_height - gap_top - gap_bottom - (dec_h + bw) - cascade_offset * (n_cascade - 1);
-    let width = if width < 1 { 1 } else { width };
-    let height = if height < 1 { 1 } else { height };
-    let pos_idx = n_cascade - 1 - idx;
-    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.
-///
-/// Uses a 2-column grid layout.
-///
-/// Screen edges use per-side gaps; inter-window spacing uses `gap`.
-///   cols = 2
-///   rows = (n_grid + cols - 1) / cols
-///   width  = (screen_w - gap_left - gap_right - (cols - 1) * gap) / cols - 2 * bw
-///   height = (screen_h - gap_top - gap_bottom - (rows - 1) * gap) / rows - (dec_h + bw)
-///   x = gap_left + bw + col * (width + 2 * bw + gap)
-///   y = bar_height + gap_top + dec_h + row * (height + (dec_h + bw) + gap)
-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,
-    bar_height: i32,
-    n_grid: i32,
-    idx: i32,
-) -> (i32, i32, i32, i32) {
-    let cols = 2i32;
-    let row = idx / cols;
-    let col = idx % cols;
-    let rows = (n_grid + cols - 1) / cols;
-    let dec_h = std::cmp::max(bw, 16);
-    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 — fills the screen minus gaps, bar, and borders.
-///
-/// Only the focused window is visible; other fullscreen windows are skipped.
-/// A fullscreen window occupies the entire screen (0, 0, screen_w, screen_h).
-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.
-///
-/// depth 0 returns the base color unchanged; each step darkens by factor.
-/// Returns byte-replicated 32-bit value for River's color format.
-///
-/// Ported from C: interp_channel()
-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
-}
-
-/// Compute a "#RRGGBB" hex color string for a given cascade depth.
-///
-/// Takes the base border color channels in byte-replicated format and
-/// interpolates them toward black by CASCADE_DEPTH_FACTOR^depth.
-///
-/// Ported from C: cascade_hex_color()
-pub fn cascade_hex_color(r: u32, g: u32, b: u32, depth: i32) -> String {
-    let ri = interp_channel(r, CASCADE_DEPTH_FACTOR, depth);
-    let gi = interp_channel(g, CASCADE_DEPTH_FACTOR, depth);
-    let bi = interp_channel(b, CASCADE_DEPTH_FACTOR, depth);
-    // Extract the low byte for display
-    let rv = ri & 0xFF;
-    let gv = gi & 0xFF;
-    let bv = bi & 0xFF;
-    format!("#{:02x}{:02x}{:02x}", rv, gv, bv)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_tile_cascade_single() {
-        // Single cascade window should fill the screen minus gaps/borders
-        let (x, y, w, h) = tile_cascade(1920, 1080, 18, 18, 18, 18, 18, 18, 32, 28, 1, 0);
-        assert_eq!(x, 36); // gap_left + bw
-        assert_eq!(y, 64); // bar_height + gap_top + bw
-                           // w = 1920 - 18 - 18 - 18*2 - 32*0 = 1920 - 72 = 1848
-        assert_eq!(w, 1848);
-        // h = 1080 - 28 (bar) - 18 - 18 - 18*2 - 32*0 = 1080 - 100 = 980
-        assert_eq!(h, 980);
-    }
-
-    #[test]
-    fn test_tile_cascade_multiple() {
-        // 3 cascade windows, idx=2 (the back one, highest index, should be up/left)
-        let (x, y, _w, _h) = tile_cascade(1920, 1080, 18, 18, 18, 18, 18, 18, 32, 28, 3, 2);
-        assert_eq!(x, 36); // gap_left + bw
-        assert_eq!(y, 64); // bar + gap_top + bw
-
-        // 3 cascade windows, idx=0 (the focused one, lowest index, should be down/right)
-        let (x, y, _w, _h) = tile_cascade(1920, 1080, 18, 18, 18, 18, 18, 18, 32, 28, 3, 0);
-        assert_eq!(x, 36 + 2 * 32); // gap_left + bw + 2 * offset
-        assert_eq!(y, 64 + 2 * 32); // bar + gap_top + bw + 2 * offset
-    }
-
-    #[test]
-    fn test_tile_cascade_minimum_size() {
-        // Very small screen, many windows — should clamp to 1
-        let (_, _, w, h) = tile_cascade(100, 100, 18, 18, 18, 18, 18, 18, 32, 28, 10, 5);
-        assert!(w >= 1);
-        assert!(h >= 1);
-    }
-
-    #[test]
-    fn test_tile_cascade_asymmetric_gaps() {
-        // Asymmetric screen gaps: top=10, left=20, right=30, bottom=40
-        let (x, y, w, h) = tile_cascade(1920, 1080, 12, 10, 20, 30, 40, 6, 24, 28, 1, 0);
-        assert_eq!(x, 20 + 6);        // gap_left + bw = 26
-        assert_eq!(y, 28 + 10 + 16);  // bar_height + gap_top + dec_h = 54
-                                       // w = 1920 - 20 - 30 - 6*2 = 1920 - 62 = 1858
-        assert_eq!(w, 1858);
-        // h = 1080 - 28 (bar) - 10 - 40 - (16 + 6) = 1080 - 100 = 980
-        assert_eq!(h, 980);
-    }
-
-    #[test]
-    fn test_tile_grid_two_windows() {
-        let (x0, y0, w0, h0) = tile_grid(1920, 1080, 18, 18, 18, 18, 18, 18, 28, 2, 0);
-        let (x1, y1, w1, h1) = tile_grid(1920, 1080, 18, 18, 18, 18, 18, 18, 28, 2, 1);
-
-        // Both windows should have same dimensions
-        assert_eq!(w0, w1);
-        assert_eq!(h0, h1);
-
-        // Window 1 is to the right of window 0
-        assert!(x1 > x0);
-        assert_eq!(y0, y1); // Same row
-
-        // Width = (1920 - 18 - 18 - 1*18) / 2 - 2*18 = (1920-54)/2 - 36 = 933 - 36 = 897
-        assert_eq!(w0, 897);
-        // rows=1, Height = (1080 - 28 (bar) - 18 - 18 - 0*18) / 1 - 2*18 = 1016 - 36 = 980
-        assert_eq!(h0, 980);
-    }
-
-    #[test]
-    fn test_tile_grid_four_windows() {
-        // 4 windows in 2x2 grid
-        let (x0, y0, _, _) = tile_grid(1920, 1080, 18, 18, 18, 18, 18, 18, 28, 4, 0);
-        let (x1, y1, _, _) = tile_grid(1920, 1080, 18, 18, 18, 18, 18, 18, 28, 4, 1);
-        let (x2, y2, _, _) = tile_grid(1920, 1080, 18, 18, 18, 18, 18, 18, 28, 4, 2);
-        let (x3, y3, _, _) = tile_grid(1920, 1080, 18, 18, 18, 18, 18, 18, 28, 4, 3);
-
-        // Row 0: windows 0,1
-        assert!(y0 == y1);
-        assert!(x0 < x1);
-        // Row 1: windows 2,3
-        assert!(y2 == y3);
-        assert!(x2 < x3);
-        // Row 1 below row 0
-        assert!(y2 > y0);
-    }
-
-    #[test]
-    fn test_tile_grid_asymmetric_gaps() {
-        // Asymmetric screen gaps with 2 windows in 1 row
-        let (x0, y0, w0, h0) = tile_grid(1920, 1080, 12, 10, 20, 30, 40, 6, 28, 2, 0);
-        let (x1, _y1, w1, _h1) = tile_grid(1920, 1080, 12, 10, 20, 30, 40, 6, 28, 2, 1);
-
-        // Width = (1920 - 20 - 30 - 1*12) / 2 - 2*6 = (1920-62)/2 - 12 = 929 - 12 = 917
-        assert_eq!(w0, 917);
-        assert_eq!(w0, w1);
-
-        // x0 = gap_left + bw = 20 + 6 = 26
-        assert_eq!(x0, 26);
-        // y0 = bar_height + gap_top + dec_h = 28 + 10 + 16 = 54
-        assert_eq!(y0, 54);
-        // rows=1, Height = (1080 - 28 (bar) - 10 - 40 - 0*12) / 1 - (16 + 6) = 1002 - 22 = 980
-        assert_eq!(h0, 980);
-
-        // x1 = gap_left + bw + 1*(917 + 2*6 + 12) = 26 + 941 = 967
-        assert_eq!(x1, 967);
-    }
-
-    #[test]
-    fn test_interp_channel_depth_zero() {
-        // depth 0 should return the base color unchanged
-        let result = interp_channel(0x5C5C5C5C, CASCADE_DEPTH_FACTOR, 0);
-        assert_eq!(result, 0x5C5C5C5C);
-    }
-
-    #[test]
-    fn test_interp_channel_depth_one() {
-        let result = interp_channel(0x90909090, CASCADE_DEPTH_FACTOR, 1);
-        // 0x90 * 0.80 = 144 * 0.80 = 115.2 → 115 = 0x73
-        assert_eq!(result & 0xFF, 0x73);
-    }
-
-    #[test]
-    fn test_interp_channel_depth_two() {
-        let result = interp_channel(0x60606060, CASCADE_DEPTH_FACTOR, 2);
-        // 0x60 * 0.80^2 = 96 * 0.64 = 61.44 → 61 = 0x3D
-        assert_eq!(result & 0xFF, 0x3D);
-    }
-
-    #[test]
-    fn test_cascade_hex_color_depth_zero() {
-        let color = cascade_hex_color(0x5C5C5C5C, 0x90909090, 0x60606060, 0);
-        assert_eq!(color, "#5c9060");
-    }
-
-    #[test]
-    fn test_cascade_hex_color_depth_one() {
-        let color = cascade_hex_color(0x5C5C5C5C, 0x90909090, 0x60606060, 1);
-        // 0x5C*0.80=0x49, 0x90*0.80=0x73, 0x60*0.80=0x4C
-        assert_eq!(color, "#49734c");
-    }
-
-    #[test]
-    fn test_cascade_hex_color_dark_gray() {
-        // Using #3e3e3e as base (normal border color)
-        let color = cascade_hex_color(0x3E3E3E3E, 0x3E3E3E3E, 0x3E3E3E3E, 0);
-        assert_eq!(color, "#3e3e3e");
-    }
-
-
-
-    #[test]
-    fn test_tile_fullscreen_basic() {
-        let (x, y, w, h) = tile_fullscreen(1920, 1080, 18, 18, 18, 18, 6, 28);
-        assert_eq!(x, 0);
-        assert_eq!(y, 0);
-        assert_eq!(w, 1920);
-        assert_eq!(h, 1080);
-    }
-
-    #[test]
-    fn test_tile_fullscreen_asymmetric_gaps() {
-        let (x, y, w, h) = tile_fullscreen(1920, 1080, 10, 20, 30, 40, 6, 28);
-        assert_eq!(x, 0);
-        assert_eq!(y, 0);
-        assert_eq!(w, 1920);
-        assert_eq!(h, 1080);
-    }
-
-    #[test]
-    fn test_tile_fullscreen_minimum_size() {
-        let (x, y, w, h) = tile_fullscreen(50, 50, 18, 18, 18, 18, 6, 28);
-        assert_eq!(x, 0);
-        assert_eq!(y, 0);
-        assert_eq!(w, 50);
-        assert_eq!(h, 50);
-    }
-}
diff --git a/src/client/types.rs b/src/client/types.rs
deleted file mode 100644
index a0acb46..0000000
--- a/src/client/types.rs
+++ /dev/null
@@ -1,781 +0,0 @@
-// Core data structures for cce-client
-
-use std::collections::HashMap;
-
-pub const NUM_TAGS: usize = 4;
-
-/// Tiling mode for a window
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum TilingMode {
-    Floating,
-    Cascade,
-    Grid,
-    Fullscreen,
-    Popup,
-    SidePanel,
-}
-
-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::SidePanel => "Side Panel",
-        }
-    }
-}
-
-/// Actions that can be triggered by keybindings or IPC
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Action {
-    None,
-    Spawn,
-    Toggle,
-    Close,
-    FocusNext,
-    FocusPrev,
-    Move,
-    Resize,
-    Exit,
-    Fullscreen,
-    LayoutNext,
-    ModeNext,
-    ModeNextShared,
-    Reload,
-    Restart,
-    View1,
-    View2,
-    View3,
-    View4,
-    Toggle1,
-    Toggle2,
-    Toggle3,
-    Toggle4,
-    SetTag1,
-    SetTag2,
-    SetTag3,
-    SetTag4,
-    Expose,
-    Minimize,
-    SidePanelLeft,
-    SidePanelRight,
-}
-
-/// Layout parameters
-#[derive(Debug, Clone)]
-pub struct Layout {
-    pub gap: i32,           // inter-window spacing
-    pub gap_top: i32,       // screen edge inset, top (above bar)
-    pub gap_left: i32,      // screen edge inset, left
-    pub gap_right: i32,     // screen edge inset, right
-    pub gap_bottom: i32,    // screen edge inset, bottom
-    pub cascade_offset: i32,
-    pub bar_height: i32,
-    pub border_width: i32,
-    pub fullscreen_border_width: i32,
-    pub cascade_border_width: i32,
-    pub grid_border_width: i32,
-    pub floating_border_width: i32,
-    pub border_r: u32,
-    pub border_g: u32,
-    pub border_b: u32,
-    pub border_a: u32,
-    pub background_r: u32,
-    pub background_g: u32,
-    pub background_b: u32,
-    pub background_a: u32,
-    pub border_font_size: i32,
-    pub transition_duration: i32,
-    pub grid_gap: i32,
-    pub border_blur: bool,
-    pub window_blur: bool,
-    pub side_panel_behavior: String,
-    pub side_panel_width: i32,
-    pub side_panel_position: String,
-    pub side_panel_border_gap: i32,
-    pub side_panel_border_opacity: i32,
-}
-
-impl Default for Layout {
-    fn default() -> Self {
-        Layout {
-            gap: 48,
-            gap_top: 48,
-            gap_left: 48,
-            gap_right: 48,
-            gap_bottom: 48,
-            cascade_offset: 20,
-            bar_height: 24,
-            border_width: 6,
-            fullscreen_border_width: 0,
-            cascade_border_width: 6,
-            grid_border_width: 6,
-            floating_border_width: 6,
-            border_r: 0x3E3E3E3Eu32,
-            border_g: 0x3E3E3E3Eu32,
-            border_b: 0x3E3E3E3Eu32,
-            border_a: 0xFFFFFFFFu32,
-            background_r: 0x0A0A0A0Au32,
-            background_g: 0x1A1A1A1Au32,
-            background_b: 0x0E0E0E0Eu32,
-            background_a: 0xFFFFFFFFu32,
-            border_font_size: 11,
-            transition_duration: 300,
-            grid_gap: 18,
-            border_blur: false,
-            window_blur: false,
-            side_panel_behavior: "inline".to_string(),
-            side_panel_width: 360,
-            side_panel_position: "left".to_string(),
-            side_panel_border_gap: 0,
-            side_panel_border_opacity: 100,
-        }
-    }
-}
-
-/// A rule that matches windows by app_id/title and assigns a tiling mode
-#[derive(Debug, Clone)]
-pub struct ModeRule {
-    pub mode: TilingMode,
-    pub app_id_pattern: String,
-    pub title_pattern: Option<String>,
-    pub single_instance: bool,
-    pub tag: i32,
-    pub circular: bool,
-    pub ssd: Option<bool>,
-}
-
-/// A pending keyboard binding waiting to be applied to seats
-#[derive(Debug, Clone)]
-pub struct PendingXkbBinding {
-    pub mods: u32,
-    pub keysym: u32,
-    pub action: Action,
-    pub command: Option<String>,
-}
-
-/// A pending pointer binding waiting to be applied to seats
-#[derive(Debug, Clone)]
-pub struct PendingPointerBinding {
-    pub mods: u32,
-    pub button: u32,
-    pub action: Action,
-}
-
-/// User data attached to XKB/pointer binding proxies, carrying the action
-/// to execute when the binding is triggered.
-#[derive(Debug, Clone)]
-pub struct BindingUserData {
-    pub seat_id: u64,
-    pub action: Action,
-    pub command: Option<String>,
-}
-
-/// An input device (keyboard or other)
-#[derive(Debug, Clone)]
-pub struct InputDevice {
-    pub is_keyboard: bool,
-}
-
-/// A managed output (monitor)
-#[derive(Debug, Clone)]
-pub struct Output {
-    pub id: u64,
-    pub removed: bool,
-    pub x: i32,
-    pub y: i32,
-    pub width: i32,
-    pub height: i32,
-    pub usable_x: i32,
-    pub usable_y: i32,
-    pub usable_width: i32,
-    pub usable_height: i32,
-    pub wl_output_name: Option<u32>,
-}
-
-impl Default for Output {
-    fn default() -> Self {
-        Output {
-            id: 0,
-            removed: false,
-            x: 0,
-            y: 0,
-            width: 0,
-            height: 0,
-            usable_x: 0,
-            usable_y: 0,
-            usable_width: 0,
-            usable_height: 0,
-            wl_output_name: None,
-        }
-    }
-}
-
-/// A managed window
-#[derive(Debug, Clone)]
-pub struct Window {
-    pub id: u64,
-    pub is_new: bool,
-    pub closed: bool,
-    pub tags: u32,
-    pub x: i32,
-    pub y: i32,
-    pub width: i32,
-    pub height: i32,
-    pub committed_width: i32,
-    pub committed_height: i32,
-    pub app_id: Option<String>,
-    pub title: Option<String>,
-    pub identifier: Option<String>,
-    pub parent_id: Option<u64>,
-    pub has_parent: bool,
-    pub pid: u32,
-    pub hint_min_width: i32,
-    pub hint_min_height: i32,
-    pub hint_max_width: i32,
-    pub hint_max_height: i32,
-    pub decoration_hint: u32,
-    pub presentation_hint: u32,
-    pub fullscreen_requested: bool,
-    pub maximize_requested: bool,
-    pub minimize_requested: bool,
-    pub minimized: bool,
-    pub tiling_mode: TilingMode,
-    pub mode_locked: bool,
-    /// Whether we've queued an xprop check for XWayland parent detection.
-    /// River doesn't forward WM_TRANSIENT_FOR for XWayland windows, so we
-    /// check via xprop as a fallback.
-    pub needs_xprop_check: bool,
-    /// How many ManageStart cycles we've waited for the xprop result file.
-    pub xprop_check_attempts: u8,
-    /// How many ManageStart cycles we've waited for window metadata (app_id/title).
-    pub metadata_check_attempts: u8,
-    pub anim_x: Option<f64>,
-    pub anim_y: Option<f64>,
-    pub anim_w: Option<f64>,
-    pub anim_h: Option<f64>,
-    pub anim_opacity: Option<f64>,
-    pub circular: bool,
-    pub size_hint_applied: bool,
-    pub last_borders: Option<(u32, i32, u32, u32, u32, u32)>,
-    pub last_opacity: Option<u32>,
-    pub last_circular: Option<u32>,
-    pub last_blur: Option<u32>,
-}
-
-impl Default for Window {
-    fn default() -> Self {
-        Window {
-            id: 0,
-            is_new: true,
-            closed: false,
-            tags: 1,
-            x: 0,
-            y: 0,
-            width: 0,
-            height: 0,
-            committed_width: 0,
-            committed_height: 0,
-            app_id: None,
-            title: None,
-            identifier: None,
-            parent_id: None,
-            has_parent: false,
-            pid: 0,
-            hint_min_width: 0,
-            hint_min_height: 0,
-            hint_max_width: 0,
-            hint_max_height: 0,
-            decoration_hint: 3,   // no_preference
-            presentation_hint: 0, // vsync
-            fullscreen_requested: false,
-            maximize_requested: false,
-            minimize_requested: false,
-            minimized: false,
-            tiling_mode: TilingMode::Floating,
-            mode_locked: false,
-            needs_xprop_check: false,
-            xprop_check_attempts: 0,
-            metadata_check_attempts: 0,
-            anim_x: None,
-            anim_y: None,
-            anim_w: None,
-            anim_h: None,
-            anim_opacity: None,
-            circular: false,
-            size_hint_applied: false,
-            last_borders: None,
-            last_opacity: None,
-            last_circular: None,
-            last_blur: None,
-        }
-    }
-}
-
-/// A seat (input device group)
-#[derive(Debug, Clone)]
-pub struct Seat {
-    pub id: u64,
-    pub is_new: bool,
-    pub removed: bool,
-    pub focused_window_id: Option<u64>,
-    pub hovered_window_id: Option<u64>,
-    pub interacted_window_id: Option<u64>,
-    pub pending_action: Action,
-    pub pending_command: Option<String>,
-}
-
-impl Default for Seat {
-    fn default() -> Self {
-        Seat {
-            id: 0,
-            is_new: true,
-            removed: false,
-            focused_window_id: None,
-            hovered_window_id: None,
-            interacted_window_id: None,
-            pending_action: Action::None,
-            pending_command: None,
-        }
-    }
-}
-
-/// The main window manager state
-#[derive(Debug, Clone)]
-pub struct WindowManager {
-    pub outputs: Vec<Output>,
-    pub windows: Vec<Window>,
-    pub seats: Vec<Seat>,
-    pub pending_bindings: Vec<PendingXkbBinding>,
-    pub pending_pointer_bindings: Vec<PendingPointerBinding>,
-    pub mode_rules: Vec<ModeRule>,
-    pub layout: Layout,
-    pub active_tags: u32,
-    pub focused_tags: u32,
-    pub config_done: bool,
-    pub in_manage_sequence: bool,
-    pub needs_render: bool,
-    pub needs_focus: bool,
-    pub needs_status_update: bool,
-    pub exit_requested: bool,
-    pub global_layout: TilingMode,
-    pub tag_layouts: [TilingMode; NUM_TAGS],
-    pub has_tag_layout: [bool; NUM_TAGS],
-    pub input_devices: Vec<InputDevice>,
-    pub env_vars: HashMap<String, String>,
-    pub pending_startup_apps: Vec<String>,
-    pub startup_spawned: bool,
-    pub output_scale: f64,
-    /// When true, apply configured output_scale via wlr-output-management
-    /// on the next output_manager done event. Set by config load and
-    /// by VT-switch-back (where wlroots resets scale to 1).
-    pub pending_scale_apply: bool,
-    /// When true, apply persisted state from ~/.cache/cce_client_state on the
-    /// next ManageStart cycle (after windows have been re-advertised).
-    /// Set to true on startup/restart, consumed after application.
-    pub needs_state_restore: bool,
-    /// Counter for how many ManageStart cycles we've waited for window metadata
-    /// before applying persisted state. Reset to 0 after state is applied.
-    pub state_restore_attempts: u8,
-    /// Whether tap-to-click is enabled on touchpad devices
-    pub tap_to_click: bool,
-    pub accel_speed: Option<f64>,
-    pub accel_profile: Option<String>,
-    pub natural_scroll: Option<bool>,
-    pub dwt: Option<bool>,
-    pub dwtp: Option<bool>,
-    pub trackpoint_accel_speed: Option<f64>,
-    pub trackpoint_accel_profile: Option<String>,
-    /// Whether tap-to-click config has been applied to libinput devices yet
-    pub tap_config_applied: bool,
-    pub cursor_theme: Option<String>,
-    pub cursor_size: Option<u32>,
-    pub cursor_theme_applied: bool,
-    /// Whether system notifications are enabled
-    pub notifications_enable: bool,
-    /// Reload commands to execute on configuration reload
-    pub reload_commands: Vec<String>,
-    pub input_controller: Option<tokio::sync::mpsc::UnboundedSender<crate::input::InputDaemonMsg>>,
-    pub trackpad_disabled: bool,
-    pub expose_active: bool,
-    pub expose_visual_active: bool,
-    pub animating: bool,
-    pub last_animation_tick: std::time::Instant,
-    pub last_frame_time: std::time::Instant,
-}
-
-impl Default for WindowManager {
-    fn default() -> Self {
-        WindowManager {
-            outputs: Vec::new(),
-            windows: Vec::new(),
-            seats: Vec::new(),
-            pending_bindings: Vec::new(),
-            pending_pointer_bindings: Vec::new(),
-            mode_rules: Vec::new(),
-            layout: Layout::default(),
-            active_tags: 1,
-            focused_tags: 0,
-            config_done: false,
-            in_manage_sequence: false,
-            needs_render: true,        // render on first frame
-            needs_focus: true,         // focus on first frame
-            needs_status_update: true, // update status files on first cycle
-            exit_requested: false,
-            global_layout: TilingMode::Cascade,
-            tag_layouts: [TilingMode::Cascade; NUM_TAGS],
-            has_tag_layout: [true; NUM_TAGS],
-            input_devices: Vec::new(),
-            env_vars: HashMap::new(),
-            pending_startup_apps: Vec::new(),
-            startup_spawned: false,
-            output_scale: 0.0,
-            pending_scale_apply: false,
-            needs_state_restore: true,
-            state_restore_attempts: 0,
-            tap_to_click: false,
-            accel_speed: None,
-            accel_profile: None,
-            natural_scroll: None,
-            dwt: None,
-            dwtp: None,
-            trackpoint_accel_speed: None,
-            trackpoint_accel_profile: None,
-            tap_config_applied: false,
-            cursor_theme: None,
-            cursor_size: None,
-            cursor_theme_applied: false,
-            notifications_enable: true,
-            reload_commands: Vec::new(),
-            input_controller: None,
-            trackpad_disabled: false,
-            expose_active: false,
-            expose_visual_active: false,
-            animating: false,
-            last_animation_tick: std::time::Instant::now(),
-            last_frame_time: std::time::Instant::now(),
-        }
-    }
-}
-
-impl WindowManager {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    /// Find a window by its ID
-    pub fn get_window(&self, id: u64) -> Option<&Window> {
-        self.windows.iter().find(|w| w.id == id)
-    }
-
-    /// Find a window by its ID (mutable)
-    pub fn get_window_mut(&mut self, id: u64) -> Option<&mut Window> {
-        self.windows.iter_mut().find(|w| w.id == id)
-    }
-
-    /// Find the first seat that has a focused window
-    pub fn first_seat_with_focus(&self) -> Option<&Seat> {
-        self.seats.iter().find(|s| s.focused_window_id.is_some())
-    }
-
-    /// Get the focused window for the first seat that has one
-    pub fn focused_window(&self) -> Option<&Window> {
-        let seat = self.first_seat_with_focus()?;
-        let wid = seat.focused_window_id?;
-        self.get_window(wid)
-    }
-
-    /// Get the focused window (mutable)
-    pub fn focused_window_mut(&mut self) -> Option<&mut Window> {
-        let (wid, _) = {
-            let seat = self.first_seat_with_focus()?;
-            (seat.focused_window_id?, seat.id)
-        };
-        self.get_window_mut(wid)
-    }
-
-    /// Move a window to the end of the windows vector.
-    /// This makes it the last cascade window (front of visual stack,
-    /// rightmost/bottommost position, brightest border).
-    /// Returns true if the window was moved, false if not found or already last.
-    pub fn move_window_to_end(&mut self, id: u64) -> bool {
-        let idx = match self.windows.iter().position(|w| w.id == id) {
-            Some(i) => i,
-            None => return false,
-        };
-        // Already last?
-        if idx == self.windows.len() - 1 {
-            return false;
-        }
-        let win = self.windows.remove(idx);
-        self.windows.push(win);
-        true
-    }
-}
-
-/// Parse a hex color string like "#RRGGBB" or "#RRGGBBAA" into
-/// byte-replicated 32-bit channel values for River's color format.
-/// River divides each u32 by maxInt(u32) to get a float, so each byte
-/// must be replicated: 0xVV -> 0xVVVVVVVV.
-pub fn parse_hex_color(s: &str) -> Option<(u32, u32, u32, u32)> {
-    let s = s.strip_prefix('#')?;
-    if s.len() != 6 && s.len() != 8 {
-        return None;
-    }
-    let r = u8::from_str_radix(&s[0..2], 16).ok()? as u32;
-    let g = u8::from_str_radix(&s[2..4], 16).ok()? as u32;
-    let b = u8::from_str_radix(&s[4..6], 16).ok()? as u32;
-    let a = if s.len() == 8 {
-        u8::from_str_radix(&s[6..8], 16).ok()? as u32
-    } else {
-        0xFFu32
-    };
-    // Byte-replicate: 0xVV -> 0xVVVVVVVV
-    Some((r * 0x01010101, g * 0x01010101, b * 0x01010101, a * 0x01010101))
-}
-
-/// Parse a tiling mode string
-pub fn parse_tiling_mode(s: &str) -> TilingMode {
-    match s {
-        "cascade" => TilingMode::Cascade,
-        "grid" => TilingMode::Grid,
-        "fullscreen" => TilingMode::Fullscreen,
-        "floating" => TilingMode::Floating,
-        "popup" => TilingMode::Popup,
-        "side-panel" | "side_panel" | "side panel" | "Side Panel" => TilingMode::SidePanel,
-        _ => TilingMode::Floating,
-    }
-}
-
-/// Parse an action string
-pub fn parse_action(s: &str) -> Action {
-    if s == "close" {
-        Action::Close
-    } else if s == "exit" {
-        Action::Exit
-    } else if s == "focus-next" {
-        Action::FocusNext
-    } else if s == "focus-prev" {
-        Action::FocusPrev
-    } else if s == "move" {
-        Action::Move
-    } else if s == "resize" {
-        Action::Resize
-    } else if s == "layout-next" {
-        Action::LayoutNext
-    } else if s == "mode-next" {
-        Action::ModeNext
-    } else if s == "mode-next-shared" {
-        Action::ModeNextShared
-    } else if s == "reload" {
-        Action::Reload
-    } else if s == "restart" {
-        Action::Restart
-    } else if s == "fullscreen" {
-        Action::Fullscreen
-    } else if s == "minimize" {
-        Action::Minimize
-    } else if s.starts_with("spawn")
-        && (s.len() == 5 || s.as_bytes()[5] == b' ' || s.as_bytes()[5] == b'-')
-    {
-        Action::Spawn
-    } else if s.starts_with("view") {
-        let rest = &s[4..];
-        let tag_str = rest
-            .strip_prefix('-')
-            .or_else(|| rest.strip_prefix(' '))
-            .unwrap_or(rest);
-        if let Ok(tag) = tag_str.parse::<i32>() {
-            if tag >= 1 && tag <= NUM_TAGS as i32 {
-                return match tag {
-                    1 => Action::View1,
-                    2 => Action::View2,
-                    3 => Action::View3,
-                    4 => Action::View4,
-                    _ => Action::None,
-                };
-            }
-        }
-        Action::None
-    } else if s.starts_with("toggle") {
-        let rest = &s[6..];
-        let tag_str = rest
-            .strip_prefix('-')
-            .or_else(|| rest.strip_prefix(' '))
-            .unwrap_or(rest);
-        if let Ok(tag) = tag_str.parse::<i32>() {
-            if tag >= 1 && tag <= NUM_TAGS as i32 {
-                return match tag {
-                    1 => Action::Toggle1,
-                    2 => Action::Toggle2,
-                    3 => Action::Toggle3,
-                    4 => Action::Toggle4,
-                    _ => Action::None,
-                };
-            }
-        }
-        if s == "toggle" {
-            Action::Toggle
-        } else {
-            Action::None
-        }
-    } else if s.starts_with("set-tag") {
-        let rest = &s[7..];
-        let tag_str = rest
-            .strip_prefix('-')
-            .or_else(|| rest.strip_prefix(' '))
-            .unwrap_or(rest);
-        if let Ok(tag) = tag_str.parse::<i32>() {
-            if tag >= 1 && tag <= NUM_TAGS as i32 {
-                return match tag {
-                    1 => Action::SetTag1,
-                    2 => Action::SetTag2,
-                    3 => Action::SetTag3,
-                    4 => Action::SetTag4,
-                    _ => Action::None,
-                };
-            }
-        }
-        Action::None
-    } else if s == "expose" {
-        Action::Expose
-    } else if s == "side-panel-left" || s == "side_panel_left" {
-        Action::SidePanelLeft
-    } else if s == "side-panel-right" || s == "side_panel_right" {
-        Action::SidePanelRight
-    } else {
-        Action::None
-    }
-}
-
-/// Parse modifier string like "alt+shift" into a bitmask
-pub fn parse_modifiers(mod_str: &str) -> u32 {
-    let mut mods = 0u32;
-    if mod_str.contains("super") || mod_str.contains("mod4") {
-        mods |= 0x40; // RIVER_SEAT_V1_MODIFIERS_MOD4
-    }
-    if mod_str.contains("shift") {
-        mods |= 0x01; // RIVER_SEAT_V1_MODIFIERS_SHIFT
-    }
-    if mod_str.contains("ctrl") {
-        mods |= 0x04; // RIVER_SEAT_V1_MODIFIERS_CTRL
-    }
-    if mod_str.contains("alt") || mod_str.contains("mod1") {
-        mods |= 0x08; // RIVER_SEAT_V1_MODIFIERS_MOD1
-    }
-    mods
-}
-
-/// Parse a button string ("left", "right", "middle", or numeric)
-pub fn parse_button(s: &str) -> u32 {
-    match s.trim() {
-        "left" => 0x110,   // BTN_LEFT
-        "right" => 0x111,  // BTN_RIGHT
-        "middle" => 0x112, // BTN_MIDDLE
-        "side" => 0x113,   // BTN_SIDE
-        "extra" => 0x114,  // BTN_EXTRA
-        _ => s.trim().parse().unwrap_or(0),
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_parse_hex_color_rgb() {
-        let (r, g, b, a) = parse_hex_color("#3e3e3e").unwrap();
-        assert_eq!(r, 0x3E3E3E3E);
-        assert_eq!(g, 0x3E3E3E3E);
-        assert_eq!(b, 0x3E3E3E3E);
-        assert_eq!(a, 0xFFFFFFFF);
-    }
-
-    #[test]
-    fn test_parse_hex_color_rgba() {
-        let (r, g, b, a) = parse_hex_color("#5c9060ff").unwrap();
-        assert_eq!(r, 0x5C5C5C5C);
-        assert_eq!(g, 0x90909090);
-        assert_eq!(b, 0x60606060);
-        assert_eq!(a, 0xFFFFFFFF);
-    }
-
-    #[test]
-    fn test_parse_hex_color_invalid() {
-        assert!(parse_hex_color("3e3e3e").is_none());
-        assert!(parse_hex_color("#3e3e").is_none());
-        assert!(parse_hex_color("#3e3e3e3e3e").is_none());
-    }
-
-    #[test]
-    fn test_parse_tiling_mode() {
-        assert_eq!(parse_tiling_mode("cascade"), TilingMode::Cascade);
-        assert_eq!(parse_tiling_mode("grid"), TilingMode::Grid);
-        assert_eq!(parse_tiling_mode("fullscreen"), TilingMode::Fullscreen);
-        assert_eq!(parse_tiling_mode("floating"), TilingMode::Floating);
-        assert_eq!(parse_tiling_mode("popup"), TilingMode::Popup);
-        assert_eq!(parse_tiling_mode("side-panel"), TilingMode::SidePanel);
-        assert_eq!(parse_tiling_mode("Side Panel"), TilingMode::SidePanel);
-        assert_eq!(parse_tiling_mode("unknown"), TilingMode::Floating);
-    }
-
-    #[test]
-    fn test_parse_action() {
-        assert_eq!(parse_action("close"), Action::Close);
-        assert_eq!(parse_action("exit"), Action::Exit);
-        assert_eq!(parse_action("focus-next"), Action::FocusNext);
-        assert_eq!(parse_action("focus-prev"), Action::FocusPrev);
-        assert_eq!(parse_action("move"), Action::Move);
-        assert_eq!(parse_action("resize"), Action::Resize);
-        assert_eq!(parse_action("layout-next"), Action::LayoutNext);
-        assert_eq!(parse_action("mode-next"), Action::ModeNext);
-        assert_eq!(parse_action("mode-next-shared"), Action::ModeNextShared);
-        assert_eq!(parse_action("reload"), Action::Reload);
-        assert_eq!(parse_action("restart"), Action::Restart);
-        assert_eq!(parse_action("spawn"), Action::Spawn);
-        assert_eq!(parse_action("spawn something"), Action::Spawn);
-        assert_eq!(parse_action("toggle"), Action::Toggle);
-        assert_eq!(parse_action("view-1"), Action::View1);
-        assert_eq!(parse_action("view-4"), Action::View4);
-        assert_eq!(parse_action("toggle-2"), Action::Toggle2);
-        assert_eq!(parse_action("set-tag-3"), Action::SetTag3);
-        assert_eq!(parse_action("expose"), Action::Expose);
-        assert_eq!(parse_action("minimize"), Action::Minimize);
-        assert_eq!(parse_action("side-panel-left"), Action::SidePanelLeft);
-        assert_eq!(parse_action("side-panel-right"), Action::SidePanelRight);
-        assert_eq!(parse_action("unknown"), Action::None);
-    }
-
-    #[test]
-    fn test_parse_modifiers() {
-        assert_eq!(parse_modifiers("alt"), 0x08);
-        assert_eq!(parse_modifiers("alt+shift"), 0x08 | 0x01);
-        assert_eq!(parse_modifiers("ctrl"), 0x04);
-        assert_eq!(parse_modifiers("super"), 0x40);
-        assert_eq!(parse_modifiers(""), 0x00);
-    }
-
-    #[test]
-    fn test_parse_button() {
-        assert_eq!(parse_button("left"), 0x110);
-        assert_eq!(parse_button("right"), 0x111);
-        assert_eq!(parse_button("middle"), 0x112);
-        assert_eq!(parse_button("272"), 272);
-    }
-
-    #[test]
-    fn test_wm_default() {
-        let wm = WindowManager::default();
-        assert_eq!(wm.active_tags, 1);
-        assert_eq!(wm.global_layout, TilingMode::Cascade);
-        assert!(!wm.config_done);
-        assert_eq!(wm.layout.gap, 48);
-        assert_eq!(wm.layout.gap_top, 48);
-        assert_eq!(wm.layout.gap_left, 48);
-        assert_eq!(wm.layout.gap_right, 48);
-        assert_eq!(wm.layout.gap_bottom, 48);
-    }
-}
diff --git a/src/client/wayland.rs b/src/client/wayland.rs
deleted file mode 100644
index 1692cd6..0000000
--- a/src/client/wayland.rs
+++ /dev/null
@@ -1,4436 +0,0 @@
-// Wayland display connection, registry, and event dispatch for cce-client
-
-use wayland_client::{
-    event_created_child, protocol::wl_registry, Connection, Dispatch, EventQueue, Proxy,
-    QueueHandle,
-};
-
-use crate::protocol::river_input_management::client::{
-    river_input_device_v1::{self, RiverInputDeviceV1},
-    river_input_manager_v1::{self, RiverInputManagerV1},
-};
-use crate::protocol::river_libinput_config::client::{
-    river_libinput_config_v1::{self, RiverLibinputConfigV1},
-    river_libinput_device_v1::{self, RiverLibinputDeviceV1},
-    river_libinput_result_v1::{self, RiverLibinputResultV1},
-};
-use crate::protocol::river_layer_shell::client::{
-    river_layer_shell_output_v1::{self, RiverLayerShellOutputV1},
-    river_layer_shell_v1::{self, RiverLayerShellV1},
-};
-use crate::protocol::river_window_management::client::{
-    river_decoration_v1::{self, RiverDecorationV1},
-    river_node_v1::{self, RiverNodeV1},
-    river_output_v1::{self, RiverOutputV1},
-    river_pointer_binding_v1::{self, RiverPointerBindingV1},
-    river_seat_v1::{self, Modifiers, RiverSeatV1},
-    river_window_manager_v1::{self, RiverWindowManagerV1},
-    river_window_v1::{self, RiverWindowV1},
-};
-use crate::protocol::river_xkb_bindings::client::{
-    river_xkb_binding_v1::{self, RiverXkbBindingV1},
-    river_xkb_bindings_seat_v1::{self, RiverXkbBindingsSeatV1},
-    river_xkb_bindings_v1::{self, RiverXkbBindingsV1},
-};
-use crate::protocol::wlr_output_management::client::{
-    zwlr_output_configuration_head_v1::{self, ZwlrOutputConfigurationHeadV1},
-    zwlr_output_configuration_v1::{self, ZwlrOutputConfigurationV1},
-    zwlr_output_head_v1::{self, ZwlrOutputHeadV1},
-    zwlr_output_manager_v1::{self, ZwlrOutputManagerV1},
-    zwlr_output_mode_v1::{self, ZwlrOutputModeV1},
-};
-
-use crate::types::{Action, BindingUserData, Output, Seat, TilingMode, Window, WindowManager};
-use wayland_client::protocol::{
-    wl_buffer, wl_compositor, wl_output, wl_pointer, wl_seat, wl_shm, wl_shm_pool, wl_surface,
-};
-use wayland_protocols::wp::cursor_shape::v1::client::{
-    wp_cursor_shape_device_v1::{self, WpCursorShapeDeviceV1, Shape},
-    wp_cursor_shape_manager_v1::{self, WpCursorShapeManagerV1},
-};
-
-// Interface name constants (from river protocol XML)
-const IFACE_WINDOW_MANAGER: &str = "river_window_manager_v1";
-const IFACE_CURSOR_SHAPE_MANAGER: &str = "wp_cursor_shape_manager_v1";
-const IFACE_XKB_BINDINGS: &str = "river_xkb_bindings_v1";
-const IFACE_LAYER_SHELL: &str = "river_layer_shell_v1";
-const IFACE_INPUT_MANAGER: &str = "river_input_manager_v1";
-const IFACE_LIBINPUT_CONFIG: &str = "river_libinput_config_v1";
-const IFACE_WLR_OUTPUT_MANAGER: &str = "zwlr_output_manager_v1";
-
-const CORNER_THRESHOLD: f64 = 16.0;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum PointerOpType {
-    Move,
-    Resize,
-    ResizeLeft,
-    ResizeRight,
-    ResizeBottom,
-    ResizeTop,
-    ResizeBottomLeft,
-    ResizeBottomRight,
-    ResizeTopLeft,
-    ResizeTopRight,
-}
-
-#[derive(Debug, Clone)]
-pub struct PointerOp {
-    pub window_id: u64,
-    pub op_type: PointerOpType,
-    pub start_x: i32,
-    pub start_y: i32,
-    pub start_width: i32,
-    pub start_height: i32,
-}
-
-#[derive(Debug, Clone)]
-pub struct PendingBorderDrag {
-    pub window_id: u64,
-    pub seat_id: u64,
-    pub start_surface_x: f64,
-    pub start_surface_y: f64,
-    pub op_type: PointerOpType,
-}
-
-fn update_screen_bounds(outputs: &[crate::types::Output]) {
-    let mut max_x = 1920;
-    let mut max_y = 1080;
-    for out in outputs {
-        if !out.removed {
-            let right = out.x + out.width;
-            let bottom = out.y + out.height;
-            if right > max_x {
-                max_x = right;
-            }
-            if bottom > max_y {
-                max_y = bottom;
-            }
-        }
-    }
-    crate::input::SCREEN_WIDTH.store(max_x, std::sync::atomic::Ordering::SeqCst);
-    crate::input::SCREEN_HEIGHT.store(max_y, std::sync::atomic::Ordering::SeqCst);
-}
-
-/// Wayland proxy objects stored alongside each Window, so we can
-/// call protocol methods (set_position, propose_dimensions, etc.) on it.
-pub struct WindowProxy {
-    pub river_window: RiverWindowV1,
-    pub decoration: Option<crate::decorations::WindowDecoration>,
-    pub dec_left: Option<crate::decorations::WindowDecoration>,
-    pub dec_right: Option<crate::decorations::WindowDecoration>,
-    pub dec_bottom: Option<crate::decorations::WindowDecoration>,
-}
-
-/// Wayland proxy objects stored alongside each Seat.
-pub struct SeatProxy {
-    pub river_seat: RiverSeatV1,
-    pub xkb_bindings_seat: Option<RiverXkbBindingsSeatV1>,
-    pub wl_seat: Option<wl_seat::WlSeat>,
-    pub wl_pointer: Option<wl_pointer::WlPointer>,
-    pub cursor_shape_device: Option<WpCursorShapeDeviceV1>,
-    pub last_pointer_enter_serial: u32,
-}
-
-/// Wayland proxy objects stored alongside each Output.
-pub struct OutputProxy {
-    pub river_output: RiverOutputV1,
-    pub layer_shell_output: Option<RiverLayerShellOutputV1>,
-}
-
-/// Tracked wl_output proxy and its physical properties.
-pub struct WlOutputInfo {
-    pub name: u32,
-    pub wl_output: wl_output::WlOutput,
-    pub width: i32,
-    pub height: i32,
-    pub x: i32,
-    pub y: i32,
-}
-
-/// The full app state combining logic state + protocol proxy storage.
-pub struct AppState {
-    pub wm: WindowManager,
-    pub wl_outputs: Vec<WlOutputInfo>,
-
-    // Protocol objects (None until bound via registry)
-    pub registry: Option<wl_registry::WlRegistry>,
-    pub window_manager: Option<RiverWindowManagerV1>,
-    pub xkb_bindings: Option<RiverXkbBindingsV1>,
-    pub layer_shell: Option<RiverLayerShellV1>,
-    pub input_manager: Option<RiverInputManagerV1>,
-    pub cursor_shape_manager: Option<WpCursorShapeManagerV1>,
-    pub compositor: Option<wl_compositor::WlCompositor>,
-    pub shm: Option<wl_shm::WlShm>,
-
-    // Whether we got all required globals
-    pub has_window_manager: bool,
-    pub has_xkb_bindings: bool,
-
-    // Proxy objects indexed by window/seat/output ID
-    pub window_proxies: Vec<(u64, WindowProxy)>,
-    pub seat_proxies: Vec<(u64, SeatProxy)>,
-    pub output_proxies: Vec<(u64, OutputProxy)>,
-
-    // River node proxies for window positioning (created via get_node request)
-    pub window_nodes: Vec<(u64, RiverNodeV1)>,
-
-    // Active binding proxies — must be held alive for bindings to remain registered.
-    // On reload, these are cleared (which destroys the old protocol objects) before
-    // new bindings are created.
-    pub xkb_binding_proxies: Vec<RiverXkbBindingV1>,
-    pub pointer_binding_proxies: Vec<RiverPointerBindingV1>,
-
-    // Next ID counter for new windows/outputs/seats
-    pub next_id: u64,
-
-    // Exit flag
-    pub exit_requested: bool,
-
-    // Debug: render cycle counter
-    pub render_count: u32,
-
-    // --- wlr-output-management protocol state ---
-    pub output_manager: Option<ZwlrOutputManagerV1>,
-    /// Latest serial from the output manager's done event.
-    /// Required to create a valid configuration.
-    pub output_serial: u32,
-    /// Discovered output heads: (head_proxy, name, enabled, current_scale)
-    /// The head proxy must be kept alive to reference it in configurations.
-    pub output_heads: Vec<OutputHeadInfo>,
-    /// Pending configuration (alive until succeeded/failed/cancelled)
-    pub output_config: Option<ZwlrOutputConfigurationV1>,
-
-    // --- Status socket sender for waybar ---
-    pub status_sender: Option<crate::status_server::StatusSender>,
-
-    // --- Libinput config protocol state ---
-    pub libinput_config: Option<RiverLibinputConfigV1>,
-    /// Tracked libinput devices with their tap state
-    pub libinput_devices: Vec<LibinputDeviceInfo>,
-    /// The surface the pointer is currently hovering over
-    pub pointer_hovered_surface: Option<wl_surface::WlSurface>,
-    pub active_pointer_op: Option<PointerOp>,
-    pub pointer_op_release_pending: bool,
-    pub pending_border_drag: Option<PendingBorderDrag>,
-    pub pending_pointer_op_type: Option<PointerOpType>,
-    pub last_pointer_surface_x: f64,
-    pub last_pointer_surface_y: f64,
-    pub input_device_names: std::collections::HashMap<u32, String>,
-    pub border_font: Option<fontdue::Font>,
-    pub border_font_path: Option<String>,
-}
-
-/// Info tracked for each libinput device discovered via river_libinput_config_v1
-pub struct LibinputDeviceInfo {
-    pub device: RiverLibinputDeviceV1,
-    /// Device name (from the river_input_device_v1 name event)
-    pub name: String,
-    pub input_device: Option<RiverInputDeviceV1>,
-    pub name_received: bool,
-    /// Number of fingers supported for tap (0 = unsupported)
-    pub tap_finger_count: i32,
-    /// Whether we've received enough events to apply tap config
-    pub tap_info_received: bool,
-    pub accel_profiles_support: Option<u32>,
-    pub natural_scroll_supported: Option<bool>,
-    pub dwt_supported: Option<bool>,
-    pub dwtp_supported: Option<bool>,
-    pub config_applied: bool,
-}
-
-/// Tracked info for a wlr-output-management head.
-pub struct OutputHeadInfo {
-    pub proxy: ZwlrOutputHeadV1,
-    pub name: String,
-    pub enabled: bool,
-    pub scale: f64,
-}
-
-impl AppState {
-    pub fn new() -> Self {
-        AppState {
-            wm: WindowManager::new(),
-            wl_outputs: Vec::new(),
-            registry: None,
-            window_manager: None,
-            xkb_bindings: None,
-            layer_shell: None,
-            input_manager: None,
-            cursor_shape_manager: None,
-            compositor: None,
-            shm: None,
-            has_window_manager: false,
-            has_xkb_bindings: false,
-            window_proxies: Vec::new(),
-            seat_proxies: Vec::new(),
-            output_proxies: Vec::new(),
-            window_nodes: Vec::new(),
-            xkb_binding_proxies: Vec::new(),
-            pointer_binding_proxies: Vec::new(),
-            next_id: 1,
-            exit_requested: false,
-            render_count: 0,
-            output_manager: None,
-            output_serial: 0,
-            output_heads: Vec::new(),
-            output_config: None,
-            status_sender: None,
-            libinput_config: None,
-            libinput_devices: Vec::new(),
-            pointer_hovered_surface: None,
-            active_pointer_op: None,
-            pointer_op_release_pending: false,
-            pending_border_drag: None,
-            pending_pointer_op_type: None,
-            last_pointer_surface_x: 0.0,
-            last_pointer_surface_y: 0.0,
-            input_device_names: std::collections::HashMap::new(),
-            border_font: None,
-            border_font_path: None,
-        }
-    }
-
-    fn alloc_id(&mut self) -> u64 {
-        let id = self.next_id;
-        self.next_id += 1;
-        id
-    }
-
-    pub fn manage_dirty(&mut self) {
-        if let Some(ref wm) = self.window_manager {
-            wm.manage_dirty();
-            self.wm.last_animation_tick = std::time::Instant::now();
-        }
-    }
-
-    pub fn get_window_proxy(&self, id: u64) -> Option<&WindowProxy> {
-        self.window_proxies
-            .iter()
-            .find(|(wid, _)| *wid == id)
-            .map(|(_, p)| p)
-    }
-
-    pub fn get_seat_proxy(&self, id: u64) -> Option<&SeatProxy> {
-        self.seat_proxies
-            .iter()
-            .find(|(sid, _)| *sid == id)
-            .map(|(_, p)| p)
-    }
-
-    pub fn get_output_proxy(&self, id: u64) -> Option<&OutputProxy> {
-        self.output_proxies
-            .iter()
-            .find(|(oid, _)| *oid == id)
-            .map(|(_, p)| p)
-    }
-
-    /// Find the internal window ID for a given RiverWindowV1 proxy.
-    fn window_id_for_proxy(&self, proxy: &RiverWindowV1) -> Option<u64> {
-        let pid = proxy.id().protocol_id();
-        self.window_proxies
-            .iter()
-            .find(|(_, wp)| wp.river_window.id().protocol_id() == pid)
-            .map(|(id, _)| *id)
-    }
-
-    /// Find the internal seat ID for a given RiverSeatV1 proxy.
-    fn seat_id_for_proxy(&self, proxy: &RiverSeatV1) -> Option<u64> {
-        let pid = proxy.id().protocol_id();
-        self.seat_proxies
-            .iter()
-            .find(|(_, sp)| sp.river_seat.id().protocol_id() == pid)
-            .map(|(id, _)| *id)
-    }
-
-    fn update_cursor_shape_for_surface(state: &mut Self, pointer: &wl_pointer::WlPointer) {
-        let seat_info = state.seat_proxies.iter().find_map(|(_, sp)| {
-            if sp.wl_pointer.as_ref() == Some(pointer) {
-                Some((sp.cursor_shape_device.clone(), sp.last_pointer_enter_serial))
-            } else {
-                None
-            }
-        });
-
-        let (device, serial) = match seat_info {
-            Some((Some(dev), ser)) => (dev, ser),
-            _ => return,
-        };
-
-        let mut shape = Shape::Default;
-
-        if let Some(ref current_surface) = state.pointer_hovered_surface {
-            let matched_window = state.window_proxies.iter().find_map(|(id, proxy)| {
-                if let Some(dec) = &proxy.decoration {
-                    if &dec.surface == current_surface {
-                        return Some((*id, "top"));
-                    }
-                }
-                if let Some(dec) = &proxy.dec_left {
-                    if &dec.surface == current_surface {
-                        return Some((*id, "left"));
-                    }
-                }
-                if let Some(dec) = &proxy.dec_right {
-                    if &dec.surface == current_surface {
-                        return Some((*id, "right"));
-                    }
-                }
-                if let Some(dec) = &proxy.dec_bottom {
-                    if &dec.surface == current_surface {
-                        return Some((*id, "bottom"));
-                    }
-                }
-                None
-            });
-
-            if let Some((wid, surface_type)) = matched_window {
-                if let Some(w) = state.wm.windows.iter().find(|win| win.id == wid) {
-                    let border_w = if state.wm.expose_active && w.tiling_mode != crate::types::TilingMode::Popup {
-                        state.wm.layout.grid_border_width
-                    } else {
-                        match w.tiling_mode {
-                            crate::types::TilingMode::Cascade => state.wm.layout.cascade_border_width,
-                            crate::types::TilingMode::Fullscreen => state.wm.layout.fullscreen_border_width,
-                            crate::types::TilingMode::Grid => state.wm.layout.grid_border_width,
-                            crate::types::TilingMode::Floating => state.wm.layout.floating_border_width,
-                            crate::types::TilingMode::Popup => 0,
-                            crate::types::TilingMode::SidePanel => state.wm.layout.cascade_border_width,
-                        }
-                    };
-                    let grab_w = border_w.max(10);
-
-                    let logical_height = border_w.max(16);
-                    shape = match surface_type {
-                        "top" => {
-                            let total_width = w.width + 2 * border_w;
-                            let mid_y = (logical_height as f64) / 2.0;
-                            if state.last_pointer_surface_y < mid_y {
-                                if state.last_pointer_surface_x < CORNER_THRESHOLD {
-                                    Shape::NwseResize
-                                } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
-                                    Shape::NeswResize
-                                } else {
-                                    Shape::NsResize
-                                }
-                            } else {
-                                Shape::Default
-                            }
-                        }
-                        "left" => {
-                            let mid_x = (grab_w as f64) / 2.0;
-                            if state.last_pointer_surface_x < mid_x {
-                                if state.last_pointer_surface_y < CORNER_THRESHOLD {
-                                    Shape::NwseResize
-                                } else if state.last_pointer_surface_y > (w.height as f64 - CORNER_THRESHOLD) {
-                                    Shape::NeswResize
-                                } else {
-                                    Shape::EwResize
-                                }
-                            } else {
-                                Shape::Default
-                            }
-                        }
-                        "right" => {
-                            let mid_x = (grab_w as f64) / 2.0;
-                            if state.last_pointer_surface_x >= mid_x {
-                                if state.last_pointer_surface_y < CORNER_THRESHOLD {
-                                    Shape::NeswResize
-                                } else if state.last_pointer_surface_y > (w.height as f64 - CORNER_THRESHOLD) {
-                                    Shape::NwseResize
-                                } else {
-                                    Shape::EwResize
-                                }
-                            } else {
-                                Shape::Default
-                            }
-                        }
-                        "bottom" => {
-                            let total_width = w.width + 2 * grab_w;
-                            let mid_y = (grab_w as f64) / 2.0;
-                            if state.last_pointer_surface_y >= mid_y {
-                                if state.last_pointer_surface_x < CORNER_THRESHOLD {
-                                    Shape::NeswResize
-                                } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
-                                    Shape::NwseResize
-                                } else {
-                                    Shape::NsResize
-                                }
-                            } else {
-                                Shape::Default
-                            }
-                        }
-                        _ => Shape::Default,
-                    };
-                }
-            }
-        }
-
-        device.set_shape(serial, shape);
-    }
-}
-
-// --- Dispatch implementations ---
-
-/// User data for the registry
-pub struct RegistryData;
-
-impl Dispatch<wl_registry::WlRegistry, RegistryData> for AppState {
-    fn event(
-        state: &mut Self,
-        registry: &wl_registry::WlRegistry,
-        event: wl_registry::Event,
-        _data: &RegistryData,
-        _conn: &Connection,
-        qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            wl_registry::Event::Global {
-                name,
-                interface,
-                version,
-            } => {
-                if interface == IFACE_WINDOW_MANAGER {
-                    if version >= 4 {
-                        eprintln!("registry: binding {} v{}", IFACE_WINDOW_MANAGER, version);
-                        // Use the Proxy trait's bind method via the interface
-                        let wm: RiverWindowManagerV1 =
-                            registry.bind::<RiverWindowManagerV1, _, _>(name, 4, qhandle, ());
-                        state.window_manager = Some(wm);
-                        state.has_window_manager = true;
-                    } else {
-                        eprintln!(
-                            "warning: {} version {} < 4, skipping",
-                            IFACE_WINDOW_MANAGER, version
-                        );
-                    }
-                } else if interface == IFACE_XKB_BINDINGS {
-                    let bind_ver = std::cmp::min(version, 2);
-                    eprintln!("registry: binding {} v{}", IFACE_XKB_BINDINGS, bind_ver);
-                    let xb: RiverXkbBindingsV1 =
-                        registry.bind::<RiverXkbBindingsV1, _, _>(name, bind_ver, qhandle, ());
-                    state.xkb_bindings = Some(xb);
-                    state.has_xkb_bindings = true;
-                } else if interface == IFACE_LAYER_SHELL {
-                    eprintln!("registry: binding {}", IFACE_LAYER_SHELL);
-                    let ls: RiverLayerShellV1 =
-                        registry.bind::<RiverLayerShellV1, _, _>(name, 1, qhandle, ());
-                    state.layer_shell = Some(ls);
-                } else if interface == IFACE_INPUT_MANAGER {
-                    eprintln!("registry: binding {}", IFACE_INPUT_MANAGER);
-                    let im: RiverInputManagerV1 =
-                        registry.bind::<RiverInputManagerV1, _, _>(name, 1, qhandle, ());
-                    state.input_manager = Some(im);
-                } else if interface == IFACE_LIBINPUT_CONFIG {
-                    eprintln!("registry: binding {}", IFACE_LIBINPUT_CONFIG);
-                    let lc: RiverLibinputConfigV1 =
-                        registry.bind::<RiverLibinputConfigV1, _, _>(name, 1, qhandle, ());
-                    state.libinput_config = Some(lc);
-                } else if interface == IFACE_WLR_OUTPUT_MANAGER {
-                    eprintln!("registry: binding {}", IFACE_WLR_OUTPUT_MANAGER);
-                    let om: ZwlrOutputManagerV1 =
-                        registry.bind::<ZwlrOutputManagerV1, _, _>(name, 4, qhandle, ());
-                    state.output_manager = Some(om);
-                } else if interface == IFACE_CURSOR_SHAPE_MANAGER {
-                    eprintln!("registry: binding {}", IFACE_CURSOR_SHAPE_MANAGER);
-                    let csm: WpCursorShapeManagerV1 =
-                        registry.bind::<WpCursorShapeManagerV1, _, _>(name, 1, qhandle, ());
-                    state.cursor_shape_manager = Some(csm);
-                } else if interface == "wl_compositor" {
-                    eprintln!("registry: binding wl_compositor name={}", name);
-                    let comp: wl_compositor::WlCompositor =
-                        registry.bind::<wl_compositor::WlCompositor, _, _>(name, 4, qhandle, ());
-                    state.compositor = Some(comp);
-                } else if interface == "wl_shm" {
-                    eprintln!("registry: binding wl_shm name={}", name);
-                    let shm: wl_shm::WlShm =
-                        registry.bind::<wl_shm::WlShm, _, _>(name, 1, qhandle, ());
-                    state.shm = Some(shm);
-                } else if interface == "wl_output" {
-                    eprintln!("registry: binding wl_output name={}", name);
-                    let wl_out: wl_output::WlOutput =
-                        registry.bind::<wl_output::WlOutput, _, _>(name, 4, qhandle, ());
-                    state.wl_outputs.push(WlOutputInfo {
-                        name,
-                        wl_output: wl_out,
-                        width: 0,
-                        height: 0,
-                        x: 0,
-                        y: 0,
-                    });
-                }
-            }
-            wl_registry::Event::GlobalRemove { name: _ } => {}
-            _ => {}
-        }
-    }
-}
-
-// --- RiverWindowManagerV1 events ---
-
-impl Dispatch<RiverWindowManagerV1, ()> for AppState {
-    event_created_child!(AppState, RiverWindowManagerV1, [
-        river_window_manager_v1::EVT_WINDOW_OPCODE => (RiverWindowV1, ()),
-        river_window_manager_v1::EVT_OUTPUT_OPCODE => (RiverOutputV1, ()),
-        river_window_manager_v1::EVT_SEAT_OPCODE => (RiverSeatV1, ()),
-    ]);
-
-    fn event(
-        state: &mut Self,
-        wm_proxy: &RiverWindowManagerV1,
-        event: river_window_manager_v1::Event,
-        _data: &(),
-        conn: &Connection,
-        qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_window_manager_v1::Event::Unavailable => {
-                eprintln!("error: another window manager is already running");
-                state.exit_requested = true;
-                state.wm.exit_requested = true;
-            }
-
-            river_window_manager_v1::Event::Finished => {
-                if state.wm.exit_requested {
-                    eprintln!("river sent finished, exiting");
-                    state.exit_requested = true;
-                } else {
-                    eprintln!("river sent finished unexpectedly, restarting");
-                    // Write to death log before fork+exit loses all traces
-                    if let Ok(mut f) = std::fs::OpenOptions::new()
-                        .create(true)
-                        .append(true)
-                        .open(crate::paths::get_death_log_path())
-                    {
-                        use std::io::Write;
-                        let _ = writeln!(
-                            f,
-                            "river sent Finished event — protocol error likely, in_manage={}, render_count={}",
-                            state.wm.in_manage_sequence,
-                            state.render_count
-                        );
-                        let _ = f.sync_all();
-                    }
-                    crate::restart::wm_restart();
-                }
-            }
-
-            river_window_manager_v1::Event::ManageStart => {
-                let ms_start = std::time::Instant::now();
-                state.wm.in_manage_sequence = true;
-                state.wm.focused_tags = 0;
-                state.wm.needs_render = true;
-
-                // Remove closed windows and their proxy objects.
-                // Collect IDs first, then clean up proxies, then remove from windows vector.
-                // This matches tinyrwm's remove_windows() pattern.
-                let closed_ids: Vec<(u64, Option<String>)> = state
-                    .wm
-                    .windows
-                    .iter()
-                    .filter(|w| w.closed)
-                    .map(|w| (w.id, w.app_id.clone()))
-                    .collect();
-                // Destroy proxy objects for closed windows.
-                // Also clean up seat state (focused_window_id, hovered_window_id,
-                // interacted_window_id) that references closed windows, matching
-                // tinyrwm's remove_windows() pattern which cleans up seat ops
-                // referencing closed window proxies.
-                for (closed_id, closed_app_id) in &closed_ids {
-                    state.window_proxies.retain_mut(|(id, wp)| {
-                        if id == closed_id {
-                            if let Some(dec) = wp.decoration.take() {
-                                dec.decoration.destroy();
-                                dec.surface.destroy();
-                            }
-                            if let Some(dec) = wp.dec_left.take() {
-                                dec.decoration.destroy();
-                                dec.surface.destroy();
-                            }
-                            if let Some(dec) = wp.dec_right.take() {
-                                dec.decoration.destroy();
-                                dec.surface.destroy();
-                            }
-                            if let Some(dec) = wp.dec_bottom.take() {
-                                dec.decoration.destroy();
-                                dec.surface.destroy();
-                            }
-                            wp.river_window.destroy();
-                            false
-                        } else {
-                            true
-                        }
-                    });
-                    state.window_nodes.retain(|(id, node)| {
-                        if id == closed_id {
-                            node.destroy();
-                            false
-                        } else {
-                            true
-                        }
-                    });
-                    // Clear seat references to this closed window.
-                    // If the focused window closed, reassign focus to another
-                    // visible window (matching tinyrwm's focus_top pattern).
-                    let active_tags = state.wm.active_tags;
-                    for seat in &mut state.wm.seats {
-                        if seat.focused_window_id == Some(*closed_id) {
-                            // Pick the last visible window (cascade front)
-                            let visible_ids: Vec<u64> = state
-                                .wm
-                                .windows
-                                .iter()
-                                .filter(|w| {
-                                    !w.closed && (w.tags & active_tags) != 0 && w.id != *closed_id && w.app_id.as_deref() != Some("cce-status-interface")
-                                })
-                                .map(|w| w.id)
-                                .collect();
-                            seat.focused_window_id = visible_ids.last().copied();
-                            if seat.focused_window_id.is_some() {
-                                state.wm.needs_focus = true;
-                                state.wm.needs_status_update = true;
-                            }
-                            eprintln!(
-                                "[manage] focused window {} (app_id={:?}) closed, reassigned to {:?}",
-                                closed_id, closed_app_id, seat.focused_window_id
-                            );
-                        }
-                        if seat.hovered_window_id == Some(*closed_id) {
-                            seat.hovered_window_id = None;
-                        }
-                        if seat.interacted_window_id == Some(*closed_id) {
-                            seat.interacted_window_id = None;
-                        }
-                    }
-                }
-                state.wm.windows.retain(|w| !w.closed);
-
-                // Remove removed outputs — destroy protocol proxies first
-                // (matching tinyrwm's remove_outputs pattern).
-                let removed_output_ids: Vec<u64> = state
-                    .wm
-                    .outputs
-                    .iter()
-                    .filter(|o| o.removed)
-                    .map(|o| o.id)
-                    .collect();
-                for removed_id in &removed_output_ids {
-                    state.output_proxies.retain(|(id, op)| {
-                        if id == removed_id {
-                            op.river_output.destroy();
-                            false
-                        } else {
-                            true
-                        }
-                    });
-                }
-                state.wm.outputs.retain(|o| !o.removed);
-                update_screen_bounds(&state.wm.outputs);
-
-                // Remove removed seats — destroy protocol proxies first
-                // (matching tinyrwm's remove_seats pattern which destroys
-                // seat proxy and all binding proxies).
-                let removed_seat_ids: Vec<u64> = state
-                    .wm
-                    .seats
-                    .iter()
-                    .filter(|s| s.removed)
-                    .map(|s| s.id)
-                    .collect();
-                for removed_id in &removed_seat_ids {
-                    state.seat_proxies.retain(|(id, sp)| {
-                        if id == removed_id {
-                            sp.river_seat.destroy();
-                            false
-                        } else {
-                            true
-                        }
-                    });
-                }
-                state.wm.seats.retain(|s| !s.removed);
-
-                // Set default layer shell on first output
-                if let Some(first_output) = state.wm.outputs.first() {
-                    if let Some(op) = state.get_output_proxy(first_output.id) {
-                        if let Some(ref lso) = op.layer_shell_output {
-                            lso.set_default();
-                        }
-                    }
-                }
-
-                // Enforce single_instance mode rules
-                enforce_single_instance(&mut state.wm);
-
-                // Apply pending bindings to seats
-                apply_pending_bindings(state, qhandle);
-
-                // Manage seats (set focused tags)
-                for seat in &state.wm.seats {
-                    if let Some(focused_id) = seat.focused_window_id {
-                        if let Some(fw) = state.wm.get_window(focused_id) {
-                            state.wm.focused_tags |= fw.tags;
-                        }
-                    }
-                }
-
-                // Check xprop results for XWayland parent detection.
-                // River doesn't send the Parent event for XWayland windows,
-                // so we detect WM_TRANSIENT_FOR / _NET_WM_WINDOW_TYPE_DIALOG|UTILITY
-                // via an async xprop check spawned in the UnreliablePid handler.
-                // This must run before assign_window_modes so has_parent is set
-                // before mode assignment decides Floating vs tiled.
-                {
-                    let mut xprop_parent_changed = false;
-                    let max_attempts: u8 = 10;
-                    for window in &mut state.wm.windows {
-                        if !window.needs_xprop_check || window.has_parent || window.closed {
-                            continue;
-                        }
-                        window.xprop_check_attempts += 1;
-                        let path = crate::paths::get_xprop_path(window.id);
-                        if let Ok(content) = std::fs::read_to_string(&path) {
-                            let _ = std::fs::remove_file(&path);
-                            // Each line: title|window_type_line
-                            // Match by title against our window's title.
-                            let my_title = window.title.as_deref().unwrap_or("");
-                            for line in content.lines() {
-                                let parts: Vec<&str> = line.splitn(2, '|').collect();
-                                if parts.len() < 2 {
-                                    continue;
-                                }
-                                let x11_title = parts[0];
-                                let wtype = parts[1];
-                                if x11_title != my_title {
-                                    continue;
-                                }
-                                let is_dialog =
-                                    wtype.contains("DIALOG") || wtype.contains("UTILITY");
-                                // Only use the window type hint, not WM_TRANSIENT_FOR alone.
-                                // Transient-for is unreliable: some apps (e.g. Houdini)
-                                // set WM_TRANSIENT_FOR on their main window (transient to the
-                                // splash screen). The window type hint is the reliable signal
-                                // that a window is actually a dialog/utility child.
-                                if is_dialog {
-                                    window.has_parent = true;
-                                    xprop_parent_changed = true;
-                                    eprintln!(
-                                        "[xprop] id={} (app_id={:?}) detected XWayland dialog/utility window",
-                                        window.id, window.app_id
-                                    );
-                                }
-                            }
-                            window.needs_xprop_check = false;
-                        } else if window.xprop_check_attempts >= max_attempts {
-                            // Give up — xprop file never appeared (native Wayland window?)
-                            window.needs_xprop_check = false;
-                            eprintln!(
-                                "[xprop] id={} (app_id={:?}) giving up after {} attempts",
-                                window.id, window.app_id, max_attempts
-                            );
-                        }
-                    }
-                    // If any window gained has_parent, trigger a re-manage so
-                    // assign_window_modes picks it up on the next cycle.
-                    if xprop_parent_changed {
-                        state.manage_dirty();
-                    }
-                }
-
-                // Apply persisted state from ~/.cache/cce_client_state on first ManageStart.
-                // This restores window tag assignments, active_tags, tag_layouts, and
-                // locked tiling modes from the previous session. Must run before
-                // assign_window_modes so restored tags/modes take effect.
-                //
-                // We retry across multiple ManageStart cycles because window metadata
-                // (identifier, app_id, title) arrives as separate events AFTER the
-                // Window creation event. On the first ManageStart, these fields may
-                // still be None, so matching would fail. We keep needs_state_restore
-                // true until we've had at least one window with metadata (or after
-                // 5 cycles, giving up gracefully).
-                if state.wm.needs_state_restore {
-                    if let Some(pstate) = crate::state::read_state() {
-                        // Check if any window has metadata yet
-                        let has_metadata = state.wm.windows.iter().any(|w| {
-                            w.identifier.is_some() || w.app_id.is_some()
-                        });
-                        if has_metadata {
-                            crate::state::apply_state(&mut state.wm, &pstate);
-                            state.wm.needs_state_restore = false;
-                            eprintln!("[state] restored state on ManageStart (windows have metadata)");
-                        } else {
-                            // Increment a counter; give up after 5 cycles
-                            state.wm.state_restore_attempts += 1;
-                            if state.wm.state_restore_attempts >= 5 {
-                                // No windows with metadata yet — probably a fresh start
-                                // with no existing windows. Apply global state only
-                                // (active_tags, tag_layouts) and stop retrying.
-                                state.wm.active_tags = pstate.active_tags;
-                                for (idx, mode, has) in &pstate.tag_layouts {
-                                    if *idx < crate::types::NUM_TAGS {
-                                        state.wm.tag_layouts[*idx] = *mode;
-                                        state.wm.has_tag_layout[*idx] = *has;
-                                    }
-                                }
-                                state.wm.needs_state_restore = false;
-                                eprintln!("[state] no windows with metadata after 5 cycles, applied global state only");
-                            }
-                        }
-                    } else {
-                        // No state file — fresh start
-                        state.wm.needs_state_restore = false;
-                    }
-                }
-
-                // Assign tiling modes to windows based on mode_rules / tag_layouts / global_layout.
-                // Must happen before manage_windows so tiling computation uses the correct modes.
-                crate::wm::assign_window_modes(&mut state.wm);
-
-                // Window management: set position + propose dimensions.
-                // These modify window management state and can ONLY be called
-                // during a manage sequence (per River protocol spec).
-                crate::wm::manage_windows(state, qhandle);
-
-                // Auto-focus: new windows on the current tags get keyboard focus.
-                // This runs after manage_windows so the new window is tiled and
-                // has a valid position/proxy, but before focus application so
-                // needs_focus will trigger the actual focus_window() call.
-                {
-                    let active_tags = state.wm.active_tags;
-                    // Find the last new window on active tags (cascade-front).
-                    // Multiple new windows can appear in one manage cycle (e.g.
-                    // spawning several apps at once); focusing the last one is
-                    // consistent with FocusNext and WindowInteraction which also
-                    // pick visible_ids.last().
-                    let new_focused_id = state
-                        .wm
-                        .windows
-                        .iter()
-                        .filter(|w| {
-                            w.is_new
-                                && !w.closed
-                                && (w.tags & active_tags) != 0
-                                && w.app_id.as_deref() != Some("cce-status-interface")
-                                && w.app_id.as_deref() != Some("clear-notification-daemon")
-                                && w.app_id.as_deref() != Some("cce-notification-daemon")
-                        })
-                        .map(|w| w.id)
-                        .last();
-                    if let Some(new_id) = new_focused_id {
-                        for seat in &mut state.wm.seats {
-                            if seat.removed {
-                                continue;
-                            }
-                            seat.focused_window_id = Some(new_id);
-                        }
-                        // Move to cascade-front position (last in windows vec)
-                        state.wm.move_window_to_end(new_id);
-                        state.wm.needs_focus = true;
-                        state.wm.needs_status_update = true;
-                        let app_id = state.wm.get_window(new_id).and_then(|w| w.app_id.clone());
-                        eprintln!(
-                            "[focus] auto-focusing new window id={} (app_id={:?})",
-                            new_id, app_id
-                        );
-                    }
-                    // Clear is_new on all windows once metadata is resolved, or after a timeout
-                    for window in &mut state.wm.windows {
-                        if window.app_id.is_none() && window.title.is_none() && window.metadata_check_attempts < 10 {
-                            window.metadata_check_attempts += 1;
-                            continue;
-                        }
-                        window.is_new = false;
-                    }
-                }
-
-                // Focus management: focus the focused window on each seat.
-                // focus_window() modifies window management state.
-                // Only call when needs_focus is set to avoid stealing focus from
-                // layer-shell clients (like fuzzel) that trigger manage sequences.
-                if state.wm.needs_focus {
-                    for seat in &state.wm.seats {
-                        if seat.removed {
-                            continue;
-                        }
-                        if let Some(focused_id) = seat.focused_window_id {
-                            if let Some((_id, sp)) =
-                                state.seat_proxies.iter().find(|(id, _)| *id == seat.id)
-                            {
-                                if let Some(wp) = state.get_window_proxy(focused_id) {
-                                    let app_id = state
-                                        .wm
-                                        .get_window(focused_id)
-                                        .and_then(|w| w.app_id.clone());
-                                    eprintln!(
-                                        "[focus] calling focus_window for id={} (app_id={:?})",
-                                        focused_id, app_id
-                                    );
-                                    sp.river_seat.focus_window(&wp.river_window);
-                                }
-                            }
-                        } else {
-                            // No focused window — clear focus
-                            if let Some((_id, sp)) =
-                                state.seat_proxies.iter().find(|(id, _)| *id == seat.id)
-                            {
-                                eprintln!("[focus] calling clear_focus");
-                                sp.river_seat.clear_focus();
-                            }
-                        }
-                    }
-                    state.wm.needs_focus = false;
-                }
-
-                if state.pointer_op_release_pending {
-                    state.pointer_op_release_pending = false;
-                    if let Some(ref op) = state.active_pointer_op {
-                        if op.op_type != PointerOpType::Move {
-                            if let Some(wp) = state.get_window_proxy(op.window_id) {
-                                wp.river_window.inform_resize_end();
-                            }
-                        }
-                    }
-                    state.active_pointer_op = None;
-                    for (_sid, sp) in &state.seat_proxies {
-                        sp.river_seat.op_end();
-                    }
-                    for seat in &mut state.wm.seats {
-                        seat.interacted_window_id = None;
-                    }
-                    state.wm.needs_render = true;
-                }
-
-                // Execute pending actions from key/pointer bindings (tinyrwm pattern).
-                // Like tinyrwm, we defer action execution to ManageStart so all
-                // state mutations happen during the manage sequence.
-                // Collect pending actions first to avoid borrow checker issues
-                // (execute_action borrows state mutably).
-                let pending: Vec<(u64, Action, Option<String>)> = state
-                    .wm
-                    .seats
-                    .iter_mut()
-                    .filter_map(|seat| {
-                        if seat.pending_action != Action::None {
-                            let action = seat.pending_action;
-                            let command = seat.pending_command.take();
-                            seat.pending_action = Action::None;
-                            Some((seat.id, action, command))
-                        } else {
-                            None
-                        }
-                    })
-                    .collect();
-                let has_pending = !pending.is_empty();
-                for (seat_id, action, command) in pending {
-                    execute_action(state, seat_id, &action, command.as_deref());
-                }
-
-                if has_pending {
-                    state.manage_dirty();
-                }
-
-                wm_proxy.manage_finish();
-                // Flush immediately so River can start the configure/render cycle
-                // without waiting for our blocking_dispatch to complete.
-                if let Err(e) = conn.flush() {
-                    eprintln!("[manage] FATAL: flush after manage_finish failed: {:?}", e);
-                }
-                state.wm.in_manage_sequence = false;
-                // eprintln!("[manage] ManageStart done in {:?}", ms_start.elapsed());
-                // NOTE: Do NOT call update_status_files() here — it calls
-                // pkill with .output() which blocks the event loop.
-            }
-
-            river_window_manager_v1::Event::RenderStart => {
-                state.render_count += 1;
-                /*
-                eprintln!(
-                    "[render] RenderStart #{} needs_render={}",
-                    state.render_count, state.wm.needs_render
-                );
-                */
-
-                // Show/hide windows based on tag visibility.
-                // This is rendering state and must happen during a render sequence.
-                let active_tags = state.wm.active_tags;
-                for window in &state.wm.windows {
-                    if window.closed {
-                        continue;
-                    }
-                    let visible = (window.tags & active_tags) != 0;
-                    if let Some(wp) = state.get_window_proxy(window.id) {
-                        if visible {
-                            wp.river_window.show();
-                        } else {
-                            wp.river_window.hide();
-                        }
-                    }
-                }
-
-                if state.wm.needs_render {
-                    // Only do rendering state here: borders, place_top/place_bottom.
-                    // set_position and propose_dimensions are window management state
-                    // and must happen during ManageStart.
-                    crate::wm::render_borders(state);
-                    crate::wm::render_opacity(state);
-                    crate::wm::render_circular(state);
-                    crate::wm::render_blur(state);
-
-                    // Update and render window title decorations on borders
-                    crate::decorations::update_decorations(state, qhandle);
-
-                    // Raise and stack windows according to z-axis logic:
-                    // 1. cce-status-interface at the absolute bottom (score = 0)
-                    // 2. Unfocused tiled/fullscreen windows (score = 1)
-                    // 3. Focused tiled/fullscreen window (score = 2)
-                    // 4. Unfocused floating windows (score = 3)
-                    // 5. Focused floating window (score = 4)
-                    let active_tags = state.wm.active_tags;
-                    let focused_id = state.wm.seats.iter().find(|s| !s.removed).and_then(|s| s.focused_window_id);
-
-                    let has_fullscreen = state.wm.windows.iter().any(|w| {
-                        w.tiling_mode == crate::types::TilingMode::Fullscreen
-                            && (w.tags & active_tags) != 0
-                            && !w.closed
-                            && !w.minimized
-                            && w.app_id.as_deref() != Some("cce-status-interface")
-                    });
-
-                    let get_window_score = |win: &crate::types::Window| -> i32 {
-                        if win.app_id.as_deref() == Some("cce-status-interface") {
-                            if has_fullscreen {
-                                0
-                            } else {
-                                5
-                            }
-                        } else if win.tiling_mode == crate::types::TilingMode::Popup {
-                            10
-                        } else if win.tiling_mode == crate::types::TilingMode::SidePanel {
-                            if state.wm.layout.side_panel_behavior == "above" {
-                                if Some(win.id) == focused_id {
-                                    8
-                                } else {
-                                    6
-                                }
-                            } else {
-                                if Some(win.id) == focused_id {
-                                    4
-                                } else {
-                                    2
-                                }
-                            }
-                        } else if win.tiling_mode != crate::types::TilingMode::Floating {
-                            if Some(win.id) == focused_id {
-                                4
-                            } else {
-                                2
-                            }
-                        } else {
-                            if Some(win.id) == focused_id {
-                                8
-                            } else {
-                                6
-                            }
-                        }
-                    };
-
-                    let mut nodes_to_place: Vec<(i32, usize, u64, Option<String>, &RiverNodeV1)> = Vec::new();
-                    for &(wid, ref node) in &state.window_nodes {
-                        if let Some((idx, win)) = state.wm.windows.iter().enumerate().find(|(_, w)| w.id == wid) {
-                            if win.closed {
-                                continue;
-                            }
-                            let visible = (win.tags & active_tags) != 0;
-                            if visible {
-                                let score = get_window_score(win);
-                                nodes_to_place.push((score, idx, win.id, win.app_id.clone(), node));
-                            }
-                        }
-                    }
-
-                    // Sort ascending by score, then by original window list index
-                    nodes_to_place.sort_by_key(|&(score, idx, _, _, _)| (score, idx));
-
-                    for &(score, _, id, ref app_id, node) in &nodes_to_place {
-                        /*
-                        eprintln!(
-                            "[render] placing node id={} (app_id={:?}) at top with score {}",
-                            id, app_id, score
-                        );
-                        */
-                        node.place_top();
-                    }
-
-                    state.wm.needs_render = false;
-                }
-
-                wm_proxy.render_finish();
-                if state.wm.animating {
-                    state.manage_dirty();
-                }
-                // Flush immediately so River receives render_finish without waiting
-                // for blocking_dispatch to complete. River has a 3-second unresponsive
-                // timeout, and if we don't flush promptly, River will kill us.
-                if let Err(e) = conn.flush() {
-                    eprintln!(
-                        "[render] FATAL: flush after render_finish #{} failed: {:?}",
-                        state.render_count, e
-                    );
-                }
-                // eprintln!("[render] render_finish #{} flushed", state.render_count);
-                // Spawn startup apps inside the callback, like tinyrwm does.
-                // Spawning between blocking_dispatch calls corrupts the Wayland
-                // connection state because the fork inherits the socket fd.
-                if !state.wm.startup_spawned {
-                    let apps: Vec<String> = state.wm.pending_startup_apps.drain(..).collect();
-                    for cmd in &apps {
-                        eprintln!("[init] spawning startup app: {}", cmd);
-                        crate::config::spawn_command_bg(cmd);
-                    }
-                    state.wm.startup_spawned = true;
-                }
-
-                // Write status files after all manage+render state is settled.
-                // Uses the deferred flag pattern: handlers set needs_status_update,
-                // we check it here inside the Dispatch callback.
-                // write_status_files() does only file I/O — no fork, no pkill.
-                if state.wm.needs_status_update {
-                    crate::status::write_status_files(&state.wm);
-
-                    // Persist state to ~/.cache/cce_client_state for restart recovery.
-                    // Safe: just file I/O, no fork, no blocking.
-                    crate::state::write_state(&state.wm);
-
-                    // Push the same data through the status socket so waybar
-                    // gets updates in real-time without needing signal-based pkill.
-                    if let Some(ref sender) = state.status_sender {
-                        let update = crate::status_server::build_status_update(&state.wm);
-                        sender.send(update);
-                    }
-
-                    state.wm.needs_status_update = false;
-                }
-
-                // Re-apply input config if it was changed via IPC
-                if !state.wm.tap_config_applied && !state.libinput_devices.is_empty() {
-                    crate::wayland::apply_input_config(state, qhandle);
-                }
-
-                if !state.wm.cursor_theme_applied {
-                    let theme = state.wm.cursor_theme.clone().unwrap_or_else(|| "default".to_string());
-                    let size = state.wm.cursor_size.unwrap_or(24);
-                    for (_, sp) in &state.seat_proxies {
-                        sp.river_seat.set_xcursor_theme(theme.clone(), size);
-                    }
-                    state.wm.cursor_theme_applied = true;
-                }
-            }
-
-            // Window event: field is `id` (the new RiverWindowV1 proxy)
-            river_window_manager_v1::Event::Window { id: river_window } => {
-                let already_tracked = state.window_proxies.iter().any(|(_, wp)| {
-                    wp.river_window.id().protocol_id() == river_window.id().protocol_id()
-                });
-                if already_tracked {
-                    eprintln!("wm_handle_window: duplicate skipped");
-                    return;
-                }
-
-                let id = state.alloc_id();
-                let mut window = Window::default();
-                window.id = id;
-                window.is_new = true;
-                window.tags = state.wm.active_tags;
-
-                state.wm.windows.push(window);
-                state
-                    .window_proxies
-                    .push((id, WindowProxy {
-                        river_window,
-                        decoration: None,
-                        dec_left: None,
-                        dec_right: None,
-                        dec_bottom: None,
-                    }));
-                eprintln!("[window] new window id={} (app_id pending)", id);
-            }
-
-            // Output event: field is `id` (the new RiverOutputV1 proxy)
-            river_window_manager_v1::Event::Output { id: river_output } => {
-                let already_tracked = state.output_proxies.iter().any(|(_, op)| {
-                    op.river_output.id().protocol_id() == river_output.id().protocol_id()
-                });
-                if already_tracked {
-                    eprintln!("wm_handle_output: duplicate skipped");
-                    return;
-                }
-
-                let id = state.alloc_id();
-                let mut output = Output::default();
-                output.id = id;
-
-                let lso = if let Some(ref ls) = state.layer_shell {
-                    Some(ls.get_output(&river_output, qhandle, ()))
-                } else {
-                    None
-                };
-
-                state.wm.outputs.push(output);
-                update_screen_bounds(&state.wm.outputs);
-                state.output_proxies.push((
-                    id,
-                    OutputProxy {
-                        river_output,
-                        layer_shell_output: lso,
-                    },
-                ));
-                eprintln!("wm_handle_output: new output id={}", id);
-            }
-
-            // Seat event: field is `id` (the new RiverSeatV1 proxy)
-            river_window_manager_v1::Event::Seat { id: river_seat } => {
-                let already_tracked = state.seat_proxies.iter().any(|(_, sp)| {
-                    sp.river_seat.id().protocol_id() == river_seat.id().protocol_id()
-                });
-                if already_tracked {
-                    return;
-                }
-
-                let id = state.alloc_id();
-                let mut seat = Seat::default();
-                seat.id = id;
-                seat.is_new = true;
-
-                let cursor_theme = state.wm.cursor_theme.clone().unwrap_or_else(|| "default".to_string());
-                let cursor_size = state.wm.cursor_size.unwrap_or(24);
-                river_seat.set_xcursor_theme(cursor_theme, cursor_size);
-
-                state.wm.seats.push(seat);
-                state.seat_proxies.push((
-                    id,
-                    SeatProxy {
-                        river_seat,
-                        xkb_bindings_seat: None,
-                        wl_seat: None,
-                        wl_pointer: None,
-                        cursor_shape_device: None,
-                        last_pointer_enter_serial: 0,
-                    },
-                ));
-                eprintln!("wm_handle_seat: new seat id={}", id);
-            }
-
-            river_window_manager_v1::Event::SessionLocked => {}
-            river_window_manager_v1::Event::SessionUnlocked => {}
-
-            _ => {}
-        }
-    }
-}
-
-// --- RiverWindowV1 events ---
-
-impl Dispatch<RiverWindowV1, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &RiverWindowV1,
-        event: river_window_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        let wid = match state.window_id_for_proxy(proxy) {
-            Some(id) => id,
-            None => return,
-        };
-
-        match event {
-            river_window_v1::Event::Closed => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.closed = true;
-                    eprintln!("window id={} (app_id={:?}) closed", wid, window.app_id);
-                }
-            }
-
-            river_window_v1::Event::Dimensions { width, height } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.committed_width = width;
-                    window.committed_height = height;
-                    let changed = window.width != width || window.height != height;
-                    if changed {
-                        eprintln!(
-                            "[window] id={} (app_id={:?}) Event::Dimensions: {}x{} (was {}x{})",
-                            wid, window.app_id, width, height, window.width, window.height
-                        );
-                        window.width = width;
-                        window.height = height;
-                        state.wm.needs_render = true;
-                        state.manage_dirty();
-                    }
-                }
-            }
-
-            river_window_v1::Event::AppId { app_id } => {
-                let mut re_eval = false;
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    if window.app_id != app_id {
-                        eprintln!(
-                            "[window] id={} app_id: {:?} -> {:?}",
-                            wid, window.app_id, app_id
-                        );
-                        window.app_id = app_id;
-                        re_eval = !window.mode_locked;
-
-                        // Check if this is a blank steam_proton helper window
-                        let is_proton = window.app_id.as_deref() == Some("steam_proton");
-                        let is_blank = window.title.is_none() || window.title.as_deref().map_or(true, |t| t.is_empty());
-                        if is_proton && is_blank {
-                            if window.tags != 0 {
-                                window.tags = 0;
-                                re_eval = true;
-                            }
-                        }
-                    }
-                }
-                if re_eval {
-                    state.wm.needs_render = true;
-                    state.manage_dirty();
-                }
-            }
-
-            river_window_v1::Event::Title { title } => {
-                let mut re_eval = false;
-                let mut needs_render = false;
-                let active_tags = state.wm.active_tags;
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    if window.title != title {
-                        window.title = title;
-                        needs_render = true;
-
-                        // Check if we need to update/restore tags based on title
-                        let is_proton = window.app_id.as_deref() == Some("steam_proton");
-                        let is_blank = window.title.is_none() || window.title.as_deref().map_or(true, |t| t.is_empty());
-                        if is_proton {
-                            if is_blank {
-                                if window.tags != 0 {
-                                    window.tags = 0;
-                                    re_eval = true;
-                                }
-                            } else {
-                                if window.tags == 0 {
-                                    window.tags = active_tags;
-                                    re_eval = true;
-                                }
-                            }
-                        }
-                    }
-                }
-                if needs_render {
-                    state.wm.needs_render = true;
-                }
-                if re_eval {
-                    state.manage_dirty();
-                }
-            }
-
-            river_window_v1::Event::DecorationHint { hint } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    // Store the raw u32 value from the WEnum
-                    window.decoration_hint = hint.into();
-                }
-            }
-
-            river_window_v1::Event::PresentationHint { hint } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.presentation_hint = hint.into();
-                }
-            }
-
-            river_window_v1::Event::Identifier { identifier } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.identifier = Some(identifier);
-                }
-            }
-
-            river_window_v1::Event::UnreliablePid { unreliable_pid } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.pid = unreliable_pid as u32;
-                    // Spawn an async xprop check for XWayland parent detection.
-                    // River doesn't forward WM_TRANSIENT_FOR for XWayland windows,
-                    // so we check via xdotool + xprop as a fallback.
-                    // The script writes results to /tmp/cce-client-xprop-{wid} which
-                    // is read on the next ManageStart cycle.
-                    if !window.has_parent && window.pid > 0 {
-                        let pid = window.pid;
-                        let id = window.id;
-                        let path = crate::paths::get_xprop_path(id);
-                        let cmd = format!(
-                            "for xid in $(xdotool search --pid {pid} 2>/dev/null); do \
-                             t=$(xdotool getwindowname $xid 2>/dev/null); \
-                             wt=$(xprop -id $xid _NET_WM_WINDOW_TYPE 2>/dev/null); \
-                             printf '%s|%s\\n' \"$t\" \"$wt\"; \
-                             done > {path}",
-                            pid = pid,
-                            path = path
-                        );
-                        crate::config::spawn_command_bg(&cmd);
-                        window.needs_xprop_check = true;
-                        eprintln!(
-                            "[window] id={} (app_id={:?}) spawned xprop check for pid={}",
-                            wid, window.app_id, pid
-                        );
-                    }
-                }
-            }
-
-            river_window_v1::Event::DimensionsHint {
-                min_width,
-                min_height,
-                max_width,
-                max_height,
-            } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.hint_min_width = min_width;
-                    window.hint_min_height = min_height;
-                    window.hint_max_width = max_width;
-                    window.hint_max_height = max_height;
-                    eprintln!(
-                        "[window] id={} (app_id={:?}) dimensions_hint: min={}x{} max={}x{}",
-                        wid, window.app_id, min_width, min_height, max_width, max_height
-                    );
-
-                    if !window.size_hint_applied && min_width > 32 {
-                        window.size_hint_applied = true;
-                        state.wm.needs_render = true;
-                        state.manage_dirty();
-                    }
-                }
-            }
-
-            river_window_v1::Event::Parent { parent } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    let had_parent = window.has_parent;
-                    match &parent {
-                        Some(parent_proxy) => {
-                            window.has_parent = true;
-                            // Look up our internal ID for the parent proxy
-                            let parent_id = state
-                                .window_proxies
-                                .iter()
-                                .find(|(_, wp)| {
-                                    wp.river_window.id().protocol_id()
-                                        == parent_proxy.id().protocol_id()
-                                })
-                                .map(|(id, _)| *id);
-                            window.parent_id = parent_id;
-                            eprintln!(
-                                "[window] id={} (app_id={:?}) has parent (internal_id={:?})",
-                                wid, window.app_id, parent_id
-                            );
-                        }
-                        None => {
-                            window.has_parent = false;
-                            window.parent_id = None;
-                        }
-                    }
-                    // Parent status changed: re-assign mode (child windows float)
-                    if window.has_parent != had_parent && !window.mode_locked {
-                        state.manage_dirty();
-                    }
-                }
-            }
-
-            river_window_v1::Event::FullscreenRequested { .. } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.fullscreen_requested = true;
-                    eprintln!(
-                        "[window] id={} (app_id={:?}) requested fullscreen",
-                        wid, window.app_id
-                    );
-                    state.manage_dirty();
-                }
-            }
-
-            river_window_v1::Event::ExitFullscreenRequested { .. } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.fullscreen_requested = false;
-                    eprintln!(
-                        "[window] id={} (app_id={:?}) requested exit fullscreen",
-                        wid, window.app_id
-                    );
-                    state.manage_dirty();
-                }
-            }
-
-            river_window_v1::Event::MaximizeRequested { .. } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.maximize_requested = true;
-                    eprintln!(
-                        "[window] id={} (app_id={:?}) requested maximize",
-                        wid, window.app_id
-                    );
-                }
-            }
-
-            river_window_v1::Event::UnmaximizeRequested { .. } => {
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.maximize_requested = false;
-                    eprintln!(
-                        "[window] id={} (app_id={:?}) requested unmaximize",
-                        wid, window.app_id
-                    );
-                }
-            }
-
-            river_window_v1::Event::MinimizeRequested { .. } => {
-                let mut minimized_id = None;
-                if let Some(window) = state.wm.get_window_mut(wid) {
-                    window.minimize_requested = true;
-                    window.minimized = true;
-                    minimized_id = Some(wid);
-                    eprintln!(
-                        "[window] id={} (app_id={:?}) requested minimize",
-                        wid, window.app_id
-                    );
-                }
-
-                if let Some(wid) = minimized_id {
-                    // Shift focus to the next visible window if the minimized window was focused
-                    if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                        if seat.focused_window_id == Some(wid) {
-                            let active_tags = state.wm.active_tags;
-                            let visible_ids: Vec<u64> = state.wm
-                                .windows
-                                .iter()
-                                .filter(|w| (w.tags & active_tags) != 0 && !w.closed && !w.minimized && w.id != wid && w.app_id.as_deref() != Some("cce-status-interface"))
-                                .map(|w| w.id)
-                                .collect();
-                            seat.focused_window_id = visible_ids.last().copied();
-                        }
-                    }
-                }
-
-                state.wm.needs_render = true;
-                state.wm.needs_focus = true;
-                state.wm.needs_status_update = true;
-                state.manage_dirty();
-            }
-
-            river_window_v1::Event::ShowWindowMenuRequested { x, y } => {
-                eprintln!(
-                    "[window] id={} show_window_menu_requested at ({}, {}) — ignored",
-                    wid, x, y
-                );
-            }
-
-            river_window_v1::Event::PointerMoveRequested { seat } => {
-                if let Some(_sid) = state.seat_id_for_proxy(&seat) {
-                    let mut window_found = false;
-                    let mut needs_render = false;
-                    if let Some(win) = state.wm.get_window_mut(wid) {
-                        if win.tiling_mode != TilingMode::Fullscreen && win.tiling_mode != TilingMode::Popup {
-                            if win.tiling_mode != TilingMode::Floating {
-                                win.tiling_mode = TilingMode::Floating;
-                                needs_render = true;
-                            }
-                            win.mode_locked = true;
-                            window_found = true;
-                        }
-                    }
-                    if window_found {
-                        if needs_render {
-                            state.wm.needs_render = true;
-                            state.wm.needs_focus = true;
-                            state.wm.needs_status_update = true;
-                        }
-                        seat.op_start_pointer();
-                        if let Some(win) = state.wm.get_window_mut(wid) {
-                            win.anim_x = None;
-                            win.anim_y = None;
-                            win.anim_w = None;
-                            win.anim_h = None;
-                            win.anim_opacity = None;
-                            state.active_pointer_op = Some(PointerOp {
-                                window_id: wid,
-                                op_type: PointerOpType::Move,
-                                start_x: win.x,
-                                start_y: win.y,
-                                start_width: win.width,
-                                start_height: win.height,
-                            });
-                        }
-                    }
-                }
-            }
-
-            river_window_v1::Event::PointerResizeRequested { seat, edges } => {
-                if let Some(_sid) = state.seat_id_for_proxy(&seat) {
-                    let edges_u32: u32 = edges.into();
-                    let op_type = match edges_u32 {
-                        1 => PointerOpType::ResizeTop,
-                        2 => PointerOpType::ResizeBottom,
-                        4 => PointerOpType::ResizeLeft,
-                        8 => PointerOpType::ResizeRight,
-                        5 => PointerOpType::ResizeTopLeft,
-                        9 => PointerOpType::ResizeTopRight,
-                        6 => PointerOpType::ResizeBottomLeft,
-                        10 => PointerOpType::ResizeBottomRight,
-                        _ => PointerOpType::Resize,
-                    };
-
-                    let mut window_found = false;
-                    let mut needs_render = false;
-                    let side_panel_pos = state.wm.layout.side_panel_position.clone();
-                    if let Some(win) = state.wm.get_window_mut(wid) {
-                        if win.tiling_mode != TilingMode::Fullscreen && win.tiling_mode != TilingMode::Popup {
-                            let is_valid_side_panel_resize = win.tiling_mode == TilingMode::SidePanel
-                                && if side_panel_pos == "right" {
-                                    matches!(
-                                        op_type,
-                                        PointerOpType::Resize
-                                            | PointerOpType::ResizeLeft
-                                            | PointerOpType::ResizeTopLeft
-                                            | PointerOpType::ResizeBottomLeft
-                                    )
-                                } else {
-                                    matches!(
-                                        op_type,
-                                        PointerOpType::Resize
-                                            | PointerOpType::ResizeRight
-                                            | PointerOpType::ResizeTopRight
-                                            | PointerOpType::ResizeBottomRight
-                                    )
-                                };
-                            if !is_valid_side_panel_resize && win.tiling_mode != TilingMode::Floating {
-                                win.tiling_mode = TilingMode::Floating;
-                                needs_render = true;
-                            }
-                            win.mode_locked = true;
-                            window_found = true;
-                        }
-                    }
-                    if window_found {
-                        if needs_render {
-                            state.wm.needs_render = true;
-                            state.wm.needs_focus = true;
-                            state.wm.needs_status_update = true;
-                        }
-                        seat.op_start_pointer();
-                        if let Some(win) = state.wm.get_window_mut(wid) {
-                            win.anim_x = None;
-                            win.anim_y = None;
-                            win.anim_w = None;
-                            win.anim_h = None;
-                            win.anim_opacity = None;
-                            
-                            state.active_pointer_op = Some(PointerOp {
-                                window_id: wid,
-                                op_type,
-                                start_x: win.x,
-                                start_y: win.y,
-                                start_width: win.width,
-                                start_height: win.height,
-                            });
-                        }
-                    }
-                }
-            }
-
-            _ => {}
-        }
-    }
-}
-
-// --- RiverSeatV1 events ---
-
-impl Dispatch<RiverSeatV1, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &RiverSeatV1,
-        event: river_seat_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        qhandle: &QueueHandle<Self>,
-    ) {
-        let sid = match state.seat_id_for_proxy(proxy) {
-            Some(id) => id,
-            None => return,
-        };
-
-        match event {
-            river_seat_v1::Event::Removed => {
-                if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == sid) {
-                    seat.removed = true;
-                }
-            }
-
-            river_seat_v1::Event::WindowInteraction {
-                window: river_window,
-            } => {
-                if let Some(wid) = state.window_id_for_proxy(&river_window) {
-                    let target_app_id = state.wm.get_window(wid).and_then(|w| w.app_id.clone());
-                    if target_app_id.as_deref() == Some("cce-status-interface") {
-                        // Do not focus status bar!
-                        return;
-                    }
-                    if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == sid) {
-                        eprintln!(
-                            "[focus] WindowInteraction: seat={} focused_window_id={} -> {} (app_id={:?})",
-                            sid,
-                            seat.focused_window_id.unwrap_or(0),
-                            wid,
-                            target_app_id
-                        );
-                        let already_focused = seat.focused_window_id == Some(wid);
-                        seat.focused_window_id = Some(wid);
-                        // Move clicked window to front of cascade stack
-                        let moved = state.wm.move_window_to_end(wid);
-
-                        // Disable expose if it was active
-                        let mut expose_changed = false;
-                        if state.wm.expose_active {
-                            crate::wm::set_expose_active(&mut state.wm, false);
-                            expose_changed = true;
-                        }
-
-                        state.wm.needs_render = true;
-                        state.wm.needs_focus = true;
-                        state.wm.needs_status_update = true;
-
-                        if !already_focused || moved || expose_changed {
-                            state.manage_dirty();
-                        }
-                    }
-                }
-            }
-
-            river_seat_v1::Event::PointerEnter {
-                window: river_window,
-            } => {
-                if let Some(wid) = state.window_id_for_proxy(&river_window) {
-                    if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == sid) {
-                        seat.hovered_window_id = Some(wid);
-
-                        if state.wm.expose_active {
-                            let already_focused = seat.focused_window_id == Some(wid);
-                            seat.focused_window_id = Some(wid);
-
-                            state.wm.needs_focus = true;
-                            state.wm.needs_render = true;
-                            state.wm.needs_status_update = true;
-
-                            if !already_focused {
-                                state.manage_dirty();
-                            }
-                        }
-                    }
-                }
-            }
-
-            river_seat_v1::Event::PointerLeave => {
-                if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == sid) {
-                    seat.hovered_window_id = None;
-                }
-            }
-
-            river_seat_v1::Event::WlSeat { name } => {
-                if let Some(ref registry) = state.registry {
-                    if let Some((_, seat_proxy)) = state.seat_proxies.iter_mut().find(|(_, sp)| sp.river_seat == *proxy) {
-                        if seat_proxy.wl_seat.is_none() {
-                            let wl_seat = registry.bind::<wl_seat::WlSeat, _, _>(name, 2, qhandle, ());
-                            seat_proxy.wl_seat = Some(wl_seat);
-                        }
-                    }
-                }
-            }
-
-            river_seat_v1::Event::OpDelta { dx, dy } => {
-                if let Some(ref op) = state.active_pointer_op {
-                    let wid = op.window_id;
-                    let bw = state.wm.layout.cascade_border_width;
-                    if let Some(win) = state.wm.get_window_mut(wid) {
-                        match op.op_type {
-                            PointerOpType::Move => {
-                                win.x = op.start_x + dx;
-                                win.y = op.start_y + dy;
-                            }
-                            PointerOpType::Resize | PointerOpType::ResizeRight => {
-                                win.width = std::cmp::max(50, op.start_width + dx);
-                                if win.tiling_mode == TilingMode::SidePanel {
-                                    win.hint_min_width = win.width + bw * 2;
-                                }
-                            }
-                            PointerOpType::ResizeLeft => {
-                                let target_width = std::cmp::max(50, op.start_width - dx);
-                                let actual_dx = op.start_width - target_width;
-                                win.x = op.start_x + actual_dx;
-                                win.width = target_width;
-                                if win.tiling_mode == TilingMode::SidePanel {
-                                    win.hint_min_width = win.width + bw * 2;
-                                }
-                            }
-                            PointerOpType::ResizeBottom => {
-                                win.height = std::cmp::max(50, op.start_height + dy);
-                            }
-                            PointerOpType::ResizeTop => {
-                                let target_height = std::cmp::max(50, op.start_height - dy);
-                                let actual_dy = op.start_height - target_height;
-                                win.y = op.start_y + actual_dy;
-                                win.height = target_height;
-                            }
-                            PointerOpType::ResizeBottomRight => {
-                                win.width = std::cmp::max(50, op.start_width + dx);
-                                win.height = std::cmp::max(50, op.start_height + dy);
-                                if win.tiling_mode == TilingMode::SidePanel {
-                                    win.hint_min_width = win.width + bw * 2;
-                                }
-                            }
-                            PointerOpType::ResizeBottomLeft => {
-                                let target_width = std::cmp::max(50, op.start_width - dx);
-                                let actual_dx = op.start_width - target_width;
-                                win.x = op.start_x + actual_dx;
-                                win.width = target_width;
-                                win.height = std::cmp::max(50, op.start_height + dy);
-                                if win.tiling_mode == TilingMode::SidePanel {
-                                    win.hint_min_width = win.width + bw * 2;
-                                }
-                            }
-                            PointerOpType::ResizeTopLeft => {
-                                let target_width = std::cmp::max(50, op.start_width - dx);
-                                let actual_dx = op.start_width - target_width;
-                                win.x = op.start_x + actual_dx;
-                                win.width = target_width;
-
-                                let target_height = std::cmp::max(50, op.start_height - dy);
-                                let actual_dy = op.start_height - target_height;
-                                win.y = op.start_y + actual_dy;
-                                win.height = target_height;
-                                if win.tiling_mode == TilingMode::SidePanel {
-                                    win.hint_min_width = win.width + bw * 2;
-                                }
-                            }
-                            PointerOpType::ResizeTopRight => {
-                                win.width = std::cmp::max(50, op.start_width + dx);
-
-                                let target_height = std::cmp::max(50, op.start_height - dy);
-                                let actual_dy = op.start_height - target_height;
-                                win.y = op.start_y + actual_dy;
-                                win.height = target_height;
-                                if win.tiling_mode == TilingMode::SidePanel {
-                                    win.hint_min_width = win.width + bw * 2;
-                                }
-                            }
-                        }
-                    }
-                    state.manage_dirty();
-                }
-            }
-            river_seat_v1::Event::OpRelease => {
-                state.pointer_op_release_pending = true;
-                state.manage_dirty();
-            }
-
-            _ => {}
-        }
-    }
-}
-
-// --- RiverOutputV1 events ---
-
-impl Dispatch<RiverOutputV1, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &RiverOutputV1,
-        event: river_output_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        let oid = state
-            .output_proxies
-            .iter()
-            .find(|(_, op)| op.river_output.id() == proxy.id())
-            .map(|(id, _)| *id);
-
-        match event {
-            river_output_v1::Event::WlOutput { name } => {
-                if let Some(oid) = oid {
-                    if let Some(output) = state.wm.outputs.iter_mut().find(|o| o.id == oid) {
-                        output.wl_output_name = Some(name);
-                        // Also try to copy dimensions from WlOutputInfo if already populated
-                        if let Some(info) = state.wl_outputs.iter().find(|info| info.name == name) {
-                            if info.width > 0 && info.height > 0 {
-                                output.width = info.width;
-                                output.height = info.height;
-                                output.x = info.x;
-                                output.y = info.y;
-                                state.wm.needs_render = true;
-                                update_screen_bounds(&state.wm.outputs);
-                                eprintln!(
-                                    "river_output linked to wl_output name={} dimensions (copied): {}x{} at ({},{})",
-                                    name, info.width, info.height, info.x, info.y
-                                );
-                            }
-                        }
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
-// --- wl_output::WlOutput events ---
-
-impl Dispatch<wl_output::WlOutput, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &wl_output::WlOutput,
-        event: wl_output::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        // Find registry name
-        let name = state
-            .wl_outputs
-            .iter()
-            .find(|info| info.wl_output.id() == proxy.id())
-            .map(|info| info.name);
-
-        let Some(name) = name else { return };
-
-        match event {
-            wl_output::Event::Geometry {
-                x,
-                y,
-                ..
-            } => {
-                if let Some(info) = state.wl_outputs.iter_mut().find(|info| info.name == name) {
-                    info.x = x;
-                    info.y = y;
-                }
-                // Also update matched Output in WindowManager
-                if let Some(output) = state.wm.outputs.iter_mut().find(|o| o.wl_output_name == Some(name)) {
-                    if output.x != x || output.y != y {
-                        output.x = x;
-                        output.y = y;
-                        state.wm.needs_render = true;
-                        update_screen_bounds(&state.wm.outputs);
-                    }
-                }
-            }
-            wl_output::Event::Mode {
-                flags,
-                width,
-                height,
-                ..
-            } => {
-                let is_current = match flags {
-                    wayland_client::WEnum::Value(mode) => mode.contains(wl_output::Mode::Current),
-                    _ => false,
-                };
-
-                if is_current {
-                    if let Some(info) = state.wl_outputs.iter_mut().find(|info| info.name == name) {
-                        info.width = width;
-                        info.height = height;
-                    }
-                    // Also update matched Output in WindowManager
-                    if let Some(output) = state.wm.outputs.iter_mut().find(|o| o.wl_output_name == Some(name)) {
-                        if output.width != width || output.height != height {
-                            output.width = width;
-                            output.height = height;
-                            state.wm.needs_render = true;
-                            update_screen_bounds(&state.wm.outputs);
-                            eprintln!(
-                                "wl_output name={} updated current mode dimensions: {}x{}",
-                                name, width, height
-                            );
-                        }
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
-// --- RiverLayerShellV1 (no events) ---
-
-impl Dispatch<RiverLayerShellV1, ()> for AppState {
-    fn event(
-        _state: &mut Self,
-        _proxy: &RiverLayerShellV1,
-        _event: river_layer_shell_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-    }
-}
-
-// --- RiverLayerShellOutputV1 events ---
-
-impl Dispatch<RiverLayerShellOutputV1, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &RiverLayerShellOutputV1,
-        event: river_layer_shell_output_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        let oid = state
-            .output_proxies
-            .iter()
-            .find(|(_, op)| {
-                op.layer_shell_output.as_ref().map_or(false, |lso| {
-                    lso.id().protocol_id() == proxy.id().protocol_id()
-                })
-            })
-            .map(|(id, _)| *id);
-
-        let Some(oid) = oid else { return };
-
-        match event {
-            river_layer_shell_output_v1::Event::NonExclusiveArea {
-                x,
-                y,
-                width,
-                height,
-            } => {
-                if let Some(output) = state.wm.outputs.iter_mut().find(|o| o.id == oid) {
-                    if output.usable_width != width
-                        || output.usable_height != height
-                        || output.usable_x != x
-                        || output.usable_y != y
-                    {
-                        output.usable_x = x;
-                        output.usable_y = y;
-                        output.usable_width = width;
-                        output.usable_height = height;
-                        state.wm.needs_render = true;
-                        eprintln!(
-                            "output id={} usable area: {}x{} at ({},{})",
-                            oid, width, height, x, y
-                        );
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
-// --- RiverInputManagerV1 events ---
-
-impl Dispatch<RiverInputManagerV1, ()> for AppState {
-    event_created_child!(AppState, RiverInputManagerV1, [
-        river_input_manager_v1::EVT_INPUT_DEVICE_OPCODE => (RiverInputDeviceV1, ()),
-    ]);
-
-    fn event(
-        state: &mut Self,
-        _proxy: &RiverInputManagerV1,
-        event: river_input_manager_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_input_manager_v1::Event::InputDevice { id: _input_device } => {
-                use crate::types::InputDevice;
-                let dev = InputDevice { is_keyboard: false };
-                state.wm.input_devices.push(dev);
-                eprintln!("input_device discovered");
-            }
-            river_input_manager_v1::Event::Finished => {}
-            _ => {}
-        }
-    }
-}
-
-// --- RiverInputDeviceV1 events ---
-
-impl Dispatch<RiverInputDeviceV1, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &RiverInputDeviceV1,
-        event: river_input_device_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_input_device_v1::Event::Type { _type: dev_type } => {
-                if let Some(dev) = state.wm.input_devices.last_mut() {
-                    // dev_type is WEnum<Type>; check for Keyboard variant
-                    dev.is_keyboard = matches!(
-                        dev_type,
-                        wayland_client::WEnum::Value(river_input_device_v1::Type::Keyboard)
-                    );
-                }
-            }
-            river_input_device_v1::Event::Name { name } => {
-                eprintln!("input_device name: {}", name);
-                state.input_device_names.insert(proxy.id().protocol_id(), name.clone());
-                if let Some(dev) = state.libinput_devices.iter_mut().find(|d| {
-                    if let Some(ref id) = d.input_device {
-                        id.id().protocol_id() == proxy.id().protocol_id()
-                    } else {
-                        false
-                    }
-                }) {
-                    dev.name = name.clone();
-                    dev.name_received = true;
-                    eprintln!("[libinput] associated name \"{}\" with device", name);
-                }
-                crate::wayland::apply_input_config(state, qhandle);
-            }
-            river_input_device_v1::Event::Removed => {}
-            _ => {}
-        }
-    }
-}
-
-// --- RiverPointerBindingV1 events ---
-
-impl Dispatch<RiverPointerBindingV1, BindingUserData> for AppState {
-    fn event(
-        state: &mut Self,
-        _proxy: &RiverPointerBindingV1,
-        event: river_pointer_binding_v1::Event,
-        data: &BindingUserData,
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_pointer_binding_v1::Event::Pressed => {
-                eprintln!(
-                    "[binding] pointer pressed: action={:?} seat_id={}",
-                    data.action, data.seat_id
-                );
-                if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == data.seat_id) {
-                    seat.pending_action = data.action.clone();
-                    seat.pending_command = data.command.clone();
-                }
-                // Trigger a manage sequence so the pending action is processed
-                state.manage_dirty();
-            }
-            _ => {}
-        }
-    }
-}
-
-// --- XKB bindings ---
-
-impl Dispatch<RiverXkbBindingsV1, ()> for AppState {
-    fn event(
-        _state: &mut Self,
-        _proxy: &RiverXkbBindingsV1,
-        _event: river_xkb_bindings_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-    }
-}
-
-impl Dispatch<RiverXkbBindingV1, BindingUserData> for AppState {
-    fn event(
-        state: &mut Self,
-        _proxy: &RiverXkbBindingV1,
-        event: river_xkb_binding_v1::Event,
-        data: &BindingUserData,
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_xkb_binding_v1::Event::Pressed => {
-                eprintln!(
-                    "[binding] xkb pressed: action={:?} command={:?} seat_id={}",
-                    data.action, data.command, data.seat_id
-                );
-                if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == data.seat_id) {
-                    seat.pending_action = data.action.clone();
-                    seat.pending_command = data.command.clone();
-                }
-                // Trigger a manage sequence so the pending action is processed
-                state.manage_dirty();
-            }
-            river_xkb_binding_v1::Event::Released => {}
-            river_xkb_binding_v1::Event::StopRepeat => {}
-            _ => {}
-        }
-    }
-}
-
-impl Dispatch<RiverXkbBindingsSeatV1, ()> for AppState {
-    fn event(
-        _state: &mut Self,
-        _proxy: &RiverXkbBindingsSeatV1,
-        _event: river_xkb_bindings_seat_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-    }
-}
-
-// --- RiverNodeV1 (no events, used for set_position) ---
-
-impl Dispatch<RiverNodeV1, ()> for AppState {
-    fn event(
-        _state: &mut Self,
-        _proxy: &RiverNodeV1,
-        _event: river_node_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        // river_node_v1 has no events
-    }
-}
-
-// --- Helper functions ---
-
-/// Execute an action triggered by a keybinding or pointer binding.
-fn execute_action(state: &mut AppState, seat_id: u64, action: &crate::types::Action, command: Option<&str>) {
-    use crate::types::Action;
-    match action {
-        Action::None => {}
-        Action::SidePanelLeft => {
-            let focused = state.wm.focused_window();
-            let is_side_panel = focused.map_or(false, |w| w.tiling_mode == TilingMode::SidePanel);
-            if is_side_panel {
-                state.wm.layout.side_panel_position = "left".to_string();
-                state.wm.needs_render = true;
-                state.wm.needs_status_update = true;
-                state.manage_dirty();
-            }
-        }
-        Action::SidePanelRight => {
-            let focused = state.wm.focused_window();
-            let is_side_panel = focused.map_or(false, |w| w.tiling_mode == TilingMode::SidePanel);
-            if is_side_panel {
-                state.wm.layout.side_panel_position = "right".to_string();
-                state.wm.needs_render = true;
-                state.wm.needs_status_update = true;
-                state.manage_dirty();
-            }
-        }
-        Action::Spawn => {
-            if let Some(cmd) = command {
-                eprintln!("spawn: {}", cmd);
-                // Close inherited FDs > 2 in the child so that spawned
-                // Wayland clients (fuzzel, etc.) never accidentally read
-                // from cce-client's Wayland socket fd. This prevents protocol
-                // corruption and the CPU spin loop that results from it.
-                use std::os::unix::process::CommandExt;
-                let _ = unsafe {
-                    std::process::Command::new("sh")
-                        .arg("-c")
-                        .arg(cmd)
-                        .env_remove("WAYLAND_DEBUG")
-                        .stdout(std::process::Stdio::null())
-                        .stderr(std::process::Stdio::null())
-                        .pre_exec(|| {
-                            let max_fd = libc::sysconf(libc::_SC_OPEN_MAX) as libc::c_int;
-                            for fd in 3..max_fd {
-                                libc::close(fd);
-                            }
-                            libc::setsid();
-                            Ok(())
-                        })
-                        .spawn()
-                };
-            }
-        }
-        Action::Toggle => {
-            if let Some(cmd) = command {
-                let prog_name = crate::config::extract_program_name(cmd);
-                // Look for an open window whose app_id (or title fallback) matches prog_name
-                let open_window_id = state.wm.windows.iter().find(|w| {
-                    if w.closed {
-                        return false;
-                    }
-                    if let Some(ref aid) = w.app_id {
-                        let aid_lower = aid.to_lowercase();
-                        let prog_lower = prog_name.to_lowercase();
-                        aid_lower == prog_lower || aid_lower.contains(&prog_lower) || prog_lower.contains(&aid_lower)
-                    } else if let Some(ref title) = w.title {
-                        let title_lower = title.to_lowercase();
-                        let prog_lower = prog_name.to_lowercase();
-                        title_lower.contains(&prog_lower)
-                    } else {
-                        false
-                    }
-                }).map(|w| w.id);
-
-                if let Some(win_id) = open_window_id {
-                    eprintln!("toggle: close window id={}", win_id);
-                    if let Some(wp) = state.get_window_proxy(win_id) {
-                        wp.river_window.close();
-                    }
-                    // Shift focus to the next visible window if the closed window was focused.
-                    if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                        if seat.focused_window_id == Some(win_id) {
-                            let visible_ids: Vec<u64> = state
-                                .wm
-                                .windows
-                                .iter()
-                                .filter(|w| {
-                                    (w.tags & state.wm.active_tags) != 0 && !w.closed && !w.minimized && w.id != win_id && w.app_id.as_deref() != Some("cce-status-interface")
-                                })
-                                .map(|w| w.id)
-                                .collect();
-                            seat.focused_window_id = visible_ids.last().copied();
-                        }
-                    }
-                    state.wm.needs_render = true;
-                    state.wm.needs_focus = true;
-                    state.wm.needs_status_update = true;
-                } else {
-                    eprintln!("toggle: spawn command={}", cmd);
-                    use std::os::unix::process::CommandExt;
-                    let _ = unsafe {
-                        std::process::Command::new("sh")
-                            .arg("-c")
-                            .arg(cmd)
-                            .env_remove("WAYLAND_DEBUG")
-                            .stdout(std::process::Stdio::null())
-                            .stderr(std::process::Stdio::null())
-                            .pre_exec(|| {
-                                let max_fd = libc::sysconf(libc::_SC_OPEN_MAX) as libc::c_int;
-                                for fd in 3..max_fd {
-                                    libc::close(fd);
-                                }
-                                libc::setsid();
-                                Ok(())
-                            })
-                            .spawn()
-                    };
-                }
-            }
-        }
-        Action::Close => {
-            // Ask the compositor to close the focused window by calling
-            // close() on its River protocol proxy. This matches tinyrwm's
-            // approach: close() sends a request to River, which asks the
-            // client to close. When the client actually closes, River sends
-            // Event::Closed, which sets window.closed = true. Then on the
-            // next ManageStart, remove_windows() drops it from the vector.
-            //
-            // close() modifies window management state and can only be called
-            // during a manage sequence — which it is, since execute_action
-            // runs inside ManageStart.
-            if let Some(seat) = state.wm.seats.first() {
-                if let Some(focused_id) = seat.focused_window_id {
-                    // Send the close request to the compositor
-                    if let Some(wp) = state.get_window_proxy(focused_id) {
-                        wp.river_window.close();
-                    }
-                    // Shift focus to the next visible window (excluding the one we just closed).
-                    // The window isn't closed=true yet (that happens when River sends Event::Closed),
-                    // so we exclude it by ID instead.
-                    let visible_ids: Vec<u64> = state
-                        .wm
-                        .windows
-                        .iter()
-                        .filter(|w| {
-                            (w.tags & state.wm.active_tags) != 0 && !w.closed && !w.minimized && w.id != focused_id && w.app_id.as_deref() != Some("cce-status-interface")
-                        })
-                        .map(|w| w.id)
-                        .collect();
-                    let next_id = visible_ids.last().copied();
-                    if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                        seat.focused_window_id = next_id;
-                    }
-                }
-            }
-            state.wm.needs_render = true;
-            state.wm.needs_focus = true;
-            state.wm.needs_status_update = true;
-        }
-        Action::Minimize => {
-            // Get the focused window ID on this seat
-            let focused_id = state.wm.seats.iter().find(|s| s.id == seat_id).and_then(|s| s.focused_window_id);
-            if let Some(focused_id) = focused_id {
-                if let Some(w) = state.wm.get_window_mut(focused_id) {
-                    w.minimized = true;
-                }
-                // Shift focus to the next visible window
-                let active_tags = state.wm.active_tags;
-                let visible_ids: Vec<u64> = state.wm
-                    .windows
-                    .iter()
-                    .filter(|w| (w.tags & active_tags) != 0 && !w.closed && !w.minimized && w.id != focused_id && w.app_id.as_deref() != Some("cce-status-interface"))
-                    .map(|w| w.id)
-                    .collect();
-                let next_id = visible_ids.last().copied();
-                if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == seat_id) {
-                    seat.focused_window_id = next_id;
-                }
-            }
-            state.wm.needs_render = true;
-            state.wm.needs_focus = true;
-            state.wm.needs_status_update = true;
-            state.manage_dirty();
-        }
-        Action::FocusNext => {
-            // Focus the next visible window (wrapping) of the same tiling mode,
-            // and move it to the front of the cascade stack (end of windows vector).
-            if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                let focused_id = seat.focused_window_id;
-                let focused_mode = focused_id.and_then(|fid| {
-                    state.wm.windows.iter().find(|w| w.id == fid).map(|w| w.tiling_mode)
-                });
-                let visible_ids: Vec<u64> = state
-                    .wm
-                    .windows
-                    .iter()
-                    .filter(|w| {
-                        (w.tags & state.wm.active_tags) != 0
-                            && !w.closed
-                            && !w.minimized
-                            && w.app_id.as_deref() != Some("cce-status-interface")
-                            && (focused_mode.is_none() || Some(w.tiling_mode) == focused_mode)
-                    })
-                    .map(|w| w.id)
-                    .collect();
-                if let Some(fid) = focused_id {
-                    if let Some(idx) = visible_ids.iter().position(|id| *id == fid) {
-                        let next_idx = (idx + 1) % visible_ids.len();
-                        let next_id = visible_ids[next_idx];
-                        seat.focused_window_id = Some(next_id);
-                        // Move the newly focused window to the end of the
-                        // windows vector so it gets the front cascade position
-                        // (rightmost/bottommost) and brightest border.
-                        state.wm.move_window_to_end(next_id);
-                        state.wm.needs_render = true;
-                        state.wm.needs_focus = true;
-                        state.wm.needs_status_update = true;
-                    }
-                } else if !visible_ids.is_empty() {
-                    let next_id = visible_ids[visible_ids.len() - 1];
-                    seat.focused_window_id = Some(next_id);
-                    state.wm.move_window_to_end(next_id);
-                    state.wm.needs_render = true;
-                    state.wm.needs_focus = true;
-                    state.wm.needs_status_update = true;
-                }
-            }
-        }
-        Action::FocusPrev => {
-            // Focus the previous visible window (wrapping) of the same tiling mode,
-            // and move it to the front of the cascade stack (end of windows vector).
-            if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                let focused_id = seat.focused_window_id;
-                let focused_mode = focused_id.and_then(|fid| {
-                    state.wm.windows.iter().find(|w| w.id == fid).map(|w| w.tiling_mode)
-                });
-                let visible_ids: Vec<u64> = state
-                    .wm
-                    .windows
-                    .iter()
-                    .filter(|w| {
-                        (w.tags & state.wm.active_tags) != 0
-                            && !w.closed
-                            && !w.minimized
-                            && w.app_id.as_deref() != Some("cce-status-interface")
-                            && (focused_mode.is_none() || Some(w.tiling_mode) == focused_mode)
-                    })
-                    .map(|w| w.id)
-                    .collect();
-                if let Some(fid) = focused_id {
-                    if let Some(idx) = visible_ids.iter().position(|id| *id == fid) {
-                        let prev_idx = if idx == 0 { visible_ids.len() - 1 } else { idx - 1 };
-                        let prev_id = visible_ids[prev_idx];
-                        seat.focused_window_id = Some(prev_id);
-                        // Move the newly focused window to the end of the
-                        // windows vector so it gets the front cascade position
-                        // (rightmost/bottommost) and brightest border.
-                        state.wm.move_window_to_end(prev_id);
-                        state.wm.needs_render = true;
-                        state.wm.needs_focus = true;
-                        state.wm.needs_status_update = true;
-                    }
-                } else if !visible_ids.is_empty() {
-                    let prev_id = visible_ids[visible_ids.len() - 1];
-                    seat.focused_window_id = Some(prev_id);
-                    state.wm.move_window_to_end(prev_id);
-                    state.wm.needs_render = true;
-                    state.wm.needs_focus = true;
-                    state.wm.needs_status_update = true;
-                }
-            }
-        }
-        Action::Move | Action::Resize => {
-            let op_type = if *action == Action::Move {
-                PointerOpType::Move
-            } else {
-                state.pending_pointer_op_type.take().unwrap_or(PointerOpType::Resize)
-            };
-
-            let target_wid = if let Some(seat) = state.wm.seats.iter().find(|s| s.id == seat_id) {
-                seat.interacted_window_id.or(seat.focused_window_id)
-            } else {
-                None
-            };
-
-            if let Some(wid) = target_wid {
-                let mut window_found = false;
-                let mut needs_render = false;
-                let side_panel_pos = state.wm.layout.side_panel_position.clone();
-                if let Some(win) = state.wm.get_window_mut(wid) {
-                    if win.tiling_mode != TilingMode::Fullscreen && win.tiling_mode != TilingMode::Popup {
-                        let is_valid_side_panel_resize = win.tiling_mode == TilingMode::SidePanel
-                            && if side_panel_pos == "right" {
-                                matches!(
-                                    op_type,
-                                    PointerOpType::Resize
-                                        | PointerOpType::ResizeLeft
-                                        | PointerOpType::ResizeTopLeft
-                                        | PointerOpType::ResizeBottomLeft
-                                )
-                            } else {
-                                matches!(
-                                    op_type,
-                                    PointerOpType::Resize
-                                        | PointerOpType::ResizeRight
-                                        | PointerOpType::ResizeTopRight
-                                        | PointerOpType::ResizeBottomRight
-                                )
-                            };
-                        if !is_valid_side_panel_resize && win.tiling_mode != TilingMode::Floating {
-                            win.tiling_mode = TilingMode::Floating;
-                            needs_render = true;
-                        }
-                        win.mode_locked = true;
-                        window_found = true;
-                    }
-                }
-
-                if window_found {
-                    if needs_render {
-                        state.wm.needs_render = true;
-                        state.wm.needs_focus = true;
-                        state.wm.needs_status_update = true;
-                    }
-                    if let Some((_, sp)) = state.seat_proxies.iter().find(|(sid, _)| *sid == seat_id) {
-                        sp.river_seat.op_start_pointer();
-                    }
-                    if op_type != PointerOpType::Move {
-                        if let Some(wp) = state.get_window_proxy(wid) {
-                            wp.river_window.inform_resize_start();
-                        }
-                    }
-                    if let Some(win) = state.wm.get_window_mut(wid) {
-                        win.anim_x = None;
-                        win.anim_y = None;
-                        win.anim_w = None;
-                        win.anim_h = None;
-                        win.anim_opacity = None;
-                        state.active_pointer_op = Some(PointerOp {
-                            window_id: wid,
-                            op_type,
-                            start_x: win.x,
-                            start_y: win.y,
-                            start_width: win.width,
-                            start_height: win.height,
-                        });
-                    }
-                }
-            }
-        }
-        Action::Exit => {
-            state.wm.exit_requested = true;
-            state.exit_requested = true;
-            if let Some(ref wm) = state.window_manager {
-                wm.exit_session();
-            }
-        }
-        Action::Fullscreen => {
-            if let Some(seat) = state.wm.seats.first() {
-                if let Some(focused_id) = seat.focused_window_id {
-                    // Read current state and compute target mode before mutable borrow
-                    let is_fullscreen = state
-                        .wm
-                        .get_window(focused_id)
-                        .map(|w| w.tiling_mode == TilingMode::Fullscreen)
-                        .unwrap_or(false);
-
-                    let resolved_mode = if is_fullscreen {
-                        // Exiting fullscreen: snapshot the window info we need for mode resolution
-                        let (app_id, title, tags, mode_locked) = state
-                            .wm
-                            .get_window(focused_id)
-                            .map(|w| (w.app_id.clone(), w.title.clone(), w.tags, w.mode_locked))
-                            .unwrap_or((None, None, 1, false));
-                        // Build a temporary Window for mode resolution
-                        let temp_win = Window {
-                            id: focused_id,
-                            is_new: false,
-                            closed: false,
-                            tags,
-                            x: 0,
-                            y: 0,
-                            width: 0,
-                            height: 0,
-                            app_id,
-                            title,
-                            identifier: None,
-                            parent_id: None,
-                            has_parent: false,
-                            pid: 0,
-                            hint_min_width: 0,
-                            hint_min_height: 0,
-                            hint_max_width: 0,
-                            hint_max_height: 0,
-                            decoration_hint: 3,
-                            presentation_hint: 0,
-                            fullscreen_requested: false,
-                            maximize_requested: false,
-                            minimize_requested: false,
-                            tiling_mode: TilingMode::Fullscreen,
-                            mode_locked,
-                            xprop_check_attempts: 0,
-                            ..Default::default()
-                        };
-                        crate::wm::get_mode_for_window(&state.wm, &temp_win)
-                            .unwrap_or(state.wm.global_layout)
-                    } else {
-                        TilingMode::Fullscreen
-                    };
-
-                    if let Some(window) = state.wm.get_window_mut(focused_id) {
-                        if is_fullscreen {
-                            window.tiling_mode = resolved_mode;
-                            window.mode_locked = false;
-                        } else {
-                            window.tiling_mode = TilingMode::Fullscreen;
-                            window.mode_locked = true;
-                        }
-                        state.wm.needs_render = true;
-                        state.wm.needs_status_update = true;
-                    }
-                }
-            }
-        }
-        Action::LayoutNext => {
-            let cycle = [
-                TilingMode::Cascade,
-                TilingMode::Grid,
-                TilingMode::Fullscreen,
-            ];
-            // Cycle the layout for the currently active tag(s) only.
-            // Determine the "current" mode from the first active tag's layout
-            // (or global_layout if no tag_layout is set for it), then advance.
-            let active_tags = state.wm.active_tags;
-            let first_tag_bit = (0..crate::types::NUM_TAGS).find(|b| (active_tags & (1u32 << b)) != 0);
-            let current = if let Some(bit) = first_tag_bit {
-                if state.wm.has_tag_layout[bit] {
-                    state.wm.tag_layouts[bit]
-                } else {
-                    state.wm.global_layout
-                }
-            } else {
-                state.wm.global_layout
-            };
-            let next = cycle
-                .iter()
-                .position(|m| *m == current)
-                .map(|i| cycle[(i + 1) % cycle.len()])
-                .unwrap_or(TilingMode::Cascade);
-
-            // Set the layout for every currently active tag.
-            for tag_bit in 0..crate::types::NUM_TAGS {
-                if (active_tags & (1u32 << tag_bit)) != 0 {
-                    state.wm.tag_layouts[tag_bit] = next;
-                    state.wm.has_tag_layout[tag_bit] = true;
-                }
-            }
-            eprintln!(
-                "layout-next: tag layout set to {} for active_tags=0b{:b}",
-                next.as_str(),
-                active_tags
-            );
-
-            if state.wm.notifications_enable {
-                crate::config::show_notification("cce-client", &format!("Layout set to {} for active tags", next.as_str()));
-            }
-
-            // Unlock windows that got their mode from the layout (not from mode_rules
-            // or manual set-mode) so assign_window_modes will reassign them.
-            // Windows with mode_locked=true were explicitly set by the user and stay.
-            // Windows matched by mode_rules will get reassigned to the same rule mode.
-            // Only windows that fell through to tag_layouts/global_layout will change.
-
-            state.wm.needs_render = true;
-            state.wm.needs_status_update = true;
-        }
-        Action::ModeNext => {
-            let cycle = [
-                TilingMode::Cascade,
-                TilingMode::Grid,
-                TilingMode::Fullscreen,
-                TilingMode::Floating,
-            ];
-            let focused_id = state
-                .wm
-                .seats
-                .iter()
-                .find(|s| !s.removed)
-                .and_then(|s| s.focused_window_id);
-            if let Some(fid) = focused_id {
-                let notifications_enable = state.wm.notifications_enable;
-                if let Some(win) = state.wm.get_window_mut(fid) {
-                    let next = cycle
-                        .iter()
-                        .position(|m| *m == win.tiling_mode)
-                        .map(|i| cycle[(i + 1) % cycle.len()])
-                        .unwrap_or(TilingMode::Cascade);
-                    eprintln!(
-                        "mode-next: window {} ({:?}) {} -> {}",
-                        fid,
-                        win.app_id,
-                        win.tiling_mode.as_str(),
-                        next.as_str()
-                    );
-                    win.tiling_mode = next;
-                    win.mode_locked = true;
-                    if notifications_enable {
-                        let win_title = win.title.as_deref().unwrap_or("Window");
-                        crate::config::show_notification("cce-client", &format!("Tiling mode set to {} for: {}", next.as_str(), win_title));
-                    }
-                    state.wm.needs_render = true;
-                    state.wm.needs_status_update = true;
-                }
-            }
-        }
-        Action::ModeNextShared => {
-            let cycle = [
-                TilingMode::Cascade,
-                TilingMode::Grid,
-                TilingMode::Fullscreen,
-                TilingMode::Floating,
-            ];
-            let focused_id = state
-                .wm
-                .seats
-                .iter()
-                .find(|s| !s.removed)
-                .and_then(|s| s.focused_window_id);
-            if let Some(fid) = focused_id {
-                let notifications_enable = state.wm.notifications_enable;
-                let current_mode = state.wm.get_window(fid).map(|w| w.tiling_mode);
-                if let Some(old_mode) = current_mode {
-                    let next = cycle
-                        .iter()
-                        .position(|m| *m == old_mode)
-                        .map(|i| cycle[(i + 1) % cycle.len()])
-                        .unwrap_or(TilingMode::Cascade);
-
-                    let active_tags = state.wm.active_tags;
-
-                    let mut updated_count = 0;
-                    for win in &mut state.wm.windows {
-                        if !win.closed
-                            && win.app_id.as_deref() != Some("cce-status-interface")
-                            && (win.tags & active_tags) != 0
-                            && win.tiling_mode == old_mode
-                        {
-                            win.tiling_mode = next;
-                            win.mode_locked = true;
-                            updated_count += 1;
-                        }
-                    }
-
-                    eprintln!(
-                        "mode-next-shared: updated {} windows (current tag, mode {}) -> {}",
-                        updated_count,
-                        old_mode.as_str(),
-                        next.as_str()
-                    );
-
-                    if notifications_enable && updated_count > 0 {
-                        crate::config::show_notification(
-                            "cce-client",
-                            &format!(
-                                "Tiling mode set to {} for all {} windows on active tag",
-                                next.as_str(),
-                                old_mode.as_str()
-                            ),
-                        );
-                    }
-
-                    state.wm.needs_render = true;
-                    state.wm.needs_status_update = true;
-                }
-            }
-        }
-        Action::Reload => {
-            crate::restart::wm_restart();
-        }
-        Action::Restart => {
-            crate::restart::wm_restart();
-        }
-        Action::Expose => {
-            let active = !state.wm.expose_active;
-            crate::wm::set_expose_active(&mut state.wm, active);
-            state.wm.needs_render = true;
-            state.wm.needs_status_update = true;
-        }
-        Action::View1 | Action::View2 | Action::View3 | Action::View4 => {
-            let tag = match action {
-                Action::View1 => 1,
-                Action::View2 => 2,
-                Action::View3 => 3,
-                Action::View4 => 4,
-                _ => return,
-            };
-            state.wm.active_tags = 1 << (tag - 1);
-
-            // Reassign focus to a visible window on the new tag
-            let visible_ids: Vec<u64> = state
-                .wm
-                .windows
-                .iter()
-                .filter(|w| (w.tags & state.wm.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                .map(|w| w.id)
-                .collect();
-            if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                seat.focused_window_id = visible_ids.last().copied();
-            }
-
-            state.wm.needs_render = true;
-            state.wm.needs_focus = true;
-            state.wm.needs_status_update = true;
-        }
-        Action::Toggle1 | Action::Toggle2 | Action::Toggle3 | Action::Toggle4 => {
-            let tag = match action {
-                Action::Toggle1 => 1,
-                Action::Toggle2 => 2,
-                Action::Toggle3 => 3,
-                Action::Toggle4 => 4,
-                _ => return,
-            };
-            state.wm.active_tags ^= 1 << (tag - 1);
-
-            // If the focused window is no longer visible, reassign focus
-            let focused_id = state
-                .wm
-                .seats
-                .iter()
-                .find(|s| !s.removed)
-                .and_then(|s| s.focused_window_id);
-            let focused_still_visible = focused_id.map_or(false, |fid| {
-                state
-                    .wm
-                    .get_window(fid)
-                    .map_or(false, |w| (w.tags & state.wm.active_tags) != 0 && !w.closed)
-            });
-            if !focused_still_visible {
-                let visible_ids: Vec<u64> = state
-                    .wm
-                    .windows
-                    .iter()
-                    .filter(|w| (w.tags & state.wm.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                    .map(|w| w.id)
-                    .collect();
-                if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                    seat.focused_window_id = visible_ids.last().copied();
-                }
-                state.wm.needs_focus = true;
-            }
-
-            state.wm.needs_render = true;
-            state.wm.needs_status_update = true;
-        }
-        Action::SetTag1 | Action::SetTag2 | Action::SetTag3 | Action::SetTag4 => {
-            let tag = match action {
-                Action::SetTag1 => 1,
-                Action::SetTag2 => 2,
-                Action::SetTag3 => 3,
-                Action::SetTag4 => 4,
-                _ => return,
-            };
-            // Read focused_id before any mutable borrow
-            let focused_id = state
-                .wm
-                .seats
-                .iter()
-                .find(|s| !s.removed)
-                .and_then(|s| s.focused_window_id);
-            if let Some(focused_id) = focused_id {
-                // Set the window's tag
-                let active_tags = state.wm.active_tags;
-                let window_left_active_tag =
-                    state.wm.get_window_mut(focused_id).map_or(false, |window| {
-                        window.tags = 1 << (tag - 1);
-                        (window.tags & active_tags) == 0
-                    });
-
-                // If the window is no longer on an active tag, shift focus
-                if window_left_active_tag {
-                    let visible_ids: Vec<u64> = state
-                        .wm
-                        .windows
-                        .iter()
-                        .filter(|w| (w.tags & state.wm.active_tags) != 0 && !w.closed && w.app_id.as_deref() != Some("cce-status-interface"))
-                        .map(|w| w.id)
-                        .collect();
-                    if let Some(seat) = state.wm.seats.iter_mut().find(|s| !s.removed) {
-                        seat.focused_window_id = visible_ids.last().copied();
-                    }
-                    state.wm.needs_focus = true;
-                }
-
-                state.wm.needs_render = true;
-                state.wm.needs_status_update = true;
-            }
-        }
-    }
-}
-
-fn enforce_single_instance(wm: &mut WindowManager) {
-    for rule in &wm.mode_rules {
-        if !rule.single_instance {
-            continue;
-        }
-        let mut first_matched = false;
-        for window in &mut wm.windows {
-            if window.closed {
-                continue;
-            }
-            let has_app_id = window.app_id.as_deref().map_or(false, |s| !s.is_empty());
-            let match_app = rule.app_id_pattern == "*"
-                || window
-                    .app_id
-                    .as_deref()
-                    .map_or(false, |aid| aid.contains(&rule.app_id_pattern))
-                || (!has_app_id && window.title.as_deref().map_or(false, |t| {
-                    let normalize = |s: &str| -> String {
-                        s.to_lowercase().replace(|c: char| c == '-' || c == '_', " ")
-                    };
-                    normalize(t).contains(&normalize(&rule.app_id_pattern))
-                }));
-            let match_title = rule.title_pattern.as_deref() == Some("*")
-                || rule.title_pattern.is_none()
-                || window.title.as_deref().map_or(false, |t| {
-                    t.contains(rule.title_pattern.as_deref().unwrap_or(""))
-                });
-            if match_app && match_title {
-                if first_matched {
-                    window.tiling_mode = TilingMode::Floating;
-                    window.mode_locked = true;
-                }
-                first_matched = true;
-            }
-        }
-    }
-}
-
-/// Convert our u32 modifier bitmask to the generated Modifiers bitflags.
-fn u32_to_modifiers(mods: u32) -> Modifiers {
-    Modifiers::from_bits_truncate(mods)
-}
-
-fn apply_pending_bindings(state: &mut AppState, qhandle: &QueueHandle<AppState>) {
-    // Clear old binding proxies (destroying them unregisters the bindings)
-    state.xkb_binding_proxies.clear();
-    state.pointer_binding_proxies.clear();
-
-    // Create xkb binding seats for any seats that don't have one yet
-    if state.xkb_bindings.is_some() {
-        for (_sid, sp) in &mut state.seat_proxies {
-            if sp.xkb_bindings_seat.is_none() {
-                if let Some(ref xb) = state.xkb_bindings {
-                    sp.xkb_bindings_seat = Some(xb.get_seat(&sp.river_seat, qhandle, ()));
-                }
-            }
-        }
-    }
-
-    // Apply xkb bindings to each seat
-    let bindings: Vec<_> = state.wm.pending_bindings.drain(..).collect();
-    /*
-    eprintln!(
-        "[bindings] applying {} xkb bindings to {} seats",
-        bindings.len(),
-        state.seat_proxies.len()
-    );
-    */
-    for pb in &bindings {
-        for (sid, sp) in &state.seat_proxies {
-            if let Some(ref xb) = state.xkb_bindings {
-                if let Some(ref _xbs) = sp.xkb_bindings_seat {
-                    let modifiers = u32_to_modifiers(pb.mods);
-                    let binding_data = BindingUserData {
-                        seat_id: *sid,
-                        action: pb.action.clone(),
-                        command: pb.command.clone(),
-                    };
-                    let binding = xb.get_xkb_binding(
-                        &sp.river_seat,
-                        pb.keysym,
-                        modifiers,
-                        qhandle,
-                        binding_data,
-                    );
-                    binding.enable();
-                    // Store the proxy so it stays alive (binding would be unregistered on drop)
-                    state.xkb_binding_proxies.push(binding);
-                }
-            }
-        }
-    }
-
-    // Apply pointer bindings to each seat
-    let ptr_bindings: Vec<_> = state.wm.pending_pointer_bindings.drain(..).collect();
-    for ppb in &ptr_bindings {
-        for (sid, sp) in &state.seat_proxies {
-            let modifiers = u32_to_modifiers(ppb.mods);
-            let binding_data = BindingUserData {
-                seat_id: *sid,
-                action: ppb.action.clone(),
-                command: None,
-            };
-            let pb =
-                sp.river_seat
-                    .get_pointer_binding(ppb.button, modifiers, qhandle, binding_data);
-            // Store the proxy so it stays alive
-            state.pointer_binding_proxies.push(pb);
-        }
-    }
-}
-
-// --- wlr-output-management dispatch implementations ---
-
-/// Helper: convert an f64 scale value to the wl_fixed_t format
-/// used by the Wayland protocol set_scale method.
-/// wayland-scanner already converts wl_fixed -> f64 for events,
-/// but set_scale takes f64 directly (the scanner handles the conversion).
-fn _f64_to_wl_fixed(v: f64) -> i32 {
-    (v * 256.0) as i32
-}
-
-impl Dispatch<ZwlrOutputManagerV1, ()> for AppState {
-    event_created_child!(AppState, ZwlrOutputManagerV1, [
-        zwlr_output_manager_v1::EVT_HEAD_OPCODE => (ZwlrOutputHeadV1, ()),
-    ]);
-
-    fn event(
-        state: &mut Self,
-        _proxy: &ZwlrOutputManagerV1,
-        event: zwlr_output_manager_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            zwlr_output_manager_v1::Event::Head { head: _ } => {
-                eprintln!("[output-mgmt] Head event (new head advertised)");
-            }
-            zwlr_output_manager_v1::Event::Done { serial } => {
-                eprintln!(
-                    "[output-mgmt] Done serial={} pending_scale_apply={} output_scale={} heads={}",
-                    serial,
-                    state.wm.pending_scale_apply,
-                    state.wm.output_scale,
-                    state.output_heads.len()
-                );
-                for h in &state.output_heads {
-                    eprintln!(
-                        "[output-mgmt]   head {} enabled={} scale={}",
-                        h.name, h.enabled, h.scale
-                    );
-                }
-                state.output_serial = serial;
-
-                // If config requested scale application and we have heads, apply it
-                if state.wm.pending_scale_apply && state.wm.output_scale > 0.0 {
-                    apply_output_scale(state, qhandle);
-                }
-            }
-            zwlr_output_manager_v1::Event::Finished => {
-                eprintln!(
-                    "[output-mgmt] manager finished, heads before clear: {}",
-                    state.output_heads.len()
-                );
-                state.output_manager = None;
-            }
-            _ => {}
-        }
-    }
-}
-
-impl Dispatch<ZwlrOutputHeadV1, ()> for AppState {
-    event_created_child!(AppState, ZwlrOutputHeadV1, [
-        zwlr_output_head_v1::EVT_MODE_OPCODE => (ZwlrOutputModeV1, ()),
-    ]);
-
-    fn event(
-        state: &mut Self,
-        proxy: &ZwlrOutputHeadV1,
-        event: zwlr_output_head_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        // Find or create the head entry by proxy ID
-        let pid = proxy.id().protocol_id();
-
-        match event {
-            zwlr_output_head_v1::Event::Name { name } => {
-                // Check if we already track this head
-                if let Some(head) = state
-                    .output_heads
-                    .iter_mut()
-                    .find(|h| h.proxy.id().protocol_id() == pid)
-                {
-                    head.name = name.clone();
-                } else {
-                    state.output_heads.push(OutputHeadInfo {
-                        proxy: proxy.clone(),
-                        name,
-                        enabled: false,
-                        scale: 1.0,
-                    });
-                }
-                eprintln!(
-                    "[output-mgmt] head name={}",
-                    state
-                        .output_heads
-                        .iter()
-                        .find(|h| h.proxy.id().protocol_id() == pid)
-                        .map(|h| h.name.as_str())
-                        .unwrap_or("?")
-                );
-            }
-            zwlr_output_head_v1::Event::Description { description } => {
-                let _ = description; // not needed for scale config
-            }
-            zwlr_output_head_v1::Event::PhysicalSize { width, height } => {
-                let _ = (width, height);
-            }
-            zwlr_output_head_v1::Event::Enabled { enabled } => {
-                let head_name = state
-                    .output_heads
-                    .iter()
-                    .find(|h| h.proxy.id().protocol_id() == pid)
-                    .map(|h| h.name.as_str())
-                    .unwrap_or("?");
-                eprintln!(
-                    "[output-mgmt] head {} Enabled enabled={} pending_scale_apply={}",
-                    head_name, enabled, state.wm.pending_scale_apply
-                );
-                if let Some(head) = state
-                    .output_heads
-                    .iter_mut()
-                    .find(|h| h.proxy.id().protocol_id() == pid)
-                {
-                    head.enabled = enabled != 0;
-                }
-            }
-            zwlr_output_head_v1::Event::Scale { scale } => {
-                let head_name = state
-                    .output_heads
-                    .iter()
-                    .find(|h| h.proxy.id().protocol_id() == pid)
-                    .map(|h| h.name.as_str())
-                    .unwrap_or("?");
-                eprintln!(
-                    "[output-mgmt] head {} Scale={} output_scale={} pending_scale_apply={}",
-                    head_name, scale, state.wm.output_scale, state.wm.pending_scale_apply
-                );
-                // wayland-scanner converts wl_fixed to f64 automatically
-                if let Some(head) = state
-                    .output_heads
-                    .iter_mut()
-                    .find(|h| h.proxy.id().protocol_id() == pid)
-                {
-                    // Detect VT-switch-back: compositor reports a scale that
-                    // differs from our configured output_scale. This covers
-                    // both: (a) the old head had scale=X and compositor sends 1.0,
-                    // and (b) the head was re-created after Finished (so its
-                    // internal scale was reset to 1.0) and compositor sends 1.0
-                    // instead of the configured scale.
-                    if state.wm.output_scale > 0.0
-                        && head.enabled
-                        && (scale - state.wm.output_scale).abs() > 0.01
-                    {
-                        eprintln!(
-                            "[output-mgmt] scale mismatch (got {}, configured {}), will re-apply",
-                            scale, state.wm.output_scale
-                        );
-                        state.wm.pending_scale_apply = true;
-                    }
-                    head.scale = scale;
-                }
-            }
-            zwlr_output_head_v1::Event::Finished => {
-                let head_name = state
-                    .output_heads
-                    .iter()
-                    .find(|h| h.proxy.id().protocol_id() == pid)
-                    .map(|h| h.name.as_str())
-                    .unwrap_or("?");
-                eprintln!(
-                    "[output-mgmt] head {} Finished (removing from heads)",
-                    head_name
-                );
-                state
-                    .output_heads
-                    .retain(|h| h.proxy.id().protocol_id() != pid);
-            }
-            zwlr_output_head_v1::Event::CurrentMode { mode: _ } => {}
-            zwlr_output_head_v1::Event::Position { x, y } => {
-                let _ = (x, y);
-            }
-            zwlr_output_head_v1::Event::Transform { transform } => {
-                let _ = transform;
-            }
-            zwlr_output_head_v1::Event::Make { make } => {
-                let _ = make;
-            }
-            zwlr_output_head_v1::Event::Model { model } => {
-                let _ = model;
-            }
-            zwlr_output_head_v1::Event::SerialNumber { serial_number } => {
-                let _ = serial_number;
-            }
-            zwlr_output_head_v1::Event::AdaptiveSync { state: _ } => {}
-            _ => {}
-        }
-    }
-}
-
-impl Dispatch<ZwlrOutputModeV1, ()> for AppState {
-    fn event(
-        _state: &mut Self,
-        _proxy: &ZwlrOutputModeV1,
-        event: zwlr_output_mode_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            zwlr_output_mode_v1::Event::Size { width, height } => {
-                let _ = (width, height);
-            }
-            zwlr_output_mode_v1::Event::Refresh { refresh } => {
-                let _ = refresh;
-            }
-            zwlr_output_mode_v1::Event::Preferred => {}
-            zwlr_output_mode_v1::Event::Finished => {}
-            _ => {}
-        }
-    }
-}
-
-impl Dispatch<ZwlrOutputConfigurationV1, ()> for AppState {
-    event_created_child!(AppState, ZwlrOutputConfigurationV1, [
-        zwlr_output_configuration_v1::REQ_ENABLE_HEAD_OPCODE => (ZwlrOutputConfigurationHeadV1, ()),
-    ]);
-
-    fn event(
-        state: &mut Self,
-        _proxy: &ZwlrOutputConfigurationV1,
-        event: zwlr_output_configuration_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            zwlr_output_configuration_v1::Event::Succeeded => {
-                eprintln!("[output-mgmt] configuration succeeded");
-                state.output_config = None;
-            }
-            zwlr_output_configuration_v1::Event::Failed => {
-                eprintln!("[output-mgmt] configuration failed");
-                state.output_config = None;
-            }
-            zwlr_output_configuration_v1::Event::Cancelled => {
-                eprintln!("[output-mgmt] configuration cancelled (stale serial, will retry)");
-                state.output_config = None;
-                // Re-queue: the done event will fire again with a fresh serial
-                state.wm.pending_scale_apply = true;
-            }
-            _ => {}
-        }
-    }
-}
-
-impl Dispatch<ZwlrOutputConfigurationHeadV1, ()> for AppState {
-    fn event(
-        _state: &mut Self,
-        _proxy: &ZwlrOutputConfigurationHeadV1,
-        _event: zwlr_output_configuration_head_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        // zwlr_output_configuration_head_v1 has no events — it's request-only
-    }
-}
-
-/// Apply the configured output scale via the wlr-output-management protocol.
-/// Called when a `done` event arrives and `pending_scale_apply` is true.
-pub fn apply_output_scale(state: &mut AppState, qhandle: &QueueHandle<AppState>) {
-    let Some(ref om) = state.output_manager else {
-        eprintln!("[output-mgmt] no output_manager, cannot apply scale");
-        return;
-    };
-
-    if state.output_heads.is_empty() {
-        eprintln!("[output-mgmt] no heads discovered yet, deferring scale apply");
-        return;
-    }
-
-    // Check if any enabled head actually needs a scale change
-    let target_scale = state.wm.output_scale;
-    let needs_change = state
-        .output_heads
-        .iter()
-        .any(|h| h.enabled && (h.scale - target_scale).abs() > 0.01);
-    if !needs_change {
-        eprintln!(
-            "[output-mgmt] all heads already at target scale {}",
-            target_scale
-        );
-        state.wm.pending_scale_apply = false;
-        return;
-    }
-
-    eprintln!(
-        "[output-mgmt] applying scale {} to {} head(s)",
-        target_scale,
-        state.output_heads.len()
-    );
-
-    let serial = state.output_serial;
-
-    // Destroy any existing pending configuration
-    if let Some(ref config) = state.output_config {
-        config.destroy();
-    }
-    state.output_config = None;
-
-    // Create a new configuration object
-    let config = om.create_configuration(serial, qhandle, ());
-
-    // For each head: enable it and set the scale
-    for head in &state.output_heads {
-        if head.enabled {
-            let config_head = config.enable_head(&head.proxy, qhandle, ());
-            config_head.set_scale(target_scale);
-        } else {
-            config.disable_head(&head.proxy);
-        }
-    }
-
-    config.apply();
-
-    // Store the config proxy so it stays alive until succeeded/failed/cancelled
-    state.output_config = Some(config);
-    state.wm.pending_scale_apply = false;
-}
-
-/// Connect to the Wayland display and set up the event queue.
-/// Returns the Connection, EventQueue, and AppState after the initial registry roundtrip.
-pub fn wayland_init() -> Result<(Connection, EventQueue<AppState>, AppState), String> {
-    let conn = Connection::connect_to_env()
-        .map_err(|e| format!("failed to connect to Wayland: {:?}", e))?;
-
-    let mut event_queue = conn.new_event_queue::<AppState>();
-    let qh = event_queue.handle();
-
-    let registry = conn.display().get_registry(&qh, RegistryData);
-
-    // Do initial roundtrip to receive global events and bind protocols
-    let mut state = AppState::new();
-    state.registry = Some(registry);
-    eprintln!("[init] first roundtrip starting...");
-    let rt1 = std::time::Instant::now();
-    event_queue
-        .roundtrip(&mut state)
-        .map_err(|e| format!("initial roundtrip failed: {:?}", e))?;
-    eprintln!(
-        "[init] first roundtrip done in {:?}, render_count={}",
-        rt1.elapsed(),
-        state.render_count
-    );
-
-    // Check we got the required protocols
-    if !state.has_window_manager {
-        return Err("river_window_manager_v1 not available".to_string());
-    }
-    if !state.has_xkb_bindings {
-        return Err("river_xkb_bindings_v1 not available".to_string());
-    }
-
-    // Second roundtrip for input device events
-    eprintln!("[init] second roundtrip starting...");
-    let rt2 = std::time::Instant::now();
-    event_queue
-        .roundtrip(&mut state)
-        .map_err(|e| format!("second roundtrip failed: {:?}", e))?;
-    eprintln!(
-        "[init] second roundtrip done in {:?}, render_count={}",
-        rt2.elapsed(),
-        state.render_count
-    );
-
-    eprintln!("cce-client: Wayland connection established");
-
-    Ok((conn, event_queue, state))
-}
-
-// --- RiverLibinputConfigV1 events ---
-
-impl Dispatch<RiverLibinputConfigV1, ()> for AppState {
-    event_created_child!(AppState, RiverLibinputConfigV1, [
-        river_libinput_config_v1::EVT_LIBINPUT_DEVICE_OPCODE => (RiverLibinputDeviceV1, ()),
-    ]);
-
-    fn event(
-        state: &mut Self,
-        _proxy: &RiverLibinputConfigV1,
-        event: river_libinput_config_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_libinput_config_v1::Event::LibinputDevice { id: device } => {
-                eprintln!("[libinput] device discovered");
-                state.libinput_devices.push(LibinputDeviceInfo {
-                    device,
-                    name: String::new(),
-                    input_device: None,
-                    name_received: false,
-                    tap_finger_count: -1, // not yet received
-                    tap_info_received: false,
-                    accel_profiles_support: None,
-                    natural_scroll_supported: None,
-                    dwt_supported: None,
-                    dwtp_supported: None,
-                    config_applied: false,
-                });
-                state.wm.tap_config_applied = false;
-            }
-            river_libinput_config_v1::Event::Finished => {}
-            _ => {}
-        }
-    }
-}
-
-// --- RiverLibinputDeviceV1 events ---
-
-impl Dispatch<RiverLibinputDeviceV1, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &RiverLibinputDeviceV1,
-        event: river_libinput_device_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_libinput_device_v1::Event::InputDevice { device } => {
-                if let Some(dev) = state.libinput_devices.iter_mut().find(|d| d.device.id().protocol_id() == proxy.id().protocol_id()) {
-                    dev.input_device = Some(device.clone());
-                    let dev_id = device.id().protocol_id();
-                    if let Some(name) = state.input_device_names.get(&dev_id) {
-                        dev.name = name.clone();
-                        dev.name_received = true;
-                        eprintln!("[libinput] associated name \"{}\" with device on InputDevice event", name);
-                    }
-                }
-                crate::wayland::apply_input_config(state, qhandle);
-            }
-            river_libinput_device_v1::Event::TapSupport { finger_count } => {
-                if let Some(dev) = state.libinput_devices.iter_mut().find(|d| d.device.id().protocol_id() == proxy.id().protocol_id()) {
-                    dev.tap_finger_count = finger_count;
-                    eprintln!("[libinput] tap support: {} fingers", finger_count);
-                }
-                crate::wayland::apply_input_config(state, qhandle);
-            }
-            river_libinput_device_v1::Event::AccelProfilesSupport { profiles } => {
-                if let Some(dev) = state.libinput_devices.iter_mut().find(|d| d.device.id().protocol_id() == proxy.id().protocol_id()) {
-                    dev.accel_profiles_support = Some(profiles.into());
-                    eprintln!("[libinput] accel profiles support: {:?}", profiles);
-                }
-                crate::wayland::apply_input_config(state, qhandle);
-            }
-            river_libinput_device_v1::Event::NaturalScrollSupport { supported } => {
-                if let Some(dev) = state.libinput_devices.iter_mut().find(|d| d.device.id().protocol_id() == proxy.id().protocol_id()) {
-                    dev.natural_scroll_supported = Some(supported != 0);
-                    eprintln!("[libinput] natural scroll support: {}", supported != 0);
-                }
-                crate::wayland::apply_input_config(state, qhandle);
-            }
-            river_libinput_device_v1::Event::DwtSupport { supported } => {
-                if let Some(dev) = state.libinput_devices.iter_mut().find(|d| d.device.id().protocol_id() == proxy.id().protocol_id()) {
-                    dev.dwt_supported = Some(supported != 0);
-                    eprintln!("[libinput] dwt support: {}", supported != 0);
-                }
-                crate::wayland::apply_input_config(state, qhandle);
-            }
-            river_libinput_device_v1::Event::DwtpSupport { supported } => {
-                if let Some(dev) = state.libinput_devices.iter_mut().find(|d| d.device.id().protocol_id() == proxy.id().protocol_id()) {
-                    dev.dwtp_supported = Some(supported != 0);
-                    eprintln!("[libinput] dwtp support: {}", supported != 0);
-                }
-                crate::wayland::apply_input_config(state, qhandle);
-            }
-            river_libinput_device_v1::Event::Removed => {
-                eprintln!("[libinput] device removed");
-                state.libinput_devices.retain(|d| d.device.id().protocol_id() != proxy.id().protocol_id());
-                state.wm.tap_config_applied = false;
-            }
-            _ => {}
-        }
-    }
-}
-
-// --- RiverLibinputResultV1 events ---
-
-impl Dispatch<RiverLibinputResultV1, ()> for AppState {
-    fn event(
-        _state: &mut Self,
-        _proxy: &RiverLibinputResultV1,
-        event: river_libinput_result_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            river_libinput_result_v1::Event::Success => {
-                eprintln!("[libinput] config applied successfully");
-            }
-            river_libinput_result_v1::Event::Unsupported => {
-                eprintln!("[libinput] config unsupported by device");
-            }
-            river_libinput_result_v1::Event::Invalid => {
-                eprintln!("[libinput] config invalid");
-            }
-        }
-    }
-}
-
-/// Apply input configuration to all libinput devices that support it.
-pub fn apply_input_config(state: &mut AppState, qhandle: &QueueHandle<AppState>) {
-    if state.wm.tap_config_applied {
-        return;
-    }
-
-    // Wait until ALL discovered devices have received their name and support events (if they have a corresponding input_device).
-    let all_info_received = state.libinput_devices.iter().all(|d| {
-        if d.input_device.is_none() {
-            true
-        } else {
-            d.name_received &&
-            d.tap_finger_count >= 0 &&
-            d.accel_profiles_support.is_some() &&
-            d.natural_scroll_supported.is_some() &&
-            d.dwt_supported.is_some() &&
-            d.dwtp_supported.is_some()
-        }
-    });
-    if !all_info_received {
-        return;
-    }
-
-    // Reset config_applied for all devices to force setting them
-    for dev_info in &mut state.libinput_devices {
-        dev_info.config_applied = false;
-    }
-
-    for dev_info in &mut state.libinput_devices {
-        if dev_info.config_applied {
-            continue;
-        }
-
-        if dev_info.input_device.is_none() {
-            dev_info.config_applied = true;
-            continue;
-        }
-
-        let is_trackpoint = dev_info.name.to_lowercase().contains("trackpoint");
-
-        // 1. Tap to click
-        if dev_info.tap_finger_count > 0 {
-            let tap_state = if state.wm.tap_to_click {
-                river_libinput_device_v1::TapState::Enabled
-            } else {
-                river_libinput_device_v1::TapState::Disabled
-            };
-            eprintln!(
-                "[libinput] setting tap={} on device ({})",
-                if state.wm.tap_to_click { "enabled" } else { "disabled" },
-                &dev_info.name
-            );
-            dev_info.device.set_tap(tap_state, qhandle, ());
-        }
-
-        // 2. Accel speed
-        let speed = if is_trackpoint {
-            state.wm.trackpoint_accel_speed.or(state.wm.accel_speed)
-        } else {
-            state.wm.accel_speed
-        };
-        if let Some(s) = speed {
-            let s = s.clamp(-1.0, 1.0);
-            let speed_bytes = s.to_ne_bytes().to_vec();
-            eprintln!("[libinput] setting accel_speed={} on device ({})", s, &dev_info.name);
-            dev_info.device.set_accel_speed(speed_bytes, qhandle, ());
-        }
-
-        // 3. Accel profile
-        let profile_str = if is_trackpoint {
-            state.wm.trackpoint_accel_profile.as_ref().or(state.wm.accel_profile.as_ref())
-        } else {
-            state.wm.accel_profile.as_ref()
-        };
-        if let Some(profile_name) = profile_str {
-            let profile = match profile_name.as_str() {
-                "flat" => Some(river_libinput_device_v1::AccelProfile::Flat),
-                "adaptive" => Some(river_libinput_device_v1::AccelProfile::Adaptive),
-                "none" => Some(river_libinput_device_v1::AccelProfile::None),
-                "custom" => Some(river_libinput_device_v1::AccelProfile::Custom),
-                _ => None,
-            };
-            if let Some(p) = profile {
-                eprintln!("[libinput] setting accel_profile={:?} on device ({})", p, &dev_info.name);
-                dev_info.device.set_accel_profile(p, qhandle, ());
-            }
-        }
-
-        // 4. Natural scroll
-        if let Some(supported) = dev_info.natural_scroll_supported {
-            if supported {
-                if let Some(natural) = state.wm.natural_scroll {
-                    let ns_state = if natural {
-                        river_libinput_device_v1::NaturalScrollState::Enabled
-                    } else {
-                        river_libinput_device_v1::NaturalScrollState::Disabled
-                    };
-                    eprintln!("[libinput] setting natural_scroll={} on device ({})", natural, &dev_info.name);
-                    dev_info.device.set_natural_scroll(ns_state, qhandle, ());
-                }
-            }
-        }
-
-        // 5. Disable while typing (dwt)
-        if let Some(supported) = dev_info.dwt_supported {
-            if supported {
-                if let Some(dwt) = state.wm.dwt {
-                    let dwt_state = if dwt {
-                        river_libinput_device_v1::DwtState::Enabled
-                    } else {
-                        river_libinput_device_v1::DwtState::Disabled
-                    };
-                    eprintln!("[libinput] setting dwt={} on device ({})", dwt, &dev_info.name);
-                    dev_info.device.set_dwt(dwt_state, qhandle, ());
-                }
-            }
-        }
-
-        // 6. Disable while trackpointing (dwtp)
-        if let Some(supported) = dev_info.dwtp_supported {
-            if supported {
-                if let Some(dwtp) = state.wm.dwtp {
-                    let dwtp_state = if dwtp {
-                        river_libinput_device_v1::DwtpState::Enabled
-                    } else {
-                        river_libinput_device_v1::DwtpState::Disabled
-                    };
-                    eprintln!("[libinput] setting dwtp={} on device ({})", dwtp, &dev_info.name);
-                    dev_info.device.set_dwtp(dwtp_state, qhandle, ());
-                }
-            }
-        }
-        // 7. Send events (enable/disable trackpad)
-        let is_touchpad = dev_info.name.to_lowercase().contains("touchpad");
-        let send_events_mode = if state.wm.trackpad_disabled && is_touchpad {
-            river_libinput_device_v1::SendEventsModes::Disabled
-        } else {
-            river_libinput_device_v1::SendEventsModes::Enabled
-        };
-        eprintln!(
-            "[libinput] setting send_events={:?} on device ({})",
-            send_events_mode, &dev_info.name
-        );
-        dev_info.device.set_send_events(send_events_mode, qhandle, ());
-
-        dev_info.config_applied = true;
-    }
-
-    let all_done = state.libinput_devices.iter().all(|d| d.config_applied);
-    if all_done && !state.libinput_devices.is_empty() {
-        state.wm.tap_config_applied = true;
-        eprintln!("[libinput] all input configurations applied to all devices");
-    }
-}
-
-// --- wl_seat events ---
-
-impl Dispatch<wl_seat::WlSeat, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &wl_seat::WlSeat,
-        event: wl_seat::Event,
-        _data: &(),
-        _conn: &Connection,
-        qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            wl_seat::Event::Capabilities { capabilities } => {
-                let has_pointer = match capabilities {
-                    wayland_client::WEnum::Value(caps) => caps.contains(wl_seat::Capability::Pointer),
-                    _ => false,
-                };
-                if let Some((_, seat_proxy)) = state.seat_proxies.iter_mut().find(|(_, sp)| {
-                    sp.wl_seat.as_ref() == Some(proxy)
-                }) {
-                    if has_pointer && seat_proxy.wl_pointer.is_none() {
-                        let wl_pointer = proxy.get_pointer(qhandle, ());
-                        
-                        if let Some(ref csm) = state.cursor_shape_manager {
-                            let device = csm.get_pointer(&wl_pointer, qhandle, ());
-                            device.set_shape(0, Shape::Default);
-                            seat_proxy.cursor_shape_device = Some(device);
-                        }
-                        
-                        seat_proxy.wl_pointer = Some(wl_pointer);
-                    } else if !has_pointer && seat_proxy.wl_pointer.is_some() {
-                        seat_proxy.cursor_shape_device = None;
-                        if let Some(pointer) = seat_proxy.wl_pointer.take() {
-                            pointer.release();
-                        }
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
-// --- wl_pointer events ---
-
-impl Dispatch<wl_pointer::WlPointer, ()> for AppState {
-    fn event(
-        state: &mut Self,
-        proxy: &wl_pointer::WlPointer,
-        event: wl_pointer::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qhandle: &QueueHandle<Self>,
-    ) {
-        match event {
-            wl_pointer::Event::Enter { serial, surface, surface_x, surface_y } => {
-                eprintln!("[pointer] enter surface={:?} x={} y={}", surface, surface_x, surface_y);
-                state.pointer_hovered_surface = Some(surface.clone());
-                state.last_pointer_surface_x = surface_x;
-                state.last_pointer_surface_y = surface_y;
-
-                if let Some((_, seat_proxy)) = state.seat_proxies.iter_mut().find(|(_, sp)| {
-                    sp.wl_pointer.as_ref() == Some(proxy)
-                }) {
-                    seat_proxy.last_pointer_enter_serial = serial;
-                }
-
-                Self::update_cursor_shape_for_surface(state, proxy);
-
-                let is_decoration = state.window_proxies.iter().any(|(_, wp)| {
-                    wp.decoration.as_ref().map_or(false, |d| &d.surface == &surface)
-                        || wp.dec_left.as_ref().map_or(false, |d| &d.surface == &surface)
-                        || wp.dec_right.as_ref().map_or(false, |d| &d.surface == &surface)
-                        || wp.dec_bottom.as_ref().map_or(false, |d| &d.surface == &surface)
-                });
-                if is_decoration {
-                    state.wm.needs_render = true;
-                }
-
-                if state.wm.expose_active {
-                    let current_surface = &surface;
-                    let matched_window = state.window_proxies.iter().find_map(|(id, proxy)| {
-                        if let Some(dec) = &proxy.decoration {
-                            if &dec.surface == current_surface {
-                                return Some(*id);
-                            }
-                        }
-                        if let Some(dec) = &proxy.dec_left {
-                            if &dec.surface == current_surface {
-                                return Some(*id);
-                            }
-                        }
-                        if let Some(dec) = &proxy.dec_right {
-                            if &dec.surface == current_surface {
-                                return Some(*id);
-                            }
-                        }
-                        if let Some(dec) = &proxy.dec_bottom {
-                            if &dec.surface == current_surface {
-                                return Some(*id);
-                            }
-                        }
-                        None
-                    });
-
-                    if let Some(wid) = matched_window {
-                        let seat_id = state.seat_proxies.iter().find_map(|(sid, sp)| {
-                            if sp.wl_pointer.as_ref() == Some(proxy) {
-                                Some(*sid)
-                            } else {
-                                None
-                            }
-                        });
-
-                        if let Some(sid) = seat_id {
-                            if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == sid) {
-                                let already_focused = seat.focused_window_id == Some(wid);
-                                seat.focused_window_id = Some(wid);
-                                state.wm.needs_focus = true;
-                                state.wm.needs_render = true;
-                                state.wm.needs_status_update = true;
-
-                                if !already_focused {
-                                    state.manage_dirty();
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            wl_pointer::Event::Leave { .. } => {
-                eprintln!("[pointer] leave");
-                state.pointer_hovered_surface = None;
-                Self::update_cursor_shape_for_surface(state, proxy);
-                state.wm.needs_render = true;
-            }
-            wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
-                state.last_pointer_surface_x = surface_x;
-                state.last_pointer_surface_y = surface_y;
-
-                Self::update_cursor_shape_for_surface(state, proxy);
-
-                if let Some(ref current_surface) = state.pointer_hovered_surface {
-                    let is_decoration = state.window_proxies.iter().any(|(_, wp)| {
-                        wp.decoration.as_ref().map_or(false, |d| &d.surface == current_surface)
-                            || wp.dec_left.as_ref().map_or(false, |d| &d.surface == current_surface)
-                            || wp.dec_right.as_ref().map_or(false, |d| &d.surface == current_surface)
-                            || wp.dec_bottom.as_ref().map_or(false, |d| &d.surface == current_surface)
-                    });
-                    if is_decoration {
-                        state.wm.needs_render = true;
-                    }
-                }
-
-                if let Some(pending) = state.pending_border_drag.clone() {
-                    let dx = surface_x - pending.start_surface_x;
-                    let dy = surface_y - pending.start_surface_y;
-                    let dist = (dx * dx + dy * dy).sqrt();
-                    // Threshold of 4.0 logical pixels to distinguish click vs drag
-                    if dist > 4.0 {
-                        state.pending_border_drag = None;
-
-                        // Start the drag! Set the window to Floating and lock it.
-                        let mut needs_render = false;
-                        let side_panel_pos = state.wm.layout.side_panel_position.clone();
-                        if let Some(win) = state.wm.get_window_mut(pending.window_id) {
-                            if win.tiling_mode != TilingMode::Fullscreen && win.tiling_mode != TilingMode::Popup {
-                                let is_valid_side_panel_resize = win.tiling_mode == TilingMode::SidePanel
-                                    && if side_panel_pos == "right" {
-                                        matches!(
-                                            pending.op_type,
-                                            PointerOpType::Resize
-                                                | PointerOpType::ResizeLeft
-                                                | PointerOpType::ResizeTopLeft
-                                                | PointerOpType::ResizeBottomLeft
-                                        )
-                                    } else {
-                                        matches!(
-                                            pending.op_type,
-                                            PointerOpType::
diff truncated