diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 8326294c75..a55edcab19 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -105,7 +105,7 @@ ashpd = "0.7.0" # todo!(linux) - Technically do not use `randr`, but it doesn't compile otherwise xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr", "xkb"] } wayland-client= { version = "0.31.2" } -wayland-protocols = { version = "0.31.2", features = ["client", "staging"] } +wayland-protocols = { version = "0.31.2", features = ["client", "staging", "unstable"] } wayland-backend = { version = "0.3.3", features = ["client_system"] } xkbcommon = { version = "0.7", features = ["wayland", "x11"] } as-raw-xcb-connection = "1" diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 0a20dfd871..68efbc6648 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -19,13 +19,16 @@ use wayland_protocols::wp::fractional_scale::v1::client::{ wp_fractional_scale_manager_v1, wp_fractional_scale_v1, }; use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter}; +use wayland_protocols::xdg::decoration::zv1::client::{ + zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1, +}; use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}; use xkbcommon::xkb; use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1; use xkbcommon::xkb::{Keycode, KEYMAP_COMPILE_NO_FLAGS}; use crate::platform::linux::client::Client; -use crate::platform::linux::wayland::window::WaylandWindow; +use crate::platform::linux::wayland::window::{WaylandDecorationState, WaylandWindow}; use crate::platform::{LinuxPlatformInner, PlatformWindow}; use crate::ScrollDelta; use crate::{ @@ -42,6 +45,7 @@ pub(crate) struct WaylandClientState { wm_base: Option, viewporter: Option, fractional_scale_manager: Option, + decoration_manager: Option, windows: Vec<(xdg_surface::XdgSurface, Rc)>, platform_inner: Rc, wl_seat: Option, @@ -70,6 +74,7 @@ impl WaylandClient { wm_base: None, viewporter: None, fractional_scale_manager: None, + decoration_manager: None, windows: Vec::new(), platform_inner: Rc::clone(&linux_platform_inner), wl_seat: None, @@ -143,6 +148,31 @@ impl Client for WaylandClient { let toplevel = xdg_surface.get_toplevel(&self.qh, ()); let wl_surface = Arc::new(wl_surface); + // Attempt to set up window decorations based on the requested configuration + // + // Note that wayland compositors may either not support decorations at all, or may + // support them but not allow clients to choose whether they are enabled or not. + // We attempt to account for these cases here. + + if let Some(decoration_manager) = state.decoration_manager.as_ref() { + // The protocol for managing decorations is present at least, but that doesn't + // mean that the compositor will allow us to use it. + + let decoration = + decoration_manager.get_toplevel_decoration(&toplevel, &self.qh, xdg_surface.id()); + + // todo!(linux) - options.titlebar is lacking information required for wayland. + // Especially, whether a titlebar is wanted in itself. + // + // Removing the titlebar also removes the entire window frame (ie. the ability to + // close, move and resize the window [snapping still works]). This needs additional + // handling in Zed, in order to implement drag handlers on a titlebar element. + // + // Since all of this handling is not present, we request server-side decorations + // for now as a stopgap solution. + decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ServerSide); + } + let viewport = state .viewporter .as_ref() @@ -210,6 +240,17 @@ impl Dispatch for WaylandClientState { registry.bind::(name, 1, qh, ()); state.viewporter = Some(view_porter); } + "zxdg_decoration_manager_v1" => { + // Unstable and optional + let decoration_manager = registry + .bind::( + name, + 1, + qh, + (), + ); + state.decoration_manager = Some(decoration_manager); + } _ => {} }; } @@ -222,6 +263,7 @@ delegate_noop!(WaylandClientState: ignore wl_shm::WlShm); delegate_noop!(WaylandClientState: ignore wl_shm_pool::WlShmPool); delegate_noop!(WaylandClientState: ignore wl_buffer::WlBuffer); delegate_noop!(WaylandClientState: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1); +delegate_noop!(WaylandClientState: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1); delegate_noop!(WaylandClientState: ignore wp_viewporter::WpViewporter); delegate_noop!(WaylandClientState: ignore wp_viewport::WpViewport); @@ -614,3 +656,42 @@ impl Dispatch for Wayland } } } + +impl Dispatch + for WaylandClientState +{ + fn event( + state: &mut Self, + _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, + event: zxdg_toplevel_decoration_v1::Event, + surface_id: &ObjectId, + _: &Connection, + _: &QueueHandle, + ) { + if let zxdg_toplevel_decoration_v1::Event::Configure { mode, .. } = event { + for window in &state.windows { + if window.0.id() == *surface_id { + match mode { + WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => { + window + .1 + .set_decoration_state(WaylandDecorationState::Server); + } + WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => { + window + .1 + .set_decoration_state(WaylandDecorationState::Client); + } + WEnum::Value(_) => { + log::warn!("Unknown decoration mode"); + } + WEnum::Unknown(v) => { + log::warn!("Unknown decoration mode: {}", v); + } + } + return; + } + } + } + } +} diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 6cc8d2015e..5f0b64cff7 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -41,6 +41,7 @@ struct WaylandWindowInner { bounds: Bounds, scale: f32, input_handler: Option, + decoration_state: WaylandDecorationState, } struct RawWindow { @@ -96,6 +97,9 @@ impl WaylandWindowInner { bounds, scale: 1.0, input_handler: None, + + // On wayland, decorations are by default provided by the client + decoration_state: WaylandDecorationState::Client, } } } @@ -187,6 +191,20 @@ impl WaylandWindowState { self.set_size_and_scale(bounds.size.width, bounds.size.height, scale) } + /// Notifies the window of the state of the decorations. + /// + /// # Note + /// + /// This API is indirectly called by the wayland compositor and + /// not meant to be called by a user who wishes to change the state + /// of the decorations. This is because the state of the decorations + /// is managed by the compositor and not the client. + pub fn set_decoration_state(&self, state: WaylandDecorationState) { + self.inner.lock().decoration_state = state; + log::trace!("Window decorations are now handled by {:?}", state); + // todo!(linux) - Handle this properly + } + pub fn close(&self) { let mut callbacks = self.callbacks.lock(); if let Some(fun) = callbacks.close.take() { @@ -377,3 +395,12 @@ impl PlatformWindow for WaylandWindow { //todo!(linux) } } + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum WaylandDecorationState { + /// Decorations are to be provided by the client + Client, + + /// Decorations are provided by the server + Server, +}