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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
//! Environment management utilities
//!
//! This module provide the tools to automatically bind the wayland global objects you need in your program.
//!
//! At the heart of this is the `environment!` macro, which allows you to signal the globals you need
//! and a struct to manage them as they are signaled in the registry.
//!
//! ## Global handlers
//!
//! Wayland globals are split in two kinds, that we will call here "single" globals and "multi" globals.
//!
//! - "single" globals represent a capability of the server. They are generally signaled in the registry
//!   from the start and never removed. They are signaled a single time. Examples of these globals are
//!   `wl_compositor`, `wl_shm` or `xdg_wm_base`.
//! - "multi" globals represent a resource that the server gives you access to. These globals can be
//!   created or removed during the run of the program, and may exist as more than one instance, each
//!   representing a different physical resource. Examples of such globals are `wl_output` or `wl_seat`.
//!
//! The objects you need to handle these globals must implement one the two traits
//! [`GlobalHandler<I>`](trait.GlobalHandler.html) or [`MultiGlobalHandler<I>`](trait.MultiGlobalHandler.html),
//! depending on the kind of globals it will handle. These objects are responsible for binding the globals
//! from the registry, and assigning them to filters to receive their events as necessary.
//!
//! This module provides a generic implementation of the [`GlobalHandler<I>`](trait.GlobalHandler.html) trait
//! as [`SimpleGlobal<I>`](struct.SimpleGlobal.html). It can manage "single" globals that do not generate
//! events, and thus require no filter.
//!
//! ## the  `environment!` macro
//!
//! This macro is at the core of this module. See its documentation for details about how to
//! use it: [`environment!`](../macro.environment.html). You can alternatively use the
//! [`default_environment!`](../macro.default_environment.html) macro to quickly setup things and bring
//! in all SCTK modules.

use std::io::Result;
use std::rc::Rc;
use std::{cell::RefCell, fmt};

use wayland_client::{
    protocol::{wl_display, wl_registry},
    Attached, DispatchData, EventQueue, GlobalEvent, GlobalManager, Interface, Proxy,
};

/*
 * Traits definitions
 */

/// Required trait for implementing a handler for "single" globals
pub trait GlobalHandler<I: Interface> {
    /// This global was created and signaled in the registry with given id and version
    fn created(
        &mut self,
        registry: Attached<wl_registry::WlRegistry>,
        id: u32,
        version: u32,
        ddata: DispatchData,
    );
    /// Access the global if it was signaled
    fn get(&self) -> Option<Attached<I>>;
}

/// Required trait for implementing a handler for "multi" globals
pub trait MultiGlobalHandler<I: Interface> {
    /// A new instance of this global was created with given id and version
    fn created(
        &mut self,
        registry: Attached<wl_registry::WlRegistry>,
        id: u32,
        version: u32,
        ddata: DispatchData,
    );
    /// The instance with given id was removed
    fn removed(&mut self, id: u32, ddata: DispatchData);
    /// Access all the currently existing instances
    fn get_all(&self) -> Vec<Attached<I>>;
}

/*
 * General Environment<E>
 */

/// A Wayland Environment
///
/// This struct is generated by the `environment!` macro, see module-level documentation
/// for more details about this.
///
/// This is the central point for accessing globals for your Wayland app. Any global that has
/// previously been declared in the `environment!` macro can be access from this type via the
/// `get_global`, `required_global` and `get_all_globals` methods.
///
/// This `Environment` is a handle that can be cloned.
pub struct Environment<E> {
    /// The underlying `GlobalManager`, if you need to do manual interaction with the
    /// registry. See `wayland-client` documentation for details.
    pub manager: GlobalManager,
    inner: Rc<RefCell<E>>,
}

impl<E: InnerEnv + 'static> Environment<E> {
    /// Create new `Environment`
    ///
    /// This requires access to a `wl_display` attached to the `event_queue`.
    /// You also need to provide an instance of the inner environment type declared
    /// using the [`environment!`](../macro.environment.html) macro.
    ///
    /// If you instead used the [`default_environment!`](../macro.default_environment.html), then
    /// you need to initialize your `Environment` using the
    /// [`new_default_environment!`](../macro.new_default_environment.html) macro.
    ///
    /// `std::io::Error` could be returned if initial roundtrips to the server failed.
    ///
    /// If this call indefinitely blocks when doing initial roundtrips this can only be
    /// caused by server bugs.
    pub fn new(
        display: &Attached<wl_display::WlDisplay>,
        queue: &mut EventQueue,
        env: E,
    ) -> Result<Environment<E>> {
        let environment = Self::new_pending(display, env);

        // Fully initialize the environment.
        queue.sync_roundtrip(&mut (), |event, _, _| {
            panic!(
                "Encountered unhandled event during initial roundtrip ({}::{})",
                event.interface, event.name
            );
        })?;
        queue.sync_roundtrip(&mut (), |event, _, _| {
            panic!(
                "Encountered unhandled event during initial roundtrip ({}::{})",
                event.interface, event.name
            );
        })?;

        Ok(environment)
    }

    /// Create new pending `Environment`
    ///
    /// This requires access to a `wl_display` attached to an event queue (on which the main SCTK logic
    /// will be attached). You also need to provide an instance of the inner environment type declared
    /// using the [`environment!`](../macro.environment.html) macro.
    ///
    /// If you instead used the [`default_environment!`](../macro.default_environment.html), then you need
    /// to initialize your `Environment` using the
    /// [`new_default_environment!`](../macro.new_default_environment.html) macro.
    ///
    /// You should prefer to use `Environment::new`, unless you want to control initialization
    /// manually or you create additional environment meaning that the initialization may be fine
    /// with just `dispatch_pending` of the event queue, instead of two roundtrips to
    /// fully initialize environment. If you manually initialize your environment two sync
    /// roundtrips are required.
    pub fn new_pending(display: &Attached<wl_display::WlDisplay>, env: E) -> Environment<E> {
        let inner = Rc::new(RefCell::new(env));

        let my_inner = inner.clone();
        let my_cb = move |event, registry, ddata: DispatchData| {
            let mut inner = my_inner.borrow_mut();
            inner.process_event(event, registry, ddata);
        };

        let manager = GlobalManager::new_with_cb(display, my_cb);

        Self { manager, inner }
    }
}

impl<E> Environment<E> {
    /// Access a "single" global
    ///
    /// This method allows you to access any "single" global that has previously
    /// been declared in the `environment!` macro. It is forwarded to the `get()`
    /// method of the appropriate `GlobalHandler`.
    ///
    /// It returns `None` if the global has not (yet) been signaled by the registry.
    pub fn get_global<I: Interface>(&self) -> Option<Attached<I>>
    where
        E: GlobalHandler<I>,
    {
        self.inner.borrow().get()
    }

    /// Access a "single" global or panic
    ///
    /// This method is similar to `get_global`, but will panic with a detailed error
    /// message if the requested global was not advertized by the server.
    pub fn require_global<I: Interface>(&self) -> Attached<I>
    where
        E: GlobalHandler<I>,
    {
        match self.inner.borrow().get() {
            Some(g) => g,
            None => panic!("[SCTK] A missing global was required: {}", I::NAME),
        }
    }

    /// Access all instances of a "multi" global
    ///
    /// This will return a `Vec` containing all currently existing instances of the
    /// requested "multi" global that has been previously declared in the `environment!`
    /// macro. It is forwarded to the `get_all()` method of the appropriate
    /// `MultiGlobalHandler`.
    pub fn get_all_globals<I: Interface>(&self) -> Vec<Attached<I>>
    where
        E: MultiGlobalHandler<I>,
    {
        self.inner.borrow().get_all()
    }

    /// Access the inner environment
    ///
    /// This gives your access, via a closure, to the inner type you declared
    /// via the [`environment!`](../macro.environment.html) or
    /// [`default_environment!`](../macro.default_environment.html) macro.
    ///
    /// This method returns the return value of your closure.
    pub fn with_inner<T, F: FnOnce(&mut E) -> T>(&self, f: F) -> T {
        let mut inner = self.inner.borrow_mut();
        f(&mut *inner)
    }
}

impl<E> Clone for Environment<E> {
    fn clone(&self) -> Environment<E> {
        Environment { manager: self.manager.clone(), inner: self.inner.clone() }
    }
}

impl<E> fmt::Debug for Environment<E>
where
    E: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Environment")
            .field("manager", &self.manager)
            .field("inner", &self.inner)
            .finish()
    }
}

/// Internal trait for the `Environment` logic
///
/// This trait is automatically implemented by the [`environment!`](../macro.environment.html)
/// macro, you should not implement it manually unless you seriously want to.
pub trait InnerEnv {
    /// Process a `GlobalEvent`
    fn process_event(
        &mut self,
        event: GlobalEvent,
        registry: Attached<wl_registry::WlRegistry>,
        data: DispatchData,
    );
}

/*
 * Simple handlers
 */

/// A minimalist global handler for "single" globals
///
/// This handler will simply register the global as soon as the registry signals
/// it, and do nothing more.
///
/// It is appropriate for globals that never generate events, like `wl_compositor`
/// or `wl_data_device_manager`.
#[derive(Debug)]
pub struct SimpleGlobal<I: Interface> {
    global: Option<Attached<I>>,
}

impl<I: Interface> SimpleGlobal<I> {
    /// Create a new handler
    pub fn new() -> SimpleGlobal<I> {
        SimpleGlobal { global: None }
    }
}

impl<I: Interface + Clone + From<Proxy<I>> + AsRef<Proxy<I>>> GlobalHandler<I> for SimpleGlobal<I> {
    fn created(
        &mut self,
        registry: Attached<wl_registry::WlRegistry>,
        id: u32,
        version: u32,
        _: DispatchData,
    ) {
        let version = I::VERSION.min(version);
        self.global = Some((*registry.bind::<I>(version, id)).clone())
    }
    fn get(&self) -> Option<Attached<I>> {
        self.global.clone()
    }
}

/*
 * environment! macro
 */

/// Macro for declaring an environment
///
/// It needs to be used in conjunction with a a `struct` you declared, which will serve as the inner
/// environment and hold the handlers for your globals.
///
/// The macro is invoked as such:
///
/// ```no_run
/// # extern crate smithay_client_toolkit as sctk;
/// # use sctk::reexports::client::protocol::{wl_compositor::WlCompositor, wl_subcompositor::WlSubcompositor, wl_output::WlOutput};
/// # use sctk::environment::SimpleGlobal;
/// # use sctk::environment;
/// # use sctk::output::OutputHandler;
/// struct MyEnv {
///     compositor: SimpleGlobal<WlCompositor>,
///     subcompositor: SimpleGlobal<WlSubcompositor>,
///     outputs: OutputHandler
/// }
///
/// environment!(MyEnv,
///     singles = [
///         WlCompositor => compositor,
///         WlSubcompositor => subcompositor,
///     ],
///     multis = [
///         WlOutput => outputs,
///     ]
/// );
/// ```
///
/// This will define how your `MyEnv` struct is able to manage the `WlCompositor`, `WlSubcompositor` and
/// `WlOutput` globals. For each global, you need to provide a pattern
/// `$type => $name` where:
///
/// - `$type` is the type (implementing the `Interface` trait from `wayland-client`) representing a global
/// - `$name` is the name of the field of `MyEnv` that is in charge of managing this global, implementing the
///   appropriate `GlobalHandler` or `MultiGlobalHandler` trait
///
/// It is possible to route several globals to the same field as long as it implements all the appropriate traits.
#[macro_export]
macro_rules! environment {
    ($env_name:ident,
        singles = [$($sty:ty => $sname:ident),* $(,)?],
        multis = [$($mty:ty => $mname:ident),* $(,)?]$(,)?
    ) => {
        impl $crate::environment::InnerEnv for $env_name {
            fn process_event(
                &mut self,
                event: $crate::reexports::client::GlobalEvent,
                registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>,
                ddata: $crate::reexports::client::DispatchData,
            ) {
                match event {
                    $crate::reexports::client::GlobalEvent::New { id, interface, version } => match &interface[..] {
                        $(
                            <$sty as $crate::reexports::client::Interface>::NAME => $crate::environment::GlobalHandler::<$sty>::created(&mut self.$sname, registry, id, version, ddata),
                        )*
                        $(
                            <$mty as $crate::reexports::client::Interface>::NAME => $crate::environment::MultiGlobalHandler::<$mty>::created(&mut self.$mname, registry, id, version, ddata),
                        )*
                        _ => { /* ignore unkown globals */ }
                    },
                    $crate::reexports::client::GlobalEvent::Removed { id, interface } => match &interface[..] {
                        $(
                            <$mty as $crate::reexports::client::Interface>::NAME => $crate::environment::MultiGlobalHandler::<$mty>::removed(&mut self.$mname, id, ddata),
                        )*
                        _ => { /* ignore unknown globals */ }
                    }
                }
            }
        }

        $(
            impl $crate::environment::GlobalHandler<$sty> for $env_name {
                fn created(&mut self, registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>, id: u32, version: u32, ddata: $crate::reexports::client::DispatchData) {
                    $crate::environment::GlobalHandler::<$sty>::created(&mut self.$sname, registry, id, version, ddata)
                }
                fn get(&self) -> Option<$crate::reexports::client::Attached<$sty>> {
                    $crate::environment::GlobalHandler::<$sty>::get(&self.$sname)
                }
            }
        )*

        $(
            impl $crate::environment::MultiGlobalHandler<$mty> for $env_name {
                fn created(&mut self, registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>, id: u32, version: u32, ddata: $crate::reexports::client::DispatchData) {
                    $crate::environment::MultiGlobalHandler::<$mty>::created(&mut self.$mname, registry, id, version, ddata)
                }
                fn removed(&mut self, id: u32, ddata: $crate::reexports::client::DispatchData) {
                    $crate::environment::MultiGlobalHandler::<$mty>::removed(&mut self.$mname, id, ddata)
                }
                fn get_all(&self) -> Vec<$crate::reexports::client::Attached<$mty>> {
                    $crate::environment::MultiGlobalHandler::<$mty>::get_all(&self.$mname)
                }
            }
        )*
    };
}