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