From 9027d8f350f5587c7d93e3b7fcf38830013e1f55 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 29 May 2024 14:28:35 +0200 Subject: [PATCH] Verify event order When debug assertions are enabled, check that events are emitted in the expected order, and if not, log an error. --- examples/window.rs | 11 ++++- src/event_loop.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/examples/window.rs b/examples/window.rs index 3a4bd6151..93539452d 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -17,7 +17,9 @@ use softbuffer::{Context, Surface}; use winit::application::ApplicationHandler; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; -use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent}; +use winit::event::{ + DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, StartCause, WindowEvent, +}; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::keyboard::{Key, ModifiersState}; use winit::window::{ @@ -303,6 +305,12 @@ impl Application { } impl ApplicationHandler for Application { + fn new_events(&mut self, _event_loop: &ActiveEventLoop, start_cause: StartCause) { + if let StartCause::Init = start_cause { + info!("Started the event loop"); + } + } + fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) { info!("User event: {event:?}"); } @@ -320,6 +328,7 @@ impl ApplicationHandler for Application { match event { WindowEvent::Resized(size) => { + info!("Resized({size:?})"); window.resize(size); }, WindowEvent::Focused(focused) => { diff --git a/src/event_loop.rs b/src/event_loop.rs index 9ee4b48c6..5de987768 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -209,6 +209,110 @@ impl EventLoop<()> { } } +#[cfg(debug_assertions)] +pub(crate) fn ensure_event_order<'a, T: 'static>( + handler: impl ApplicationHandler + 'a, +) -> impl ApplicationHandler + 'a { + use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; + use crate::window::WindowId; + + #[derive(Default, Debug, PartialEq, Eq, Clone)] + enum State { + #[default] + NotRunning, + Suspended, + Running, + Waiting, + } + + impl State { + #[track_caller] + fn expect(&self, expected: State) { + if *self != expected { + tracing::error!("expected state to be {expected:?}, found {self:?}"); + } + } + + #[track_caller] + fn transition(&mut self, from: State, to: State) { + if *self != from { + tracing::error!( + "invalid state transition to {to:?}. Expected {from:?}, found {self:?}" + ); + } + *self = to; + } + } + + struct EnsureEventOrder { + inner: A, + state: State, + } + + impl, T: 'static> ApplicationHandler for EnsureEventOrder { + fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { + match cause { + StartCause::Init => self.state.transition(State::NotRunning, State::Suspended), + _ => self.state.transition(State::Waiting, State::Running), + } + + self.inner.new_events(event_loop, cause); + } + + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + self.state.transition(State::Suspended, State::Running); + self.inner.resumed(event_loop); + } + + fn suspended(&mut self, event_loop: &ActiveEventLoop) { + self.state.transition(State::Running, State::Suspended); + self.inner.suspended(event_loop); + } + + fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { + self.state.transition(State::Running, State::Waiting); + self.inner.about_to_wait(event_loop); + } + + fn exiting(&mut self, event_loop: &ActiveEventLoop) { + self.state.transition(State::Suspended, State::NotRunning); + self.inner.exiting(event_loop); + } + + fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { + self.state.expect(State::Running); + self.inner.user_event(event_loop, event); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: WindowEvent, + ) { + self.state.expect(State::Running); + self.inner.window_event(event_loop, window_id, event); + } + + fn device_event( + &mut self, + event_loop: &ActiveEventLoop, + device_id: DeviceId, + event: DeviceEvent, + ) { + self.state.expect(State::Running); + self.inner.device_event(event_loop, device_id, event); + } + + fn memory_warning(&mut self, event_loop: &ActiveEventLoop) { + // TODO: What states are allowed when receiving this? + self.inner.memory_warning(event_loop); + } + } + + EnsureEventOrder { inner: handler, state: State::NotRunning } +} + impl EventLoop { /// Start building a new event loop, with the given type as the user event /// type. @@ -247,6 +351,8 @@ impl EventLoop { #[inline] #[cfg(not(all(web_platform, target_feature = "exception-handling")))] pub fn run_app>(self, app: &mut A) -> Result<(), EventLoopError> { + #[cfg(debug_assertions)] + let app = &mut ensure_event_order(app); self.event_loop.run_app(app) }