| 1 | //! Helpers for handling the initialization of an app |
| 2 | //! |
| 3 | //! At the startup of your Wayland app, the initial step is generally to retrieve the list of globals |
| 4 | //! advertized by the compositor from the registry. Using the [`Dispatch`] mechanism for this task can be |
| 5 | //! very unpractical, this is why this module provides a special helper for handling the registry. |
| 6 | //! |
| 7 | //! The entry point of this helper is the [`registry_queue_init`] function. Given a reference to your |
| 8 | //! [`Connection`] it will create an [`EventQueue`], retrieve the initial list of globals, and register a |
| 9 | //! handler using your provided `Dispatch<WlRegistry,_>` implementation for handling dynamic registry events. |
| 10 | //! |
| 11 | //! ## Example |
| 12 | //! |
| 13 | //! ```no_run |
| 14 | //! use wayland_client::{ |
| 15 | //! Connection, Dispatch, QueueHandle, |
| 16 | //! globals::{registry_queue_init, Global, GlobalListContents}, |
| 17 | //! protocol::{wl_registry, wl_compositor}, |
| 18 | //! }; |
| 19 | //! # use std::sync::Mutex; |
| 20 | //! # struct State; |
| 21 | //! |
| 22 | //! // You need to provide a Dispatch<WlRegistry, GlobalListContents> impl for your app |
| 23 | //! impl wayland_client::Dispatch<wl_registry::WlRegistry, GlobalListContents> for State { |
| 24 | //! fn event( |
| 25 | //! state: &mut State, |
| 26 | //! proxy: &wl_registry::WlRegistry, |
| 27 | //! event: wl_registry::Event, |
| 28 | //! // This mutex contains an up-to-date list of the currently known globals |
| 29 | //! // including the one that was just added or destroyed |
| 30 | //! data: &GlobalListContents, |
| 31 | //! conn: &Connection, |
| 32 | //! qhandle: &QueueHandle<State>, |
| 33 | //! ) { |
| 34 | //! /* react to dynamic global events here */ |
| 35 | //! } |
| 36 | //! } |
| 37 | //! |
| 38 | //! let conn = Connection::connect_to_env().unwrap(); |
| 39 | //! let (globals, queue) = registry_queue_init::<State>(&conn).unwrap(); |
| 40 | //! |
| 41 | //! # impl wayland_client::Dispatch<wl_compositor::WlCompositor, ()> for State { |
| 42 | //! # fn event( |
| 43 | //! # state: &mut State, |
| 44 | //! # proxy: &wl_compositor::WlCompositor, |
| 45 | //! # event: wl_compositor::Event, |
| 46 | //! # data: &(), |
| 47 | //! # conn: &Connection, |
| 48 | //! # qhandle: &QueueHandle<State>, |
| 49 | //! # ) {} |
| 50 | //! # } |
| 51 | //! // now you can bind the globals you need for your app |
| 52 | //! let compositor: wl_compositor::WlCompositor = globals.bind(&queue.handle(), 4..=5, ()).unwrap(); |
| 53 | //! ``` |
| 54 | |
| 55 | use std::{ |
| 56 | fmt, |
| 57 | ops::RangeInclusive, |
| 58 | os::unix::io::OwnedFd, |
| 59 | sync::{ |
| 60 | atomic::{AtomicBool, Ordering}, |
| 61 | Arc, Mutex, |
| 62 | }, |
| 63 | }; |
| 64 | |
| 65 | use wayland_backend::{ |
| 66 | client::{Backend, InvalidId, ObjectData, ObjectId, WaylandError}, |
| 67 | protocol::Message, |
| 68 | }; |
| 69 | |
| 70 | use crate::{ |
| 71 | protocol::{wl_display, wl_registry}, |
| 72 | Connection, Dispatch, EventQueue, Proxy, QueueHandle, |
| 73 | }; |
| 74 | |
| 75 | /// Initialize a new event queue with its associated registry and retrieve the initial list of globals |
| 76 | /// |
| 77 | /// See [the module level documentation][self] for more. |
| 78 | pub fn registry_queue_init<State>( |
| 79 | conn: &Connection, |
| 80 | ) -> Result<(GlobalList, EventQueue<State>), GlobalError> |
| 81 | where |
| 82 | State: Dispatch<wl_registry::WlRegistry, GlobalListContents> + 'static, |
| 83 | { |
| 84 | let event_queue: EventQueue = conn.new_event_queue(); |
| 85 | let display = conn.display(); |
| 86 | let data: Arc> = Arc::new(data:RegistryState { |
| 87 | globals: GlobalListContents { contents: Default::default() }, |
| 88 | handle: event_queue.handle(), |
| 89 | initial_roundtrip_done: AtomicBool::new(false), |
| 90 | }); |
| 91 | let registry: ! = display.send_constructor(wl_display::Request::GetRegistry {}, data.clone())?; |
| 92 | // We don't need to dispatch the event queue as for now nothing will be sent to it |
| 93 | conn.roundtrip()?; |
| 94 | data.initial_roundtrip_done.store(val:true, order:Ordering::Relaxed); |
| 95 | Ok((GlobalList { registry }, event_queue)) |
| 96 | } |
| 97 | |
| 98 | /// A helper for global initialization. |
| 99 | /// |
| 100 | /// See [the module level documentation][self] for more. |
| 101 | #[derive (Debug)] |
| 102 | pub struct GlobalList { |
| 103 | registry: wl_registry::WlRegistry, |
| 104 | } |
| 105 | |
| 106 | impl GlobalList { |
| 107 | /// Access the contents of the list of globals |
| 108 | pub fn contents(&self) -> &GlobalListContents { |
| 109 | self.registry.data::<GlobalListContents>().unwrap() |
| 110 | } |
| 111 | |
| 112 | /// Binds a global, returning a new protocol object associated with the global. |
| 113 | /// |
| 114 | /// The `version` specifies the range of versions that should be bound. This function will guarantee the |
| 115 | /// version of the returned protocol object is the lower of the maximum requested version and the advertised |
| 116 | /// version. |
| 117 | /// |
| 118 | /// If the lower bound of the `version` is less than the version advertised by the server, then |
| 119 | /// [`BindError::UnsupportedVersion`] is returned. |
| 120 | /// |
| 121 | /// ## Multi-instance/Device globals. |
| 122 | /// |
| 123 | /// This function is not intended to be used with globals that have multiple instances such as `wl_output` |
| 124 | /// and `wl_seat`. These types of globals need their own initialization mechanism because these |
| 125 | /// multi-instance globals may be removed at runtime. To handle then, you should instead rely on the |
| 126 | /// `Dispatch` implementation for `WlRegistry` of your `State`. |
| 127 | /// |
| 128 | /// # Panics |
| 129 | /// |
| 130 | /// This function will panic if the maximum requested version is greater than the known maximum version of |
| 131 | /// the interface. The known maximum version is determined by the code generated using wayland-scanner. |
| 132 | pub fn bind<I, State, U>( |
| 133 | &self, |
| 134 | qh: &QueueHandle<State>, |
| 135 | version: RangeInclusive<u32>, |
| 136 | udata: U, |
| 137 | ) -> Result<I, BindError> |
| 138 | where |
| 139 | I: Proxy + 'static, |
| 140 | State: Dispatch<I, U> + 'static, |
| 141 | U: Send + Sync + 'static, |
| 142 | { |
| 143 | let version_start = *version.start(); |
| 144 | let version_end = *version.end(); |
| 145 | let interface = I::interface(); |
| 146 | |
| 147 | if *version.end() > interface.version { |
| 148 | // This is a panic because it's a compile-time programmer error, not a runtime error. |
| 149 | panic!("Maximum version ( {}) of {} was higher than the proxy's maximum version ( {}); outdated wayland XML files?" , |
| 150 | version.end(), interface.name, interface.version); |
| 151 | } |
| 152 | |
| 153 | let globals = &self.registry.data::<GlobalListContents>().unwrap().contents; |
| 154 | let guard = globals.lock().unwrap(); |
| 155 | let (name, version) = guard |
| 156 | .iter() |
| 157 | // Find the with the correct interface |
| 158 | .filter_map(|Global { name, interface: interface_name, version }| { |
| 159 | // TODO: then_some |
| 160 | if interface.name == &interface_name[..] { |
| 161 | Some((*name, *version)) |
| 162 | } else { |
| 163 | None |
| 164 | } |
| 165 | }) |
| 166 | .next() |
| 167 | .ok_or(BindError::NotPresent)?; |
| 168 | |
| 169 | // Test version requirements |
| 170 | if version < version_start { |
| 171 | return Err(BindError::UnsupportedVersion); |
| 172 | } |
| 173 | |
| 174 | // To get the version to bind, take the lower of the version advertised by the server and the maximum |
| 175 | // requested version. |
| 176 | let version = version.min(version_end); |
| 177 | |
| 178 | Ok(self.registry.bind(name, version, qh, udata)) |
| 179 | } |
| 180 | |
| 181 | /// Returns the [`WlRegistry`][wl_registry] protocol object. |
| 182 | /// |
| 183 | /// This may be used if more direct control when creating globals is needed. |
| 184 | pub fn registry(&self) -> &wl_registry::WlRegistry { |
| 185 | &self.registry |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | /// An error that may occur when initializing the global list. |
| 190 | #[derive (Debug)] |
| 191 | pub enum GlobalError { |
| 192 | /// The backend generated an error |
| 193 | Backend(WaylandError), |
| 194 | |
| 195 | /// An invalid object id was acted upon. |
| 196 | InvalidId(InvalidId), |
| 197 | } |
| 198 | |
| 199 | impl std::error::Error for GlobalError { |
| 200 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| 201 | match self { |
| 202 | GlobalError::Backend(source: &WaylandError) => Some(source), |
| 203 | GlobalError::InvalidId(source: &InvalidId) => std::error::Error::source(self:source), |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | impl std::fmt::Display for GlobalError { |
| 209 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 210 | match self { |
| 211 | GlobalError::Backend(source: &WaylandError) => { |
| 212 | write!(f, "Backend error: {source}" ) |
| 213 | } |
| 214 | GlobalError::InvalidId(source: &InvalidId) => write!(f, " {source}" ), |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | impl From<WaylandError> for GlobalError { |
| 220 | fn from(source: WaylandError) -> Self { |
| 221 | GlobalError::Backend(source) |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | impl From<InvalidId> for GlobalError { |
| 226 | fn from(source: InvalidId) -> Self { |
| 227 | GlobalError::InvalidId(source) |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | /// An error that occurs when a binding a global fails. |
| 232 | #[derive (Debug)] |
| 233 | pub enum BindError { |
| 234 | /// The requested version of the global is not supported. |
| 235 | UnsupportedVersion, |
| 236 | |
| 237 | /// The requested global was not found in the registry. |
| 238 | NotPresent, |
| 239 | } |
| 240 | |
| 241 | impl std::error::Error for BindError {} |
| 242 | |
| 243 | impl fmt::Display for BindError { |
| 244 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
| 245 | match self { |
| 246 | BindError::UnsupportedVersion {} => { |
| 247 | write!(f, "the requested version of the global is not supported" ) |
| 248 | } |
| 249 | BindError::NotPresent {} => { |
| 250 | write!(f, "the requested global was not found in the registry" ) |
| 251 | } |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | /// Description of a global. |
| 257 | #[derive (Debug, Clone, PartialEq, Eq)] |
| 258 | pub struct Global { |
| 259 | /// The name of the global. |
| 260 | /// |
| 261 | /// This is an identifier used by the server to reference some specific global. |
| 262 | pub name: u32, |
| 263 | /// The interface of the global. |
| 264 | /// |
| 265 | /// This describes what type of protocol object the global is. |
| 266 | pub interface: String, |
| 267 | /// The advertised version of the global. |
| 268 | /// |
| 269 | /// This specifies the maximum version of the global that may be bound. This means any lower version of |
| 270 | /// the global may be bound. |
| 271 | pub version: u32, |
| 272 | } |
| 273 | |
| 274 | /// A container representing the current contents of the list of globals |
| 275 | #[derive (Debug)] |
| 276 | pub struct GlobalListContents { |
| 277 | contents: Mutex<Vec<Global>>, |
| 278 | } |
| 279 | |
| 280 | impl GlobalListContents { |
| 281 | /// Access the list of globals |
| 282 | /// |
| 283 | /// Your closure is invoked on the global list, and its return value is forwarded to the return value |
| 284 | /// of this function. This allows you to process the list without making a copy. |
| 285 | pub fn with_list<T, F: FnOnce(&[Global]) -> T>(&self, f: F) -> T { |
| 286 | let guard: MutexGuard<'_, Vec> = self.contents.lock().unwrap(); |
| 287 | f(&guard) |
| 288 | } |
| 289 | |
| 290 | /// Get a copy of the contents of the list of globals. |
| 291 | pub fn clone_list(&self) -> Vec<Global> { |
| 292 | self.contents.lock().unwrap().clone() |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | struct RegistryState<State> { |
| 297 | globals: GlobalListContents, |
| 298 | handle: QueueHandle<State>, |
| 299 | initial_roundtrip_done: AtomicBool, |
| 300 | } |
| 301 | |
| 302 | impl<State: 'static> ObjectData for RegistryState<State> |
| 303 | where |
| 304 | State: Dispatch<wl_registry::WlRegistry, GlobalListContents>, |
| 305 | { |
| 306 | fn event( |
| 307 | self: Arc<Self>, |
| 308 | backend: &Backend, |
| 309 | msg: Message<ObjectId, OwnedFd>, |
| 310 | ) -> Option<Arc<dyn ObjectData>> { |
| 311 | let conn = Connection::from_backend(backend.clone()); |
| 312 | |
| 313 | // The registry messages don't contain any fd, so use some type trickery to |
| 314 | // clone the message |
| 315 | #[derive (Debug, Clone)] |
| 316 | enum Void {} |
| 317 | let msg: Message<ObjectId, Void> = msg.map_fd(|_| unreachable!()); |
| 318 | let to_forward = if self.initial_roundtrip_done.load(Ordering::Relaxed) { |
| 319 | Some(msg.clone().map_fd(|v| match v {})) |
| 320 | } else { |
| 321 | None |
| 322 | }; |
| 323 | // and restore the type |
| 324 | let msg = msg.map_fd(|v| match v {}); |
| 325 | |
| 326 | // Can't do much if the server sends a malformed message |
| 327 | if let Ok((_, event)) = wl_registry::WlRegistry::parse_event(&conn, msg) { |
| 328 | match event { |
| 329 | wl_registry::Event::Global { name, interface, version } => { |
| 330 | let mut guard = self.globals.contents.lock().unwrap(); |
| 331 | guard.push(Global { name, interface, version }); |
| 332 | } |
| 333 | |
| 334 | wl_registry::Event::GlobalRemove { name: remove } => { |
| 335 | let mut guard = self.globals.contents.lock().unwrap(); |
| 336 | guard.retain(|Global { name, .. }| name != &remove); |
| 337 | } |
| 338 | } |
| 339 | }; |
| 340 | |
| 341 | if let Some(msg) = to_forward { |
| 342 | // forward the message to the event queue as normal |
| 343 | self.handle |
| 344 | .inner |
| 345 | .lock() |
| 346 | .unwrap() |
| 347 | .enqueue_event::<wl_registry::WlRegistry, GlobalListContents>(msg, self.clone()) |
| 348 | } |
| 349 | |
| 350 | // We do not create any objects in this event handler. |
| 351 | None |
| 352 | } |
| 353 | |
| 354 | fn destroyed(&self, _id: ObjectId) { |
| 355 | // A registry cannot be destroyed unless disconnected. |
| 356 | } |
| 357 | |
| 358 | fn data_as_any(&self) -> &dyn std::any::Any { |
| 359 | &self.globals |
| 360 | } |
| 361 | } |
| 362 | |