From bc0a66ab3199bff3da801d1de151d83e4ac3c98f Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Fri, 16 Jun 2023 19:43:54 +0200 Subject: [PATCH] examples: Implement proper `Event::Resumed` semantics On Android the backing buffer (`NativeWindow`) disappears when the application is not focussed and/or the screen is locked. Winit handles this by requiring apps to create their `raw_window_handle()` consumers _after_ `Event::Resumed` and to clean it up _before_ returning from `Event::Suspended`. For consistency Winit also sends `Resumed` on all other platforms during init. --- examples/utils/winit_app.rs | 72 ++++++++++++++++++++++++++----------- examples/winit.rs | 21 ++++++----- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/examples/utils/winit_app.rs b/examples/utils/winit_app.rs index f427894..7dc0f44 100644 --- a/examples/utils/winit_app.rs +++ b/examples/utils/winit_app.rs @@ -31,76 +31,95 @@ pub(crate) fn make_window( } /// Easily constructable winit application. -pub(crate) struct WinitApp { - /// Closure to initialize state. +pub(crate) struct WinitApp { + /// Closure to initialize `state`. init: Init, + /// Closure to initialize `surface_state`. + init_surface: InitSurface, + /// Closure to run on window events. event: Handler, /// Contained state. state: Option, + + /// + surface_state: Option, } /// Builder that makes it so we don't have to name `T`. -pub(crate) struct WinitAppBuilder { - /// Closure to initialize state. +pub(crate) struct WinitAppBuilder { + /// Closure to initialize `state`. init: Init, + /// Closure to initialize `surface_state`. + init_surface: InitSurface, + /// Eat the type parameter. - _marker: PhantomData>, + _marker: PhantomData<(Option, Option)>, } -impl WinitAppBuilder +impl WinitAppBuilder where Init: FnMut(&ActiveEventLoop) -> T, + InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S, { /// Create with an "init" closure. - pub(crate) fn with_init(init: Init) -> Self { + pub(crate) fn with_init(init: Init, init_surface: InitSurface) -> Self { Self { init, + init_surface, _marker: PhantomData, } } /// Build a new application. - pub(crate) fn with_event_handler(self, handler: F) -> WinitApp + pub(crate) fn with_event_handler(self, handler: F) -> WinitApp where - F: FnMut(&mut T, Event<()>, &ActiveEventLoop), + F: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop), { - WinitApp::new(self.init, handler) + WinitApp::new(self.init, self.init_surface, handler) } } -impl WinitApp +impl WinitApp where Init: FnMut(&ActiveEventLoop) -> T, - Handler: FnMut(&mut T, Event<()>, &ActiveEventLoop), + InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S, + Handler: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop), { /// Create a new application. - pub(crate) fn new(init: Init, event: Handler) -> Self { + pub(crate) fn new(init: Init, init_surface: InitSurface, event: Handler) -> Self { Self { init, + init_surface, event, state: None, + surface_state: None, } } } -impl ApplicationHandler for WinitApp +impl ApplicationHandler + for WinitApp where Init: FnMut(&ActiveEventLoop) -> T, - Handler: FnMut(&mut T, Event<()>, &ActiveEventLoop), + InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S, + Handler: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop), { fn resumed(&mut self, el: &ActiveEventLoop) { debug_assert!(self.state.is_none()); - self.state = Some((self.init)(el)); + let mut state = (self.init)(el); + self.surface_state = Some((self.init_surface)(el, &mut state)); + self.state = Some(state); } fn suspended(&mut self, _event_loop: &ActiveEventLoop) { - let state = self.state.take(); - debug_assert!(state.is_some()); - drop(state); + // TODO: Should we run a destruction function for the user? + let surface_state = self.surface_state.take(); + debug_assert!(surface_state.is_some()); + drop(surface_state); } fn window_event( @@ -110,12 +129,23 @@ where event: WindowEvent, ) { let state = self.state.as_mut().unwrap(); - (self.event)(state, Event::WindowEvent { window_id, event }, event_loop); + let surface_state = self.surface_state.as_mut(); + (self.event)( + state, + surface_state, + Event::WindowEvent { window_id, event }, + event_loop, + ); } fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { if let Some(state) = self.state.as_mut() { - (self.event)(state, Event::AboutToWait, event_loop); + (self.event)( + state, + self.surface_state.as_mut(), + Event::AboutToWait, + event_loop, + ); } } } diff --git a/examples/winit.rs b/examples/winit.rs index 7678fa5..fe80211 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -9,16 +9,17 @@ mod winit_app; fn main() { let event_loop = EventLoop::new().unwrap(); - let app = winit_app::WinitAppBuilder::with_init(|elwt| { - let window = winit_app::make_window(elwt, |w| w); + let app = winit_app::WinitAppBuilder::with_init( + |elwt| { + let window = winit_app::make_window(elwt, |w| w); - let context = softbuffer::Context::new(window.clone()).unwrap(); - let surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); - (window, surface) - }) - .with_event_handler(|state, event, elwt| { - let (window, surface) = state; + (window, context) + }, + |_elwt, (window, context)| softbuffer::Surface::new(context, window.clone()).unwrap(), + ) + .with_event_handler(|(window, _context), surface, event, elwt| { elwt.set_control_flow(ControlFlow::Wait); match event { @@ -26,6 +27,10 @@ fn main() { window_id, event: WindowEvent::RedrawRequested, } if window_id == window.id() => { + let Some(surface) = surface else { + eprintln!("RedrawRequested fired before Resumed or after Suspended"); + return; + }; if let (Some(width), Some(height)) = { let size = window.inner_size(); (NonZeroU32::new(size.width), NonZeroU32::new(size.height))