diff --git a/src/application.rs b/src/application.rs index 2dc224c941..f361ca76c7 100644 --- a/src/application.rs +++ b/src/application.rs @@ -41,11 +41,21 @@ pub trait ApplicationHandler { /// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event /// [`bfcache`]: https://web.dev/bfcache/ /// + /// ### Android + /// + /// On Android, the [`resumed()`] method is called when the `Activity` is (again, if after a + /// prior [`suspended()`]) being displayed to the user. This is a good place to begin drawing + /// visual elements, running animations, etc. It is driven by Android's [`onStart()`] method. + /// + /// [`onStart()`]: https://developer.android.com/reference/android/app/Activity#onStart() + /// /// ### Others /// - /// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported. + /// **macOS / Orbital / Wayland / Windows / X11:** Unsupported. /// - /// [`resumed()`]: Self::resumed + /// [`resumed()`]: Self::resumed() + /// [`suspended()`]: Self::suspended() + /// [`exiting()`]: Self::exiting() fn resumed(&mut self, event_loop: &dyn ActiveEventLoop) { let _ = event_loop; } @@ -71,26 +81,24 @@ pub trait ApplicationHandler { /// /// ### Android /// - /// On Android, the [`can_create_surfaces()`] method is called when a new [`SurfaceView`] has - /// been created. This is expected to closely correlate with the [`onResume`] lifecycle - /// event but there may technically be a discrepancy. - /// - /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() + /// On Android, the [`can_create_surfaces()`] method is called when a new [`NativeWindow`] + /// (native [`Surface`]) is created which backs the application window. This is expected to + /// closely correlate with the [`onStart`] lifecycle event which typically results in a surface + /// to be created after the app becomes visible. /// - /// Applications that need to run on Android must wait until they have been "resumed" before + /// Applications that need to run on Android must wait until they have received a surface before /// they will be able to create a render surface (such as an `EGLSurface`, [`VkSurfaceKHR`] - /// or [`wgpu::Surface`]) which depend on having a [`SurfaceView`]. Applications must also - /// assume that if they are [suspended], then their render surfaces are invalid and should - /// be dropped. + /// or [`wgpu::Surface`]) which depend on having a [`NativeWindow`]. Applications must handle + /// [`destroy_surfaces()`], where their render surfaces are invalid and should be dropped. /// - /// [suspended]: Self::destroy_surfaces - /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView - /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle + /// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window + /// [`Surface`]: https://developer.android.com/reference/android/view/Surface + /// [`onStart`]: https://developer.android.com/reference/android/app/Activity#onStart() /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html /// - /// [`can_create_surfaces()`]: Self::can_create_surfaces - /// [`destroy_surfaces()`]: Self::destroy_surfaces + /// [`can_create_surfaces()`]: Self::can_create_surfaces() + /// [`destroy_surfaces()`]: Self::destroy_surfaces() fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop); /// Called after a wake up is requested using [`EventLoopProxy::wake_up()`]. @@ -235,18 +243,30 @@ pub trait ApplicationHandler { /// ### Web /// /// On Web, the [`suspended()`] method is called in response to a [`pagehide`] event if the - /// page is being restored from the [`bfcache`] (back/forward cache) - an in-memory cache that + /// page is being stored in the [`bfcache`] (back/forward cache) - an in-memory cache that /// stores a complete snapshot of a page (including the JavaScript heap) as the user is /// navigating away. /// /// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event /// [`bfcache`]: https://web.dev/bfcache/ /// + /// ### Android + /// + /// On Android, the [`suspended()`] method is called when the `Activity` is no longer visible + /// 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()`]. + /// + /// [`onStop()`]: https://developer.android.com/reference/android/app/Activity#onStop() + /// /// ### Others /// - /// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported. + /// **macOS / Orbital / Wayland / Windows / X11:** Unsupported. /// - /// [`suspended()`]: Self::suspended + /// [`resumed()`]: Self::resumed() + /// [`suspended()`]: Self::suspended() + /// [`exiting()`]: Self::exiting() fn suspended(&mut self, event_loop: &dyn ActiveEventLoop) { let _ = event_loop; } @@ -259,24 +279,22 @@ pub trait ApplicationHandler { /// /// ### Android /// - /// On Android, the [`destroy_surfaces()`] method is called when the application's associated - /// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`] - /// lifecycle event but there may technically be a discrepancy. - /// - /// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause() + /// On Android, the [`destroy_surfaces()`] method is called when the application's + /// [`NativeWindow`] (native [`Surface`]) is destroyed. This is expected to closely correlate + /// with the [`onStop`] lifecycle event which typically results in the surface to be destroyed + /// after the app becomes invisible. /// - /// Applications that need to run on Android should assume their [`SurfaceView`] has been + /// Applications that need to run on Android should assume their [`NativeWindow`] has been /// destroyed, which indirectly invalidates any existing render surfaces that may have been /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). /// - /// After being [suspended] on Android applications must drop all render surfaces before - /// the event callback completes, which may be re-created when the application is next - /// [resumed]. + /// When receiving [`destroy_surfaces()`] Android applications should drop all render surfaces + /// before the event callback completes, which may be re-created when the application next + /// receives [`can_create_surfaces()`]. /// - /// [suspended]: Self::destroy_surfaces - /// [resumed]: Self::can_create_surfaces - /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView - /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle + /// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window + /// [`Surface`]: https://developer.android.com/reference/android/view/Surface + /// [`onStop`]: https://developer.android.com/reference/android/app/Activity#onStop() /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html /// @@ -284,8 +302,8 @@ pub trait ApplicationHandler { /// /// - **iOS / macOS / Orbital / Wayland / Web / Windows / X11:** Unsupported. /// - /// [`can_create_surfaces()`]: Self::can_create_surfaces - /// [`destroy_surfaces()`]: Self::destroy_surfaces + /// [`can_create_surfaces()`]: Self::can_create_surfaces() + /// [`destroy_surfaces()`]: Self::destroy_surfaces() fn destroy_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { let _ = event_loop; } diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index c1fbb8447b..31b64d1aba 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -84,14 +84,14 @@ changelog entry. - Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now only wakes up the loop. - On X11, implement smooth resizing through the sync extension API. -- `ApplicationHandler::create|destroy_surfaces()` was split off from +- `ApplicationHandler::can_create|destroy_surfaces()` was split off from `ApplicationHandler::resumed/suspended()`. `ApplicationHandler::can_create_surfaces()` should, for portability reasons to Android, be the only place to create render surfaces. - `ApplicationHandler::resumed/suspended()` are now only emitted by iOS and Web - and now signify actually resuming/suspending the application. + `ApplicationHandler::resumed/suspended()` are now only emitted by iOS, Web + and Android, and now signify actually resuming/suspending the application. - Rename `platform::web::*ExtWebSys` to `*ExtWeb`. - Change signature of `EventLoop::run_app`, `EventLoopExtPumpEvents::pump_app_events` and `EventLoopExtRunOnDemand::run_app_on_demand` to accept a `impl ApplicationHandler` directly, diff --git a/src/event.rs b/src/event.rs index 85a47c90a0..edb3ea7161 100644 --- a/src/event.rs +++ b/src/event.rs @@ -63,51 +63,51 @@ use crate::window::{ActivationToken, Theme, WindowId}; #[allow(dead_code)] #[derive(Debug, Clone, PartialEq)] pub(crate) enum Event { - /// See [`ApplicationHandler::new_events`] for details. + /// See [`ApplicationHandler::new_events()`] for details. /// - /// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events + /// [`ApplicationHandler::new_events()`]: crate::application::ApplicationHandler::new_events() NewEvents(StartCause), - /// See [`ApplicationHandler::window_event`] for details. + /// See [`ApplicationHandler::window_event()`] for details. /// - /// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event + /// [`ApplicationHandler::window_event()`]: crate::application::ApplicationHandler::window_event() #[allow(clippy::enum_variant_names)] WindowEvent { window_id: WindowId, event: WindowEvent }, - /// See [`ApplicationHandler::device_event`] for details. + /// See [`ApplicationHandler::device_event()`] for details. /// - /// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event + /// [`ApplicationHandler::device_event()`]: crate::application::ApplicationHandler::device_event() #[allow(clippy::enum_variant_names)] DeviceEvent { device_id: Option, event: DeviceEvent }, - /// See [`ApplicationHandler::suspended`] for details. + /// See [`ApplicationHandler::suspended()`] for details. /// - /// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended + /// [`ApplicationHandler::suspended()`]: crate::application::ApplicationHandler::suspended() Suspended, - /// See [`ApplicationHandler::can_create_surfaces`] for details. + /// See [`ApplicationHandler::can_create_surfaces()`] for details. /// - /// [`ApplicationHandler::can_create_surfaces`]: crate::application::ApplicationHandler::can_create_surfaces + /// [`ApplicationHandler::can_create_surfaces()`]: crate::application::ApplicationHandler::can_create_surfaces() CreateSurfaces, - /// See [`ApplicationHandler::resumed`] for details. + /// See [`ApplicationHandler::resumed()`] for details. /// - /// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed + /// [`ApplicationHandler::resumed()`]: crate::application::ApplicationHandler::resumed() Resumed, - /// See [`ApplicationHandler::about_to_wait`] for details. + /// See [`ApplicationHandler::about_to_wait()`] for details. /// - /// [`ApplicationHandler::about_to_wait`]: crate::application::ApplicationHandler::about_to_wait + /// [`ApplicationHandler::about_to_wait()`]: crate::application::ApplicationHandler::about_to_wait() AboutToWait, - /// See [`ApplicationHandler::exiting`] for details. + /// See [`ApplicationHandler::exiting()`] for details. /// - /// [`ApplicationHandler::exiting`]: crate::application::ApplicationHandler::exiting + /// [`ApplicationHandler::exiting()`]: crate::application::ApplicationHandler::exiting() LoopExiting, - /// See [`ApplicationHandler::memory_warning`] for details. + /// See [`ApplicationHandler::memory_warning()`] for details. /// - /// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning + /// [`ApplicationHandler::memory_warning()`]: crate::application::ApplicationHandler::memory_warning() MemoryWarning, /// User requested a wake up. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 760d10a02a..912e61e54a 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -218,11 +218,11 @@ impl EventLoop { app.memory_warning(&self.window_target); }, MainEvent::Start => { - // XXX: how to forward this state to applications? - warn!("TODO: forward onStart notification to application"); + app.resumed(self.window_target()); }, MainEvent::Resume { .. } => { debug!("App Resumed - is running"); + // TODO: This is incorrect - will be solved in https://github.com/rust-windowing/winit/pull/3897 self.running = true; }, MainEvent::SaveState { .. } => { @@ -232,11 +232,11 @@ impl EventLoop { }, MainEvent::Pause => { debug!("App Paused - stopped running"); + // TODO: This is incorrect - will be solved in https://github.com/rust-windowing/winit/pull/3897 self.running = false; }, MainEvent::Stop => { - // XXX: how to forward this state to applications? - warn!("TODO: forward onStop notification to application"); + app.suspended(self.window_target()); }, MainEvent::Destroy => { // XXX: maybe exit mainloop to drop things before being diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index c78ce2921d..10aac698f0 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -218,10 +218,10 @@ declare_class!( trace_scope!("windowDidResignKey:"); // It happens rather often, e.g. when the user is Cmd+Tabbing, that the // NSWindowDelegate will receive a didResignKey event despite no event - // being received when the modifiers are released. This is because + // being received when the modifiers are released. This is because // flagsChanged events are received by the NSView instead of the // NSWindowDelegate, and as a result a tracked modifiers state can quite - // easily fall out of synchrony with reality. This requires us to emit + // easily fall out of synchrony with reality. This requires us to emit // a synthetic ModifiersChanged event when we lose focus. self.view().reset_modifiers();