Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
Implement clean exit of applications on compositor exit and fix blur scaling
src/server/window.rs | 7 +++-
src/server/window_manager.rs | 93 +++++++++++++++++++++++++++++++++++++++++++-
src/server/xdg_toplevel.rs | 3 +-
3 files changed, 99 insertions(+), 4 deletions(-)
diff --git a/src/server/window.rs b/src/server/window.rs
index ac8eef9..83ed25d 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -856,6 +856,7 @@ impl Window {
(*(*window).server).wm.remove_from_history(window);
(*(*window).server).wm.windows.remove((*window).ref_key);
+ (*(*window).server).wm.check_clean_exit_progress();
let _ = Box::from_raw(window);
}
@@ -1563,7 +1564,8 @@ impl Window {
let app_id = self.get_app_id_string().unwrap_or_default();
let is_status = self.tiling_mode == crate::tiling::TilingMode::Status ||
app_id.starts_with("cce-status");
- let blur_enabled = requested.blur;
+ let is_cce_app = app_id.starts_with("cce-");
+ let blur_enabled = requested.blur && (self.wm_requested.ssd || is_cce_app || is_status);
let mut ignore_transparent = (*self.server).wm.layout.window_backdrop_blur_ignore_transparent;
if is_status {
ignore_transparent = (*self.server).wm.layout.status_backdrop_blur_ignore_transparent;
@@ -2848,7 +2850,8 @@ impl Decoration {
if is_status {
ignore_transparent = (*server).wm.layout.status_backdrop_blur_ignore_transparent;
}
- let blur_enabled = self.rendering_requested.blur;
+ let is_cce_app = app_id.starts_with("cce-");
+ let blur_enabled = self.rendering_requested.blur && ((*self.window).wm_requested.ssd || is_cce_app || is_status);
ffi::river_scene_node_enable_blur(self.surfaces.tree as *mut ffi::wlr_scene_node, blur_enabled, (*server).wm.layout.scenefx_optimized_blur, ignore_transparent, 0, 0, 0, 0);
let scale = (*self.window).scale;
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index cb4e382..bc86fcb 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -120,6 +120,8 @@ pub struct WindowManager {
pub last_viewport_pan_x: f64,
pub last_viewport_pan_y: f64,
pub viewport_is_active: bool,
+ pub clean_exit_in_progress: bool,
+ pub clean_exit_timer: *mut ffi::wl_event_source,
}
impl WindowManager {
@@ -205,10 +207,19 @@ impl WindowManager {
self.ipc_rx = None;
self.ipc_timer = ffi::wl_event_loop_add_timer(event_loop, Some(handle_ipc_timer), self as *mut WindowManager as *mut _);
if self.ipc_timer.is_null() {
+ ffi::wl_event_source_remove(self.timeout);
return Err("Failed to create IPC timer event source");
}
ffi::wl_event_source_timer_update(self.ipc_timer, 10);
+ self.clean_exit_timer = ffi::wl_event_loop_add_timer(event_loop, Some(handle_clean_exit_timeout), self as *mut WindowManager as *mut _);
+ if self.clean_exit_timer.is_null() {
+ ffi::wl_event_source_remove(self.timeout);
+ ffi::wl_event_source_remove(self.ipc_timer);
+ return Err("Failed to create clean exit timer event source");
+ }
+ self.clean_exit_in_progress = false;
+
self.global = ffi::wl_global_create(
(*server).wl_server,
&ffi::zcce_window_manager_v1_interface,
@@ -364,6 +375,72 @@ impl WindowManager {
}
}
+ pub unsafe fn start_clean_exit(&mut self) {
+ if self.clean_exit_in_progress {
+ return;
+ }
+ log::info!("Starting clean exit process...");
+ self.clean_exit_in_progress = true;
+
+ // Save state before closing windows
+ self.save_state();
+
+ // Get list of windows we need to wait for to close cleanly
+ let mut normal_windows = Vec::new();
+ for &w in self.windows.iter() {
+ if w.is_null() || (*w).closed || matches!((*w).state, crate::window::WindowState::Closing | crate::window::WindowState::Init) {
+ continue;
+ }
+ if (*w).is_status_bar() || (*w).is_wallpaper() {
+ continue;
+ }
+ normal_windows.push(w);
+ }
+
+ if normal_windows.is_empty() {
+ log::info!("No active windows to close. Exiting immediately.");
+ ffi::wl_display_terminate((*self.server).wl_server);
+ return;
+ }
+
+ log::info!("Sending close request to {} windows...", normal_windows.len());
+ for &w in &normal_windows {
+ log::info!("Closing window: {:?}", (*w).get_title_string());
+ (*w).close();
+ }
+
+ // Set a clean exit timer fallback to 2.0 seconds (2000 ms)
+ ffi::wl_event_source_timer_update(self.clean_exit_timer, 2000);
+ }
+
+ pub unsafe fn check_clean_exit_progress(&mut self) {
+ if !self.clean_exit_in_progress {
+ return;
+ }
+
+ let mut normal_windows_count = 0;
+ for &w in self.windows.iter() {
+ if w.is_null() || (*w).closed || matches!((*w).state, crate::window::WindowState::Closing | crate::window::WindowState::Init) {
+ continue;
+ }
+ if (*w).is_status_bar() || (*w).is_wallpaper() {
+ continue;
+ }
+ normal_windows_count += 1;
+ }
+
+ if normal_windows_count == 0 {
+ log::info!("All windows closed cleanly. Exiting display server.");
+ if !self.clean_exit_timer.is_null() {
+ ffi::wl_event_source_remove(self.clean_exit_timer);
+ self.clean_exit_timer = std::ptr::null_mut();
+ }
+ ffi::wl_display_terminate((*self.server).wl_server);
+ } else {
+ log::info!("Clean exit: waiting for {} remaining windows to close...", normal_windows_count);
+ }
+ }
+
pub unsafe fn match_and_remove_restore_state(&mut self, app_id: &str, title: &str) -> Option<SavedWindowState> {
if app_id.is_empty() {
return None;
@@ -466,6 +543,10 @@ impl WindowManager {
ffi::wl_event_source_remove(self.timeout);
self.timeout = std::ptr::null_mut();
}
+ if !self.clean_exit_timer.is_null() {
+ ffi::wl_event_source_remove(self.clean_exit_timer);
+ self.clean_exit_timer = std::ptr::null_mut();
+ }
if !self.animation_timer.is_null() {
ffi::wl_event_source_remove(self.animation_timer);
self.animation_timer = std::ptr::null_mut();
@@ -1973,7 +2054,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
}
Action::Exit => {
log::info!("monolithic execute_action: Exit requested");
- ffi::wl_display_terminate((*self.server).wl_server);
+ self.start_clean_exit();
}
Action::Fullscreen => {
if let Some(seat) = self.first_seat() {
@@ -2964,6 +3045,16 @@ unsafe extern "C" fn dirty_idle_callback(data: *mut std::ffi::c_void) {
}
}
+unsafe extern "C" fn handle_clean_exit_timeout(data: *mut std::ffi::c_void) -> std::os::raw::c_int {
+ let wm = data as *mut WindowManager;
+ if wm.is_null() {
+ return 0;
+ }
+ log::info!("Clean exit timeout reached. Forcing display termination.");
+ ffi::wl_display_terminate((*(*wm).server).wl_server);
+ 0
+}
+
unsafe extern "C" fn handle_timeout(data: *mut std::ffi::c_void) -> std::os::raw::c_int {
let wm = data as *mut WindowManager;
if wm.is_null() {
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index 7baacff..ef68362 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -450,7 +450,8 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
let is_status = (*window).tiling_mode == crate::tiling::TilingMode::Status ||
app_id.starts_with("cce-status");
let use_optimized = if is_status { false } else { (*(*window).server).wm.layout.scenefx_optimized_blur };
- let blur_enabled = (*window).rendering_requested.blur;
+ let is_cce_app = app_id.starts_with("cce-");
+ let blur_enabled = (*window).rendering_requested.blur && ((*window).wm_requested.ssd || is_cce_app || is_status);
ffi::river_scene_node_enable_blur(
(*window).tree as *mut ffi::wlr_scene_node,
blur_enabled,