wayland: Fix window close (#10702)

apricotbucket28 created

Partially fixes https://github.com/zed-industries/zed/issues/10483 (X11
still has this issue)

Also adds some missing destroy() calls for some objects.
Thanks @phisch!

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/linux/wayland/client.rs | 32 +++++++++--
crates/gpui/src/platform/linux/wayland/window.rs | 49 ++++++++++++-----
2 files changed, 61 insertions(+), 20 deletions(-)

Detailed changes

crates/gpui/src/platform/linux/wayland/client.rs 🔗

@@ -118,8 +118,8 @@ pub(crate) struct WaylandClientState {
     loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
     cursor_icon_name: String,
     cursor: Cursor,
-    clipboard: Clipboard,
-    primary: Primary,
+    clipboard: Option<Clipboard>,
+    primary: Option<Primary>,
     event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
     common: LinuxCommon,
 }
@@ -163,6 +163,12 @@ impl WaylandClientStatePtr {
                 state.mouse_focused_window = Some(window);
             }
         }
+        if state.windows.is_empty() {
+            // Drop the clipboard to prevent a seg fault after we've closed all Wayland connections.
+            state.clipboard = None;
+            state.primary = None;
+            state.common.signal.stop();
+        }
     }
 }
 
@@ -278,8 +284,8 @@ impl WaylandClient {
             cursor_icon_name: "arrow".to_string(),
             enter_token: None,
             cursor,
-            clipboard,
-            primary,
+            clipboard: Some(clipboard),
+            primary: Some(primary),
             event_loop: Some(event_loop),
         }));
 
@@ -378,17 +384,29 @@ impl LinuxClient for WaylandClient {
     }
 
     fn write_to_primary(&self, item: crate::ClipboardItem) {
-        self.0.borrow_mut().primary.set_contents(item.text);
+        self.0
+            .borrow_mut()
+            .primary
+            .as_mut()
+            .unwrap()
+            .set_contents(item.text);
     }
 
     fn write_to_clipboard(&self, item: crate::ClipboardItem) {
-        self.0.borrow_mut().clipboard.set_contents(item.text);
+        self.0
+            .borrow_mut()
+            .clipboard
+            .as_mut()
+            .unwrap()
+            .set_contents(item.text);
     }
 
     fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
         self.0
             .borrow_mut()
             .primary
+            .as_mut()
+            .unwrap()
             .get_contents()
             .ok()
             .map(|s| crate::ClipboardItem {
@@ -401,6 +419,8 @@ impl LinuxClient for WaylandClient {
         self.0
             .borrow_mut()
             .clipboard
+            .as_mut()
+            .unwrap()
             .get_contents()
             .ok()
             .map(|s| crate::ClipboardItem {

crates/gpui/src/platform/linux/wayland/window.rs 🔗

@@ -68,6 +68,7 @@ unsafe impl HasRawDisplayHandle for RawWindow {
 pub struct WaylandWindowState {
     xdg_surface: xdg_surface::XdgSurface,
     pub surface: wl_surface::WlSurface,
+    decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
     toplevel: xdg_toplevel::XdgToplevel,
     viewport: Option<wp_viewport::WpViewport>,
     outputs: HashSet<ObjectId>,
@@ -90,11 +91,13 @@ pub struct WaylandWindowStatePtr {
 }
 
 impl WaylandWindowState {
+    #[allow(clippy::too_many_arguments)]
     pub(crate) fn new(
         surface: wl_surface::WlSurface,
         xdg_surface: xdg_surface::XdgSurface,
-        viewport: Option<wp_viewport::WpViewport>,
         toplevel: xdg_toplevel::XdgToplevel,
+        decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
+        viewport: Option<wp_viewport::WpViewport>,
         client: WaylandClientStatePtr,
         globals: Globals,
         options: WindowParams,
@@ -132,6 +135,7 @@ impl WaylandWindowState {
         Self {
             xdg_surface,
             surface,
+            decoration,
             toplevel,
             viewport,
             globals,
@@ -158,16 +162,27 @@ impl Drop for WaylandWindow {
         let mut state = self.0.state.borrow_mut();
         let surface_id = state.surface.id();
         let client = state.client.clone();
+
         state.renderer.destroy();
+        if let Some(decoration) = &state.decoration {
+            decoration.destroy();
+        }
         state.toplevel.destroy();
+        if let Some(viewport) = &state.viewport {
+            viewport.destroy();
+        }
         state.xdg_surface.destroy();
         state.surface.destroy();
 
         let state_ptr = self.0.clone();
-        state.globals.executor.spawn(async move {
-            state_ptr.close();
-            client.drop_window(&surface_id)
-        });
+        state
+            .globals
+            .executor
+            .spawn(async move {
+                state_ptr.close();
+                client.drop_window(&surface_id)
+            })
+            .detach();
         drop(state);
     }
 }
@@ -197,13 +212,18 @@ impl WaylandWindow {
         }
 
         // Attempt to set up window decorations based on the requested configuration
-        if let Some(decoration_manager) = globals.decoration_manager.as_ref() {
-            let decoration =
-                decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id());
-
-            // Request client side decorations if possible
-            decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ClientSide);
-        }
+        let decoration = globals
+            .decoration_manager
+            .as_ref()
+            .map(|decoration_manager| {
+                let decoration = decoration_manager.get_toplevel_decoration(
+                    &toplevel,
+                    &globals.qh,
+                    surface.id(),
+                );
+                decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ClientSide);
+                decoration
+            });
 
         let viewport = globals
             .viewporter
@@ -216,8 +236,9 @@ impl WaylandWindow {
             state: Rc::new(RefCell::new(WaylandWindowState::new(
                 surface.clone(),
                 xdg_surface,
-                viewport,
                 toplevel,
+                decoration,
+                viewport,
                 client,
                 globals,
                 params,
@@ -319,7 +340,7 @@ impl WaylandWindowStatePtr {
                     }
                     result
                 } else {
-                    false
+                    true
                 }
             }
             _ => false,