From 8a4034e478d816c7bf84269c74d8fc568408c948 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:08:09 +0100 Subject: [PATCH] Update to latest calloop and wayland-client --- Cargo.toml | 4 +- examples/simplest-window.rs | 2 +- src/lib.rs | 73 +++++++++++++++++++------------------ 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d25dcdd..a12bf95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,9 @@ keywords = ["wayland", "windowing"] rust-version = "1.65.0" [dependencies] -wayland-client = "0.31.0" +wayland-client = "0.31.1" wayland-backend = "0.3.0" -calloop = { git = "https://github.com/DJMcNab/calloop/", rev = "9df1f5c949c3d7f23817b2bcbd5c481ff0ec7e65" } +calloop = "0.12.1" log = { version = "0.4.19", optional = true } rustix = { version = "0.38.4", default-features = false, features = ["std"] } diff --git a/examples/simplest-window.rs b/examples/simplest-window.rs index fd47b4e..4be36a6 100644 --- a/examples/simplest-window.rs +++ b/examples/simplest-window.rs @@ -25,7 +25,7 @@ fn main() { let mut event_loop: EventLoop = EventLoop::try_new().expect("Failed to initialize the event loop!"); let loop_handle = event_loop.handle(); - WaylandSource::new(event_queue).unwrap().insert(loop_handle).unwrap(); + WaylandSource::new(conn, event_queue).insert(loop_handle).unwrap(); let stop_handle = event_loop.get_signal(); let mut state = State { diff --git a/src/lib.rs b/src/lib.rs index 8527e18..ba538c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,17 +28,17 @@ //! } //! ``` +#![forbid(unsafe_op_in_unsafe_fn)] use std::io; -use std::os::unix::io::{AsRawFd, RawFd}; -use calloop::generic::{FdWrapper, Generic}; +use calloop::generic::Generic; use calloop::{ EventSource, InsertError, Interest, LoopHandle, Mode, Poll, PostAction, Readiness, RegistrationToken, Token, TokenFactory, }; use rustix::io::Errno; use wayland_backend::client::{ReadEventsGuard, WaylandError}; -use wayland_client::{DispatchError, EventQueue}; +use wayland_client::{Connection, DispatchError, EventQueue}; #[cfg(feature = "log")] use log::error as log_error; @@ -57,8 +57,12 @@ use std::eprintln as log_error; /// loop and automatically dispatch pending events on the event queue. #[derive(Debug)] pub struct WaylandSource { + // In theory, we could use the same event queue inside `connection_source` + // However, calloop's safety requirements mean that we cannot then give + // mutable access to the queue, which is incompatible with our current interface + // Additionally, `Connection` is cheaply cloneable, so it's not a huge burden queue: EventQueue, - fd: Generic>, + connection_source: Generic, read_guard: Option, /// Calloop's before_will_sleep method allows /// skipping the sleeping by returning a `Token`. @@ -72,40 +76,34 @@ pub struct WaylandSource { impl WaylandSource { /// Wrap an [`EventQueue`] as a [`WaylandSource`]. /// - /// If this returns None, that means that acquiring a read guard failed. - /// See [EventQueue::prepare_read] for details - /// This guard is only used to get the wayland file descriptor + /// `queue` must be from the connection `Connection`. + /// This is not a safety invariant, but not following this may cause + /// freezes or hangs #[must_use] - pub fn new(queue: EventQueue) -> Option> { - let guard = queue.prepare_read()?; - let fd = Generic::new( - // Safety: `connection_fd` returns the wayland socket fd, - // and EventQueue (eventually) owns this socket - // fd is only used in calloop, which guarantees - // that the source is unregistered before dropping it, so the - // fd cannot be used after being freed - // Wayland-backend should probably document some guarantees here to make this sound, - // but we know that they are unlikely to make the queue have a different socket/fd - // - there is no public API to do so - unsafe { FdWrapper::new(guard.connection_fd().as_raw_fd()) }, - Interest::READ, - Mode::Level, - ); - drop(guard); + pub fn new(connection: Connection, queue: EventQueue) -> WaylandSource { + let connection_source = Generic::new(connection, Interest::READ, Mode::Level); - Some(WaylandSource { queue, fd, read_guard: None, fake_token: None, stored_error: Ok(()) }) + WaylandSource { + queue, + connection_source, + read_guard: None, + fake_token: None, + stored_error: Ok(()), + } } /// Access the underlying event queue /// - /// Note that you should be careful when interacting with it if you invoke - /// methods that interact with the wayland socket (such as `dispatch()` - /// or `prepare_read()`). These may interfere with the proper waking up - /// of this event source in the event loop. + /// Note that you should not replace this queue with a queue from a different + /// `Connection`, as that may cause freezes or other hangs. pub fn queue(&mut self) -> &mut EventQueue { &mut self.queue } + /// Access the connection to the Wayland server + pub fn connection(&self) -> &Connection { + self.connection_source.get_ref() + } /// Insert this source into the given event loop. /// /// This adapter will pass the event loop's shared data as the `D` type for @@ -141,15 +139,18 @@ impl EventSource for WaylandSource { { debug_assert!(self.read_guard.is_none()); - let queue = &mut self.queue; // Take the stored error std::mem::replace(&mut self.stored_error, Ok(()))?; - // Because our polling is based on a `Generic` source, in theory we might want - // to to call the process_events handler on fd. However, - // we know that Generic's `process_events` call is a no-op, so we can just - // handle the event ourselves + // We know that the event will either be a fake event + // produced in `before_will_sleep`, or a "real" event from the underlying + // source (self.queue_events). Our behaviour in both cases is the same. + // In theory we might want to call the process_events handler on the underlying + // event source. However, we know that Generic's `process_events` call is a no-op, + // so we just handle the event ourselves. + // Safety: We don't replace the + let queue = &mut self.queue; // Dispatch any pending events in the queue Self::loop_callback_pending(queue, &mut callback)?; @@ -165,7 +166,7 @@ impl EventSource for WaylandSource { token_factory: &mut TokenFactory, ) -> calloop::Result<()> { self.fake_token = Some(token_factory.token()); - self.fd.register(poll, token_factory) + self.connection_source.register(poll, token_factory) } fn reregister( @@ -173,11 +174,11 @@ impl EventSource for WaylandSource { poll: &mut Poll, token_factory: &mut TokenFactory, ) -> calloop::Result<()> { - self.fd.reregister(poll, token_factory) + self.connection_source.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> { - self.fd.unregister(poll) + self.connection_source.unregister(poll) } fn before_sleep(&mut self) -> calloop::Result> {