1use 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 fn bind<P: Proxy, D>(
55 &self,
56 conn: &mut Connection<D>,
57 version: impl VersionBounds,
58 ) -> Result<P, BindError>;
59
60 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 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 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];