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 = &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
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
425pub(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 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
463pub(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 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 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#[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}