From fbc6fdc30cc9056ca77d62c1ed5da95903e2932b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 3 Dec 2024 12:17:58 +0100 Subject: [PATCH] Add initialization closure and drop on exit --- examples/child_window.rs | 34 +++---- examples/control_flow.rs | 35 +++++--- examples/pump_events.rs | 11 ++- examples/run_on_demand.rs | 28 +++--- examples/window.rs | 89 ++++++++++--------- examples/x11_embed.rs | 29 +++--- src/application.rs | 43 +++------ src/event.rs | 46 ---------- src/event_loop.rs | 59 ++++++++++-- src/lib.rs | 83 +++++++++-------- src/platform/macos.rs | 2 +- src/platform/run_on_demand.rs | 24 ++--- src/platform/web.rs | 8 +- src/platform_impl/android/mod.rs | 16 ++-- src/platform_impl/apple/appkit/app_state.rs | 22 +++-- src/platform_impl/apple/appkit/event_loop.rs | 13 +-- src/platform_impl/apple/event_handler.rs | 67 +++++++++----- src/platform_impl/linux/mod.rs | 13 +-- .../linux/wayland/event_loop/mod.rs | 16 ++-- src/platform_impl/linux/x11/mod.rs | 16 ++-- src/platform_impl/orbital/event_loop.rs | 8 +- src/platform_impl/web/event_loop/mod.rs | 1 - src/platform_impl/web/event_loop/runner.rs | 4 +- src/platform_impl/windows/event_loop.rs | 22 +++-- .../windows/event_loop/runner.rs | 11 +-- 25 files changed, 379 insertions(+), 321 deletions(-) diff --git a/examples/child_window.rs b/examples/child_window.rs index 43ed75c9b1..e932081321 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_surface_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_surface_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 e6d105ad5e..e777a210b7 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_surface_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 3d87e2f8d9..bdd5a5036f 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -70,8 +70,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. @@ -88,21 +87,24 @@ struct Application { /// /// With OpenGL it could be EGLDisplay. #[cfg(not(android_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(android_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 @@ -120,7 +122,7 @@ impl Application { .into_iter() .collect(); - Self { + let mut app = Self { receiver, sender, #[cfg(not(android_platform))] @@ -128,7 +130,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( @@ -560,14 +571,12 @@ 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(); + #[cfg(not(android_platform))] + fn can_create_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()); + window.resize(window.window.surface_size()); + } } fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { @@ -578,9 +587,10 @@ impl ApplicationHandler for Application { } #[cfg(not(android_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(target_os = "macos")] @@ -607,9 +617,9 @@ struct WindowState { ime: bool, /// Render surface. /// - /// NOTE: This surface must be dropped before the `Window`. + /// `None` when not between `can_create_surfaces` and `destroy_surfaces`. #[cfg(not(android_platform))] - surface: Surface, Arc>, + surface: Option, Arc>>, /// The actual winit Window. window: Arc, /// The window theme we're drawing with. @@ -642,11 +652,6 @@ 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(android_platform))] - let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?; - let theme = window.theme().unwrap_or(Theme::Dark); info!("Theme: {theme:?}"); let named_idx = 0; @@ -656,15 +661,14 @@ impl WindowState { let ime = true; window.set_ime_allowed(ime); - let size = window.surface_size(); - let mut state = Self { + Ok(Self { #[cfg(macos_platform)] option_as_alt: window.option_as_alt(), custom_idx: app.custom_cursors.as_ref().map(Vec::len).unwrap_or(1) - 1, cursor_grab: CursorGrabMode::None, named_idx, #[cfg(not(android_platform))] - surface, + surface: None, window, theme, ime, @@ -675,10 +679,7 @@ impl WindowState { rotated: Default::default(), panned: Default::default(), zoom: Default::default(), - }; - - state.resize(size); - Ok(state) + }) } pub fn toggle_ime(&mut self) { @@ -852,7 +853,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(); } @@ -945,7 +950,7 @@ impl WindowState { return Ok(()); } - let mut buffer = self.surface.buffer_mut()?; + let mut buffer = self.surface.as_mut().unwrap().buffer_mut()?; // Draw a different color inside the safe area let surface_size = self.window.surface_size(); diff --git a/examples/x11_embed.rs b/examples/x11_embed.rs index 1b9a796ff6..7ef41b8a08 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_surface_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_surface_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 320c654e24..40e8c70657 100644 --- a/src/application.rs +++ b/src/application.rs @@ -7,6 +7,8 @@ use crate::platform::macos::ApplicationHandlerExtMacOS; use crate::window::WindowId; /// The handler of the application events. +/// +/// This is [dropped][std::ops::Drop] when the event loop is being shut down. pub trait ApplicationHandler { /// Emitted when new events arrive from the OS to be processed. /// @@ -101,7 +103,9 @@ pub trait ApplicationHandler { /// /// [`can_create_surfaces()`]: Self::can_create_surfaces() /// [`destroy_surfaces()`]: Self::destroy_surfaces() - fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop); + fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { + let _ = event_loop; + } /// Called after a wake up is requested using [`EventLoopProxy::wake_up()`]. /// @@ -142,8 +146,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. /// // @@ -162,8 +164,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 || { @@ -182,9 +182,8 @@ pub trait ApplicationHandler { /// } /// }); /// - /// event_loop.run_app(&mut app)?; + /// event_loop.run(|_event_loop| MyApp { receiver })?; /// - /// drop(app); /// background_thread.join().unwrap(); /// /// Ok(()) @@ -286,13 +285,13 @@ pub trait ApplicationHandler { /// with the [`onStop`] lifecycle event which typically results in the surface to be destroyed /// after the app becomes invisible. /// - /// Applications that need to run on Android should assume their [`NativeWindow`] 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`]). + /// 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`]). /// - /// When receiving [`destroy_surfaces()`] Android applications should drop all render surfaces - /// before the event callback completes, which may be re-created when the application next - /// receives [`can_create_surfaces()`]. + /// This means that in this method, you must drop all render surfaces before the event callback + /// completes, and only re-create them in or after [`can_create_surfaces()`] is next recieved. /// /// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window /// [`Surface`]: https://developer.android.com/reference/android/view/Surface @@ -310,14 +309,6 @@ pub trait ApplicationHandler { let _ = event_loop; } - /// Emitted when the event loop is being shut down. - /// - /// 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) { - let _ = event_loop; - } - /// Emitted when the application has received a memory warning. /// /// ## Platform-specific @@ -413,11 +404,6 @@ impl ApplicationHandler for &mut A { (**self).destroy_surfaces(event_loop); } - #[inline] - fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) { - (**self).exiting(event_loop); - } - #[inline] fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) { (**self).memory_warning(event_loop); @@ -487,11 +473,6 @@ impl ApplicationHandler for Box { (**self).destroy_surfaces(event_loop); } - #[inline] - fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) { - (**self).exiting(event_loop); - } - #[inline] fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) { (**self).memory_warning(event_loop); diff --git a/src/event.rs b/src/event.rs index 5a89923ec3..3a7a1ad67c 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() @@ -1195,7 +1150,6 @@ mod tests { let wid = WindowId::from_raw(0); 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 059613459b..559d812802 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -195,10 +195,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 @@ -213,7 +251,7 @@ 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. @@ -223,11 +261,20 @@ impl EventLoop { /// [^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 @@ -392,9 +439,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 3dadb80d1b..bc90ba7978 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,23 +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; -//! -//! # // Intentionally use `fn main` for clarity -//! fn main() { -//! 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 @@ -26,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 @@ -49,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, 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"); @@ -81,16 +73,19 @@ //! // 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(); -//! } +//! self.window.request_redraw(); +//! }, //! _ => (), //! } //! } //! } //! //! # // Intentionally use `fn main` for clarity -//! fn main() { -//! 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. @@ -101,8 +96,21 @@ //! // input, and uses significantly less power/CPU time than ControlFlow::Poll. //! event_loop.set_control_flow(ControlFlow::Wait); //! -//! let mut app = App::default(); -//! event_loop.run_app(&mut app); +//! // 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. +//! +//! // Create a simple window with default attributes. +//! let window = event_loop +//! .create_window(Window::default_attributes()) +//! .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(()) //! } //! ``` //! @@ -250,20 +258,21 @@ //! |32-bit ARM Android |`arm-linux-androideabi` |Android | //! |64-bit SPARC Linux with glibc |`sparc64-unknown-linux-gnu` |X11, Wayland | //! +//! [`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 79c0d57c8b..464883da0e 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 28227662d8..904695283e 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -190,10 +190,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. @@ -206,9 +206,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 0d07506a02..722282e5fb 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -496,15 +496,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 = init_closure(&self.window_target); + loop { match self.pump_app_events(None, &mut app) { PumpStatus::Exit(0) => { @@ -546,8 +552,6 @@ impl EventLoop { if self.exiting() { self.loop_running = false; - app.exiting(&self.window_target); - PumpStatus::Exit(0) } else { PumpStatus::Continue diff --git a/src/platform_impl/apple/appkit/app_state.rs b/src/platform_impl/apple/appkit/app_state.rs index 9ff1e95720..219d845921 100644 --- a/src/platform_impl/apple/appkit/app_state.rs +++ b/src/platform_impl/apple/appkit/app_state.rs @@ -9,12 +9,12 @@ use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningAppli use objc2_foundation::{MainThreadMarker, NSNotification}; use super::super::event_handler::EventHandler; -use super::event_loop::{stop_app_immediately, ActiveEventLoop, EventLoopProxy, PanicInfo}; +use super::event_loop::{stop_app_immediately, EventLoopProxy, PanicInfo}; use super::menu; use super::observer::{EventLoopWaker, RunLoop}; use crate::application::ApplicationHandler; use crate::event::{StartCause, WindowEvent}; -use crate::event_loop::ControlFlow; +use crate::event_loop::{ActiveEventLoop, ControlFlow}; use crate::window::WindowId; #[derive(Debug)] @@ -163,17 +163,25 @@ impl AppState { pub fn will_terminate(self: &Rc, _notification: &NSNotification) { trace_scope!("NSApplicationWillTerminateNotification"); // TODO: Notify every window that it will be destroyed, like done in iOS? + self.with_handler(|app, event_loop| { + app.exiting(event_loop); + }); self.internal_exit(); } /// 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) + let init_closure = Box::new( + |active_event_loop: &'_ dyn ActiveEventLoop| -> Box { + Box::new(init_closure(active_event_loop)) + }, + ); + self.event_handler.set(init_closure, closure) } pub fn event_loop_proxy(&self) -> &Arc { @@ -208,10 +216,6 @@ impl AppState { /// NOTE: that if the `NSApplication` has been launched then that state is preserved, /// and we won't need to re-launch the app if subsequent EventLoops are run. pub fn internal_exit(self: &Rc) { - self.with_handler(|app, event_loop| { - app.exiting(event_loop); - }); - self.set_is_running(false); self.set_stop_on_redraw(false); self.set_stop_before_wait(false); diff --git a/src/platform_impl/apple/appkit/event_loop.rs b/src/platform_impl/apple/appkit/event_loop.rs index 4e8648d0f8..72437e9d14 100644 --- a/src/platform_impl/apple/appkit/event_loop.rs +++ b/src/platform_impl/apple/appkit/event_loop.rs @@ -274,20 +274,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..602f74ab2f 100644 --- a/src/platform_impl/apple/event_handler.rs +++ b/src/platform_impl/apple/event_handler.rs @@ -1,33 +1,55 @@ -use std::cell::RefCell; +use std::cell::Cell; use std::{fmt, mem}; use crate::application::ApplicationHandler; +use crate::event_loop::ActiveEventLoop; /// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access /// to it within the execution of a closure. #[derive(Default)] pub(crate) struct EventHandler { - /// This can be in the following states: - /// - Not registered by the event loop (None). - /// - Present (Some(handler)). - /// - Currently executing the handler / in use (RefCell borrowed). - inner: RefCell>, + state: Cell, +} + +type InitClosure<'handler> = + Box Box + 'handler>; + +#[derive(Default)] +enum State { + /// Not registered by the event loop. + #[default] + NotRegistered, + /// The event is registered by the event loop. + Registered(InitClosure<'static>), + /// The application has been initialized, and we're ready to handle events. + Ready(Box), + /// Currently executing the handler. + CurrentlyExecuting, + /// The application has been terminated. + Terminated, + // TODO: Invalid state? } impl fmt::Debug for EventHandler { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let state = match self.inner.try_borrow().as_deref() { - Ok(Some(_)) => "", - Ok(None) => "", - Err(_) => "", + let state = self.state.replace(State::CurrentlyExecuting); + // NOTE: We're very careful not to panic inside the "critial" section here. + let string = match &state { + State::NotRegistered => "", + State::Registered(_) => "", + State::Ready(_) => "", + State::CurrentlyExecuting => "", + State::Terminated => "", }; - f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive() + self.state.set(state); + + f.debug_struct("EventHandler").field("state", &string).finish_non_exhaustive() } } impl EventHandler { - pub(crate) fn new() -> Self { - Self { inner: RefCell::new(None) } + pub(crate) const fn new() -> Self { + Self { state: Cell::new(State::NotRegistered) } } /// Set the event loop handler for the duration of the given closure. @@ -37,7 +59,7 @@ impl EventHandler { /// from within the closure. pub(crate) fn set<'handler, R>( &self, - app: &'handler mut dyn ApplicationHandler, + init_closure: InitClosure<'handler>, closure: impl FnOnce() -> R, ) -> R { // SAFETY: We extend the lifetime of the handler here so that we can @@ -46,14 +68,9 @@ impl EventHandler { // This is sound, since we make sure to unset the handler again at the // end of this function, and as such the lifetime isn't actually // extended beyond `'handler`. - let handler = unsafe { - mem::transmute::< - &'handler mut dyn ApplicationHandler, - &'static mut dyn ApplicationHandler, - >(app) - }; + let handler = unsafe { mem::transmute::, InitClosure<'static>>(app) }; - match self.inner.try_borrow_mut().as_deref_mut() { + match self.state.try_borrow_mut().as_deref_mut() { Ok(Some(_)) => { unreachable!("tried to set handler while another was already set"); }, @@ -69,7 +86,7 @@ impl EventHandler { impl Drop for ClearOnDrop<'_> { fn drop(&mut self) { - match self.0.inner.try_borrow_mut().as_deref_mut() { + match self.0.state.try_borrow_mut().as_deref_mut() { Ok(data @ Some(_)) => { *data = None; }, @@ -101,6 +118,10 @@ impl EventHandler { // soundness. } + fn init(&self) {} + + fn terminate(&self) {} + #[cfg(target_os = "macos")] pub(crate) fn in_use(&self) -> bool { self.inner.try_borrow().is_err() @@ -120,7 +141,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/linux/mod.rs b/src/platform_impl/linux/mod.rs index 55ba7e2e02..bd769178bd 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -360,15 +360,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 0ce019f6b1..d82dc84fbc 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -146,15 +146,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) => { @@ -198,8 +204,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 diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 2f186051cd..9859d1a38b 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -373,15 +373,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) => { @@ -429,8 +435,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 diff --git a/src/platform_impl/orbital/event_loop.rs b/src/platform_impl/orbital/event_loop.rs index 1b9cc6042c..1ca0ef479b 100644 --- a/src/platform_impl/orbital/event_loop.rs +++ b/src/platform_impl/orbital/event_loop.rs @@ -483,7 +483,11 @@ 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); @@ -654,8 +658,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 f42baf5728..7b2be2674e 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -96,7 +96,6 @@ fn handle_event(app: &mut A, target: &ActiveEventLoop, ev 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 a7a8e920de..a8c90bbaf2 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -609,7 +609,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 @@ -713,7 +713,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 0b99ca6bfa..419f530544 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -220,15 +220,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; @@ -250,7 +256,6 @@ impl EventLoop { 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), }); } @@ -317,7 +322,6 @@ impl EventLoop { 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), }); @@ -348,8 +352,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 3b712167ec..ffed0b8344 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"),