From 478b4df22425df08ca20950df7d26a65b3730570 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 1 Sep 2024 08:23:21 +0200 Subject: [PATCH] Allow specifying an initialization closure --- examples/child_window.rs | 34 ++--- examples/control_flow.rs | 35 +++-- examples/pump_events.rs | 11 +- examples/run_on_demand.rs | 28 ++-- examples/window.rs | 81 ++++++----- examples/x11_embed.rs | 29 ++-- src/application.rs | 126 +++++------------ src/event.rs | 46 ------- src/event_loop.rs | 61 ++++++++- src/lib.rs | 95 +++++++------ src/platform/macos.rs | 2 +- src/platform/run_on_demand.rs | 24 ++-- src/platform/web.rs | 8 +- src/platform_impl/android/mod.rs | 129 ++++++++++++++---- src/platform_impl/apple/appkit/app_state.rs | 8 +- src/platform_impl/apple/appkit/event_loop.rs | 13 +- src/platform_impl/apple/event_handler.rs | 24 +++- src/platform_impl/apple/uikit/app_state.rs | 9 +- src/platform_impl/linux/mod.rs | 13 +- .../linux/wayland/event_loop/mod.rs | 22 ++- src/platform_impl/linux/x11/mod.rs | 22 ++- src/platform_impl/orbital/event_loop.rs | 12 +- src/platform_impl/web/event_loop/mod.rs | 2 - src/platform_impl/web/event_loop/runner.rs | 10 +- src/platform_impl/windows/event_loop.rs | 24 ++-- .../windows/event_loop/runner.rs | 16 +-- 26 files changed, 469 insertions(+), 415 deletions(-) diff --git a/examples/child_window.rs b/examples/child_window.rs index 9feb814066..8ceea193df 100644 --- a/examples/child_window.rs +++ b/examples/child_window.rs @@ -13,26 +13,12 @@ fn main() -> Result<(), impl std::error::Error> { #[path = "util/fill.rs"] mod fill; - #[derive(Default)] struct Application { - parent_window_id: Option, + parent_window_id: WindowId, windows: HashMap>, } impl ApplicationHandler for Application { - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - let attributes = WindowAttributes::default() - .with_title("parent window") - .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) - .with_inner_size(LogicalSize::new(640.0f32, 480.0f32)); - let window = event_loop.create_window(attributes).unwrap(); - - println!("Parent window id: {:?})", window.id()); - self.parent_window_id = Some(window.id()); - - self.windows.insert(window.id(), window); - } - fn window_event( &mut self, event_loop: &dyn ActiveEventLoop, @@ -56,7 +42,7 @@ fn main() -> Result<(), impl std::error::Error> { event: KeyEvent { state: ElementState::Pressed, .. }, .. } => { - let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap(); + let parent_window = self.windows.get(&self.parent_window_id).unwrap(); let child_window = spawn_child_window(parent_window.as_ref(), event_loop); let child_id = child_window.id(); println!("Child window created with id: {child_id:?}"); @@ -72,6 +58,20 @@ fn main() -> Result<(), impl std::error::Error> { } } + fn init(event_loop: &dyn ActiveEventLoop) -> Application { + let attributes = WindowAttributes::default() + .with_title("parent window") + .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) + .with_inner_size(LogicalSize::new(640.0f32, 480.0f32)); + let window = event_loop.create_window(attributes).unwrap(); + + println!("Parent window id: {:?})", window.id()); + + let parent_window_id = window.id(); + let windows = HashMap::from([(window.id(), window)]); + Application { parent_window_id, windows } + } + fn spawn_child_window( parent: &dyn Window, event_loop: &dyn ActiveEventLoop, @@ -89,7 +89,7 @@ fn main() -> Result<(), impl std::error::Error> { } let event_loop = EventLoop::new().unwrap(); - event_loop.run_app(Application::default()) + event_loop.run(init) } #[cfg(not(any(x11_platform, macos_platform, windows_platform)))] diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 47a518c181..25be16061f 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -43,16 +43,31 @@ fn main() -> Result<(), impl std::error::Error> { let event_loop = EventLoop::new().unwrap(); - event_loop.run_app(ControlFlowDemo::default()) + event_loop.run(ControlFlowDemo::new) } -#[derive(Default)] struct ControlFlowDemo { mode: Mode, request_redraw: bool, wait_cancelled: bool, close_requested: bool, - window: Option>, + window: Box, +} + +impl ControlFlowDemo { + fn new(event_loop: &dyn ActiveEventLoop) -> Self { + let window_attributes = WindowAttributes::default().with_title( + "Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.", + ); + let window = event_loop.create_window(window_attributes).unwrap(); + Self { + mode: Mode::default(), + request_redraw: false, + wait_cancelled: false, + close_requested: false, + window, + } + } } impl ApplicationHandler for ControlFlowDemo { @@ -65,13 +80,6 @@ impl ApplicationHandler for ControlFlowDemo { } } - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - let window_attributes = WindowAttributes::default().with_title( - "Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.", - ); - self.window = Some(event_loop.create_window(window_attributes).unwrap()); - } - fn window_event( &mut self, _event_loop: &dyn ActiveEventLoop, @@ -112,9 +120,8 @@ impl ApplicationHandler for ControlFlowDemo { _ => (), }, WindowEvent::RedrawRequested => { - let window = self.window.as_ref().unwrap(); - window.pre_present_notify(); - fill::fill_window(window.as_ref()); + self.window.pre_present_notify(); + fill::fill_window(&*self.window); }, _ => (), } @@ -122,7 +129,7 @@ impl ApplicationHandler for ControlFlowDemo { fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { if self.request_redraw && !self.wait_cancelled && !self.close_requested { - self.window.as_ref().unwrap().request_redraw(); + self.window.request_redraw(); } match self.mode { diff --git a/examples/pump_events.rs b/examples/pump_events.rs index 3ec8abe38b..039097ca6e 100644 --- a/examples/pump_events.rs +++ b/examples/pump_events.rs @@ -8,7 +8,7 @@ fn main() -> std::process::ExitCode { use std::time::Duration; use winit::application::ApplicationHandler; - use winit::event::WindowEvent; + use winit::event::{StartCause, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}; use winit::window::{Window, WindowAttributes, WindowId}; @@ -22,9 +22,12 @@ fn main() -> std::process::ExitCode { } impl ApplicationHandler for PumpDemo { - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - let window_attributes = WindowAttributes::default().with_title("A fantastic window!"); - self.window = Some(event_loop.create_window(window_attributes).unwrap()); + fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) { + if matches!(cause, StartCause::Init) && self.window.is_none() { + let window_attributes = + WindowAttributes::default().with_title("A fantastic window!"); + self.window = Some(event_loop.create_window(window_attributes).unwrap()); + } } fn window_event( diff --git a/examples/run_on_demand.rs b/examples/run_on_demand.rs index ee856e82b3..bf6ba7a902 100644 --- a/examples/run_on_demand.rs +++ b/examples/run_on_demand.rs @@ -21,14 +21,8 @@ fn main() -> Result<(), Box> { window: Option>, } - impl ApplicationHandler for App { - fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) { - if let Some(window) = self.window.as_ref() { - window.request_redraw(); - } - } - - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { + impl App { + fn init_window(&mut self, event_loop: &dyn ActiveEventLoop) { let window_attributes = WindowAttributes::default() .with_title("Fantastic window number one!") .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)); @@ -36,6 +30,14 @@ fn main() -> Result<(), Box> { self.window_id = Some(window.id()); self.window = Some(window); } + } + + impl ApplicationHandler for App { + fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) { + if let Some(window) = self.window.as_ref() { + window.request_redraw(); + } + } fn window_event( &mut self, @@ -81,14 +83,20 @@ fn main() -> Result<(), Box> { let mut event_loop = EventLoop::new().unwrap(); let mut app = App { idx: 1, ..Default::default() }; - event_loop.run_app_on_demand(&mut app)?; + event_loop.run_on_demand(|event_loop| { + app.init_window(event_loop); + &mut app + })?; println!("--------------------------------------------------------- Finished first loop"); println!("--------------------------------------------------------- Waiting 5 seconds"); std::thread::sleep(Duration::from_secs(5)); app.idx += 1; - event_loop.run_app_on_demand(&mut app)?; + event_loop.run_on_demand(|event_loop| { + app.init_window(event_loop); + &mut app + })?; println!("--------------------------------------------------------- Finished second loop"); Ok(()) } diff --git a/examples/window.rs b/examples/window.rs index c546fb5d06..456e0ba7a3 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -66,8 +66,7 @@ fn main() -> Result<(), Box> { }); } - let app = Application::new(&event_loop, receiver, sender); - Ok(event_loop.run_app(app)?) + Ok(event_loop.run(|event_loop| Application::new(event_loop, receiver, sender))?) } /// Application state and event handling. @@ -84,21 +83,24 @@ struct Application { /// /// With OpenGL it could be EGLDisplay. #[cfg(not(any(android_platform, ios_platform)))] - context: Option>>, + context: Context>, } impl Application { - fn new(event_loop: &EventLoop, receiver: Receiver, sender: Sender) -> Self { - // SAFETY: we drop the context right before the event loop is stopped, thus making it safe. + fn new( + event_loop: &dyn ActiveEventLoop, + receiver: Receiver, + sender: Sender, + ) -> Self { + // SAFETY: The context is stored in the application, which is dropped right before the event + // loop is stopped, thus making it safe. #[cfg(not(any(android_platform, ios_platform)))] - let context = Some( - Context::new(unsafe { - std::mem::transmute::, DisplayHandle<'static>>( - event_loop.display_handle().unwrap(), - ) - }) - .unwrap(), - ); + let context = Context::new(unsafe { + std::mem::transmute::, DisplayHandle<'static>>( + event_loop.display_handle().unwrap(), + ) + }) + .unwrap(); // You'll have to choose an icon size at your own discretion. On X11, the desired size // varies by WM, and on Windows, you still have to account for screen scaling. Here @@ -116,7 +118,7 @@ impl Application { .into_iter() .collect(); - Self { + let mut app = Self { receiver, sender, #[cfg(not(any(android_platform, ios_platform)))] @@ -124,7 +126,16 @@ impl Application { custom_cursors, icon, windows: Default::default(), - } + }; + + app.dump_monitors(event_loop); + + // Create initial window. + app.create_window(event_loop, None).expect("failed to create initial window"); + + app.print_help(); + + app } fn create_window( @@ -528,16 +539,6 @@ impl ApplicationHandler for Application { info!("Device {device_id:?} event: {event:?}"); } - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - info!("Ready to create surfaces"); - self.dump_monitors(event_loop); - - // Create initial window. - self.create_window(event_loop, None).expect("failed to create initial window"); - - self.print_help(); - } - fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { if self.windows.is_empty() { info!("No windows left, exiting..."); @@ -546,9 +547,17 @@ impl ApplicationHandler for Application { } #[cfg(not(any(android_platform, ios_platform)))] - fn exiting(&mut self, _event_loop: &dyn ActiveEventLoop) { - // We must drop the context here. - self.context = None; + fn destroy_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) { + for window in self.windows.values_mut() { + window.surface = None; + } + } + + #[cfg(not(any(android_platform, ios_platform)))] + fn recreate_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) { + for window in self.windows.values_mut() { + window.surface = Some(Surface::new(&self.context, Arc::clone(&window.window)).unwrap()); + } } } @@ -557,10 +566,8 @@ struct WindowState { /// IME input. ime: bool, /// Render surface. - /// - /// NOTE: This surface must be dropped before the `Window`. #[cfg(not(any(android_platform, ios_platform)))] - surface: Surface, Arc>, + surface: Option, Arc>>, /// The actual winit Window. window: Arc, /// The window theme we're drawing with. @@ -593,10 +600,8 @@ impl WindowState { fn new(app: &Application, window: Box) -> Result> { let window: Arc = Arc::from(window); - // SAFETY: the surface is dropped before the `window` which provided it with handle, thus - // it doesn't outlive it. #[cfg(not(any(android_platform, ios_platform)))] - let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?; + let surface = Some(Surface::new(&app.context, Arc::clone(&window))?); let theme = window.theme().unwrap_or(Theme::Dark); info!("Theme: {theme:?}"); @@ -803,7 +808,11 @@ impl WindowState { (Some(width), Some(height)) => (width, height), _ => return, }; - self.surface.resize(width, height).expect("failed to resize inner buffer"); + self.surface + .as_mut() + .unwrap() + .resize(width, height) + .expect("failed to resize inner buffer"); } self.window.request_redraw(); } @@ -904,7 +913,7 @@ impl WindowState { Theme::Dark => DARK_GRAY, }; - let mut buffer = self.surface.buffer_mut()?; + let mut buffer = self.surface.as_mut().unwrap().buffer_mut()?; buffer.fill(color); self.window.pre_present_notify(); buffer.present()?; diff --git a/examples/x11_embed.rs b/examples/x11_embed.rs index 9f52741016..19b5257624 100644 --- a/examples/x11_embed.rs +++ b/examples/x11_embed.rs @@ -13,39 +13,28 @@ fn main() -> Result<(), Box> { mod fill; pub struct XEmbedDemo { - parent_window_id: u32, - window: Option>, + window: Box, } impl ApplicationHandler for XEmbedDemo { - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - let window_attributes = WindowAttributes::default() - .with_title("An embedded window!") - .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) - .with_embed_parent_window(self.parent_window_id); - - self.window = Some(event_loop.create_window(window_attributes).unwrap()); - } - fn window_event( &mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent, ) { - let window = self.window.as_ref().unwrap(); match event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::RedrawRequested => { - window.pre_present_notify(); - fill::fill_window(window.as_ref()); + self.window.pre_present_notify(); + fill::fill_window(&*self.window); }, _ => (), } } fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) { - self.window.as_ref().unwrap().request_redraw(); + self.window.request_redraw(); } } @@ -58,7 +47,15 @@ fn main() -> Result<(), Box> { tracing_subscriber::fmt::init(); let event_loop = EventLoop::new()?; - Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?) + Ok(event_loop.run(|event_loop| { + let window_attributes = WindowAttributes::default() + .with_title("An embedded window!") + .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) + .with_embed_parent_window(parent_window_id); + + let window = event_loop.create_window(window_attributes).unwrap(); + XEmbedDemo { window } + })?) } #[cfg(not(x11_platform))] diff --git a/src/application.rs b/src/application.rs index 8b268b05d5..1ab3a42744 100644 --- a/src/application.rs +++ b/src/application.rs @@ -5,6 +5,8 @@ use crate::event_loop::ActiveEventLoop; use crate::window::WindowId; /// The handler of the application events. +/// +/// This is dropped when the event loop is being shut down. pub trait ApplicationHandler { /// Emitted when new events arrive from the OS to be processed. /// @@ -50,49 +52,6 @@ pub trait ApplicationHandler { let _ = event_loop; } - /// Emitted from the point onwards the application should create render surfaces. - /// - /// See [`destroy_surfaces()`]. - /// - /// ## Portability - /// - /// It's recommended that applications should only initialize their render surfaces after the - /// [`can_create_surfaces()`] method is called. Some systems (specifically Android) won't allow - /// applications to create a render surface until that point. - /// - /// For consistency, all platforms call this method even if they don't themselves have a formal - /// surface destroy/create lifecycle. For systems without a surface destroy/create lifecycle the - /// [`can_create_surfaces()`] event is always emitted after the [`StartCause::Init`] event. - /// - /// Applications should be able to gracefully handle back-to-back [`can_create_surfaces()`] and - /// [`destroy_surfaces()`] calls. - /// - /// ## Platform-specific - /// - /// ### Android - /// - /// On Android, the [`can_create_surfaces()`] method is called when a new [`SurfaceView`] has - /// been created. This is expected to closely correlate with the [`onResume`] lifecycle - /// event but there may technically be a discrepancy. - /// - /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() - /// - /// Applications that need to run on Android must wait until they have been "resumed" before - /// they will be able to create a render surface (such as an `EGLSurface`, [`VkSurfaceKHR`] - /// or [`wgpu::Surface`]) which depend on having a [`SurfaceView`]. Applications must also - /// assume that if they are [suspended], then their render surfaces are invalid and should - /// be dropped. - /// - /// [suspended]: Self::destroy_surfaces - /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView - /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle - /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html - /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html - /// - /// [`can_create_surfaces()`]: Self::can_create_surfaces - /// [`destroy_surfaces()`]: Self::destroy_surfaces - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop); - /// Called after a wake up is requested using [`EventLoopProxy::wake_up()`]. /// /// Multiple calls to the aforementioned method will be merged, and will only wake the event @@ -132,8 +91,6 @@ pub trait ApplicationHandler { /// # ) { /// # } /// # - /// # fn can_create_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {} - /// # /// fn proxy_wake_up(&mut self, _event_loop: &dyn ActiveEventLoop) { /// // Iterate current events, since wake-ups may have been merged. /// // @@ -152,8 +109,6 @@ pub trait ApplicationHandler { /// /// let (sender, receiver) = mpsc::channel(); /// - /// let mut app = MyApp { receiver }; - /// /// // Send an event in a loop /// let proxy = event_loop.create_proxy(); /// let background_thread = thread::spawn(move || { @@ -172,9 +127,8 @@ pub trait ApplicationHandler { /// } /// }); /// - /// event_loop.run_app(&mut app)?; + /// event_loop.run(|_event_loop| MyApp { receiver })?; /// - /// drop(app); /// background_thread.join().unwrap(); /// /// Ok(()) @@ -253,48 +207,52 @@ pub trait ApplicationHandler { /// Emitted when the application must destroy its render surfaces. /// - /// See [`can_create_surfaces()`] for more details. + /// [`recreate_surfaces()`] is emitted after this when it's safe to re-create surfaces. /// /// ## Platform-specific /// /// ### Android /// - /// On Android, the [`destroy_surfaces()`] method is called when the application's associated - /// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`] - /// lifecycle event but there may technically be a discrepancy. + /// Applications that need to run on Android must be able to handle their underlying + /// [`SurfaceView`] being destroyed, which in turn indirectly invalidates any existing + /// render surfaces that may have been created outside of Winit (such as an `EGLSurface`, + /// [`VkSurfaceKHR`] or [`wgpu::Surface`]). /// - /// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause() + /// This means that in this method, you must drop all render surfaces before the event callback + /// completes, and only re-create them in [`recreate_surfaces()`]. /// - /// Applications that need to run on Android should assume their [`SurfaceView`] has been - /// destroyed, which indirectly invalidates any existing render surfaces that may have been - /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). + /// This is expected to closely correlate with the [`onPause`] lifecycle event but there may + /// technically be a discrepancy. /// - /// After being [suspended] on Android applications must drop all render surfaces before - /// the event callback completes, which may be re-created when the application is next - /// [resumed]. - /// - /// [suspended]: Self::destroy_surfaces - /// [resumed]: Self::can_create_surfaces + /// [`recreate_surfaces()`]: Self::recreate_surfaces /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView - /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html - /// - /// ### Others - /// - /// - **iOS / macOS / Orbital / Wayland / Web / Windows / X11:** Unsupported. - /// - /// [`can_create_surfaces()`]: Self::can_create_surfaces - /// [`destroy_surfaces()`]: Self::destroy_surfaces + /// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause() fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { let _ = event_loop; } - /// Emitted when the event loop is being shut down. + /// Emitted when the application should re-create render surfaces, after destroying them in + /// [`destroy_surfaces()`], see that for details. + /// + /// ## Platform-specific + /// + /// ### Android /// - /// This is irreversible - if this method is called, it is guaranteed that the event loop - /// will exit right after. - fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) { + /// On Android, surfaces should be created in the initialization closure given to + /// [`EventLoop::run()`], just like on all other platforms. When they need to be re-created + /// after being destroyed by the system, because a new [`SurfaceView`] has been created, you + /// will need to use this callback. + /// + /// This is expected to closely correlate with the [`onResume`] lifecycle event but there may + /// technically be a discrepancy. + /// + /// [`destroy_surfaces()`]: Self::destroy_surfaces + /// [`EventLoop::run()`]: crate::event_loop::EventLoop::run + /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView + /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() + fn recreate_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { let _ = event_loop; } @@ -339,11 +297,6 @@ impl ApplicationHandler for &mut A { (**self).resumed(event_loop); } - #[inline] - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - (**self).can_create_surfaces(event_loop); - } - #[inline] fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) { (**self).proxy_wake_up(event_loop); @@ -385,8 +338,8 @@ impl ApplicationHandler for &mut A { } #[inline] - fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) { - (**self).exiting(event_loop); + fn recreate_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { + (**self).recreate_surfaces(event_loop); } #[inline] @@ -407,11 +360,6 @@ impl ApplicationHandler for Box { (**self).resumed(event_loop); } - #[inline] - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - (**self).can_create_surfaces(event_loop); - } - #[inline] fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) { (**self).proxy_wake_up(event_loop); @@ -453,8 +401,8 @@ impl ApplicationHandler for Box { } #[inline] - fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) { - (**self).exiting(event_loop); + fn recreate_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { + (**self).recreate_surfaces(event_loop); } #[inline] diff --git a/src/event.rs b/src/event.rs index ec7fd97569..f0552957da 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,39 +1,4 @@ //! The event enums and assorted supporting types. -//! -//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get -//! processed and used to modify the program state. For more details, see the root-level -//! documentation. -//! -//! Some of these events represent different "parts" of a traditional event-handling loop. You could -//! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this: -//! -//! ```rust,ignore -//! let mut start_cause = StartCause::Init; -//! -//! while !elwt.exiting() { -//! app.new_events(event_loop, start_cause); -//! -//! for event in (window events, user events, device events) { -//! // This will pick the right method on the application based on the event. -//! app.handle_event(event_loop, event); -//! } -//! -//! for window_id in (redraw windows) { -//! app.window_event(event_loop, window_id, RedrawRequested); -//! } -//! -//! app.about_to_wait(event_loop); -//! start_cause = wait_if_necessary(); -//! } -//! -//! app.exiting(event_loop); -//! ``` -//! -//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully -//! describes what happens in what order. -//! -//! [`EventLoop::run_app(...)`]: crate::event_loop::EventLoop::run_app -//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil use std::path::PathBuf; use std::sync::{Mutex, Weak}; #[cfg(not(web_platform))] @@ -85,11 +50,6 @@ pub(crate) enum Event { /// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended Suspended, - /// See [`ApplicationHandler::can_create_surfaces`] for details. - /// - /// [`ApplicationHandler::can_create_surfaces`]: crate::application::ApplicationHandler::can_create_surfaces - CreateSurfaces, - /// See [`ApplicationHandler::resumed`] for details. /// /// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed @@ -100,11 +60,6 @@ pub(crate) enum Event { /// [`ApplicationHandler::about_to_wait`]: crate::application::ApplicationHandler::about_to_wait AboutToWait, - /// See [`ApplicationHandler::exiting`] for details. - /// - /// [`ApplicationHandler::exiting`]: crate::application::ApplicationHandler::exiting - LoopExiting, - /// See [`ApplicationHandler::memory_warning`] for details. /// /// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning @@ -1055,7 +1010,6 @@ mod tests { let wid = WindowId::dummy(); x(NewEvents(event::StartCause::Init)); x(AboutToWait); - x(LoopExiting); x(Suspended); x(Resumed); diff --git a/src/event_loop.rs b/src/event_loop.rs index d631e0d847..d23e55377e 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -193,10 +193,48 @@ impl EventLoop { } impl EventLoop { - /// Run the application with the event loop on the calling thread. + /// Run the event loop on the current thread. + /// + /// You pass in a closure that returns your application state. This closure has access to the + /// currently running event loop, allowing you to initialize your windows and surfaces in here. /// /// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// + /// ## Event loop flow + /// + /// This function internally handles the different parts of a traditional event-handling loop. + /// You could imagine this method being implemented like this: + /// + /// ```rust,ignore + /// // Initialize. + /// let mut app = init_closure(event_loop); + /// let mut start_cause = StartCause::Init; + /// + /// // Run loop. + /// while !elwt.exiting() { + /// // Wake up. + /// app.new_events(event_loop, start_cause); + /// + /// // Handle events by the user. + /// for (device_id, event) in incoming_device_events { + /// app.device_event(event_loop, device_id, event); + /// } + /// for (window_id, event) in incoming_window_events { + /// app.window_event(event_loop, window_id, event); + /// } + /// + /// // Done handling events, wait until we're woken up again. + /// app.about_to_wait(event_loop); + /// start_cause = wait_if_necessary(); + /// } + /// + /// // Finished running, drop application state. + /// drop(app); + /// ``` + /// + /// This is of course a very coarse-grained overview, and leaves out timing details like + /// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`]. + /// /// ## Platform-specific /// /// - **iOS:** Will never return to the caller and so values not passed to this function will @@ -211,21 +249,32 @@ impl EventLoop { doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]" )] #[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")] - /// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to + /// [^1] instead of [`run()`] to avoid the need for the Javascript exception trick, and to /// make it clearer that the event loop runs asynchronously (via the browser's own, /// internal, event loop) and doesn't block the current thread of execution like it does /// on other platforms. /// /// This function won't be available with `target_feature = "exception-handling"`. + /// - **Android:** The initialization closure is only called once the application is at a point + /// where the underlying surface is initialized. /// /// [^1]: `spawn_app()` is only available on the Web platform. /// /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() - /// [`run_app()`]: Self::run_app() + /// [`run()`]: Self::run() #[inline] #[cfg(not(all(web_platform, target_feature = "exception-handling")))] + pub fn run( + self, + init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + self.event_loop.run(init_closure) + } + + /// Run the event loop with the given application state. + #[deprecated = "less flexible version of `run`"] pub fn run_app(self, app: A) -> Result<(), EventLoopError> { - self.event_loop.run_app(app) + self.run(|_event_loop| app) } /// Creates an [`EventLoopProxy`] that can be used to dispatch user events @@ -391,9 +440,7 @@ pub trait ActiveEventLoop: AsAny { /// Gets the current [`ControlFlow`]. fn control_flow(&self) -> ControlFlow; - /// This exits the event loop. - /// - /// See [`exiting`][crate::application::ApplicationHandler::exiting]. + /// This stops the event loop. fn exit(&self); /// Returns if the [`EventLoop`] is about to stop. diff --git a/src/lib.rs b/src/lib.rs index f747c13928..b62bfb3c0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,13 @@ //! Winit is a cross-platform window creation and event loop management library. //! -//! # Building windows -//! -//! Before you can create a [`Window`], you first need to build an [`EventLoop`]. This is done with -//! the [`EventLoop::new()`] function. -//! -//! ```no_run -//! use winit::event_loop::EventLoop; -//! let event_loop = EventLoop::new().unwrap(); -//! ``` +//! # Event handling //! -//! Then you create a [`Window`] with [`create_window`]. +//! Basically all of the functionality that Winit exposes requires an [`ActiveEventLoop`], which you +//! can get access to by running an [`EventLoop`] using [`EventLoop::new()`] and +//! [`EventLoop::run()`]. //! -//! # Event handling +//! Once it's running, you can create your [`Window`]s with [`ActiveEventLoop::create_window()`] by +//! passing in the desired [`WindowAttributes`]. //! //! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can //! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the @@ -21,9 +16,10 @@ //! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a //! [`DeviceEvent`]. //! -//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will -//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and -//! will run until [`exit()`] is used, at which point [`exiting()`] is called. +//! You retrieve by implementing [`ApplicationHandler`] for a new type, which will be the state of +//! your application. The methods in this trait will continuously receive events until +//! [`ActiveEventLoop::exit()`] is used, at which point your application state will be dropped, and +//! the application shuts down. //! //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator`-based event loop //! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works @@ -44,19 +40,20 @@ //! use winit::application::ApplicationHandler; //! use winit::event::WindowEvent; //! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; -//! use winit::window::{Window, WindowId, WindowAttributes}; +//! use winit::window::{Window, WindowAttributes, WindowId}; //! -//! #[derive(Default)] //! struct App { -//! window: Option>, +//! window: Box, //! } //! //! impl ApplicationHandler for App { -//! fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { -//! self.window = Some(event_loop.create_window(WindowAttributes::default()).unwrap()); -//! } -//! -//! fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, id: WindowId, event: WindowEvent) { +//! fn window_event( +//! &mut self, +//! event_loop: &dyn ActiveEventLoop, +//! id: WindowId, +//! event: WindowEvent, +//! ) { +//! // Called by `EventLoop::run` when a new event happens on the window. //! match event { //! WindowEvent::CloseRequested => { //! println!("The close button was pressed; stopping"); @@ -74,28 +71,45 @@ //! // Queue a RedrawRequested event. //! // //! // You only need to call this if you've determined that you need to redraw in -//! // applications which do not always need to. Applications that redraw continuously -//! // can render here instead. -//! self.window.as_ref().unwrap().request_redraw(); -//! } +//! // applications which do not always need to. +//! self.window.request_redraw(); +//! }, //! _ => (), //! } //! } //! } //! -//! let event_loop = EventLoop::new().unwrap(); +//! fn main() -> Result<(), Box> { +//! // Create a new event loop. +//! let event_loop = EventLoop::new()?; +//! +//! // Configure settings before launching. +//! +//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't +//! // dispatched any events. This is ideal for games and similar applications. +//! event_loop.set_control_flow(ControlFlow::Poll); //! -//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't -//! // dispatched any events. This is ideal for games and similar applications. -//! event_loop.set_control_flow(ControlFlow::Poll); +//! // ControlFlow::Wait pauses the event loop if no events are available to process. +//! // This is ideal for non-game applications that only update in response to user +//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. +//! event_loop.set_control_flow(ControlFlow::Wait); //! -//! // ControlFlow::Wait pauses the event loop if no events are available to process. -//! // This is ideal for non-game applications that only update in response to user -//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. -//! event_loop.set_control_flow(ControlFlow::Wait); +//! // Launch and begin running our event loop. +//! event_loop.run(|event_loop| { +//! // The event loop has launched, and we can initialize our UI state in this closure. //! -//! let mut app = App::default(); -//! event_loop.run_app(&mut app); +//! // Create a simple window with default attributes. +//! let window = event_loop +//! .create_window(WindowAttributes::default()) +//! .expect("failed creating window"); +//! +//! // Give our newly created application state to Winit, which will, when necessary, call +//! // the `ApplicationHandler` methods configured above. +//! App { window } +//! })?; +//! +//! Ok(()) +//! } //! ``` //! //! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be @@ -147,20 +161,21 @@ //! See the [`platform`] module for documentation on platform-specific cargo //! features. //! +//! [`ActiveEventLoop`]: event_loop::ActiveEventLoop //! [`EventLoop`]: event_loop::EventLoop //! [`EventLoop::new()`]: event_loop::EventLoop::new -//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app -//! [`exit()`]: event_loop::ActiveEventLoop::exit +//! [`EventLoop::run()`]: event_loop::EventLoop::run +//! [`ActiveEventLoop::exit()`]: event_loop::ActiveEventLoop::exit //! [`Window`]: window::Window //! [`WindowId`]: window::WindowId //! [`WindowAttributes`]: window::WindowAttributes //! [window_new]: window::Window::new -//! [`create_window`]: event_loop::ActiveEventLoop::create_window +//! [`ActiveEventLoop::create_window()`]: event_loop::ActiveEventLoop::create_window //! [`Window::id()`]: window::Window::id //! [`WindowEvent`]: event::WindowEvent //! [`DeviceEvent`]: event::DeviceEvent +//! [`ApplicationHandler`]: application::ApplicationHandler //! [`Event::UserEvent`]: event::Event::UserEvent -//! [`exiting()`]: crate::application::ApplicationHandler::exiting //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle //! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle //! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland. diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 6121dbf0c6..a97cae870d 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -65,7 +65,7 @@ //! let app = NSApplication::sharedApplication(mtm); //! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate))); //! -//! // event_loop.run_app(&mut my_app); +//! // event_loop.run(|event_loop| { ... })?; //! Ok(()) //! } //! ``` diff --git a/src/platform/run_on_demand.rs b/src/platform/run_on_demand.rs index 851e3450fc..bd4f0f0f06 100644 --- a/src/platform/run_on_demand.rs +++ b/src/platform/run_on_demand.rs @@ -1,16 +1,14 @@ use crate::application::ApplicationHandler; use crate::error::EventLoopError; -use crate::event_loop::EventLoop; +use crate::event_loop::{ActiveEventLoop, EventLoop}; #[cfg(doc)] -use crate::{ - event_loop::ActiveEventLoop, platform::pump_events::EventLoopExtPumpEvents, window::Window, -}; +use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window}; /// Additional methods on [`EventLoop`] to return control flow to the caller. pub trait EventLoopExtRunOnDemand { /// Run the application with the event loop on the calling thread. /// - /// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) + /// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) /// closures and it is possible to return control back to the caller without /// consuming the `EventLoop` (by using [`exit()`]) and /// so the event loop can be re-run after it has exit. @@ -23,7 +21,7 @@ pub trait EventLoopExtRunOnDemand { /// to while maintaining the full state of your application. (If you need something like this /// you can look at the [`EventLoopExtPumpEvents::pump_app_events()`] API) /// - /// Each time `run_app_on_demand` is called the startup sequence of `init`, followed by + /// Each time `run_on_demand` is called the startup sequence of `init`, followed by /// `resume` is being preserved. /// /// See the [`set_control_flow()`] docs on how to change the event loop's behavior. @@ -40,7 +38,7 @@ pub trait EventLoopExtRunOnDemand { /// [^1] more than once instead). /// - No [`Window`] state can be carried between separate runs of the event loop. /// - /// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you + /// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you /// specifically need the ability to re-run a single event loop more than once /// /// # Supported Platforms @@ -60,12 +58,18 @@ pub trait EventLoopExtRunOnDemand { /// /// [`exit()`]: ActiveEventLoop::exit() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() - fn run_app_on_demand(&mut self, app: A) -> Result<(), EventLoopError>; + fn run_on_demand( + &mut self, + init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, + ) -> Result<(), EventLoopError>; } impl EventLoopExtRunOnDemand for EventLoop { - fn run_app_on_demand(&mut self, app: A) -> Result<(), EventLoopError> { - self.event_loop.run_app_on_demand(app) + fn run_on_demand( + &mut self, + init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + self.event_loop.run_on_demand(init_closure) } } diff --git a/src/platform/web.rs b/src/platform/web.rs index a2c5c2651e..cd120ab774 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -192,10 +192,10 @@ pub trait EventLoopExtWeb { /// Initializes the winit event loop. /// /// Unlike - #[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run_app()`")] + #[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run()`")] #[cfg_attr( not(all(web_platform, target_feature = "exception-handling")), - doc = "[`run_app()`]" + doc = "[`run()`]" )] /// [^1], this returns immediately, and doesn't throw an exception in order to /// satisfy its [`!`] return type. @@ -208,9 +208,9 @@ pub trait EventLoopExtWeb { /// #[cfg_attr( not(all(web_platform, target_feature = "exception-handling")), - doc = "[`run_app()`]: EventLoop::run_app()" + doc = "[`run()`]: EventLoop::run()" )] - /// [^1]: `run_app()` is _not_ available on Wasm when the target supports `exception-handling`. + /// [^1]: `run()` is _not_ available on Wasm when the target supports `exception-handling`. fn spawn_app(self, app: A); /// Sets the strategy for [`ControlFlow::Poll`]. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 88fa45bba7..2d035d2e0b 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -1,5 +1,6 @@ use std::cell::Cell; use std::hash::Hash; +use std::mem::replace; use std::num::{NonZeroU16, NonZeroU32}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -122,6 +123,55 @@ impl Default for PlatformSpecificEventLoopAttributes { } } +enum ApplicationState { + SurfacesNotReady(F), + Ready(A), + Invalid, +} + +impl A> ApplicationState { + fn surfaces_ready(&mut self, event_loop: &dyn RootActiveEventLoop) { + // Temporarily transition to an invalid state to make the compiler happy + match replace(self, Self::Invalid) { + Self::SurfacesNotReady(handler) => { + let mut app = handler(event_loop); + // We run lifecycle events here that didn't get emitted before now because the + // application wasn't yet ready. + app.new_events(event_loop, StartCause::Init); + + *self = Self::Ready(app); + }, + Self::Ready(mut app) => { + app.recreate_surfaces(event_loop); + *self = Self::Ready(app); + }, + Self::Invalid => unreachable!("invalid state"), + } + } + + #[track_caller] + fn handle_if_ready(&mut self, closure: impl FnOnce(&mut A)) { + match self { + Self::SurfacesNotReady(_) => { + tracing::trace!("event happened before application handler was initialized") + }, + Self::Ready(app) => closure(app), + Self::Invalid => unreachable!("invalid state"), + } + } + + #[track_caller] + fn handle(&mut self, closure: impl FnOnce(&mut A)) { + match self { + Self::SurfacesNotReady(_) => { + tracing::error!("tried to call application handler before it being initialized") + }, + Self::Ready(app) => closure(app), + Self::Invalid => unreachable!("invalid state"), + } + } +} + impl EventLoop { pub(crate) fn new( attributes: &PlatformSpecificEventLoopAttributes, @@ -157,10 +207,10 @@ impl EventLoop { &self.window_target } - fn single_iteration( + fn single_iteration A>( &mut self, main_event: Option>, - app: &mut A, + app_state: &mut ApplicationState, ) { trace!("Mainloop iteration"); @@ -168,17 +218,17 @@ impl EventLoop { let mut pending_redraw = self.pending_redraw; let mut resized = false; - app.new_events(&self.window_target, cause); + app_state.handle_if_ready(|app| app.new_events(&self.window_target, cause)); if let Some(event) = main_event { trace!("Handling main event {:?}", event); match event { MainEvent::InitWindow { .. } => { - app.can_create_surfaces(&self.window_target); + app_state.surfaces_ready(&self.window_target); }, MainEvent::TerminateWindow { .. } => { - app.destroy_surfaces(&self.window_target); + app_state.handle(|app| app.destroy_surfaces(&self.window_target)); }, MainEvent::WindowResized { .. } => resized = true, MainEvent::RedrawNeeded { .. } => pending_redraw = true, @@ -189,13 +239,13 @@ impl EventLoop { HAS_FOCUS.store(true, Ordering::Relaxed); let window_id = window::WindowId(WindowId); let event = event::WindowEvent::Focused(true); - app.window_event(&self.window_target, window_id, event); + app_state.handle(|app| app.window_event(&self.window_target, window_id, event)); }, MainEvent::LostFocus => { HAS_FOCUS.store(false, Ordering::Relaxed); let window_id = window::WindowId(WindowId); let event = event::WindowEvent::Focused(false); - app.window_event(&self.window_target, window_id, event); + app_state.handle(|app| app.window_event(&self.window_target, window_id, event)); }, MainEvent::ConfigChanged { .. } => { let old_scale_factor = scale_factor(&self.android_app); @@ -210,11 +260,12 @@ impl EventLoop { scale_factor, }; - app.window_event(&self.window_target, window_id, event); + app_state + .handle(|app| app.window_event(&self.window_target, window_id, event)); } }, MainEvent::LowMemory => { - app.memory_warning(&self.window_target); + app_state.handle(|app| app.memory_warning(&self.window_target)); }, MainEvent::Start => { // XXX: how to forward this state to applications? @@ -259,7 +310,7 @@ impl EventLoop { let android_app = self.android_app.clone(); // Process input events - match android_app.input_events_iter() { + app_state.handle_if_ready(|app| match android_app.input_events_iter() { Ok(mut input_iter) => loop { let read_event = input_iter.next(|event| self.handle_input_event(&android_app, event, app)); @@ -271,11 +322,13 @@ impl EventLoop { Err(err) => { tracing::warn!("Failed to get input events iterator: {err:?}"); }, - } + }); - if self.window_target.proxy_wake_up.swap(false, Ordering::Relaxed) { - app.proxy_wake_up(&self.window_target); - } + app_state.handle_if_ready(|app| { + if self.window_target.proxy_wake_up.swap(false, Ordering::Relaxed) { + app.proxy_wake_up(&self.window_target); + } + }); if self.running { if resized { @@ -288,7 +341,7 @@ impl EventLoop { }; let window_id = window::WindowId(WindowId); let event = event::WindowEvent::Resized(size); - app.window_event(&self.window_target, window_id, event); + app_state.handle(|app| app.window_event(&self.window_target, window_id, event)); } pending_redraw |= self.redraw_flag.get_and_reset(); @@ -296,12 +349,12 @@ impl EventLoop { pending_redraw = false; let window_id = window::WindowId(WindowId); let event = event::WindowEvent::RedrawRequested; - app.window_event(&self.window_target, window_id, event); + app_state.handle(|app| app.window_event(&self.window_target, window_id, event)); } } // This is always the last event we dispatch before poll again - app.about_to_wait(&self.window_target); + app_state.handle_if_ready(|app| app.about_to_wait(&self.window_target)); self.pending_redraw = pending_redraw; } @@ -413,17 +466,21 @@ impl EventLoop { input_status } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) + pub fn run( + mut self, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + self.run_on_demand(init_closure) } - pub fn run_app_on_demand( + pub fn run_on_demand( &mut self, - mut app: A, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, ) -> Result<(), EventLoopError> { self.window_target.clear_exit(); + let mut app_state = ApplicationState::SurfacesNotReady(init_closure); loop { - match self.pump_app_events(None, &mut app) { + match self.pump_app_events_inner(None, &mut app_state) { PumpStatus::Exit(0) => { break Ok(()); }, @@ -440,7 +497,21 @@ impl EventLoop { pub fn pump_app_events( &mut self, timeout: Option, - mut app: A, + app: A, + ) -> PumpStatus { + fn type_helper(_event_loop: &dyn RootActiveEventLoop) -> A { + unimplemented!() + } + #[allow(unused_assignments)] + let mut app_state = ApplicationState::SurfacesNotReady(type_helper::); + app_state = ApplicationState::Ready(app); + self.pump_app_events_inner(timeout, &mut app_state) + } + + fn pump_app_events_inner A>( + &mut self, + timeout: Option, + app_state: &mut ApplicationState, ) -> PumpStatus { if !self.loop_running { self.loop_running = true; @@ -452,29 +523,27 @@ impl EventLoop { self.cause = StartCause::Init; // run the initial loop iteration - self.single_iteration(None, &mut app); + self.single_iteration(None, app_state); } // Consider the possibility that the `StartCause::Init` iteration could // request to Exit if !self.exiting() { - self.poll_events_with_timeout(timeout, &mut app); + self.poll_events_with_timeout(timeout, app_state); } if self.exiting() { self.loop_running = false; - app.exiting(&self.window_target); - PumpStatus::Exit(0) } else { PumpStatus::Continue } } - fn poll_events_with_timeout( + fn poll_events_with_timeout A>( &mut self, mut timeout: Option, - app: &mut A, + app_state: &mut ApplicationState, ) { let start = Instant::now(); @@ -540,7 +609,7 @@ impl EventLoop { }, }; - self.single_iteration(main_event, app); + self.single_iteration(main_event, app_state); }); } diff --git a/src/platform_impl/apple/appkit/app_state.rs b/src/platform_impl/apple/appkit/app_state.rs index 1b68717378..cfed761601 100644 --- a/src/platform_impl/apple/appkit/app_state.rs +++ b/src/platform_impl/apple/appkit/app_state.rs @@ -14,7 +14,7 @@ use super::observer::{EventLoopWaker, RunLoop}; use super::{menu, WindowId}; use crate::application::ApplicationHandler; use crate::event::{StartCause, WindowEvent}; -use crate::event_loop::ControlFlow; +use crate::event_loop::{ActiveEventLoop, ControlFlow}; use crate::window::WindowId as RootWindowId; #[derive(Debug)] @@ -153,12 +153,12 @@ impl AppState { /// Place the event handler in the application state for the duration /// of the given closure. - pub fn set_event_handler( + pub fn set_init_closure( &self, - handler: &mut dyn ApplicationHandler, + init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, closure: impl FnOnce() -> R, ) -> R { - self.event_handler.set(handler, closure) + self.event_handler.set(init_closure, closure) } pub fn proxy_wake_up(&self) -> Arc { diff --git a/src/platform_impl/apple/appkit/event_loop.rs b/src/platform_impl/apple/appkit/event_loop.rs index 4f51a4bad3..3b2ec4e31a 100644 --- a/src/platform_impl/apple/appkit/event_loop.rs +++ b/src/platform_impl/apple/appkit/event_loop.rs @@ -278,20 +278,23 @@ impl EventLoop { &self.window_target } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) + pub fn run( + mut self, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + self.run_on_demand(init_closure) } // NB: we don't base this on `pump_events` because for `MacOs` we can't support // `pump_events` elegantly (we just ask to run the loop for a "short" amount of // time and so a layered implementation would end up using a lot of CPU due to // redundant wake ups. - pub fn run_app_on_demand( + pub fn run_on_demand( &mut self, - mut app: A, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, ) -> Result<(), EventLoopError> { self.app_state.clear_exit(); - self.app_state.set_event_handler(&mut app, || { + self.app_state.set_init_closure(init_closure, || { autoreleasepool(|_| { // clear / normalize pump_events state self.app_state.set_wait_timeout(None); diff --git a/src/platform_impl/apple/event_handler.rs b/src/platform_impl/apple/event_handler.rs index 37765d760c..a314a2ee3f 100644 --- a/src/platform_impl/apple/event_handler.rs +++ b/src/platform_impl/apple/event_handler.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::{fmt, mem}; use crate::application::ApplicationHandler; @@ -11,7 +11,17 @@ pub(crate) struct EventHandler { /// - Not registered by the event loop (None). /// - Present (Some(handler)). /// - Currently executing the handler / in use (RefCell borrowed). - inner: RefCell>, + inner: Cell, +} + +#[derive(Default)] +enum State { + #[default] + NotRegistered, + Registered(Box Box + 'static>), + Ready(Box), + CurrentlyExecuting, + Deinitialized, } impl fmt::Debug for EventHandler { @@ -27,7 +37,7 @@ impl fmt::Debug for EventHandler { impl EventHandler { pub(crate) fn new() -> Self { - Self { inner: RefCell::new(None) } + Self { inner: Cell::new(State::NotRegistered) } } /// Set the event loop handler for the duration of the given closure. @@ -37,7 +47,7 @@ impl EventHandler { /// from within the closure. pub(crate) fn set<'handler, R>( &self, - app: &'handler mut dyn ApplicationHandler, + init_closure: Box Box + 'handler>, closure: impl FnOnce() -> R, ) -> R { // SAFETY: We extend the lifetime of the handler here so that we can @@ -48,8 +58,8 @@ impl EventHandler { // extended beyond `'handler`. let handler = unsafe { mem::transmute::< - &'handler mut dyn ApplicationHandler, - &'static mut dyn ApplicationHandler, + Box, + Box, >(app) }; @@ -120,7 +130,7 @@ impl EventHandler { // // If the handler unwinds, the `RefMut` will ensure that the // handler is no longer borrowed. - callback(*user_app); + callback(user_app); }, Ok(None) => { // `NSApplication`, our app state and this handler are all diff --git a/src/platform_impl/apple/uikit/app_state.rs b/src/platform_impl/apple/uikit/app_state.rs index 376f30b827..37526921fe 100644 --- a/src/platform_impl/apple/uikit/app_state.rs +++ b/src/platform_impl/apple/uikit/app_state.rs @@ -77,7 +77,6 @@ fn handle_event(mtm: MainThreadMarker, event: Event) { Event::UserWakeUp => app.proxy_wake_up(event_loop), Event::Suspended => app.suspended(event_loop), Event::Resumed => app.resumed(event_loop), - Event::CreateSurfaces => app.can_create_surfaces(event_loop), Event::AboutToWait => app.about_to_wait(event_loop), Event::LoopExiting => app.exiting(event_loop), Event::MemoryWarning => app.memory_warning(event_loop), @@ -419,12 +418,8 @@ pub fn did_finish_launching(mtm: MainThreadMarker) { let events = AppState::get_mut(mtm).did_finish_launching_transition(); - let events = [ - EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)), - EventWrapper::StaticEvent(Event::CreateSurfaces), - ] - .into_iter() - .chain(events); + let events = + [EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init))].into_iter().chain(events); handle_nonuser_events(mtm, events); } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 0935d6e1ef..fbf3675cac 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -451,15 +451,18 @@ impl EventLoop { } } - pub fn run_app(self, app: A) -> Result<(), EventLoopError> { - x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app)) + pub fn run( + self, + init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + x11_or_wayland!(match self; EventLoop(evlp) => evlp.run(init_closure)) } - pub fn run_app_on_demand( + pub fn run_on_demand( &mut self, - app: A, + init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A, ) -> Result<(), EventLoopError> { - x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app)) + x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(init_closure)) } pub fn pump_app_events( diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 4dc6747293..d221426c09 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -154,15 +154,21 @@ impl EventLoop { Ok(event_loop) } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) + pub fn run( + mut self, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + self.run_on_demand(init_closure) } - pub fn run_app_on_demand( + pub fn run_on_demand( &mut self, - mut app: A, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, ) -> Result<(), EventLoopError> { self.active_event_loop.clear_exit(); + + let mut app = init_closure(&self.active_event_loop); + let exit = loop { match self.pump_app_events(None, &mut app) { PumpStatus::Exit(0) => { @@ -206,8 +212,6 @@ impl EventLoop { if let Some(code) = self.exit_code() { self.loop_running = false; - app.exiting(&self.active_event_loop); - PumpStatus::Exit(code) } else { PumpStatus::Continue @@ -296,12 +300,6 @@ impl EventLoop { app.new_events(&self.active_event_loop, cause); - // NB: For consistency all platforms must call `can_create_surfaces` even though Wayland - // applications don't themselves have a formal surface destroy/create lifecycle. - if cause == StartCause::Init { - app.can_create_surfaces(&self.active_event_loop); - } - // Indicate user wake up. if self.with_state(|state| mem::take(&mut state.proxy_wake_up)) { app.proxy_wake_up(&self.active_event_loop); diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index b156d7d437..9e4889e87e 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -372,15 +372,21 @@ impl EventLoop { &self.event_processor.target } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) + pub fn run( + mut self, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + self.run_on_demand(init_closure) } - pub fn run_app_on_demand( + pub fn run_on_demand( &mut self, - mut app: A, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, ) -> Result<(), EventLoopError> { self.event_processor.target.clear_exit(); + + let mut app = init_closure(&self.event_processor.target); + let exit = loop { match self.pump_app_events(None, &mut app) { PumpStatus::Exit(0) => { @@ -426,8 +432,6 @@ impl EventLoop { if let Some(code) = self.exit_code() { self.loop_running = false; - app.exiting(self.window_target()); - PumpStatus::Exit(code) } else { PumpStatus::Continue @@ -508,12 +512,6 @@ impl EventLoop { fn single_iteration(&mut self, app: &mut A, cause: StartCause) { app.new_events(&self.event_processor.target, cause); - // NB: For consistency all platforms must call `can_create_surfaces` even though X11 - // applications don't themselves have a formal surface destroy/create lifecycle. - if cause == StartCause::Init { - app.can_create_surfaces(&self.event_processor.target) - } - // Process all pending events self.drain_events(app); diff --git a/src/platform_impl/orbital/event_loop.rs b/src/platform_impl/orbital/event_loop.rs index 7c6f43e67a..28983e4a41 100644 --- a/src/platform_impl/orbital/event_loop.rs +++ b/src/platform_impl/orbital/event_loop.rs @@ -498,15 +498,15 @@ impl EventLoop { } } - pub fn run_app(mut self, mut app: A) -> Result<(), EventLoopError> { + pub fn run( + mut self, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + let mut app = init_closure(&self.window_target); let mut start_cause = StartCause::Init; loop { app.new_events(&self.window_target, start_cause); - if start_cause == StartCause::Init { - app.can_create_surfaces(&self.window_target); - } - // Handle window creates. while let Some(window) = { let mut creates = self.window_target.creates.lock().unwrap(); @@ -671,8 +671,6 @@ impl EventLoop { } } - app.exiting(&self.window_target); - Ok(()) } diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index 89721cac8d..78bc916723 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -95,9 +95,7 @@ fn handle_event(app: &mut A, target: &ActiveEventLoop, ev Event::UserWakeUp => app.proxy_wake_up(target), Event::Suspended => app.suspended(target), Event::Resumed => app.resumed(target), - Event::CreateSurfaces => app.can_create_surfaces(target), Event::AboutToWait => app.about_to_wait(target), - Event::LoopExiting => app.exiting(target), Event::MemoryWarning => app.memory_warning(target), } } diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index 64c65a34eb..9c033a489f 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -451,11 +451,7 @@ impl Shared { } fn init(&self) { - // NB: For consistency all platforms must call `can_create_surfaces` even though Web - // applications don't themselves have a formal surface destroy/create lifecycle. - self.run_until_cleared( - [Event::NewEvents(StartCause::Init), Event::CreateSurfaces].into_iter(), - ); + self.run_until_cleared([Event::NewEvents(StartCause::Init)].into_iter()); } // Run the polling logic for the Poll ControlFlow, which involves clearing the queue @@ -611,7 +607,7 @@ impl Shared { self.apply_control_flow(); // We don't call `handle_loop_destroyed` here because we don't need to // perform cleanup when the Web browser is going to destroy the page. - self.handle_event(Event::LoopExiting); + todo!("drop the application handler"); } // handle_event takes in events and either queues them or applies a callback @@ -715,7 +711,7 @@ impl Shared { } fn handle_loop_destroyed(&self) { - self.handle_event(Event::LoopExiting); + todo!("drop the application handler"); let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut()); *self.0.page_transition_event_handle.borrow_mut() = None; *self.0.on_mouse_move.borrow_mut() = None; diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a4e3ef35b9..cdbd497b0f 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -210,15 +210,21 @@ impl EventLoop { &self.window_target } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) + pub fn run( + mut self, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, + ) -> Result<(), EventLoopError> { + self.run_on_demand(init_closure) } - pub fn run_app_on_demand( + pub fn run_on_demand( &mut self, - mut app: A, + init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A, ) -> Result<(), EventLoopError> { self.window_target.clear_exit(); + + let mut app = init_closure(&self.window_target); + { let runner = &self.window_target.runner_shared; @@ -238,9 +244,7 @@ impl EventLoop { Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref), Event::Suspended => app.suspended(event_loop_windows_ref), Event::Resumed => app.resumed(event_loop_windows_ref), - Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref), Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), - Event::LoopExiting => app.exiting(event_loop_windows_ref), Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), }); } @@ -304,9 +308,7 @@ impl EventLoop { Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref), Event::Suspended => app.suspended(event_loop_windows_ref), Event::Resumed => app.resumed(event_loop_windows_ref), - Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref), Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), - Event::LoopExiting => app.exiting(event_loop_windows_ref), Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), }); @@ -334,8 +336,10 @@ impl EventLoop { PumpStatus::Continue }; - // We wait until we've checked for an exit status before clearing the - // application callback, in case we need to dispatch a LoopExiting event + // We wait until we've checked for an exit status before clearing the application callback, + // in case any of the methods above ends up triggering an event. + // + // This drops the user's `ApplicationHandler`. // // # Safety // This pairs up with our call to `runner.set_event_handler` and ensures diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index f84dde9b31..22c0a73181 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -248,9 +248,8 @@ impl EventLoopRunner { } } - /// Dispatch control flow events (`NewEvents`, `AboutToWait`, and - /// `LoopExiting`) as necessary to bring the internal `RunnerState` to the - /// new runner state. + /// Dispatch control flow events (`new_events`, `about_to_wait`) as necessary to bring the + /// internal `RunnerState` to the new runner state. /// /// The state transitions are defined as follows: /// @@ -294,7 +293,6 @@ impl EventLoopRunner { self.call_new_events(true); self.call_event_handler(Event::AboutToWait); self.last_events_cleared.set(Instant::now()); - self.call_event_handler(Event::LoopExiting); }, (_, Uninitialized) => panic!("cannot move state to Uninitialized"), @@ -302,9 +300,7 @@ impl EventLoopRunner { (Idle, HandlingMainEvents) => { self.call_new_events(false); }, - (Idle, Destroyed) => { - self.call_event_handler(Event::LoopExiting); - }, + (Idle, Destroyed) => {}, (HandlingMainEvents, Idle) => { // This is always the last event we dispatch before waiting for new events @@ -314,7 +310,6 @@ impl EventLoopRunner { (HandlingMainEvents, Destroyed) => { self.call_event_handler(Event::AboutToWait); self.last_events_cleared.set(Instant::now()); - self.call_event_handler(Event::LoopExiting); }, (Destroyed, _) => panic!("cannot move state from Destroyed"), @@ -344,11 +339,6 @@ impl EventLoopRunner { }, }; self.call_event_handler(Event::NewEvents(start_cause)); - // NB: For consistency all platforms must call `can_create_surfaces` even though Windows - // applications don't themselves have a formal surface destroy/create lifecycle. - if init { - self.call_event_handler(Event::CreateSurfaces); - } self.dispatch_buffered_events(); } }