winit/event_loop: Add register_app and run_app_never_return
To allow users to explicitly choose the run semantics that they want.
This commit is contained in:
parent
f69b601abb
commit
4c5bf0ee08
16 changed files with 102 additions and 71 deletions
|
|
@ -500,10 +500,6 @@ impl EventLoop {
|
|||
input_status
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
|
|
|
|||
|
|
@ -232,10 +232,6 @@ impl EventLoop {
|
|||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(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
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
14
winit-core/src/event_loop/never_return.rs
Normal file
14
winit-core/src/event_loop/never_return.rs
Normal file
|
|
@ -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<A: ApplicationHandler + 'static>(self, app: A) -> !;
|
||||
}
|
||||
17
winit-core/src/event_loop/register.rs
Normal file
17
winit-core/src/event_loop/register.rs
Normal file
|
|
@ -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<A: ApplicationHandler + 'static>(self, app: A);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -481,7 +481,10 @@ impl EventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, mut app: A) -> Result<(), EventLoopError> {
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
let mut start_cause = StartCause::Init;
|
||||
loop {
|
||||
app.new_events(&self.window_target, start_cause);
|
||||
|
|
|
|||
|
|
@ -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<A: ApplicationHandler + 'static>(self, app: A) -> ! {
|
||||
pub fn run_app_never_return<A: ApplicationHandler + 'static>(self, app: A) -> ! {
|
||||
let application: Option<Retained<UIApplication>> =
|
||||
unsafe { msg_send![UIApplication::class(), sharedApplication] };
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -169,10 +169,6 @@ impl EventLoop {
|
|||
Ok(event_loop)
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ impl EventLoop {
|
|||
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler + 'static>(self, app: A) -> Result<(), EventLoopError> {
|
||||
pub fn register_app<A: ApplicationHandler + 'static>(self, app: A) {
|
||||
self.elw.run(Box::new(app));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
|
||||
|
|
|
|||
|
|
@ -247,10 +247,6 @@ impl EventLoop {
|
|||
ActiveEventLoop::from_ref(&self.runner)
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
|
|
|
|||
|
|
@ -427,10 +427,6 @@ impl EventLoop {
|
|||
&self.event_processor.target
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
|
|
|
|||
|
|
@ -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<dyn std::error::Error>> {
|
||||
use std::time::Duration;
|
||||
|
||||
|
|
@ -93,7 +93,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<A: ApplicationHandler + 'static>(self, app: A) -> Result<(), EventLoopError> {
|
||||
self.event_loop.run_app(app)
|
||||
#[allow(unused_mut)]
|
||||
pub fn run_app<A: ApplicationHandler + 'static>(
|
||||
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<A: ApplicationHandler + 'static>(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 {
|
||||
|
|
|
|||
|
|
@ -147,10 +147,6 @@ impl EventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app))
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
app: A,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue