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)
                    }
                }
            }
        )*
    };
);