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
use std::sync::{Arc, Mutex};

use crate::protocol::wl_display;
use crate::protocol::wl_registry;
use crate::{Attached, DispatchData, Interface, Main, Proxy};

struct Inner {
    list: Vec<(u32, String, u32)>,
}

/// An utility to manage global objects
///
/// This utility provides an implemenation for the registry
/// that track the list of globals for you, as well as utilities
/// to bind them.
#[derive(Clone)]
pub struct GlobalManager {
    inner: Arc<Mutex<Inner>>,
    registry: Main<wl_registry::WlRegistry>,
}

/// An error that occurred trying to bind a global
#[derive(Debug, PartialEq)]
pub enum GlobalError {
    /// The requested global was missing
    Missing,
    /// The global advertised by the server has a lower version number
    /// than the one requested
    VersionTooLow(u32),
}

impl ::std::error::Error for GlobalError {}

impl ::std::fmt::Display for GlobalError {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        match *self {
            GlobalError::Missing => f.write_str("The requested global was missing."),
            GlobalError::VersionTooLow(_) => {
                f.write_str("The requested global's version is too low.")
            }
        }
    }
}

/// Event provided to the user callback of GlobalManager
pub enum GlobalEvent {
    /// A new global was created
    New {
        /// Id of the new global
        id: u32,
        /// Interface of the new global
        interface: String,
        /// Maximum supported version of the new global
        version: u32,
    },
    /// A global was removed
    Removed {
        /// Id of the removed global
        id: u32,
        /// Interface of the removed global
        interface: String,
    },
}

impl GlobalManager {
    /// Create a global manager handling a registry
    ///
    /// You need to provide an attached handle of the Waland display, and the
    /// global manager will be managed by the associated event queue.
    pub fn new(display: &Attached<wl_display::WlDisplay>) -> GlobalManager {
        let inner = Arc::new(Mutex::new(Inner { list: Vec::new() }));
        let inner_clone = inner.clone();

        let registry = display
            .as_ref()
            .send::<wl_registry::WlRegistry>(wl_display::Request::GetRegistry {}, None)
            .unwrap();
        registry.quick_assign(move |_proxy, msg, _data| {
            let mut inner = inner.lock().unwrap();
            match msg {
                wl_registry::Event::Global { name, interface, version } => {
                    inner.list.push((name, interface, version));
                }
                wl_registry::Event::GlobalRemove { name } => {
                    inner.list.retain(|&(n, _, _)| n != name);
                }
            }
        });

        GlobalManager { inner: inner_clone, registry }
    }

    /// Create a global manager handling a registry with a callback
    ///
    /// This global manager will track globals as a simple one, but will
    /// also forward the registry events to your callback.
    ///
    /// This can be used if you want to handle specially certain globals, but want
    /// to use the default mechanism for the rest.
    ///
    /// You need to provide an attached handle of the Waland display, and the
    /// global manager will be managed by the associated event queue.
    pub fn new_with_cb<F>(
        display: &Attached<wl_display::WlDisplay>,
        mut callback: F,
    ) -> GlobalManager
    where
        F: FnMut(GlobalEvent, Attached<wl_registry::WlRegistry>, DispatchData) + 'static,
    {
        let inner = Arc::new(Mutex::new(Inner { list: Vec::new() }));
        let inner_clone = inner.clone();

        let registry = display
            .as_ref()
            .send::<wl_registry::WlRegistry>(wl_display::Request::GetRegistry {}, None)
            .unwrap();
        registry.quick_assign(move |proxy, msg, data| {
            let mut inner = inner.lock().unwrap();
            let inner = &mut *inner;
            match msg {
                wl_registry::Event::Global {
                    name,
                    interface,
                    version,
                } => {
                    inner.list.push((name, interface.clone(), version));
                    callback(
                        GlobalEvent::New {
                            id: name,
                            interface,
                            version,
                        },
                        (*proxy).clone(),
                        data,
                    );
                }
                wl_registry::Event::GlobalRemove { name } => {
                    if let Some((i, _)) = inner.list.iter().enumerate().find(|&(_, &(n, _, _))| n == name) {
                        let (id, interface, _) = inner.list.swap_remove(i);
                        callback(GlobalEvent::Removed { id, interface }, (*proxy).clone(), data);
                    } else {
                        panic!(
                            "Wayland protocol error: the server removed non-existing global \"{}\".",
                            name
                        );
                    }
                }
            }
        });

        GlobalManager { inner: inner_clone, registry }
    }

    /// Instantiate a global with a specific version
    ///
    /// Meaning of requests and events can change depending on the object version you use,
    /// as such unless you specifically want to support several versions of a protocol, it is
    /// recommended to use this method with an hardcoded value for the version (the one you'll
    /// use a as reference for your implementation). Notably you should *not* use `I::VERSION`
    /// as a version, as this value can change when the protocol files are updated.
    ///
    /// This method is only appropriate for globals that are expected to
    /// not exist with multiplicity (such as `wl_compositor` or `wl_shm`),
    /// as it will always bind the first one that was advertized.
    pub fn instantiate_exact<I>(&self, version: u32) -> Result<Main<I>, GlobalError>
    where
        I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
    {
        let inner = self.inner.lock().unwrap();
        for &(id, ref interface, server_version) in &inner.list {
            if interface == I::NAME {
                if version > server_version {
                    return Err(GlobalError::VersionTooLow(server_version));
                } else {
                    return Ok(self.registry.bind::<I>(version, id));
                }
            }
        }
        Err(GlobalError::Missing)
    }

    /// Instantiate a global from a version range
    ///
    /// If you want to support several versions of a particular global, this method allows you to
    /// specify a range of versions that you accept. It'll bind the highest possible version that
    /// is between `min_version` and `max_version` inclusive, and return an error if the highest
    /// version supported by the compositor is lower than `min_version`. As for
    /// `instantiate_exact`, you should not use `I::VERSION` here: the versions your code support
    /// do not change when the protocol files are updated.
    ///
    /// When trying to support several versions of a protocol, you can check which version has
    /// actually been used on any object using the `Proxy::version()` method.
    ///
    /// As `instantiate_exact`, it should only be used for singleton globals, for the same reasons.
    pub fn instantiate_range<I>(
        &self,
        min_version: u32,
        max_version: u32,
    ) -> Result<Main<I>, GlobalError>
    where
        I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
    {
        let inner = self.inner.lock().unwrap();
        for &(id, ref interface, version) in &inner.list {
            if interface == I::NAME {
                if version >= min_version {
                    let version = ::std::cmp::min(version, max_version);
                    return Ok(self.registry.bind::<I>(version, id));
                } else {
                    return Err(GlobalError::VersionTooLow(version));
                }
            }
        }
        Err(GlobalError::Missing)
    }

    /// Retrieve the list of currently known globals
    pub fn list(&self) -> Vec<(u32, String, u32)> {
        self.inner.lock().unwrap().list.clone()
    }
}

/// A trait for implementation of the global advertisement
///
/// It is automatically implemented for `FnMut(Main<I>, DispatchData)` closures,
/// in which case the `error` messages are ignored.
pub trait GlobalImplementor<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>> {
    /// A new global of given interface has been instantiated and you can assign
    /// a filter to it.
    fn new_global(&mut self, global: Main<I>, data: DispatchData);
    /// A global was advertised but its version was lower than the minimal version
    /// you requested.
    ///
    /// The advertised version is provided as argument.
    fn error(&mut self, _version: u32, _data: DispatchData) {}
}

impl<F, I: Interface> GlobalImplementor<I> for F
where
    I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
    F: FnMut(Main<I>, DispatchData),
{
    fn new_global(&mut self, global: Main<I>, data: DispatchData) {
        (*self)(global, data)
    }
}

/// Convenience macro to create a `GlobalManager` callback
///
/// This macro aims to simplify the specific but common case of
/// providing a callback to the `GlobalManager` that needs to
/// auto-bind all advertised instances of some specific globals
/// whenever they happen. Typically, your application will likely
/// want to keep track of all `wl_seat` and `wl_output` globals
/// to be able to correctly react to user input and their different
/// monitors.
///
/// The output of this macro is a closure, that can be given to
/// `GlobalManager::new_with_cb` as the callback argument.
///
/// Example use is typically:
///
/// ```no_run
/// # #[macro_use] extern crate wayland_client;
/// use wayland_client::GlobalManager;
/// # use wayland_client::{Display, Main, DispatchData};
/// use wayland_client::protocol::{wl_output, wl_seat};
///
/// # fn main() {
/// # let display = Display::connect_to_env().unwrap();
/// # let mut event_queue = display.create_event_queue();
/// # let seat_implementor: fn(Main<_>, DispatchData) = unimplemented!();
/// # let output_implementor: fn(Main<_>, DispatchData) = unimplemented!();
/// let globals = GlobalManager::new_with_cb(
///     &display.attach(event_queue.token()),
///     global_filter!(
///         // Bind all wl_seat with version 4
///         [wl_seat::WlSeat, 4, seat_implementor],
///         // Bind all wl_output with version 1
///         [wl_output::WlOutput, 1, output_implementor]
///     )
/// );
/// # }
/// ```
///
/// The supplied callbacks for each global kind must be an instance of a type
/// implementing the `GlobalImplementor<I>` trait. The argument provided to your
/// callback is a `Main` handle of the newly instantiated global, and you should assign it
/// to a filter in this callback if you plan to do so.. The error case happens if the server
/// advertised a lower version of the global than the one you requested, in which case you
/// are given the version it advertised in the error method, if you want to handle it graciously.
///
/// You can also provide closures for the various callbacks, in this case the errors will
/// be ignored. However, due to a lack of capability of rustc's inference, you'll likely need
/// to add some type annotation to your closure, typically something like this:
///
/// ```ignore
/// global_filter!(
///     [Interface, version, |proxy: Main<_>, dispatch_data| {
///         /* Setup the global as required */
///     }]
/// );
/// ```
#[macro_export]
macro_rules! global_filter {
    ($([$interface:ty, $version:expr, $callback:expr]),*) => {
        {
            use $crate::protocol::wl_registry;
            use $crate::{GlobalEvent, Interface, Attached, GlobalImplementor, DispatchData};
            type Callback = Box<dyn FnMut(u32, u32, Attached<wl_registry::WlRegistry>, DispatchData<'_>)>;
            let mut callbacks: Vec<(&'static str, Callback)> = Vec::new();
            // Create the callback list
            $({
                let mut cb = { $callback };
                callbacks.push((
                    <$interface as Interface>::NAME,
                    Box::new(move |id, version, registry: Attached<wl_registry::WlRegistry>, ddata: DispatchData| {
                        if version < $version {
                            GlobalImplementor::<$interface>::error(&mut cb, version, ddata);
                        } else {
                            let proxy = registry.bind::<$interface>(version, id);
                            GlobalImplementor::<$interface>::new_global(&mut cb, proxy, ddata);
                        }
                    }) as Box<_>
                ));
            })*

            // return the global closure
            move |event: GlobalEvent, registry: Attached<wl_registry::WlRegistry>, ddata| {
                if let GlobalEvent::New { id, interface, version } = event {
                    for &mut (iface, ref mut cb) in &mut callbacks {
                        if iface == interface {
                            cb(id, version, registry, ddata);
                            break;
                        }
                    }
                }
            }
        }
    }
}