Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
Implement standalone Maximized window mode with grid snapping
src/cce_ctl.rs | 4 +-
src/server/cce_window_management.rs | 2 +-
src/server/config.rs | 1 +
src/server/cursor.rs | 48 +++++++++++++++++
src/server/tiling.rs | 2 +
src/server/window.rs | 14 ++++-
src/server/window_manager.rs | 101 ++++++++++++++++++++++++++++++++++++
src/server/xdg_toplevel.rs | 2 +-
src/server/xwayland_window.rs | 2 +-
9 files changed, 170 insertions(+), 6 deletions(-)
diff --git a/src/cce_ctl.rs b/src/cce_ctl.rs
index 17940aa..c23a0e5 100644
--- a/src/cce_ctl.rs
+++ b/src/cce_ctl.rs
@@ -51,8 +51,8 @@ fn usage(name: &str, to_stderr: bool) {
print(" pbind <mods> <button> <action>");
print(" retile");
print(" set-viewport <1-4>");
- print(" mode <cascade|grid|fullscreen|floating|popup> <app_id> [title]");
- print(" viewport-layout <1-4> <cascade|grid|fullscreen|floating|popup>");
+ print(" mode <cascade|grid|fullscreen|floating|popup|maximized> <app_id> [title]");
+ print(" viewport-layout <1-4> <cascade|grid|fullscreen|floating|popup|maximized>");
print(" pointer-location");
print(" pointer-move-to <x> <y>");
print(" pointer-move-by <dx> <dy>");
diff --git a/src/server/cce_window_management.rs b/src/server/cce_window_management.rs
index e88b133..881e716 100644
--- a/src/server/cce_window_management.rs
+++ b/src/server/cce_window_management.rs
@@ -155,7 +155,7 @@ unsafe extern "C" fn toplevel_set_maximized(
let server = (*data).server;
let window_key = (*data).window_key;
if let Some(window) = resolve_window(server, window_key) {
- (*window).tiling_mode = crate::tiling::TilingMode::Cascade;
+ (*window).tiling_mode = crate::tiling::TilingMode::Maximized;
(*window).mode_locked = true;
(*window).wm_scheduled.maximize_requested = crate::window::MaximizeRequest::Maximize;
(*server).wm.dirty_windowing();
diff --git a/src/server/config.rs b/src/server/config.rs
index 12df8fe..cbd0ac2 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -485,6 +485,7 @@ pub fn parse_tiling_mode(s: &str) -> TilingMode {
"popup" => TilingMode::Popup,
"sidepanel" | "side_panel" | "side-panel" | "pinned" => TilingMode::Pinned,
"status" => TilingMode::Status,
+ "maximized" => TilingMode::Maximized,
_ => TilingMode::Cascade,
}
}
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 5a4b3ff..eed5ae5 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -669,6 +669,22 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
&& (*target_win).tiling_mode != crate::tiling::TilingMode::Fullscreen
&& (*target_win).tiling_mode != crate::tiling::TilingMode::Pinned
{
+ if (*target_win).was_maximized {
+ (*target_win).box_geom.width = (*target_win).saved_maximized_width;
+ (*target_win).box_geom.height = (*target_win).saved_maximized_height;
+ (*target_win).virtual_x = (*target_win).saved_maximized_virtual_x;
+ (*target_win).virtual_y = (*target_win).saved_maximized_virtual_y;
+ (*target_win).was_maximized = false;
+
+ (*target_win).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: (*target_win).saved_maximized_width as u32,
+ height: (*target_win).saved_maximized_height as u32,
+ });
+ (*target_win).wm_requested.bounds = crate::window::Dimensions {
+ width: (*target_win).saved_maximized_width as u32,
+ height: (*target_win).saved_maximized_height as u32,
+ };
+ }
(*target_win).tiling_mode = crate::tiling::TilingMode::Floating;
(*target_win).mode_locked = true;
}
@@ -742,6 +758,22 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
BorderZone::Resize(edges) => {
if (*event).button == 0x110 { // BTN_LEFT
if initial_mode != crate::tiling::TilingMode::Floating {
+ if (*border_target_win).was_maximized {
+ (*border_target_win).box_geom.width = (*border_target_win).saved_maximized_width;
+ (*border_target_win).box_geom.height = (*border_target_win).saved_maximized_height;
+ (*border_target_win).virtual_x = (*border_target_win).saved_maximized_virtual_x;
+ (*border_target_win).virtual_y = (*border_target_win).saved_maximized_virtual_y;
+ (*border_target_win).was_maximized = false;
+
+ (*border_target_win).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: (*border_target_win).saved_maximized_width as u32,
+ height: (*border_target_win).saved_maximized_height as u32,
+ });
+ (*border_target_win).wm_requested.bounds = crate::window::Dimensions {
+ width: (*border_target_win).saved_maximized_width as u32,
+ height: (*border_target_win).saved_maximized_height as u32,
+ };
+ }
(*border_target_win).tiling_mode = crate::tiling::TilingMode::Floating;
(*border_target_win).mode_locked = true;
}
@@ -782,6 +814,22 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
if initial_mode != crate::tiling::TilingMode::Floating
&& initial_mode != crate::tiling::TilingMode::Pinned
{
+ if (*border_target_win).was_maximized {
+ (*border_target_win).box_geom.width = (*border_target_win).saved_maximized_width;
+ (*border_target_win).box_geom.height = (*border_target_win).saved_maximized_height;
+ (*border_target_win).virtual_x = (*border_target_win).saved_maximized_virtual_x;
+ (*border_target_win).virtual_y = (*border_target_win).saved_maximized_virtual_y;
+ (*border_target_win).was_maximized = false;
+
+ (*border_target_win).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: (*border_target_win).saved_maximized_width as u32,
+ height: (*border_target_win).saved_maximized_height as u32,
+ });
+ (*border_target_win).wm_requested.bounds = crate::window::Dimensions {
+ width: (*border_target_win).saved_maximized_width as u32,
+ height: (*border_target_win).saved_maximized_height as u32,
+ };
+ }
(*border_target_win).tiling_mode = crate::tiling::TilingMode::Floating;
(*border_target_win).mode_locked = true;
}
diff --git a/src/server/tiling.rs b/src/server/tiling.rs
index 05e5c28..cb3e799 100644
--- a/src/server/tiling.rs
+++ b/src/server/tiling.rs
@@ -9,6 +9,7 @@ pub enum TilingMode {
Popup,
Pinned,
Status,
+ Maximized,
}
impl TilingMode {
@@ -21,6 +22,7 @@ impl TilingMode {
TilingMode::Popup => "Popup",
TilingMode::Pinned => "Pinned",
TilingMode::Status => "Status",
+ TilingMode::Maximized => "Maximized",
}
}
}
diff --git a/src/server/window.rs b/src/server/window.rs
index 1c08ca8..174a11b 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -235,6 +235,11 @@ pub struct Window {
pub saved_height: i32,
pub saved_virtual_x: f64,
pub saved_virtual_y: f64,
+ pub was_maximized: bool,
+ pub saved_maximized_width: i32,
+ pub saved_maximized_height: i32,
+ pub saved_maximized_virtual_x: f64,
+ pub saved_maximized_virtual_y: f64,
pub wm_scheduled: WmScheduledState,
pub wm_sent: WmSentState,
@@ -402,6 +407,11 @@ impl Window {
saved_height: 0,
saved_virtual_x: 0.0,
saved_virtual_y: 0.0,
+ was_maximized: false,
+ saved_maximized_width: 0,
+ saved_maximized_height: 0,
+ saved_maximized_virtual_x: 0.0,
+ saved_maximized_virtual_y: 0.0,
wm_scheduled: WmScheduledState {
dimensions_hint: DimensionsHint { min_width: 0, min_height: 0, max_width: 0, max_height: 0 },
decoration_hint: ffi::zcce_window_v1_decoration_hint_ZCCE_WINDOW_V1_DECORATION_HINT_ONLY_SUPPORTS_CSD,
@@ -1350,7 +1360,9 @@ impl Window {
};
self.wm_requested.dimensions = None;
- let is_maximized_layout = self.tiling_mode == crate::tiling::TilingMode::Cascade || self.tiling_mode == crate::tiling::TilingMode::Grid;
+ let is_maximized_layout = self.tiling_mode == crate::tiling::TilingMode::Cascade
+ || self.tiling_mode == crate::tiling::TilingMode::Grid
+ || self.tiling_mode == crate::tiling::TilingMode::Maximized;
self.configure_scheduled = Configure {
width,
height,
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 7bc525e..e9f35c0 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1126,6 +1126,63 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
}
}
+ // Manage entering/exiting Maximized state for normal windows
+ for &win_ptr in &normal_windows {
+ let mode = (*win_ptr).tiling_mode;
+ if mode == crate::tiling::TilingMode::Maximized && !(*win_ptr).was_maximized {
+ // Entering Maximized mode
+ let mut w = (*win_ptr).box_geom.width;
+ let mut h = (*win_ptr).box_geom.height;
+ if w <= 0 {
+ w = if (*win_ptr).wm_scheduled.dimensions_hint.min_width > 32 {
+ (*win_ptr).wm_scheduled.dimensions_hint.min_width as i32
+ } else {
+ 800
+ };
+ }
+ if h <= 0 {
+ h = if (*win_ptr).wm_scheduled.dimensions_hint.min_height > 32 {
+ (*win_ptr).wm_scheduled.dimensions_hint.min_height as i32
+ } else {
+ 600
+ };
+ }
+ (*win_ptr).saved_maximized_width = w;
+ (*win_ptr).saved_maximized_height = h;
+ (*win_ptr).saved_maximized_virtual_x = (*win_ptr).virtual_x;
+ (*win_ptr).saved_maximized_virtual_y = (*win_ptr).virtual_y;
+ (*win_ptr).was_maximized = true;
+ log::info!("[Maximized] Saved window {:?} geometry: {}x{} at ({}, {})",
+ (*win_ptr).get_title_string().as_deref().unwrap_or(""),
+ (*win_ptr).saved_maximized_width, (*win_ptr).saved_maximized_height,
+ (*win_ptr).saved_maximized_virtual_x, (*win_ptr).saved_maximized_virtual_y
+ );
+ } else if mode != crate::tiling::TilingMode::Maximized && (*win_ptr).was_maximized {
+ // Exiting Maximized mode
+ if (*win_ptr).saved_maximized_width > 0 && (*win_ptr).saved_maximized_height > 0 {
+ (*win_ptr).box_geom.width = (*win_ptr).saved_maximized_width;
+ (*win_ptr).box_geom.height = (*win_ptr).saved_maximized_height;
+ (*win_ptr).virtual_x = (*win_ptr).saved_maximized_virtual_x;
+ (*win_ptr).virtual_y = (*win_ptr).saved_maximized_virtual_y;
+ (*win_ptr).was_maximized = false;
+
+ (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: (*win_ptr).saved_maximized_width as u32,
+ height: (*win_ptr).saved_maximized_height as u32,
+ });
+ (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+ width: (*win_ptr).saved_maximized_width as u32,
+ height: (*win_ptr).saved_maximized_height as u32,
+ };
+ log::info!("[Maximized] Restored window {:?} geometry: {}x{} at ({}, {})",
+ (*win_ptr).get_title_string().as_deref().unwrap_or(""),
+ (*win_ptr).saved_maximized_width, (*win_ptr).saved_maximized_height,
+ (*win_ptr).saved_maximized_virtual_x, (*win_ptr).saved_maximized_virtual_y
+ );
+ }
+ }
+ }
+
// Arrange normal windows on the virtual surface
for &win_ptr in &normal_windows {
let mode = (*win_ptr).tiling_mode;
@@ -1175,6 +1232,50 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
height: phys_h as u32,
};
(*win_ptr).wm_requested.tiled = 1 | 2 | 4 | 8;
+ } else if mode == crate::tiling::TilingMode::Maximized {
+ // Maximized mode: Resizes to fully fill all cells of the desktop grid it is fully/partially inside of.
+ let scale = self.layout.desktop_grid_scale;
+
+ // Use saved maximized geometry for cell calculation
+ let x1 = (*win_ptr).saved_maximized_virtual_x;
+ let y1 = (*win_ptr).saved_maximized_virtual_y;
+ let w = (*win_ptr).saved_maximized_width as f64;
+ let h = (*win_ptr).saved_maximized_height as f64;
+ let x2 = x1 + w;
+ let y2 = y1 + h;
+
+ let col_min = (x1 / scale).floor() as i32;
+ let col_max = ((x2 / scale).ceil() as i32 - 1).max(col_min);
+ let row_min = (y1 / scale).floor() as i32;
+ let row_max = ((y2 / scale).ceil() as i32 - 1).max(row_min);
+
+ let snapped_x1 = col_min as f64 * scale;
+ let snapped_x2 = (col_max + 1) as f64 * scale;
+ let snapped_y1 = row_min as f64 * scale;
+ let snapped_y2 = (row_max + 1) as f64 * scale;
+
+ let fw = snapped_x2 - snapped_x1;
+ let fh = snapped_y2 - snapped_y1;
+
+ // Update current virtual position for rendering
+ (*win_ptr).virtual_x = snapped_x1;
+ (*win_ptr).virtual_y = snapped_y1;
+
+ let final_x = phys_x + (((*win_ptr).virtual_x - self.desk_pan_x) * self.desk_zoom) as i32;
+ let final_y = phys_y + (((*win_ptr).virtual_y - self.desk_pan_y) * self.desk_zoom) as i32;
+
+ (*win_ptr).rendering_requested.x = final_x;
+ (*win_ptr).rendering_requested.y = final_y;
+ (*win_ptr).scale = self.desk_zoom;
+
+ (*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 {
// Regular pannable window on the virtual surface
let fw = if let Some(resize_size) = self.get_active_resize_dimensions(win_ptr) {
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index b2e1098..57d93c0 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -610,7 +610,7 @@ unsafe extern "C" fn handle_request_maximize(listener: *mut ffi::wl_listener, _d
let window = (*toplevel).window;
if ffi::river_wlr_xdg_toplevel_get_requested_maximized((*toplevel).wlr_toplevel) {
- (*window).tiling_mode = crate::tiling::TilingMode::Cascade;
+ (*window).tiling_mode = crate::tiling::TilingMode::Maximized;
(*window).mode_locked = true;
(*window).wm_scheduled.maximize_requested = crate::window::MaximizeRequest::Maximize;
} else {
diff --git a/src/server/xwayland_window.rs b/src/server/xwayland_window.rs
index fabddf4..563152d 100644
--- a/src/server/xwayland_window.rs
+++ b/src/server/xwayland_window.rs
@@ -548,7 +548,7 @@ unsafe extern "C" fn handle_request_maximize(listener: *mut ffi::wl_listener, _d
let maximized = (*(*xwindow).xsurface).maximized_vert || (*(*xwindow).xsurface).maximized_horz;
let window = (*xwindow).window;
if maximized {
- (*window).tiling_mode = crate::tiling::TilingMode::Cascade;
+ (*window).tiling_mode = crate::tiling::TilingMode::Maximized;
(*window).mode_locked = true;
} else {
(*window).tiling_mode = crate::tiling::TilingMode::Floating;