From 42a3c729d5519d9073d7906f1fca613291e6e41d Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 11:10:54 -0400 Subject: [PATCH 01/11] =?UTF-8?q?build:=20upgrade=20`winit`=200.29.2=20?= =?UTF-8?q?=E2=86=92=200.30.0,=20avoid=20nontrivial=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: marc2332 Co-Authored-By: Marijn Suijten --- glutin-winit/CHANGELOG.md | 3 + glutin-winit/Cargo.toml | 2 +- glutin-winit/src/lib.rs | 56 +++++++++---------- glutin_examples/Cargo.toml | 4 +- glutin_examples/examples/android.rs | 4 +- .../examples/switch_render_thread.rs | 14 ++--- glutin_examples/src/lib.rs | 25 +++------ 7 files changed, 52 insertions(+), 56 deletions(-) diff --git a/glutin-winit/CHANGELOG.md b/glutin-winit/CHANGELOG.md index 445b92c548..769c567e2c 100644 --- a/glutin-winit/CHANGELOG.md +++ b/glutin-winit/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +- **Breaking:** Update _winit_ to `0.30`. See [winit's CHANGELOG](https://github.com/rust-windowing/winit/releases/tag/v0.30.0) for more info. +- Update `DisplayBuilder` to use `WindowAttributes` instead of `WindowBuilder`. + # Version 0.4.2 - **Breaking:** Update _glutin_ to `0.31.0`. See [glutin's CHANGELOG](https://github.com/rust-windowing/glutin/releases/tag/v0.31.0) for more info. diff --git a/glutin-winit/Cargo.toml b/glutin-winit/Cargo.toml index 3a63d8b432..399187a95f 100644 --- a/glutin-winit/Cargo.toml +++ b/glutin-winit/Cargo.toml @@ -21,7 +21,7 @@ wayland = ["glutin/wayland", "winit/wayland"] [dependencies] glutin = { version = "0.31.0", path = "../glutin", default-features = false } raw-window-handle = "0.6" -winit = { version = "0.29.2", default-features = false, features = ["rwh_06"] } +winit = { version = "0.30.0", default-features = false, features = ["rwh_06"] } [build-dependencies] cfg_aliases = "0.2.1" diff --git a/glutin-winit/src/lib.rs b/glutin-winit/src/lib.rs index 4dc9129f6d..521490401e 100644 --- a/glutin-winit/src/lib.rs +++ b/glutin-winit/src/lib.rs @@ -25,13 +25,13 @@ use raw_window_handle::HasWindowHandle; use raw_window_handle::{HasDisplayHandle, RawWindowHandle}; use winit::error::OsError; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::{Window, WindowBuilder}; +use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::window::{Window, WindowAttributes}; #[cfg(glx_backend)] use winit::platform::x11::register_xlib_error_hook; #[cfg(x11_platform)] -use winit::platform::x11::WindowBuilderExtX11; +use winit::platform::x11::WindowAttributesExtX11; #[cfg(all(not(egl_backend), not(glx_backend), not(wgl_backend), not(cgl_backend)))] compile_error!("Please select at least one api backend"); @@ -50,7 +50,7 @@ compile_error!("Please select at least one api backend"); #[derive(Default, Debug, Clone)] pub struct DisplayBuilder { preference: ApiPreference, - window_builder: Option, + window_attributes: Option, } impl DisplayBuilder { @@ -65,30 +65,30 @@ impl DisplayBuilder { self } - /// The window builder to use when building a window. + /// The window attributes to use when building a window. /// /// By default no window is created. - pub fn with_window_builder(mut self, window_builder: Option) -> Self { - self.window_builder = window_builder; + pub fn with_window_attributes(mut self, window_attributes: Option) -> Self { + self.window_attributes = window_attributes; self } /// Initialize the OpenGL platform and create a compatible window to use - /// with it when the [`WindowBuilder`] was passed with - /// [`Self::with_window_builder`]. It's optional, since on some + /// with it when the [`WindowAttributes`] was passed with + /// [`Self::with_window_attributes()`]. It's optional, since on some /// platforms like `Android` it is not available early on, so you want to /// find configuration and later use it with the [`finalize_window`]. /// But if you don't care about such platform you can always pass - /// [`WindowBuilder`]. + /// [`WindowAttributes`]. /// /// # Api-specific /// - /// **WGL:** - [`WindowBuilder`] **must** be passed in - /// [`Self::with_window_builder`] if modern OpenGL(ES) is desired, + /// **WGL:** - [`WindowAttributes`] **must** be passed in + /// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired, /// otherwise only builtin functions like `glClear` will be available. pub fn build( mut self, - window_target: &EventLoopWindowTarget, + event_loop: &EventLoop, template_builder: ConfigTemplateBuilder, config_picker: Picker, ) -> Result<(Option, Config), Box> @@ -97,8 +97,8 @@ impl DisplayBuilder { { // XXX with WGL backend window should be created first. #[cfg(wgl_backend)] - let window = if let Some(wb) = self.window_builder.take() { - Some(wb.build(window_target)?) + let window = if let Some(wa) = self.window_attributes.take() { + Some(event_loop.create_window(wa)?) } else { None }; @@ -111,7 +111,7 @@ impl DisplayBuilder { #[cfg(not(wgl_backend))] let raw_window_handle = None; - let gl_display = create_display(window_target, self.preference, raw_window_handle)?; + let gl_display = create_display(event_loop, self.preference, raw_window_handle)?; // XXX the native window must be passed to config picker when WGL is used // otherwise very limited OpenGL features will be supported. @@ -130,8 +130,8 @@ impl DisplayBuilder { }; #[cfg(not(wgl_backend))] - let window = if let Some(wb) = self.window_builder.take() { - Some(finalize_window(window_target, wb, &gl_config)?) + let window = if let Some(wa) = self.window_attributes.take() { + Some(finalize_window(event_loop, wa, &gl_config)?) } else { None }; @@ -141,7 +141,7 @@ impl DisplayBuilder { } fn create_display( - window_target: &EventLoopWindowTarget, + event_loop: &EventLoop, _api_preference: ApiPreference, _raw_window_handle: Option, ) -> Result> { @@ -173,7 +173,7 @@ fn create_display( ApiPreference::FallbackEgl => DisplayApiPreference::WglThenEgl(_raw_window_handle), }; - let handle = window_target.display_handle()?.as_raw(); + let handle = event_loop.display_handle()?.as_raw(); unsafe { Ok(Display::new(handle, _preference)?) } } @@ -183,24 +183,24 @@ fn create_display( /// /// [`Window`]: winit::window::Window /// [`Config`]: glutin::config::Config -pub fn finalize_window( - window_target: &EventLoopWindowTarget, - mut builder: WindowBuilder, +pub fn finalize_window( + event_loop: &ActiveEventLoop, + mut attributes: WindowAttributes, gl_config: &Config, ) -> Result { // Disable transparency if the end config doesn't support it. if gl_config.supports_transparency() == Some(false) { - builder = builder.with_transparent(false); + attributes = attributes.with_transparent(false); } #[cfg(x11_platform)] - let builder = if let Some(x11_visual) = gl_config.x11_visual() { - builder.with_x11_visual(x11_visual.visual_id() as _) + let attributes = if let Some(x11_visual) = gl_config.x11_visual() { + attributes.with_x11_visual(x11_visual.visual_id() as _) } else { - builder + attributes }; - builder.build(window_target) + event_loop.create_window(attributes) } /// Simplified version of the [`DisplayApiPreference`] which is used to simplify diff --git a/glutin_examples/Cargo.toml b/glutin_examples/Cargo.toml index a79fde076e..281e1bdbaa 100644 --- a/glutin_examples/Cargo.toml +++ b/glutin_examples/Cargo.toml @@ -23,10 +23,10 @@ glutin = { path = "../glutin", default-features = false } glutin-winit = { path = "../glutin-winit", default-features = false } png = { version = "0.17.6", optional = true } raw-window-handle = "0.6" -winit = { version = "0.29.2", default-features = false, features = ["rwh_05"] } +winit = { version = "0.30.0", default-features = false, features = ["rwh_06"] } [target.'cfg(target_os = "android")'.dependencies] -winit = { version = "0.29.2", default-features = false, features = ["android-native-activity", "rwh_06"] } +winit = { version = "0.30.0", default-features = false, features = ["android-native-activity", "rwh_06"] } [build-dependencies] gl_generator = "0.14" diff --git a/glutin_examples/examples/android.rs b/glutin_examples/examples/android.rs index fcd3bb31c1..818c2e44d0 100644 --- a/glutin_examples/examples/android.rs +++ b/glutin_examples/examples/android.rs @@ -1,10 +1,10 @@ #![cfg(android_platform)] -use winit::event_loop::EventLoopBuilder; +use winit::event_loop::EventLoop; use winit::platform::android::EventLoopBuilderExtAndroid; #[no_mangle] fn android_main(app: winit::platform::android::activity::AndroidApp) { - let event_loop = EventLoopBuilder::new().with_android_app(app).build().unwrap(); + let event_loop = EventLoop::builder().with_android_app(app).build().unwrap(); glutin_examples::main(event_loop).unwrap() } diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index 1f6584637a..5d2ba2fa4a 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -16,11 +16,11 @@ use glutin_winit::{self, DisplayBuilder, GlWindow}; use raw_window_handle::HasWindowHandle; use winit::dpi::PhysicalSize; use winit::event::{ElementState, Event, WindowEvent}; -use winit::event_loop::{EventLoopBuilder, EventLoopProxy}; -use winit::window::{Window, WindowBuilder}; +use winit::event_loop::{EventLoop, EventLoopProxy}; +use winit::window::Window; fn main() -> Result<(), Box> { - let event_loop = EventLoopBuilder::::with_user_event().build().unwrap(); + let event_loop = EventLoop::::with_user_event().build().unwrap(); let (_window, render_context) = create_window_with_render_context(&event_loop)?; let render_context = Arc::new(Mutex::new(render_context)); @@ -39,8 +39,8 @@ fn main() -> Result<(), Box> { }; app_state.send_event_to_current_render_thread(RenderThreadEvent::MakeCurrent); - event_loop.run(move |event, elwt| match event { - Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => elwt.exit(), + event_loop.run(move |event, event_loop| match event { + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => event_loop.exit(), Event::WindowEvent { event: WindowEvent::Resized(size), .. } => { if size.width != 0 && size.height != 0 { app_state.send_event_to_current_render_thread(RenderThreadEvent::Resize( @@ -167,11 +167,11 @@ impl RenderContext { fn create_window_with_render_context( event_loop: &winit::event_loop::EventLoop, ) -> Result<(Window, RenderContext), Box> { - let window_builder = WindowBuilder::new().with_transparent(true); + let window_attributes = Window::default_attributes().with_transparent(true); let template = ConfigTemplateBuilder::new().with_alpha_size(8); - let display_builder = DisplayBuilder::new().with_window_builder(Some(window_builder)); + let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes)); let (mut window, gl_config) = display_builder.build(event_loop, template, gl_config_picker)?; diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 62360fc45c..46da7d37c1 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -7,7 +7,7 @@ use gl::types::GLfloat; use raw_window_handle::HasWindowHandle; use winit::event::{Event, KeyEvent, WindowEvent}; use winit::keyboard::{Key, NamedKey}; -use winit::window::WindowBuilder; +use winit::window::Window; use glutin::config::{Config, ConfigTemplateBuilder}; use glutin::context::{ContextApi, ContextAttributesBuilder, Version}; @@ -25,16 +25,9 @@ pub mod gl { } pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box> { - // Only Windows requires the window to be present before creating the display. - // Other platforms don't really need one. - // - // XXX if you don't care about running on Android or so you can safely remove - // this condition and always pass the window builder. - let window_builder = cfg!(wgl_backend).then(|| { - WindowBuilder::new() - .with_transparent(true) - .with_title("Glutin triangle gradient example (press Escape to exit)") - }); + let window_attributes = Window::default_attributes() + .with_transparent(true) + .with_title("Glutin triangle gradient example (press Escape to exit)"); // The template will match only the configurations supporting rendering // to windows. @@ -47,7 +40,7 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box) -> Result<(), Box { #[cfg(android_platform)] println!("Android window available"); let window = window.take().unwrap_or_else(|| { - let window_builder = WindowBuilder::new() + let window_attributes = Window::default_attributes() .with_transparent(true) .with_title("Glutin triangle gradient example (press Escape to exit)"); - glutin_winit::finalize_window(window_target, window_builder, &gl_config) + glutin_winit::finalize_window(event_loop, window_attributes, &gl_config) .unwrap() }); @@ -164,7 +157,7 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box window_target.exit(), + } => event_loop.exit(), _ => (), }, Event::AboutToWait => { From 05febd0571e680b52c1a9a0b6ca3f90d4d641d74 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 13:58:37 -0400 Subject: [PATCH 02/11] =?UTF-8?q?refactor(glutin=5Fexamples):=20lib:=20con?= =?UTF-8?q?text/surface/window=20tuple=20to=20`struct=20AppState=20{=20?= =?UTF-8?q?=E2=80=A6=20}`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- glutin_examples/src/lib.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 46da7d37c1..571b52b22a 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -10,10 +10,10 @@ use winit::keyboard::{Key, NamedKey}; use winit::window::Window; use glutin::config::{Config, ConfigTemplateBuilder}; -use glutin::context::{ContextApi, ContextAttributesBuilder, Version}; +use glutin::context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext, Version}; use glutin::display::GetGlDisplay; use glutin::prelude::*; -use glutin::surface::SwapInterval; +use glutin::surface::{Surface, SwapInterval, WindowSurface}; use glutin_winit::{self, DisplayBuilder, GlWindow}; @@ -121,7 +121,7 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box { // This event is only raised on Android, where the backing NativeWindow for a GL @@ -130,7 +130,7 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box (), }, Event::AboutToWait => { - if let Some((gl_context, gl_surface, window)) = &state { + if let Some(AppState { gl_context, gl_surface, window }) = &state { let renderer = renderer.as_ref().unwrap(); renderer.draw(); window.request_redraw(); @@ -176,6 +176,12 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box, + window: Window, +} + // Find the config with the maximum number of samples, so our triangle will be // smooth. pub fn gl_config_picker(configs: Box + '_>) -> Config { From 2dabaab8df6713b37170f09be9c0dc76b3aebffa Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 13:26:52 -0400 Subject: [PATCH 03/11] refactor(glutin_examples): lib: begin `App` state model --- glutin_examples/src/lib.rs | 44 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 571b52b22a..0dcd4aa39f 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -10,7 +10,9 @@ use winit::keyboard::{Key, NamedKey}; use winit::window::Window; use glutin::config::{Config, ConfigTemplateBuilder}; -use glutin::context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext, Version}; +use glutin::context::{ + ContextApi, ContextAttributesBuilder, NotCurrentContext, PossiblyCurrentContext, Version, +}; use glutin::display::GetGlDisplay; use glutin::prelude::*; use glutin::surface::{Surface, SwapInterval, WindowSurface}; @@ -70,7 +72,7 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box) -> Result<(), Box { @@ -107,12 +108,12 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box) -> Result<(), Box { // This event is only raised on Android, where the backing NativeWindow for a GL @@ -130,8 +131,9 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box (), }, Event::AboutToWait => { - if let Some(AppState { gl_context, gl_surface, window }) = &state { - let renderer = renderer.as_ref().unwrap(); + 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(); @@ -176,6 +180,18 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box, + state: Option, + renderer: Option, +} + +impl App { + fn new(not_current_gl_context: NotCurrentContext) -> Self { + Self { not_current_gl_context: Some(not_current_gl_context), state: None, renderer: None } + } +} + struct AppState { gl_context: PossiblyCurrentContext, gl_surface: Surface, From 41d2580fef6221d07c02939d59a12c71b5f79246 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 13:20:04 -0400 Subject: [PATCH 04/11] refactor(glutin_examples): `switch_render_thread`: lower `render_context` construction --- glutin_examples/examples/switch_render_thread.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index 5d2ba2fa4a..898ca34209 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -22,13 +22,13 @@ use winit::window::Window; fn main() -> Result<(), Box> { let event_loop = EventLoop::::with_user_event().build().unwrap(); - let (_window, render_context) = create_window_with_render_context(&event_loop)?; - let render_context = Arc::new(Mutex::new(render_context)); - // `EventLoopProxy` allows you to dispatch custom events to the main Winit event // loop from any thread. let event_loop_proxy = event_loop.create_proxy(); + let (_window, render_context) = create_window_with_render_context(&event_loop)?; + let render_context = Arc::new(Mutex::new(render_context)); + let (_render_threads, render_thread_senders) = spawn_render_threads(render_context, event_loop_proxy); From 94e4d735f2b61e67dc12210577ab21aaece34f8b Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 13:26:52 -0400 Subject: [PATCH 05/11] refactor(glutin_examples): `switch_render_thread`: begin `App` state model --- .../examples/switch_render_thread.rs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index 898ca34209..f5486cbac7 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -30,39 +30,44 @@ fn main() -> Result<(), Box> { let render_context = Arc::new(Mutex::new(render_context)); let (_render_threads, render_thread_senders) = - spawn_render_threads(render_context, event_loop_proxy); + spawn_render_threads(render_context, event_loop_proxy.clone()); - let mut app_state = AppState { + let app_state = AppState { render_thread_senders, render_thread_index: 0, thread_switch_in_progress: false, }; app_state.send_event_to_current_render_thread(RenderThreadEvent::MakeCurrent); + let mut app = App { state: Some(app_state) }; + event_loop.run(move |event, event_loop| match event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => event_loop.exit(), Event::WindowEvent { event: WindowEvent::Resized(size), .. } => { if size.width != 0 && size.height != 0 { - app_state.send_event_to_current_render_thread(RenderThreadEvent::Resize( - PhysicalSize { + app.state.as_ref().unwrap().send_event_to_current_render_thread( + RenderThreadEvent::Resize(PhysicalSize { width: NonZeroU32::new(size.width).unwrap(), height: NonZeroU32::new(size.height).unwrap(), - }, - )); + }), + ); } }, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } => { - app_state.send_event_to_current_render_thread(RenderThreadEvent::Draw); + app.state + .as_ref() + .unwrap() + .send_event_to_current_render_thread(RenderThreadEvent::Draw); }, Event::WindowEvent { event: WindowEvent::MouseInput { state: ElementState::Pressed, .. }, .. } => { - app_state.start_render_thread_switch(); + app.state.as_mut().unwrap().start_render_thread_switch(); }, Event::UserEvent(event) => match event { PlatformThreadEvent::ContextNotCurrent => { - app_state.complete_render_thread_switch(); + app.state.as_mut().unwrap().complete_render_thread_switch(); }, }, _ => (), @@ -71,6 +76,10 @@ fn main() -> Result<(), Box> { Ok(()) } +struct App { + state: Option, +} + struct AppState { render_thread_senders: Vec>, render_thread_index: usize, From 341e84101cfab720be2fbc5d8a4ef26c32e53a22 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 13:29:56 -0400 Subject: [PATCH 06/11] refactor(glutin_examples): `switch_render_thread`: move event handling to new `App::handle_event` --- .../examples/switch_render_thread.rs | 70 ++++++++++--------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index f5486cbac7..b9977e1023 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -16,7 +16,7 @@ use glutin_winit::{self, DisplayBuilder, GlWindow}; use raw_window_handle::HasWindowHandle; use winit::dpi::PhysicalSize; use winit::event::{ElementState, Event, WindowEvent}; -use winit::event_loop::{EventLoop, EventLoopProxy}; +use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}; use winit::window::Window; fn main() -> Result<(), Box> { @@ -41,37 +41,7 @@ fn main() -> Result<(), Box> { let mut app = App { state: Some(app_state) }; - event_loop.run(move |event, event_loop| match event { - Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => event_loop.exit(), - Event::WindowEvent { event: WindowEvent::Resized(size), .. } => { - if size.width != 0 && size.height != 0 { - app.state.as_ref().unwrap().send_event_to_current_render_thread( - RenderThreadEvent::Resize(PhysicalSize { - width: NonZeroU32::new(size.width).unwrap(), - height: NonZeroU32::new(size.height).unwrap(), - }), - ); - } - }, - Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } => { - app.state - .as_ref() - .unwrap() - .send_event_to_current_render_thread(RenderThreadEvent::Draw); - }, - Event::WindowEvent { - event: WindowEvent::MouseInput { state: ElementState::Pressed, .. }, - .. - } => { - app.state.as_mut().unwrap().start_render_thread_switch(); - }, - Event::UserEvent(event) => match event { - PlatformThreadEvent::ContextNotCurrent => { - app.state.as_mut().unwrap().complete_render_thread_switch(); - }, - }, - _ => (), - })?; + event_loop.run(move |event, event_loop| app.handle_event(event, event_loop))?; Ok(()) } @@ -80,6 +50,42 @@ struct App { state: Option, } +impl App { + fn handle_event(&mut self, event: Event, event_loop: &ActiveEventLoop) { + match event { + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => event_loop.exit(), + Event::WindowEvent { event: WindowEvent::Resized(size), .. } => { + if size.width != 0 && size.height != 0 { + self.state.as_ref().unwrap().send_event_to_current_render_thread( + RenderThreadEvent::Resize(PhysicalSize { + width: NonZeroU32::new(size.width).unwrap(), + height: NonZeroU32::new(size.height).unwrap(), + }), + ); + } + }, + Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } => { + self.state + .as_ref() + .unwrap() + .send_event_to_current_render_thread(RenderThreadEvent::Draw); + }, + Event::WindowEvent { + event: WindowEvent::MouseInput { state: ElementState::Pressed, .. }, + .. + } => { + self.state.as_mut().unwrap().start_render_thread_switch(); + }, + Event::UserEvent(event) => match event { + PlatformThreadEvent::ContextNotCurrent => { + self.state.as_mut().unwrap().complete_render_thread_switch(); + }, + }, + _ => (), + } + } +} + struct AppState { render_thread_senders: Vec>, render_thread_index: usize, From 29b14dab37afc4693633437e1dafe79cfa25ea20 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 13:34:41 -0400 Subject: [PATCH 07/11] refactor(glutin_examples): `switch_render_thread`: delegate `App::handle_event` branches into new `{window,user}_event` handlers --- .../examples/switch_render_thread.rs | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index b9977e1023..b69216a9b7 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -53,8 +53,23 @@ struct App { impl App { fn handle_event(&mut self, event: Event, event_loop: &ActiveEventLoop) { match event { - Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => event_loop.exit(), - Event::WindowEvent { event: WindowEvent::Resized(size), .. } => { + Event::WindowEvent { event, window_id } => { + self.window_event(event_loop, window_id, event) + }, + Event::UserEvent(event) => self.user_event(event_loop, event), + _ => (), + } + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + match event { + WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::Resized(size) => { if size.width != 0 && size.height != 0 { self.state.as_ref().unwrap().send_event_to_current_render_thread( RenderThreadEvent::Resize(PhysicalSize { @@ -64,26 +79,26 @@ impl App { ); } }, - Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } => { + WindowEvent::RedrawRequested => { self.state .as_ref() .unwrap() .send_event_to_current_render_thread(RenderThreadEvent::Draw); }, - Event::WindowEvent { - event: WindowEvent::MouseInput { state: ElementState::Pressed, .. }, - .. - } => { + WindowEvent::MouseInput { state: ElementState::Pressed, .. } => { self.state.as_mut().unwrap().start_render_thread_switch(); }, - Event::UserEvent(event) => match event { - PlatformThreadEvent::ContextNotCurrent => { - self.state.as_mut().unwrap().complete_render_thread_switch(); - }, - }, _ => (), } } + + fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: PlatformThreadEvent) { + match event { + PlatformThreadEvent::ContextNotCurrent => { + self.state.as_mut().unwrap().complete_render_thread_switch() + }, + } + } } struct AppState { From c1711b71c842a6e093db2c32ee2dfd62ed502cac Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 14:57:50 -0400 Subject: [PATCH 08/11] refactor(glutin_examples): move window creation inside event loop --- glutin-winit/src/lib.rs | 10 +- .../examples/switch_render_thread.rs | 59 +++++++--- glutin_examples/src/lib.rs | 111 +++++++++++------- 3 files changed, 113 insertions(+), 67 deletions(-) diff --git a/glutin-winit/src/lib.rs b/glutin-winit/src/lib.rs index 521490401e..751fb8d692 100644 --- a/glutin-winit/src/lib.rs +++ b/glutin-winit/src/lib.rs @@ -25,7 +25,7 @@ use raw_window_handle::HasWindowHandle; use raw_window_handle::{HasDisplayHandle, RawWindowHandle}; use winit::error::OsError; -use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::event_loop::ActiveEventLoop; use winit::window::{Window, WindowAttributes}; #[cfg(glx_backend)] @@ -86,9 +86,9 @@ impl DisplayBuilder { /// **WGL:** - [`WindowAttributes`] **must** be passed in /// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired, /// otherwise only builtin functions like `glClear` will be available. - pub fn build( + pub fn build( mut self, - event_loop: &EventLoop, + event_loop: &ActiveEventLoop, template_builder: ConfigTemplateBuilder, config_picker: Picker, ) -> Result<(Option, Config), Box> @@ -140,8 +140,8 @@ impl DisplayBuilder { } } -fn create_display( - event_loop: &EventLoop, +fn create_display( + event_loop: &ActiveEventLoop, _api_preference: ApiPreference, _raw_window_handle: Option, ) -> Result> { diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index b69216a9b7..9015497ad5 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -26,33 +26,29 @@ fn main() -> Result<(), Box> { // loop from any thread. let event_loop_proxy = event_loop.create_proxy(); - let (_window, render_context) = create_window_with_render_context(&event_loop)?; - let render_context = Arc::new(Mutex::new(render_context)); + let mut app = App::new(event_loop_proxy); - let (_render_threads, render_thread_senders) = - spawn_render_threads(render_context, event_loop_proxy.clone()); + event_loop.run(|event, event_loop| app.handle_event(event, event_loop))?; - let app_state = AppState { - render_thread_senders, - render_thread_index: 0, - thread_switch_in_progress: false, - }; - app_state.send_event_to_current_render_thread(RenderThreadEvent::MakeCurrent); - - let mut app = App { state: Some(app_state) }; - - event_loop.run(move |event, event_loop| app.handle_event(event, event_loop))?; - - Ok(()) + app.exit_state } struct App { + event_loop_proxy: EventLoopProxy, + exit_state: Result<(), Box>, state: Option, } +impl App { + fn new(event_loop_proxy: EventLoopProxy) -> Self { + Self { event_loop_proxy, exit_state: Ok(()), state: None } + } +} + impl App { fn handle_event(&mut self, event: Event, event_loop: &ActiveEventLoop) { match event { + Event::Resumed => self.resumed(event_loop), Event::WindowEvent { event, window_id } => { self.window_event(event_loop, window_id, event) }, @@ -61,6 +57,34 @@ impl App { } } + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + if self.state.is_some() { + return; + } + + let (window, render_context) = match create_window_with_render_context(event_loop) { + Ok(ok) => ok, + Err(e) => { + self.exit_state = Err(e); + event_loop.exit(); + return; + }, + }; + let render_context = Arc::new(Mutex::new(render_context)); + + let (_render_threads, render_thread_senders) = + spawn_render_threads(render_context, self.event_loop_proxy.clone()); + + let state = AppState { + _window: window, + render_thread_senders, + render_thread_index: 0, + thread_switch_in_progress: false, + }; + state.send_event_to_current_render_thread(RenderThreadEvent::MakeCurrent); + assert!(self.state.replace(state).is_none()); + } + fn window_event( &mut self, event_loop: &ActiveEventLoop, @@ -102,6 +126,7 @@ impl App { } struct AppState { + _window: Window, render_thread_senders: Vec>, render_thread_index: usize, thread_switch_in_progress: bool, @@ -195,7 +220,7 @@ impl RenderContext { } fn create_window_with_render_context( - event_loop: &winit::event_loop::EventLoop, + event_loop: &ActiveEventLoop, ) -> Result<(Window, RenderContext), Box> { let window_attributes = Window::default_attributes().with_transparent(true); diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 0dcd4aa39f..260b026bd7 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -44,50 +44,63 @@ 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() + .and_then(|window| window.window_handle().ok()) + .map(|handle| handle.as_raw()); + + // 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"); @@ -177,18 +190,26 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box>, not_current_gl_context: Option, state: Option, renderer: Option, } impl App { - fn new(not_current_gl_context: NotCurrentContext) -> Self { - Self { not_current_gl_context: Some(not_current_gl_context), state: None, renderer: None } + fn new(display_builder: DisplayBuilder) -> Self { + Self { + display_builder, + exit_state: Ok(()), + not_current_gl_context: None, + state: None, + renderer: None, + } } } From 8afd5f791e52da8f723ab3f57d17e6084be65ae0 Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 15:10:26 -0400 Subject: [PATCH 09/11] refactor(glutin_examples): `switch_render_thread`: migrate from `EventLoop::run` to `EventLoop::run_app` --- .../examples/switch_render_thread.rs | 18 ++++-------------- glutin_examples/examples/window.rs | 4 ++-- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index 9015497ad5..17c1cfeb86 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -14,8 +14,9 @@ use glutin_examples::gl::types::GLfloat; use glutin_examples::{gl_config_picker, Renderer}; use glutin_winit::{self, DisplayBuilder, GlWindow}; use raw_window_handle::HasWindowHandle; +use winit::application::ApplicationHandler; use winit::dpi::PhysicalSize; -use winit::event::{ElementState, Event, WindowEvent}; +use winit::event::{ElementState, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}; use winit::window::Window; @@ -28,7 +29,7 @@ fn main() -> Result<(), Box> { let mut app = App::new(event_loop_proxy); - event_loop.run(|event, event_loop| app.handle_event(event, event_loop))?; + event_loop.run_app(&mut app)?; app.exit_state } @@ -45,18 +46,7 @@ impl App { } } -impl App { - fn handle_event(&mut self, event: Event, event_loop: &ActiveEventLoop) { - match event { - Event::Resumed => self.resumed(event_loop), - Event::WindowEvent { event, window_id } => { - self.window_event(event_loop, window_id, event) - }, - Event::UserEvent(event) => self.user_event(event_loop, event), - _ => (), - } - } - +impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.state.is_some() { return; diff --git a/glutin_examples/examples/window.rs b/glutin_examples/examples/window.rs index f2c97c6026..3417f2f67b 100644 --- a/glutin_examples/examples/window.rs +++ b/glutin_examples/examples/window.rs @@ -1,7 +1,7 @@ use std::error::Error; -use winit::event_loop::EventLoopBuilder; +use winit::event_loop::EventLoop; fn main() -> Result<(), Box> { - glutin_examples::main(EventLoopBuilder::new().build().unwrap()) + glutin_examples::main(EventLoop::new().unwrap()) } From b12c7eebad0968f5d9aa540db98075b224524d6a Mon Sep 17 00:00:00 2001 From: Erich Gubler Date: Tue, 14 May 2024 15:10:26 -0400 Subject: [PATCH 10/11] refactor(glutin_examples): `lib`: migrate from `EventLoop::run` to `EventLoop::run_app` --- glutin_examples/src/lib.rs | 290 +++++++++++++++++++------------------ 1 file changed, 149 insertions(+), 141 deletions(-) diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 260b026bd7..6bad398fea 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::HasWindowHandle; -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,156 +45,162 @@ 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() - .and_then(|window| window.window_handle().ok()) - .map(|handle| handle.as_raw()); - - // 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()) - .expect("Failed to build surface attributes"); - 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() + .and_then(|window| window.window_handle().ok()) + .map(|handle| handle.as_raw()); + + // 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()) + .expect("Failed to build surface attributes"); + 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, @@ -202,8 +209,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, From bd053c1bf0a68df4d390f982c04882626c0572c7 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 31 May 2024 16:53:40 -0400 Subject: [PATCH 11/11] feat: add sealed `trait GlutinEventLoop` to abstract over `{,Active}EventLoop` in `winit` 0.30.0 Co-Authored-By: Erich Gubler --- glutin-winit/CHANGELOG.md | 1 + glutin-winit/src/event_loop.rs | 51 ++++++++++++++++++++++++++++++++++ glutin-winit/src/lib.rs | 20 +++++++++---- 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 glutin-winit/src/event_loop.rs diff --git a/glutin-winit/CHANGELOG.md b/glutin-winit/CHANGELOG.md index 769c567e2c..5f943fce73 100644 --- a/glutin-winit/CHANGELOG.md +++ b/glutin-winit/CHANGELOG.md @@ -1,6 +1,7 @@ # Unreleased - **Breaking:** Update _winit_ to `0.30`. See [winit's CHANGELOG](https://github.com/rust-windowing/winit/releases/tag/v0.30.0) for more info. +- Add the `GlutinEventLoop` trait to maintain compatibility with the now deprecated `EventLoop` but also support the new `ActiveEventLoop`. - Update `DisplayBuilder` to use `WindowAttributes` instead of `WindowBuilder`. # Version 0.4.2 diff --git a/glutin-winit/src/event_loop.rs b/glutin-winit/src/event_loop.rs new file mode 100644 index 0000000000..13c157a49b --- /dev/null +++ b/glutin-winit/src/event_loop.rs @@ -0,0 +1,51 @@ +use raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle}; +use winit::error::OsError; +use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::window::{Window, WindowAttributes}; + +use crate::private::Sealed; + +/// [`ActiveEventLoop`] is the recommended way to interact with the event +/// loop, but for compatibility purposes [`EventLoop`] is also supported +/// although not recommended anymore as it has been deprecated by Winit. +pub trait GlutinEventLoop: Sealed { + /// Create the window. + /// + /// Possible causes of error include denied permission, incompatible system, + /// and lack of memory. + /// + /// ## Platform-specific + /// + /// - **Web:** The window is created but not inserted into the web page + /// automatically. Please see the web platform module for more + /// information. + fn create_window(&self, window_attributes: WindowAttributes) -> Result; + + /// Get a handle to the display controller of the windowing system. + fn glutin_display_handle(&self) -> Result, HandleError>; +} + +impl Sealed for ActiveEventLoop {} + +impl GlutinEventLoop for ActiveEventLoop { + fn create_window(&self, window_attributes: WindowAttributes) -> Result { + self.create_window(window_attributes) + } + + fn glutin_display_handle(&self) -> Result, HandleError> { + self.display_handle() + } +} + +impl Sealed for EventLoop {} + +impl GlutinEventLoop for EventLoop { + #[allow(deprecated)] + fn create_window(&self, window_attributes: WindowAttributes) -> Result { + self.create_window(window_attributes) + } + + fn glutin_display_handle(&self) -> Result, HandleError> { + self.display_handle() + } +} diff --git a/glutin-winit/src/lib.rs b/glutin-winit/src/lib.rs index 751fb8d692..00b9a8f23d 100644 --- a/glutin-winit/src/lib.rs +++ b/glutin-winit/src/lib.rs @@ -8,8 +8,10 @@ #![deny(missing_docs)] #![cfg_attr(clippy, deny(warnings))] +mod event_loop; mod window; +use event_loop::GlutinEventLoop; pub use window::GlWindow; use std::error::Error; @@ -23,9 +25,8 @@ use glutin::prelude::*; #[cfg(wgl_backend)] use raw_window_handle::HasWindowHandle; -use raw_window_handle::{HasDisplayHandle, RawWindowHandle}; +use raw_window_handle::RawWindowHandle; use winit::error::OsError; -use winit::event_loop::ActiveEventLoop; use winit::window::{Window, WindowAttributes}; #[cfg(glx_backend)] @@ -36,6 +37,13 @@ use winit::platform::x11::WindowAttributesExtX11; #[cfg(all(not(egl_backend), not(glx_backend), not(wgl_backend), not(cgl_backend)))] compile_error!("Please select at least one api backend"); +pub(crate) mod private { + /// Prevent traits from being implemented downstream, since those are used + /// purely for documentation organization and simplify platform api + /// implementation maintenance. + pub trait Sealed {} +} + /// The helper to perform [`Display`] creation and OpenGL platform /// bootstrapping with the help of [`winit`] with little to no platform specific /// code. @@ -88,7 +96,7 @@ impl DisplayBuilder { /// otherwise only builtin functions like `glClear` will be available. pub fn build( mut self, - event_loop: &ActiveEventLoop, + event_loop: &impl GlutinEventLoop, template_builder: ConfigTemplateBuilder, config_picker: Picker, ) -> Result<(Option, Config), Box> @@ -141,7 +149,7 @@ impl DisplayBuilder { } fn create_display( - event_loop: &ActiveEventLoop, + event_loop: &impl GlutinEventLoop, _api_preference: ApiPreference, _raw_window_handle: Option, ) -> Result> { @@ -173,7 +181,7 @@ fn create_display( ApiPreference::FallbackEgl => DisplayApiPreference::WglThenEgl(_raw_window_handle), }; - let handle = event_loop.display_handle()?.as_raw(); + let handle = event_loop.glutin_display_handle()?.as_raw(); unsafe { Ok(Display::new(handle, _preference)?) } } @@ -184,7 +192,7 @@ fn create_display( /// [`Window`]: winit::window::Window /// [`Config`]: glutin::config::Config pub fn finalize_window( - event_loop: &ActiveEventLoop, + event_loop: &impl GlutinEventLoop, mut attributes: WindowAttributes, gl_config: &Config, ) -> Result {