Compare commits

...

2 Commits

Author SHA1 Message Date
Mads Marquart
89687656d6 Emit suspended on shutdown 2024-05-29 14:28:36 +02:00
Mads Marquart
9027d8f350 Verify event order
When debug assertions are enabled, check that events are emitted in the
expected order, and if not, log an error.
2024-05-29 14:28:35 +02:00
3 changed files with 117 additions and 1 deletions

View File

@@ -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<UserEvent> 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<UserEvent> for Application {
match event {
WindowEvent::Resized(size) => {
info!("Resized({size:?})");
window.resize(size);
},
WindowEvent::Focused(focused) => {

View File

@@ -209,6 +209,110 @@ impl EventLoop<()> {
}
}
#[cfg(debug_assertions)]
pub(crate) fn ensure_event_order<'a, T: 'static>(
handler: impl ApplicationHandler<T> + 'a,
) -> impl ApplicationHandler<T> + '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<A> {
inner: A,
state: State,
}
impl<A: ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for EnsureEventOrder<A> {
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<T> EventLoop<T> {
/// Start building a new event loop, with the given type as the user event
/// type.
@@ -247,6 +351,8 @@ impl<T> EventLoop<T> {
#[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
#[cfg(debug_assertions)]
let app = &mut ensure_event_order(app);
self.event_loop.run_app(app)
}

View File

@@ -193,6 +193,7 @@ impl ApplicationDelegate {
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
/// and we won't need to re-launch the app if subsequent EventLoops are run.
pub fn internal_exit(&self) {
self.handle_event(Event::Suspended);
self.handle_event(Event::LoopExiting);
self.set_is_running(false);