wayrs_client/
global.rs

1//! Utils for working with global objects
2
3use std::ffi::{CStr, CString};
4use std::fmt;
5use std::ops;
6
7use crate::object::Proxy;
8use crate::protocol::wl_registry::GlobalArgs;
9use crate::{Connection, EventCtx};
10
11pub type Global = GlobalArgs;
12pub type Globals = [Global];
13
14#[derive(Debug)]
15pub enum BindError {
16    IncorrectInterface {
17        actual: CString,
18        requested: &'static CStr,
19    },
20    UnsupportedVersion {
21        actual: u32,
22        min: u32,
23    },
24    GlobalNotFound(&'static CStr),
25}
26
27impl std::error::Error for BindError {}
28
29impl fmt::Display for BindError {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::IncorrectInterface { actual, requested } => write!(
33                f,
34                "global has interface {actual:?} but {requested:?} was requested"
35            ),
36            Self::UnsupportedVersion { actual, min } => write!(
37                f,
38                "global has version {actual} but a minimum version of {min} was requested"
39            ),
40            Self::GlobalNotFound(name) => write!(f, "global with interface {name:?} not found"),
41        }
42    }
43}
44
45pub trait GlobalExt {
46    fn is<P: Proxy>(&self) -> bool;
47
48    /// Bind a global.
49    ///
50    /// The version argmuent can be a:
51    /// - Number - require a specific version
52    /// - Range to inclusive (`..=b` - bind a version in range `[1, b]`)
53    /// - Range inclusive (`a..=b` - bind a version in range `[a, b]`)
54    fn bind<P: Proxy, D>(
55        &self,
56        conn: &mut Connection<D>,
57        version: impl VersionBounds,
58    ) -> Result<P, BindError>;
59
60    /// Same as [`bind`](Self::bind) but also sets the callback
61    fn bind_with_cb<P: Proxy, D, F: FnMut(EventCtx<D, P>) + Send + 'static>(
62        &self,
63        conn: &mut Connection<D>,
64        version: impl VersionBounds,
65        cb: F,
66    ) -> Result<P, BindError>;
67}
68
69pub trait GlobalsExt {
70    #[deprecated = "use Connection::bind_singleton() instead"]
71    fn bind<P: Proxy, D>(
72        &self,
73        conn: &mut Connection<D>,
74        version: impl VersionBounds,
75    ) -> Result<P, BindError>;
76
77    #[deprecated = "use Connection::bind_singleton_with_cb() instead"]
78    fn bind_with_cb<P: Proxy, D, F: FnMut(EventCtx<D, P>) + Send + 'static>(
79        &self,
80        conn: &mut Connection<D>,
81        version: impl VersionBounds,
82        cb: F,
83    ) -> Result<P, BindError>;
84}
85
86impl GlobalExt for Global {
87    fn is<P: Proxy>(&self) -> bool {
88        P::INTERFACE.name == self.interface.as_c_str()
89    }
90
91    /// Bind the first instance of a global. Works great for singletons.
92    ///
93    /// The version argmuent can be a:
94    /// - Number - require a specific version
95    /// - Range to inclusive (`..=b` - bind a version in range `[1, b]`)
96    /// - Range inclusive (`a..=b` - bind a version in range `[a, b]`)
97    fn bind<P: Proxy, D>(
98        &self,
99        conn: &mut Connection<D>,
100        version: impl VersionBounds,
101    ) -> Result<P, BindError> {
102        if !self.is::<P>() {
103            return Err(BindError::IncorrectInterface {
104                actual: self.interface.to_owned(),
105                requested: P::INTERFACE.name,
106            });
107        }
108
109        assert!(version.upper() <= P::INTERFACE.version);
110
111        if self.version < version.lower() {
112            return Err(BindError::UnsupportedVersion {
113                actual: self.version,
114                min: version.lower(),
115            });
116        }
117
118        let reg = conn.registry();
119        let version = u32::min(version.upper(), self.version);
120
121        Ok(reg.bind(conn, self.name, version))
122    }
123
124    /// Same as [`bind`](Self::bind) but also sets the callback
125    fn bind_with_cb<P: Proxy, D, F: FnMut(EventCtx<D, P>) + Send + 'static>(
126        &self,
127        conn: &mut Connection<D>,
128        version: impl VersionBounds,
129        cb: F,
130    ) -> Result<P, BindError> {
131        if !self.is::<P>() {
132            return Err(BindError::IncorrectInterface {
133                actual: self.interface.to_owned(),
134                requested: P::INTERFACE.name,
135            });
136        }
137
138        assert!(version.upper() <= P::INTERFACE.version);
139
140        if self.version < version.lower() {
141            return Err(BindError::UnsupportedVersion {
142                actual: self.version,
143                min: version.lower(),
144            });
145        }
146
147        let reg = conn.registry();
148        let version = u32::min(version.upper(), self.version);
149
150        Ok(reg.bind_with_cb(conn, self.name, version, cb))
151    }
152}
153
154impl GlobalsExt for Globals {
155    fn bind<P: Proxy, D>(
156        &self,
157        conn: &mut Connection<D>,
158        version: impl VersionBounds,
159    ) -> Result<P, BindError> {
160        let global = self
161            .iter()
162            .find(|g| g.is::<P>())
163            .ok_or(BindError::GlobalNotFound(P::INTERFACE.name))?;
164        global.bind(conn, version)
165    }
166
167    fn bind_with_cb<P: Proxy, D, F: FnMut(EventCtx<D, P>) + Send + 'static>(
168        &self,
169        conn: &mut Connection<D>,
170        version: impl VersionBounds,
171        cb: F,
172    ) -> Result<P, BindError> {
173        let global = self
174            .iter()
175            .find(|g| g.is::<P>())
176            .ok_or(BindError::GlobalNotFound(P::INTERFACE.name))?;
177        global.bind_with_cb(conn, version, cb)
178    }
179}
180
181pub trait VersionBounds: private::Sealed {
182    fn lower(&self) -> u32;
183    fn upper(&self) -> u32;
184}
185
186mod private {
187    pub trait Sealed {}
188}
189
190macro_rules! impl_version_bounds {
191    ($($ty:ty => ($self:ident) => $lower:expr, $upper:expr;)*) => {
192        $(
193            impl private::Sealed for $ty {}
194            impl VersionBounds for $ty {
195                fn lower(&$self) -> u32 {
196                    $lower
197                }
198                fn upper(&$self) -> u32 {
199                    $upper
200                }
201            }
202        )*
203    };
204}
205
206impl_version_bounds! [
207    u32 => (self) => *self, *self;
208    ops::RangeToInclusive<u32> => (self) => 1, self.end;
209    ops::RangeInclusive<u32> => (self) => *self.start(), *self.end();
210];