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

commit9781baae393096f9470b75f1433ee68d8e3c3aa9
parent561026fa73
authorLucas Galante <[email protected]>
date2026-06-15 13:07
fix: resolve core dump on compositor shutdown by unregistering cursor event listeners and detaching devices from seat

 src/server/cursor.rs        |  6 ++++++
 src/server/input_manager.rs | 36 ++++++++++++++++++++++++++++++++---
 src/server/server.rs        | 46 +++++++++++++++++++++++++++++++++++----------
 3 files changed, 75 insertions(+), 13 deletions(-)

diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 0458992..6931d84 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -268,6 +268,12 @@ impl Cursor {
     }
 
     pub unsafe fn deinit(&mut self) {
+        wl_listener_remove(&mut self.motion_listener);
+        wl_listener_remove(&mut self.motion_absolute_listener);
+        wl_listener_remove(&mut self.button_listener);
+        wl_listener_remove(&mut self.axis_listener);
+        wl_listener_remove(&mut self.frame_listener);
+
         wl_listener_remove(&mut self.tablet_tool_axis_listener);
         wl_listener_remove(&mut self.tablet_tool_proximity_listener);
         wl_listener_remove(&mut self.tablet_tool_tip_listener);
diff --git a/src/server/input_manager.rs b/src/server/input_manager.rs
index 54f8d54..98f1c45 100644
--- a/src/server/input_manager.rs
+++ b/src/server/input_manager.rs
@@ -103,7 +103,9 @@ impl InputManager {
     }
 
     pub unsafe fn deinit(&mut self) {
+        log::info!("[deinit] InputManager::deinit started");
         if !self.global.is_null() {
+            log::info!("[deinit] destroying input manager global");
             ffi::wl_global_destroy(self.global);
             self.global = std::ptr::null_mut();
         }
@@ -117,14 +119,42 @@ impl InputManager {
             curr = next;
         }
 
-        if !self.default_seat.is_null() {
-            Seat::destroy(self.default_seat);
-            self.default_seat = std::ptr::null_mut();
+        // Detach all devices from their seats and set their seat pointer to null
+        // so they do not attempt to access a freed seat during backend destruction.
+        log::info!("[deinit] detaching devices");
+        let devices_head = &mut self.devices as *mut ffi::wl_list as *mut WlList;
+        let mut curr_dev = (*devices_head).next;
+        while curr_dev != devices_head {
+            let next_dev = (*curr_dev).next;
+            let device = crate::container_of!(curr_dev, crate::input_device::InputDevice, link);
+            if !(*device).seat.is_null() {
+                log::info!("[deinit] detaching device from seat");
+                (*(*device).seat).detach_device(device);
+                (*device).seat = std::ptr::null_mut();
+            }
+            curr_dev = next_dev;
+        }
+
+        // Destroy all seats
+        log::info!("[deinit] destroying seats");
+        let seats_head = &mut self.seats as *mut ffi::wl_list as *mut WlList;
+        let mut curr_seat = (*seats_head).next;
+        while curr_seat != seats_head {
+            let next_seat = (*curr_seat).next;
+            let seat = crate::container_of!(curr_seat, Seat, link);
+            log::info!("[deinit] calling Seat::destroy for {:?}", (*seat).wlr_seat);
+            Seat::destroy(seat);
+            curr_seat = next_seat;
         }
+        self.default_seat = std::ptr::null_mut();
+        log::info!("[deinit] seats destroyed");
+
+        log::info!("[deinit] removing input manager listeners");
         wl_listener_remove(&mut self.new_input_listener);
         wl_listener_remove(&mut self.new_text_input);
         wl_listener_remove(&mut self.new_input_method);
         wl_listener_remove(&mut self.new_virtual_pointer_listener);
+        log::info!("[deinit] InputManager::deinit finished");
     }
 }
 
diff --git a/src/server/server.rs b/src/server/server.rs
index 42e377a..692998e 100644
--- a/src/server/server.rs
+++ b/src/server/server.rs
@@ -728,6 +728,33 @@ impl Server {
 
     pub fn deinit(&mut self) {
         unsafe {
+            log::info!("[deinit] Server::deinit started");
+            // 1. Terminate all client connections first
+            log::info!("[deinit] wl_display_destroy_clients started");
+            ffi::wl_display_destroy_clients(self.wl_server);
+            log::info!("[deinit] wl_display_destroy_clients finished");
+
+            // 2. Deinitialize subcomponents while backend, renderer, allocator, and display are valid
+            log::info!("[deinit] self.om.deinit started");
+            self.om.deinit();
+            log::info!("[deinit] self.om.deinit finished");
+
+            log::info!("[deinit] self.input_manager.deinit started");
+            self.input_manager.deinit();
+            log::info!("[deinit] self.input_manager.deinit finished");
+
+            log::info!("[deinit] deinitializing other subcomponents");
+            self.idle_inhibit_manager.deinit();
+            self.lock_manager.deinit();
+            self.layer_shell.deinit();
+            self.inspector.deinit();
+            self.xkb_bindings.deinit();
+            self.libinput_config.deinit();
+            self.xkb_config.deinit();
+            log::info!("[deinit] other subcomponents deinitialized");
+
+            // 3. Remove signal listeners registered directly by the server
+            log::info!("[deinit] removing server listeners");
             ffi::wl_event_source_remove(self.sigint_source);
             ffi::wl_event_source_remove(self.sigterm_source);
 
@@ -736,27 +763,26 @@ impl Server {
             wl_listener_remove(&mut self.new_toplevel_decoration);
             wl_listener_remove(&mut self.request_activate);
             wl_listener_remove(&mut self.request_set_cursor_shape);
-            // wl_listener_remove(&mut self.toplevel_capture_request);
 
+            // 4. Destroy Xwayland if active
             if !self.xwayland.is_null() {
                 wl_listener_remove(&mut self.new_xsurface);
                 ffi::wlr_xwayland_destroy(self.xwayland);
             }
+            log::info!("[deinit] server listeners removed");
 
-            ffi::wl_display_destroy_clients(self.wl_server);
+            // 5. Destroy wlroots core hardware interfaces
+            log::info!("[deinit] destroying backend");
             ffi::wlr_backend_destroy(self.backend);
-
+            log::info!("[deinit] destroying renderer");
             ffi::wlr_renderer_destroy(self.renderer);
+            log::info!("[deinit] destroying allocator");
             ffi::wlr_allocator_destroy(self.allocator);
 
-            self.om.deinit();
-            self.input_manager.deinit();
-            self.idle_inhibit_manager.deinit();
-            self.lock_manager.deinit();
-            self.layer_shell.deinit();
-            self.inspector.deinit();
-
+            // 6. Finally, destroy the display
+            log::info!("[deinit] destroying display");
             ffi::wl_display_destroy(self.wl_server);
+            log::info!("[deinit] Server::deinit finished successfully");
         }
     }
 }