Skip to main content

wayland_client/
conn.rs

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