Skip to main content

wayland_client/
conn.rs

1use std::{
2    env, fmt,
3    io::ErrorKind,
4    mem,
5    os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
6    os::unix::net::UnixStream,
7    path::PathBuf,
8    sync::{
9        Arc,
10        atomic::{AtomicBool, Ordering},
11    },
12};
13
14use wayland_backend::{
15    client::{Backend, InvalidId, ObjectData, ObjectId, ReadEventsGuard, WaylandError},
16    protocol::{ObjectInfo, ProtocolError},
17};
18
19use crate::{EventQueue, Proxy, protocol::wl_display::WlDisplay};
20
21/// The Wayland connection
22///
23/// This is the main type representing your connection to the Wayland server, though most of the interaction
24/// with the protocol are actually done using other types. The two main uses a simple app has for the
25/// [`Connection`] are:
26///
27/// - Obtaining the initial [`WlDisplay`] through the [`display()`][Self::display()] method.
28/// - Creating new [`EventQueue`]s with the [`new_event_queue()`][Self::new_event_queue()] method.
29///
30/// It can be created through the [`connect_to_env()`][Self::connect_to_env()] method to follow the
31/// configuration from the environment (which is what you'll do most of the time), or using the
32/// [`from_socket()`][Self::from_socket()] method if you retrieved your connected Wayland socket through
33/// other means.
34///
35/// In case you need to plug yourself into an external Wayland connection that you don't control, you'll
36/// likely get access to it as a [`Backend`], in which case you can create a [`Connection`] from it using
37/// the [`from_backend()`][Self::from_backend()] method.
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct Connection {
40    pub(crate) backend: Backend,
41}
42
43unsafe fn stream_from_wayland_socket_var() -> Result<Option<UnixStream>, ConnectError> {
44    if let Ok(txt) = env::var("WAYLAND_SOCKET") {
45        // We should connect to the provided WAYLAND_SOCKET
46        let fd = txt.parse::<RawFd>().map_err(|_| ConnectError::InvalidFd)?;
47        // Verify `fd` isn't negative, or stdin/out/err
48        if fd <= 2 {
49            return Err(ConnectError::InvalidFd);
50        }
51        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
52        // remove the variable so any child processes don't see it
53        // TODO: Audit that the environment access only happens in single-threaded code.
54        unsafe { env::remove_var("WAYLAND_SOCKET") };
55        let Ok(flags) = rustix::io::fcntl_getfd(&fd) else {
56            // Don't call `close` on drop
57            mem::forget(fd);
58            // Failed to call `F_GETFD`; likely closed file descriptor
59            return Err(ConnectError::InvalidFd);
60        };
61        if flags.contains(rustix::io::FdFlags::CLOEXEC) {
62            // If `CLOEXEC` is already set, this is not the file descriptor
63            // passed by the parent process, or it has already been consumed.
64            return Err(ConnectError::InvalidFd);
65        }
66        // set the `CLOEXEC` flag on this FD
67        rustix::io::fcntl_setfd(&fd, flags | rustix::io::FdFlags::CLOEXEC)
68            .map_err(|_| ConnectError::InvalidFd)?;
69        // setting the `CLOEXEC` flag worked
70        Ok(Some(UnixStream::from(fd)))
71    } else {
72        Ok(None)
73    }
74}
75
76fn stream_from_wayland_display_var() -> Option<UnixStream> {
77    let socket_name = env::var_os("WAYLAND_DISPLAY").map(Into::<PathBuf>::into)?;
78
79    let socket_path = if socket_name.is_absolute() {
80        socket_name
81    } else {
82        let mut socket_path = env::var_os("XDG_RUNTIME_DIR").map(Into::<PathBuf>::into)?;
83        if !socket_path.is_absolute() {
84            return None;
85        }
86        socket_path.push(socket_name);
87        socket_path
88    };
89
90    UnixStream::connect(socket_path).ok()
91}
92
93impl Connection {
94    /// Try to connect to the Wayland server following the environment
95    ///
96    /// This first attempt to connect to the file descriptor specified in `WAYLAND_SOCKET`.
97    /// And then removes that variable from the environment. If that variable does
98    /// not exist, it then connects based on the `WAYLAND_DISPLAY` variable, like
99    /// [Self::connect_to_env_threadsafe]. This matches the behavior of `libwayland-client`.
100    ///
101    /// This is the standard way to initialize a Wayland connection.
102    ///
103    /// # Safety
104    ///
105    /// [Unsetting the env var may be unsound in a multithreaded
106    /// program](https://doc.rust-lang.org/std/env/fn.remove_var.html#safety), and if
107    /// the `WAYLAND_SOCKET` variable has a bogus value that is already an FD number in
108    /// use for something else, that may also be unsound.
109    ///
110    /// Ideally, a process using Wayland should call this once at the start of `main()`
111    /// before spawning other threads. [Self::connect_to_env_threadsafe] may be used if this
112    /// is not practical.
113    pub unsafe fn connect_to_env() -> Result<Self, ConnectError> {
114        let stream = if let Some(stream) = unsafe { stream_from_wayland_socket_var()? } {
115            stream
116        } else {
117            stream_from_wayland_display_var().ok_or(ConnectError::NoCompositor)?
118        };
119
120        Self::from_socket(stream)
121    }
122
123    /// Connect to the Wayland server using the `WAYLAND_DISPLAY` env var
124    ///
125    /// Unlike [`Self::connect_to_env`], this does **not** consider the `WAYLAND_SOCKET`
126    /// variable, which makes this function safe to call in any context. Under a normal
127    /// Wayland compositor, using `WAYLAND_DISPLAY` is generally sufficient.
128    pub fn connect_to_env_threadsafe() -> Result<Self, ConnectError> {
129        let stream = stream_from_wayland_display_var().ok_or(ConnectError::NoCompositor)?;
130        Self::from_socket(stream)
131    }
132
133    /// Initialize a Wayland connection from an already existing Unix stream
134    pub fn from_socket(stream: UnixStream) -> Result<Self, ConnectError> {
135        let backend = Backend::connect(stream).map_err(|_| ConnectError::NoWaylandLib)?;
136        Ok(Self { backend })
137    }
138
139    /// Get the `WlDisplay` associated with this connection
140    pub fn display(&self) -> WlDisplay {
141        let display_id = self.backend.display_id();
142        Proxy::from_id(self, display_id).unwrap()
143    }
144
145    /// Create a new event queue
146    pub fn new_event_queue<State>(&self) -> EventQueue<State> {
147        EventQueue::new(self.clone())
148    }
149
150    /// Wrap an existing [`Backend`] into a [`Connection`]
151    pub fn from_backend(backend: Backend) -> Self {
152        Self { backend }
153    }
154
155    /// Get the [`Backend`] underlying this [`Connection`]
156    pub fn backend(&self) -> Backend {
157        self.backend.clone()
158    }
159
160    /// Flush pending outgoing events to the server
161    ///
162    /// This needs to be done regularly to ensure the server receives all your requests, though several
163    /// dispatching methods do it implicitly (this is stated in their documentation when they do).
164    pub fn flush(&self) -> Result<(), WaylandError> {
165        self.backend.flush()
166    }
167
168    /// Start a synchronized read from the socket
169    ///
170    /// This is needed if you plan to wait on readiness of the Wayland socket using an event loop. See
171    /// [`ReadEventsGuard`] for details. Once the events are received, you'll then need to dispatch them from
172    /// their event queues using [`EventQueue::dispatch_pending()`].
173    ///
174    /// If you don't need to manage multiple event sources, see
175    /// [`EventQueue::blocking_dispatch()`] for a simpler mechanism.
176    #[must_use]
177    pub fn prepare_read(&self) -> Option<ReadEventsGuard> {
178        self.backend.prepare_read()
179    }
180
181    /// Do a roundtrip to the server
182    ///
183    /// This method will block until the Wayland server has processed and answered all your
184    /// preceding requests. This is notably useful during the initial setup of an app, to wait for
185    /// the initial state from the server.
186    ///
187    /// See [`EventQueue::roundtrip()`] for a version that includes the dispatching of the event queue.
188    pub fn roundtrip(&self) -> Result<usize, WaylandError> {
189        let done = Arc::new(SyncData::default());
190        let display = self.display();
191        self.send_request(
192            &display,
193            crate::protocol::wl_display::Request::Sync {},
194            Some(done.clone()),
195        )
196        .map_err(|_| WaylandError::Io(rustix::io::Errno::PIPE.into()))?;
197
198        let mut dispatched = 0;
199
200        loop {
201            self.backend.flush()?;
202
203            if let Some(guard) = self.backend.prepare_read() {
204                dispatched += blocking_read(guard)?;
205            } else {
206                dispatched += self.backend.dispatch_inner_queue()?;
207            }
208
209            // see if the successful read included our callback
210            if done.done.load(Ordering::Relaxed) {
211                break;
212            }
213        }
214
215        Ok(dispatched)
216    }
217
218    /// Retrieve the protocol error that occured on the connection if any
219    ///
220    /// If this method returns [`Some`], it means your Wayland connection is already dead.
221    pub fn protocol_error(&self) -> Option<ProtocolError> {
222        match self.backend.last_error()? {
223            WaylandError::Protocol(err) => Some(err),
224            WaylandError::Io(_) => None,
225        }
226    }
227
228    /// Send a request associated with the provided object
229    ///
230    /// This is a low-level interface used by the code generated by `wayland-scanner`, you will likely
231    /// instead use the methods of the types representing each interface, or the [`Proxy::send_request()`] and
232    /// [`Proxy::send_constructor()`].
233    pub fn send_request<I: Proxy>(
234        &self,
235        proxy: &I,
236        request: I::Request<'_>,
237        data: Option<Arc<dyn ObjectData>>,
238    ) -> Result<ObjectId, InvalidId> {
239        let (msg, child_spec) = proxy.write_request(self, request)?;
240        self.backend.send_request(msg, data, child_spec)
241    }
242
243    /// Get the protocol information related to given object ID
244    pub fn object_info(&self, id: &ObjectId) -> Result<ObjectInfo, InvalidId> {
245        self.backend.info(id)
246    }
247
248    /// Get the object data for a given object ID
249    ///
250    /// This is a low-level interface used by the code generated by `wayland-scanner`, a higher-level
251    /// interface for manipulating the user-data assocated to [`Dispatch`][crate::Dispatch] implementations
252    /// is given as [`Proxy::data()`]. Also see [`Proxy::object_data()`].
253    pub fn get_object_data(&self, id: &ObjectId) -> Result<Arc<dyn ObjectData>, InvalidId> {
254        self.backend.get_data(id)
255    }
256
257    /// Set maximum buffer size.
258    #[cfg(feature = "libwayland_1_23")]
259    pub fn set_max_buffer_size(&self, max_buffer_size: Option<usize>) {
260        self.backend.set_max_buffer_size(max_buffer_size);
261    }
262}
263
264pub(crate) fn blocking_read(guard: ReadEventsGuard) -> Result<usize, WaylandError> {
265    let fd = guard.connection_fd();
266    let mut fds = [rustix::event::PollFd::new(
267        &fd,
268        rustix::event::PollFlags::IN | rustix::event::PollFlags::ERR,
269    )];
270
271    loop {
272        match rustix::event::poll(&mut fds, None) {
273            Ok(_) => break,
274            Err(rustix::io::Errno::INTR) => continue,
275            Err(e) => return Err(WaylandError::Io(e.into())),
276        }
277    }
278
279    // at this point the fd is ready
280    match guard.read() {
281        Ok(n) => Ok(n),
282        // if we are still "wouldblock", just return 0; the caller will retry.
283        Err(WaylandError::Io(e)) if e.kind() == ErrorKind::WouldBlock => Ok(0),
284        Err(e) => Err(e),
285    }
286}
287
288/// An error when trying to establish a Wayland connection.
289#[derive(Debug)]
290pub enum ConnectError {
291    /// The wayland library could not be loaded.
292    NoWaylandLib,
293
294    /// Could not find wayland compositor
295    NoCompositor,
296
297    /// `WAYLAND_SOCKET` was set but contained garbage
298    InvalidFd,
299}
300
301impl std::error::Error for ConnectError {}
302
303impl fmt::Display for ConnectError {
304    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
305        match self {
306            ConnectError::NoWaylandLib => {
307                write!(f, "The wayland library could not be loaded")
308            }
309            ConnectError::NoCompositor => {
310                write!(f, "Could not find wayland compositor")
311            }
312            ConnectError::InvalidFd => {
313                write!(f, "WAYLAND_SOCKET was set but contained garbage")
314            }
315        }
316    }
317}
318
319impl AsFd for Connection {
320    /// Provides fd from [`Backend::poll_fd()`] for polling.
321    fn as_fd(&self) -> BorrowedFd<'_> {
322        self.backend.poll_fd()
323    }
324}
325
326impl AsRawFd for Connection {
327    /// Provides fd from [`Backend::poll_fd()`] for polling.
328    fn as_raw_fd(&self) -> RawFd {
329        self.backend.poll_fd().as_raw_fd()
330    }
331}
332
333/*
334    wl_callback object data for wl_display.sync
335*/
336
337#[derive(Default)]
338pub(crate) struct SyncData {
339    pub(crate) done: AtomicBool,
340}
341
342impl ObjectData for SyncData {
343    fn event(
344        self: Arc<Self>,
345        _handle: &Backend,
346        _msg: wayland_backend::protocol::OwnedMessage<ObjectId>,
347    ) -> Option<Arc<dyn ObjectData>> {
348        self.done.store(true, Ordering::Relaxed);
349        None
350    }
351
352    fn destroyed(&self, _: &ObjectId) {}
353}