wayland: Fix window state issues (#13885)

apricotbucket28 created

Fixes some issues with the CSD added in
https://github.com/zed-industries/zed/pull/13611

Here's a video comparing the master branch (yellow icon) with this PR
(blue icon):


https://github.com/zed-industries/zed/assets/71973804/35be443a-8f24-4aed-910b-625bad9821e2

_Note: the flicker at the bottom of the window when maximizing is an
issue with the KDE floating task bar, it happens with all programs._

Release Notes:

- N/A

Change summary

crates/gpui/src/platform.rs                      | 10 ++
crates/gpui/src/platform/linux/wayland/client.rs |  2 
crates/gpui/src/platform/linux/wayland/window.rs | 72 ++++++++---------
crates/title_bar/src/platforms/platform_linux.rs |  3 
4 files changed, 47 insertions(+), 40 deletions(-)

Detailed changes

crates/gpui/src/platform.rs 🔗

@@ -281,6 +281,16 @@ pub struct Tiling {
 }
 
 impl Tiling {
+    /// Initializes a [`Tiling`] type with all sides tiled
+    pub fn tiled() -> Self {
+        Self {
+            top: true,
+            left: true,
+            right: true,
+            bottom: true,
+        }
+    }
+
     /// Whether any edge is tiled
     pub fn is_tiled(&self) -> bool {
         self.top || self.left || self.right || self.bottom

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

@@ -100,7 +100,6 @@ pub struct WaylandWindowState {
     in_progress_window_controls: Option<WindowControls>,
     window_controls: WindowControls,
     inset: Option<Pixels>,
-    requested_inset: Option<Pixels>,
 }
 
 #[derive(Clone)]
@@ -189,7 +188,6 @@ impl WaylandWindowState {
                 window_menu: true,
             },
             inset: None,
-            requested_inset: None,
         })
     }
 
@@ -316,12 +314,11 @@ impl WaylandWindowStatePtr {
         Rc::ptr_eq(&self.state, &other.state)
     }
 
-    pub fn frame(&self, request_frame_callback: bool) {
-        if request_frame_callback {
-            let mut state = self.state.borrow_mut();
-            state.surface.frame(&state.globals.qh, state.surface.id());
-            drop(state);
-        }
+    pub fn frame(&self) {
+        let mut state = self.state.borrow_mut();
+        state.surface.frame(&state.globals.qh, state.surface.id());
+        drop(state);
+
         let mut cb = self.callbacks.borrow_mut();
         if let Some(fun) = cb.request_frame.as_mut() {
             fun();
@@ -351,13 +348,12 @@ impl WaylandWindowStatePtr {
                         state.fullscreen = configure.fullscreen;
                         state.maximized = configure.maximized;
                         state.tiling = configure.tiling;
-                        if got_unmaximized {
-                            configure.size = Some(state.window_bounds.size);
-                        } else if !configure.maximized {
-                            configure.size =
-                                compute_outer_size(state.inset, configure.size, state.tiling);
-                        }
                         if !configure.fullscreen && !configure.maximized {
+                            configure.size = if got_unmaximized {
+                                Some(state.window_bounds.size)
+                            } else {
+                                compute_outer_size(state.inset, configure.size, state.tiling)
+                            };
                             if let Some(size) = configure.size {
                                 state.window_bounds = Bounds {
                                     origin: Point::default(),
@@ -373,12 +369,27 @@ impl WaylandWindowStatePtr {
                 }
                 let mut state = self.state.borrow_mut();
                 state.xdg_surface.ack_configure(serial);
-                let request_frame_callback = !state.acknowledged_first_configure;
-                state.acknowledged_first_configure = true;
 
+                let window_geometry = inset_by_tiling(
+                    state.bounds.map_origin(|_| px(0.0)),
+                    state.inset.unwrap_or(px(0.0)),
+                    state.tiling,
+                )
+                .map(|v| v.0 as i32)
+                .map_size(|v| if v <= 0 { 1 } else { v });
+
+                state.xdg_surface.set_window_geometry(
+                    window_geometry.origin.x,
+                    window_geometry.origin.y,
+                    window_geometry.size.width,
+                    window_geometry.size.height,
+                );
+
+                let request_frame_callback = !state.acknowledged_first_configure;
                 if request_frame_callback {
+                    state.acknowledged_first_configure = true;
                     drop(state);
-                    self.frame(true);
+                    self.frame();
                 }
             }
             _ => {}
@@ -470,6 +481,10 @@ impl WaylandWindowStatePtr {
                     }
                 }
 
+                if fullscreen || maximized {
+                    tiling = Tiling::tiled();
+                }
+
                 let mut state = self.state.borrow_mut();
                 state.in_progress_configure = Some(InProgressConfigure {
                     size,
@@ -893,26 +908,7 @@ impl PlatformWindow for WaylandWindow {
     }
 
     fn completed_frame(&self) {
-        let mut state = self.borrow_mut();
-        if let Some(area) = state.requested_inset {
-            state.inset = Some(area);
-        }
-
-        let window_geometry = inset_by_tiling(
-            state.bounds.map_origin(|_| px(0.0)),
-            state.inset.unwrap_or(px(0.0)),
-            state.tiling,
-        )
-        .map(|v| v.0 as i32)
-        .map_size(|v| if v <= 0 { 1 } else { v });
-
-        state.xdg_surface.set_window_geometry(
-            window_geometry.origin.x,
-            window_geometry.origin.y,
-            window_geometry.size.width,
-            window_geometry.size.height,
-        );
-
+        let state = self.borrow();
         state.surface.commit();
     }
 
@@ -973,7 +969,7 @@ impl PlatformWindow for WaylandWindow {
     fn set_client_inset(&self, inset: Pixels) {
         let mut state = self.borrow_mut();
         if Some(inset) != state.inset {
-            state.requested_inset = Some(inset);
+            state.inset = Some(inset);
             update_window(state);
         }
     }

crates/title_bar/src/platforms/platform_linux.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{prelude::*, Action};
+use gpui::{prelude::*, Action, MouseButton};
 
 use ui::prelude::*;
 
@@ -23,6 +23,7 @@ impl RenderOnce for LinuxWindowControls {
             .id("generic-window-controls")
             .px_3()
             .gap_3()
+            .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
             .child(WindowControl::new(
                 "minimize",
                 WindowControlType::Minimize,