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

commitecc9fc26851685ebb47fe2f4777b3c081734bb6a
parentc0d151fd19
authorLucas Galante <[email protected]>
date2026-06-15 08:24
Implement output scaling, side panel support, restacking stability fixes, and general cleanup in monolithic cce-server

 Cargo.toml                   |   2 +-
 src/server/config.rs         |  20 +-
 src/server/layer_shell.rs    |   2 +-
 src/server/output.rs         |   2 +-
 src/server/tiling.rs         |   2 +-
 src/server/window.rs         | 111 ++++++++---
 src/server/window_manager.rs | 436 +++++++++++++++++++++++++++++++++----------
 src/server/wm_node.rs        |  16 +-
 src/server/xdg_toplevel.rs   |  46 +----
 9 files changed, 456 insertions(+), 181 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 6172850..d1a60c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,4 +29,4 @@ path = "src/bin/cce.rs"
 [profile.release]
 debug = true
 
-[workspace]
+
diff --git a/src/server/config.rs b/src/server/config.rs
index beae02a..2851851 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -104,8 +104,6 @@ pub enum Action {
     LayoutNext,
     ModeNext,
     ModeNextShared,
-    Reload,
-    Restart,
     View1,
     View2,
     View3,
@@ -163,6 +161,16 @@ pub struct PointerBind {
     pub action: Action,
 }
 
+#[derive(Debug, Deserialize, Clone)]
+pub struct OutputConfig {
+    #[serde(default = "default_scale")]
+    pub scale: f64,
+}
+
+fn default_scale() -> f64 {
+    1.0
+}
+
 #[derive(Debug, Deserialize)]
 pub struct Config {
     #[serde(default)]
@@ -179,6 +187,8 @@ pub struct Config {
     pub tag_layout: Vec<TagLayoutConfig>,
     #[serde(default)]
     pub startup: Vec<StartupConfig>,
+    #[serde(default)]
+    pub output: Option<OutputConfig>,
 }
 
 #[derive(Debug, Deserialize)]
@@ -381,10 +391,6 @@ pub fn parse_action(s: &str) -> Action {
         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" {
@@ -548,6 +554,8 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
         Err(e) => return Err(format!("TOML parse error: {}", e)),
     };
 
+    state.output_scale = config.output.as_ref().map(|o| o.scale as f32).unwrap_or(1.0f32);
+
     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;
diff --git a/src/server/layer_shell.rs b/src/server/layer_shell.rs
index 9a7c5e8..c3d7a49 100644
--- a/src/server/layer_shell.rs
+++ b/src/server/layer_shell.rs
@@ -74,7 +74,7 @@ impl LayerShell {
     pub unsafe fn supported(&self) -> bool {
         let wm_v1 = (*self.server).wm.object;
         if wm_v1.is_null() {
-            return false;
+            return true;
         }
         let wm_client = ffi::wl_resource_get_client(wm_v1);
 
diff --git a/src/server/output.rs b/src/server/output.rs
index e6357f5..4e642cc 100644
--- a/src/server/output.rs
+++ b/src/server/output.rs
@@ -347,7 +347,7 @@ impl Output {
             x: 0,
             y: 0,
             mode: OutputMode::None,
-            scale: 1.0,
+            scale: (*server).wm.output_scale,
             transform: ffi::wl_output_transform_WL_OUTPUT_TRANSFORM_NORMAL,
             adaptive_sync: ffi::river_wlr_output_get_adaptive_sync_status(wlr_output) == ffi::wlr_output_adaptive_sync_status_WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED,
             auto_layout: true,
diff --git a/src/server/tiling.rs b/src/server/tiling.rs
index 9c21887..f538e84 100644
--- a/src/server/tiling.rs
+++ b/src/server/tiling.rs
@@ -69,7 +69,7 @@ pub fn tile_grid(
     n_grid: i32,
     idx: i32,
 ) -> (i32, i32, i32, i32) {
-    let cols = 2i32;
+    let cols = if n_grid == 1 { 1i32 } else { 2i32 };
     let row = idx / cols;
     let col = idx % cols;
     let rows = (n_grid + cols - 1) / cols;
diff --git a/src/server/window.rs b/src/server/window.rs
index 2d5fc32..8922138 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -455,19 +455,22 @@ impl Window {
         }
     }
 
-    pub unsafe fn is_antigravity(&self) -> bool {
-        let app_id_ptr = self.get_app_id();
-        if app_id_ptr.is_null() { return false; }
-        let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_str().unwrap_or("").to_lowercase();
-        app_id.contains("antigravity")
+    pub unsafe fn get_app_id_string(&self) -> Option<String> {
+        let ptr = self.get_app_id();
+        if ptr.is_null() {
+            None
+        } else {
+            Some(std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned())
+        }
     }
 
-    pub unsafe fn is_chromium_electron(&self) -> bool {
-        if self.is_antigravity() { return true; }
-        let app_id_ptr = self.get_app_id();
-        if app_id_ptr.is_null() { return false; }
-        let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_str().unwrap_or("").to_lowercase();
-        app_id.contains("chromium") || app_id.contains("chrome") || app_id.contains("electron") || app_id.contains("code") || app_id.contains("discord") || app_id.contains("slack")
+    pub unsafe fn get_title_string(&self) -> Option<String> {
+        let ptr = self.get_title();
+        if ptr.is_null() {
+            None
+        } else {
+            Some(std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned())
+        }
     }
 
     pub unsafe fn get_parent(&self) -> *mut Window {
@@ -557,6 +560,27 @@ impl Window {
         assert!(!matches!(self.impl_type, WindowImpl::Destroying));
         assert_eq!(self.state, WindowState::Initialized);
         self.state = WindowState::Mapped;
+
+        let app_id_ptr = self.get_app_id();
+        let is_status_bar = if !app_id_ptr.is_null() {
+            let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy();
+            app_id == "cce-status-interface"
+        } else {
+            false
+        };
+
+        if !is_status_bar {
+            let seats = &mut (*self.server).input_manager.seats as *mut ffi::wl_list as *mut WlList;
+            let mut curr = (*seats).next;
+            while curr != seats {
+                let next = (*curr).next;
+                let seat = crate::container_of!(curr, crate::seat::Seat, link);
+                (*seat).focus(crate::seat::Focus::Window(self as *mut Window));
+                curr = next;
+            }
+        }
+
+        (*self.server).wm.dirty_windowing();
         Ok(())
     }
 
@@ -749,6 +773,44 @@ impl Window {
             WindowState::Ready | WindowState::Initialized | WindowState::Mapped => {
                 let wm_v1 = (*self.server).wm.object;
                 if wm_v1.is_null() {
+                    let is_linked = self.node.link.prev as *const _ != &self.node.link as *const _;
+                    if !is_linked {
+                        wl_list_remove(&mut self.node.link as *mut ffi::wl_list as *mut WlList);
+                        let rendering_list = &mut (*self.server).wm.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
+                        wl_list_insert((*rendering_list).prev, &mut self.node.link as *mut ffi::wl_list as *mut WlList);
+
+                        if self.foreign_toplevel_handle.is_null() {
+                            let list = (*self.server).foreign_toplevel_list;
+                            let title = self.get_title();
+                            let app_id = self.get_app_id();
+                            let state = ffi::wlr_ext_foreign_toplevel_handle_v1_state {
+                                title,
+                                app_id,
+                            };
+                            let handle = ffi::wlr_ext_foreign_toplevel_handle_v1_create(list, &state);
+                            if !handle.is_null() {
+                                self.foreign_toplevel_handle = handle;
+                                (*handle).data = self as *mut Window as *mut _;
+                            }
+                        }
+
+                        if self.wlr_toplevel_handle.is_null() {
+                            let manager = (*self.server).wlr_foreign_toplevel_manager;
+                            let handle = ffi::wlr_foreign_toplevel_handle_v1_create(manager);
+                            if !handle.is_null() {
+                                self.wlr_toplevel_handle = handle;
+                                let title = self.get_title();
+                                if !title.is_null() {
+                                    ffi::wlr_foreign_toplevel_handle_v1_set_title(handle, title);
+                                }
+                                let app_id = self.get_app_id();
+                                if !app_id.is_null() {
+                                    ffi::wlr_foreign_toplevel_handle_v1_set_app_id(handle, app_id);
+                                }
+                            }
+                        }
+                        self.rendering_scheduled.resend_dimensions = true;
+                    }
                     return;
                 }
                 let new_resource = self.object.is_null();
@@ -1028,19 +1090,7 @@ impl Window {
         };
         self.wm_requested.dimensions = None;
 
-        if self.wm_requested.ssd && self.is_chromium_electron() && self.wm_requested.fullscreen.is_null() {
-            let margin_x = if self.margin_x > 0 { self.margin_x } else { 10 };
-            let margin_y = if self.margin_y > 0 { self.margin_y } else { 10 };
-            let is_antigravity = self.is_antigravity();
-            let (left_margin, right_margin) = if margin_x == 10 && !is_antigravity { (10, 34) } else { (margin_x, margin_x) };
-            let (top_margin, bottom_margin) = if margin_y == 10 && !is_antigravity { (10, 34) } else { (margin_y, margin_y) };
-            if let Some(w) = width {
-                width = Some(w + left_margin as u32 + right_margin as u32);
-            }
-            if let Some(h) = height {
-                height = Some(h + top_margin as u32 + bottom_margin as u32);
-            }
-        }
+
 
         self.configure_scheduled = Configure {
             width,
@@ -1267,7 +1317,16 @@ impl Window {
             let output = self.wm_requested.fullscreen;
             self.box_geom.x = (*output).sent.x;
             self.box_geom.y = (*output).sent.y;
-            ffi::wlr_scene_node_set_enabled(self.fullscreen_background as *mut ffi::wlr_scene_node, true);
+
+            let app_id_ptr = self.get_app_id();
+            let is_status_bar = if !app_id_ptr.is_null() {
+                let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy();
+                app_id == "cce-status-interface"
+            } else {
+                false
+            };
+
+            ffi::wlr_scene_node_set_enabled(self.fullscreen_background as *mut ffi::wlr_scene_node, !is_status_bar);
             let (width, height) = (*output).sent.dimensions();
             ffi::wlr_scene_rect_set_size(self.fullscreen_background, width as i32, height as i32);
             clip = ffi::wlr_box { x: 0, y: 0, width: width as i32, height: height as i32 };
@@ -1337,7 +1396,7 @@ impl Window {
 
     pub unsafe fn draw_borders(&mut self) {
         let requested = &self.rendering_requested;
-        if requested.circular || requested.border.width == 0 {
+        if requested.circular || requested.border.width == 0 || !self.wm_requested.ssd {
             ffi::wlr_scene_node_set_enabled(self.border.left as *mut ffi::wlr_scene_node, false);
             ffi::wlr_scene_node_set_enabled(self.border.right as *mut ffi::wlr_scene_node, false);
             ffi::wlr_scene_node_set_enabled(self.border.top as *mut ffi::wlr_scene_node, false);
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index d087d21..3ef7482 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -66,6 +66,7 @@ pub struct WindowManager {
     pub ipc_timer: *mut ffi::wl_event_source,
     pub startup: Vec<crate::config::StartupConfig>,
     pub status_sender: Option<crate::status_server::StatusSender>,
+    pub output_scale: f32,
 }
 
 impl WindowManager {
@@ -75,6 +76,7 @@ impl WindowManager {
         ffi::wl_list_init(&mut self.sent.outputs);
         self.scheduled.output_config = std::ptr::null_mut();
         self.sent.output_config = std::ptr::null_mut();
+        self.output_scale = 1.0;
         Ok(())
     }
 
@@ -108,6 +110,7 @@ impl WindowManager {
         self.tag_layouts = [crate::tiling::TilingMode::Cascade; 4];
         self.has_tag_layout = [false; 4];
         self.layout = crate::config::Layout::default();
+        self.output_scale = 1.0;
         self.mode_rules = Vec::new();
         self.keybinds = Vec::new();
         self.pointer_binds = Vec::new();
@@ -423,6 +426,8 @@ impl WindowManager {
             }
         }
 
+        self.keep_status_bar_on_top();
+
         let mut hasher = std::collections::hash_map::DefaultHasher::new();
         let render_list = &mut self.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
         let mut curr = (*render_list).next;
@@ -522,26 +527,30 @@ impl WindowManager {
     // Add legacy fields so structural offsets are preserved if layout-based code is compiled
     pub fn sent_outputs_compat(&self) {}
 
+    pub unsafe fn get_rule_for_window(&self, win: *mut Window) -> Option<&crate::config::ModeRule> {
+        let app_id = (*win).get_app_id_string();
+        let title = (*win).get_title_string();
+
+        for rule in &self.mode_rules {
+            let match_app = rule.app_id_pattern == "*"
+                || app_id.as_ref().map_or(false, |aid| aid.contains(&rule.app_id_pattern));
+            let match_title = rule.title_pattern.as_ref().map_or(true, |tp| {
+                title.as_ref().map_or(false, |t| t.contains(tp))
+            });
+
+            if match_app && match_title {
+                return Some(rule);
+            }
+        }
+        None
+    }
+
     pub unsafe fn get_mode_for_window(&self, win: *mut Window) -> crate::tiling::TilingMode {
         if (*win).mode_locked {
             return (*win).tiling_mode;
         }
 
-        // Query app_id/title
-        let app_id_ptr = (*win).get_app_id();
-        let app_id = if app_id_ptr.is_null() {
-            None
-        } else {
-            Some(std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy().into_owned())
-        };
-
-        let title_ptr = (*win).get_title();
-        let title = if title_ptr.is_null() {
-            None
-        } else {
-            Some(std::ffi::CStr::from_ptr(title_ptr).to_string_lossy().into_owned())
-        };
-
+        let app_id = (*win).get_app_id_string();
         if app_id.as_deref() == Some("cce-status-interface") {
             return crate::tiling::TilingMode::Fullscreen;
         }
@@ -553,17 +562,8 @@ impl WindowManager {
             return crate::tiling::TilingMode::Floating;
         }
 
-        // Match mode rules
-        for rule in &self.mode_rules {
-            let match_app = rule.app_id_pattern == "*"
-                || app_id.as_ref().map_or(false, |aid| aid.contains(&rule.app_id_pattern));
-            let match_title = rule.title_pattern.as_ref().map_or(true, |tp| {
-                title.as_ref().map_or(false, |t| t.contains(tp))
-            });
-
-            if match_app && match_title {
-                return rule.mode;
-            }
+        if let Some(rule) = self.get_rule_for_window(win) {
+            return rule.mode;
         }
 
         // Check tag layouts
@@ -581,10 +581,8 @@ impl WindowManager {
         log::info!("Monolithic arrange_views triggered. Windows: {}", self.windows.count());
         for (idx, &win_ptr) in self.windows.iter().enumerate() {
             if win_ptr.is_null() { continue; }
-            let title_ptr = (*win_ptr).get_title();
-            let title = if title_ptr.is_null() { "None".to_string() } else { std::ffi::CStr::from_ptr(title_ptr).to_string_lossy().into_owned() };
-            let aid_ptr = (*win_ptr).get_app_id();
-            let aid = if aid_ptr.is_null() { "None".to_string() } else { std::ffi::CStr::from_ptr(aid_ptr).to_string_lossy().into_owned() };
+            let title = (*win_ptr).get_title_string().unwrap_or_else(|| "None".to_string());
+            let aid = (*win_ptr).get_app_id_string().unwrap_or_else(|| "None".to_string());
             log::info!("  window #{}: title={:?}, app_id={:?}, state={:?}, closed={}", idx, title, aid, (*win_ptr).state, (*win_ptr).closed);
         }
         
@@ -640,6 +638,7 @@ impl WindowManager {
 
             let mut tiled_windows: Vec<*mut Window> = Vec::new();
             let mut floating_windows: Vec<*mut Window> = Vec::new();
+            let mut side_panel_windows: Vec<*mut Window> = Vec::new();
 
             for &win_ptr in self.windows.iter() {
                 if (*win_ptr).closed {
@@ -659,13 +658,65 @@ impl WindowManager {
                 let mode = self.get_mode_for_window(win_ptr);
                 (*win_ptr).tiling_mode = mode;
 
+                // Apply ModeRule SSD configuration if defined and not locked
+                if !(*win_ptr).mode_locked {
+                    if let Some(rule) = self.get_rule_for_window(win_ptr) {
+                        if let Some(rule_ssd) = rule.ssd {
+                            (*win_ptr).wm_requested.ssd = rule_ssd;
+                        }
+                    }
+                }
+
+                let app_id = (*win_ptr).get_app_id_string();
+
+                if app_id.as_deref() == Some("cce-status-interface") {
+                    let wlr_box = (*output).sent.box_layout();
+                    (*win_ptr).rendering_requested.x = wlr_box.x;
+                    (*win_ptr).rendering_requested.y = wlr_box.y;
+                    (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+                        width: wlr_box.width as u32,
+                        height: wlr_box.height as u32,
+                    });
+                    (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+                        width: wlr_box.width as u32,
+                        height: wlr_box.height as u32,
+                    };
+                    (*win_ptr).wm_requested.tiled = 0;
+                    (*win_ptr).wm_requested.ssd = false;
+                    continue;
+                }
+
                 if mode == crate::tiling::TilingMode::Floating || mode == crate::tiling::TilingMode::Popup {
                     floating_windows.push(win_ptr);
+                } else if mode == crate::tiling::TilingMode::SidePanel {
+                    side_panel_windows.push(win_ptr);
                 } else {
                     tiled_windows.push(win_ptr);
                 }
             }
 
+            let side_panel_win = side_panel_windows.first().copied();
+            let mut side_panel_w = 0;
+            let mut shift_x = 0;
+            if let Some(sp_win) = side_panel_win {
+                let hint_min_w = (*sp_win).wm_scheduled.dimensions_hint.min_width as i32;
+                side_panel_w = if hint_min_w > 32 {
+                    std::cmp::max(self.layout.side_panel_width, hint_min_w)
+                } else {
+                    self.layout.side_panel_width
+                };
+                if self.layout.side_panel_behavior != "above" {
+                    shift_x = side_panel_w + self.layout.side_panel_border_gap;
+                }
+            }
+
+            let tiled_usable_w = (usable_w - shift_x).max(1);
+            let tiled_usable_x = if self.layout.side_panel_position == "right" || self.layout.side_panel_behavior == "above" {
+                usable_x
+            } else {
+                usable_x + shift_x
+            };
+
             let n_tiled = tiled_windows.len() as i32;
             let current_layout = if n_tiled > 0 {
                 self.get_mode_for_window(tiled_windows[0])
@@ -686,19 +737,19 @@ impl WindowManager {
                 let (x, y, w, h) = match current_layout {
                     crate::tiling::TilingMode::Cascade => {
                         crate::tiling::tile_cascade(
-                            usable_w, usable_h, gap, gap_top, gap_left, gap_right, gap_bottom,
+                            tiled_usable_w, usable_h, gap, gap_top, gap_left, gap_right, gap_bottom,
                             bw, cascade_offset, bar_height, n_tiled, idx as i32
                         )
                     }
                     crate::tiling::TilingMode::Grid => {
                         crate::tiling::tile_grid(
-                            usable_w, usable_h, gap, gap_top, gap_left, gap_right, gap_bottom,
+                            tiled_usable_w, usable_h, gap, gap_top, gap_left, gap_right, gap_bottom,
                             bw, bar_height, n_tiled, idx as i32
                         )
                     }
                     crate::tiling::TilingMode::Fullscreen => {
                         crate::tiling::tile_fullscreen(
-                            usable_w, usable_h, gap_top, gap_left, gap_right, gap_bottom,
+                            tiled_usable_w, usable_h, gap_top, gap_left, gap_right, gap_bottom,
                             bw, bar_height
                         )
                     }
@@ -707,7 +758,7 @@ impl WindowManager {
                     }
                 };
 
-                let final_x = usable_x + x;
+                let final_x = tiled_usable_x + x;
                 let final_y = usable_y + y;
 
                 (*win_ptr).rendering_requested.x = final_x;
@@ -724,7 +775,7 @@ impl WindowManager {
                 (*win_ptr).wm_requested.ssd = true;
 
                 let is_focused = win_ptr == focused_window;
-                let (r, g, b, mut a) = if is_focused {
+                let (r, g_val, b, mut a) = if is_focused {
                     (
                         self.layout.border_r,
                         self.layout.border_g,
@@ -749,7 +800,7 @@ impl WindowManager {
                     edges: crate::window::Edges { top: true, bottom: true, left: true, right: true },
                     width: bw as u32,
                     r,
-                    g,
+                    g: g_val,
                     b,
                     a,
                 };
@@ -758,10 +809,59 @@ impl WindowManager {
                 (*win_ptr).rendering_requested.opacity = if is_focused { 1.0f32 } else { 0.85f32 };
             }
 
+            let g = self.layout.side_panel_border_gap;
+            let dec_h = std::cmp::max(bw, 16);
+            for (sp_idx, &win_ptr) in side_panel_windows.iter().enumerate() {
+                if sp_idx == 0 {
+                    let sp_x = if self.layout.side_panel_position == "right" {
+                        usable_x + usable_w - side_panel_w - g + bw
+                    } else {
+                        usable_x + g + bw
+                    };
+                    let sp_y = bar_height + usable_y + dec_h + g;
+                    let sp_h = (usable_h - bar_height - (dec_h + bw) - 2 * g).max(1);
+                    let sp_w = (side_panel_w - bw * 2).max(1);
+
+                    (*win_ptr).rendering_requested.x = sp_x;
+                    (*win_ptr).rendering_requested.y = sp_y;
+                    (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+                        width: sp_w as u32,
+                        height: sp_h as u32,
+                    });
+                    (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+                        width: sp_w as u32,
+                        height: sp_h as u32,
+                    };
+                    (*win_ptr).wm_requested.tiled = 1 | 2 | 4 | 8;
+                    (*win_ptr).wm_requested.ssd = true;
+
+                    let opacity_factor = self.layout.side_panel_border_opacity as f32 / 100.0;
+                    let is_focused = win_ptr == focused_window;
+                    let r = self.layout.border_r;
+                    let g_color = self.layout.border_g;
+                    let b = self.layout.border_b;
+                    let a = (self.layout.border_a as f32 * opacity_factor) as u32;
+
+                    (*win_ptr).rendering_requested.border = crate::window::Border {
+                        edges: crate::window::Edges { top: true, bottom: true, left: true, right: true },
+                        width: bw as u32,
+                        r,
+                        g: g_color,
+                        b,
+                        a,
+                    };
+                    (*win_ptr).rendering_requested.blur = self.layout.window_blur;
+                    (*win_ptr).rendering_requested.opacity = if is_focused { 1.0f32 } else { 0.85f32 * opacity_factor };
+                } else {
+                    floating_windows.push(win_ptr);
+                }
+            }
+
+            let mut idx_floating = 0;
             for &win_ptr in &floating_windows {
                 let is_focused = win_ptr == focused_window;
                 let r = self.layout.border_r;
-                let g = self.layout.border_g;
+                let g_val = self.layout.border_g;
                 let b = self.layout.border_b;
                 let a = self.layout.border_a;
 
@@ -769,12 +869,97 @@ impl WindowManager {
                     edges: crate::window::Edges { top: true, bottom: true, left: true, right: true },
                     width: bw as u32,
                     r,
-                    g,
+                    g: g_val,
                     b,
                     a,
                 };
                 (*win_ptr).rendering_requested.blur = self.layout.window_blur;
                 (*win_ptr).rendering_requested.opacity = if is_focused { 1.0f32 } else { 0.90f32 };
+
+                if (*win_ptr).tiling_mode == crate::tiling::TilingMode::Popup {
+                    let hint_min_w = (*win_ptr).wm_scheduled.dimensions_hint.min_width as i32;
+                    let hint_min_h = (*win_ptr).wm_scheduled.dimensions_hint.min_height as i32;
+                    let fw = if (*win_ptr).box_geom.width > 0 {
+                        (*win_ptr).box_geom.width as i32
+                    } else if hint_min_w > 32 {
+                        hint_min_w
+                    } else {
+                        360
+                    };
+                    let fh = if (*win_ptr).box_geom.height > 0 {
+                        (*win_ptr).box_geom.height as i32
+                    } else if hint_min_h > 32 {
+                        hint_min_h
+                    } else {
+                        100
+                    };
+                    let fx = usable_x + usable_w - fw - self.layout.gap_right;
+                    let fy = usable_y + bar_height + self.layout.gap_top;
+
+                    (*win_ptr).rendering_requested.x = fx;
+                    (*win_ptr).rendering_requested.y = fy;
+                    (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+                        width: fw as u32,
+                        height: fh as u32,
+                    });
+                    (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+                        width: fw as u32,
+                        height: fh as u32,
+                    };
+                } else {
+                    let fbw = self.layout.floating_border_width;
+                    let mut fw = if (*win_ptr).box_geom.width > 0 {
+                        (*win_ptr).box_geom.width as i32
+                    } else if (*win_ptr).wm_scheduled.dimensions_hint.min_width > 32 {
+                        (*win_ptr).wm_scheduled.dimensions_hint.min_width as i32
+                    } else {
+                        usable_w * 2 / 3
+                    };
+                    let mut fh = if (*win_ptr).box_geom.height > 0 {
+                        (*win_ptr).box_geom.height as i32
+                    } else if (*win_ptr).wm_scheduled.dimensions_hint.min_height > 32 {
+                        (*win_ptr).wm_scheduled.dimensions_hint.min_height as i32
+                    } else {
+                        usable_h * 2 / 3
+                    };
+
+                    let max_w = usable_w - gap_left - gap_right;
+                    let max_h = usable_h - bar_height - gap_top - gap_bottom;
+                    fw = fw.clamp(1, max_w.max(1));
+                    fh = fh.clamp(1, max_h.max(1));
+
+                    let mut fx = if (*win_ptr).rendering_requested.x != 0 || (*win_ptr).rendering_requested.y != 0 {
+                        (*win_ptr).rendering_requested.x
+                    } else {
+                        usable_x + gap_left + fbw + cascade_offset * idx_floating
+                    };
+                    let mut fy = if (*win_ptr).rendering_requested.x != 0 || (*win_ptr).rendering_requested.y != 0 {
+                        (*win_ptr).rendering_requested.y
+                    } else {
+                        usable_y + gap_left + fbw + bar_height + gap_top + cascade_offset * idx_floating
+                    };
+
+                    let min_x = usable_x + gap_left;
+                    let max_x = usable_x + usable_w - gap_right - fw;
+                    let min_y = usable_y + bar_height + gap_top;
+                    let max_y = usable_y + usable_h - gap_bottom - fh;
+
+                    fx = fx.clamp(min_x, max_x.max(min_x));
+                    fy = fy.clamp(min_y, max_y.max(min_y));
+
+                    (*win_ptr).rendering_requested.x = fx;
+                    (*win_ptr).rendering_requested.y = fy;
+                    (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+                        width: fw as u32,
+                        height: fh as u32,
+                    });
+                    (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+                        width: fw as u32,
+                        height: fh as u32,
+                    };
+
+                    idx_floating += 1;
+                }
             }
         }
         self.update_status();
@@ -824,11 +1009,42 @@ impl WindowManager {
         }
     }
 
+    pub unsafe fn keep_status_bar_on_top(&mut self) {
+        let mut status_bar_windows = Vec::new();
+        for &win_ptr in self.windows.iter() {
+            if win_ptr.is_null() || (*win_ptr).closed {
+                continue;
+            }
+            if !matches!((*win_ptr).state, crate::window::WindowState::Mapped) {
+                continue;
+            }
+            if let Some(app_id) = (*win_ptr).get_app_id_string() {
+                if app_id == "cce-status-interface" {
+                    let link = &(*win_ptr).node.link;
+                    if !link.prev.is_null() && !link.next.is_null() {
+                        status_bar_windows.push(win_ptr);
+                    }
+                }
+            }
+        }
+        for win_ptr in status_bar_windows {
+            let node_link = &mut (*win_ptr).node.link as *mut ffi::wl_list as *mut WlList;
+            let list_head = &mut self.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
+            if (*node_link).next != list_head {
+                crate::server::wl_list_remove(node_link);
+                crate::server::wl_list_insert((*list_head).prev, node_link);
+            }
+        }
+    }
+
     pub unsafe fn raise_window(&mut self, window: *mut Window) {
         let node_link = &mut (*window).node.link as *mut ffi::wl_list as *mut WlList;
-        crate::server::wl_list_remove(node_link);
         let list_head = &mut self.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
-        crate::server::wl_list_insert((*list_head).prev, node_link);
+        if (*node_link).next != list_head {
+            crate::server::wl_list_remove(node_link);
+            crate::server::wl_list_insert((*list_head).prev, node_link);
+        }
+        self.keep_status_bar_on_top();
     }
 
     pub unsafe fn execute_action(&mut self, action: &crate::config::Action, command: Option<&str>) {
@@ -865,18 +1081,8 @@ impl WindowManager {
                     let mut matched_win: *mut Window = std::ptr::null_mut();
                     for &w in self.windows.iter() {
                         if !w.is_null() && !(*w).closed {
-                            let aid_ptr = (*w).get_app_id();
-                            let aid = if aid_ptr.is_null() {
-                                None
-                            } else {
-                                Some(std::ffi::CStr::from_ptr(aid_ptr).to_string_lossy().into_owned())
-                            };
-                            let title_ptr = (*w).get_title();
-                            let title = if title_ptr.is_null() {
-                                None
-                            } else {
-                                Some(std::ffi::CStr::from_ptr(title_ptr).to_string_lossy().into_owned())
-                            };
+                            let aid = (*w).get_app_id_string();
+                            let title = (*w).get_title_string();
 
                             let mut match_aid = false;
                             if let Some(ref aid_str) = aid {
@@ -900,12 +1106,7 @@ impl WindowManager {
                     }
 
                     if !matched_win.is_null() {
-                        let aid_ptr = (*matched_win).get_app_id();
-                        let aid = if aid_ptr.is_null() {
-                            String::new()
-                        } else {
-                            std::ffi::CStr::from_ptr(aid_ptr).to_string_lossy().into_owned()
-                        };
+                        let aid = (*matched_win).get_app_id_string().unwrap_or_default();
                         log::info!("toggle: closing window {:?}", aid);
                         (*matched_win).close();
                         if let Some(seat) = self.first_seat() {
@@ -942,6 +1143,58 @@ impl WindowManager {
                     }
                 }
             }
+            Action::FocusNext | Action::FocusPrev => {
+                if let Some(seat) = self.first_seat() {
+                    let focused_win = if let crate::seat::Focus::Window(fw) = (*seat).focused {
+                        fw
+                    } else {
+                        std::ptr::null_mut()
+                    };
+
+                    let render_list = &mut self.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
+                    let mut curr = (*render_list).next;
+                    let mut visible_windows = Vec::new();
+                    while curr != render_list {
+                        let next = (*curr).next;
+                        let node = crate::container_of!(curr, crate::wm_node::WmNode, link);
+                        if let crate::wm_node::WmNodeType::Window(window) = (*node).get() {
+                            if !window.is_null() && !(*window).closed && !(*window).minimized && ((*window).tags & self.active_tags) != 0 {
+                                let is_status_bar = (*window).get_app_id_string()
+                                    .map_or(false, |aid| aid == "cce-status-interface");
+                                if !is_status_bar {
+                                    visible_windows.push(window);
+                                }
+                            }
+                        }
+                        curr = next;
+                    }
+
+                    let n = visible_windows.len();
+                    if n > 0 {
+                        let current_idx = visible_windows.iter().position(|&w| w == focused_win);
+                        let target_idx = match current_idx {
+                            Some(idx) => {
+                                if *action == Action::FocusNext {
+                                    (idx + 1) % n
+                                } else {
+                                    (idx + n - 1) % n
+                                }
+                            }
+                            None => {
+                                if *action == Action::FocusNext {
+                                    0
+                                } else {
+                                    n - 1
+                                }
+                            }
+                        };
+                        let target_win = visible_windows[target_idx];
+                        (*seat).focus(crate::seat::Focus::Window(target_win));
+                        self.raise_window(target_win);
+                        self.dirty_windowing();
+                    }
+                }
+            }
             Action::Exit => {
                 log::info!("monolithic execute_action: Exit requested");
                 ffi::wl_display_terminate((*self.server).wl_server);
@@ -1047,17 +1300,7 @@ impl WindowManager {
                     }
                 }
             }
-            Action::Reload | Action::Restart => {
-                if let Some(cfg_path) = crate::config::default_config_path() {
-                    log::info!("Reloading configuration from {}", cfg_path);
-                    if let Err(e) = crate::config::parse_config(&cfg_path, self) {
-                        log::error!("Failed to reload config: {}", e);
-                    } else {
-                        self.dirty_windowing();
-                    }
-                }
-            }
-            Action::View1 | Action::View2 | Action::View3 | Action::View4 => {
+             Action::View1 | Action::View2 | Action::View3 | Action::View4 => {
                 let tag = match action {
                     Action::View1 => 1,
                     Action::View2 => 2,
@@ -1197,18 +1440,8 @@ impl WindowManager {
                     let mut target: *mut Window = std::ptr::null_mut();
                     for &w in self.windows.iter() {
                         if !w.is_null() && !(*w).closed && !(*w).minimized && ((*w).tags & self.active_tags) != 0 {
-                            let aid_ptr = (*w).get_app_id();
-                            let aid = if aid_ptr.is_null() {
-                                None
-                            } else {
-                                Some(std::ffi::CStr::from_ptr(aid_ptr).to_string_lossy().into_owned())
-                            };
-                            let title_ptr = (*w).get_title();
-                            let title = if title_ptr.is_null() {
-                                None
-                            } else {
-                                Some(std::ffi::CStr::from_ptr(title_ptr).to_string_lossy().into_owned())
-                            };
+                            let aid = (*w).get_app_id_string();
+                            let title = (*w).get_title_string();
 
                             let mut match_found = false;
                             if let Some(ref aid_str) = aid {
@@ -1239,10 +1472,6 @@ impl WindowManager {
                 self.execute_action(&crate::config::Action::Exit, None);
                 "ok\n".to_string()
             }
-            "reload" | "restart" => {
-                self.execute_action(&crate::config::Action::Reload, None);
-                "ok\n".to_string()
-            }
             "retile" => {
                 self.dirty_windowing();
                 "ok\n".to_string()
@@ -1263,17 +1492,26 @@ impl WindowManager {
                     "gap_left" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap_left = v; } }
                     "gap_right" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap_right = v; } }
                     "gap_bottom" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap_bottom = v; } }
-                    "offset" => { if let Ok(v) = val.parse::<i32>() { self.layout.cascade_offset = v; } }
+                    "offset" | "cascade_offset" => { if let Ok(v) = val.parse::<i32>() { self.layout.cascade_offset = v; } }
                     "grid_gap" => { if let Ok(v) = val.parse::<i32>() { self.layout.grid_gap = v; } }
                     "bar_height" => { if let Ok(v) = val.parse::<i32>() { self.layout.bar_height = v; } }
                     "border_width" => { if let Ok(v) = val.parse::<i32>() { self.layout.border_width = v; } }
                     "fullscreen_border_width" => { if let Ok(v) = val.parse::<i32>() { self.layout.fullscreen_border_width = v; } }
+                    "cascade_border_width" => { if let Ok(v) = val.parse::<i32>() { self.layout.cascade_border_width = v; } }
+                    "grid_border_width" => { if let Ok(v) = val.parse::<i32>() { self.layout.grid_border_width = v; } }
+                    "floating_border_width" => { if let Ok(v) = val.parse::<i32>() { self.layout.floating_border_width = v; } }
+                    "transition_duration" => { if let Ok(v) = val.parse::<i32>() { self.layout.transition_duration = v; } }
                     "border_color" => {
                         let border_color_val = crate::config::parse_hex_color(val);
                         self.layout.border_r = (border_color_val >> 16) & 0xFF;
                         self.layout.border_g = (border_color_val >> 8) & 0xFF;
                         self.layout.border_b = border_color_val & 0xFF;
                     }
+                    "side_panel_width" => { if let Ok(v) = val.parse::<i32>() { self.layout.side_panel_width = v; } }
+                    "side_panel_behavior" => { self.layout.side_panel_behavior = val.to_string(); }
+                    "side_panel_position" => { self.layout.side_panel_position = val.to_string(); }
+                    "side_panel_border_gap" => { if let Ok(v) = val.parse::<i32>() { self.layout.side_panel_border_gap = v; } }
+                    "side_panel_border_opacity" => { if let Ok(v) = val.parse::<i32>() { self.layout.side_panel_border_opacity = v; } }
                     _ => return format!("error: unknown layout key: {}\n", key),
                 }
                 self.dirty_windowing();
@@ -1352,14 +1590,14 @@ unsafe extern "C" fn dirty_idle_callback(data: *mut std::ffi::c_void) {
     }
     (*wm).dirty_idle = std::ptr::null_mut();
     
-    if (*wm).scheduled.dirty || (*wm).scheduled.dirty_lazy {
-        (*wm).scheduled.dirty = true;
-        (*wm).scheduled.dirty_lazy = false;
-        (*wm).manage_start();
-    }
-    
-    if (*wm).rendering_scheduled.dirty && matches!((*wm).state, WindowManagerState::Idle) {
-        (*wm).render_start();
+    if matches!((*wm).state, WindowManagerState::Idle) {
+        if (*wm).scheduled.dirty || (*wm).scheduled.dirty_lazy {
+            (*wm).scheduled.dirty = true;
+            (*wm).scheduled.dirty_lazy = false;
+            (*wm).manage_start();
+        } else if (*wm).rendering_scheduled.dirty {
+            (*wm).render_start();
+        }
     }
 }
 
@@ -1519,6 +1757,15 @@ unsafe extern "C" fn bind(
         return;
     }
 
+    let mut pid = 0;
+    let mut uid = 0;
+    let mut gid = 0;
+    ffi::wl_client_get_credentials(client, &mut pid, &mut uid, &mut gid);
+    let cmdline = std::fs::read_to_string(format!("/proc/{}/cmdline", pid))
+        .unwrap_or_default()
+        .replace('\0', " ");
+    log::info!("Client binding river_window_manager_v1: PID={}, cmdline='{}'", pid, cmdline);
+
     let resource = ffi::wl_resource_create(client, &ffi::river_window_manager_v1_interface, version as i32, id);
     if resource.is_null() {
         ffi::wl_client_post_no_memory(client);
@@ -1527,6 +1774,7 @@ unsafe extern "C" fn bind(
     }
 
     if !(*wm).object.is_null() {
+        log::warn!("river_window_manager_v1 already bound, rejecting new client PID={}", pid);
         ffi::wl_resource_post_event(resource, ffi::RIVER_WINDOW_MANAGER_V1_UNAVAILABLE);
         ffi::wl_resource_set_implementation(
             resource,
diff --git a/src/server/wm_node.rs b/src/server/wm_node.rs
index d7af93d..de11417 100644
--- a/src/server/wm_node.rs
+++ b/src/server/wm_node.rs
@@ -142,10 +142,12 @@ unsafe extern "C" fn node_place_top(
     if !(*server).wm.ensure_rendering() {
         return;
     }
-    wl_list_remove(&mut (*node).link as *mut ffi::wl_list as *mut WlList);
-    
+    let node_link = &mut (*node).link as *mut ffi::wl_list as *mut WlList;
     let list_head = &mut (*server).wm.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
-    wl_list_insert((*list_head).prev, &mut (*node).link as *mut ffi::wl_list as *mut WlList);
+    if (*node_link).next != list_head {
+        wl_list_remove(node_link);
+        wl_list_insert((*list_head).prev, node_link);
+    }
 }
 
 unsafe extern "C" fn node_place_bottom(
@@ -163,10 +165,12 @@ unsafe extern "C" fn node_place_bottom(
     if !(*server).wm.ensure_rendering() {
         return;
     }
-    wl_list_remove(&mut (*node).link as *mut ffi::wl_list as *mut WlList);
-    
+    let node_link = &mut (*node).link as *mut ffi::wl_list as *mut WlList;
     let list_head = &mut (*server).wm.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
-    wl_list_insert(list_head, &mut (*node).link as *mut ffi::wl_list as *mut WlList);
+    if (*node_link).prev != list_head {
+        wl_list_remove(node_link);
+        wl_list_insert(list_head, node_link);
+    }
 }
 
 unsafe extern "C" fn node_place_above(
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index 0843c46..0726c28 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -411,7 +411,6 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
     let capture_node = &mut (*(*window).capture_scene).tree as *mut ffi::wlr_scene_tree as *mut ffi::wlr_scene_node;
     let mut geom = std::mem::zeroed();
     ffi::river_wlr_xdg_surface_get_geometry(base, &mut geom);
-    override_geometry_if_needed(window, &mut geom);
     ffi::wlr_scene_subsurface_tree_set_clip(capture_node, &geom);
 
     let mut min_w = 0;
@@ -443,7 +442,6 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
             let old_geometry = (*toplevel).geometry;
             let mut new_geometry = std::mem::zeroed();
             ffi::river_wlr_xdg_surface_get_geometry(base, &mut new_geometry);
-            override_geometry_if_needed(window, &mut new_geometry);
             (*toplevel).geometry = new_geometry;
 
             let size_changed = new_geometry.width != old_geometry.width || new_geometry.height != old_geometry.height;
@@ -464,7 +462,6 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
         ConfigureState::Acked | ConfigureState::TimedOutAcked => {
             let mut new_geometry = std::mem::zeroed();
             ffi::river_wlr_xdg_surface_get_geometry(base, &mut new_geometry);
-            override_geometry_if_needed(window, &mut new_geometry);
             (*toplevel).geometry = new_geometry;
 
             (*window).rendering_scheduled.width = new_geometry.width as u32;
@@ -658,45 +655,4 @@ impl XdgDecoration {
     }
 }
 
-pub unsafe fn override_geometry_if_needed(window: *mut crate::window::Window, geom: &mut ffi::wlr_box) {
-    let is_chromium = (*window).is_chromium_electron();
-    if is_chromium {
-        log::info!(
-            "CHROMIUM COMMIT before override: x={} y={} w={} h={} wm_ssd={}",
-            geom.x,
-            geom.y,
-            geom.width,
-            geom.height,
-            (*window).wm_requested.ssd,
-        );
-    }
-    if (*window).wm_requested.ssd && is_chromium {
-        if geom.x == 10 || geom.y == 10 {
-            (*window).margin_x = 10;
-            (*window).margin_y = 10;
-        } else if geom.x == 6 || geom.y == 6 {
-            (*window).margin_x = 6;
-            (*window).margin_y = 6;
-        }
-        
-        let margin_x = if (*window).margin_x > 0 { (*window).margin_x } else { 10 };
-        let margin_y = if (*window).margin_y > 0 { (*window).margin_y } else { 10 };
-        
-        let is_antigravity = (*window).is_antigravity();
-        let (left_margin, right_margin) = if margin_x == 10 && !is_antigravity { (10, 34) } else { (margin_x, margin_x) };
-        let (top_margin, bottom_margin) = if margin_y == 10 && !is_antigravity { (10, 34) } else { (margin_y, margin_y) };
-        
-        geom.x = left_margin;
-        geom.y = top_margin;
-        geom.width = geom.width.saturating_sub(left_margin + right_margin);
-        geom.height = geom.height.saturating_sub(top_margin + bottom_margin);
-        
-        log::info!(
-            "CHROMIUM COMMIT after override: x={} y={} w={} h={}",
-            geom.x,
-            geom.y,
-            geom.width,
-            geom.height,
-        );
-    }
-}
+