Enable server side decorations on wayland (#8037)

This PR enables server side decorations on Wayland if possible. This is
stopgap solution, so that the window can be moved, resized and dragged
on Wayland sessions at all.


![image](https://github.com/zed-industries/zed/assets/25827180/3dc9af53-76c0-4664-8746-ed6a6e5eafe7)

Since Wayland compositors can decide to force either mode (as in,
forcing server or client side decorations), this requires additional
handling in zed. Since zed doesn't provide any of that handling as of
now, as a temporary solution server side decorations are always
requested.
This commit is contained in:
Janrupf 2024-02-20 02:53:31 +01:00 committed by GitHub
parent 77974a4367
commit fddb778e5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 110 additions and 2 deletions

View File

@ -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"

View File

@ -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;
}
}
}
}
}

View File

@ -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,
}