@@ -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"
@@ -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<xdg_wm_base::XdgWmBase>,
viewporter: Option<wp_viewporter::WpViewporter>,
fractional_scale_manager: Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
+ decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
windows: Vec<(xdg_surface::XdgSurface, Rc<WaylandWindowState>)>,
platform_inner: Rc<LinuxPlatformInner>,
wl_seat: Option<wl_seat::WlSeat>,
@@ -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<wl_registry::WlRegistry, ()> for WaylandClientState {
registry.bind::<wp_viewporter::WpViewporter, _, _>(name, 1, qh, ());
state.viewporter = Some(view_porter);
}
+ "zxdg_decoration_manager_v1" => {
+ // Unstable and optional
+ let decoration_manager = registry
+ .bind::<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, _, _>(
+ 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<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for Wayland
}
}
}
+
+impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
+ for WaylandClientState
+{
+ fn event(
+ state: &mut Self,
+ _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
+ event: zxdg_toplevel_decoration_v1::Event,
+ surface_id: &ObjectId,
+ _: &Connection,
+ _: &QueueHandle<Self>,
+ ) {
+ 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;
+ }
+ }
+ }
+ }
+}
@@ -41,6 +41,7 @@ struct WaylandWindowInner {
bounds: Bounds<i32>,
scale: f32,
input_handler: Option<PlatformInputHandler>,
+ 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,
+}