wayland.xdg-decor: Implement XDG Decoration protocol

This commit is contained in:
Poly 2021-10-17 19:41:27 +02:00 committed by Victor Berger
parent 77c970e197
commit 10aee60b87
3 changed files with 197 additions and 0 deletions

View File

@ -0,0 +1,153 @@
//! XDG Window decoration manager
//!
//! This interface allows a compositor to announce support for server-side decorations.
//!
//! A client can use this protocol to request being decorated by a supporting compositor.
//!
//!
//! ```no_run
//! # extern crate wayland_server;
//! #
//! use smithay::wayland::shell::xdg::decoration::{init_xdg_decoration_manager, XdgDecorationRequest};
//! use smithay::reexports::wayland_protocols::unstable::xdg_decoration::v1::server::zxdg_toplevel_decoration_v1::Mode;
//!
//! # let mut display = wayland_server::Display::new();
//!
//! init_xdg_decoration_manager(
//! &mut display,
//! |req, _ddata| match req {
//! XdgDecorationRequest::NewToplevelDecoration { toplevel } => {
//! let res = toplevel.with_pending_state(|state| {
//! // Advertise server side decoration
//! state.decoration_mode = Some(Mode::ServerSide);
//! });
//!
//! if res.is_ok() {
//! toplevel.send_configure();
//! }
//! }
//! XdgDecorationRequest::SetMode { .. } => {}
//! XdgDecorationRequest::UnsetMode { .. } => {}
//! },
//! None,
//! );
//!
use std::{cell::RefCell, ops::Deref, rc::Rc};
use wayland_protocols::unstable::xdg_decoration::v1::server::{
zxdg_decoration_manager_v1::{self, ZxdgDecorationManagerV1},
zxdg_toplevel_decoration_v1::{self, Mode, ZxdgToplevelDecorationV1},
};
use wayland_server::{DispatchData, Display, Filter, Global, Main};
use super::ToplevelSurface;
use crate::wayland::shell::xdg::xdg_handlers::ShellSurfaceUserData;
/// Events generated by xdg decoration manager
#[derive(Debug)]
pub enum XdgDecorationRequest {
/// A new toplevel decoration was instantiated
NewToplevelDecoration {
/// The toplevel asosiated with decoration
toplevel: ToplevelSurface,
},
/// Informs the compositor that the client prefers the provided decoration mode.
SetMode {
/// The toplevel asosiated with decoration
toplevel: ToplevelSurface,
/// The decoration mode
mode: Mode,
},
/// Informs the compositor that the client doesn't prefer a particular decoration mode.
UnsetMode {
/// The toplevel asosiated with decoration
toplevel: ToplevelSurface,
},
}
/// Create a new XDG Decoration Manager global
pub fn init_xdg_decoration_manager<L, Impl>(
display: &mut Display,
implementation: Impl,
_logger: L,
) -> Global<ZxdgDecorationManagerV1>
where
L: Into<Option<::slog::Logger>>,
Impl: FnMut(XdgDecorationRequest, DispatchData<'_>) + 'static,
{
let cb = Rc::new(RefCell::new(implementation));
display.create_global(
1,
Filter::new(
move |(manager, _version): (Main<ZxdgDecorationManagerV1>, _), _, _| {
let cb = cb.clone();
manager.quick_assign(move |_manager, request, ddata| {
match request {
zxdg_decoration_manager_v1::Request::Destroy => {
// All is handled by destructor.
}
zxdg_decoration_manager_v1::Request::GetToplevelDecoration { id, toplevel } => {
if let Some(data) = toplevel.as_ref().user_data().get::<ShellSurfaceUserData>() {
if data.decoration.borrow().is_none() {
*data.decoration.borrow_mut() = Some(id.deref().clone());
} else {
use wayland_protocols::unstable::xdg_decoration::v1::server::zxdg_toplevel_decoration_v1::Error;
id.as_ref().post_error(Error::AlreadyConstructed as u32, "toplevel decoration is already constructed".to_string());
}
let toplevel = ToplevelSurface {
shell_surface: toplevel.clone(),
wl_surface: data.wl_surface.clone(),
};
(&mut *cb.borrow_mut())(
XdgDecorationRequest::NewToplevelDecoration {
toplevel: toplevel.clone(),
},
ddata,
);
let cb = cb.clone();
id.quick_assign(move |_, request, ddata| match request {
zxdg_toplevel_decoration_v1::Request::SetMode { mode } => {
(&mut *cb.borrow_mut())(
XdgDecorationRequest::SetMode {
toplevel: toplevel.clone(),
mode,
},
ddata,
);
}
zxdg_toplevel_decoration_v1::Request::UnsetMode => {
(&mut *cb.borrow_mut())(
XdgDecorationRequest::UnsetMode {
toplevel: toplevel.clone(),
},
ddata,
);
}
_ => {}
});
}
id.assign_destructor(Filter::new(
move |_decoration: ZxdgToplevelDecorationV1, _, _| {
if let Some(data) =
toplevel.as_ref().user_data().get::<ShellSurfaceUserData>()
{
*data.decoration.borrow_mut() = None;
}
},
));
}
_ => unreachable!(),
}
});
},
),
)
}
pub(super) fn send_decoration_configure(id: &ZxdgToplevelDecorationV1, mode: Mode) {
id.configure(mode)
}

View File

@ -74,6 +74,8 @@ use std::{
sync::{Arc, Mutex},
};
use wayland_protocols::unstable::xdg_decoration;
use wayland_protocols::unstable::xdg_decoration::v1::server::zxdg_toplevel_decoration_v1;
use wayland_protocols::xdg_shell::server::xdg_surface;
use wayland_protocols::xdg_shell::server::{xdg_popup, xdg_positioner, xdg_toplevel, xdg_wm_base};
use wayland_server::DispatchData;
@ -82,8 +84,12 @@ use wayland_server::{
Display, Filter, Global, UserDataMap,
};
use self::xdg_handlers::ShellSurfaceUserData;
use super::PingError;
pub mod decoration;
// handlers for the xdg_shell protocol
pub(super) mod xdg_handlers;
@ -549,6 +555,9 @@ pub struct ToplevelState {
/// The output for a fullscreen display
pub fullscreen_output: Option<wl_output::WlOutput>,
/// The xdg decoration mode of the surface
pub decoration_mode: Option<zxdg_toplevel_decoration_v1::Mode>,
}
impl Default for ToplevelState {
@ -557,6 +566,7 @@ impl Default for ToplevelState {
fullscreen_output: None,
states: Default::default(),
size: None,
decoration_mode: None,
}
}
}
@ -567,6 +577,7 @@ impl Clone for ToplevelState {
fullscreen_output: self.fullscreen_output.clone(),
states: self.states.clone(),
size: self.size,
decoration_mode: self.decoration_mode,
}
}
}
@ -980,6 +991,35 @@ impl ToplevelSurface {
})
.unwrap_or(None);
if let Some(configure) = configure {
let decoration_mode = compositor::with_states(surface, |states| {
let attributes = states
.data_map
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap();
attributes.current.decoration_mode.clone()
})
.unwrap_or(None);
if configure.state.decoration_mode != decoration_mode {
if let Some(data) = self
.shell_surface
.as_ref()
.user_data()
.get::<ShellSurfaceUserData>()
{
if let Some(decoration) = &*data.decoration.borrow() {
self::decoration::send_decoration_configure(
decoration,
configure.state.decoration_mode.unwrap_or(
xdg_decoration::v1::server::zxdg_toplevel_decoration_v1::Mode::ClientSide,
),
);
}
}
}
self::xdg_handlers::send_toplevel_configure(&self.shell_surface, configure)
}
}

View File

@ -4,6 +4,7 @@ use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
use crate::wayland::compositor;
use crate::wayland::shell::xdg::{PopupState, XDG_POPUP_ROLE, XDG_TOPLEVEL_ROLE};
use crate::wayland::Serial;
use wayland_protocols::unstable::xdg_decoration::v1::server::zxdg_toplevel_decoration_v1;
use wayland_protocols::xdg_shell::server::{
xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
};
@ -259,6 +260,7 @@ fn xdg_surface_implementation(
wl_surface: data.wl_surface.clone(),
xdg_surface: xdg_surface.clone(),
wm_base: data.wm_base.clone(),
decoration: Default::default(),
});
data.shell_data
@ -334,6 +336,7 @@ fn xdg_surface_implementation(
wl_surface: data.wl_surface.clone(),
xdg_surface: xdg_surface.clone(),
wm_base: data.wm_base.clone(),
decoration: Default::default(),
});
data.shell_data
@ -471,6 +474,7 @@ pub(crate) struct ShellSurfaceUserData {
pub(crate) wl_surface: wl_surface::WlSurface,
pub(crate) wm_base: xdg_wm_base::XdgWmBase,
pub(crate) xdg_surface: xdg_surface::XdgSurface,
pub(crate) decoration: RefCell<Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>>,
}
// Utility functions allowing to factor out a lot of the upcoming logic