Skip to content

Commit

Permalink
event_loop: remove generic user event
Browse files Browse the repository at this point in the history
Let the users wake up the event loop and then they could poll their
user sources.

Co-authored-by: Mads Marquart <[email protected]>
Co-authored-by: daxpedda <[email protected]>
  • Loading branch information
3 people authored Jun 24, 2024
1 parent 7d12879 commit ecb887e
Show file tree
Hide file tree
Showing 44 changed files with 673 additions and 964 deletions.
2 changes: 1 addition & 1 deletion examples/child_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
18 changes: 6 additions & 12 deletions examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn main() -> Result<(), Box<dyn Error>> {

tracing::init();

let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
let event_loop = EventLoop::new()?;
let _event_loop_proxy = event_loop.create_proxy();

// Wire the user event from another thread.
Expand All @@ -54,7 +54,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// 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));
}
});
Expand All @@ -64,12 +64,6 @@ fn main() -> Result<(), Box<dyn Error>> {
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.
Expand All @@ -85,7 +79,7 @@ struct Application {
}

impl Application {
fn new<T>(event_loop: &EventLoop<T>) -> 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(
Expand Down Expand Up @@ -308,9 +302,9 @@ impl Application {
}
}

impl ApplicationHandler<UserEvent> 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(
Expand Down
108 changes: 96 additions & 12 deletions src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::event_loop::ActiveEventLoop;
use crate::window::WindowId;

/// The handler of the application events.
pub trait ApplicationHandler<T: 'static = ()> {
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
Expand Down Expand Up @@ -82,11 +82,95 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// [`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<u64>,
/// }
///
/// 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<dyn std::error::Error>> {
/// 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.
Expand Down Expand Up @@ -224,7 +308,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
}
}

impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &mut A {
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
Expand All @@ -236,8 +320,8 @@ impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> 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]
Expand Down Expand Up @@ -281,7 +365,7 @@ impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &m
}
}

impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Box<A> {
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
Expand All @@ -293,8 +377,8 @@ impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> 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]
Expand Down
9 changes: 9 additions & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
4 changes: 2 additions & 2 deletions src/changelog/v0.30.md
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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};
Expand Down
52 changes: 12 additions & 40 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<T: 'static> {
pub(crate) enum Event {
/// See [`ApplicationHandler::new_events`] for details.
///
/// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events
Expand All @@ -67,18 +71,15 @@ pub enum Event<T: 'static> {
/// 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
Expand All @@ -103,24 +104,9 @@ pub enum Event<T: 'static> {
///
/// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
MemoryWarning,
}

impl<T> Event<T> {
#[allow(clippy::result_large_err)]
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
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.
Expand Down Expand Up @@ -1032,7 +1018,6 @@ mod tests {

// Mainline events.
let wid = unsafe { WindowId::dummy() };
x(UserEvent(()));
x(NewEvents(event::StartCause::Init));
x(AboutToWait);
x(LoopExiting);
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand Down
Loading

0 comments on commit ecb887e

Please sign in to comment.