wayland.xdg-decor: Implement XDG Decoration protocol
This commit is contained in:
parent
77c970e197
commit
10aee60b87
|
@ -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)
|
||||||
|
}
|
|
@ -74,6 +74,8 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
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_surface;
|
||||||
use wayland_protocols::xdg_shell::server::{xdg_popup, xdg_positioner, xdg_toplevel, xdg_wm_base};
|
use wayland_protocols::xdg_shell::server::{xdg_popup, xdg_positioner, xdg_toplevel, xdg_wm_base};
|
||||||
use wayland_server::DispatchData;
|
use wayland_server::DispatchData;
|
||||||
|
@ -82,8 +84,12 @@ use wayland_server::{
|
||||||
Display, Filter, Global, UserDataMap,
|
Display, Filter, Global, UserDataMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::xdg_handlers::ShellSurfaceUserData;
|
||||||
|
|
||||||
use super::PingError;
|
use super::PingError;
|
||||||
|
|
||||||
|
pub mod decoration;
|
||||||
|
|
||||||
// handlers for the xdg_shell protocol
|
// handlers for the xdg_shell protocol
|
||||||
pub(super) mod xdg_handlers;
|
pub(super) mod xdg_handlers;
|
||||||
|
|
||||||
|
@ -549,6 +555,9 @@ pub struct ToplevelState {
|
||||||
|
|
||||||
/// The output for a fullscreen display
|
/// The output for a fullscreen display
|
||||||
pub fullscreen_output: Option<wl_output::WlOutput>,
|
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 {
|
impl Default for ToplevelState {
|
||||||
|
@ -557,6 +566,7 @@ impl Default for ToplevelState {
|
||||||
fullscreen_output: None,
|
fullscreen_output: None,
|
||||||
states: Default::default(),
|
states: Default::default(),
|
||||||
size: None,
|
size: None,
|
||||||
|
decoration_mode: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -567,6 +577,7 @@ impl Clone for ToplevelState {
|
||||||
fullscreen_output: self.fullscreen_output.clone(),
|
fullscreen_output: self.fullscreen_output.clone(),
|
||||||
states: self.states.clone(),
|
states: self.states.clone(),
|
||||||
size: self.size,
|
size: self.size,
|
||||||
|
decoration_mode: self.decoration_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -980,6 +991,35 @@ impl ToplevelSurface {
|
||||||
})
|
})
|
||||||
.unwrap_or(None);
|
.unwrap_or(None);
|
||||||
if let Some(configure) = configure {
|
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)
|
self::xdg_handlers::send_toplevel_configure(&self.shell_surface, configure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::{cell::RefCell, ops::Deref as _, sync::Mutex};
|
||||||
use crate::wayland::compositor;
|
use crate::wayland::compositor;
|
||||||
use crate::wayland::shell::xdg::{PopupState, XDG_POPUP_ROLE, XDG_TOPLEVEL_ROLE};
|
use crate::wayland::shell::xdg::{PopupState, XDG_POPUP_ROLE, XDG_TOPLEVEL_ROLE};
|
||||||
use crate::wayland::Serial;
|
use crate::wayland::Serial;
|
||||||
|
use wayland_protocols::unstable::xdg_decoration::v1::server::zxdg_toplevel_decoration_v1;
|
||||||
use wayland_protocols::xdg_shell::server::{
|
use wayland_protocols::xdg_shell::server::{
|
||||||
xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
|
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(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
xdg_surface: xdg_surface.clone(),
|
xdg_surface: xdg_surface.clone(),
|
||||||
wm_base: data.wm_base.clone(),
|
wm_base: data.wm_base.clone(),
|
||||||
|
decoration: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
||||||
data.shell_data
|
data.shell_data
|
||||||
|
@ -334,6 +336,7 @@ fn xdg_surface_implementation(
|
||||||
wl_surface: data.wl_surface.clone(),
|
wl_surface: data.wl_surface.clone(),
|
||||||
xdg_surface: xdg_surface.clone(),
|
xdg_surface: xdg_surface.clone(),
|
||||||
wm_base: data.wm_base.clone(),
|
wm_base: data.wm_base.clone(),
|
||||||
|
decoration: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
||||||
data.shell_data
|
data.shell_data
|
||||||
|
@ -471,6 +474,7 @@ pub(crate) struct ShellSurfaceUserData {
|
||||||
pub(crate) wl_surface: wl_surface::WlSurface,
|
pub(crate) wl_surface: wl_surface::WlSurface,
|
||||||
pub(crate) wm_base: xdg_wm_base::XdgWmBase,
|
pub(crate) wm_base: xdg_wm_base::XdgWmBase,
|
||||||
pub(crate) xdg_surface: xdg_surface::XdgSurface,
|
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
|
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||||
|
|
Loading…
Reference in New Issue