smithay_client_toolkit/
registry.rs

1//! Utilities for binding globals with [`wl_registry`] in delegates.
2//!
3//! This module is based around the [`RegistryHandler`] trait and [`RegistryState`].
4//!
5//! [`RegistryState`] provides an interface to bind globals regularly, creating an object with each new
6//! instantiation or caching bound globals to prevent duplicate object instances from being created. Binding
7//! a global regularly is accomplished through [`RegistryState::bind_one`].
8//!
9//! The [`delegate_registry`](crate::delegate_registry) macro is used to implement handling for [`wl_registry`].
10//!
11//! ## Sample implementation of [`RegistryHandler`]
12//!
13//! ```
14//! use smithay_client_toolkit::reexports::client::{
15//!     Connection, Dispatch, QueueHandle,
16//!     delegate_dispatch,
17//!     globals::GlobalList,
18//!     protocol::wl_output,
19//! };
20//!
21//! use smithay_client_toolkit::registry::{
22//!     GlobalProxy, ProvidesRegistryState, RegistryHandler, RegistryState,
23//! };
24//!
25//! struct ExampleApp {
26//!     /// The registry state is needed to use the global abstractions.
27//!     registry_state: RegistryState,
28//!     /// This is a type we want to delegate global handling to.
29//!     delegate_that_wants_registry: Delegate,
30//! }
31//!
32//! /// The delegate a global should be provided to.
33//! struct Delegate {
34//!     outputs: Vec<wl_output::WlOutput>,
35//! }
36//!
37//! // When implementing RegistryHandler, you must be able to dispatch any type you could bind using the registry state.
38//! impl<D> RegistryHandler<D> for Delegate
39//! where
40//!     // In order to bind a global, you must statically assert the global may be handled with the data type.
41//!     D: Dispatch<wl_output::WlOutput, ()>
42//!         // ProvidesRegistryState provides a function to access the RegistryState within the impl.
43//!         + ProvidesRegistryState
44//!         // We need some way to access our part of the application's state.  This uses AsMut,
45//!         // but you may prefer to create your own trait to avoid making .as_mut() ambiguous.
46//!         + AsMut<Delegate>
47//!         + 'static,
48//! {
49//!   /// New global added after initial enumeration.
50//!    fn new_global(
51//!        data: &mut D,
52//!        conn: &Connection,
53//!        qh: &QueueHandle<D>,
54//!        name: u32,
55//!        interface: &str,
56//!        version: u32,
57//!    ) {
58//!         if interface == "wl_output" {
59//!             // Bind `wl_output` with newest version from 1 to 4 the compositor supports
60//!             let output = data.registry().bind_specific(qh, name, 1..=4, ()).unwrap();
61//!             data.as_mut().outputs.push(output);
62//!         }
63//!
64//!         // You could either handle errors here or when attempting to use the interface.  Most
65//!         // Wayland protocols are optional, so if your application can function without a
66//!         // protocol it should try to do so; the From impl of GlobalProxy is written to make
67//!         // this straightforward.
68//!     }
69//! }
70//! ```
71
72use crate::{error::GlobalError, globals::ProvidesBoundGlobal};
73use wayland_client::{
74    globals::{BindError, Global, GlobalList, GlobalListContents},
75    protocol::wl_registry,
76    Connection, Dispatch, Proxy, QueueHandle,
77};
78
79/// A trait implemented by modular parts of a smithay's client toolkit and protocol delegates that may be used
80/// to receive notification of a global being created or destroyed.
81///
82/// Delegates that choose to implement this trait may be used in [`registry_handlers`] which
83/// automatically notifies delegates about the creation and destruction of globals.
84///
85/// [`registry_handlers`]: crate::registry_handlers
86///
87/// Note that in order to delegate registry handling to a type which implements this trait, your `D` data type
88/// must implement [`ProvidesRegistryState`].
89pub trait RegistryHandler<D>
90where
91    D: ProvidesRegistryState,
92{
93    /// Called when a new global has been advertised by the compositor.
94    ///
95    /// The provided registry handle may be used to bind the global.  This is not called during
96    /// initial enumeration of globals. It is primarily useful for multi-instance globals such as
97    /// `wl_output` and `wl_seat`.
98    ///
99    /// The default implementation does nothing.
100    fn new_global(
101        data: &mut D,
102        conn: &Connection,
103        qh: &QueueHandle<D>,
104        name: u32,
105        interface: &str,
106        version: u32,
107    ) {
108        let _ = (data, conn, qh, name, interface, version);
109    }
110
111    /// Called when a global has been destroyed by the compositor.
112    ///
113    /// The default implementation does nothing.
114    fn remove_global(
115        data: &mut D,
116        conn: &Connection,
117        qh: &QueueHandle<D>,
118        name: u32,
119        interface: &str,
120    ) {
121        let _ = (data, conn, qh, name, interface);
122    }
123}
124
125/// Trait which asserts a data type may provide a mutable reference to the registry state.
126///
127/// Typically this trait will be required by delegates or [`RegistryHandler`] implementations which need
128/// to access the registry utilities provided by Smithay's client toolkit.
129pub trait ProvidesRegistryState: Sized {
130    /// Returns a mutable reference to the registry state.
131    fn registry(&mut self) -> &mut RegistryState;
132
133    /// Called when a new global has been advertised by the compositor.
134    ///
135    /// This is not called during initial global enumeration.
136    fn runtime_add_global(
137        &mut self,
138        conn: &Connection,
139        qh: &QueueHandle<Self>,
140        name: u32,
141        interface: &str,
142        version: u32,
143    );
144
145    /// Called when a global has been destroyed by the compositor.
146    fn runtime_remove_global(
147        &mut self,
148        conn: &Connection,
149        qh: &QueueHandle<Self>,
150        name: u32,
151        interface: &str,
152    );
153}
154
155/// State object associated with the registry handling for smithay's client toolkit.
156///
157/// This object provides utilities to cache bound globals that are needed by multiple modules.
158#[derive(Debug)]
159pub struct RegistryState {
160    registry: wl_registry::WlRegistry,
161    globals: Vec<Global>,
162}
163
164impl RegistryState {
165    /// Creates a new registry handle.
166    ///
167    /// This type may be used to bind globals as they are advertised.
168    pub fn new(global_list: &GlobalList) -> Self {
169        let registry = global_list.registry().clone();
170        let globals = global_list.contents().clone_list();
171
172        RegistryState { registry, globals }
173    }
174
175    pub fn registry(&self) -> &wl_registry::WlRegistry {
176        &self.registry
177    }
178
179    /// Returns an iterator over all globals.
180    ///
181    /// This list may change if the compositor adds or removes globals after initial
182    /// enumeration.
183    ///
184    /// No guarantees are provided about the ordering of the globals in this iterator.
185    pub fn globals(&self) -> impl Iterator<Item = &Global> + '_ {
186        self.globals.iter()
187    }
188
189    /// Returns an iterator over all globals implementing the given interface.
190    ///
191    /// This may be more efficient than searching [Self::globals].
192    pub fn globals_by_interface<'a>(
193        &'a self,
194        interface: &'a str,
195    ) -> impl Iterator<Item = &Global> + 'a {
196        self.globals.iter().filter(move |g| g.interface == interface)
197    }
198
199    /// Binds a global, returning a new object associated with the global.
200    ///
201    /// This should not be used to bind globals that have multiple instances such as `wl_output`;
202    /// use [Self::bind_all] instead.
203    pub fn bind_one<I, D, U>(
204        &self,
205        qh: &QueueHandle<D>,
206        version: std::ops::RangeInclusive<u32>,
207        udata: U,
208    ) -> Result<I, BindError>
209    where
210        D: Dispatch<I, U> + 'static,
211        I: Proxy + 'static,
212        U: Send + Sync + 'static,
213    {
214        bind_one(&self.registry, &self.globals, qh, version, udata)
215    }
216
217    /// Binds a global, returning a new object associated with the global.
218    ///
219    /// This binds a specific object by its name as provided by the [RegistryHandler::new_global]
220    /// callback.
221    pub fn bind_specific<I, D, U>(
222        &self,
223        qh: &QueueHandle<D>,
224        name: u32,
225        version: std::ops::RangeInclusive<u32>,
226        udata: U,
227    ) -> Result<I, BindError>
228    where
229        D: Dispatch<I, U> + 'static,
230        I: Proxy + 'static,
231        U: Send + Sync + 'static,
232    {
233        let iface = I::interface();
234        if *version.end() > iface.version {
235            // This is a panic because it's a compile-time programmer error, not a runtime error.
236            panic!("Maximum version ({}) was higher than the proxy's maximum version ({}); outdated wayland XML files?",
237                version.end(), iface.version);
238        }
239        // Optimize for runtime_add_global which will use the last entry
240        for global in self.globals.iter().rev() {
241            if global.name != name || global.interface != iface.name {
242                continue;
243            }
244            if global.version < *version.start() {
245                return Err(BindError::UnsupportedVersion);
246            }
247            let version = global.version.min(*version.end());
248            let proxy = self.registry.bind(global.name, version, qh, udata);
249            log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
250
251            return Ok(proxy);
252        }
253        Err(BindError::NotPresent)
254    }
255
256    /// Binds all globals with a given interface.
257    pub fn bind_all<I, D, U, F>(
258        &self,
259        qh: &QueueHandle<D>,
260        version: std::ops::RangeInclusive<u32>,
261        make_udata: F,
262    ) -> Result<Vec<I>, BindError>
263    where
264        D: Dispatch<I, U> + 'static,
265        I: Proxy + 'static,
266        F: FnMut(u32) -> U,
267        U: Send + Sync + 'static,
268    {
269        bind_all(&self.registry, &self.globals, qh, version, make_udata)
270    }
271}
272
273/// Delegates the handling of [`wl_registry`].
274///
275/// Anything which implements [`RegistryHandler`] may be used in the delegate.
276///
277/// ## Usage
278///
279/// ```
280/// use smithay_client_toolkit::{
281///     delegate_registry, delegate_shm, registry_handlers,
282///     shm::{ShmHandler, Shm},
283/// };
284///
285/// struct ExampleApp {
286///     shm_state: Shm,
287/// }
288///
289/// // Here is the implementation of wl_shm to compile:
290/// delegate_shm!(ExampleApp);
291///
292/// impl ShmHandler for ExampleApp {
293///     fn shm_state(&mut self) -> &mut Shm {
294///         &mut self.shm_state
295///     }
296/// }
297/// ```
298#[macro_export]
299macro_rules! delegate_registry {
300    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
301        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
302            [
303                $crate::reexports::client::protocol::wl_registry::WlRegistry: $crate::reexports::client::globals::GlobalListContents
304            ]  => $crate::registry::RegistryState
305        );
306    };
307}
308
309impl<D> Dispatch<wl_registry::WlRegistry, GlobalListContents, D> for RegistryState
310where
311    D: Dispatch<wl_registry::WlRegistry, GlobalListContents> + ProvidesRegistryState,
312{
313    fn event(
314        state: &mut D,
315        _: &wl_registry::WlRegistry,
316        event: wl_registry::Event,
317        _: &GlobalListContents,
318        conn: &Connection,
319        qh: &QueueHandle<D>,
320    ) {
321        match event {
322            wl_registry::Event::Global { name, interface, version } => {
323                let iface = interface.clone();
324                state.registry().globals.push(Global { name, interface, version });
325                state.runtime_add_global(conn, qh, name, &iface, version);
326            }
327
328            wl_registry::Event::GlobalRemove { name } => {
329                if let Some(i) = state.registry().globals.iter().position(|g| g.name == name) {
330                    let global = state.registry().globals.swap_remove(i);
331                    state.runtime_remove_global(conn, qh, name, &global.interface);
332                }
333            }
334
335            _ => unreachable!("wl_registry is frozen"),
336        }
337    }
338}
339
340/// A helper for storing a bound global.
341///
342/// This helper is intended to simplify the implementation of [RegistryHandler] for state objects
343/// that cache a bound global.
344#[derive(Debug)]
345pub enum GlobalProxy<I> {
346    /// The requested global was not present after a complete enumeration.
347    NotPresent,
348    /// The cached global.
349    Bound(I),
350}
351
352impl<I> From<Result<I, BindError>> for GlobalProxy<I> {
353    fn from(r: Result<I, BindError>) -> Self {
354        match r {
355            Ok(proxy) => GlobalProxy::Bound(proxy),
356            Err(_) => GlobalProxy::NotPresent,
357        }
358    }
359}
360
361impl<I: Proxy> GlobalProxy<I> {
362    pub fn get(&self) -> Result<&I, GlobalError> {
363        self.with_min_version(0)
364    }
365
366    pub fn with_min_version(&self, min_version: u32) -> Result<&I, GlobalError> {
367        match self {
368            GlobalProxy::Bound(proxy) => {
369                if proxy.version() < min_version {
370                    Err(GlobalError::InvalidVersion {
371                        name: I::interface().name,
372                        required: min_version,
373                        available: proxy.version(),
374                    })
375                } else {
376                    Ok(proxy)
377                }
378            }
379            GlobalProxy::NotPresent => Err(GlobalError::MissingGlobal(I::interface().name)),
380        }
381    }
382}
383
384#[derive(Debug)]
385pub struct SimpleGlobal<I, const MAX_VERSION: u32> {
386    proxy: GlobalProxy<I>,
387}
388
389impl<I: Proxy + 'static, const MAX_VERSION: u32> SimpleGlobal<I, MAX_VERSION> {
390    pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Self, BindError>
391    where
392        State: Dispatch<I, (), State> + 'static,
393    {
394        let proxy = globals.bind(qh, 0..=MAX_VERSION, ())?;
395        Ok(Self { proxy: GlobalProxy::Bound(proxy) })
396    }
397
398    pub fn get(&self) -> Result<&I, GlobalError> {
399        self.proxy.get()
400    }
401
402    pub fn with_min_version(&self, min_version: u32) -> Result<&I, GlobalError> {
403        self.proxy.with_min_version(min_version)
404    }
405}
406
407impl<I: Proxy + Clone, const MAX_VERSION: u32> ProvidesBoundGlobal<I, MAX_VERSION>
408    for SimpleGlobal<I, MAX_VERSION>
409{
410    fn bound_global(&self) -> Result<I, GlobalError> {
411        self.proxy.get().cloned()
412    }
413}
414
415impl<D, I, const MAX_VERSION: u32> Dispatch<I, (), D> for SimpleGlobal<I, MAX_VERSION>
416where
417    D: Dispatch<I, ()>,
418    I: Proxy,
419{
420    fn event(_: &mut D, _: &I, _: <I as Proxy>::Event, _: &(), _: &Connection, _: &QueueHandle<D>) {
421        unreachable!("SimpleGlobal is not suitable for {} which has events", I::interface().name);
422    }
423}
424
425/// Binds all globals with a given interface.
426pub(crate) fn bind_all<I, D, U, F>(
427    registry: &wl_registry::WlRegistry,
428    globals: &[Global],
429    qh: &QueueHandle<D>,
430    version: std::ops::RangeInclusive<u32>,
431    mut make_udata: F,
432) -> Result<Vec<I>, BindError>
433where
434    D: Dispatch<I, U> + 'static,
435    I: Proxy + 'static,
436    F: FnMut(u32) -> U,
437    U: Send + Sync + 'static,
438{
439    let iface = I::interface();
440    if *version.end() > iface.version {
441        // This is a panic because it's a compile-time programmer error, not a runtime error.
442        panic!("Maximum version ({}) was higher than the proxy's maximum version ({}); outdated wayland XML files?",
443            version.end(), iface.version);
444    }
445    let mut rv = Vec::new();
446    for global in globals {
447        if global.interface != iface.name {
448            continue;
449        }
450        if global.version < *version.start() {
451            return Err(BindError::UnsupportedVersion);
452        }
453        let version = global.version.min(*version.end());
454        let udata = make_udata(global.name);
455        let proxy = registry.bind(global.name, version, qh, udata);
456        log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
457
458        rv.push(proxy);
459    }
460    Ok(rv)
461}
462
463/// Binds a global, returning a new object associated with the global.
464pub(crate) fn bind_one<I, D, U>(
465    registry: &wl_registry::WlRegistry,
466    globals: &[Global],
467    qh: &QueueHandle<D>,
468    version: std::ops::RangeInclusive<u32>,
469    udata: U,
470) -> Result<I, BindError>
471where
472    D: Dispatch<I, U> + 'static,
473    I: Proxy + 'static,
474    U: Send + Sync + 'static,
475{
476    let iface = I::interface();
477    if *version.end() > iface.version {
478        // This is a panic because it's a compile-time programmer error, not a runtime error.
479        panic!("Maximum version ({}) of {} was higher than the proxy's maximum version ({}); outdated wayland XML files?",
480            version.end(), iface.name, iface.version);
481    }
482    if *version.end() < iface.version {
483        // This is a reminder to evaluate the new API and bump the maximum in order to be able
484        // to use new APIs.  Actual use of new APIs still needs runtime version checks.
485        log::trace!(target: "sctk", "Version {} of {} is available; binding is currently limited to {}", iface.version, iface.name, version.end());
486    }
487    for global in globals {
488        if global.interface != iface.name {
489            continue;
490        }
491        if global.version < *version.start() {
492            return Err(BindError::UnsupportedVersion);
493        }
494        let version = global.version.min(*version.end());
495        let proxy = registry.bind(global.name, version, qh, udata);
496        log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
497
498        return Ok(proxy);
499    }
500    Err(BindError::NotPresent)
501}
502
503#[macro_export]
504macro_rules! delegate_simple {
505    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty:ty, $iface:ty, $max:expr) => {
506        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $iface: () ]
507            => $crate::registry::SimpleGlobal<$iface, $max>
508        );
509    };
510}
511
512/// A helper macro for implementing [`ProvidesRegistryState`].
513///
514/// See [`delegate_registry`] for an example.
515#[macro_export]
516macro_rules! registry_handlers {
517    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $($ty:ty),* $(,)?) => {
518        fn runtime_add_global(
519            &mut self,
520            conn: &$crate::reexports::client::Connection,
521            qh: &$crate::reexports::client::QueueHandle<Self>,
522            name: u32,
523            interface: &str,
524            version: u32,
525        ) {
526            $(
527                <$ty as $crate::registry::RegistryHandler<Self>>::new_global(self, conn, qh, name, interface, version);
528            )*
529        }
530
531        fn runtime_remove_global(
532            &mut self,
533            conn: &$crate::reexports::client::Connection,
534            qh: &$crate::reexports::client::QueueHandle<Self>,
535            name: u32,
536            interface: &str,
537        ) {
538            $(
539                <$ty as $crate::registry::RegistryHandler<Self>>::remove_global(self, conn, qh, name, interface);
540            )*
541        }
542    }
543}