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}