/// Common boilerplate for setting up a winit application. use std::marker::PhantomData; use std::sync::Arc as Rc; use winit::application::ApplicationHandler; use winit::event::{DeviceEvent, DeviceId, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::window::{Window, WindowAttributes, WindowId}; /// Run a Winit application. #[allow(unused_mut)] pub(crate) fn run_app( event_loop: EventLoop, mut app: impl ApplicationHandler + 'static, ) { #[cfg(not(target_family = "wasm"))] event_loop.run_app(app).unwrap(); #[cfg(target_family = "wasm")] winit::platform::web::EventLoopExtWebSys::spawn_app(event_loop, app); } /// Create a window from a set of window attributes. #[allow(dead_code)] pub(crate) fn make_window( elwt: &dyn ActiveEventLoop, f: impl FnOnce(WindowAttributes) -> WindowAttributes, ) -> Rc { let attributes = f(WindowAttributes::default()); #[cfg(target_family = "wasm")] let attributes = winit::platform::web::WindowAttributesExtWebSys::with_append( attributes, true, ); let window = elwt.create_window(attributes); window.unwrap().into() } /// Easily constructable winit application. pub(crate) struct WinitApp< T, S, Init, InitSurface, Handler, DeviceEventHandler, AboutToWaitHandler, > { /// Closure to initialize `state`. init: Init, /// Closure to initialize `surface_state`. init_surface: InitSurface, /// Closure to run on window events. event: Handler, /// Closure to run on device events. device_event: DeviceEventHandler, /// Closure to run on about_to_wait events. about_to_wait: AboutToWaitHandler, /// Contained state. state: Option, /// Contained surface state. surface_state: Option, } /// Builder that makes it so we don't have to name `T`. pub(crate) struct WinitAppBuilder { /// Closure to initialize `state`. init: Init, /// Closure to initialize `surface_state`. init_surface: InitSurface, /// Eat the type parameter. _marker: PhantomData<(Option, Option)>, } impl WinitAppBuilder where Init: FnMut(&dyn ActiveEventLoop) -> T, InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S, { /// Create with an "init" closure. pub(crate) fn with_init( init: Init, init_surface: InitSurface, ) -> Self { Self { init, init_surface, _marker: PhantomData } } /// Build a new application. #[allow(clippy::type_complexity)] pub(crate) fn with_event_handler( self, handler: F, ) -> WinitApp< T, S, Init, InitSurface, F, impl FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop), impl FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), > where F: FnMut( &mut T, Option<&mut S>, WindowId, WindowEvent, &dyn ActiveEventLoop, ), { WinitApp::new( self.init, self.init_surface, handler, |_, _, _, _| {}, |_, _, _| {}, ) } } impl< T, S, Init, InitSurface, Handler, DeviceEventHandler, AboutToWaitHandler, > WinitApp< T, S, Init, InitSurface, Handler, DeviceEventHandler, AboutToWaitHandler, > where Init: FnMut(&dyn ActiveEventLoop) -> T, InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S, Handler: FnMut( &mut T, Option<&mut S>, WindowId, WindowEvent, &dyn ActiveEventLoop, ), DeviceEventHandler: FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop), AboutToWaitHandler: FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), { /// Create a new application. pub(crate) fn new( init: Init, init_surface: InitSurface, event: Handler, device_event: DeviceEventHandler, about_to_wait: AboutToWaitHandler, ) -> Self { Self { init, init_surface, event, device_event, about_to_wait, state: None, surface_state: None, } } /// Build a new application. #[allow(dead_code)] pub(crate) fn with_about_to_wait_handler( self, about_to_wait: F, ) -> WinitApp where F: FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), { WinitApp::new( self.init, self.init_surface, self.event, self.device_event, about_to_wait, ) } /// Build a new application. #[allow(dead_code)] pub(crate) fn with_device_event_handler( self, device_event: F, ) -> WinitApp where F: FnMut( &mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop, ), { WinitApp::new( self.init, self.init_surface, self.event, device_event, self.about_to_wait, ) } } impl< T, S, Init, InitSurface, Handler, DeviceEventHandler, AboutToWaitHandler, > ApplicationHandler for WinitApp< T, S, Init, InitSurface, Handler, DeviceEventHandler, AboutToWaitHandler, > where Init: FnMut(&dyn ActiveEventLoop) -> T, InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S, Handler: FnMut( &mut T, Option<&mut S>, WindowId, WindowEvent, &dyn ActiveEventLoop, ), DeviceEventHandler: FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop), AboutToWaitHandler: FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), { fn resumed(&mut self, el: &dyn ActiveEventLoop) { debug_assert!(self.state.is_none()); 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: &dyn ActiveEventLoop) { let surface_state = self.surface_state.take(); debug_assert!(surface_state.is_some()); drop(surface_state); } fn window_event( &mut self, event_loop: &dyn ActiveEventLoop, window_id: WindowId, event: WindowEvent, ) { let state = self.state.as_mut().unwrap(); let surface_state = self.surface_state.as_mut(); (self.event)(state, surface_state, window_id, event, event_loop); } fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { if let Some(state) = self.state.as_mut() { let surface_state = self.surface_state.as_mut(); (self.about_to_wait)(state, surface_state, event_loop); } } fn device_event( &mut self, event_loop: &dyn ActiveEventLoop, _device_id: Option, event: DeviceEvent, ) { let state = self.state.as_mut().unwrap(); let surface_state = self.surface_state.as_mut(); (self.device_event)(state, surface_state, event, event_loop); } fn can_create_surfaces(&mut self, el: &dyn ActiveEventLoop) { let mut state = (self.init)(el); self.surface_state = Some((self.init_surface)(el, &mut state)); self.state = Some(state); } }