Skip to content

Commit

Permalink
Implement ApplicationHandler::create|destroy_surfaces()
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Jun 28, 2024
1 parent 82d9bbe commit 515b6fe
Show file tree
Hide file tree
Showing 21 changed files with 174 additions and 110 deletions.
2 changes: 1 addition & 1 deletion examples/child_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
}

impl ApplicationHandler for Application {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
let attributes = Window::default_attributes()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
Expand Down
2 changes: 1 addition & 1 deletion examples/control_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl ApplicationHandler for ControlFlowDemo {
}
}

fn resumed(&mut self, event_loop: &ActiveEventLoop) {
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
Expand Down
2 changes: 1 addition & 1 deletion examples/pump_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() -> std::process::ExitCode {
}

impl ApplicationHandler for PumpDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
Expand Down
2 changes: 1 addition & 1 deletion examples/run_on_demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}

fn resumed(&mut self, event_loop: &ActiveEventLoop) {
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes()
.with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
Expand Down
4 changes: 2 additions & 2 deletions examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,8 @@ impl ApplicationHandler for Application {
info!("Device {device_id:?} event: {event:?}");
}

fn resumed(&mut self, event_loop: &ActiveEventLoop) {
info!("Resumed the event loop");
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
info!("Ready to create surfaces");
self.dump_monitors(event_loop);

// Create initial window.
Expand Down
2 changes: 1 addition & 1 deletion examples/x11_embed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main() -> Result<(), Box<dyn Error>> {
}

impl ApplicationHandler for XEmbedDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes()
.with_title("An embedded window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
Expand Down
215 changes: 128 additions & 87 deletions src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,69 +18,80 @@ pub trait ApplicationHandler {

/// Emitted when the application has been resumed.
///
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a
/// formal suspend/resume lifecycle. For systems without a formal suspend/resume lifecycle
/// the `Resumed` event is always emitted after the
/// [`NewEvents(StartCause::Init)`][StartCause::Init] event.
///
/// # Portability
///
/// It's recommended that applications should only initialize their graphics context and create
/// a window after they have received their first `Resumed` event. Some systems
/// (specifically Android) won't allow applications to create a render surface until they are
/// resumed.
///
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed`
/// events.
/// See [`suspended()`][Self::suspended].
///
/// Also see [`Suspended`] notes.
/// ## Platform-specific
///
/// ## Android
/// ### iOS
///
/// On Android, the `Resumed` event is sent 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.
/// On iOS, the [`resumed()`] method is called in response to an [`applicationDidBecomeActive`]
/// callback which means the application is about to transition from the inactive to active
/// state (according to the [iOS application lifecycle]).
///
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// Applications that need to run on Android must wait until they have been `Resumed`
/// 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.
/// ### Web
///
/// Also see [`Suspended`] notes.
/// On Web, the [`resumed()`] method is called in response to a [`pageshow`] event if the
/// page is being restored from 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.
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`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
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ## iOS
/// ### Others
///
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`]
/// callback which means the application is "active" (according to the
/// [iOS application lifecycle]).
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
/// [`resumed()`]: Self::resumed
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}

/// Emitted from the point onwards the application should create render surfaces.
///
/// ## Web
/// See [`destroy_surfaces()`].
///
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
/// with the property [`persisted`] being true, which means that the page is being
/// restored from 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.
/// ## Portability
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
/// [`Suspended`]: Self::suspended
fn resumed(&mut self, event_loop: &ActiveEventLoop);
/// It's recommended that applications should only initialize their render surfaces after the
/// [`create_surfaces()`] method is called. Some systems (specifically Android) won't allow
/// applications to create a render surface until that point.
///
/// For consistency, all platforms call this method even if they don't themselves have a formal
/// surface destroy/create lifecycle. For systems without a surface destroy/create lifecycle the
/// [`create_surfaces()`] event is always emitted after the [`StartCause::Init`] event.
///
/// Applications should be able to gracefully handle back-to-back [`create_surfaces()`] and
/// [`destroy_surfaces()`] calls.
///
/// ## Platform-specific
///
/// ### Android
///
/// On Android, the [`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()
///
/// Applications that need to run on Android must wait until they have been "resumed" 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.
///
/// [suspended]: Self::destroy_surfaces
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`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
///
/// [`create_surfaces()`]: Self::create_surfaces
/// [`destroy_surfaces()`]: Self::destroy_surfaces
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop);

/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
///
Expand Down Expand Up @@ -208,25 +219,47 @@ pub trait ApplicationHandler {

/// Emitted when the application has been suspended.
///
/// # Portability
/// See [`resumed()`][Self::resumed].
///
/// Not all platforms support the notion of suspending applications, and there may be no
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
/// Winit does not currently try to emit pseudo `Suspended` events before the application
/// quits on platforms without an application lifecycle.
/// ## Platform-specific
///
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`]
/// events.
/// ### iOS
///
/// On iOS, the [`suspended()`] method is called in response to an
/// [`applicationWillResignActive`] callback which means that the application is about to
/// transition from the active to inactive state (according to the [iOS application lifecycle]).
///
/// Also see [`Resumed`] notes.
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ### Web
///
/// ## Android
/// 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
/// stores a complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// On Android, the `Suspended` event is only sent when the application's associated
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`bfcache`]: https://web.dev/bfcache/
///
/// ### Others
///
/// **Android / macOS / Orbital / Wayland / Windows / X11:** Unsupported.
///
/// [`suspended()`]: Self::suspended
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}

/// Emitted when the application must destroy its render surfaces.
///
/// See [`create_surfaces()`] for more details.
///
/// ## Platform-specific
///
/// ### 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.
///
Expand All @@ -236,38 +269,24 @@ pub trait ApplicationHandler {
/// 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
/// 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`].
/// [resumed].
///
/// [suspended]: Self::destroy_surfaces
/// [resumed]: Self::create_surfaces
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`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
///
/// ## iOS
///
/// On iOS, the `Suspended` event is currently emitted in response to an
/// [`applicationWillResignActive`] callback which means that the application is
/// about to transition from the active to inactive state (according to the
/// [iOS application lifecycle]).
///
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
/// ### Others
///
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
/// with the property [`persisted`] being true, which means that the page is being
/// put 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.
/// - **iOS / macOS / Orbital / Wayland / Web / Windows / X11:** Unsupported.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
/// [`Resumed`]: Self::resumed
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
/// [`create_surfaces()`]: Self::create_surfaces
/// [`destroy_surfaces()`]: Self::destroy_surfaces
fn destroy_surfaces(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}

Expand Down Expand Up @@ -308,6 +327,7 @@ pub trait ApplicationHandler {
}
}

#[deny(clippy::missing_trait_methods)]
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
Expand All @@ -319,6 +339,11 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
(**self).resumed(event_loop);
}

#[inline]
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
(**self).create_surfaces(event_loop);
}

#[inline]
fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) {
(**self).proxy_wake_up(event_loop);
Expand Down Expand Up @@ -354,6 +379,11 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
(**self).suspended(event_loop);
}

#[inline]
fn destroy_surfaces(&mut self, event_loop: &ActiveEventLoop) {
(**self).destroy_surfaces(event_loop);
}

#[inline]
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
(**self).exiting(event_loop);
Expand All @@ -365,6 +395,7 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
}
}

#[deny(clippy::missing_trait_methods)]
impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
Expand All @@ -376,6 +407,11 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
(**self).resumed(event_loop);
}

#[inline]
fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
(**self).create_surfaces(event_loop);
}

#[inline]
fn proxy_wake_up(&mut self, event_loop: &ActiveEventLoop) {
(**self).proxy_wake_up(event_loop);
Expand Down Expand Up @@ -411,6 +447,11 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
(**self).suspended(event_loop);
}

#[inline]
fn destroy_surfaces(&mut self, event_loop: &ActiveEventLoop) {
(**self).destroy_surfaces(event_loop);
}

#[inline]
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
(**self).exiting(event_loop);
Expand Down
8 changes: 8 additions & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ changelog entry.
to send specific data to be processed on the main thread.
- Changed `EventLoopProxy::send_event` to `EventLoopProxy::wake_up`, it now
only wakes up the loop.
- `ApplicationHandler::create|destroy_surfaces()` was split off from
`ApplicationHandler::resumed/suspended()`.

`ApplicationHandler::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.

### Removed

Expand Down
5 changes: 5 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ pub(crate) enum Event {
/// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended
Suspended,

/// See [`ApplicationHandler::create_surfaces`] for details.
///
/// [`ApplicationHandler::create_surfaces`]: crate::application::ApplicationHandler::create_surfaces
CreateSurfaces,

/// See [`ApplicationHandler::resumed`] for details.
///
/// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
//! }
//!
//! impl ApplicationHandler for App {
//! fn resumed(&mut self, event_loop: &ActiveEventLoop) {
//! fn create_surfaces(&mut self, event_loop: &ActiveEventLoop) {
//! self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
//! }
//!
Expand Down
Loading

0 comments on commit 515b6fe

Please sign in to comment.