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}