diff --git a/winit-android/src/event_loop.rs b/winit-android/src/event_loop.rs index 1396574c..1a098eae 100644 --- a/winit-android/src/event_loop.rs +++ b/winit-android/src/event_loop.rs @@ -500,10 +500,6 @@ impl EventLoop { input_status } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) - } - pub fn run_app_on_demand( &mut self, mut app: A, diff --git a/winit-appkit/src/event_loop.rs b/winit-appkit/src/event_loop.rs index 4b5e48dd..408c53a1 100644 --- a/winit-appkit/src/event_loop.rs +++ b/winit-appkit/src/event_loop.rs @@ -232,10 +232,6 @@ impl EventLoop { &self.window_target } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) - } - // 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 diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index edc06dc6..0337e5c6 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -1,4 +1,6 @@ +pub mod never_return; pub mod pump_events; +pub mod register; pub mod run_on_demand; use std::fmt::{self, Debug}; diff --git a/winit-core/src/event_loop/never_return.rs b/winit-core/src/event_loop/never_return.rs new file mode 100644 index 00000000..4a94f94c --- /dev/null +++ b/winit-core/src/event_loop/never_return.rs @@ -0,0 +1,14 @@ +use crate::application::ApplicationHandler; + +/// Additional methods on `EventLoop` for platforms whose run method never return. +pub trait EventLoopExtNeverReturn { + /// Run the event loop and call `process::exit` once finished. + /// + /// ## Platform-specific + /// + /// - **iOS**: This registers the callbacks with the system and calls `UIApplicationMain`. + /// - **macOS**: Unimplemented (TODO: Should call `NSApplicationMain`). + /// - **Android/Orbital/Wayland/Windows/X11**: Unsupported. + /// - **Web**: Impossible to support properly. + fn run_app_never_return(self, app: A) -> !; +} diff --git a/winit-core/src/event_loop/register.rs b/winit-core/src/event_loop/register.rs new file mode 100644 index 00000000..503a7469 --- /dev/null +++ b/winit-core/src/event_loop/register.rs @@ -0,0 +1,17 @@ +use crate::application::ApplicationHandler; + +/// Additional methods on `EventLoop` that registers it with the system event loop. +pub trait EventLoopExtRegister { + /// Initialize and register the application with the system's event loop, such that the + /// callbacks will be run later. + /// + /// ## Platform-specific + /// + /// - **Web**: Once the event loop has been destroyed, it's possible to reinitialize another + /// event loop by calling this function again. This can be useful if you want to recreate the + /// event loop while the WebAssembly module is still loaded. For example, this can be used to + /// recreate the event loop when switching between tabs on a single page application. + /// - **iOS/macOS**: Unimplemented. + /// - **Android/Orbital/Wayland/Windows/X11**: Unsupported. + fn register_app(self, app: A); +} diff --git a/winit-core/src/event_loop/run_on_demand.rs b/winit-core/src/event_loop/run_on_demand.rs index a95a05bd..7c53987a 100644 --- a/winit-core/src/event_loop/run_on_demand.rs +++ b/winit-core/src/event_loop/run_on_demand.rs @@ -6,15 +6,13 @@ use crate::{ window::Window, }; -#[allow(rustdoc::broken_intra_doc_links)] // FIXME(madsmtm): Fix these. /// 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`) - /// state 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. + /// state 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. /// /// It's expected that each run of the loop will be for orthogonal instantiations of your /// Winit application, but internally each instantiation may re-use some common window diff --git a/winit-orbital/src/event_loop.rs b/winit-orbital/src/event_loop.rs index e3331347..8ef97156 100644 --- a/winit-orbital/src/event_loop.rs +++ b/winit-orbital/src/event_loop.rs @@ -481,7 +481,10 @@ impl EventLoop { } } - pub fn run_app(mut self, mut app: A) -> Result<(), EventLoopError> { + pub fn run_app_on_demand( + &mut self, + mut app: A, + ) -> Result<(), EventLoopError> { let mut start_cause = StartCause::Init; loop { app.new_events(&self.window_target, start_cause); diff --git a/winit-uikit/src/event_loop.rs b/winit-uikit/src/event_loop.rs index 22e94400..c5897edc 100644 --- a/winit-uikit/src/event_loop.rs +++ b/winit-uikit/src/event_loop.rs @@ -239,7 +239,7 @@ impl EventLoop { } // Require `'static` for correctness, we won't be able to `Drop` the user's state otherwise. - pub fn run_app(self, app: A) -> ! { + pub fn run_app_never_return(self, app: A) -> ! { let application: Option> = unsafe { msg_send![UIApplication::class(), sharedApplication] }; assert!( diff --git a/winit-wayland/src/event_loop/mod.rs b/winit-wayland/src/event_loop/mod.rs index 780239c9..3a6ca698 100644 --- a/winit-wayland/src/event_loop/mod.rs +++ b/winit-wayland/src/event_loop/mod.rs @@ -169,10 +169,6 @@ impl EventLoop { Ok(event_loop) } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) - } - pub fn run_app_on_demand( &mut self, mut app: A, diff --git a/winit-web/src/event_loop/mod.rs b/winit-web/src/event_loop/mod.rs index 2cf2732b..8b722729 100644 --- a/winit-web/src/event_loop/mod.rs +++ b/winit-web/src/event_loop/mod.rs @@ -39,9 +39,8 @@ impl EventLoop { EVENT_LOOP_CREATED.store(false, Ordering::Relaxed); } - pub fn run_app(self, app: A) -> Result<(), EventLoopError> { + pub fn register_app(self, app: A) { self.elw.run(Box::new(app)); - Ok(()) } pub fn window_target(&self) -> &dyn RootActiveEventLoop { diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index 3a5720ec..e202281b 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -247,10 +247,6 @@ impl EventLoop { ActiveEventLoop::from_ref(&self.runner) } - pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { - self.run_app_on_demand(app) - } - pub fn run_app_on_demand( &mut self, mut app: A, diff --git a/winit-x11/src/event_loop.rs b/winit-x11/src/event_loop.rs index b7b08467..b89a06c1 100644 --- a/winit-x11/src/event_loop.rs +++ b/winit-x11/src/event_loop.rs @@ -427,10 +427,6 @@ 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_app_on_demand( &mut self, mut app: A, diff --git a/winit/examples/run_on_demand.rs b/winit/examples/run_on_demand.rs index f076b43b..f25bfc59 100644 --- a/winit/examples/run_on_demand.rs +++ b/winit/examples/run_on_demand.rs @@ -1,7 +1,7 @@ #![allow(clippy::single_match)] // Limit this example to only compatible platforms. -#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))] +#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, orbital_platform))] fn main() -> Result<(), Box> { use std::time::Duration; @@ -93,7 +93,13 @@ fn main() -> Result<(), Box> { Ok(()) } -#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))] +#[cfg(not(any( + windows_platform, + macos_platform, + x11_platform, + wayland_platform, + orbital_platform +)))] fn main() { println!("This example is not supported on this platform"); } diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index eacfa59a..50c1b204 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -40,6 +40,11 @@ changelog entry. ## Unreleased +### Added + +- Add `EventLoopExtRegister::register_app` for being explicit about how the event loop runs on Web. +- Add `EventLoopExtNeverReturn::run_app_never_return` for being explicit about how the event loop runs on iOS. + ### Changed - On Web, avoid throwing an exception in `EventLoop::run_app`, instead preferring to return to the caller. diff --git a/winit/src/event_loop.rs b/winit/src/event_loop.rs index 2ac533f9..6882ebf1 100644 --- a/winit/src/event_loop.rs +++ b/winit/src/event_loop.rs @@ -178,22 +178,28 @@ impl EventLoop { /// The semantics of this function can be a bit confusing, because the way different platforms /// control their event loop varies significantly. /// - /// On most platforms (Android, X11, Wayland, Windows, macOS), this blocks the caller, runs the - /// event loop internally, and then returns once [`ActiveEventLoop::exit`] is called. + /// On most platforms (Android, macOS, Orbital, X11, Wayland, Windows), this blocks the caller, + /// runs the event loop internally, and then returns once [`ActiveEventLoop::exit`] is called. + /// See [`run_app_on_demand`] for more detailed semantics. /// /// On iOS, this will register the application handler, and then call [`UIApplicationMain`] /// (which is the only way to run the system event loop), which never returns to the caller - /// (the process instead exits after the handler has been dropped). + /// (the process instead exits after the handler has been dropped). See also + /// [`run_app_never_return`]. /// /// On the web, this works by registering the application handler, and then immediately /// returning to the caller. This is necessary because WebAssembly (and JavaScript) is always /// executed in the context of the browser's own (internal) event loop, and thus we need to - /// return to avoid blocking that and allow events to later be delivered asynchronously. + /// return to avoid blocking that and allow events to later be delivered asynchronously. See + /// also [`register_app`]. /// /// If you call this function inside `fn main`, you usually do not need to think about these /// details. /// /// [`UIApplicationMain`]: https://developer.apple.com/documentation/uikit/uiapplicationmain(_:_:_:_:)-1yub7?language=objc + /// [`run_app_on_demand`]: crate::event_loop::run_on_demand::EventLoopExtRunOnDemand::run_app_on_demand + /// [`run_app_never_return`]: crate::event_loop::never_return::EventLoopExtNeverReturn::run_app_never_return + /// [`register_app`]: crate::event_loop::register::EventLoopExtRegister::register_app /// /// ## Static /// @@ -204,40 +210,37 @@ impl EventLoop { /// To be clear, you should avoid doing e.g. `event_loop.run_app(&mut app)?`, and prefer /// `event_loop.run_app(app)?` instead. /// - /// If this requirement is prohibitive for you, consider using - #[cfg_attr( - any( - windows_platform, - macos_platform, - android_platform, - x11_platform, - wayland_platform, - docsrs, - ), - doc = "[`EventLoopExtRunOnDemand::run_app_on_demand`](crate::event_loop::run_on_demand::EventLoopExtRunOnDemand::run_app_on_demand)" - )] - #[cfg_attr( - not(any( - windows_platform, - macos_platform, - android_platform, - x11_platform, - wayland_platform, - docsrs, - )), - doc = "`EventLoopExtRunOnDemand::run_app_on_demand`" - )] - /// instead (though note that this is not available on iOS and web). - /// - /// ## Platform-specific - /// - /// - **Web** Once your handler has been dropped, it's possible to reinitialize another event - /// loop by calling this function again. This can be useful if you want to recreate the event - /// loop while the WebAssembly module is still loaded. For example, this can be used to - /// recreate the event loop when switching between tabs on a single page application. + /// If this requirement is prohibitive for you, consider using [`run_app_on_demand`] instead + /// (though note that this is not available on iOS and web). #[inline] - pub fn run_app(self, app: A) -> Result<(), EventLoopError> { - self.event_loop.run_app(app) + #[allow(unused_mut)] + pub fn run_app( + mut self, + mut app: A, + ) -> Result<(), EventLoopError> { + #[cfg(any( + windows_platform, + macos_platform, + android_platform, + orbital_platform, + x11_platform, + wayland_platform, + ))] + { + let result = self.event_loop.run_app_on_demand(&mut app); + // SAFETY: unsure that the state is dropped before the exit from the event loop. + drop(app); + result + } + #[cfg(web_platform)] + { + self.event_loop.register_app(app); + Ok(()) + } + #[cfg(ios_platform)] + { + self.event_loop.run_app_never_return(app) + } } /// Creates an [`EventLoopProxy`] that can be used to dispatch user events @@ -342,6 +345,7 @@ impl winit_core::event_loop::pump_events::EventLoopExtPumpEvents for EventLoop { windows_platform, macos_platform, android_platform, + orbital_platform, x11_platform, wayland_platform, docsrs, @@ -352,6 +356,13 @@ impl winit_core::event_loop::run_on_demand::EventLoopExtRunOnDemand for EventLoo } } +#[cfg(any(web_platform, docsrs))] +impl winit_core::event_loop::register::EventLoopExtRegister for EventLoop { + fn register_app(self, app: A) { + self.event_loop.register_app(app) + } +} + #[cfg(android_platform)] impl winit_android::EventLoopExtAndroid for EventLoop { fn android_app(&self) -> &winit_android::activity::AndroidApp { diff --git a/winit/src/platform_impl/linux/mod.rs b/winit/src/platform_impl/linux/mod.rs index a771696d..877e0dc6 100644 --- a/winit/src/platform_impl/linux/mod.rs +++ b/winit/src/platform_impl/linux/mod.rs @@ -147,10 +147,6 @@ 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_app_on_demand( &mut self, app: A,