1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
//! 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)
}
}
}
)*
};
);