Drop application handler on run loop exit (#4149)
Calling the `Drop` impl of the user's `ApplicationHandler` is important on iOS and Web, since they don't return from `EventLoop::run_app`. And now that we reliably call `Drop`, the `ApplicationHandler::exited` event/callback is unnecessary; using `Drop` composes much better (open files etc. stored in the app state will be automatically flushed), and prevents weirdness like attempting to create a new window while exiting.
This commit is contained in:
parent
ef37b1d5dd
commit
afb731bb52
19 changed files with 170 additions and 137 deletions
|
|
@ -592,18 +592,18 @@ 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;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Application {
|
||||
fn drop(&mut self) {
|
||||
info!("Application exited");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl ApplicationHandlerExtMacOS for Application {
|
||||
fn standard_key_binding(
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ use winit::window::{Window, WindowAttributes, WindowId};
|
|||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct App {
|
||||
|
|
@ -70,9 +72,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
#[cfg(web_platform)]
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
tracing::init();
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let mut app = App::default();
|
||||
|
||||
// For alternative loop run options see `pump_events` and `run_on_demand` examples.
|
||||
event_loop.run_app(&mut app).map_err(Into::into)
|
||||
event_loop.run_app(App::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,17 @@ use crate::event_loop::ActiveEventLoop;
|
|||
use crate::platform::macos::ApplicationHandlerExtMacOS;
|
||||
use crate::window::WindowId;
|
||||
|
||||
/// The handler of the application events.
|
||||
/// The handler of application-level events.
|
||||
///
|
||||
/// See [the top-level docs] for example usage, and [`EventLoop::run_app`] for an overview of when
|
||||
/// events are delivered.
|
||||
///
|
||||
/// This is [dropped] when the event loop is shut down. Note that this only works if you're passing
|
||||
/// the entire state to [`EventLoop::run_app`] (passing `&mut app` won't work).
|
||||
///
|
||||
/// [the top-level docs]: crate
|
||||
/// [`EventLoop::run_app`]: crate::event_loop::EventLoop::run_app
|
||||
/// [dropped]: std::ops::Drop
|
||||
pub trait ApplicationHandler {
|
||||
/// Emitted when new events arrive from the OS to be processed.
|
||||
///
|
||||
|
|
@ -57,7 +67,6 @@ pub trait ApplicationHandler {
|
|||
///
|
||||
/// [`resumed()`]: Self::resumed()
|
||||
/// [`suspended()`]: Self::suspended()
|
||||
/// [`exiting()`]: Self::exiting()
|
||||
fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
|
@ -162,8 +171,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 || {
|
||||
|
|
@ -171,7 +178,7 @@ pub trait ApplicationHandler {
|
|||
/// loop {
|
||||
/// println!("sending: {i}");
|
||||
/// if sender.send(i).is_err() {
|
||||
/// // Stop sending once `MyApp` is dropped
|
||||
/// // Stop sending once the receiver is dropped
|
||||
/// break;
|
||||
/// }
|
||||
/// // Trigger the wake-up _after_ we placed the event in the channel.
|
||||
|
|
@ -182,9 +189,8 @@ pub trait ApplicationHandler {
|
|||
/// }
|
||||
/// });
|
||||
///
|
||||
/// event_loop.run_app(&mut app)?;
|
||||
/// event_loop.run_app(MyApp { receiver })?;
|
||||
///
|
||||
/// drop(app);
|
||||
/// background_thread.join().unwrap();
|
||||
///
|
||||
/// Ok(())
|
||||
|
|
@ -203,6 +209,10 @@ pub trait ApplicationHandler {
|
|||
);
|
||||
|
||||
/// Emitted when the OS sends an event to a device.
|
||||
///
|
||||
/// For this to be called, it must be enabled with [`EventLoop::listen_device_events`].
|
||||
///
|
||||
/// [`EventLoop::listen_device_events`]: crate::event_loop::EventLoop::listen_device_events
|
||||
fn device_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
|
|
@ -258,7 +268,7 @@ pub trait ApplicationHandler {
|
|||
/// to the user. This is a good place to stop refreshing UI, running animations and other visual
|
||||
/// things. It is driven by Android's [`onStop()`] method.
|
||||
///
|
||||
/// After this event the application either receives [`resumed()`] again, or [`exiting()`].
|
||||
/// After this event the application either receives [`resumed()`] again, or will exit.
|
||||
///
|
||||
/// [`onStop()`]: https://developer.android.com/reference/android/app/Activity#onStop()
|
||||
///
|
||||
|
|
@ -268,7 +278,6 @@ pub trait ApplicationHandler {
|
|||
///
|
||||
/// [`resumed()`]: Self::resumed()
|
||||
/// [`suspended()`]: Self::suspended()
|
||||
/// [`exiting()`]: Self::exiting()
|
||||
fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let _ = event_loop;
|
||||
}
|
||||
|
|
@ -310,14 +319,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 +414,6 @@ impl<A: ?Sized + ApplicationHandler> 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 +483,6 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<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);
|
||||
|
|
|
|||
|
|
@ -228,6 +228,8 @@ changelog entry.
|
|||
- Remove `Window::inner_position`, use the new `Window::surface_position` instead.
|
||||
- Remove `CustomCursorExtWeb`, use the `CustomCursorSource`.
|
||||
- Remove `CustomCursor::from_rgba`, use `CustomCursorSource` instead.
|
||||
- Removed `ApplicationHandler::exited`, the event loop being shut down can now be listened to in
|
||||
the `Drop` impl on the application handler.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
39
src/event.rs
39
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))]
|
||||
|
|
@ -641,10 +606,12 @@ impl FingerId {
|
|||
///
|
||||
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera
|
||||
/// or first-person game controls. Many physical actions, such as mouse movement, can produce both
|
||||
/// device and window events. Because window events typically arise from virtual devices
|
||||
/// device and [window events]. Because window events typically arise from virtual devices
|
||||
/// (corresponding to GUI pointers and keyboard focus) the device IDs may not match.
|
||||
///
|
||||
/// Note that these events are delivered regardless of input focus.
|
||||
///
|
||||
/// [window events]: WindowEvent
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum DeviceEvent {
|
||||
/// Change in physical position of a pointing device.
|
||||
|
|
|
|||
|
|
@ -186,7 +186,56 @@ impl EventLoop {
|
|||
impl EventLoop {
|
||||
/// Run the application with the event loop on the calling thread.
|
||||
///
|
||||
/// 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 can imagine this method as being implemented like this:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let mut start_cause = StartCause::Init;
|
||||
///
|
||||
/// // Run the event loop.
|
||||
/// while !event_loop.exiting() {
|
||||
/// // Wake up.
|
||||
/// app.new_events(event_loop, start_cause);
|
||||
///
|
||||
/// // Indicate that surfaces can now safely be created.
|
||||
/// if start_cause == StartCause::Init {
|
||||
/// app.can_create_surfaces(event_loop);
|
||||
/// }
|
||||
///
|
||||
/// // Handle proxy wake-up event.
|
||||
/// if event_loop.proxy_wake_up_set() {
|
||||
/// event_loop.proxy_wake_up_clear();
|
||||
/// app.proxy_wake_up(event_loop);
|
||||
/// }
|
||||
///
|
||||
/// // Handle actions done by the user / system such as moving the cursor, resizing the
|
||||
/// // window, changing the window theme, etc.
|
||||
/// for event in event_loop.events() {
|
||||
/// match event {
|
||||
/// window event => app.window_event(event_loop, window_id, event),
|
||||
/// device event => app.device_event(event_loop, device_id, event),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Handle redraws.
|
||||
/// for window_id in event_loop.pending_redraws() {
|
||||
/// app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
|
||||
/// }
|
||||
///
|
||||
/// // Done handling events, wait until we're woken up again.
|
||||
/// app.about_to_wait(event_loop);
|
||||
/// start_cause = event_loop.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`], but
|
||||
/// it should give you an idea of how things fit together.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
|
|
@ -381,14 +430,21 @@ pub trait ActiveEventLoop: AsAny + fmt::Debug {
|
|||
/// Gets the current [`ControlFlow`].
|
||||
fn control_flow(&self) -> ControlFlow;
|
||||
|
||||
/// This exits the event loop.
|
||||
/// Stop the event loop.
|
||||
///
|
||||
/// See [`exiting`][crate::application::ApplicationHandler::exiting].
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// ### iOS
|
||||
///
|
||||
/// It is not possible to programmatically exit/quit an application on iOS, so this function is
|
||||
/// a no-op there. See also [this technical Q&A][qa1561].
|
||||
///
|
||||
/// [qa1561]: https://developer.apple.com/library/archive/qa/qa1561/_index.html
|
||||
fn exit(&self);
|
||||
|
||||
/// Returns if the [`EventLoop`] is about to stop.
|
||||
/// Returns whether the [`EventLoop`] is about to stop.
|
||||
///
|
||||
/// See [`exit()`][Self::exit].
|
||||
/// Set by [`exit()`][Self::exit].
|
||||
fn exiting(&self) -> bool;
|
||||
|
||||
/// Gets a persistent reference to the underlying platform display.
|
||||
|
|
|
|||
32
src/lib.rs
32
src/lib.rs
|
|
@ -26,9 +26,8 @@
|
|||
//! 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 can retrieve events by calling [`EventLoop::run_app()`]. This function will dispatch events
|
||||
//! for every [`Window`] that was created with that particular [`EventLoop`].
|
||||
//!
|
||||
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
||||
//! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works
|
||||
|
|
@ -58,10 +57,19 @@
|
|||
//!
|
||||
//! impl ApplicationHandler for App {
|
||||
//! fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
//! // The event loop has launched, and we can initialize our UI state.
|
||||
//!
|
||||
//! // Create a simple window with default attributes.
|
||||
//! 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_app` when a new event happens on the window.
|
||||
//! match event {
|
||||
//! WindowEvent::CloseRequested => {
|
||||
//! println!("The close button was pressed; stopping");
|
||||
|
|
@ -82,15 +90,18 @@
|
|||
//! // applications which do not always need to. Applications that redraw continuously
|
||||
//! // can render here instead.
|
||||
//! self.window.as_ref().unwrap().request_redraw();
|
||||
//! }
|
||||
//! },
|
||||
//! _ => (),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # // Intentionally use `fn main` for clarity
|
||||
//! fn main() {
|
||||
//! let event_loop = EventLoop::new().unwrap();
|
||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! // 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 +112,10 @@
|
|||
//! // 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 the event loop.
|
||||
//! event_loop.run_app(App::default())?;
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
|
@ -261,7 +274,6 @@
|
|||
//! [`Window::id()`]: window::Window::id
|
||||
//! [`WindowEvent`]: event::WindowEvent
|
||||
//! [`DeviceEvent`]: event::DeviceEvent
|
||||
//! [`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.
|
||||
|
|
|
|||
|
|
@ -85,12 +85,9 @@
|
|||
//!
|
||||
//! - applicationDidBecomeActive is Resumed
|
||||
//! - applicationWillResignActive is Suspended
|
||||
//! - applicationWillTerminate is LoopExiting
|
||||
//! - applicationWillTerminate corresponds to `Drop`ping the application handler.
|
||||
//!
|
||||
//! Keep in mind that after LoopExiting event is received every attempt to draw with
|
||||
//! opengl will result in segfault.
|
||||
//!
|
||||
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
|
||||
//! Note that an app may not receive the `Drop` event if suspended; it might be SIGKILL'ed.
|
||||
//!
|
||||
//! ## Custom `UIApplicationDelegate`
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -546,8 +546,6 @@ impl EventLoop {
|
|||
if self.exiting() {
|
||||
self.loop_running = false;
|
||||
|
||||
app.exiting(&self.window_target);
|
||||
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ impl AppState {
|
|||
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
|
||||
trace_scope!("NSApplicationWillTerminateNotification");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
self.event_handler.terminate();
|
||||
self.internal_exit();
|
||||
}
|
||||
|
||||
|
|
@ -164,10 +165,10 @@ impl AppState {
|
|||
/// of the given closure.
|
||||
pub fn set_event_handler<R>(
|
||||
&self,
|
||||
handler: &mut dyn ApplicationHandler,
|
||||
handler: impl ApplicationHandler,
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
self.event_handler.set(handler, closure)
|
||||
self.event_handler.set(Box::new(handler), closure)
|
||||
}
|
||||
|
||||
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
|
||||
|
|
@ -202,10 +203,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>) {
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -285,10 +285,10 @@ impl EventLoop {
|
|||
// redundant wake ups.
|
||||
pub fn run_app_on_demand<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
mut app: A,
|
||||
app: A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
self.app_state.clear_exit();
|
||||
self.app_state.set_event_handler(&mut app, || {
|
||||
self.app_state.set_event_handler(app, || {
|
||||
autoreleasepool(|_| {
|
||||
// clear / normalize pump_events state
|
||||
self.app_state.set_wait_timeout(None);
|
||||
|
|
@ -324,9 +324,9 @@ impl EventLoop {
|
|||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
mut app: A,
|
||||
app: A,
|
||||
) -> PumpStatus {
|
||||
self.app_state.set_event_handler(&mut app, || {
|
||||
self.app_state.set_event_handler(app, || {
|
||||
autoreleasepool(|_| {
|
||||
// As a special case, if the application hasn't been launched yet then we at least
|
||||
// run the loop until it has fully launched.
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use crate::application::ApplicationHandler;
|
|||
#[derive(Default)]
|
||||
pub(crate) struct EventHandler {
|
||||
/// This can be in the following states:
|
||||
/// - Not registered by the event loop (None).
|
||||
/// - Not registered by the event loop, or terminated (None).
|
||||
/// - Present (Some(handler)).
|
||||
/// - Currently executing the handler / in use (RefCell borrowed).
|
||||
inner: RefCell<Option<&'static mut dyn ApplicationHandler>>,
|
||||
inner: RefCell<Option<Box<dyn ApplicationHandler + 'static>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for EventHandler {
|
||||
|
|
@ -37,7 +37,7 @@ impl EventHandler {
|
|||
/// from within the closure.
|
||||
pub(crate) fn set<'handler, R>(
|
||||
&self,
|
||||
app: &'handler mut dyn ApplicationHandler,
|
||||
app: Box<dyn ApplicationHandler + 'handler>,
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
// SAFETY: We extend the lifetime of the handler here so that we can
|
||||
|
|
@ -48,8 +48,8 @@ impl EventHandler {
|
|||
// extended beyond `'handler`.
|
||||
let handler = unsafe {
|
||||
mem::transmute::<
|
||||
&'handler mut dyn ApplicationHandler,
|
||||
&'static mut dyn ApplicationHandler,
|
||||
Box<dyn ApplicationHandler + 'handler>,
|
||||
Box<dyn ApplicationHandler + 'static>,
|
||||
>(app)
|
||||
};
|
||||
|
||||
|
|
@ -71,10 +71,13 @@ impl EventHandler {
|
|||
fn drop(&mut self) {
|
||||
match self.0.inner.try_borrow_mut().as_deref_mut() {
|
||||
Ok(data @ Some(_)) => {
|
||||
*data = None;
|
||||
let handler = data.take();
|
||||
// Explicitly `Drop` the application handler.
|
||||
drop(handler);
|
||||
},
|
||||
Ok(None) => {
|
||||
tracing::error!("tried to clear handler, but no handler was set");
|
||||
// Allowed, happens if the handler was cleared manually
|
||||
// elsewhere (such as in `applicationWillTerminate:`).
|
||||
},
|
||||
Err(_) => {
|
||||
// Note: This is not expected to ever happen, this
|
||||
|
|
@ -110,16 +113,16 @@ impl EventHandler {
|
|||
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
|
||||
}
|
||||
|
||||
pub(crate) fn handle(&self, callback: impl FnOnce(&mut dyn ApplicationHandler)) {
|
||||
pub(crate) fn handle(&self, callback: impl FnOnce(&mut (dyn ApplicationHandler + '_))) {
|
||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||
Ok(Some(user_app)) => {
|
||||
Ok(Some(ref mut user_app)) => {
|
||||
// It is important that we keep the reference borrowed here,
|
||||
// so that `in_use` can properly detect that the handler is
|
||||
// still in use.
|
||||
//
|
||||
// If the handler unwinds, the `RefMut` will ensure that the
|
||||
// handler is no longer borrowed.
|
||||
callback(*user_app);
|
||||
callback(&mut **user_app);
|
||||
},
|
||||
Ok(None) => {
|
||||
// `NSApplication`, our app state and this handler are all
|
||||
|
|
@ -133,4 +136,21 @@ impl EventHandler {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn terminate(&self) {
|
||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||
Ok(data @ Some(_)) => {
|
||||
let handler = data.take();
|
||||
// Explicitly `Drop` the application handler.
|
||||
drop(handler);
|
||||
},
|
||||
Ok(None) => {
|
||||
// When terminating, we expect the application handler to still be registered.
|
||||
tracing::error!("tried to clear handler, but no handler was set");
|
||||
},
|
||||
Err(_) => {
|
||||
panic!("tried to clear handler while an event is currently being handled");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,8 +305,8 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<W
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn launch(mtm: MainThreadMarker, app: &mut dyn ApplicationHandler, run: impl FnOnce()) {
|
||||
get_handler(mtm).set(app, run)
|
||||
pub(crate) fn launch(mtm: MainThreadMarker, app: impl ApplicationHandler, run: impl FnOnce()) {
|
||||
get_handler(mtm).set(Box::new(app), run)
|
||||
}
|
||||
|
||||
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||
|
|
@ -495,13 +495,11 @@ pub(crate) fn terminated(application: &UIApplication) {
|
|||
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
this.terminated_transition();
|
||||
drop(this);
|
||||
|
||||
get_handler(mtm).handle(|app| app.exiting(&ActiveEventLoop { mtm }));
|
||||
|
||||
let this = AppState::get_mut(mtm);
|
||||
// Prevent EventLoopProxy from firing again.
|
||||
this.event_loop_proxy.invalidate();
|
||||
drop(this);
|
||||
|
||||
get_handler(mtm).terminate();
|
||||
}
|
||||
|
||||
fn handle_wrapped_event(mtm: MainThreadMarker, event: EventWrapper) {
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ impl EventLoop {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(self, mut app: A) -> ! {
|
||||
pub fn run_app<A: ApplicationHandler>(self, app: A) -> ! {
|
||||
let application: Option<Retained<UIApplication>> =
|
||||
unsafe { msg_send![UIApplication::class(), sharedApplication] };
|
||||
assert!(
|
||||
|
|
@ -259,7 +259,7 @@ impl EventLoop {
|
|||
fn _NSGetArgv() -> *mut *mut *mut c_char;
|
||||
}
|
||||
|
||||
app_state::launch(self.mtm, &mut app, || unsafe {
|
||||
app_state::launch(self.mtm, app, || unsafe {
|
||||
UIApplicationMain(
|
||||
*_NSGetArgc(),
|
||||
NonNull::new(*_NSGetArgv()).unwrap(),
|
||||
|
|
|
|||
|
|
@ -203,11 +203,10 @@ impl EventLoop {
|
|||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut app);
|
||||
}
|
||||
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
app.exiting(&self.active_event_loop);
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
|
|
|
|||
|
|
@ -498,8 +498,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
|
||||
|
|
|
|||
|
|
@ -650,8 +650,6 @@ impl EventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
app.exiting(&self.window_target);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -158,7 +158,6 @@ impl Runner {
|
|||
Event::Resumed => self.app.resumed(&self.event_loop),
|
||||
Event::CreateSurfaces => self.app.can_create_surfaces(&self.event_loop),
|
||||
Event::AboutToWait => self.app.about_to_wait(&self.event_loop),
|
||||
Event::LoopExiting => self.app.exiting(&self.event_loop),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -639,7 +638,9 @@ 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);
|
||||
//
|
||||
// We do want to run the application handler's `Drop` impl.
|
||||
*self.0.runner.borrow_mut() = RunnerEnum::Destroyed;
|
||||
}
|
||||
|
||||
// handle_event takes in events and either queues them or applies a callback
|
||||
|
|
@ -737,7 +738,6 @@ impl Shared {
|
|||
}
|
||||
|
||||
fn handle_loop_destroyed(&self) {
|
||||
self.handle_event(Event::LoopExiting);
|
||||
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;
|
||||
|
|
@ -879,6 +879,5 @@ pub(crate) enum Event {
|
|||
CreateSurfaces,
|
||||
Resumed,
|
||||
AboutToWait,
|
||||
LoopExiting,
|
||||
UserWakeUp,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,7 +333,6 @@ impl EventLoopRunner {
|
|||
self.call_new_events(true);
|
||||
self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop));
|
||||
self.last_events_cleared.set(Instant::now());
|
||||
self.call_event_handler(|app, event_loop| app.exiting(event_loop));
|
||||
},
|
||||
(_, Uninitialized) => panic!("cannot move state to Uninitialized"),
|
||||
|
||||
|
|
@ -341,9 +340,7 @@ impl EventLoopRunner {
|
|||
(Idle, HandlingMainEvents) => {
|
||||
self.call_new_events(false);
|
||||
},
|
||||
(Idle, Destroyed) => {
|
||||
self.call_event_handler(|app, event_loop| app.exiting(event_loop));
|
||||
},
|
||||
(Idle, Destroyed) => {},
|
||||
|
||||
(HandlingMainEvents, Idle) => {
|
||||
// This is always the last event we dispatch before waiting for new events
|
||||
|
|
@ -353,7 +350,6 @@ impl EventLoopRunner {
|
|||
(HandlingMainEvents, Destroyed) => {
|
||||
self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop));
|
||||
self.last_events_cleared.set(Instant::now());
|
||||
self.call_event_handler(|app, event_loop| app.exiting(event_loop));
|
||||
},
|
||||
|
||||
(Destroyed, _) => panic!("cannot move state from Destroyed"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue