smithay/src/wayland/compositor/roles.rs

255 lines
9.0 KiB
Rust

//! Tools for handling surface roles
//!
//! In the Wayland protocol, surfaces can have several different roles, which
//! define how they are to be used. The core protocol defines 3 of these roles:
//!
//! - `shell_surface`: This surface is to be considered as what is most often
//! called a "window".
//! - `pointer_surface`: This surface represent the contents of a pointer icon
//! and replaces the default pointer.
//! - `subsurface`: This surface is part of a subsurface tree, and as such has
//! a parent surface.
//!
//! A surface can have only one role at any given time. To change he role of a
//! surface, the client must first remove the previous role before assigning the
//! new one. A surface without a role is not displayed at all.
//!
//! This module provides tools to manage roles of a surface in a composable way
//! allowing all handlers of smithay to manage surface roles while being aware
//! of the possible role conflicts.
//!
//! ## General mechanism
//!
//! First, all roles need to have an unique type, holding its metadata and identifying it
//! to the type-system. Even if your role does not hold any metadata, you still need its
//! unique type, using a unit-like struct rather than `()`.
//!
//! You then need a type for managing the roles of a surface. This type holds information
//! about what is the current role of a surface, and what is the metadata associated with
//! it.
//!
//! For convenience, you can use the `define_roles!` macro provided by Smithay to define this
//! type. You can call it like this:
//!
//! ```
//! # #[macro_use]
//! # extern crate smithay;
//! #
//! // Metadata for a first role
//! #[derive(Default)]
//! pub struct MyRoleMetadata {
//! }
//!
//! // Metadata for a second role
//! #[derive(Default)]
//! pub struct MyRoleMetadata2 {
//! }
//!
//! define_roles!(Roles =>
//! // You can put several roles like this
//! // first identifier is the name of the variant for this
//! // role in the generated enum, second is the token type
//! // for this role
//! [MyRoleName, MyRoleMetadata]
//! [MyRoleName2, MyRoleMetadata2]
//! /* ... */
//! );
//!
//! # fn main() {}
//! ```
//!
//! And this will expand to an enum like this:
//!
//! ```ignore
//! pub enum Roles {
//! NoRole,
//! // The subsurface role is always inserted, as it is required
//! // by the CompositorHandler
//! Subsurface(::smithay::compositor::SubsurfaceAttributes),
//! // all your other roles come here
//! MyRoleName(MyRoleMetadata),
//! MyRoleName2(MyRoleMetadata2),
//! /* ... */
//! }
//! ```
//!
//! as well as implement a few trait for it, allowing it to be used by
//! all smithay handlers:
//!
//! - The trait [`RoleType`](::wayland::compositor::roles::RoleType),
//! which defines it as a type handling roles
//! - For each of your roles, the trait [`Role<Token>`](::wayland::compositor::roles::Role)
//! (where `Token` is your token type), marking its ability to handle this given role.
//!
//! All handlers that handle a specific role will require you to provide
//! them with a [`CompositorToken<U, R, H>`](::wayland::compositor::CompositorToken)
//! where `R: Role<TheToken>`.
//!
//! See the documentation of these traits for their specific definition and
//! capabilities.
/// An error type signifying that the surface does not have expected role
///
/// Generated if you attempt a role operation on a surface that does
/// not have the role you asked for.
#[derive(Debug)]
pub struct WrongRole;
/// A trait representing a type that can manage surface roles
pub trait RoleType {
/// Check if the associated surface has a role
///
/// Only reports if the surface has any role or no role.
/// To check for a role in particular, see [`Role::has`].
fn has_role(&self) -> bool;
}
/// A trait representing the capability of a [`RoleType`] to handle a given role
///
/// This trait allows to interact with the different roles a [`RoleType`] can
/// handle.
///
/// This trait is meant to be used generically, for example, to retrieve the
/// data associated with a given role with token `TheRole`:
///
/// ```ignore
/// let data = <MyRoles as Role<RoleData>>::data(my_roles)
/// .expect("The surface does not have this role.");
/// ```
///
/// The methods of this trait are mirrored on
/// [`CompositorToken`](::wayland::compositor::CompositorToken) for easy
/// access to the role data of the surfaces.
///
/// Note that if a role is automatically handled for you by a Handler provided
/// by smithay, you should not set or unset it manually on a surface. Doing so
/// would likely corrupt the internal state of these handlers, causing spurious
/// protocol errors and unreliable behaviour overall.
pub trait Role<R>: RoleType {
/// Set the role for the associated surface with default associated data
///
/// Fails if the surface already has a role
fn set(&mut self) -> Result<(), ()>
where
R: Default,
{
self.set_with(Default::default()).map_err(|_| ())
}
/// Set the role for the associated surface with given data
///
/// Fails if the surface already has a role and returns the data
fn set_with(&mut self, data: R) -> Result<(), R>;
/// Check if the associated surface has this role
fn has(&self) -> bool;
/// Access the data associated with this role if its the current one
fn data(&self) -> Result<&R, WrongRole>;
/// Mutably access the data associated with this role if its the current one
fn data_mut(&mut self) -> Result<&mut R, WrongRole>;
/// Remove this role from the associated surface
///
/// Fails if the surface does not currently have this role
fn unset(&mut self) -> Result<R, WrongRole>;
}
#[macro_export]
macro_rules! define_roles(
($enum_name: ident) => {
define_roles!($enum_name =>);
};
($enum_name:ident => $([ $role_name: ident, $role_data: ty])*) => {
define_roles!(__impl $enum_name =>
// add in subsurface role
[Subsurface, $crate::wayland::compositor::SubsurfaceRole]
$([$role_name, $role_data])*
);
};
(__impl $enum_name:ident => $([ $role_name: ident, $role_data: ty])*) => {
pub enum $enum_name {
NoRole,
$($role_name($role_data)),*
}
impl Default for $enum_name {
fn default() -> $enum_name {
$enum_name::NoRole
}
}
impl $crate::wayland::compositor::roles::RoleType for $enum_name {
fn has_role(&self) -> bool {
if let $enum_name::NoRole = *self {
false
} else {
true
}
}
}
$(
impl $crate::wayland::compositor::roles::Role<$role_data> for $enum_name {
fn set_with(&mut self, data: $role_data) -> ::std::result::Result<(), $role_data> {
if let $enum_name::NoRole = *self {
*self = $enum_name::$role_name(data);
Ok(())
} else {
Err(data)
}
}
fn has(&self) -> bool {
if let $enum_name::$role_name(_) = *self {
true
} else {
false
}
}
fn data(&self) -> ::std::result::Result<
&$role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
if let $enum_name::$role_name(ref data) = *self {
Ok(data)
} else {
Err($crate::wayland::compositor::roles::WrongRole)
}
}
fn data_mut(&mut self) -> ::std::result::Result<
&mut $role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
if let $enum_name::$role_name(ref mut data) = *self {
Ok(data)
} else {
Err($crate::wayland::compositor::roles::WrongRole)
}
}
fn unset(&mut self) -> ::std::result::Result<
$role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
// remove self to make borrow checker happy
let temp = ::std::mem::replace(self, $enum_name::NoRole);
if let $enum_name::$role_name(data) = temp {
Ok(data)
} else {
// put it back in place
::std::mem::replace(self, temp);
Err($crate::wayland::compositor::roles::WrongRole)
}
}
}
)*
};
);