wl_client/queue/with_data.rs
1#[expect(unused_imports)]
2use crate::queue::QueueData;
3use {
4 crate::{Connection, Queue, QueueOwner, utils::block_on::block_on},
5 std::{
6 any::{TypeId, type_name},
7 ffi::CStr,
8 fmt::{Debug, Formatter},
9 io,
10 marker::PhantomData,
11 ops::Deref,
12 ptr,
13 },
14};
15
16#[cfg(test)]
17mod tests;
18
19/// An adapter for [`Queue`]s with mutable data.
20///
21/// This type is returned by [`Connection::create_queue_with_data`] and
22/// [`Connection::create_local_queue_with_data`] and can also be created from any queue
23/// by calling [`Queue::with_data`].
24///
25/// This type must be used to dispatch queues that were created with one of the two
26/// functions above. It derefs to [`Queue`] but re-declares all of the dispatching
27/// functions to also accept a `&mut T` that will be passed to the event handlers.
28///
29/// # Example
30///
31/// ```
32/// # use wl_client::{proxy, Libwayland};
33/// # use wl_client::test_protocols_data::core::wl_callback::WlCallback;
34/// # use wl_client::test_protocols_data::core::wl_display::WlDisplay;
35/// #
36/// let lib = Libwayland::open().unwrap();
37/// let con = lib.connect_to_default_display().unwrap();
38/// let (_queue, queue) = con.create_queue_with_data::<State>(c"queue name");
39///
40/// struct State {
41/// done: bool,
42/// }
43/// let mut state = State {
44/// done: false,
45/// };
46///
47/// let sync = queue.display::<WlDisplay>().sync();
48/// proxy::set_event_handler(&sync, WlCallback::on_done(|state: &mut State, _, _| {
49/// state.done = true;
50/// }));
51/// queue.dispatch_roundtrip_blocking(&mut state).unwrap();
52/// assert!(state.done);
53/// ```
54pub struct QueueWithData<T>
55where
56 T: 'static,
57{
58 /// The underlying queue. We ensure the following invariant: Either
59 /// [QueueData::mut_data_type] is `None` or it is the type ID of `T`.
60 queue: Queue,
61 _phantom: PhantomData<fn(&mut T)>,
62}
63
64impl Connection {
65 /// Creates a new queue with mutable data.
66 ///
67 /// This function is the same as [`Connection::create_queue`] except that event
68 /// handlers attached to this queue can receive a `&mut T`. When dispatching the queue,
69 /// a `&mut T` must be passed into one of the dispatcher functions of
70 /// [`QueueWithData`]. The dispatcher functions declared on [`Queue`] cannot be used
71 /// to dispatch this queue.
72 ///
73 /// # Example
74 ///
75 /// ```
76 /// # use wl_client::Libwayland;
77 /// #
78 /// let lib = Libwayland::open().unwrap();
79 /// let con = lib.connect_to_default_display().unwrap();
80 /// let (_queue_owner, _queue) = con.create_queue_with_data::<State>(c"queue name");
81 ///
82 /// struct State {
83 /// // ...
84 /// }
85 /// ```
86 pub fn create_queue_with_data<T>(&self, name: &CStr) -> (QueueOwner, QueueWithData<T>)
87 where
88 T: 'static,
89 {
90 let owner =
91 self.create_queue2(name, false, Some(TypeId::of::<T>()), Some(type_name::<T>()));
92 let queue = owner.with_data();
93 (owner, queue)
94 }
95
96 /// Creates a new queue with mutable data.
97 ///
98 /// This function is the same as [`Connection::create_local_queue`] except that event
99 /// handlers attached to this queue can receive a `&mut T`. When dispatching the queue,
100 /// a `&mut T` must be passed into one of the dispatcher functions of
101 /// [`QueueWithData`]. The dispatcher functions declared on [`Queue`] cannot be used
102 /// to dispatch this queue.
103 ///
104 /// # Example
105 ///
106 /// ```
107 /// # use wl_client::Libwayland;
108 /// #
109 /// let lib = Libwayland::open().unwrap();
110 /// let con = lib.connect_to_default_display().unwrap();
111 /// let (_queue_owner, _queue) = con.create_local_queue_with_data::<State>(c"queue name");
112 ///
113 /// struct State {
114 /// // ...
115 /// }
116 /// ```
117 pub fn create_local_queue_with_data<T>(&self, name: &CStr) -> (QueueOwner, QueueWithData<T>)
118 where
119 T: 'static,
120 {
121 let owner = self.create_queue2(name, true, Some(TypeId::of::<T>()), Some(type_name::<T>()));
122 let queue = owner.with_data();
123 (owner, queue)
124 }
125}
126
127impl Queue {
128 /// Creates an adapter for queues with mutable data.
129 ///
130 /// If the queue was created with [`Connection::create_queue_with_data`] or
131 /// [`Connection::create_local_queue_with_data`], then this function can only be used
132 /// with the same `T` that was used in those function calls.
133 ///
134 /// For convenience, if this queue was created without data, this function can be
135 /// used with any `T`.
136 ///
137 /// # Panic
138 ///
139 /// This function panics if this queue
140 /// - was created with [`Connection::create_queue_with_data`] or
141 /// [`Connection::create_local_queue_with_data`], and
142 /// - it was created with a different data type.
143 pub fn with_data<T>(&self) -> QueueWithData<T>
144 where
145 T: 'static,
146 {
147 let d = &*self.queue_data;
148 if d.mut_data_type.is_some() && d.mut_data_type != Some(TypeId::of::<T>()) {
149 let rn = type_name::<T>();
150 panic!(
151 "This queue only supports mutable data of type `{}` but the \
152 requested type is `{rn}`",
153 d.mut_data_type_name.unwrap(),
154 );
155 }
156 QueueWithData {
157 queue: self.clone(),
158 _phantom: Default::default(),
159 }
160 }
161
162 /// Returns the type of mutable data required when dispatching this queue.
163 pub(crate) fn mut_data_type(&self) -> (Option<TypeId>, Option<&'static str>) {
164 let d = &*self.queue_data;
165 (d.mut_data_type, d.mut_data_type_name)
166 }
167
168 /// Returns the non-null mutable data pointer.
169 ///
170 /// When libwayland dispatches the event handler of a proxy attached to this queue,
171 /// the returned pointer can be dereferenced to `&mut T` where `T` is the type
172 /// returned by [`proxy::low_level::EventHandler::mutable_type`] or `()` if
173 /// `mutable_type` returns `None`.
174 ///
175 /// (It is up to the caller to ensure that the pointer is only dereferenced according
176 /// to the requirements of stacked borrows.)
177 ///
178 /// # Safety
179 ///
180 /// - The queue mutex must be held.
181 pub(crate) unsafe fn data(&self) -> *mut u8 {
182 // SAFETY: - the requirement is forwarded to the caller
183 // - event handlers are only ever dispatched from within
184 // `dispatch_pending_internal` and that function is the only code that
185 // modifies this field
186 // - if EventHandler::mutable_type is Some and not TypeId::of::<()>, then
187 // `set_event_handler3` has checked that it is the same as the
188 // `mut_data_type` of this queue.
189 // - therefore the safety requirements of `dispatch_pending_internal`
190 // require that the pointer stored in `mut_data` is `&mut T` where `T`
191 // is the type returned by EventHandler::mutable_type.
192 // - if this is being called from an event handler and there is another
193 // event handler invocation of this queue further up in the stack,
194 // then there must another invocation of `dispatch_pending_internal`
195 // between these two stack entries and `dispatch_pending_internal` has
196 // set the pointer to a fresh pointer that satisfies stacked borrows.
197 unsafe { self.queue_data.mut_data.get().0 }
198 }
199}
200
201impl<T> QueueWithData<T>
202where
203 T: 'static,
204{
205 /// Blocks the current thread until at least one event has been dispatched.
206 ///
207 /// This function is the same as [`Queue::dispatch_blocking`] but accepts a `&mut T`
208 /// that will be passed to event handlers.
209 ///
210 /// # Panic
211 ///
212 /// - Panics if this is a [local queue](Connection::create_local_queue) and the current
213 /// thread is not the thread that this queue was created in.
214 ///
215 /// # Example
216 ///
217 /// ```
218 /// # use wl_client::Libwayland;
219 /// # use wl_client::test_protocols_data::core::wl_display::WlDisplay;
220 /// #
221 /// let lib = Libwayland::open().unwrap();
222 /// let con = lib.connect_to_default_display().unwrap();
223 /// let (_queue, queue) = con.create_queue_with_data::<State>(c"queue name");
224 ///
225 /// struct State {
226 /// // ...
227 /// }
228 /// let mut state = State {
229 /// // ...
230 /// };
231 ///
232 /// // For this example, ensure that the compositor sends an event in the near future.
233 /// let _sync = queue.display::<WlDisplay>().sync();
234 ///
235 /// queue.dispatch_blocking(&mut state).unwrap();
236 /// ```
237 pub fn dispatch_blocking(&self, data: &mut T) -> io::Result<u64> {
238 block_on(self.dispatch_async(data))
239 }
240
241 /// Completes when at least one event has been dispatched.
242 ///
243 /// This function is the same as [`QueueWithData::dispatch_blocking`] except that it is
244 /// async and does not block the current thread.
245 ///
246 /// # Panic
247 ///
248 /// - Panics if this is a [local queue](Connection::create_local_queue) and the thread
249 /// polling the future is not the thread that this queue was created in.
250 ///
251 /// # Example
252 ///
253 /// ```
254 /// # use wl_client::Libwayland;
255 /// # use wl_client::test_protocols_data::core::wl_display::WlDisplay;
256 /// #
257 /// # tokio_test::block_on(async {
258 /// let lib = Libwayland::open().unwrap();
259 /// let con = lib.connect_to_default_display().unwrap();
260 /// let (_queue, queue) = con.create_queue_with_data(c"queue name");
261 ///
262 /// struct State {
263 /// // ...
264 /// }
265 /// let mut state = State {
266 /// // ...
267 /// };
268 ///
269 /// // For this example, ensure that the compositor sends an event in the near future.
270 /// let _sync = queue.display::<WlDisplay>().sync();
271 ///
272 /// queue.dispatch_async(&mut state).await.unwrap();
273 /// # });
274 /// ```
275 pub async fn dispatch_async(&self, data: &mut T) -> io::Result<u64> {
276 self.connection.wait_for_events(&[self]).await?;
277 self.dispatch_pending(data)
278 }
279
280 /// Blocks the current thread until the compositor has processed all previous requests
281 /// and all of its response events have been dispatched.
282 ///
283 /// This function is the same as [`Queue::dispatch_roundtrip_blocking`] but accepts a
284 /// `&mut T` that will be passed to event handlers.
285 ///
286 /// # Panic
287 ///
288 /// - Panics if this is a [local queue](Connection::create_local_queue) and the current
289 /// thread is not the thread that this queue was created in.
290 ///
291 /// # Example
292 ///
293 /// ```
294 /// # use std::sync::Arc;
295 /// # use std::sync::atomic::AtomicBool;
296 /// # use std::sync::atomic::Ordering::Relaxed;
297 /// # use wl_client::{proxy, Libwayland};
298 /// # use wl_client::test_protocols_data::core::wl_callback::{WlCallback, WlCallbackEventHandler, WlCallbackRef};
299 /// # use wl_client::test_protocols_data::core::wl_display::WlDisplay;
300 /// #
301 /// let lib = Libwayland::open().unwrap();
302 /// let con = lib.connect_to_default_display().unwrap();
303 /// let (_queue, queue) = con.create_queue_with_data::<State>(c"");
304 /// let display: WlDisplay = queue.display();
305 ///
306 /// struct State {
307 /// done: bool,
308 /// }
309 /// let mut state = State {
310 /// done: false,
311 /// };
312 ///
313 /// // send some messages to the compositor
314 /// let sync = display.sync();
315 /// proxy::set_event_handler(&sync, WlCallback::on_done(move |state: &mut State, _, _| {
316 /// state.done = true;
317 /// }));
318 ///
319 /// // perform a roundtrip
320 /// queue.dispatch_roundtrip_blocking(&mut state).unwrap();
321 ///
322 /// // assert that we've received the response
323 /// assert!(state.done);
324 /// ```
325 pub fn dispatch_roundtrip_blocking(&self, data: &mut T) -> io::Result<()> {
326 block_on(self.dispatch_roundtrip_async(data))
327 }
328
329 /// Completes when the compositor has processed all previous requests and all of its
330 /// response events have been dispatched.
331 ///
332 /// This function is the same as [`QueueWithData::dispatch_roundtrip_blocking`] except
333 /// that it is async and does not block the current thread.
334 ///
335 /// If the future completes with `Ok(())`, then the future completes after (in the
336 /// sense of the C++ memory model) the event handlers of all previous events have been
337 /// invoked.
338 ///
339 /// # Panic
340 ///
341 /// - Panics if this is a [local queue](Connection::create_local_queue) and the current
342 /// thread is not the thread that this queue was created in.
343 ///
344 /// # Example
345 ///
346 /// ```
347 /// # use std::sync::Arc;
348 /// # use std::sync::atomic::AtomicBool;
349 /// # use std::sync::atomic::Ordering::Relaxed;
350 /// # use wl_client::{proxy, Libwayland};
351 /// # use wl_client::test_protocols_data::core::wl_callback::{WlCallback, WlCallbackEventHandler, WlCallbackRef};
352 /// # use wl_client::test_protocols_data::core::wl_display::WlDisplay;
353 /// #
354 /// # tokio_test::block_on(async {
355 /// let lib = Libwayland::open().unwrap();
356 /// let con = lib.connect_to_default_display().unwrap();
357 /// let (_queue, queue) = con.create_queue_with_data::<State>(c"queue name");
358 /// let display: WlDisplay = queue.display();
359 ///
360 /// struct State {
361 /// done: bool,
362 /// }
363 /// let mut state = State {
364 /// done: false,
365 /// };
366 ///
367 /// // send some messages to the compositor
368 /// let sync = display.sync();
369 /// proxy::set_event_handler(&sync, WlCallback::on_done(move |state: &mut State, _, _| {
370 /// state.done = true;
371 /// }));
372 ///
373 /// // perform a roundtrip
374 /// queue.dispatch_roundtrip_async(&mut state).await.unwrap();
375 ///
376 /// // assert that we've received the response
377 /// assert!(state.done);
378 /// # });
379 /// ```
380 pub async fn dispatch_roundtrip_async(&self, data: &mut T) -> io::Result<()> {
381 self.dispatch_roundtrip_async_internal(|| self.dispatch_pending(data))
382 .await
383 }
384
385 /// Dispatches enqueued events.
386 ///
387 /// This function is the same as [`Queue::dispatch_pending`] but accepts a `&mut T`
388 /// that will be passed to event handlers.
389 ///
390 /// # Panic
391 ///
392 /// - Panics if this is a [local queue](Connection::create_local_queue) and the current
393 /// thread is not the thread that this queue was created in.
394 ///
395 /// # Example
396 ///
397 /// ```
398 /// # use std::sync::Arc;
399 /// # use std::sync::atomic::AtomicBool;
400 /// # use std::sync::atomic::Ordering::Relaxed;
401 /// # use wl_client::{proxy, Libwayland};
402 /// # use wl_client::test_protocol_helpers::callback;
403 /// # use wl_client::test_protocols_data::core::wl_callback::{WlCallback, WlCallbackEventHandler, WlCallbackRef};
404 /// # use wl_client::test_protocols_data::core::wl_display::WlDisplay;
405 /// #
406 /// # tokio_test::block_on(async {
407 /// let lib = Libwayland::open().unwrap();
408 /// let con = lib.connect_to_default_display().unwrap();
409 /// let (_queue, queue) = con.create_queue_with_data(c"queue name");
410 /// let display: WlDisplay = queue.display();
411 ///
412 /// struct State {
413 /// done: bool,
414 /// }
415 /// let mut state = State {
416 /// done: false,
417 /// };
418 ///
419 /// let sync = display.sync();
420 /// proxy::set_event_handler(&sync, WlCallback::on_done(move |state: &mut State, _, _| {
421 /// state.done = true;
422 /// }));
423 ///
424 /// while !state.done {
425 /// queue.wait_for_events().await.unwrap();
426 /// // Dispatch the events.
427 /// queue.dispatch_pending(&mut state).unwrap();
428 /// }
429 /// # });
430 /// ```
431 pub fn dispatch_pending(&self, data: &mut T) -> io::Result<u64> {
432 // SAFETY: - If mut_data_type is Some, then the invariants guarantee that it is
433 // the type ID of T.
434 // - Otherwise, `&mut T = &mut U`.
435 unsafe { self.dispatch_pending_internal(ptr::from_mut(data).cast()) }
436 }
437}
438
439impl<T> Clone for QueueWithData<T>
440where
441 T: 'static,
442{
443 fn clone(&self) -> Self {
444 Self {
445 queue: self.queue.clone(),
446 _phantom: Default::default(),
447 }
448 }
449}
450
451impl<T> Deref for QueueWithData<T>
452where
453 T: 'static,
454{
455 type Target = Queue;
456
457 fn deref(&self) -> &Self::Target {
458 &self.queue
459 }
460}
461
462impl<T> PartialEq for QueueWithData<T>
463where
464 T: 'static,
465{
466 fn eq(&self, other: &Self) -> bool {
467 self.queue == other.queue
468 }
469}
470
471impl<T> Eq for QueueWithData<T> where T: 'static {}
472
473impl<T> Debug for QueueWithData<T>
474where
475 T: 'static,
476{
477 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
478 Debug::fmt(&self.queue, f)
479 }
480}