//! 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`, which defines it as a type handling roles //! - For each of your roles, the trait `Role` (where `Token` is your //! token type), marking its hability to handle this given role. //! //! All handlers that handle a specific role will require you to provide //! them with a `CompositorToken` where `R: Role`. //! //! 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 = >::data(my_roles) /// .expect("The surface does not have this role."); /// ``` /// /// The methods of this trait are mirrored on `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: 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; } #[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::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::compositor::roles::RoleType for $enum_name { fn has_role(&self) -> bool { if let $enum_name::NoRole = *self { false } else { true } } } $( impl $crate::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::compositor::roles::WrongRole> { if let $enum_name::$role_name(ref data) = *self { Ok(data) } else { Err($crate::compositor::roles::WrongRole) } } fn data_mut(&mut self) -> ::std::result::Result<&mut $role_data, $crate::compositor::roles::WrongRole> { if let $enum_name::$role_name(ref mut data) = *self { Ok(data) } else { Err($crate::compositor::roles::WrongRole) } } fn unset(&mut self) -> ::std::result::Result<$role_data, $crate::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::compositor::roles::WrongRole) } } } )* }; );