diff --git a/README.md b/README.md index be7084b..9b52f84 100644 --- a/README.md +++ b/README.md @@ -79,21 +79,27 @@ mod winit_app; fn main() { let event_loop = EventLoop::new().unwrap(); - let mut app = winit_app::WinitAppBuilder::with_init(|elwt| { - let window = { - let window = elwt.create_window(Window::default_attributes()); - Rc::new(window.unwrap()) - }; - let context = softbuffer::Context::new(window.clone()).unwrap(); - let surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); - - (window, surface) - }).with_event_handler(|state, event, elwt| { - let (window, surface) = state; + let mut app = winit_app::WinitAppBuilder::with_init( + |elwt| { + let window = { + let window = elwt.create_window(Window::default_attributes()); + Rc::new(window.unwrap()) + }; + let context = softbuffer::Context::new(window.clone()).unwrap(); + + (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 { Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested } if window_id == window.id() => { + let Some(surface) = surface else { + eprintln!("RedrawRequested fired before Resumed or after Suspended"); + return; + }; let (width, height) = { let size = window.inner_size(); (size.width, size.height) diff --git a/examples/animation.rs b/examples/animation.rs index 6df2265..c075923 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -14,19 +14,23 @@ fn main() { let event_loop = EventLoop::new().unwrap(); let start = Instant::now(); - let app = winit_app::WinitAppBuilder::with_init(|event_loop| { - let window = winit_app::make_window(event_loop, |w| w); + let app = winit_app::WinitAppBuilder::with_init( + |event_loop| { + let window = winit_app::make_window(event_loop, |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(); - let old_size = (0, 0); - let frames = pre_render_frames(0, 0); + let old_size = (0, 0); + let frames = pre_render_frames(0, 0); - (window, surface, old_size, frames) - }) - .with_event_handler(move |state, event, elwt| { - let (window, surface, old_size, frames) = state; + (window, context, old_size, frames) + }, + |_elwft, (window, context, _old_size, _frames)| { + softbuffer::Surface::new(context, window.clone()).unwrap() + }, + ) + .with_event_handler(move |state, surface, event, elwt| { + let (window, _context, old_size, frames) = state; elwt.set_control_flow(ControlFlow::Poll); @@ -35,6 +39,11 @@ fn main() { window_id, event: WindowEvent::Resized(size), } if window_id == window.id() => { + let Some(surface) = surface else { + eprintln!("Resized fired before Resumed or after Suspended"); + return; + }; + if let (Some(width), Some(height)) = (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) { @@ -45,6 +54,11 @@ 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; + }; + let size = window.inner_size(); if let (Some(width), Some(height)) = (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) diff --git a/examples/fruit.rs b/examples/fruit.rs index cecd4af..0402e8a 100644 --- a/examples/fruit.rs +++ b/examples/fruit.rs @@ -14,28 +14,32 @@ fn main() { let event_loop = EventLoop::new().unwrap(); - let app = winit_app::WinitAppBuilder::with_init(move |elwt| { - let window = winit_app::make_window(elwt, |w| { - w.with_inner_size(winit::dpi::PhysicalSize::new(width, height)) - }); + let app = winit_app::WinitAppBuilder::with_init( + move |elwt| { + let window = winit_app::make_window(elwt, |w| { + w.with_inner_size(winit::dpi::PhysicalSize::new(width, height)) + }); - let context = softbuffer::Context::new(window.clone()).unwrap(); - let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); - // Intentionally only set the size of the surface once, at creation. - // This is needed if the window chooses to ignore the size we passed in above, and for the - // platforms softbuffer supports that don't yet extract the size from the window. - surface - .resize( - NonZeroU32::new(width).unwrap(), - NonZeroU32::new(height).unwrap(), - ) - .unwrap(); - - (window, surface) - }) - .with_event_handler(move |state, event, elwt| { - let (window, surface) = state; + (window, context) + }, + move |_elwt, (window, context)| { + let mut surface = softbuffer::Surface::new(context, window.clone()).unwrap(); + // Intentionally only set the size of the surface once, at creation. + // This is needed if the window chooses to ignore the size we passed in above, and for the + // platforms softbuffer supports that don't yet extract the size from the window. + surface + .resize( + NonZeroU32::new(width).unwrap(), + NonZeroU32::new(height).unwrap(), + ) + .unwrap(); + surface + }, + ) + .with_event_handler(move |state, surface, event, elwt| { + let (window, _context) = state; elwt.set_control_flow(ControlFlow::Wait); match event { @@ -43,6 +47,11 @@ 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; + }; + let mut buffer = surface.buffer_mut().unwrap(); let width = fruit.width() as usize; for (x, y, pixel) in fruit.pixels() { diff --git a/examples/rectangle.rs b/examples/rectangle.rs index 41f8be4..b92f30b 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -25,20 +25,24 @@ fn redraw(buffer: &mut [u32], width: usize, height: usize, flag: bool) { 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.with_title("Press space to show/hide a rectangle") - }); + let app = winit_app::WinitAppBuilder::with_init( + |elwt| { + let window = winit_app::make_window(elwt, |w| { + w.with_title("Press space to show/hide a rectangle") + }); - 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(); - let flag = false; + let flag = false; - (window, surface, flag) - }) - .with_event_handler(|state, event, elwt| { - let (window, surface, flag) = state; + (window, context, flag) + }, + |_elwt, (window, context, _flag)| { + softbuffer::Surface::new(context, window.clone()).unwrap() + }, + ) + .with_event_handler(|state, surface, event, elwt| { + let (window, _context, flag) = state; elwt.set_control_flow(ControlFlow::Wait); @@ -47,6 +51,11 @@ fn main() { window_id, event: WindowEvent::Resized(size), } if window_id == window.id() => { + let Some(surface) = surface else { + eprintln!("Resized fired before Resumed or after Suspended"); + return; + }; + if let (Some(width), Some(height)) = (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) { @@ -58,6 +67,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; + }; // Grab the window's client area dimensions, and ensure they're valid let size = window.inner_size(); if let (Some(width), Some(height)) = diff --git a/examples/utils/winit_app.rs b/examples/utils/winit_app.rs index f427894..3b4cb4d 100644 --- a/examples/utils/winit_app.rs +++ b/examples/utils/winit_app.rs @@ -31,76 +31,94 @@ 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, + + /// 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. +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); + let surface_state = self.surface_state.take(); + debug_assert!(surface_state.is_some()); + drop(surface_state); } fn window_event( @@ -110,12 +128,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 3d3f261..09c6c10 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,11 @@ fn main() { window_id, event: WindowEvent::Resized(size), } if window_id == window.id() => { + let Some(surface) = surface else { + eprintln!("Resized fired before Resumed or after Suspended"); + return; + }; + if let (Some(width), Some(height)) = (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) { @@ -36,6 +42,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; + }; let size = window.inner_size(); if let (Some(width), Some(height)) = (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) diff --git a/examples/winit_multithread.rs b/examples/winit_multithread.rs index 4c2be4d..c0d208d 100644 --- a/examples/winit_multithread.rs +++ b/examples/winit_multithread.rs @@ -19,16 +19,15 @@ mod ex { fn render_thread( window: Arc, - surface: Arc>, - do_render: mpsc::Receiver<()>, + do_render: mpsc::Receiver>>, done: mpsc::Sender<()>, ) { loop { println!("waiting for render..."); - if do_render.recv().is_err() { - // Main thread is dead. + let Ok(surface) = do_render.recv() else { + println!("main thread destroyed"); break; - } + }; // Perform the rendering. let mut surface = surface.lock().unwrap(); @@ -63,34 +62,39 @@ mod ex { pub(super) fn entry() { let event_loop = EventLoop::new().unwrap(); - let app = winit_app::WinitAppBuilder::with_init(|elwt| { - let attributes = Window::default_attributes(); - #[cfg(target_arch = "wasm32")] - let attributes = - winit::platform::web::WindowAttributesExtWebSys::with_append(attributes, true); - let window = Arc::new(elwt.create_window(attributes).unwrap()); - - let context = softbuffer::Context::new(window.clone()).unwrap(); - let surface = { + let app = winit_app::WinitAppBuilder::with_init( + |elwt| { + let attributes = Window::default_attributes(); + #[cfg(target_arch = "wasm32")] + let attributes = + winit::platform::web::WindowAttributesExtWebSys::with_append(attributes, true); + let window = Arc::new(elwt.create_window(attributes).unwrap()); + + let context = softbuffer::Context::new(window.clone()).unwrap(); + + // Spawn a thread to handle rendering for this specific surface. The channels will + // be closed and the thread will be stopped whenever this surface (the returned + // context below) is dropped, so that it can all be recreated again (on Android) + // when a new surface is created. + let (start_render, do_render) = mpsc::channel(); + let (render_done, finish_render) = mpsc::channel(); + println!("starting thread..."); + std::thread::spawn({ + let window = window.clone(); + move || render_thread(window, do_render, render_done) + }); + + (window, context, start_render, finish_render) + }, + |_elwt, (window, context, _start_render, _finish_render)| { println!("making surface..."); - let surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); - Arc::new(Mutex::new(surface)) - }; - - // Spawn a thread to handle rendering. - let (start_render, do_render) = mpsc::channel(); - let (render_done, finish_render) = mpsc::channel(); - println!("starting thread..."); - std::thread::spawn({ - let window = window.clone(); - let surface = surface.clone(); - move || render_thread(window, surface, do_render, render_done) - }); - - (window, surface, start_render, finish_render) - }) - .with_event_handler(|state, event, elwt| { - let (window, _surface, start_render, finish_render) = state; + Arc::new(Mutex::new( + softbuffer::Surface::new(context, window.clone()).unwrap(), + )) + }, + ) + .with_event_handler(|state, surface, event, elwt| { + let (window, _context, start_render, finish_render) = state; elwt.set_control_flow(ControlFlow::Wait); match event { @@ -98,8 +102,12 @@ mod ex { window_id, event: WindowEvent::RedrawRequested, } if window_id == window.id() => { + let Some(surface) = surface else { + eprintln!("RedrawRequested fired before Resumed or after Suspended"); + return; + }; // Start the render and then finish it. - start_render.send(()).unwrap(); + start_render.send(surface.clone()).unwrap(); finish_render.recv().unwrap(); } Event::WindowEvent { diff --git a/examples/winit_wrong_sized_buffer.rs b/examples/winit_wrong_sized_buffer.rs index 95e3e07..0c58a11 100644 --- a/examples/winit_wrong_sized_buffer.rs +++ b/examples/winit_wrong_sized_buffer.rs @@ -12,24 +12,28 @@ const BUFFER_HEIGHT: usize = 128; 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 mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); - // Intentionally set the size of the surface to something different than the size of the window. - surface - .resize( - NonZeroU32::new(BUFFER_WIDTH as u32).unwrap(), - NonZeroU32::new(BUFFER_HEIGHT as u32).unwrap(), - ) - .unwrap(); - - (window, surface) - }) - .with_event_handler(|state, event, elwt| { - let (window, surface) = state; + (window, context) + }, + |_elwt, (window, context)| { + let mut surface = softbuffer::Surface::new(context, window.clone()).unwrap(); + // Intentionally set the size of the surface to something different than the size of the window. + surface + .resize( + NonZeroU32::new(BUFFER_WIDTH as u32).unwrap(), + NonZeroU32::new(BUFFER_HEIGHT as u32).unwrap(), + ) + .unwrap(); + surface + }, + ) + .with_event_handler(|state, surface, event, elwt| { + let (window, _context) = state; elwt.set_control_flow(ControlFlow::Wait); match event { @@ -37,6 +41,11 @@ 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; + }; + let mut buffer = surface.buffer_mut().unwrap(); for y in 0..BUFFER_HEIGHT { for x in 0..BUFFER_WIDTH {