Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
Support 8 compass directions for status module layout (nw, n, ne, sw, s, se, w, e) with center alignment
src/server/cursor.rs | 44 +++++++++++--
src/server/window.rs | 6 ++
src/server/window_manager.rs | 147 ++++++++++++++++++++++++++++++++++++-------
3 files changed, 172 insertions(+), 25 deletions(-)
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 66769d1..78b3138 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -1069,9 +1069,12 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
let win = op.window_ptr;
let mut closest_edge = crate::window::StatusEdge::Top;
let mut min_dist = f64::MAX;
+ let app_id = (*win).get_app_id_string().unwrap_or_default();
+ log::info!("[StatusRelease] Released status window: app_id={}, lx={}, ly={}", app_id, lx, ly);
let outputs_list = &mut (*server).om.outputs as *mut ffi::wl_list as *mut WlList;
let mut curr_out = (*outputs_list).next;
+ let mut found_out = false;
while curr_out != outputs_list {
let output = crate::container_of!(curr_out, crate::output::Output, link);
if (*output).sent.state == crate::output::OutputStateValue::Enabled {
@@ -1081,22 +1084,55 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
let ow = wlr_box.width as f64;
let oh = wlr_box.height as f64;
+ log::info!("[StatusRelease] Checking output: box_geom=({}, {}, {}, {})", ox, oy, ow, oh);
if lx >= ox && lx <= ox + ow && ly >= oy && ly <= oy + oh {
+ found_out = true;
let dt = ly - oy;
let db = (oy + oh) - ly;
let dl = lx - ox;
let dr = (ox + ow) - lx;
- if dt < min_dist { min_dist = dt; closest_edge = crate::window::StatusEdge::Top; }
- if db < min_dist { min_dist = db; closest_edge = crate::window::StatusEdge::Bottom; }
- if dl < min_dist { min_dist = dl; closest_edge = crate::window::StatusEdge::Left; }
- if dr < min_dist { min_dist = dr; closest_edge = crate::window::StatusEdge::Right; }
+ log::info!("[StatusRelease] Distances: top={}, bottom={}, left={}, right={}", dt, db, dl, dr);
+ enum EdgeBasic { Top, Bottom, Left, Right }
+ let mut edge = EdgeBasic::Top;
+ if dt < min_dist { min_dist = dt; edge = EdgeBasic::Top; }
+ if db < min_dist { min_dist = db; edge = EdgeBasic::Bottom; }
+ if dl < min_dist { min_dist = dl; edge = EdgeBasic::Left; }
+ if dr < min_dist { min_dist = dr; edge = EdgeBasic::Right; }
+
+ match edge {
+ EdgeBasic::Top => {
+ if lx < ox + ow / 3.0 {
+ closest_edge = crate::window::StatusEdge::TopLeft;
+ } else if lx > ox + 2.0 * ow / 3.0 {
+ closest_edge = crate::window::StatusEdge::TopRight;
+ } else {
+ closest_edge = crate::window::StatusEdge::TopCenter;
+ }
+ }
+ EdgeBasic::Bottom => {
+ if lx < ox + ow / 3.0 {
+ closest_edge = crate::window::StatusEdge::BottomLeft;
+ } else if lx > ox + 2.0 * ow / 3.0 {
+ closest_edge = crate::window::StatusEdge::BottomRight;
+ } else {
+ closest_edge = crate::window::StatusEdge::BottomCenter;
+ }
+ }
+ EdgeBasic::Left => {
+ closest_edge = crate::window::StatusEdge::Left;
+ }
+ EdgeBasic::Right => {
+ closest_edge = crate::window::StatusEdge::Right;
+ }
+ }
break;
}
}
curr_out = (*curr_out).next;
}
+ log::info!("[StatusRelease] Snapping app_id={} closest_edge={:?}, found_out={}", app_id, closest_edge, found_out);
(*win).status_edge = closest_edge;
seat.op_end();
cursor.pressed.remove(&(*event).button);
diff --git a/src/server/window.rs b/src/server/window.rs
index 5c8e60a..02af0c8 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -265,6 +265,12 @@ pub struct Window {
pub enum StatusEdge {
Top,
Bottom,
+ TopLeft,
+ TopCenter,
+ TopRight,
+ BottomLeft,
+ BottomCenter,
+ BottomRight,
Left,
Right,
}
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 702fe50..0cd8bae 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1172,18 +1172,49 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
let mut has_left = false;
let mut has_right = false;
+ let resolve_edge = |win_ptr: *mut Window| {
+ let edge = (*win_ptr).status_edge;
+ if edge == crate::window::StatusEdge::Top {
+ if let Some(app_id) = (*win_ptr).get_app_id_string() {
+ if app_id.contains("viewport") {
+ crate::window::StatusEdge::TopLeft
+ } else if app_id.contains("window") {
+ crate::window::StatusEdge::TopCenter
+ } else {
+ crate::window::StatusEdge::TopRight
+ }
+ } else {
+ crate::window::StatusEdge::TopLeft
+ }
+ } else if edge == crate::window::StatusEdge::Bottom {
+ if let Some(app_id) = (*win_ptr).get_app_id_string() {
+ if app_id.contains("viewport") {
+ crate::window::StatusEdge::BottomLeft
+ } else if app_id.contains("window") {
+ crate::window::StatusEdge::BottomCenter
+ } else {
+ crate::window::StatusEdge::BottomRight
+ }
+ } else {
+ crate::window::StatusEdge::BottomLeft
+ }
+ } else {
+ edge
+ }
+ };
+
for &win_ptr in self.windows.iter() {
if win_ptr.is_null() || (*win_ptr).closed {
continue;
}
if (*win_ptr).is_status_bar() {
- match (*win_ptr).status_edge {
- crate::window::StatusEdge::Top => {
+ match resolve_edge(win_ptr) {
+ crate::window::StatusEdge::Top | crate::window::StatusEdge::TopLeft | crate::window::StatusEdge::TopCenter | crate::window::StatusEdge::TopRight => {
if !self.status_hide_mode {
has_top = true;
}
}
- crate::window::StatusEdge::Bottom => {
+ crate::window::StatusEdge::Bottom | crate::window::StatusEdge::BottomLeft | crate::window::StatusEdge::BottomCenter | crate::window::StatusEdge::BottomRight => {
has_bottom = true;
}
crate::window::StatusEdge::Left => {
@@ -1601,10 +1632,11 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
let bar_h = self.layout.bar_height as u32;
let spacing = 12;
let margin = 12;
-
let mut top_left = Vec::new();
+ let mut top_center = Vec::new();
let mut top_right = Vec::new();
let mut bottom_left = Vec::new();
+ let mut bottom_center = Vec::new();
let mut bottom_right = Vec::new();
let mut left_side = Vec::new();
let mut right_side = Vec::new();
@@ -1637,22 +1669,25 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
continue;
}
- match (*win_ptr).status_edge {
- crate::window::StatusEdge::Top => {
- if app_id.starts_with("cce-status-left-") {
- top_left.push(win_ptr);
- } else if app_id.starts_with("cce-status-right-") {
- top_right.push(win_ptr);
- } else {
- full_top.push(win_ptr);
- }
+ let edge = resolve_edge(win_ptr);
+ match edge {
+ crate::window::StatusEdge::TopLeft => {
+ top_left.push(win_ptr);
}
- crate::window::StatusEdge::Bottom => {
- if app_id.starts_with("cce-status-left-") {
- bottom_left.push(win_ptr);
- } else {
- bottom_right.push(win_ptr);
- }
+ crate::window::StatusEdge::TopCenter => {
+ top_center.push(win_ptr);
+ }
+ crate::window::StatusEdge::TopRight => {
+ top_right.push(win_ptr);
+ }
+ crate::window::StatusEdge::BottomLeft => {
+ bottom_left.push(win_ptr);
+ }
+ crate::window::StatusEdge::BottomCenter => {
+ bottom_center.push(win_ptr);
+ }
+ crate::window::StatusEdge::BottomRight => {
+ bottom_right.push(win_ptr);
}
crate::window::StatusEdge::Left => {
left_side.push(win_ptr);
@@ -1660,11 +1695,17 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
crate::window::StatusEdge::Right => {
right_side.push(win_ptr);
}
+ _ => {
+ full_top.push(win_ptr);
+ }
}
+ log::info!("[ArrangeStatus] app_id={} status_edge={:?}", app_id, (*win_ptr).status_edge);
}
}
}
+ log::info!("[ArrangeStatus] top_left_len={}, top_center_len={}, top_right_len={}, left_side_len={}", top_left.len(), top_center.len(), top_right.len(), left_side.len());
+
const LEFT_ORDER: &[&str] = &["viewport", "window"];
const RIGHT_ORDER: &[&str] = &["tray", "cpu", "memory", "brightness", "volume", "battery", "clock"];
@@ -1685,8 +1726,10 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
};
sort_left(&mut top_left);
+ sort_left(&mut top_center);
sort_right(&mut top_right);
sort_left(&mut bottom_left);
+ sort_left(&mut bottom_center);
sort_right(&mut bottom_right);
// 1. Top Edge
@@ -1705,14 +1748,17 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
}
let top_right_boundary = wlr_box.x + wlr_box.width - margin - top_right_width_needed;
+ // 1a. Left Group (TopLeft / nw)
let mut cur_left_x = wlr_box.x + margin;
- for win_ptr in top_left {
+ for &win_ptr in &top_left {
let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);
let mut w = if prev_len > 0 { prev_len as u32 } else { 100 };
let max_allowed_w = top_right_boundary - cur_left_x - spacing;
if w as i32 > max_allowed_w {
w = std::cmp::max(max_allowed_w, 20) as u32;
}
+ let app_id = (*win_ptr).get_app_id_string().unwrap_or_default();
+ log::info!("[TopLeftLayout] app_id={} x={}, w={}", app_id, cur_left_x, w);
(*win_ptr).rendering_requested.x = cur_left_x;
(*win_ptr).rendering_requested.y = status_y_top;
(*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions { width: w, height: bar_h });
@@ -1720,6 +1766,36 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
cur_left_x += w as i32 + spacing;
}
+ // 1b. Center Group (TopCenter / n)
+ let mut top_center_width_needed = 0;
+ for &win_ptr in &top_center {
+ let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);
+ let w = if prev_len > 0 { prev_len as u32 } else { 100 };
+ top_center_width_needed += w as i32 + spacing;
+ }
+ if top_center_width_needed > 0 {
+ top_center_width_needed -= spacing;
+ }
+ let center_start_x = wlr_box.x + (wlr_box.width - top_center_width_needed) / 2;
+ let mut cur_center_x = std::cmp::max(center_start_x, cur_left_x + spacing);
+
+ for &win_ptr in &top_center {
+ let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);
+ let mut w = if prev_len > 0 { prev_len as u32 } else { 100 };
+ let max_allowed_w = top_right_boundary - cur_center_x - spacing;
+ if w as i32 > max_allowed_w {
+ w = std::cmp::max(max_allowed_w, 20) as u32;
+ }
+ let app_id = (*win_ptr).get_app_id_string().unwrap_or_default();
+ log::info!("[TopCenterLayout] app_id={} x={}, w={}", app_id, cur_center_x, w);
+ (*win_ptr).rendering_requested.x = cur_center_x;
+ (*win_ptr).rendering_requested.y = status_y_top;
+ (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions { width: w, height: bar_h });
+ (*win_ptr).wm_requested.bounds = crate::window::Dimensions { width: w, height: bar_h };
+ cur_center_x += w as i32 + spacing;
+ }
+
+ // 1c. Right Group (TopRight / ne)
let mut cur_right_x = wlr_box.x + wlr_box.width - margin;
for win_ptr in top_right.into_iter().rev() {
let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);
@@ -1750,8 +1826,9 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
}
let bottom_right_boundary = wlr_box.x + wlr_box.width - margin - bottom_right_width_needed;
+ // 2a. Left Group (BottomLeft / sw)
let mut cur_left_x = wlr_box.x + margin;
- for win_ptr in bottom_left {
+ for &win_ptr in &bottom_left {
let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);
let mut w = if prev_len > 0 { prev_len as u32 } else { 100 };
let max_allowed_w = bottom_right_boundary - cur_left_x - spacing;
@@ -1765,6 +1842,34 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
cur_left_x += w as i32 + spacing;
}
+ // 2b. Center Group (BottomCenter / s)
+ let mut bottom_center_width_needed = 0;
+ for &win_ptr in &bottom_center {
+ let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);
+ let w = if prev_len > 0 { prev_len as u32 } else { 100 };
+ bottom_center_width_needed += w as i32 + spacing;
+ }
+ if bottom_center_width_needed > 0 {
+ bottom_center_width_needed -= spacing;
+ }
+ let center_start_x = wlr_box.x + (wlr_box.width - bottom_center_width_needed) / 2;
+ let mut cur_center_x = std::cmp::max(center_start_x, cur_left_x + spacing);
+
+ for &win_ptr in &bottom_center {
+ let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);
+ let mut w = if prev_len > 0 { prev_len as u32 } else { 100 };
+ let max_allowed_w = bottom_right_boundary - cur_center_x - spacing;
+ if w as i32 > max_allowed_w {
+ w = std::cmp::max(max_allowed_w, 20) as u32;
+ }
+ (*win_ptr).rendering_requested.x = cur_center_x;
+ (*win_ptr).rendering_requested.y = status_y_bottom;
+ (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions { width: w, height: bar_h });
+ (*win_ptr).wm_requested.bounds = crate::window::Dimensions { width: w, height: bar_h };
+ cur_center_x += w as i32 + spacing;
+ }
+
+ // 2c. Right Group (BottomRight / se)
let mut cur_right_x = wlr_box.x + wlr_box.width - margin;
for win_ptr in bottom_right.into_iter().rev() {
let prev_len = std::cmp::max((*win_ptr).box_geom.width, (*win_ptr).box_geom.height);