Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions winit-android/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 0 additions & 4 deletions winit-appkit/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions winit-core/src/event_loop/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
14 changes: 14 additions & 0 deletions winit-core/src/event_loop/never_return.rs
Original file line number Diff line number Diff line change
@@ -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) -> !;
Comment thread
kchibisov marked this conversation as resolved.
}
17 changes: 17 additions & 0 deletions winit-core/src/event_loop/register.rs
Original file line number Diff line number Diff line change
@@ -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);
}
11 changes: 3 additions & 8 deletions winit-core/src/event_loop/run_on_demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
/// 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.
/// 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
Expand All @@ -31,8 +29,7 @@ pub trait EventLoopExtRunOnDemand {
///
/// # Caveats
/// - This extension isn't available on all platforms, since it's not always possible to return
/// to the caller (specifically this is impossible on iOS and Web - though with the Web
/// backend it is possible to use `EventLoopExtWeb::spawn_app()`[^1] more than once instead).
/// to the caller (specifically this is impossible on iOS and Web).
/// - 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
Expand All @@ -51,8 +48,6 @@ pub trait EventLoopExtRunOnDemand {
/// are delivered via callbacks based on an event loop that is internal to the browser itself.
/// - **iOS:** It's not possible to stop and start an `UIApplication` repeatedly on iOS.
///
/// [^1]: `spawn_app()` is only available on the Web platforms.
///
/// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
Expand Down
5 changes: 4 additions & 1 deletion winit-orbital/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion winit-uikit/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ impl EventLoop {
})
}

pub fn run_app<A: ApplicationHandler>(self, app: A) -> ! {
// Require `'static` for correctness, we won't be able to `Drop` the user's state otherwise.
pub fn run_app_never_return<A: ApplicationHandler + 'static>(self, app: A) -> ! {
let application: Option<Retained<UIApplication>> =
unsafe { msg_send![UIApplication::class(), sharedApplication] };
assert!(
Expand All @@ -250,6 +251,10 @@ impl EventLoop {

// We intentionally override neither the application nor the delegate,
// to allow the user to do so themselves!
//
// NOTE: `UIApplicationMain` is _the only way_ to start the event loop on iOS/UIKit, there
// are no other feasible options. See also:
// <https://github.com/rust-windowing/winit/pull/4165#issuecomment-2760514167>
app_state::launch(self.mtm, app, || UIApplication::main(None, None, self.mtm))
}

Expand Down
4 changes: 0 additions & 4 deletions winit-wayland/src/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
28 changes: 2 additions & 26 deletions winit-web/src/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,8 @@ impl EventLoop {
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
}

pub fn run_app<A: ApplicationHandler>(self, app: A) -> ! {
let app = Box::new(app);

// SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe
// because this function will never return and all resources not cleaned up by the point we
// `throw` will leak, making this actually `'static`.
let app = unsafe {
std::mem::transmute::<
Box<dyn ApplicationHandler + '_>,
Box<dyn ApplicationHandler + 'static>,
>(app)
};

self.elw.run(app, false);

// Throw an exception to break out of Rust execution and use unreachable to tell the
// compiler this function won't return, giving it a return type of '!'
backend::throw(
"Using exceptions for control flow, don't mind me. This isn't actually an error!",
);

unreachable!();
}

pub fn spawn_app<A: ApplicationHandler + 'static>(self, app: A) {
self.elw.run(Box::new(app), true);
pub fn register_app<A: ApplicationHandler + 'static>(self, app: A) {
self.elw.run(Box::new(app));
}

pub fn window_target(&self) -> &dyn RootActiveEventLoop {
Expand Down
10 changes: 1 addition & 9 deletions winit-web/src/event_loop/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ struct Execution {
exit: Cell<bool>,
runner: RefCell<RunnerEnum>,
suspended: Cell<bool>,
event_loop_recreation: Cell<bool>,
events: RefCell<VecDeque<Event>>,
id: Cell<usize>,
window: web_sys::Window,
Expand Down Expand Up @@ -195,7 +194,6 @@ impl Shared {
exit: Cell::new(false),
runner: RefCell::new(RunnerEnum::Pending),
suspended: Cell::new(false),
event_loop_recreation: Cell::new(false),
events: RefCell::new(VecDeque::new()),
window,
navigator,
Expand Down Expand Up @@ -772,9 +770,7 @@ impl Shared {
// * For each undropped `Window`:
// * The `register_redraw_request` closure.
// * The `destroy_fn` closure.
if self.0.event_loop_recreation.get() {
EventLoop::allow_event_loop_recreation();
}
EventLoop::allow_event_loop_recreation();
}

// Check if the event loop is currently closed
Expand Down Expand Up @@ -809,10 +805,6 @@ impl Shared {
}
}

pub fn event_loop_recreation(&self, allow: bool) {
self.0.event_loop_recreation.set(allow)
}

pub(crate) fn control_flow(&self) -> ControlFlow {
self.0.control_flow.get()
}
Expand Down
3 changes: 1 addition & 2 deletions winit-web/src/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ impl ActiveEventLoop {
Self { runner: runner::Shared::new(), modifiers: ModifiersShared::default() }
}

pub(crate) fn run(&self, app: Box<dyn ApplicationHandler>, event_loop_recreation: bool) {
self.runner.event_loop_recreation(event_loop_recreation);
pub(crate) fn run(&self, app: Box<dyn ApplicationHandler>) {
self.runner.start(app, self.clone());
}

Expand Down
25 changes: 0 additions & 25 deletions winit-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ use std::task::{Context, Poll};
use ::web_sys::HtmlCanvasElement;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor, CustomCursorSource};
use winit_core::error::NotSupportedError;
use winit_core::event_loop::ActiveEventLoop;
Expand Down Expand Up @@ -237,30 +236,6 @@ impl Default for WindowAttributesWeb {

/// Additional methods on `EventLoop` that are specific to the Web.
pub trait EventLoopExtWeb {
/// Initializes the winit event loop.
///
/// Unlike
#[cfg_attr(target_feature = "exception-handling", doc = "`run_app()`")]
#[cfg_attr(
not(target_feature = "exception-handling"),
doc = "[`run_app()`]"
)]
/// [^1], this returns immediately, and doesn't throw an exception in order to
/// satisfy its [`!`] return type.
///
/// 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.
#[rustfmt::skip]
///
#[cfg_attr(
not(target_feature = "exception-handling"),
doc = "[`run_app()`]: EventLoop::run_app()"
)]
/// [^1]: `run_app()` is _not_ available on Wasm when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A);

/// Sets the strategy for [`ControlFlow::Poll`].
///
/// See [`PollStrategy`].
Expand Down
4 changes: 0 additions & 4 deletions winit-web/src/web_sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ pub use self::resize_scaling::ResizeScaleHandle;
pub use self::safe_area::SafeAreaHandle;
pub use self::schedule::Schedule;

pub fn throw(msg: &str) {
wasm_bindgen::throw_str(msg);
}

pub struct PageTransitionEventHandle {
_show_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
_hide_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
Expand Down
4 changes: 0 additions & 4 deletions winit-win32/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 0 additions & 4 deletions winit-x11/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 8 additions & 2 deletions winit/examples/run_on_demand.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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");
}
11 changes: 11 additions & 0 deletions winit/src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,14 @@ The migration guide could reference other migration examples in the current
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.
This requires passing a `'static` application to ensure that the application state will live as long as necessary.
- On Web, the event loop can now always be re-created once it has finished running.
Loading
Loading