1use 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
79pub trait RegistryHandler<D>
90where
91 D: ProvidesRegistryState,
92{
93 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 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
125pub trait ProvidesRegistryState: Sized {
130 fn registry(&mut self) -> &mut RegistryState;
132
133 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 fn runtime_remove_global(
147 &mut self,
148 conn: &Connection,
149 qh: &QueueHandle<Self>,
150 name: u32,
151 interface: &str,
152 );
153}
154
155#[derive(Debug)]
159pub struct RegistryState {
160 registry: wl_registry::WlRegistry,
161 globals: Vec<Global>,
162}
163
164impl RegistryState {
165 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 pub fn globals(&self) -> impl Iterator<Item = &Global> + '_ {
186 self.globals.iter()
187 }
188
189 pub fn globals_by_interface<'a>(
193 &'a self,
194 interface: &'a str,
195 ) -> impl Iterator<Item = &'a Global> + 'a {
196 self.globals.iter().filter(move |g| g.interface == interface)
197 }
198
199 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 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 panic!("Maximum version ({}) was higher than the proxy's maximum version ({}); outdated wayland XML files?",
237 version.end(), iface.version);
238 }
239 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 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#[macro_export]
298macro_rules! delegate_registry {
299 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
300 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
301 [
302 $crate::reexports::client::protocol::wl_registry::WlRegistry: $crate::reexports::client::globals::GlobalListContents
303 ] => $crate::registry::RegistryState
304 );
305 };
306}
307
308impl<D> Dispatch<wl_registry::WlRegistry, GlobalListContents, D> for RegistryState
309where
310 D: Dispatch<wl_registry::WlRegistry, GlobalListContents> + ProvidesRegistryState,
311{
312 fn event(
313 state: &mut D,
314 _: &wl_registry::WlRegistry,
315 event: wl_registry::Event,
316 _: &GlobalListContents,
317 conn: &Connection,
318 qh: &QueueHandle<D>,
319 ) {
320 match event {
321 wl_registry::Event::Global { name, interface, version } => {
322 let iface = interface.clone();
323 state.registry().globals.push(Global { name, interface, version });
324 state.runtime_add_global(conn, qh, name, &iface, version);
325 }
326
327 wl_registry::Event::GlobalRemove { name } => {
328 if let Some(i) = state.registry().globals.iter().position(|g| g.name == name) {
329 let global = state.registry().globals.swap_remove(i);
330 state.runtime_remove_global(conn, qh, name, &global.interface);
331 }
332 }
333
334 _ => unreachable!("wl_registry is frozen"),
335 }
336 }
337}
338
339#[derive(Debug)]
344pub enum GlobalProxy<I> {
345 NotPresent,
347 Bound(I),
349}
350
351impl<I> From<Result<I, BindError>> for GlobalProxy<I> {
352 fn from(r: Result<I, BindError>) -> Self {
353 match r {
354 Ok(proxy) => GlobalProxy::Bound(proxy),
355 Err(_) => GlobalProxy::NotPresent,
356 }
357 }
358}
359
360impl<I: Proxy> GlobalProxy<I> {
361 pub fn get(&self) -> Result<&I, GlobalError> {
362 self.with_min_version(0)
363 }
364
365 pub fn with_min_version(&self, min_version: u32) -> Result<&I, GlobalError> {
366 match self {
367 GlobalProxy::Bound(proxy) => {
368 if proxy.version() < min_version {
369 Err(GlobalError::InvalidVersion {
370 name: I::interface().name,
371 required: min_version,
372 available: proxy.version(),
373 })
374 } else {
375 Ok(proxy)
376 }
377 }
378 GlobalProxy::NotPresent => Err(GlobalError::MissingGlobal(I::interface().name)),
379 }
380 }
381}
382
383#[derive(Debug)]
384pub struct SimpleGlobal<I, const MAX_VERSION: u32> {
385 proxy: GlobalProxy<I>,
386}
387
388impl<I: Proxy + 'static, const MAX_VERSION: u32> SimpleGlobal<I, MAX_VERSION> {
389 pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Self, BindError>
390 where
391 State: Dispatch<I, (), State> + 'static,
392 {
393 let proxy = globals.bind(qh, 0..=MAX_VERSION, ())?;
394 Ok(Self { proxy: GlobalProxy::Bound(proxy) })
395 }
396
397 pub fn get(&self) -> Result<&I, GlobalError> {
398 self.proxy.get()
399 }
400
401 pub fn with_min_version(&self, min_version: u32) -> Result<&I, GlobalError> {
402 self.proxy.with_min_version(min_version)
403 }
404
405 pub fn from_bound(proxy: I) -> Self {
409 Self { proxy: GlobalProxy::Bound(proxy) }
410 }
411}
412
413impl<I: Proxy + Clone, const MAX_VERSION: u32> ProvidesBoundGlobal<I, MAX_VERSION>
414 for SimpleGlobal<I, MAX_VERSION>
415{
416 fn bound_global(&self) -> Result<I, GlobalError> {
417 self.proxy.get().cloned()
418 }
419}
420
421pub(crate) fn bind_all<I, D, U, F>(
423 registry: &wl_registry::WlRegistry,
424 globals: &[Global],
425 qh: &QueueHandle<D>,
426 version: std::ops::RangeInclusive<u32>,
427 mut make_udata: F,
428) -> Result<Vec<I>, BindError>
429where
430 D: Dispatch<I, U> + 'static,
431 I: Proxy + 'static,
432 F: FnMut(u32) -> U,
433 U: Send + Sync + 'static,
434{
435 let iface = I::interface();
436 if *version.end() > iface.version {
437 panic!("Maximum version ({}) was higher than the proxy's maximum version ({}); outdated wayland XML files?",
439 version.end(), iface.version);
440 }
441 let mut rv = Vec::new();
442 for global in globals {
443 if global.interface != iface.name {
444 continue;
445 }
446 if global.version < *version.start() {
447 return Err(BindError::UnsupportedVersion);
448 }
449 let version = global.version.min(*version.end());
450 let udata = make_udata(global.name);
451 let proxy = registry.bind(global.name, version, qh, udata);
452 log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
453
454 rv.push(proxy);
455 }
456 Ok(rv)
457}
458
459pub(crate) fn bind_one<I, D, U>(
461 registry: &wl_registry::WlRegistry,
462 globals: &[Global],
463 qh: &QueueHandle<D>,
464 version: std::ops::RangeInclusive<u32>,
465 udata: U,
466) -> Result<I, BindError>
467where
468 D: Dispatch<I, U> + 'static,
469 I: Proxy + 'static,
470 U: Send + Sync + 'static,
471{
472 let iface = I::interface();
473 if *version.end() > iface.version {
474 panic!("Maximum version ({}) of {} was higher than the proxy's maximum version ({}); outdated wayland XML files?",
476 version.end(), iface.name, iface.version);
477 }
478 if *version.end() < iface.version {
479 log::trace!(target: "sctk", "Version {} of {} is available; binding is currently limited to {}", iface.version, iface.name, version.end());
482 }
483 for global in globals {
484 if global.interface != iface.name {
485 continue;
486 }
487 if global.version < *version.start() {
488 return Err(BindError::UnsupportedVersion);
489 }
490 let version = global.version.min(*version.end());
491 let proxy = registry.bind(global.name, version, qh, udata);
492 log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
493
494 return Ok(proxy);
495 }
496 Err(BindError::NotPresent)
497}
498
499#[macro_export]
503macro_rules! registry_handlers {
504 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $($ty:ty),* $(,)?) => {
505 fn runtime_add_global(
506 &mut self,
507 conn: &$crate::reexports::client::Connection,
508 qh: &$crate::reexports::client::QueueHandle<Self>,
509 name: u32,
510 interface: &str,
511 version: u32,
512 ) {
513 $(
514 <$ty as $crate::registry::RegistryHandler<Self>>::new_global(self, conn, qh, name, interface, version);
515 )*
516 }
517
518 fn runtime_remove_global(
519 &mut self,
520 conn: &$crate::reexports::client::Connection,
521 qh: &$crate::reexports::client::QueueHandle<Self>,
522 name: u32,
523 interface: &str,
524 ) {
525 $(
526 <$ty as $crate::registry::RegistryHandler<Self>>::remove_global(self, conn, qh, name, interface);
527 )*
528 }
529 }
530}