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]
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#[derive(Debug)]
345pub enum GlobalProxy<I> {
346 NotPresent,
348 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 pub fn from_bound(proxy: I) -> Self {
410 Self { proxy: GlobalProxy::Bound(proxy) }
411 }
412}
413
414impl<I: Proxy + Clone, const MAX_VERSION: u32> ProvidesBoundGlobal<I, MAX_VERSION>
415 for SimpleGlobal<I, MAX_VERSION>
416{
417 fn bound_global(&self) -> Result<I, GlobalError> {
418 self.proxy.get().cloned()
419 }
420}
421
422impl<D, I, const MAX_VERSION: u32> Dispatch<I, (), D> for SimpleGlobal<I, MAX_VERSION>
423where
424 D: Dispatch<I, ()>,
425 I: Proxy,
426{
427 fn event(_: &mut D, _: &I, _: <I as Proxy>::Event, _: &(), _: &Connection, _: &QueueHandle<D>) {
428 unreachable!("SimpleGlobal is not suitable for {} which has events", I::interface().name);
429 }
430}
431
432pub(crate) fn bind_all<I, D, U, F>(
434 registry: &wl_registry::WlRegistry,
435 globals: &[Global],
436 qh: &QueueHandle<D>,
437 version: std::ops::RangeInclusive<u32>,
438 mut make_udata: F,
439) -> Result<Vec<I>, BindError>
440where
441 D: Dispatch<I, U> + 'static,
442 I: Proxy + 'static,
443 F: FnMut(u32) -> U,
444 U: Send + Sync + 'static,
445{
446 let iface = I::interface();
447 if *version.end() > iface.version {
448 panic!("Maximum version ({}) was higher than the proxy's maximum version ({}); outdated wayland XML files?",
450 version.end(), iface.version);
451 }
452 let mut rv = Vec::new();
453 for global in globals {
454 if global.interface != iface.name {
455 continue;
456 }
457 if global.version < *version.start() {
458 return Err(BindError::UnsupportedVersion);
459 }
460 let version = global.version.min(*version.end());
461 let udata = make_udata(global.name);
462 let proxy = registry.bind(global.name, version, qh, udata);
463 log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
464
465 rv.push(proxy);
466 }
467 Ok(rv)
468}
469
470pub(crate) fn bind_one<I, D, U>(
472 registry: &wl_registry::WlRegistry,
473 globals: &[Global],
474 qh: &QueueHandle<D>,
475 version: std::ops::RangeInclusive<u32>,
476 udata: U,
477) -> Result<I, BindError>
478where
479 D: Dispatch<I, U> + 'static,
480 I: Proxy + 'static,
481 U: Send + Sync + 'static,
482{
483 let iface = I::interface();
484 if *version.end() > iface.version {
485 panic!("Maximum version ({}) of {} was higher than the proxy's maximum version ({}); outdated wayland XML files?",
487 version.end(), iface.name, iface.version);
488 }
489 if *version.end() < iface.version {
490 log::trace!(target: "sctk", "Version {} of {} is available; binding is currently limited to {}", iface.version, iface.name, version.end());
493 }
494 for global in globals {
495 if global.interface != iface.name {
496 continue;
497 }
498 if global.version < *version.start() {
499 return Err(BindError::UnsupportedVersion);
500 }
501 let version = global.version.min(*version.end());
502 let proxy = registry.bind(global.name, version, qh, udata);
503 log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
504
505 return Ok(proxy);
506 }
507 Err(BindError::NotPresent)
508}
509
510#[macro_export]
511macro_rules! delegate_simple {
512 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty:ty, $iface:ty, $max:expr) => {
513 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $iface: () ]
514 => $crate::registry::SimpleGlobal<$iface, $max>
515 );
516 };
517}
518
519#[macro_export]
523macro_rules! registry_handlers {
524 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $($ty:ty),* $(,)?) => {
525 fn runtime_add_global(
526 &mut self,
527 conn: &$crate::reexports::client::Connection,
528 qh: &$crate::reexports::client::QueueHandle<Self>,
529 name: u32,
530 interface: &str,
531 version: u32,
532 ) {
533 $(
534 <$ty as $crate::registry::RegistryHandler<Self>>::new_global(self, conn, qh, name, interface, version);
535 )*
536 }
537
538 fn runtime_remove_global(
539 &mut self,
540 conn: &$crate::reexports::client::Connection,
541 qh: &$crate::reexports::client::QueueHandle<Self>,
542 name: u32,
543 interface: &str,
544 ) {
545 $(
546 <$ty as $crate::registry::RegistryHandler<Self>>::remove_global(self, conn, qh, name, interface);
547 )*
548 }
549 }
550}