From ecb887e5c3d1f8757853a88bc54adc6db9da2cdf Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Mon, 24 Jun 2024 13:04:55 +0300 Subject: [PATCH] event_loop: remove generic user event Let the users wake up the event loop and then they could poll their user sources. Co-authored-by: Mads Marquart Co-authored-by: daxpedda --- examples/child_window.rs | 2 +- examples/window.rs | 18 +- src/application.rs | 108 +++++++++-- src/changelog/unreleased.md | 9 + src/changelog/v0.30.md | 4 +- src/event.rs | 52 ++---- src/event_loop.rs | 112 +++++------- src/lib.rs | 7 +- src/platform/android.rs | 4 +- src/platform/ios.rs | 2 +- src/platform/macos.rs | 2 +- src/platform/pump_events.rs | 13 +- src/platform/run_on_demand.rs | 13 +- src/platform/wayland.rs | 4 +- src/platform/web.rs | 13 +- src/platform/windows.rs | 2 +- src/platform/x11.rs | 4 +- src/platform_impl/android/mod.rs | 126 ++++--------- src/platform_impl/ios/app_state.rs | 15 +- src/platform_impl/ios/event_loop.rs | 78 ++++---- src/platform_impl/linux/mod.rs | 45 ++--- .../linux/wayland/event_loop/mod.rs | 56 +++--- .../linux/wayland/event_loop/proxy.rs | 27 +-- .../linux/wayland/event_loop/sink.rs | 4 +- src/platform_impl/linux/wayland/state.rs | 4 + .../linux/x11/event_processor.rs | 153 +++++++--------- src/platform_impl/linux/x11/mod.rs | 98 +++++----- src/platform_impl/linux/x11/window.rs | 10 +- src/platform_impl/macos/app_state.rs | 23 +-- src/platform_impl/macos/event_handler.rs | 15 +- src/platform_impl/macos/event_loop.rs | 143 ++++----------- src/platform_impl/orbital/event_loop.rs | 46 ++--- src/platform_impl/web/async/waker.rs | 67 +++---- src/platform_impl/web/event_loop/mod.rs | 45 ++--- src/platform_impl/web/event_loop/proxy.rs | 22 +-- src/platform_impl/web/event_loop/runner.rs | 42 ++--- .../web/event_loop/window_target.rs | 6 +- src/platform_impl/web/web_sys/canvas.rs | 2 +- src/platform_impl/windows/drop_handler.rs | 6 +- src/platform_impl/windows/event_loop.rs | 172 ++++++------------ .../windows/event_loop/runner.rs | 42 ++--- src/platform_impl/windows/window.rs | 6 +- tests/send_objects.rs | 8 +- tests/sync_object.rs | 7 +- 44 files changed, 673 insertions(+), 964 deletions(-) diff --git a/examples/child_window.rs b/examples/child_window.rs index 68e81308bb..257859db9f 100644 --- a/examples/child_window.rs +++ b/examples/child_window.rs @@ -85,7 +85,7 @@ fn main() -> Result<(), impl std::error::Error> { event_loop.create_window(window_attributes).unwrap() } - let event_loop: EventLoop<()> = EventLoop::new().unwrap(); + let event_loop = EventLoop::new().unwrap(); let mut app = Application::default(); event_loop.run_app(&mut app) } diff --git a/examples/window.rs b/examples/window.rs index 11f324713c..3a2a1f72ab 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -44,7 +44,7 @@ fn main() -> Result<(), Box> { tracing::init(); - let event_loop = EventLoop::::with_user_event().build()?; + let event_loop = EventLoop::new()?; let _event_loop_proxy = event_loop.create_proxy(); // Wire the user event from another thread. @@ -54,7 +54,7 @@ fn main() -> Result<(), Box> { // from a different thread. info!("Starting to send user event every second"); loop { - let _ = _event_loop_proxy.send_event(UserEvent::WakeUp); + _event_loop_proxy.wake_up(); std::thread::sleep(std::time::Duration::from_secs(1)); } }); @@ -64,12 +64,6 @@ fn main() -> Result<(), Box> { event_loop.run_app(&mut state).map_err(Into::into) } -#[allow(dead_code)] -#[derive(Debug, Clone, Copy)] -enum UserEvent { - WakeUp, -} - /// Application state and event handling. struct Application { /// Custom cursors assets. @@ -85,7 +79,7 @@ struct Application { } impl Application { - fn new(event_loop: &EventLoop) -> Self { + fn new(event_loop: &EventLoop) -> Self { // SAFETY: we drop the context right before the event loop is stopped, thus making it safe. #[cfg(not(any(android_platform, ios_platform)))] let context = Some( @@ -308,9 +302,9 @@ impl Application { } } -impl ApplicationHandler for Application { - fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) { - info!("User event: {event:?}"); +impl ApplicationHandler for Application { + fn proxy_wake_up(&mut self, _event_loop: &ActiveEventLoop) { + info!("User wake up"); } fn window_event( diff --git a/src/application.rs b/src/application.rs index 977a7c79f2..dbb9ac1d25 100644 --- a/src/application.rs +++ b/src/application.rs @@ -5,7 +5,7 @@ use crate::event_loop::ActiveEventLoop; use crate::window::WindowId; /// The handler of the application events. -pub trait ApplicationHandler { +pub trait ApplicationHandler { /// Emitted when new events arrive from the OS to be processed. /// /// This is a useful place to put code that should be done before you start processing @@ -82,11 +82,95 @@ pub trait ApplicationHandler { /// [`Suspended`]: Self::suspended fn resumed(&mut self, event_loop: &ActiveEventLoop); - /// Emitted when an event is sent from [`EventLoopProxy::send_event`]. - /// - /// [`EventLoopProxy::send_event`]: crate::event_loop::EventLoopProxy::send_event - fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { - let _ = (event_loop, event); + /// Called after a wake up is requested using [`EventLoopProxy::wake_up()`]. + /// + /// Multiple calls to the aforementioned method will be merged, and will only wake the event + /// loop once; however, due to the nature of multi-threading some wake ups may appear + /// spuriously. For these reasons, you should not rely on the number of times that this was + /// called. + /// + /// The order in which this is emitted in relation to other events is not guaranteed. The time + /// at which this will be emitted is not guaranteed, only that it will happen "soon". That is, + /// there may be several executions of the event loop, including multiple redraws to windows, + /// between [`EventLoopProxy::wake_up()`] being called and the event being delivered. + /// + /// [`EventLoopProxy::wake_up()`]: crate::event_loop::EventLoopProxy::wake_up + /// + /// # Example + /// + /// Use a [`std::sync::mpsc`] channel to handle events from a different thread. + /// + /// ```no_run + /// use std::sync::mpsc; + /// use std::thread; + /// use std::time::Duration; + /// + /// use winit::application::ApplicationHandler; + /// use winit::event_loop::{ActiveEventLoop, EventLoop}; + /// + /// struct MyApp { + /// receiver: mpsc::Receiver, + /// } + /// + /// impl ApplicationHandler for MyApp { + /// # fn window_event( + /// # &mut self, + /// # _event_loop: &ActiveEventLoop, + /// # _window_id: winit::window::WindowId, + /// # _event: winit::event::WindowEvent, + /// # ) { + /// # } + /// # + /// # fn resumed(&mut self, _event_loop: &ActiveEventLoop) {} + /// # + /// fn proxy_wake_up(&mut self, _event_loop: &ActiveEventLoop) { + /// // Iterate current events, since wake-ups may have been merged. + /// // + /// // Note: We take care not to use `recv` or `iter` here, as those are blocking, + /// // and that would be bad for performance and might lead to a deadlock. + /// for i in self.receiver.try_iter() { + /// println!("received: {i}"); + /// } + /// } + /// + /// // Rest of `ApplicationHandler` + /// } + /// + /// fn main() -> Result<(), Box> { + /// let event_loop = EventLoop::new()?; + /// + /// let (sender, receiver) = mpsc::channel(); + /// + /// let mut app = MyApp { receiver }; + /// + /// // Send an event in a loop + /// let proxy = event_loop.create_proxy(); + /// let background_thread = thread::spawn(move || { + /// let mut i = 0; + /// loop { + /// println!("sending: {i}"); + /// if sender.send(i).is_err() { + /// // Stop sending once `MyApp` is dropped + /// break; + /// } + /// // Trigger the wake-up _after_ we placed the event in the channel. + /// // Otherwise, `proxy_wake_up` might be triggered prematurely. + /// proxy.wake_up(); + /// i += 1; + /// thread::sleep(Duration::from_secs(1)); + /// } + /// }); + /// + /// event_loop.run_app(&mut app)?; + /// + /// drop(app); + /// background_thread.join().unwrap(); + /// + /// Ok(()) + /// } + /// ``` + fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) { + let _ = event_loop; } /// Emitted when the OS sends an event to a winit window. @@ -224,7 +308,7 @@ pub trait ApplicationHandler { } } -impl, T: 'static> ApplicationHandler for &mut A { +impl ApplicationHandler for &mut A { #[inline] fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { (**self).new_events(event_loop, cause); @@ -236,8 +320,8 @@ impl, T: 'static> ApplicationHandler for &m } #[inline] - fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { - (**self).user_event(event_loop, event); + fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) { + (**self).proxy_wake_up(event_loop); } #[inline] @@ -281,7 +365,7 @@ impl, T: 'static> ApplicationHandler for &m } } -impl, T: 'static> ApplicationHandler for Box { +impl ApplicationHandler for Box { #[inline] fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { (**self).new_events(event_loop, cause); @@ -293,8 +377,8 @@ impl, T: 'static> ApplicationHandler for Bo } #[inline] - fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { - (**self).user_event(event_loop, event); + fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) { + (**self).proxy_wake_up(event_loop); } #[inline] diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 83af3409a4..55fdec30a0 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -44,9 +44,18 @@ changelog entry. - On Web, let events wake up event loop immediately when using `ControlFlow::Poll`. - Bump MSRV from `1.70` to `1.73`. +- Changed `ApplicationHandler::user_event` to `user_wake_up`, removing the + generic user event. + + Winit will now only indicate that wake up happened, you will have to pair + this with an external mechanism like `std::sync::mpsc::channel` if you want + to send specific data to be processed on the main thread. +- Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now + only wakes up the loop. ### Removed - Remove `EventLoop::run`. - Remove `EventLoopExtRunOnDemand::run_on_demand`. - Remove `EventLoopExtPumpEvents::pump_events`. +- Remove `Event`. diff --git a/src/changelog/v0.30.md b/src/changelog/v0.30.md index 185b388b25..9a9f45e75e 100644 --- a/src/changelog/v0.30.md +++ b/src/changelog/v0.30.md @@ -148,7 +148,7 @@ we move particular `match event` arms into methods on `ApplicationHandler`, for example: - ```rust,no_run + ```rust,no_run,ignore use winit::application::ApplicationHandler; use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId}; use winit::event_loop::{EventLoop, ActiveEventLoop}; @@ -211,7 +211,7 @@ Using the migration example from above, you can change your code as follows: - ```rust,no_run + ```rust,no_run,ignore use winit::application::ApplicationHandler; use winit::event::{Event, WindowEvent, DeviceEvent, DeviceId}; use winit::event_loop::{EventLoop, ActiveEventLoop}; diff --git a/src/event.rs b/src/event.rs index 30a71d6514..22d31eca48 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,4 +1,4 @@ -//! The [`Event`] enum and assorted supporting types. +//! The event enums and assorted supporting types. //! //! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get //! processed and used to modify the program state. For more details, see the root-level @@ -54,11 +54,15 @@ use crate::platform_impl; use crate::window::Window; use crate::window::{ActivationToken, Theme, WindowId}; +// TODO: Remove once the backends can call `ApplicationHandler` methods directly. For now backends +// like Windows and Web require `Event` to wire user events, otherwise each backend will have to +// wrap `Event` in some other structure. /// Describes a generic event. /// /// See the module-level docs for more information on the event loop manages each event. +#[allow(dead_code)] #[derive(Debug, Clone, PartialEq)] -pub enum Event { +pub(crate) enum Event { /// See [`ApplicationHandler::new_events`] for details. /// /// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events @@ -67,18 +71,15 @@ pub enum Event { /// See [`ApplicationHandler::window_event`] for details. /// /// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event + #[allow(clippy::enum_variant_names)] WindowEvent { window_id: WindowId, event: WindowEvent }, /// See [`ApplicationHandler::device_event`] for details. /// /// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event + #[allow(clippy::enum_variant_names)] DeviceEvent { device_id: DeviceId, event: DeviceEvent }, - /// See [`ApplicationHandler::user_event`] for details. - /// - /// [`ApplicationHandler::user_event`]: crate::application::ApplicationHandler::user_event - UserEvent(T), - /// See [`ApplicationHandler::suspended`] for details. /// /// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended @@ -103,24 +104,9 @@ pub enum Event { /// /// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning MemoryWarning, -} -impl Event { - #[allow(clippy::result_large_err)] - pub fn map_nonuser_event(self) -> Result, Event> { - use self::Event::*; - match self { - UserEvent(_) => Err(self), - WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }), - DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }), - NewEvents(cause) => Ok(NewEvents(cause)), - AboutToWait => Ok(AboutToWait), - LoopExiting => Ok(LoopExiting), - Suspended => Ok(Suspended), - Resumed => Ok(Resumed), - MemoryWarning => Ok(MemoryWarning), - } - } + /// User requested a wake up. + UserWakeUp, } /// Describes the reason the event loop is resuming. @@ -1032,7 +1018,6 @@ mod tests { // Mainline events. let wid = unsafe { WindowId::dummy() }; - x(UserEvent(())); x(NewEvents(event::StartCause::Init)); x(AboutToWait); x(LoopExiting); @@ -1116,25 +1101,12 @@ mod tests { #[allow(clippy::redundant_clone)] #[test] fn test_event_clone() { - foreach_event!(|event: event::Event<()>| { + foreach_event!(|event: event::Event| { let event2 = event.clone(); assert_eq!(event, event2); }) } - #[test] - fn test_map_nonuser_event() { - foreach_event!(|event: event::Event<()>| { - let is_user = matches!(event, event::Event::UserEvent(())); - let event2 = event.map_nonuser_event::<()>(); - if is_user { - assert_eq!(event2, Err(event::Event::UserEvent(()))); - } else { - assert!(event2.is_ok()); - } - }) - } - #[test] fn test_force_normalize() { let force = event::Force::Normalized(0.0); @@ -1155,7 +1127,7 @@ mod tests { #[allow(clippy::clone_on_copy)] #[test] fn ensure_attrs_do_not_panic() { - foreach_event!(|event: event::Event<()>| { + foreach_event!(|event: event::Event| { let _ = format!("{:?}", event); }); let _ = event::StartCause::Init.clone(); diff --git a/src/event_loop.rs b/src/event_loop.rs index 9ee4b48c6e..e658699f8e 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -3,15 +3,16 @@ //! //! If you want to send custom events to the event loop, use //! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its -//! [`send_event`][EventLoopProxy::send_event] method. +//! [`wake_up`][EventLoopProxy::wake_up] method. Then during handling the wake up +//! you can poll your event sources. //! //! See the root-level documentation for information on how to create and use an event loop to //! handle events. +use std::fmt; use std::marker::PhantomData; #[cfg(any(x11_platform, wayland_platform))] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::{error, fmt}; #[cfg(not(web_platform))] use std::time::{Duration, Instant}; @@ -39,8 +40,8 @@ use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes}; /// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread. /// /// [`Window`]: crate::window::Window -pub struct EventLoop { - pub(crate) event_loop: platform_impl::EventLoop, +pub struct EventLoop { + pub(crate) event_loop: platform_impl::EventLoop, pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync } @@ -58,16 +59,15 @@ pub struct ActiveEventLoop { /// This is used to make specifying options that affect the whole application /// easier. But note that constructing multiple event loops is not supported. /// -/// This can be created using [`EventLoop::new`] or [`EventLoop::with_user_event`]. +/// This can be created using [`EventLoop::builder`]. #[derive(Default)] -pub struct EventLoopBuilder { +pub struct EventLoopBuilder { pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, - _p: PhantomData, } static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); -impl EventLoopBuilder<()> { +impl EventLoopBuilder { /// Start building a new event loop. #[inline] #[deprecated = "use `EventLoop::builder` instead"] @@ -76,7 +76,7 @@ impl EventLoopBuilder<()> { } } -impl EventLoopBuilder { +impl EventLoopBuilder { /// Builds a new event loop. /// /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, @@ -111,7 +111,7 @@ impl EventLoopBuilder { doc = "[`.with_android_app(app)`]: #only-available-on-android" )] #[inline] - pub fn build(&mut self) -> Result, EventLoopError> { + pub fn build(&mut self) -> Result { let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered(); if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { @@ -132,7 +132,7 @@ impl EventLoopBuilder { } } -impl fmt::Debug for EventLoop { +impl fmt::Debug for EventLoop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("EventLoop { .. }") } @@ -146,12 +146,12 @@ impl fmt::Debug for ActiveEventLoop { /// Set through [`ActiveEventLoop::set_control_flow()`]. /// -/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted. +/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called. /// /// Defaults to [`Wait`]. /// /// [`Wait`]: Self::Wait -/// [`Event::AboutToWait`]: crate::event::Event::AboutToWait +/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum ControlFlow { /// When the current loop iteration finishes, immediately begin a new iteration regardless of @@ -189,12 +189,12 @@ impl ControlFlow { } } -impl EventLoop<()> { +impl EventLoop { /// Create the event loop. /// /// This is an alias of `EventLoop::builder().build()`. #[inline] - pub fn new() -> Result, EventLoopError> { + pub fn new() -> Result { Self::builder().build() } @@ -204,18 +204,12 @@ impl EventLoop<()> { /// /// To get the actual event loop, call [`build`][EventLoopBuilder::build] on that. #[inline] - pub fn builder() -> EventLoopBuilder<()> { - Self::with_user_event() + pub fn builder() -> EventLoopBuilder { + EventLoopBuilder { platform_specific: Default::default() } } } -impl EventLoop { - /// Start building a new event loop, with the given type as the user event - /// type. - pub fn with_user_event() -> EventLoopBuilder { - EventLoopBuilder { platform_specific: Default::default(), _p: PhantomData } - } - +impl EventLoop { /// Run the application with the event loop on the calling thread. /// /// See the [`set_control_flow()`] docs on how to change the event loop's behavior. @@ -246,13 +240,13 @@ impl EventLoop { /// [^1]: `EventLoopExtWebSys::spawn_app()` is only available on Web. #[inline] #[cfg(not(all(web_platform, target_feature = "exception-handling")))] - pub fn run_app>(self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(self, app: &mut A) -> Result<(), EventLoopError> { self.event_loop.run_app(app) } /// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// to the main event loop, possibly from another thread. - pub fn create_proxy(&self) -> EventLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { event_loop_proxy: self.event_loop.create_proxy() } } @@ -308,14 +302,14 @@ impl EventLoop { } #[cfg(feature = "rwh_06")] -impl rwh_06::HasDisplayHandle for EventLoop { +impl rwh_06::HasDisplayHandle for EventLoop { fn display_handle(&self) -> Result, rwh_06::HandleError> { rwh_06::HasDisplayHandle::display_handle(self.event_loop.window_target()) } } #[cfg(feature = "rwh_05")] -unsafe impl rwh_05::HasRawDisplayHandle for EventLoop { +unsafe impl rwh_05::HasRawDisplayHandle for EventLoop { /// Returns a [`rwh_05::RawDisplayHandle`] for the event loop. fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle { rwh_05::HasRawDisplayHandle::raw_display_handle(self.event_loop.window_target()) @@ -323,7 +317,7 @@ unsafe impl rwh_05::HasRawDisplayHandle for EventLoop { } #[cfg(any(x11_platform, wayland_platform))] -impl AsFd for EventLoop { +impl AsFd for EventLoop { /// Get the underlying [EventLoop]'s `fd` which you can register /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// loop must be polled with the [`pump_app_events`] API. @@ -337,7 +331,7 @@ impl AsFd for EventLoop { } #[cfg(any(x11_platform, wayland_platform))] -impl AsRawFd for EventLoop { +impl AsRawFd for EventLoop { /// Get the underlying [EventLoop]'s raw `fd` which you can register /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// loop must be polled with the [`pump_app_events`] API. @@ -434,7 +428,7 @@ impl ActiveEventLoop { /// This exits the event loop. /// - /// See [`LoopExiting`][crate::event::Event::LoopExiting]. + /// See [`exiting`][crate::application::ApplicationHandler::exiting]. pub fn exit(&self) { let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered(); @@ -519,53 +513,39 @@ unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle { } } -/// Used to send custom events to [`EventLoop`]. -pub struct EventLoopProxy { - event_loop_proxy: platform_impl::EventLoopProxy, -} - -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - Self { event_loop_proxy: self.event_loop_proxy.clone() } - } +/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly. +#[derive(Clone)] +pub struct EventLoopProxy { + event_loop_proxy: platform_impl::EventLoopProxy, } -impl EventLoopProxy { - /// Send an event to the [`EventLoop`] from which this proxy was created. This emits a - /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this - /// function. +impl EventLoopProxy { + /// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being + /// called. /// - /// Returns an `Err` if the associated [`EventLoop`] no longer exists. + /// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the + /// documentation on that for details. /// - /// [`UserEvent(event)`]: Event::UserEvent - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered(); - - self.event_loop_proxy.send_event(event) + /// If the event loop is no longer running, this is a no-op. + /// + /// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up + /// + /// # Platform-specific + /// + /// - **Windows**: The wake-up may be ignored under high contention, see [#3687]. + /// + /// [#3687]: https://github.com/rust-windowing/winit/pull/3687 + pub fn wake_up(&self) { + self.event_loop_proxy.wake_up(); } } -impl fmt::Debug for EventLoopProxy { +impl fmt::Debug for EventLoopProxy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("EventLoopProxy { .. }") } } -/// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that -/// no longer exists. -/// -/// Contains the original event given to [`EventLoopProxy::send_event`]. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventLoopClosed(pub T); - -impl fmt::Display for EventLoopClosed { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Tried to wake up a closed `EventLoop`") - } -} - -impl error::Error for EventLoopClosed {} - /// Control when device events are captured. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] pub enum DeviceEvents { diff --git a/src/lib.rs b/src/lib.rs index eb1bd68633..f93eeddc2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,12 +19,11 @@ //! window or a key getting pressed while the window is focused. Devices can generate //! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window. //! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a -//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if -//! desired. +//! [`DeviceEvent`]. //! //! You can retrieve events by calling [`EventLoop::run_app()`]. This function will //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and -//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`]. +//! will run until [`exit()`] is used, at which point [`exiting()`] is called. //! //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator`-based event loop //! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works @@ -163,7 +162,7 @@ //! [`WindowEvent`]: event::WindowEvent //! [`DeviceEvent`]: event::DeviceEvent //! [`Event::UserEvent`]: event::Event::UserEvent -//! [`Event::LoopExiting`]: event::Event::LoopExiting +//! [`exiting()`]: crate::application::ApplicationHandler::exiting //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle //! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle //! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland. diff --git a/src/platform/android.rs b/src/platform/android.rs index 4d4c591e06..46a46423fd 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -78,7 +78,7 @@ use self::activity::{AndroidApp, ConfigurationRef, Rect}; /// Additional methods on [`EventLoop`] that are specific to Android. pub trait EventLoopExtAndroid {} -impl EventLoopExtAndroid for EventLoop {} +impl EventLoopExtAndroid for EventLoop {} /// Additional methods on [`ActiveEventLoop`] that are specific to Android. pub trait ActiveEventLoopExtAndroid {} @@ -119,7 +119,7 @@ pub trait EventLoopBuilderExtAndroid { fn handle_volume_keys(&mut self) -> &mut Self; } -impl EventLoopBuilderExtAndroid for EventLoopBuilder { +impl EventLoopBuilderExtAndroid for EventLoopBuilder { fn with_android_app(&mut self, app: AndroidApp) -> &mut Self { self.platform_specific.android_app = Some(app); self diff --git a/src/platform/ios.rs b/src/platform/ios.rs index 59f5396163..6a3cd38334 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -76,7 +76,7 @@ pub trait EventLoopExtIOS { fn idiom(&self) -> Idiom; } -impl EventLoopExtIOS for EventLoop { +impl EventLoopExtIOS for EventLoop { fn idiom(&self) -> Idiom { self.event_loop.idiom() } diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 66f25c92b2..8938a5ca89 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -338,7 +338,7 @@ pub trait EventLoopBuilderExtMacOS { fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self; } -impl EventLoopBuilderExtMacOS for EventLoopBuilder { +impl EventLoopBuilderExtMacOS for EventLoopBuilder { #[inline] fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self { self.platform_specific.activation_policy = activation_policy; diff --git a/src/platform/pump_events.rs b/src/platform/pump_events.rs index 4140e8dbc5..7c711b567e 100644 --- a/src/platform/pump_events.rs +++ b/src/platform/pump_events.rs @@ -5,11 +5,6 @@ use crate::event_loop::EventLoop; /// Additional methods on [`EventLoop`] for pumping events within an external event loop pub trait EventLoopExtPumpEvents { - /// A type provided by the user that can be passed through [`Event::UserEvent`]. - /// - /// [`Event::UserEvent`]: crate::event::Event::UserEvent - type UserEvent: 'static; - /// Pump the `EventLoop` to check for and dispatch pending events. /// /// This API is designed to enable applications to integrate Winit into an @@ -104,17 +99,15 @@ pub trait EventLoopExtPumpEvents { /// If you render outside of Winit you are likely to see window resizing artifacts /// since MacOS expects applications to render synchronously during any `drawRect` /// callback. - fn pump_app_events>( + fn pump_app_events( &mut self, timeout: Option, app: &mut A, ) -> PumpStatus; } -impl EventLoopExtPumpEvents for EventLoop { - type UserEvent = T; - - fn pump_app_events>( +impl EventLoopExtPumpEvents for EventLoop { + fn pump_app_events( &mut self, timeout: Option, app: &mut A, diff --git a/src/platform/run_on_demand.rs b/src/platform/run_on_demand.rs index c80d4904a1..ffa38fb03c 100644 --- a/src/platform/run_on_demand.rs +++ b/src/platform/run_on_demand.rs @@ -7,11 +7,6 @@ use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window}; /// Additional methods on [`EventLoop`] to return control flow to the caller. pub trait EventLoopExtRunOnDemand { - /// A type provided by the user that can be passed through [`Event::UserEvent`]. - /// - /// [`Event::UserEvent`]: crate::event::Event::UserEvent - type UserEvent: 'static; - /// Run the application with the event loop on the calling thread. /// /// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) @@ -58,16 +53,14 @@ pub trait EventLoopExtRunOnDemand { /// /// [`exit()`]: ActiveEventLoop::exit() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() - fn run_app_on_demand>( + fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError>; } -impl EventLoopExtRunOnDemand for EventLoop { - type UserEvent = T; - - fn run_app_on_demand>( +impl EventLoopExtRunOnDemand for EventLoop { + fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError> { diff --git a/src/platform/wayland.rs b/src/platform/wayland.rs index db6a217dcb..28adf42ceb 100644 --- a/src/platform/wayland.rs +++ b/src/platform/wayland.rs @@ -38,7 +38,7 @@ pub trait EventLoopExtWayland { fn is_wayland(&self) -> bool; } -impl EventLoopExtWayland for EventLoop { +impl EventLoopExtWayland for EventLoop { #[inline] fn is_wayland(&self) -> bool { self.event_loop.is_wayland() @@ -57,7 +57,7 @@ pub trait EventLoopBuilderExtWayland { fn with_any_thread(&mut self, any_thread: bool) -> &mut Self; } -impl EventLoopBuilderExtWayland for EventLoopBuilder { +impl EventLoopBuilderExtWayland for EventLoopBuilder { #[inline] fn with_wayland(&mut self) -> &mut Self { self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::Wayland); diff --git a/src/platform/web.rs b/src/platform/web.rs index 261808269e..1e804f495b 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -155,11 +155,6 @@ impl WindowAttributesExtWebSys for WindowAttributes { /// Additional methods on `EventLoop` that are specific to the web. pub trait EventLoopExtWebSys { - /// A type provided by the user that can be passed through [`Event::UserEvent`]. - /// - /// [`Event::UserEvent`]: crate::event::Event::UserEvent - type UserEvent: 'static; - /// Initializes the winit event loop. /// /// Unlike @@ -182,7 +177,7 @@ pub trait EventLoopExtWebSys { doc = "[`run_app()`]: EventLoop::run_app()" )] /// [^1]: `run_app()` is _not_ available on WASM when the target supports `exception-handling`. - fn spawn_app + 'static>(self, app: A); + fn spawn_app(self, app: A); /// Sets the strategy for [`ControlFlow::Poll`]. /// @@ -213,10 +208,8 @@ pub trait EventLoopExtWebSys { fn wait_until_strategy(&self) -> WaitUntilStrategy; } -impl EventLoopExtWebSys for EventLoop { - type UserEvent = T; - - fn spawn_app + 'static>(self, app: A) { +impl EventLoopExtWebSys for EventLoop { + fn spawn_app(self, app: A) { self.event_loop.spawn_app(app); } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 5b79771a91..c1956bb7c9 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -227,7 +227,7 @@ pub trait EventLoopBuilderExtWindows { F: FnMut(*const c_void) -> bool + 'static; } -impl EventLoopBuilderExtWindows for EventLoopBuilder { +impl EventLoopBuilderExtWindows for EventLoopBuilder { #[inline] fn with_any_thread(&mut self, any_thread: bool) -> &mut Self { self.platform_specific.any_thread = any_thread; diff --git a/src/platform/x11.rs b/src/platform/x11.rs index 749c400ab0..663dc704b6 100644 --- a/src/platform/x11.rs +++ b/src/platform/x11.rs @@ -105,7 +105,7 @@ pub trait EventLoopExtX11 { fn is_x11(&self) -> bool; } -impl EventLoopExtX11 for EventLoop { +impl EventLoopExtX11 for EventLoop { #[inline] fn is_x11(&self) -> bool { !self.event_loop.is_wayland() @@ -124,7 +124,7 @@ pub trait EventLoopBuilderExtX11 { fn with_any_thread(&mut self, any_thread: bool) -> &mut Self; } -impl EventLoopBuilderExtX11 for EventLoopBuilder { +impl EventLoopBuilderExtX11 for EventLoopBuilder { #[inline] fn with_x11(&mut self) -> &mut Self { self.platform_specific.forced_backend = Some(crate::platform_impl::Backend::X); diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 35751766ce..363298f12f 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use std::hash::Hash; use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{mpsc, Arc, Mutex}; +use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction}; @@ -42,41 +42,6 @@ fn min_timeout(a: Option, b: Option) -> Option { a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))) } -struct PeekableReceiver { - recv: mpsc::Receiver, - first: Option, -} - -impl PeekableReceiver { - pub fn from_recv(recv: mpsc::Receiver) -> Self { - Self { recv, first: None } - } - - pub fn has_incoming(&mut self) -> bool { - if self.first.is_some() { - return true; - } - match self.recv.try_recv() { - Ok(v) => { - self.first = Some(v); - true - }, - Err(mpsc::TryRecvError::Empty) => false, - Err(mpsc::TryRecvError::Disconnected) => { - warn!("Channel was disconnected when checking incoming"); - false - }, - } - } - - pub fn try_recv(&mut self) -> Result { - if let Some(first) = self.first.take() { - return Ok(first); - } - self.recv.try_recv() - } -} - #[derive(Clone)] struct SharedFlagSetter { flag: Arc, @@ -132,13 +97,12 @@ impl RedrawRequester { #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra {} -pub struct EventLoop { +pub struct EventLoop { android_app: AndroidApp, window_target: event_loop::ActiveEventLoop, redraw_flag: SharedFlag, - user_events_sender: mpsc::Sender, - user_events_receiver: PeekableReceiver, // must wake looper whenever something gets sent - loop_running: bool, // Dispatched `NewEvents` + proxy_wake_up: Arc, + loop_running: bool, // Dispatched `NewEvents` running: bool, pending_redraw: bool, cause: StartCause, @@ -158,11 +122,11 @@ impl Default for PlatformSpecificEventLoopAttributes { } } -impl EventLoop { +impl EventLoop { pub(crate) fn new( attributes: &PlatformSpecificEventLoopAttributes, ) -> Result { - let (user_events_sender, user_events_receiver) = mpsc::channel(); + let proxy_wake_up = Arc::new(AtomicBool::new(false)); let android_app = attributes.android_app.as_ref().expect( "An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \ @@ -185,8 +149,7 @@ impl EventLoop { _marker: PhantomData, }, redraw_flag, - user_events_sender, - user_events_receiver: PeekableReceiver::from_recv(user_events_receiver), + proxy_wake_up, loop_running: false, running: false, pending_redraw: false, @@ -196,7 +159,7 @@ impl EventLoop { }) } - fn single_iteration>( + fn single_iteration( &mut self, main_event: Option>, app: &mut A, @@ -315,11 +278,8 @@ impl EventLoop { }, } - // Empty the user event buffer - { - while let Ok(event) = self.user_events_receiver.try_recv() { - app.user_event(self.window_target(), event); - } + if self.proxy_wake_up.swap(false, Ordering::Relaxed) { + app.proxy_wake_up(self.window_target()); } if self.running { @@ -351,7 +311,7 @@ impl EventLoop { self.pending_redraw = pending_redraw; } - fn handle_input_event>( + fn handle_input_event( &mut self, android_app: &AndroidApp, event: &InputEvent<'_>, @@ -458,11 +418,11 @@ impl EventLoop { input_status } - pub fn run_app>(mut self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(mut self, app: &mut A) -> Result<(), EventLoopError> { self.run_app_on_demand(app) } - pub fn run_app_on_demand>( + pub fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError> { @@ -481,7 +441,7 @@ impl EventLoop { } } - pub fn pump_app_events>( + pub fn pump_app_events( &mut self, timeout: Option, app: &mut A, @@ -515,7 +475,7 @@ impl EventLoop { } } - fn poll_events_with_timeout>( + fn poll_events_with_timeout( &mut self, mut timeout: Option, app: &mut A, @@ -524,22 +484,23 @@ impl EventLoop { self.pending_redraw |= self.redraw_flag.get_and_reset(); - timeout = - if self.running && (self.pending_redraw || self.user_events_receiver.has_incoming()) { - // If we already have work to do then we don't want to block on the next poll - Some(Duration::ZERO) - } else { - let control_flow_timeout = match self.control_flow() { - ControlFlow::Wait => None, - ControlFlow::Poll => Some(Duration::ZERO), - ControlFlow::WaitUntil(wait_deadline) => { - Some(wait_deadline.saturating_duration_since(start)) - }, - }; - - min_timeout(control_flow_timeout, timeout) + timeout = if self.running + && (self.pending_redraw || self.proxy_wake_up.load(Ordering::Relaxed)) + { + // If we already have work to do then we don't want to block on the next poll + Some(Duration::ZERO) + } else { + let control_flow_timeout = match self.control_flow() { + ControlFlow::Wait => None, + ControlFlow::Poll => Some(Duration::ZERO), + ControlFlow::WaitUntil(wait_deadline) => { + Some(wait_deadline.saturating_duration_since(start)) + }, }; + min_timeout(control_flow_timeout, timeout) + }; + let android_app = self.android_app.clone(); // Don't borrow self as part of poll expression android_app.poll_events(timeout, |poll_event| { let mut main_event = None; @@ -556,7 +517,7 @@ impl EventLoop { // We also ignore wake ups while suspended. self.pending_redraw |= self.redraw_flag.get_and_reset(); if !self.running - || (!self.pending_redraw && !self.user_events_receiver.has_incoming()) + || (!self.pending_redraw && !self.proxy_wake_up.load(Ordering::Relaxed)) { return; } @@ -590,9 +551,9 @@ impl EventLoop { &self.window_target } - pub fn create_proxy(&self) -> EventLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { - user_events_sender: self.user_events_sender.clone(), + proxy_wake_up: self.proxy_wake_up.clone(), waker: self.android_app.create_waker(), } } @@ -606,25 +567,16 @@ impl EventLoop { } } -pub struct EventLoopProxy { - user_events_sender: mpsc::Sender, +#[derive(Clone)] +pub struct EventLoopProxy { + proxy_wake_up: Arc, waker: AndroidAppWaker, } -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - EventLoopProxy { - user_events_sender: self.user_events_sender.clone(), - waker: self.waker.clone(), - } - } -} - -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed> { - self.user_events_sender.send(event).map_err(|err| event_loop::EventLoopClosed(err.0))?; +impl EventLoopProxy { + pub fn wake_up(&self) { + self.proxy_wake_up.store(true, Ordering::Relaxed); self.waker.wake(); - Ok(()) } } diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index f62fe5e70a..a7cd751edb 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -40,12 +40,9 @@ macro_rules! bug_assert { }; } -#[derive(Debug)] -pub(crate) struct HandlePendingUserEvents; - pub(crate) struct EventLoopHandler { #[allow(clippy::type_complexity)] - pub(crate) handler: Box, &RootActiveEventLoop)>, + pub(crate) handler: Box, pub(crate) event_loop: RootActiveEventLoop, } @@ -59,14 +56,14 @@ impl fmt::Debug for EventLoopHandler { } impl EventLoopHandler { - fn handle_event(&mut self, event: Event) { + fn handle_event(&mut self, event: Event) { (self.handler)(event, &self.event_loop) } } #[derive(Debug)] pub(crate) enum EventWrapper { - StaticEvent(Event), + StaticEvent(Event), ScaleFactorChanged(ScaleFactorChanged), } @@ -88,7 +85,7 @@ enum UserCallbackTransitionResult<'a> { }, } -impl Event { +impl Event { fn is_redraw(&self) -> bool { matches!(self, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. }) } @@ -625,7 +622,7 @@ fn handle_user_events(mtm: MainThreadMarker) { } drop(this); - handler.handle_event(Event::UserEvent(HandlePendingUserEvents)); + handler.handle_event(Event::UserWakeUp); loop { let mut this = AppState::get_mut(mtm); @@ -658,7 +655,7 @@ fn handle_user_events(mtm: MainThreadMarker) { } } - handler.handle_event(Event::UserEvent(HandlePendingUserEvents)); + handler.handle_event(Event::UserWakeUp); } } diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index bcf42a77ee..828087a2be 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -2,7 +2,8 @@ use std::collections::VecDeque; use std::ffi::{c_char, c_int, c_void}; use std::marker::PhantomData; use std::ptr::{self, NonNull}; -use std::sync::mpsc::{self, Receiver, Sender}; +use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; +use std::sync::Arc; use core_foundation::base::{CFIndex, CFRelease}; use core_foundation::runloop::{ @@ -19,11 +20,9 @@ use objc2_ui_kit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserI use crate::application::ApplicationHandler; use crate::error::EventLoopError; use crate::event::Event; -use crate::event_loop::{ - ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed, -}; +use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents}; use crate::platform::ios::Idiom; -use crate::platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents}; +use crate::platform_impl::platform::app_state::EventLoopHandler; use crate::window::{CustomCursor, CustomCursorSource}; use super::app_delegate::AppDelegate; @@ -109,10 +108,10 @@ impl OwnedDisplayHandle { } } -fn map_user_event>( +fn map_user_event( app: &mut A, - receiver: mpsc::Receiver, -) -> impl FnMut(Event, &RootActiveEventLoop) + '_ { + proxy_wake_up: Arc, +) -> impl FnMut(Event, &RootActiveEventLoop) + '_ { move |event, window_target| match event { Event::NewEvents(cause) => app.new_events(window_target, cause), Event::WindowEvent { window_id, event } => { @@ -121,9 +120,9 @@ fn map_user_event>( Event::DeviceEvent { device_id, event } => { app.device_event(window_target, device_id, event) }, - Event::UserEvent(_) => { - for event in receiver.try_iter() { - app.user_event(window_target, event); + Event::UserWakeUp => { + if proxy_wake_up.swap(false, AtomicOrdering::Relaxed) { + app.proxy_wake_up(window_target); } }, Event::Suspended => app.suspended(window_target), @@ -134,20 +133,19 @@ fn map_user_event>( } } -pub struct EventLoop { +pub struct EventLoop { mtm: MainThreadMarker, - sender: Sender, - receiver: Receiver, + proxy_wake_up: Arc, window_target: RootActiveEventLoop, } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes {} -impl EventLoop { +impl EventLoop { pub(crate) fn new( _: &PlatformSpecificEventLoopAttributes, - ) -> Result, EventLoopError> { + ) -> Result { let mtm = MainThreadMarker::new() .expect("On iOS, `EventLoop` must be created on the main thread"); @@ -160,20 +158,19 @@ impl EventLoop { SINGLETON_INIT = true; } - let (sender, receiver) = mpsc::channel(); - // this line sets up the main run loop before `UIApplicationMain` setup_control_flow_observers(); + let proxy_wake_up = Arc::new(AtomicBool::new(false)); + Ok(EventLoop { mtm, - sender, - receiver, + proxy_wake_up, window_target: RootActiveEventLoop { p: ActiveEventLoop { mtm }, _marker: PhantomData }, }) } - pub fn run_app>(self, app: &mut A) -> ! { + pub fn run_app(self, app: &mut A) -> ! { let application: Option> = unsafe { msg_send_id![UIApplication::class(), sharedApplication] }; assert!( @@ -183,12 +180,12 @@ impl EventLoop { `EventLoop::run_app` calls `UIApplicationMain` on iOS", ); - let handler = map_user_event(app, self.receiver); + let handler = map_user_event(app, self.proxy_wake_up.clone()); let handler = unsafe { std::mem::transmute::< - Box, &RootActiveEventLoop)>, - Box, &RootActiveEventLoop)>, + Box, + Box, >(Box::new(handler)) }; @@ -216,8 +213,8 @@ impl EventLoop { unreachable!() } - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy::new(self.sender.clone()) + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy::new(self.proxy_wake_up.clone()) } pub fn window_target(&self) -> &RootActiveEventLoop { @@ -226,7 +223,7 @@ impl EventLoop { } // EventLoopExtIOS -impl EventLoop { +impl EventLoop { pub fn idiom(&self) -> Idiom { match UIDevice::currentDevice(self.mtm).userInterfaceIdiom() { UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified, @@ -239,21 +236,21 @@ impl EventLoop { } } -pub struct EventLoopProxy { - sender: Sender, +pub struct EventLoopProxy { + proxy_wake_up: Arc, source: CFRunLoopSourceRef, } -unsafe impl Send for EventLoopProxy {} -unsafe impl Sync for EventLoopProxy {} +unsafe impl Send for EventLoopProxy {} +unsafe impl Sync for EventLoopProxy {} -impl Clone for EventLoopProxy { - fn clone(&self) -> EventLoopProxy { - EventLoopProxy::new(self.sender.clone()) +impl Clone for EventLoopProxy { + fn clone(&self) -> EventLoopProxy { + EventLoopProxy::new(self.proxy_wake_up.clone()) } } -impl Drop for EventLoopProxy { +impl Drop for EventLoopProxy { fn drop(&mut self) { unsafe { CFRunLoopSourceInvalidate(self.source); @@ -262,8 +259,8 @@ impl Drop for EventLoopProxy { } } -impl EventLoopProxy { - fn new(sender: Sender) -> EventLoopProxy { +impl EventLoopProxy { + fn new(proxy_wake_up: Arc) -> EventLoopProxy { unsafe { // just wake up the eventloop extern "C" fn event_loop_proxy_handler(_: *const c_void) {} @@ -287,19 +284,18 @@ impl EventLoopProxy { CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopWakeUp(rl); - EventLoopProxy { sender, source } + EventLoopProxy { proxy_wake_up, source } } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.sender.send(event).map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?; + pub fn wake_up(&self) { + self.proxy_wake_up.store(true, AtomicOrdering::Relaxed); unsafe { // let the main thread know there's a new event CFRunLoopSourceSignal(self.source); let rl = CFRunLoopGetMain(); CFRunLoopWakeUp(rl); } - Ok(()) } } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 5a61eb5562..64bae79298 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -21,7 +21,7 @@ use smol_str::SmolStr; use self::x11::{X11Error, XConnection, XError, XNotSupported}; use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError}; -use crate::event_loop::{AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed}; +use crate::event_loop::{AsyncRequestSerial, ControlFlow, DeviceEvents}; use crate::icon::Icon; use crate::keyboard::Key; #[cfg(x11_platform)] @@ -691,27 +691,22 @@ unsafe extern "C" fn x_error_callback( 0 } -pub enum EventLoop { +pub enum EventLoop { #[cfg(wayland_platform)] - Wayland(Box>), + Wayland(Box), #[cfg(x11_platform)] - X(x11::EventLoop), + X(x11::EventLoop), } -pub enum EventLoopProxy { +#[derive(Clone)] +pub enum EventLoopProxy { #[cfg(x11_platform)] - X(x11::EventLoopProxy), + X(x11::EventLoopProxy), #[cfg(wayland_platform)] - Wayland(wayland::EventLoopProxy), -} - -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.clone(); as EventLoopProxy) - } + Wayland(wayland::EventLoopProxy), } -impl EventLoop { +impl EventLoop { pub(crate) fn new( attributes: &PlatformSpecificEventLoopAttributes, ) -> Result { @@ -770,12 +765,12 @@ impl EventLoop { } #[cfg(wayland_platform)] - fn new_wayland_any_thread() -> Result, EventLoopError> { + fn new_wayland_any_thread() -> Result { wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp))) } #[cfg(x11_platform)] - fn new_x11_any_thread() -> Result, EventLoopError> { + fn new_x11_any_thread() -> Result { let xconn = match X11_BACKEND.lock().unwrap().as_ref() { Ok(xconn) => xconn.clone(), Err(_) => return Err(EventLoopError::NotSupported(NotSupportedError::new())), @@ -794,22 +789,22 @@ impl EventLoop { } } - pub fn create_proxy(&self) -> EventLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy) } - pub fn run_app>(self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(self, app: &mut A) -> Result<(), EventLoopError> { x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app)) } - pub fn run_app_on_demand>( + pub fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError> { x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app)) } - pub fn pump_app_events>( + pub fn pump_app_events( &mut self, timeout: Option, app: &mut A, @@ -822,21 +817,21 @@ impl EventLoop { } } -impl AsFd for EventLoop { +impl AsFd for EventLoop { fn as_fd(&self) -> BorrowedFd<'_> { x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_fd()) } } -impl AsRawFd for EventLoop { +impl AsRawFd for EventLoop { fn as_raw_fd(&self) -> RawFd { x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_raw_fd()) } } -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event)) +impl EventLoopProxy { + pub fn wake_up(&self) { + x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.wake_up()) } } diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 687d934e72..ab494a7da2 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -5,7 +5,6 @@ use std::io::Result as IOResult; use std::marker::PhantomData; use std::mem; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; -use std::rc::Rc; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -40,7 +39,7 @@ use super::{logical_to_physical_rounded, DeviceId, WaylandError, WindowId}; type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource, WinitState>; /// The Wayland event loop. -pub struct EventLoop { +pub struct EventLoop { /// Has `run` or `run_on_demand` been called or a call to `pump_events` that starts the loop loop_running: bool, @@ -48,13 +47,8 @@ pub struct EventLoop { compositor_updates: Vec, window_ids: Vec, - /// Sender of user events. - user_events_sender: calloop::channel::Sender, - - // XXX can't remove RefCell out of here, unless we can plumb generics into the `Window`, which - // we don't really want, since it'll break public API by a lot. - /// Pending events from the user. - pending_user_events: Rc>>, + /// Event loop proxy + event_loop_proxy: EventLoopProxy, /// The Wayland dispatcher to has raw access to the queue when needed, such as /// when creating a new window. @@ -71,8 +65,8 @@ pub struct EventLoop { event_loop: calloop::EventLoop<'static, WinitState>, } -impl EventLoop { - pub fn new() -> Result, EventLoopError> { +impl EventLoop { + pub fn new() -> Result { macro_rules! map_err { ($e:expr, $err:expr) => { $e.map_err(|error| os_error!($err(error).into())) @@ -115,16 +109,12 @@ impl EventLoop { )?; // Setup the user proxy. - let pending_user_events = Rc::new(RefCell::new(Vec::new())); - let pending_user_events_clone = pending_user_events.clone(); - let (user_events_sender, user_events_channel) = calloop::channel::channel(); + let (ping, ping_source) = calloop::ping::make_ping().unwrap(); let result = event_loop .handle() - .insert_source(user_events_channel, move |event, _, winit_state: &mut WinitState| { - if let calloop::channel::Event::Msg(msg) = event { - winit_state.dispatched_events = true; - pending_user_events_clone.borrow_mut().push(msg); - } + .insert_source(ping_source, move |_, _, winit_state: &mut WinitState| { + winit_state.dispatched_events = true; + winit_state.proxy_wake_up = true; }) .map_err(|error| error.error); map_err!(result, WaylandError::Calloop)?; @@ -162,8 +152,7 @@ impl EventLoop { window_ids: Vec::new(), connection, wayland_dispatcher, - user_events_sender, - pending_user_events, + event_loop_proxy: EventLoopProxy::new(ping), event_loop, window_target: RootActiveEventLoop { p: PlatformActiveEventLoop::Wayland(window_target), @@ -174,11 +163,11 @@ impl EventLoop { Ok(event_loop) } - pub fn run_app>(mut self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(mut self, app: &mut A) -> Result<(), EventLoopError> { self.run_app_on_demand(app) } - pub fn run_app_on_demand>( + pub fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError> { @@ -205,7 +194,7 @@ impl EventLoop { exit } - pub fn pump_app_events>( + pub fn pump_app_events( &mut self, timeout: Option, app: &mut A, @@ -233,7 +222,7 @@ impl EventLoop { } } - pub fn poll_events_with_timeout>( + pub fn poll_events_with_timeout( &mut self, mut timeout: Option, app: &mut A, @@ -302,7 +291,7 @@ impl EventLoop { self.single_iteration(app, cause); } - fn single_iteration>(&mut self, app: &mut A, cause: StartCause) { + fn single_iteration(&mut self, app: &mut A, cause: StartCause) { // NOTE currently just indented to simplify the diff // We retain these grow-only scratch buffers as part of the EventLoop @@ -321,10 +310,9 @@ impl EventLoop { app.resumed(&self.window_target); } - // Handle pending user events. We don't need back buffer, since we can't dispatch - // user events indirectly via callback to the user. - for user_event in self.pending_user_events.borrow_mut().drain(..) { - app.user_event(&self.window_target, user_event); + // Indicate user wake up. + if self.with_state(|state| mem::take(&mut state.proxy_wake_up)) { + app.proxy_wake_up(&self.window_target); } // Drain the pending compositor updates. @@ -525,8 +513,8 @@ impl EventLoop { } #[inline] - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy::new(self.user_events_sender.clone()) + pub fn create_proxy(&self) -> EventLoopProxy { + self.event_loop_proxy.clone() } #[inline] @@ -588,13 +576,13 @@ impl EventLoop { } } -impl AsFd for EventLoop { +impl AsFd for EventLoop { fn as_fd(&self) -> BorrowedFd<'_> { self.event_loop.as_fd() } } -impl AsRawFd for EventLoop { +impl AsRawFd for EventLoop { fn as_raw_fd(&self) -> RawFd { self.event_loop.as_raw_fd() } diff --git a/src/platform_impl/linux/wayland/event_loop/proxy.rs b/src/platform_impl/linux/wayland/event_loop/proxy.rs index 9dc7d99280..9fc2c58ee0 100644 --- a/src/platform_impl/linux/wayland/event_loop/proxy.rs +++ b/src/platform_impl/linux/wayland/event_loop/proxy.rs @@ -1,28 +1,19 @@ //! An event loop proxy. -use std::sync::mpsc::SendError; - -use sctk::reexports::calloop::channel::Sender; - -use crate::event_loop::EventLoopClosed; +use sctk::reexports::calloop::ping::Ping; /// A handle that can be sent across the threads and used to wake up the `EventLoop`. -pub struct EventLoopProxy { - user_events_sender: Sender, -} - -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - EventLoopProxy { user_events_sender: self.user_events_sender.clone() } - } +#[derive(Clone)] +pub struct EventLoopProxy { + ping: Ping, } -impl EventLoopProxy { - pub fn new(user_events_sender: Sender) -> Self { - Self { user_events_sender } +impl EventLoopProxy { + pub fn new(ping: Ping) -> Self { + Self { ping } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.user_events_sender.send(event).map_err(|SendError(error)| EventLoopClosed(error)) + pub fn wake_up(&self) { + self.ping.ping(); } } diff --git a/src/platform_impl/linux/wayland/event_loop/sink.rs b/src/platform_impl/linux/wayland/event_loop/sink.rs index e506b4a84f..ac3fb633c9 100644 --- a/src/platform_impl/linux/wayland/event_loop/sink.rs +++ b/src/platform_impl/linux/wayland/event_loop/sink.rs @@ -12,7 +12,7 @@ use super::{DeviceId, WindowId}; /// to the winit's user. #[derive(Default)] pub struct EventSink { - pub window_events: Vec>, + pub(crate) window_events: Vec, } impl EventSink { @@ -47,7 +47,7 @@ impl EventSink { } #[inline] - pub fn drain(&mut self) -> Drain<'_, Event<()>> { + pub(crate) fn drain(&mut self) -> Drain<'_, Event> { self.window_events.drain(..) } } diff --git a/src/platform_impl/linux/wayland/state.rs b/src/platform_impl/linux/wayland/state.rs index 8c021bb9fd..b7b1e498be 100644 --- a/src/platform_impl/linux/wayland/state.rs +++ b/src/platform_impl/linux/wayland/state.rs @@ -115,6 +115,9 @@ pub struct WinitState { /// Whether we have dispatched events to the user thus we want to /// send `AboutToWait` and normally wakeup the user. pub dispatched_events: bool, + + /// Whether the user initiated a wake up. + pub proxy_wake_up: bool, } impl WinitState { @@ -192,6 +195,7 @@ impl WinitState { loop_handle, // Make it true by default. dispatched_events: true, + proxy_wake_up: false, }) } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 9c6b95a2ac..424464ce77 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -72,9 +72,9 @@ pub struct EventProcessor { } impl EventProcessor { - pub fn process_event(&mut self, xev: &mut XEvent, mut callback: F) + pub(crate) fn process_event(&mut self, xev: &mut XEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { self.process_xevent(xev, &mut callback); @@ -139,9 +139,9 @@ impl EventProcessor { } } - fn process_xevent(&mut self, xev: &mut XEvent, mut callback: F) + fn process_xevent(&mut self, xev: &mut XEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let event_type = xev.get_type(); @@ -387,9 +387,9 @@ impl EventProcessor { } } - fn client_message(&mut self, xev: &XClientMessageEvent, mut callback: F) + fn client_message(&mut self, xev: &XClientMessageEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); let atoms = wt.xconn.atoms(); @@ -552,9 +552,9 @@ impl EventProcessor { } } - fn selection_notify(&mut self, xev: &XSelectionEvent, mut callback: F) + fn selection_notify(&mut self, xev: &XSelectionEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); let atoms = wt.xconn.atoms(); @@ -586,9 +586,9 @@ impl EventProcessor { } } - fn configure_notify(&self, xev: &XConfigureEvent, mut callback: F) + fn configure_notify(&self, xev: &XConfigureEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -781,9 +781,9 @@ impl EventProcessor { }); } - fn map_notify(&self, xev: &XMapEvent, mut callback: F) + fn map_notify(&self, xev: &XMapEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let window = xev.window as xproto::Window; let window_id = mkwid(window); @@ -799,9 +799,9 @@ impl EventProcessor { callback(&self.target, event); } - fn destroy_notify(&self, xev: &XDestroyWindowEvent, mut callback: F) + fn destroy_notify(&self, xev: &XDestroyWindowEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -823,9 +823,9 @@ impl EventProcessor { callback(&self.target, Event::WindowEvent { window_id, event: WindowEvent::Destroyed }); } - fn property_notify(&mut self, xev: &XPropertyEvent, mut callback: F) + fn property_notify(&mut self, xev: &XPropertyEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); let atoms = wt.x_connection().atoms(); @@ -838,9 +838,9 @@ impl EventProcessor { } } - fn visibility_notify(&self, xev: &XVisibilityEvent, mut callback: F) + fn visibility_notify(&self, xev: &XVisibilityEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let xwindow = xev.window as xproto::Window; @@ -855,9 +855,9 @@ impl EventProcessor { }); } - fn expose(&self, xev: &XExposeEvent, mut callback: F) + fn expose(&self, xev: &XExposeEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { // Multiple Expose events may be received for subareas of a window. // We issue `RedrawRequested` only for the last event of such a series. @@ -871,13 +871,9 @@ impl EventProcessor { } } - fn xinput_key_input( - &mut self, - xev: &mut XKeyEvent, - state: ElementState, - mut callback: F, - ) where - F: FnMut(&RootAEL, Event), + fn xinput_key_input(&mut self, xev: &mut XKeyEvent, state: ElementState, mut callback: F) + where + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -989,13 +985,13 @@ impl EventProcessor { } } - fn send_synthic_modifier_from_core( + fn send_synthic_modifier_from_core( &mut self, window_id: crate::window::WindowId, state: u16, mut callback: F, ) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let keymap = match self.xkb_context.keymap_mut() { Some(keymap) => keymap, @@ -1022,13 +1018,9 @@ impl EventProcessor { callback(&self.target, event); } - fn xinput2_button_input( - &self, - event: &XIDeviceEvent, - state: ElementState, - mut callback: F, - ) where - F: FnMut(&RootAEL, Event), + fn xinput2_button_input(&self, event: &XIDeviceEvent, state: ElementState, mut callback: F) + where + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); let window_id = mkwid(event.event as xproto::Window); @@ -1079,9 +1071,9 @@ impl EventProcessor { callback(&self.target, event); } - fn xinput2_mouse_motion(&self, event: &XIDeviceEvent, mut callback: F) + fn xinput2_mouse_motion(&self, event: &XIDeviceEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1157,9 +1149,9 @@ impl EventProcessor { } } - fn xinput2_mouse_enter(&self, event: &XIEnterEvent, mut callback: F) + fn xinput2_mouse_enter(&self, event: &XIEnterEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1202,9 +1194,9 @@ impl EventProcessor { } } - fn xinput2_mouse_left(&self, event: &XILeaveEvent, mut callback: F) + fn xinput2_mouse_left(&self, event: &XILeaveEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); let window = event.event as xproto::Window; @@ -1225,9 +1217,9 @@ impl EventProcessor { } } - fn xinput2_focused(&mut self, xev: &XIFocusInEvent, mut callback: F) + fn xinput2_focused(&mut self, xev: &XIFocusInEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); let window = xev.event as xproto::Window; @@ -1284,9 +1276,9 @@ impl EventProcessor { callback(&self.target, event); } - fn xinput2_unfocused(&mut self, xev: &XIFocusOutEvent, mut callback: F) + fn xinput2_unfocused(&mut self, xev: &XIFocusOutEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); let window = xev.event as xproto::Window; @@ -1336,13 +1328,9 @@ impl EventProcessor { } } - fn xinput2_touch( - &mut self, - xev: &XIDeviceEvent, - phase: TouchPhase, - mut callback: F, - ) where - F: FnMut(&RootAEL, Event), + fn xinput2_touch(&mut self, xev: &XIDeviceEvent, phase: TouchPhase, mut callback: F) + where + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1382,13 +1370,9 @@ impl EventProcessor { } } - fn xinput2_raw_button_input( - &self, - xev: &XIRawEvent, - state: ElementState, - mut callback: F, - ) where - F: FnMut(&RootAEL, Event), + fn xinput2_raw_button_input(&self, xev: &XIRawEvent, state: ElementState, mut callback: F) + where + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1404,9 +1388,9 @@ impl EventProcessor { } } - fn xinput2_raw_mouse_motion(&self, xev: &XIRawEvent, mut callback: F) + fn xinput2_raw_mouse_motion(&self, xev: &XIRawEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1464,13 +1448,9 @@ impl EventProcessor { } } - fn xinput2_raw_key_input( - &mut self, - xev: &XIRawEvent, - state: ElementState, - mut callback: F, - ) where - F: FnMut(&RootAEL, Event), + fn xinput2_raw_key_input(&mut self, xev: &XIRawEvent, state: ElementState, mut callback: F) + where + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1490,9 +1470,9 @@ impl EventProcessor { }); } - fn xinput2_hierarchy_changed(&mut self, xev: &XIHierarchyEvent, mut callback: F) + fn xinput2_hierarchy_changed(&mut self, xev: &XIHierarchyEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1517,9 +1497,9 @@ impl EventProcessor { } } - fn xkb_event(&mut self, xev: &XkbAnyEvent, mut callback: F) + fn xkb_event(&mut self, xev: &XkbAnyEvent, mut callback: F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); match xev.xkb_type { @@ -1596,14 +1576,14 @@ impl EventProcessor { } } - pub fn update_mods_from_xinput2_event( + pub(crate) fn update_mods_from_xinput2_event( &mut self, mods: &XIModifierState, group: &XIModifierState, force: bool, mut callback: F, ) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { if let Some(state) = self.xkb_context.state_mut() { state.update_modifiers( @@ -1627,12 +1607,9 @@ impl EventProcessor { } } - fn update_mods_from_query( - &mut self, - window_id: crate::window::WindowId, - mut callback: F, - ) where - F: FnMut(&RootAEL, Event), + fn update_mods_from_query(&mut self, window_id: crate::window::WindowId, mut callback: F) + where + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); @@ -1661,13 +1638,13 @@ impl EventProcessor { self.send_modifiers(window_id, mods.into(), true, &mut callback) } - pub fn update_mods_from_core_event( + pub(crate) fn update_mods_from_core_event( &mut self, window_id: crate::window::WindowId, state: u16, mut callback: F, ) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let xkb_mask = self.xkb_mod_mask_from_core(state); let xkb_state = match self.xkb_context.state_mut() { @@ -1740,7 +1717,7 @@ impl EventProcessor { /// /// The event won't be sent when the `modifiers` match the previously `sent` modifiers value, /// unless `force` is passed. The `force` should be passed when the active window changes. - fn send_modifiers)>( + fn send_modifiers( &self, window_id: crate::window::WindowId, modifiers: ModifiersState, @@ -1758,14 +1735,14 @@ impl EventProcessor { } } - fn handle_pressed_keys( + fn handle_pressed_keys( target: &RootAEL, window_id: crate::window::WindowId, state: ElementState, xkb_context: &mut Context, callback: &mut F, ) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); @@ -1800,9 +1777,9 @@ impl EventProcessor { } } - fn process_dpi_change(&self, callback: &mut F) + fn process_dpi_change(&self, callback: &mut F) where - F: FnMut(&RootAEL, Event), + F: FnMut(&RootAEL, Event), { let wt = Self::window_target(&self.target); wt.xconn.reload_database().expect("failed to reload Xft database"); diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 58d48b484e..b299cfb36b 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -9,7 +9,7 @@ use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; -use std::{fmt, ptr, slice, str}; +use std::{fmt, mem, ptr, slice, str}; use calloop::generic::Generic; use calloop::ping::Ping; @@ -28,7 +28,7 @@ use x11rb::xcb_ffi::ReplyOrIdError; use crate::application::ApplicationHandler; use crate::error::{EventLoopError, OsError as RootOsError}; use crate::event::{Event, StartCause, WindowEvent}; -use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed}; +use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents}; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::common::xkb::Context; use crate::platform_impl::platform::{min_timeout, WindowId}; @@ -81,12 +81,11 @@ impl Clone for WakeSender { } impl WakeSender { - pub fn send(&self, t: T) -> Result<(), EventLoopClosed> { - let res = self.sender.send(t).map_err(|e| EventLoopClosed(e.0)); + pub fn send(&self, t: T) { + let res = self.sender.send(t); if res.is_ok() { self.waker.ping(); } - res } } @@ -141,15 +140,13 @@ pub struct ActiveEventLoop { device_events: Cell, } -pub struct EventLoop { +pub struct EventLoop { loop_running: bool, event_loop: Loop<'static, EventLoopState>, - waker: calloop::ping::Ping, event_processor: EventProcessor, redraw_receiver: PeekableReceiver, - user_receiver: PeekableReceiver, activation_receiver: PeekableReceiver, - user_sender: Sender, + event_loop_proxy: EventLoopProxy, /// The current state of the event loop. state: EventLoopState, @@ -160,20 +157,13 @@ type ActivationToken = (WindowId, crate::event_loop::AsyncRequestSerial); struct EventLoopState { /// The latest readiness state for the x11 file descriptor x11_readiness: Readiness, -} - -pub struct EventLoopProxy { - user_sender: WakeSender, -} -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - EventLoopProxy { user_sender: self.user_sender.clone() } - } + /// User requested a wake up. + proxy_wake_up: bool, } -impl EventLoop { - pub(crate) fn new(xconn: Arc) -> EventLoop { +impl EventLoop { + pub(crate) fn new(xconn: Arc) -> EventLoop { let root = xconn.default_root().root; let atoms = xconn.atoms(); @@ -277,7 +267,16 @@ impl EventLoop { let (activation_token_sender, activation_token_channel) = mpsc::channel(); // Create a channel for sending user events. - let (user_sender, user_channel) = mpsc::channel(); + let (user_waker, user_waker_source) = + calloop::ping::make_ping().expect("Failed to create user event loop waker."); + event_loop + .handle() + .insert_source(user_waker_source, move |_, _, state| { + // No extra handling is required, we just need to wake-up. + state.proxy_wake_up = true; + }) + .expect("Failed to register the event loop waker source"); + let event_loop_proxy = EventLoopProxy::new(user_waker); let xkb_context = Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap(); @@ -358,31 +357,27 @@ impl EventLoop { EventLoop { loop_running: false, event_loop, - waker, event_processor, redraw_receiver: PeekableReceiver::from_recv(redraw_channel), activation_receiver: PeekableReceiver::from_recv(activation_token_channel), - user_receiver: PeekableReceiver::from_recv(user_channel), - user_sender, - state: EventLoopState { x11_readiness: Readiness::EMPTY }, + event_loop_proxy, + state: EventLoopState { x11_readiness: Readiness::EMPTY, proxy_wake_up: false }, } } - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - user_sender: WakeSender { sender: self.user_sender.clone(), waker: self.waker.clone() }, - } + pub fn create_proxy(&self) -> EventLoopProxy { + self.event_loop_proxy.clone() } pub(crate) fn window_target(&self) -> &RootAEL { &self.event_processor.target } - pub fn run_app>(mut self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(mut self, app: &mut A) -> Result<(), EventLoopError> { self.run_app_on_demand(app) } - pub fn run_app_on_demand>( + pub fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError> { @@ -412,7 +407,7 @@ impl EventLoop { exit } - pub fn pump_app_events>( + pub fn pump_app_events( &mut self, timeout: Option, app: &mut A, @@ -442,11 +437,11 @@ impl EventLoop { fn has_pending(&mut self) -> bool { self.event_processor.poll() - || self.user_receiver.has_incoming() + || self.state.proxy_wake_up || self.redraw_receiver.has_incoming() } - pub fn poll_events_with_timeout>( + pub fn poll_events_with_timeout( &mut self, mut timeout: Option, app: &mut A, @@ -511,7 +506,7 @@ impl EventLoop { self.single_iteration(app, cause); } - fn single_iteration>(&mut self, app: &mut A, cause: StartCause) { + fn single_iteration(&mut self, app: &mut A, cause: StartCause) { app.new_events(&self.event_processor.target, cause); // NB: For consistency all platforms must emit a 'resumed' event even though X11 @@ -546,10 +541,8 @@ impl EventLoop { } // Empty the user event buffer - { - while let Ok(event) = self.user_receiver.try_recv() { - app.user_event(&self.event_processor.target, event); - } + if mem::take(&mut self.state.proxy_wake_up) { + app.proxy_wake_up(&self.event_processor.target); } // Empty the redraw requests @@ -574,19 +567,19 @@ impl EventLoop { app.about_to_wait(&self.event_processor.target); } - fn drain_events>(&mut self, app: &mut A) { + fn drain_events(&mut self, app: &mut A) { let mut xev = MaybeUninit::uninit(); while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { let mut xev = unsafe { xev.assume_init() }; - self.event_processor.process_event(&mut xev, |window_target, event: Event| { + self.event_processor.process_event(&mut xev, |window_target, event: Event| { if let Event::WindowEvent { window_id: crate::window::WindowId(wid), event: WindowEvent::RedrawRequested, } = event { let window_target = EventProcessor::window_target(window_target); - window_target.redraw_sender.send(wid).unwrap(); + window_target.redraw_sender.send(wid); } else { match event { Event::WindowEvent { window_id, event } => { @@ -623,13 +616,13 @@ impl EventLoop { } } -impl AsFd for EventLoop { +impl AsFd for EventLoop { fn as_fd(&self) -> BorrowedFd<'_> { self.event_loop.as_fd() } } -impl AsRawFd for EventLoop { +impl AsRawFd for EventLoop { fn as_raw_fd(&self) -> RawFd { self.event_loop.as_raw_fd() } @@ -729,9 +722,9 @@ impl ActiveEventLoop { } } -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.user_sender.send(event).map_err(|e| EventLoopClosed(e.0)) +impl EventLoopProxy { + pub fn wake_up(&self) { + self.ping.ping(); } } @@ -815,6 +808,17 @@ impl Drop for Window { } } +#[derive(Clone)] +pub struct EventLoopProxy { + ping: Ping, +} + +impl EventLoopProxy { + fn new(ping: Ping) -> Self { + Self { ping } + } +} + /// Generic sum error type for X11 errors. #[derive(Debug)] pub enum X11Error { diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index ea7c02354a..b281b936f4 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -876,11 +876,11 @@ impl UnownedWindow { /// Refresh the API for the given monitor. #[inline] - pub(super) fn refresh_dpi_for_monitor( + pub(super) fn refresh_dpi_for_monitor( &self, new_monitor: &X11MonitorHandle, maybe_prev_scale_factor: Option, - mut callback: impl FnMut(Event), + mut callback: impl FnMut(Event), ) { // Check if the self is on this monitor let monitor = self.shared_state_lock().last_monitor.clone(); @@ -1805,9 +1805,7 @@ impl UnownedWindow { #[inline] pub fn request_activation_token(&self) -> Result { let serial = AsyncRequestSerial::get(); - self.activation_sender - .send((self.id(), serial)) - .expect("activation token channel should never be closed"); + self.activation_sender.send((self.id(), serial)); Ok(serial) } @@ -1818,7 +1816,7 @@ impl UnownedWindow { #[inline] pub fn request_redraw(&self) { - self.redraw_sender.send(WindowId(self.xwindow as _)).unwrap(); + self.redraw_sender.send(WindowId(self.xwindow as _)); } #[inline] diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 1f0eb2e827..84d9a4fa53 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -1,6 +1,8 @@ use std::cell::{Cell, RefCell}; use std::mem; use std::rc::Weak; +use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; +use std::sync::Arc; use std::time::Instant; use objc2::rc::Retained; @@ -24,6 +26,7 @@ pub(super) struct AppState { default_menu: bool, activate_ignoring_other_apps: bool, run_loop: RunLoop, + proxy_wake_up: Arc, event_handler: EventHandler, stop_on_launch: Cell, stop_before_wait: Cell, @@ -77,11 +80,13 @@ impl ApplicationDelegate { pub(super) fn new( mtm: MainThreadMarker, activation_policy: NSApplicationActivationPolicy, + proxy_wake_up: Arc, default_menu: bool, activate_ignoring_other_apps: bool, ) -> Retained { let this = mtm.alloc().set_ivars(AppState { activation_policy, + proxy_wake_up, default_menu, activate_ignoring_other_apps, run_loop: RunLoop::main(mtm), @@ -168,7 +173,7 @@ impl ApplicationDelegate { /// of the given closure. pub fn set_event_handler( &self, - handler: &mut dyn ApplicationHandler, + handler: &mut dyn ApplicationHandler, closure: impl FnOnce() -> R, ) -> R { self.ivars().event_handler.set(handler, closure) @@ -275,8 +280,7 @@ impl ApplicationDelegate { #[track_caller] pub fn maybe_queue_with_handler( &self, - callback: impl FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop) - + 'static, + callback: impl FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop) + 'static, ) { // Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.) // result in an event being queued, and applied at a later point. @@ -296,11 +300,9 @@ impl ApplicationDelegate { } #[track_caller] - fn with_handler< - F: FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop), - >( + fn with_handler( &self, - callback: F, + callback: impl FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop), ) { let event_loop = ActiveEventLoop::new_root(self.retain()); self.ivars().event_handler.handle(callback, &event_loop); @@ -361,7 +363,9 @@ impl ApplicationDelegate { return; } - self.with_handler(|app, event_loop| app.user_event(event_loop, HandlePendingUserEvents)); + if self.ivars().proxy_wake_up.swap(false, AtomicOrdering::Relaxed) { + self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop)); + } let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut()); for window_id in redraw { @@ -393,9 +397,6 @@ impl ApplicationDelegate { } } -#[derive(Debug)] -pub(crate) struct HandlePendingUserEvents; - /// Returns the minimum `Option`, taking into account that `None` /// equates to an infinite timeout, not a zero timeout (so can't just use /// `Option::min`) diff --git a/src/platform_impl/macos/event_handler.rs b/src/platform_impl/macos/event_handler.rs index b38d05890b..c8a33ad915 100644 --- a/src/platform_impl/macos/event_handler.rs +++ b/src/platform_impl/macos/event_handler.rs @@ -1,7 +1,6 @@ use std::cell::RefCell; use std::{fmt, mem}; -use super::app_state::HandlePendingUserEvents; use crate::application::ApplicationHandler; use crate::event_loop::ActiveEventLoop as RootActiveEventLoop; @@ -11,7 +10,7 @@ pub(crate) struct EventHandler { /// - Not registered by the event loop (None). /// - Present (Some(handler)). /// - Currently executing the handler / in use (RefCell borrowed). - inner: RefCell>>, + inner: RefCell>, } impl fmt::Debug for EventHandler { @@ -37,7 +36,7 @@ impl EventHandler { /// from within the closure. pub(crate) fn set<'handler, R>( &self, - app: &'handler mut dyn ApplicationHandler, + app: &'handler mut dyn ApplicationHandler, closure: impl FnOnce() -> R, ) -> R { // SAFETY: We extend the lifetime of the handler here so that we can @@ -48,8 +47,8 @@ impl EventHandler { // extended beyond `'handler`. let handler = unsafe { mem::transmute::< - &'handler mut dyn ApplicationHandler, - &'static mut dyn ApplicationHandler, + &'handler mut dyn ApplicationHandler, + &'static mut dyn ApplicationHandler, >(app) }; @@ -109,11 +108,9 @@ impl EventHandler { matches!(self.inner.try_borrow().as_deref(), Ok(Some(_))) } - pub(crate) fn handle< - F: FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop), - >( + pub(crate) fn handle( &self, - callback: F, + callback: impl FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop), event_loop: &RootActiveEventLoop, ) { match self.inner.try_borrow_mut().as_deref_mut() { diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 69caaafcbd..48de1cc92e 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -6,7 +6,8 @@ use std::os::raw::c_void; use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe}; use std::ptr; use std::rc::{Rc, Weak}; -use std::sync::mpsc; +use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; +use std::sync::Arc; use std::time::{Duration, Instant}; use core_foundation::base::{CFIndex, CFRelease}; @@ -21,15 +22,13 @@ use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow}; use objc2_foundation::{MainThreadMarker, NSObjectProtocol}; use super::app::WinitApplication; -use super::app_state::{ApplicationDelegate, HandlePendingUserEvents}; +use super::app_state::ApplicationDelegate; use super::event::dummy_event; use super::monitor::{self, MonitorHandle}; use super::observer::setup_control_flow_observers; use crate::application::ApplicationHandler; use crate::error::EventLoopError; -use crate::event_loop::{ - ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents, EventLoopClosed, -}; +use crate::event_loop::{ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents}; use crate::platform::macos::ActivationPolicy; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::platform::cursor::CustomCursor; @@ -155,82 +154,7 @@ impl ActiveEventLoop { } } -// Temporary helper, will be removed in https://github.com/rust-windowing/winit/pull/3694 -struct MapUserEvent { - app: A, - receiver: Rc>, -} - -impl, T: 'static> ApplicationHandler - for MapUserEvent -{ - #[inline] - fn new_events( - &mut self, - event_loop: &crate::event_loop::ActiveEventLoop, - cause: crate::event::StartCause, - ) { - self.app.new_events(event_loop, cause); - } - - #[inline] - fn resumed(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { - self.app.resumed(event_loop); - } - - #[inline] - fn user_event( - &mut self, - event_loop: &crate::event_loop::ActiveEventLoop, - _event: HandlePendingUserEvents, - ) { - for event in self.receiver.try_iter() { - self.app.user_event(event_loop, event); - } - } - - #[inline] - fn window_event( - &mut self, - event_loop: &crate::event_loop::ActiveEventLoop, - window_id: crate::window::WindowId, - event: crate::event::WindowEvent, - ) { - self.app.window_event(event_loop, window_id, event); - } - - #[inline] - fn device_event( - &mut self, - event_loop: &crate::event_loop::ActiveEventLoop, - device_id: crate::event::DeviceId, - event: crate::event::DeviceEvent, - ) { - self.app.device_event(event_loop, device_id, event); - } - - #[inline] - fn about_to_wait(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { - self.app.about_to_wait(event_loop); - } - - #[inline] - fn suspended(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { - self.app.suspended(event_loop); - } - - #[inline] - fn exiting(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { - self.app.exiting(event_loop); - } - - #[inline] - fn memory_warning(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { - self.app.memory_warning(event_loop); - } -} - -pub struct EventLoop { +pub struct EventLoop { /// Store a reference to the application for convenience. /// /// We intentionally don't store `WinitApplication` since we want to have @@ -242,9 +166,7 @@ pub struct EventLoop { /// keep it around here as well. delegate: Retained, - // Event sender and receiver, used for EventLoopProxy. - sender: mpsc::Sender, - receiver: Rc>, + proxy_wake_up: Arc, window_target: RootWindowTarget, panic_info: Rc, @@ -267,7 +189,7 @@ impl Default for PlatformSpecificEventLoopAttributes { } } -impl EventLoop { +impl EventLoop { pub(crate) fn new( attributes: &PlatformSpecificEventLoopAttributes, ) -> Result { @@ -289,9 +211,13 @@ impl EventLoop { ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory, ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited, }; + + let proxy_wake_up = Arc::new(AtomicBool::new(false)); + let delegate = ApplicationDelegate::new( mtm, activation_policy, + proxy_wake_up.clone(), attributes.default_menu, attributes.activate_ignoring_other_apps, ); @@ -303,16 +229,14 @@ impl EventLoop { let panic_info: Rc = Default::default(); setup_control_flow_observers(mtm, Rc::downgrade(&panic_info)); - let (sender, receiver) = mpsc::channel(); Ok(EventLoop { app, delegate: delegate.clone(), - sender, - receiver: Rc::new(receiver), window_target: RootWindowTarget { p: ActiveEventLoop { delegate, mtm }, _marker: PhantomData, }, + proxy_wake_up, panic_info, }) } @@ -321,7 +245,7 @@ impl EventLoop { &self.window_target } - pub fn run_app>(mut self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(mut self, app: &mut A) -> Result<(), EventLoopError> { self.run_app_on_demand(app) } @@ -329,13 +253,11 @@ impl EventLoop { // `pump_events` elegantly (we just ask to run the loop for a "short" amount of // time and so a layered implementation would end up using a lot of CPU due to // redundant wake ups. - pub fn run_app_on_demand>( + pub fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError> { - let mut handler = MapUserEvent { app, receiver: self.receiver.clone() }; - - self.delegate.set_event_handler(&mut handler, || { + self.delegate.set_event_handler(app, || { autoreleasepool(|_| { // clear / normalize pump_events state self.delegate.set_wait_timeout(None); @@ -368,14 +290,12 @@ impl EventLoop { Ok(()) } - pub fn pump_app_events>( + pub fn pump_app_events( &mut self, timeout: Option, app: &mut A, ) -> PumpStatus { - let mut handler = MapUserEvent { app, receiver: self.receiver.clone() }; - - self.delegate.set_event_handler(&mut handler, || { + self.delegate.set_event_handler(app, || { autoreleasepool(|_| { // As a special case, if the application hasn't been launched yet then we at least // run the loop until it has fully launched. @@ -439,8 +359,8 @@ impl EventLoop { }) } - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy::new(self.sender.clone()) + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy::new(self.proxy_wake_up.clone()) } } @@ -498,15 +418,15 @@ pub fn stop_app_on_panic R + UnwindSafe, R>( } } -pub struct EventLoopProxy { - sender: mpsc::Sender, +pub struct EventLoopProxy { + proxy_wake_up: Arc, source: CFRunLoopSourceRef, } -unsafe impl Send for EventLoopProxy {} -unsafe impl Sync for EventLoopProxy {} +unsafe impl Send for EventLoopProxy {} +unsafe impl Sync for EventLoopProxy {} -impl Drop for EventLoopProxy { +impl Drop for EventLoopProxy { fn drop(&mut self) { unsafe { CFRelease(self.source as _); @@ -514,14 +434,14 @@ impl Drop for EventLoopProxy { } } -impl Clone for EventLoopProxy { +impl Clone for EventLoopProxy { fn clone(&self) -> Self { - EventLoopProxy::new(self.sender.clone()) + EventLoopProxy::new(self.proxy_wake_up.clone()) } } -impl EventLoopProxy { - fn new(sender: mpsc::Sender) -> Self { +impl EventLoopProxy { + fn new(proxy_wake_up: Arc) -> Self { unsafe { // just wake up the eventloop extern "C" fn event_loop_proxy_handler(_: *const c_void) {} @@ -545,18 +465,17 @@ impl EventLoopProxy { CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopWakeUp(rl); - EventLoopProxy { sender, source } + EventLoopProxy { proxy_wake_up, source } } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.sender.send(event).map_err(|mpsc::SendError(x)| EventLoopClosed(x))?; + pub fn wake_up(&self) { + self.proxy_wake_up.store(true, AtomicOrdering::Relaxed); unsafe { // let the main thread know there's a new event CFRunLoopSourceSignal(self.source); let rl = CFRunLoopGetMain(); CFRunLoopWakeUp(rl); } - Ok(()) } } diff --git a/src/platform_impl/orbital/event_loop.rs b/src/platform_impl/orbital/event_loop.rs index 538920e31c..4ef1a79fa7 100644 --- a/src/platform_impl/orbital/event_loop.rs +++ b/src/platform_impl/orbital/event_loop.rs @@ -272,16 +272,18 @@ impl EventState { } } -pub struct EventLoop { +pub struct EventLoop { windows: Vec<(Arc, EventState)>, window_target: event_loop::ActiveEventLoop, - user_events_sender: mpsc::Sender, - user_events_receiver: mpsc::Receiver, + user_events_sender: mpsc::SyncSender<()>, + user_events_receiver: mpsc::Receiver<()>, } -impl EventLoop { +impl EventLoop { pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result { - let (user_events_sender, user_events_receiver) = mpsc::channel(); + // NOTE: Create a channel which can hold only one event to automatically _squash_ user + // events. + let (user_events_sender, user_events_receiver) = mpsc::sync_channel(1); let event_socket = Arc::new( RedoxSocket::event() @@ -323,7 +325,7 @@ impl EventLoop { }) } - fn process_event>( + fn process_event( window_id: WindowId, event_option: EventOption, event_state: &mut EventState, @@ -500,7 +502,7 @@ impl EventLoop { } } - pub fn run_app>(mut self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(mut self, app: &mut A) -> Result<(), EventLoopError> { let mut start_cause = StartCause::Init; loop { app.new_events(&self.window_target, start_cause); @@ -594,8 +596,8 @@ impl EventLoop { i += 1; } - while let Ok(event) = self.user_events_receiver.try_recv() { - app.user_event(&self.window_target, event); + while self.user_events_receiver.try_recv().is_ok() { + app.proxy_wake_up(&self.window_target); } // To avoid deadlocks the redraws lock is not held during event processing. @@ -683,7 +685,7 @@ impl EventLoop { &self.window_target } - pub fn create_proxy(&self) -> EventLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { user_events_sender: self.user_events_sender.clone(), wake_socket: self.window_target.p.wake_socket.clone(), @@ -691,24 +693,22 @@ impl EventLoop { } } -pub struct EventLoopProxy { - user_events_sender: mpsc::Sender, +pub struct EventLoopProxy { + user_events_sender: mpsc::SyncSender<()>, wake_socket: Arc, } -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed> { - self.user_events_sender - .send(event) - .map_err(|mpsc::SendError(x)| event_loop::EventLoopClosed(x))?; - - self.wake_socket.wake().unwrap(); - - Ok(()) +impl EventLoopProxy { + pub fn wake_up(&self) { + // When we fail to send the event it means that we haven't woken up to read the previous + // event. + if self.user_events_sender.try_send(()).is_ok() { + self.wake_socket.wake().unwrap(); + } } } -impl Clone for EventLoopProxy { +impl Clone for EventLoopProxy { fn clone(&self) -> Self { Self { user_events_sender: self.user_events_sender.clone(), @@ -717,7 +717,7 @@ impl Clone for EventLoopProxy { } } -impl Unpin for EventLoopProxy {} +impl Unpin for EventLoopProxy {} pub struct ActiveEventLoop { control_flow: Cell, diff --git a/src/platform_impl/web/async/waker.rs b/src/platform_impl/web/async/waker.rs index 40b316dc8d..a1a1515fc1 100644 --- a/src/platform_impl/web/async/waker.rs +++ b/src/platform_impl/web/async/waker.rs @@ -1,19 +1,18 @@ use std::future; -use std::num::NonZeroUsize; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::task::Poll; use super::super::main_thread::MainThreadMarker; use super::{AtomicWaker, Wrapper}; -pub struct WakerSpawner(Wrapper, Sender, NonZeroUsize>); +pub struct WakerSpawner(Wrapper, Sender, ()>); -pub struct Waker(Wrapper, Sender, NonZeroUsize>); +pub struct Waker(Wrapper, Sender, ()>); struct Handler { value: T, - handler: fn(&T, NonZeroUsize, bool), + handler: fn(&T, bool), } #[derive(Clone)] @@ -21,13 +20,9 @@ struct Sender(Arc); impl WakerSpawner { #[track_caller] - pub fn new( - main_thread: MainThreadMarker, - value: T, - handler: fn(&T, NonZeroUsize, bool), - ) -> Option { + pub fn new(main_thread: MainThreadMarker, value: T, handler: fn(&T, bool)) -> Option { let inner = Arc::new(Inner { - counter: AtomicUsize::new(0), + awoken: AtomicBool::new(false), waker: AtomicWaker::new(), closed: AtomicBool::new(false), }); @@ -39,49 +34,43 @@ impl WakerSpawner { let wrapper = Wrapper::new( main_thread, handler, - |handler, count| { + |handler, _| { let handler = handler.borrow(); let handler = handler.as_ref().unwrap(); - (handler.handler)(&handler.value, count, true); + (handler.handler)(&handler.value, true); }, { let inner = Arc::clone(&inner); move |handler| async move { - while let Some(count) = future::poll_fn(|cx| { - let count = inner.counter.swap(0, Ordering::Relaxed); - - match NonZeroUsize::new(count) { - Some(count) => Poll::Ready(Some(count)), - None => { - inner.waker.register(cx.waker()); - - let count = inner.counter.swap(0, Ordering::Relaxed); - - match NonZeroUsize::new(count) { - Some(count) => Poll::Ready(Some(count)), - None => { - if inner.closed.load(Ordering::Relaxed) { - return Poll::Ready(None); - } - - Poll::Pending - }, + while future::poll_fn(|cx| { + if inner.awoken.swap(false, Ordering::Relaxed) { + Poll::Ready(true) + } else { + inner.waker.register(cx.waker()); + + if inner.awoken.swap(false, Ordering::Relaxed) { + Poll::Ready(true) + } else { + if inner.closed.load(Ordering::Relaxed) { + return Poll::Ready(false); } - }, + + Poll::Pending + } } }) .await { let handler = handler.borrow(); let handler = handler.as_ref().unwrap(); - (handler.handler)(&handler.value, count, false); + (handler.handler)(&handler.value, false); } } }, sender, |inner, _| { - inner.0.counter.fetch_add(1, Ordering::Relaxed); + inner.0.awoken.store(true, Ordering::Relaxed); inner.0.waker.wake(); }, )?; @@ -93,13 +82,13 @@ impl WakerSpawner { Waker(self.0.clone()) } - pub fn fetch(&self) -> usize { + pub fn take(&self) -> bool { debug_assert!( MainThreadMarker::new().is_some(), "this should only be called from the main thread" ); - self.0.with_sender_data(|inner| inner.0.counter.swap(0, Ordering::Relaxed)) + self.0.with_sender_data(|inner| inner.0.awoken.swap(false, Ordering::Relaxed)) } } @@ -114,7 +103,7 @@ impl Drop for WakerSpawner { impl Waker { pub fn wake(&self) { - self.0.send(NonZeroUsize::MIN) + self.0.send(()) } } @@ -125,7 +114,7 @@ impl Clone for Waker { } struct Inner { - counter: AtomicUsize, + awoken: AtomicBool, waker: AtomicWaker, closed: AtomicBool, } diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index b13c2ee957..6e66e8e0b9 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -1,5 +1,4 @@ use std::marker::PhantomData; -use std::sync::mpsc::{self, Receiver, Sender}; use crate::application::ApplicationHandler; use crate::error::EventLoopError; @@ -17,36 +16,30 @@ mod window_target; pub(crate) use proxy::EventLoopProxy; pub(crate) use window_target::{ActiveEventLoop, OwnedDisplayHandle}; -pub struct EventLoop { +pub struct EventLoop { elw: RootActiveEventLoop, - user_event_sender: Sender, - user_event_receiver: Receiver, } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes {} -impl EventLoop { +impl EventLoop { pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result { - let (user_event_sender, user_event_receiver) = mpsc::channel(); let elw = RootActiveEventLoop { p: ActiveEventLoop::new(), _marker: PhantomData }; - Ok(EventLoop { elw, user_event_sender, user_event_receiver }) + Ok(EventLoop { elw }) } - pub fn run_app>(self, app: &mut A) -> ! { + pub fn run_app(self, app: &mut A) -> ! { let target = RootActiveEventLoop { p: self.elw.p.clone(), _marker: PhantomData }; // SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`. - let handler: Box)> = - Box::new(|event| handle_event(app, &target, &self.user_event_receiver, event)); + let handler: Box = Box::new(|event| handle_event(app, &target, event)); // SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe // because this function will never return and all resources not cleaned up by the point we // `throw` will leak, making this actually `'static`. let handler = unsafe { - std::mem::transmute::)>, Box) + 'static>>( - handler, - ) + std::mem::transmute::, Box>(handler) }; self.elw.p.run(handler, false); @@ -59,19 +52,14 @@ impl EventLoop { unreachable!(); } - pub fn spawn_app + 'static>(self, mut app: A) { + pub fn spawn_app(self, mut app: A) { let target = RootActiveEventLoop { p: self.elw.p.clone(), _marker: PhantomData }; - self.elw.p.run( - Box::new(move |event| { - handle_event(&mut app, &target, &self.user_event_receiver, event) - }), - true, - ); + self.elw.p.run(Box::new(move |event| handle_event(&mut app, &target, event)), true); } - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy::new(self.elw.p.waker(), self.user_event_sender.clone()) + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy::new(self.elw.p.waker()) } pub fn window_target(&self) -> &RootActiveEventLoop { @@ -95,21 +83,12 @@ impl EventLoop { } } -fn handle_event>( - app: &mut A, - target: &RootActiveEventLoop, - user_event_receiver: &Receiver, - event: Event<()>, -) { +fn handle_event(app: &mut A, target: &RootActiveEventLoop, event: Event) { match event { Event::NewEvents(cause) => app.new_events(target, cause), Event::WindowEvent { window_id, event } => app.window_event(target, window_id, event), Event::DeviceEvent { device_id, event } => app.device_event(target, device_id, event), - Event::UserEvent(_) => { - let event = - user_event_receiver.try_recv().expect("user event signaled but not received"); - app.user_event(target, event); - }, + Event::UserWakeUp => app.proxy_wake_up(target), Event::Suspended => app.suspended(target), Event::Resumed => app.resumed(target), Event::AboutToWait => app.about_to_wait(target), diff --git a/src/platform_impl/web/event_loop/proxy.rs b/src/platform_impl/web/event_loop/proxy.rs index bd8e714635..07cf10f128 100644 --- a/src/platform_impl/web/event_loop/proxy.rs +++ b/src/platform_impl/web/event_loop/proxy.rs @@ -1,29 +1,19 @@ use std::rc::Weak; -use std::sync::mpsc::{SendError, Sender}; use super::runner::Execution; -use crate::event_loop::EventLoopClosed; use crate::platform_impl::platform::r#async::Waker; -pub struct EventLoopProxy { +#[derive(Clone)] +pub struct EventLoopProxy { runner: Waker>, - sender: Sender, } -impl EventLoopProxy { - pub fn new(runner: Waker>, sender: Sender) -> Self { - Self { runner, sender } +impl EventLoopProxy { + pub fn new(runner: Waker>) -> Self { + Self { runner } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.sender.send(event).map_err(|SendError(event)| EventLoopClosed(event))?; + pub fn wake_up(&self) { self.runner.wake(); - Ok(()) - } -} - -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - Self { runner: self.runner.clone(), sender: self.sender.clone() } } } diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index 66028cc9fd..92a7d624b8 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -18,7 +18,6 @@ use js_sys::Function; use std::cell::{Cell, RefCell}; use std::collections::{HashSet, VecDeque}; use std::iter; -use std::num::NonZeroUsize; use std::ops::Deref; use std::rc::{Rc, Weak}; use wasm_bindgen::prelude::{wasm_bindgen, Closure}; @@ -28,7 +27,7 @@ use web_time::{Duration, Instant}; pub struct Shared(Rc); -pub(super) type EventHandler = dyn FnMut(Event<()>); +pub(super) type EventHandler = dyn FnMut(Event); impl Clone for Shared { fn clone(&self) -> Self { @@ -137,13 +136,12 @@ impl Shared { let document = window.document().expect("Failed to obtain document"); Shared(Rc::::new_cyclic(|weak| { - let proxy_spawner = - WakerSpawner::new(main_thread, weak.clone(), |runner, count, local| { - if let Some(runner) = runner.upgrade() { - Shared(runner).send_user_events(count, local) - } - }) - .expect("`EventLoop` has to be created in the main thread"); + let proxy_spawner = WakerSpawner::new(main_thread, weak.clone(), |runner, local| { + if let Some(runner) = runner.upgrade() { + Shared(runner).send_proxy_wake_up(local); + } + }) + .expect("`EventLoop` has to be created in the main thread"); Execution { main_thread, @@ -203,7 +201,7 @@ impl Shared { // Set the event callback to use for the event loop runner // This the event callback is a fairly thin layer over the user-provided callback that closes // over a RootActiveEventLoop reference - pub fn set_listener(&self, event_handler: Box) { + pub(crate) fn set_listener(&self, event_handler: Box) { { let mut runner = self.0.runner.borrow_mut(); assert!(matches!(*runner, RunnerEnum::Pending)); @@ -466,11 +464,11 @@ impl Shared { self.send_events(iter::once(event)); } - // Add a series of user events to the event loop runner + // Add a user event to the event loop runner. // // This will schedule the event loop to wake up instead of waking it up immediately if its not // running. - pub(crate) fn send_user_events(&self, count: NonZeroUsize, local: bool) { + pub(crate) fn send_proxy_wake_up(&self, local: bool) { // If the event loop is closed, it should discard any new events if self.is_closed() { return; @@ -492,9 +490,7 @@ impl Shared { let this = Rc::downgrade(&self.0); move || { if let Some(shared) = this.upgrade() { - Shared(shared).send_events( - iter::repeat(Event::UserEvent(())).take(count.get()), - ) + Shared(shared).send_event(Event::UserWakeUp) } } }) @@ -505,7 +501,7 @@ impl Shared { } } - self.send_events(iter::repeat(Event::UserEvent(())).take(count.get())) + self.send_event(Event::UserWakeUp); } // Add a series of events to the event loop runner @@ -648,8 +644,10 @@ impl Shared { // Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle. events.extend( - iter::repeat(Event::UserEvent(())) - .take(self.0.proxy_spawner.fetch()) + self.0 + .proxy_spawner + .take() + .then_some(Event::UserWakeUp) .map(EventWrapper::from), ); @@ -737,7 +735,7 @@ impl Shared { // * The `register_redraw_request` closure. // * The `destroy_fn` closure. if self.0.event_loop_recreation.get() { - crate::event_loop::EventLoopBuilder::<()>::allow_event_loop_recreation(); + crate::event_loop::EventLoopBuilder::allow_event_loop_recreation(); } } @@ -817,12 +815,12 @@ impl Shared { } pub(crate) enum EventWrapper { - Event(Event<()>), + Event(Event), ScaleChange { canvas: Weak>, size: PhysicalSize, scale: f64 }, } -impl From> for EventWrapper { - fn from(value: Event<()>) -> Self { +impl From for EventWrapper { + fn from(value: Event) -> Self { Self::Event(value) } } diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index e27d024051..69c081c2b0 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -55,7 +55,11 @@ impl ActiveEventLoop { Self { runner: runner::Shared::new(), modifiers: ModifiersShared::default() } } - pub fn run(&self, event_handler: Box, event_loop_recreation: bool) { + pub(crate) fn run( + &self, + event_handler: Box, + event_loop_recreation: bool, + ) { self.runner.event_loop_recreation(event_loop_recreation); self.runner.set_listener(event_handler); } diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 644ef76c8b..6f6524994d 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -483,7 +483,7 @@ impl Canvas { pub(crate) fn handle_scale_change( &self, runner: &super::super::event_loop::runner::Shared, - event_handler: impl FnOnce(crate::event::Event<()>), + event_handler: impl FnOnce(crate::event::Event), current_size: PhysicalSize, scale: f64, ) { diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 38cb5c54da..2aab2cdff8 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -25,7 +25,7 @@ pub struct FileDropHandlerData { pub interface: IDropTarget, refcount: AtomicUsize, window: HWND, - send_event: Box)>, + send_event: Box, cursor_effect: u32, hovered_is_valid: bool, /* If the currently hovered item is not valid there must not be any * `HoveredFileCancelled` emitted */ @@ -37,7 +37,7 @@ pub struct FileDropHandler { #[allow(non_snake_case)] impl FileDropHandler { - pub fn new(window: HWND, send_event: Box)>) -> FileDropHandler { + pub(crate) fn new(window: HWND, send_event: Box) -> FileDropHandler { let data = Box::new(FileDropHandlerData { interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl }, refcount: AtomicUsize::new(1), @@ -211,7 +211,7 @@ impl FileDropHandler { } impl FileDropHandlerData { - fn send_event(&self, event: Event<()>) { + fn send_event(&self, event: Event) { (self.send_event)(event); } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4bc6499c80..f4a02c230e 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -8,7 +8,6 @@ use std::ffi::c_void; use std::marker::PhantomData; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; -use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::{Arc, Mutex, MutexGuard}; use std::time::{Duration, Instant}; use std::{mem, panic, ptr}; @@ -63,7 +62,7 @@ use crate::error::EventLoopError; use crate::event::{ DeviceEvent, Event, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, WindowEvent, }; -use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed}; +use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents}; use crate::keyboard::ModifiersState; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::platform::dark_mode::try_theme; @@ -84,35 +83,14 @@ use crate::platform_impl::platform::{ use crate::window::{ CustomCursor as RootCustomCursor, CustomCursorSource, WindowId as RootWindowId, }; -use runner::{EventLoopRunner, EventLoopRunnerShared}; +use runner::EventLoopRunner; use super::window::set_skip_taskbar; use super::SelectedCursor; -/// some backends like macos uses an uninhabited `Never` type, -/// on windows, `UserEvent`s are also dispatched through the -/// WNDPROC callback, and due to the re-entrant nature of the -/// callback, recursively delivered events must be queued in a -/// buffer, the current implementation put this queue in -/// `EventLoopRunner`, which is shared between the event pumping -/// loop and the callback. because it's hard to decide from the -/// outside whether a event needs to be buffered, I decided not -/// use `Event` for the shared runner state, but use unit -/// as a placeholder so user events can be buffered as usual, -/// the real `UserEvent` is pulled from the mpsc channel directly -/// when the placeholder event is delivered to the event handler -pub(crate) struct UserEventPlaceholder; - -// here below, the generic `EventLoopRunnerShared` is replaced with -// `EventLoopRunnerShared` so we can get rid -// of the generic parameter T in types which don't depend on T. -// this is the approach which requires minimum changes to current -// backend implementation. it should be considered transitional -// and should be refactored and cleaned up eventually, I hope. - pub(crate) struct WindowData { pub window_state: Arc>, - pub event_loop_runner: EventLoopRunnerShared, + pub event_loop_runner: Rc, pub key_event_builder: KeyEventBuilder, pub _file_drop_handler: Option, pub userdata_removed: Cell, @@ -120,7 +98,7 @@ pub(crate) struct WindowData { } impl WindowData { - fn send_event(&self, event: Event) { + fn send_event(&self, event: Event) { self.event_loop_runner.send_event(event); } @@ -130,11 +108,11 @@ impl WindowData { } struct ThreadMsgTargetData { - event_loop_runner: EventLoopRunnerShared, + event_loop_runner: Rc, } impl ThreadMsgTargetData { - fn send_event(&self, event: Event) { + fn send_event(&self, event: Event) { self.event_loop_runner.send_event(event); } } @@ -146,9 +124,7 @@ pub(crate) enum ProcResult { Value(isize), } -pub struct EventLoop { - user_event_sender: Sender, - user_event_receiver: Receiver, +pub struct EventLoop { window_target: RootAEL, msg_hook: Option bool + 'static>>, } @@ -168,10 +144,10 @@ impl Default for PlatformSpecificEventLoopAttributes { pub struct ActiveEventLoop { thread_id: u32, thread_msg_target: HWND, - pub(crate) runner_shared: EventLoopRunnerShared, + pub(crate) runner_shared: Rc, } -impl EventLoop { +impl EventLoop { pub(crate) fn new( attributes: &mut PlatformSpecificEventLoopAttributes, ) -> Result { @@ -194,7 +170,6 @@ impl EventLoop { let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target)); - let (user_event_sender, user_event_receiver) = mpsc::channel(); insert_event_target_window_data(thread_msg_target, runner_shared.clone()); raw_input::register_all_mice_and_keyboards_for_raw_input( thread_msg_target, @@ -202,8 +177,6 @@ impl EventLoop { ); Ok(EventLoop { - user_event_sender, - user_event_receiver, window_target: RootAEL { p: ActiveEventLoop { thread_id, thread_msg_target, runner_shared }, _marker: PhantomData, @@ -216,11 +189,11 @@ impl EventLoop { &self.window_target } - pub fn run_app>(mut self, app: &mut A) -> Result<(), EventLoopError> { + pub fn run_app(mut self, app: &mut A) -> Result<(), EventLoopError> { self.run_app_on_demand(app) } - pub fn run_app_on_demand>( + pub fn run_app_on_demand( &mut self, app: &mut A, ) -> Result<(), EventLoopError> { @@ -228,38 +201,24 @@ impl EventLoop { let runner = &self.window_target.p.runner_shared; let event_loop_windows_ref = &self.window_target; - let user_event_receiver = &self.user_event_receiver; // # Safety // We make sure to call runner.clear_event_handler() before // returning unsafe { - runner.set_event_handler(move |event| { - match event { - Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause), - Event::WindowEvent { window_id, event } => { - app.window_event(event_loop_windows_ref, window_id, event) - }, - Event::DeviceEvent { device_id, event } => { - app.device_event(event_loop_windows_ref, device_id, event) - }, - // The shared `EventLoopRunner` is not parameterized - // `EventLoopProxy::send_event()` calls `PostMessage` - // to wakeup and dispatch a placeholder `UserEvent`, - // when we received the placeholder event here, the - // real UserEvent(T) should already be put in the - // mpsc channel and ready to be pulled - Event::UserEvent(_) => { - let event = user_event_receiver - .try_recv() - .expect("user event signaled but not received"); - app.user_event(event_loop_windows_ref, event); - }, - Event::Suspended => app.suspended(event_loop_windows_ref), - Event::Resumed => app.resumed(event_loop_windows_ref), - Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), - Event::LoopExiting => app.exiting(event_loop_windows_ref), - Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), - } + runner.set_event_handler(move |event| match event { + Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause), + Event::WindowEvent { window_id, event } => { + app.window_event(event_loop_windows_ref, window_id, event) + }, + Event::DeviceEvent { device_id, event } => { + app.device_event(event_loop_windows_ref, device_id, event) + }, + Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref), + Event::Suspended => app.suspended(event_loop_windows_ref), + Event::Resumed => app.resumed(event_loop_windows_ref), + Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), + Event::LoopExiting => app.exiting(event_loop_windows_ref), + Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), }); } } @@ -293,7 +252,7 @@ impl EventLoop { } } - pub fn pump_app_events>( + pub fn pump_app_events( &mut self, timeout: Option, app: &mut A, @@ -301,7 +260,7 @@ impl EventLoop { { let runner = &self.window_target.p.runner_shared; let event_loop_windows_ref = &self.window_target; - let user_event_receiver = &self.user_event_receiver; + // let user_event_receiver = &self.user_event_receiver; // # Safety // We make sure to call runner.clear_event_handler() before @@ -311,33 +270,20 @@ impl EventLoop { // to leave the runner in an unsound state with an associated // event handler. unsafe { - runner.set_event_handler(move |event| { - match event { - Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause), - Event::WindowEvent { window_id, event } => { - app.window_event(event_loop_windows_ref, window_id, event) - }, - Event::DeviceEvent { device_id, event } => { - app.device_event(event_loop_windows_ref, device_id, event) - }, - // The shared `EventLoopRunner` is not parameterized - // `EventLoopProxy::send_event()` calls `PostMessage` - // to wakeup and dispatch a placeholder `UserEvent`, - // when we received the placeholder event here, the - // real UserEvent(T) should already be put in the - // mpsc channel and ready to be pulled - Event::UserEvent(_) => { - let event = user_event_receiver - .try_recv() - .expect("user event signaled but not received"); - app.user_event(event_loop_windows_ref, event); - }, - Event::Suspended => app.suspended(event_loop_windows_ref), - Event::Resumed => app.resumed(event_loop_windows_ref), - Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), - Event::LoopExiting => app.exiting(event_loop_windows_ref), - Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), - } + runner.set_event_handler(move |event| match event { + Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause), + Event::WindowEvent { window_id, event } => { + app.window_event(event_loop_windows_ref, window_id, event) + }, + Event::DeviceEvent { device_id, event } => { + app.device_event(event_loop_windows_ref, device_id, event) + }, + Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref), + Event::Suspended => app.suspended(event_loop_windows_ref), + Event::Resumed => app.resumed(event_loop_windows_ref), + Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), + Event::LoopExiting => app.exiting(event_loop_windows_ref), + Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), }); runner.wakeup(); @@ -522,11 +468,8 @@ impl EventLoop { } } - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - target_window: self.window_target.p.thread_msg_target, - event_send: self.user_event_sender.clone(), - } + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { target_window: self.window_target.p.thread_msg_target } } fn exit_code(&self) -> Option { @@ -698,7 +641,7 @@ fn dur2timeout(dur: Duration) -> u32 { .unwrap_or(INFINITE) } -impl Drop for EventLoop { +impl Drop for EventLoop { fn drop(&mut self) { unsafe { DestroyWindow(self.window_target.p.thread_msg_target); @@ -756,27 +699,16 @@ impl EventLoopThreadExecutor { type ThreadExecFn = Box>; -pub struct EventLoopProxy { +#[derive(Clone)] +pub struct EventLoopProxy { target_window: HWND, - event_send: Sender, } -unsafe impl Send for EventLoopProxy {} -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - Self { target_window: self.target_window, event_send: self.event_send.clone() } - } -} +unsafe impl Send for EventLoopProxy {} -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.event_send - .send(event) - .map(|result| { - unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) }; - result - }) - .map_err(|e| EventLoopClosed(e.0)) +impl EventLoopProxy { + pub fn wake_up(&self) { + unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) }; } } @@ -908,7 +840,7 @@ fn create_event_target_window() -> HWND { fn insert_event_target_window_data( thread_msg_target: HWND, - event_loop_runner: EventLoopRunnerShared, + event_loop_runner: Rc, ) { let userdata = ThreadMsgTargetData { event_loop_runner }; let input_ptr = Box::into_raw(Box::new(userdata)); @@ -2454,7 +2386,7 @@ unsafe extern "system" fn thread_event_target_callback( // user event is still in the mpsc channel and will be pulled // once the placeholder event is delivered to the wrapper // `event_handler` - userdata.send_event(Event::UserEvent(UserEventPlaceholder)); + userdata.send_event(Event::UserWakeUp); 0 }, _ if msg == EXEC_MSG_ID.get() => { diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index ad6c801965..d350861538 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -1,7 +1,6 @@ use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; -use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::Instant; use std::{mem, panic}; @@ -16,11 +15,9 @@ use crate::window::WindowId; use super::ControlFlow; -pub(crate) type EventLoopRunnerShared = Rc>; +type EventHandler = Cell>>; -type EventHandler = Cell)>>>; - -pub(crate) struct EventLoopRunner { +pub(crate) struct EventLoopRunner { // The event loop's win32 handles pub(super) thread_msg_target: HWND, @@ -33,8 +30,8 @@ pub(crate) struct EventLoopRunner { exit: Cell>, runner_state: Cell, last_events_cleared: Cell, - event_handler: EventHandler, - event_buffer: RefCell>>, + event_handler: EventHandler, + event_buffer: RefCell>, panic_error: Cell>, } @@ -55,13 +52,13 @@ pub(crate) enum RunnerState { Destroyed, } -enum BufferedEvent { - Event(Event), +enum BufferedEvent { + Event(Event), ScaleFactorChanged(WindowId, f64, PhysicalSize), } -impl EventLoopRunner { - pub(crate) fn new(thread_msg_target: HWND) -> EventLoopRunner { +impl EventLoopRunner { + pub(crate) fn new(thread_msg_target: HWND) -> EventLoopRunner { EventLoopRunner { thread_msg_target, interrupt_msg_dispatch: Cell::new(false), @@ -88,13 +85,12 @@ impl EventLoopRunner { /// undefined behaviour. pub(crate) unsafe fn set_event_handler(&self, f: F) where - F: FnMut(Event), + F: FnMut(Event), { // Erase closure lifetime. // SAFETY: Caller upholds that the lifetime of the closure is upheld. - let f = unsafe { - mem::transmute::)>, Box)>>(Box::new(f)) - }; + let f = + unsafe { mem::transmute::, Box>(Box::new(f)) }; let old_event_handler = self.event_handler.replace(Some(f)); assert!(old_event_handler.is_none()); } @@ -124,7 +120,7 @@ impl EventLoopRunner { } /// State retrieval functions. -impl EventLoopRunner { +impl EventLoopRunner { #[allow(unused)] pub fn thread_msg_target(&self) -> HWND { self.thread_msg_target @@ -166,7 +162,7 @@ impl EventLoopRunner { } /// Misc. functions -impl EventLoopRunner { +impl EventLoopRunner { pub fn catch_unwind(&self, f: impl FnOnce() -> R) -> Option { let panic_error = self.panic_error.take(); if panic_error.is_none() { @@ -196,7 +192,7 @@ impl EventLoopRunner { } /// Event dispatch functions. -impl EventLoopRunner { +impl EventLoopRunner { pub(crate) fn prepare_wait(&self) { self.move_state_to(RunnerState::Idle); } @@ -205,7 +201,7 @@ impl EventLoopRunner { self.move_state_to(RunnerState::HandlingMainEvents); } - pub(crate) fn send_event(&self, event: Event) { + pub(crate) fn send_event(&self, event: Event) { if let Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } = event { self.call_event_handler(event); // As a rule, to ensure that `pump_events` can't block an external event loop @@ -226,7 +222,7 @@ impl EventLoopRunner { self.move_state_to(RunnerState::Destroyed); } - fn call_event_handler(&self, event: Event) { + fn call_event_handler(&self, event: Event) { self.catch_unwind(|| { let mut event_handler = self.event_handler.take().expect( "either event handler is re-entrant (likely), or no event handler is registered \ @@ -358,8 +354,8 @@ impl EventLoopRunner { } } -impl BufferedEvent { - pub fn from_event(event: Event) -> BufferedEvent { +impl BufferedEvent { + pub fn from_event(event: Event) -> BufferedEvent { match event { Event::WindowEvent { event: WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer }, @@ -373,7 +369,7 @@ impl BufferedEvent { } } - pub fn dispatch_event(self, dispatch: impl FnOnce(Event)) { + pub fn dispatch_event(self, dispatch: impl FnOnce(Event)) { match self { Self::Event(event) => dispatch(event), Self::ScaleFactorChanged(window_id, scale_factor, new_inner_size) => { diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 23fec32b34..403d62df31 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -1186,11 +1186,7 @@ impl<'a> InitData<'a> { let file_drop_runner = self.event_loop.runner_shared.clone(); let file_drop_handler = FileDropHandler::new( win.window, - Box::new(move |event| { - if let Ok(e) = event.map_nonuser_event() { - file_drop_runner.send_event(e) - } - }), + Box::new(move |event| file_drop_runner.send_event(event)), ); let handler_interface_ptr = diff --git a/tests/send_objects.rs b/tests/send_objects.rs index b8a5d3b73a..0d6cfb2dc0 100644 --- a/tests/send_objects.rs +++ b/tests/send_objects.rs @@ -3,16 +3,11 @@ fn needs_send() {} #[test] fn event_loop_proxy_send() { - #[allow(dead_code)] - fn is_send() { - // ensures that `winit::EventLoopProxy` implements `Send` - needs_send::>(); - } + needs_send::(); } #[test] fn window_send() { - // ensures that `winit::Window` implements `Send` needs_send::(); } @@ -23,7 +18,6 @@ fn window_builder_send() { #[test] fn ids_send() { - // ensures that the various `..Id` types implement `Send` needs_send::(); needs_send::(); needs_send::(); diff --git a/tests/sync_object.rs b/tests/sync_object.rs index c7abbeb208..47a78f42a5 100644 --- a/tests/sync_object.rs +++ b/tests/sync_object.rs @@ -3,16 +3,11 @@ fn needs_sync() {} #[test] fn event_loop_proxy_send() { - #[allow(dead_code)] - fn is_send() { - // ensures that `winit::EventLoopProxy` implements `Sync` - needs_sync::>(); - } + needs_sync::(); } #[test] fn window_sync() { - // ensures that `winit::Window` implements `Sync` needs_sync::(); }