Re-work event loop run() API so it can return a Result

This re-works the portable `run()` API that consumes the `EventLoop` and
runs the loop on the calling thread until the app exits.

This can be supported across _all_ platforms and compared to the
previous `run() -> !` API is now able to return a `Result` status on all
platforms except iOS and Web. Fixes: #2709

By moving away from `run() -> !` we stop calling `std::process::exit()`
internally as a means to kill the process without returning which means
it's possible to return an exit status and applications can return from
their `main()` function normally.

This also fixes Android support where an Activity runs in a thread but
we can't assume to have full ownership of the process (other services
could be running in separate threads).

Additionally all examples have generally been updated so that `main()`
returns a `Result` from `run()`

Fixes: #2709
This commit is contained in:
Robert Bragg 2023-04-11 12:50:52 +01:00 committed by Kirill Chibisov
parent a6f414d732
commit 0d366ffbda
39 changed files with 99 additions and 123 deletions

View file

@ -18,6 +18,7 @@ use std::time::{Duration, Instant};
#[cfg(wasm_platform)]
use web_time::{Duration, Instant};
use crate::error::RunLoopError;
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
/// Provides a way to retrieve events from the system and from the windows that were registered to
@ -285,23 +286,33 @@ impl<T> EventLoop<T> {
EventLoopBuilder::<T>::with_user_event().build()
}
/// Hijacks the calling thread and initializes the winit event loop with the provided
/// closure. Since the closure is `'static`, it must be a `move` closure if it needs to
/// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// to dispatch any pending events.
///
/// Since the closure is `'static`, it must be a `move` closure if it needs to
/// access any data from the calling context.
///
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
/// event loop's behavior.
///
/// Any values not passed to this function will *not* be dropped.
///
/// ## Platform-specific
///
/// - **X11 / Wayland:** The program terminates with exit code 1 if the display server
/// disconnects.
/// - **iOS:** Will never return to the caller and so values not passed to this function will
/// *not* be dropped before the process exits.
/// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript exception
/// (that Rust doesn't see) that will also mean that the rest of the function is never executed
/// and any values not passed to this function will *not* be dropped.
///
/// Web applications are recommended to use `spawn()` 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.
///
/// [`ControlFlow`]: crate::event_loop::ControlFlow
#[inline]
pub fn run<F>(self, event_handler: F) -> !
pub fn run<F>(self, event_handler: F) -> Result<(), RunLoopError>
where
F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
{

View file

@ -526,17 +526,12 @@ impl<T: 'static> EventLoop<T> {
self.pending_redraw = pending_redraw;
}
pub fn run<F>(mut self, event_handler: F) -> !
pub fn run<F>(mut self, event_handler: F) -> Result<(), RunLoopError>
where
F: 'static
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
let exit_code = match self.run_ondemand(event_handler) {
Err(RunLoopError::ExitFailure(code)) => code,
Err(_err) => 1,
Ok(_) => 0,
};
::std::process::exit(exit_code);
self.run_ondemand(event_handler)
}
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), RunLoopError>

View file

@ -830,11 +830,11 @@ impl<T: 'static> EventLoop<T> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
}
pub fn run<F>(self, callback: F) -> !
pub fn run<F>(mut self, callback: F) -> Result<(), RunLoopError>
where
F: 'static + FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run(callback))
self.run_ondemand(callback)
}
pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), RunLoopError>

View file

@ -145,18 +145,6 @@ impl<T: 'static> EventLoop<T> {
Ok(event_loop)
}
pub fn run<F>(mut self, callback: F) -> !
where
F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow) + 'static,
{
let exit_code = match self.run_ondemand(callback) {
Err(RunLoopError::ExitFailure(code)) => code,
Err(_err) => 1,
Ok(_) => 0,
};
::std::process::exit(exit_code)
}
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), RunLoopError>
where
F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),

View file

@ -432,18 +432,6 @@ impl<T: 'static> EventLoop<T> {
&self.target
}
pub fn run<F>(mut self, callback: F) -> !
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow) + 'static,
{
let exit_code = match self.run_ondemand(callback) {
Err(RunLoopError::ExitFailure(code)) => code,
Err(_err) => 1,
Ok(_) => 0,
};
::std::process::exit(exit_code)
}
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), RunLoopError>
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),

View file

@ -6,7 +6,7 @@ use std::{
mem,
os::raw::c_void,
panic::{catch_unwind, resume_unwind, AssertUnwindSafe, RefUnwindSafe, UnwindSafe},
process, ptr,
ptr,
rc::{Rc, Weak},
sync::mpsc,
};
@ -192,16 +192,11 @@ impl<T> EventLoop<T> {
&self.window_target
}
pub fn run<F>(mut self, callback: F) -> !
pub fn run<F>(mut self, callback: F) -> Result<(), RunLoopError>
where
F: 'static + FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{
let exit_code = match self.run_ondemand(callback) {
Err(RunLoopError::ExitFailure(code)) => code,
Err(_err) => 1,
Ok(_) => 0,
};
process::exit(exit_code);
self.run_ondemand(callback)
}
// NB: we don't base this on `pump_events` because for `MacOs` we can't support

View file

@ -12,6 +12,7 @@ use orbclient::{
use raw_window_handle::{OrbitalDisplayHandle, RawDisplayHandle};
use crate::{
error::RunLoopError,
event::{self, Ime, Modifiers, StartCause},
event_loop::{self, ControlFlow},
keyboard::{
@ -442,7 +443,7 @@ impl<T: 'static> EventLoop<T> {
}
}
pub fn run<F>(mut self, event_handler: F) -> !
pub fn run<F>(mut self, mut event_handler_inner: F) -> Result<(), RunLoopError>
where
F: 'static
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
@ -688,7 +689,11 @@ impl<T: 'static> EventLoop<T> {
&mut control_flow,
);
::std::process::exit(code);
if code == 0 {
Ok(())
} else {
Err(RunLoopError::ExitFailure(code))
}
}
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {

View file

@ -243,16 +243,11 @@ impl<T: 'static> EventLoop<T> {
&self.window_target
}
pub fn run<F>(mut self, event_handler: F) -> !
pub fn run<F>(mut self, event_handler: F) -> Result<(), RunLoopError>
where
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let exit_code = match self.run_ondemand(event_handler) {
Err(RunLoopError::ExitFailure(code)) => code,
Err(_err) => 1,
Ok(_) => 0,
};
::std::process::exit(exit_code);
self.run_ondemand(event_handler)
}
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), RunLoopError>