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}