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: I) => 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: &I) => {
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: I = globals.bind(qh, version: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: &Interface = 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 = Vec::new();
446 for global: &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: u32 = global.version.min(*version.end());
454 let udata: U = make_udata(global.name);
455 let proxy: I = 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}
544