| 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()`][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)] |
| 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()`]. |
| 133 | /// |
| 134 | /// If you don't need to manage multiple event sources, see |
| 135 | /// [`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 | |