From bf431b9268e7c9cb01bb680b479359d7add2632f Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 15:10:26 -0400 Subject: [PATCH] refactor(glutin_examples): `lib`: migrate from `EventLoop::run` to `EventLoop::run_app` --- glutin_examples/src/lib.rs | 280 +++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 136 deletions(-) diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 1329bc3d2c..7d9bc6b714 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -5,7 +5,8 @@ use std::ops::Deref; use gl::types::GLfloat; use raw_window_handle::HasRawWindowHandle; -use winit::event::{Event, KeyEvent, WindowEvent}; +use winit::application::ApplicationHandler; +use winit::event::{KeyEvent, WindowEvent}; use winit::keyboard::{Key, NamedKey}; use winit::window::Window; @@ -44,151 +45,157 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box { - let (mut window, gl_config) = match app.display_builder.clone().build( - event_loop, - template.clone(), - gl_config_picker, - ) { - Ok(ok) => ok, - Err(e) => { - app.exit_state = Err(e); - event_loop.exit(); - return; - }, - }; - - println!("Picked a config with {} samples", gl_config.num_samples()); - - let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle()); - - // XXX The display could be obtained from any object created by it, so we can - // query it from the config. - let gl_display = gl_config.display(); - - // The context creation part. - let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle); - - // Since glutin by default tries to create OpenGL core context, which may not be - // present we should try gles. - let fallback_context_attributes = ContextAttributesBuilder::new() - .with_context_api(ContextApi::Gles(None)) - .build(raw_window_handle); - - // There are also some old devices that support neither modern OpenGL nor GLES. - // To support these we can try and create a 2.1 context. - let legacy_context_attributes = ContextAttributesBuilder::new() - .with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1)))) - .build(raw_window_handle); - - app.not_current_gl_context.replace(unsafe { - gl_display.create_context(&gl_config, &context_attributes).unwrap_or_else( - |_| { - gl_display - .create_context(&gl_config, &fallback_context_attributes) - .unwrap_or_else(|_| { - gl_display - .create_context(&gl_config, &legacy_context_attributes) - .expect("failed to create context") - }) - }, - ) - }); - - #[cfg(android_platform)] - println!("Android window available"); - - let window = window.take().unwrap_or_else(|| { - let window_attributes = Window::default_attributes() - .with_transparent(true) - .with_title("Glutin triangle gradient example (press Escape to exit)"); - glutin_winit::finalize_window(event_loop, window_attributes, &gl_config) - .unwrap() - }); - - let attrs = window.build_surface_attributes(Default::default()); - let gl_surface = unsafe { - gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() - }; - - // Make it current. - let gl_context = - app.not_current_gl_context.take().unwrap().make_current(&gl_surface).unwrap(); - - // The context needs to be current for the Renderer to set up shaders and - // buffers. It also performs function loading, which needs a current context on - // WGL. - app.renderer.get_or_insert_with(|| Renderer::new(&gl_display)); - - // Try setting vsync. - if let Err(res) = gl_surface - .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) - { - eprintln!("Error setting vsync: {res:?}"); - } + let mut app = App::new(template, display_builder); + event_loop.run_app(&mut app)?; - assert!(app.state.replace(AppState { gl_context, gl_surface, window }).is_none()); - }, - Event::Suspended => { - // This event is only raised on Android, where the backing NativeWindow for a GL - // Surface can appear and disappear at any moment. - println!("Android window removed"); - - // Destroy the GL Surface and un-current the GL Context before ndk-glue releases - // the window back to the system. - let gl_context = app.state.take().unwrap().gl_context; - assert!(app - .not_current_gl_context - .replace(gl_context.make_not_current().unwrap()) - .is_none()); - }, - Event::WindowEvent { event, .. } => match event { - WindowEvent::Resized(size) => { - if size.width != 0 && size.height != 0 { - // Some platforms like EGL require resizing GL surface to update the size - // Notable platforms here are Wayland and macOS, other don't require it - // and the function is no-op, but it's wise to resize it for portability - // reasons. - if let Some(AppState { gl_context, gl_surface, window: _ }) = - app.state.as_ref() - { - gl_surface.resize( - gl_context, - NonZeroU32::new(size.width).unwrap(), - NonZeroU32::new(size.height).unwrap(), - ); - let renderer = app.renderer.as_ref().unwrap(); - renderer.resize(size.width as i32, size.height as i32); - } - } - }, - WindowEvent::CloseRequested - | WindowEvent::KeyboardInput { - event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. }, - .. - } => event_loop.exit(), - _ => (), + app.exit_state +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + let (mut window, gl_config) = match self.display_builder.clone().build( + event_loop, + self.template.clone(), + gl_config_picker, + ) { + Ok(ok) => ok, + Err(e) => { + self.exit_state = Err(e); + event_loop.exit(); + return; }, - Event::AboutToWait => { - if let Some(AppState { gl_context, gl_surface, window }) = app.state.as_ref() { - let renderer = app.renderer.as_ref().unwrap(); - renderer.draw(); - window.request_redraw(); + }; + + println!("Picked a config with {} samples", gl_config.num_samples()); + + let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle()); + + // XXX The display could be obtained from any object created by it, so we can + // query it from the config. + let gl_display = gl_config.display(); + + // The context creation part. + let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle); + + // Since glutin by default tries to create OpenGL core context, which may not be + // present we should try gles. + let fallback_context_attributes = ContextAttributesBuilder::new() + .with_context_api(ContextApi::Gles(None)) + .build(raw_window_handle); + + // There are also some old devices that support neither modern OpenGL nor GLES. + // To support these we can try and create a 2.1 context. + let legacy_context_attributes = ContextAttributesBuilder::new() + .with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1)))) + .build(raw_window_handle); + + self.not_current_gl_context.replace(unsafe { + gl_display.create_context(&gl_config, &context_attributes).unwrap_or_else(|_| { + gl_display.create_context(&gl_config, &fallback_context_attributes).unwrap_or_else( + |_| { + gl_display + .create_context(&gl_config, &legacy_context_attributes) + .expect("failed to create context") + }, + ) + }) + }); + + #[cfg(android_platform)] + println!("Android window available"); + + let window = window.take().unwrap_or_else(|| { + let window_attributes = Window::default_attributes() + .with_transparent(true) + .with_title("Glutin triangle gradient example (press Escape to exit)"); + glutin_winit::finalize_window(event_loop, window_attributes, &gl_config).unwrap() + }); + + let attrs = window.build_surface_attributes(Default::default()); + let gl_surface = + unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() }; + + // Make it current. + let gl_context = + self.not_current_gl_context.take().unwrap().make_current(&gl_surface).unwrap(); + + // The context needs to be current for the Renderer to set up shaders and + // buffers. It also performs function loading, which needs a current context on + // WGL. + self.renderer.get_or_insert_with(|| Renderer::new(&gl_display)); + + // Try setting vsync. + if let Err(res) = gl_surface + .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) + { + eprintln!("Error setting vsync: {res:?}"); + } - gl_surface.swap_buffers(gl_context).unwrap(); + assert!(self.state.replace(AppState { gl_context, gl_surface, window }).is_none()); + } + + fn suspended(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { + // This event is only raised on Android, where the backing NativeWindow for a GL + // Surface can appear and disappear at any moment. + println!("Android window removed"); + + // Destroy the GL Surface and un-current the GL Context before ndk-glue releases + // the window back to the system. + let gl_context = self.state.take().unwrap().gl_context; + assert!(self + .not_current_gl_context + .replace(gl_context.make_not_current().unwrap()) + .is_none()); + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + match event { + WindowEvent::Resized(size) => { + if size.width != 0 && size.height != 0 { + // Some platforms like EGL require resizing GL surface to update the size + // Notable platforms here are Wayland and macOS, other don't require it + // and the function is no-op, but it's wise to resize it for portability + // reasons. + if let Some(AppState { gl_context, gl_surface, window: _ }) = + self.state.as_ref() + { + gl_surface.resize( + gl_context, + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ); + let renderer = self.renderer.as_ref().unwrap(); + renderer.resize(size.width as i32, size.height as i32); + } } }, + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. }, + .. + } => event_loop.exit(), _ => (), } - })?; + } - app.exit_state + fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { + if let Some(AppState { gl_context, gl_surface, window }) = self.state.as_ref() { + let renderer = self.renderer.as_ref().unwrap(); + renderer.draw(); + window.request_redraw(); + + gl_surface.swap_buffers(gl_context).unwrap(); + } + } } struct App { + template: ConfigTemplateBuilder, display_builder: DisplayBuilder, exit_state: Result<(), Box>, not_current_gl_context: Option, @@ -197,8 +204,9 @@ struct App { } impl App { - fn new(display_builder: DisplayBuilder) -> Self { + fn new(template: ConfigTemplateBuilder, display_builder: DisplayBuilder) -> Self { Self { + template, display_builder, exit_state: Ok(()), not_current_gl_context: None,