wl_client/
connection.rs

1use {
2    crate::{
3        Libwayland,
4        connection::{data::ConnectionData2, flush::Flusher, read_lock::SharedReadLock},
5        ffi::wl_display,
6        utils::{executor::Executor, poller::Poller},
7    },
8    std::{
9        ffi::CStr,
10        fmt::{Debug, Formatter},
11        io,
12        os::fd::{IntoRawFd, OwnedFd},
13        ptr::{self, NonNull},
14        sync::Arc,
15    },
16};
17
18mod flush;
19pub(crate) mod read_lock;
20#[cfg(test)]
21mod tests;
22pub(crate) mod wait_for_events;
23
24/// A connection to a wayland compositor.
25///
26/// You can create a connection by using one of the methods on [`Libwayland`].
27///
28/// Each connection wraps a libwayland `wl_display` pointer. This pointer can be owned or
29/// borrowed. If the pointer is owned by the time the last reference to the connection is
30/// dropped, the `wl_display` is destroyed. If the connection owns the pointer, you can
31/// take ownership of the pointer by calling [`Connection::take_ownership`].
32///
33/// # Example
34///
35/// ```
36/// # use wl_client::Libwayland;
37/// #
38/// let lib = Libwayland::open().unwrap();
39/// let _con = lib.connect_to_default_display();
40/// ```
41#[derive(Clone)]
42pub struct Connection {
43    data: Arc<ConnectionData1>,
44}
45
46struct ConnectionData1 {
47    pub(super) shared_read_lock: SharedReadLock,
48    poller: Poller,
49    flusher: Flusher,
50    executor: Executor,
51    // Note: Keep this last so that the connection is kept open until all threads have
52    // been joined. This simplifies testing with miri.
53    data: Arc<ConnectionData2>,
54}
55
56impl Libwayland {
57    /// Connects to the default display.
58    ///
59    /// The default display is usually identified by the `WAYLAND_DISPLAY` environment
60    /// variable but falls back to `wayland-0` if the environment variable is not set.
61    ///
62    /// # Example
63    ///
64    /// ```
65    /// # use wl_client::Libwayland;
66    /// #
67    /// let lib = Libwayland::open().unwrap();
68    /// let _con = lib.connect_to_default_display().unwrap();
69    /// ```
70    pub fn connect_to_default_display(&'static self) -> io::Result<Connection> {
71        self.connect_to_display(None)
72    }
73
74    /// Connects to a display with a given name.
75    ///
76    /// The name of the display should usually be of the form `wayland-N` or it should be
77    /// the absolute path of a display socket.
78    ///
79    /// # Example
80    ///
81    /// ```
82    /// # use wl_client::Libwayland;
83    /// #
84    /// let lib = Libwayland::open().unwrap();
85    /// let _con = lib.connect_to_named_display(c"wayland-1").unwrap();
86    /// ```
87    pub fn connect_to_named_display(&'static self, display: &CStr) -> io::Result<Connection> {
88        self.connect_to_display(Some(display))
89    }
90
91    fn connect_to_display(&'static self, display_name: Option<&CStr>) -> io::Result<Connection> {
92        let display_name = display_name.map(|n| n.as_ptr()).unwrap_or(ptr::null());
93        // SAFETY: display_name is null or a CStr pointer.
94        let wl_display = unsafe { self.wl_display_connect(display_name) };
95        // SAFETY: wl_display was just returned by a libwayland connect function.
96        unsafe { self.wrap_owned_raw_pointer(wl_display) }
97    }
98
99    /// Consumes an existing socket connected to a wayland compositor.
100    ///
101    /// Unlike [`Libwayland::connect_to_default_display`], this function does not perform
102    /// any blocking IO.
103    pub fn connect_to_fd(&'static self, fd: OwnedFd) -> io::Result<Connection> {
104        // SAFETY: - fd.into_raw_fd() returns a valid file descriptor.
105        let wl_display = unsafe { self.wl_display_connect_to_fd(fd.into_raw_fd()) };
106        // SAFETY: wl_display was just returned by a libwayland connect function.
107        unsafe { self.wrap_owned_raw_pointer(wl_display) }
108    }
109
110    /// # Safety
111    ///
112    /// `wl_display` must have been returned by one of the libwayland connect functions.
113    unsafe fn wrap_owned_raw_pointer(
114        &'static self,
115        wl_display: *mut wl_display,
116    ) -> io::Result<Connection> {
117        let Some(wl_display) = NonNull::new(wl_display) else {
118            return Err(io::Error::last_os_error());
119        };
120        // SAFETY: - if libwayland returns a non-null pointer, it is valid
121        //         - we just created the display so we have ownership
122        unsafe { self.wrap_owned_pointer(wl_display) }
123    }
124
125    /// Takes ownership of an existing `wl_display`.
126    ///
127    /// If the display is owned when the last clone of the [`Connection`] is dropped, the
128    /// display will be destroyed. All proxies and queues created from the `Connection`
129    /// will contain a clone of the connection to keep the connection alive.
130    ///
131    /// For proxies and queues that already exist at the time this function is called, you
132    /// must manage the lifetime requirements manually.
133    ///
134    /// # Safety
135    ///
136    /// - `wl_display` must be valid and must stay valid for the lifetime of this object
137    ///   and its clones.
138    /// - The display file descriptor must be open and owned by the `wl_display`.
139    /// - If the display is owned by the time the connection is dropped, all proxies
140    ///   and queues created from this object must have been destroyed before then.
141    ///
142    /// # Example
143    ///
144    /// ```
145    /// # use wl_client::Libwayland;
146    /// #
147    /// let lib = Libwayland::open().unwrap();
148    /// let wl_display = {
149    ///     let con = lib.connect_to_default_display().unwrap();
150    ///     con.take_ownership().unwrap()
151    /// };
152    /// // SAFETY: We took the display from a freshly created Connection so it is valid
153    /// //         and has no queues or proxies attached.
154    /// let _con = unsafe { lib.wrap_owned_pointer(wl_display) };
155    /// ```
156    pub unsafe fn wrap_owned_pointer(
157        &'static self,
158        wl_display: NonNull<wl_display>,
159    ) -> io::Result<Connection> {
160        // SAFETY: The requirements are forwarded to the caller
161        unsafe { self.wrap_pointer(wl_display, true) }
162    }
163
164    /// Borrows an existing `wl_display`.
165    ///
166    /// # Safety
167    ///
168    /// - `wl_display` must be valid and must stay valid for the lifetime of this object
169    ///   and its clones.
170    /// - The display file descriptor must be open and owned by the `wl_display`.
171    ///
172    /// # Example
173    ///
174    /// ```
175    /// # use wl_client::Libwayland;
176    /// #
177    /// let lib = Libwayland::open().unwrap();
178    /// let con = lib.connect_to_default_display().unwrap();
179    /// let wl_display = con.wl_display();
180    /// {
181    ///     // SAFETY: - We took the display from a freshly created Connection so it is valid.
182    ///     //         - We drop the connection before dropping the outer connection that
183    ///     //           owns the wl_display.
184    ///     let _con = unsafe { lib.wrap_borrowed_pointer(wl_display) };
185    /// }
186    /// ```
187    pub unsafe fn wrap_borrowed_pointer(
188        &'static self,
189        wl_display: NonNull<wl_display>,
190    ) -> io::Result<Connection> {
191        // SAFETY: owned is false and the requirements are forwarded to the caller
192        unsafe { self.wrap_pointer(wl_display, false) }
193    }
194
195    /// Creates a new Connection from a wl_display.
196    ///
197    /// The display will be closed when the last clone of this object is dropped. All
198    ///
199    /// # Safety
200    ///
201    /// - `wl_display` must be valid and must stay valid for the lifetime of this object.
202    /// - The display file descriptor must be open and owned by the wl_display.
203    /// - If the wl_display is owned by the time this object is dropped, all proxies
204    ///   and queues created from this object must have been destroyed before then.
205    unsafe fn wrap_pointer(
206        &'static self,
207        wl_display: NonNull<wl_display>,
208        owned: bool,
209    ) -> io::Result<Connection> {
210        // SAFETY: - The requirements are forwarded to the caller and Self always contains a
211        //           reference to the ConnectionData2, delaying its drop until no earlier
212        //           than the drop of this object.
213        //         - All proxies and queues that we create will contain a clone of this
214        //           object.
215        let data = unsafe { Arc::new(ConnectionData2::new(self, wl_display, owned)) };
216        let executor = Executor::new()?;
217        let poller = Poller::new(&data)?;
218        let data = Arc::new(ConnectionData1 {
219            flusher: Flusher::new(&poller, &executor, &data),
220            poller,
221            shared_read_lock: SharedReadLock::new(&data)?,
222            executor,
223            data,
224        });
225        Ok(Connection { data })
226    }
227}
228
229impl Connection {
230    /// Returns whether this connection owns the underlying `wl_display`.
231    ///
232    /// When the last reference to the connection is dropped, the `wl_display` will be
233    /// destroyed if and only if the display is owned at that time.
234    ///
235    /// If this function returns `false`, then it will always return `false`.
236    ///
237    /// If this function returns `true`, then it might return `false` in the future if
238    /// ownership is consumed by calling [`Self::take_ownership`].
239    ///
240    /// # Example
241    ///
242    /// ```
243    /// # use wl_client::Libwayland;
244    /// #
245    /// let lib = Libwayland::open().unwrap();
246    /// let con = lib.connect_to_default_display().unwrap();
247    /// assert!(con.is_owned());
248    /// let wl_display = con.wl_display();
249    /// {
250    ///     // SAFETY: - We took the display from a freshly created Connection so it is valid.
251    ///     //         - We drop the connection before dropping the outer connection that
252    ///     //           owns the wl_display.
253    ///     let con = unsafe { lib.wrap_borrowed_pointer(wl_display).unwrap() };
254    ///     assert!(con.is_borrowed());
255    /// }
256    /// ```
257    pub fn is_owned(&self) -> bool {
258        self.data.data.is_owned()
259    }
260
261    /// Returns whether this connection borrows the underlying `wl_display`.
262    ///
263    /// This is the same as `!self.is_owned()`.
264    pub fn is_borrowed(&self) -> bool {
265        !self.is_owned()
266    }
267
268    /// Returns the underlying `wl_display` pointer.
269    ///
270    /// This is always a valid pointer.
271    ///
272    /// # Example
273    ///
274    /// ```
275    /// # use wl_client::Libwayland;
276    /// #
277    /// let lib = Libwayland::open().unwrap();
278    /// let con = lib.connect_to_default_display().unwrap();
279    /// let _wl_display = con.wl_display();
280    /// ```
281    pub fn wl_display(&self) -> NonNull<wl_display> {
282        self.data.data.wl_display().0
283    }
284
285    /// Takes ownership of the underlying `wl_display`.
286    ///
287    /// If this returns `Some`, then ownership has been transferred from the connection
288    /// to the caller. After this function returns, the connection no longer has ownership
289    /// of the underlying `wl_display` and will not destroy the display.
290    ///
291    /// # Example
292    ///
293    /// ```
294    /// # use wl_client::Libwayland;
295    /// #
296    /// let lib = Libwayland::open().unwrap();
297    /// let wl_display = {
298    ///     let con = lib.connect_to_default_display().unwrap();
299    ///     con.take_ownership().unwrap()
300    /// };
301    /// // SAFETY: We took the display from a freshly created Connection so it is valid
302    /// //         and has no queues or proxies attached.
303    /// let _con = unsafe { lib.wrap_owned_pointer(wl_display) };
304    /// ```
305    pub fn take_ownership(&self) -> Option<NonNull<wl_display>> {
306        self.data.data.take_ownership()
307    }
308
309    /// Returns a reference to the [`Libwayland`] singleton.
310    pub fn libwayland(&self) -> &'static Libwayland {
311        self.data.data.libwayland
312    }
313
314    /// Returns the last error that occurred on the connection, if any.
315    ///
316    /// Since all errors are fatal, if this function returns an error, the connection can
317    /// no longer be used.
318    ///
319    /// # Example
320    ///
321    /// ```
322    /// # use wl_client::Libwayland;
323    /// #
324    /// let lib = Libwayland::open().unwrap();
325    /// let con = lib.connect_to_default_display().unwrap();
326    /// assert!(con.error().is_ok());
327    /// ```
328    pub fn error(&self) -> io::Result<()> {
329        self.data.data.error()
330    }
331}
332
333pub(super) mod data {
334    use {
335        crate::{
336            Libwayland,
337            ffi::{wl_display, wl_event_queue},
338            utils::sync_ptr::SyncNonNull,
339        },
340        std::{
341            io,
342            os::fd::{AsFd, BorrowedFd},
343            ptr::NonNull,
344            sync::atomic::{AtomicBool, Ordering::Relaxed},
345        },
346    };
347
348    /// The core wrapper around a wl_display.
349    ///
350    /// This objects contains a wl_display and a flag that determines whether the display
351    /// is owned by this object.
352    ///
353    /// The contained wl_display is a valid pointer at all times.
354    ///
355    /// If the display is owned by the time this object is dropped, it is destroyed.
356    pub(super) struct ConnectionData2 {
357        pub(crate) libwayland: &'static Libwayland,
358        /// This is the libwayland display. The pointer is always valid and the contained fd
359        /// is open.
360        wl_display: SyncNonNull<wl_display>,
361        /// This indicates ownership of the wl_display. If someone wants to take ownership of
362        /// the display, e.g. to close it, they must check that this field is true and then
363        /// replace it by false.
364        owned: AtomicBool,
365        /// An event queue belonging to this display that has no proxies attached. This
366        /// pointer is valid until this object is dropped. We sometimes dispatch this
367        /// queue to ensure that there are no pending wl_display.error messages. Such
368        /// messages are always dispatched when any queue is dispatched.
369        dummy_queue: SyncNonNull<wl_event_queue>,
370    }
371
372    impl ConnectionData2 {
373        /// Wraps an existing wl_display.
374        ///
375        /// If owned is true, this object takes ownership of the display and will close it
376        /// when this object is dropped.
377        ///
378        /// # Safety
379        ///
380        /// - `wl_display` must be valid and must stay valid for the lifetime of this object.
381        /// - If the wl_display is owned by the time this object is dropped, all proxies
382        ///   and queues created from this object must have been destroyed before then.
383        pub(crate) unsafe fn new(
384            libwayland: &'static Libwayland,
385            wl_display: NonNull<wl_display>,
386            owned: bool,
387        ) -> Self {
388            // SAFETY: By the prerequisites of this function, wl_display is valid.
389            let queue = unsafe { libwayland.wl_display_create_queue(wl_display.as_ptr()) };
390            let queue = NonNull::new(queue).unwrap();
391            Self {
392                libwayland,
393                wl_display: SyncNonNull(wl_display),
394                owned: AtomicBool::new(owned),
395                dummy_queue: SyncNonNull(queue),
396            }
397        }
398
399        /// Returns the contained wl_display.
400        ///
401        /// This is always a valid pointer.
402        ///
403        /// The display has a valid, open file descriptor.
404        pub(crate) fn wl_display(&self) -> SyncNonNull<wl_display> {
405            self.wl_display
406        }
407
408        /// Takes ownership of the contained wl_display, if possible.
409        ///
410        /// This is always a valid pointer.
411        pub(super) fn take_ownership(&self) -> Option<NonNull<wl_display>> {
412            if self.owned.swap(false, Relaxed) {
413                Some(self.wl_display.0)
414            } else {
415                None
416            }
417        }
418
419        /// Returns whether the wl_display is owned by this object.
420        pub(super) fn is_owned(&self) -> bool {
421            self.owned.load(Relaxed)
422        }
423
424        pub(super) fn error(&self) -> io::Result<()> {
425            // SAFETY: wl_display always returns a valid pointer
426            let err = unsafe {
427                self.libwayland
428                    .wl_display_get_error(self.wl_display().as_ptr())
429            };
430            if err != 0 {
431                return Err(io::Error::from_raw_os_error(err));
432            }
433            Ok(())
434        }
435
436        pub(super) fn ensure_no_error(&self) -> io::Result<()> {
437            // SAFETY: - wl_display always returns a valid pointer
438            //         - by the invariants, dummy_queue is valid and belongs to this
439            //           display
440            //         - by the invariants, no proxies are attached to the queue
441            unsafe {
442                self.libwayland.wl_display_dispatch_queue_pending(
443                    self.wl_display().as_ptr(),
444                    self.dummy_queue.as_ptr(),
445                )
446            };
447            self.error()
448        }
449    }
450
451    impl AsFd for ConnectionData2 {
452        fn as_fd(&self) -> BorrowedFd<'_> {
453            // SAFETY: The display function returns a valid pointer.
454            let fd = unsafe {
455                self.libwayland
456                    .wl_display_get_fd(self.wl_display().as_ptr())
457            };
458            // SAFETY: The display returned by the display function has a valid, open file
459            //         descriptor. Since the BorrowedFd borrows self, the file descriptor will
460            //         stay valid since the display stays valid.
461            unsafe { BorrowedFd::borrow_raw(fd) }
462        }
463    }
464
465    impl Drop for ConnectionData2 {
466        fn drop(&mut self) {
467            // SAFETY: - by the invariants, dummy_queue is valid
468            //         - by the invariants, no proxies are attached to the queue
469            unsafe {
470                self.libwayland
471                    .wl_event_queue_destroy(self.dummy_queue.as_ptr());
472            }
473            if let Some(display) = self.take_ownership() {
474                // SAFETY: - we just took ownership of the wl_display
475                //         - by the invariants, the display is valid
476                //         - by the invariants, all dependent objects must have been destroyed
477                unsafe {
478                    self.libwayland.wl_display_disconnect(display.as_ptr());
479                }
480            }
481        }
482    }
483}
484
485impl PartialEq for Connection {
486    fn eq(&self, other: &Self) -> bool {
487        self.data.data.wl_display() == other.data.data.wl_display()
488    }
489}
490
491impl Eq for Connection {}
492
493impl Debug for Connection {
494    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
495        f.debug_struct("Connection")
496            .field("wl_display", &self.wl_display())
497            .finish_non_exhaustive()
498    }
499}