| 1 | //! End user application handling. |
| 2 | |
| 3 | use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; |
| 4 | use crate::event_loop::ActiveEventLoop; |
| 5 | use crate::window::WindowId; |
| 6 | |
| 7 | /// The handler of the application events. |
| 8 | pub trait ApplicationHandler<T: 'static = ()> { |
| 9 | /// Emitted when new events arrive from the OS to be processed. |
| 10 | /// |
| 11 | /// This is a useful place to put code that should be done before you start processing |
| 12 | /// events, such as updating frame timing information for benchmarking or checking the |
| 13 | /// [`StartCause`] to see if a timer set by |
| 14 | /// [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil] has elapsed. |
| 15 | fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { |
| 16 | let _ = (event_loop, cause); |
| 17 | } |
| 18 | |
| 19 | /// Emitted when the application has been resumed. |
| 20 | /// |
| 21 | /// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a |
| 22 | /// formal suspend/resume lifecycle. For systems without a formal suspend/resume lifecycle |
| 23 | /// the `Resumed` event is always emitted after the |
| 24 | /// [`NewEvents(StartCause::Init)`][StartCause::Init] event. |
| 25 | /// |
| 26 | /// # Portability |
| 27 | /// |
| 28 | /// It's recommended that applications should only initialize their graphics context and create |
| 29 | /// a window after they have received their first `Resumed` event. Some systems |
| 30 | /// (specifically Android) won't allow applications to create a render surface until they are |
| 31 | /// resumed. |
| 32 | /// |
| 33 | /// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally |
| 34 | /// driven by multiple platform-specific events, and that there may be subtle differences across |
| 35 | /// platforms with how these internal events are delivered, it's recommended that applications |
| 36 | /// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` |
| 37 | /// events. |
| 38 | /// |
| 39 | /// Also see [`Suspended`] notes. |
| 40 | /// |
| 41 | /// ## Android |
| 42 | /// |
| 43 | /// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is |
| 44 | /// expected to closely correlate with the [`onResume`] lifecycle event but there may |
| 45 | /// technically be a discrepancy. |
| 46 | /// |
| 47 | /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() |
| 48 | /// |
| 49 | /// Applications that need to run on Android must wait until they have been `Resumed` |
| 50 | /// before they will be able to create a render surface (such as an `EGLSurface`, |
| 51 | /// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a |
| 52 | /// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their |
| 53 | /// render surfaces are invalid and should be dropped. |
| 54 | /// |
| 55 | /// Also see [`Suspended`] notes. |
| 56 | /// |
| 57 | /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView |
| 58 | /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle |
| 59 | /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html |
| 60 | /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html |
| 61 | /// |
| 62 | /// ## iOS |
| 63 | /// |
| 64 | /// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`] |
| 65 | /// callback which means the application is "active" (according to the |
| 66 | /// [iOS application lifecycle]). |
| 67 | /// |
| 68 | /// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive |
| 69 | /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle |
| 70 | /// |
| 71 | /// ## Web |
| 72 | /// |
| 73 | /// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event |
| 74 | /// with the property [`persisted`] being true, which means that the page is being |
| 75 | /// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that |
| 76 | /// stores a complete snapshot of a page (including the JavaScript heap) as the |
| 77 | /// user is navigating away. |
| 78 | /// |
| 79 | /// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event |
| 80 | /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted |
| 81 | /// [`bfcache`]: https://web.dev/bfcache/ |
| 82 | /// [`Suspended`]: Self::suspended |
| 83 | fn resumed(&mut self, event_loop: &ActiveEventLoop); |
| 84 | |
| 85 | /// Emitted when an event is sent from [`EventLoopProxy::send_event`]. |
| 86 | /// |
| 87 | /// [`EventLoopProxy::send_event`]: crate::event_loop::EventLoopProxy::send_event |
| 88 | fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { |
| 89 | let _ = (event_loop, event); |
| 90 | } |
| 91 | |
| 92 | /// Emitted when the OS sends an event to a winit window. |
| 93 | fn window_event( |
| 94 | &mut self, |
| 95 | event_loop: &ActiveEventLoop, |
| 96 | window_id: WindowId, |
| 97 | event: WindowEvent, |
| 98 | ); |
| 99 | |
| 100 | /// Emitted when the OS sends an event to a device. |
| 101 | fn device_event( |
| 102 | &mut self, |
| 103 | event_loop: &ActiveEventLoop, |
| 104 | device_id: DeviceId, |
| 105 | event: DeviceEvent, |
| 106 | ) { |
| 107 | let _ = (event_loop, device_id, event); |
| 108 | } |
| 109 | |
| 110 | /// Emitted when the event loop is about to block and wait for new events. |
| 111 | /// |
| 112 | /// Most applications shouldn't need to hook into this event since there is no real relationship |
| 113 | /// between how often the event loop needs to wake up and the dispatching of any specific |
| 114 | /// events. |
| 115 | /// |
| 116 | /// High frequency event sources, such as input devices could potentially lead to lots of wake |
| 117 | /// ups and also lots of corresponding `AboutToWait` events. |
| 118 | /// |
| 119 | /// This is not an ideal event to drive application rendering from and instead applications |
| 120 | /// should render in response to [`WindowEvent::RedrawRequested`] events. |
| 121 | fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { |
| 122 | let _ = event_loop; |
| 123 | } |
| 124 | |
| 125 | /// Emitted when the application has been suspended. |
| 126 | /// |
| 127 | /// # Portability |
| 128 | /// |
| 129 | /// Not all platforms support the notion of suspending applications, and there may be no |
| 130 | /// technical way to guarantee being able to emit a `Suspended` event if the OS has |
| 131 | /// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason, |
| 132 | /// Winit does not currently try to emit pseudo `Suspended` events before the application |
| 133 | /// quits on platforms without an application lifecycle. |
| 134 | /// |
| 135 | /// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally |
| 136 | /// driven by multiple platform-specific events, and that there may be subtle differences across |
| 137 | /// platforms with how these internal events are delivered, it's recommended that applications |
| 138 | /// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] |
| 139 | /// events. |
| 140 | /// |
| 141 | /// Also see [`Resumed`] notes. |
| 142 | /// |
| 143 | /// ## Android |
| 144 | /// |
| 145 | /// On Android, the `Suspended` event is only sent when the application's associated |
| 146 | /// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`] |
| 147 | /// lifecycle event but there may technically be a discrepancy. |
| 148 | /// |
| 149 | /// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause() |
| 150 | /// |
| 151 | /// Applications that need to run on Android should assume their [`SurfaceView`] has been |
| 152 | /// destroyed, which indirectly invalidates any existing render surfaces that may have been |
| 153 | /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). |
| 154 | /// |
| 155 | /// After being `Suspended` on Android applications must drop all render surfaces before |
| 156 | /// the event callback completes, which may be re-created when the application is next |
| 157 | /// [`Resumed`]. |
| 158 | /// |
| 159 | /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView |
| 160 | /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle |
| 161 | /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html |
| 162 | /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html |
| 163 | /// |
| 164 | /// ## iOS |
| 165 | /// |
| 166 | /// On iOS, the `Suspended` event is currently emitted in response to an |
| 167 | /// [`applicationWillResignActive`] callback which means that the application is |
| 168 | /// about to transition from the active to inactive state (according to the |
| 169 | /// [iOS application lifecycle]). |
| 170 | /// |
| 171 | /// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive |
| 172 | /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle |
| 173 | /// |
| 174 | /// ## Web |
| 175 | /// |
| 176 | /// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event |
| 177 | /// with the property [`persisted`] being true, which means that the page is being |
| 178 | /// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a |
| 179 | /// complete snapshot of a page (including the JavaScript heap) as the user is |
| 180 | /// navigating away. |
| 181 | /// |
| 182 | /// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event |
| 183 | /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted |
| 184 | /// [`bfcache`]: https://web.dev/bfcache/ |
| 185 | /// [`Resumed`]: Self::resumed |
| 186 | fn suspended(&mut self, event_loop: &ActiveEventLoop) { |
| 187 | let _ = event_loop; |
| 188 | } |
| 189 | |
| 190 | /// Emitted when the event loop is being shut down. |
| 191 | /// |
| 192 | /// This is irreversible - if this method is called, it is guaranteed that the event loop |
| 193 | /// will exit right after. |
| 194 | fn exiting(&mut self, event_loop: &ActiveEventLoop) { |
| 195 | let _ = event_loop; |
| 196 | } |
| 197 | |
| 198 | /// Emitted when the application has received a memory warning. |
| 199 | /// |
| 200 | /// ## Platform-specific |
| 201 | /// |
| 202 | /// ### Android |
| 203 | /// |
| 204 | /// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The |
| 205 | /// application must [release memory] or risk being killed. |
| 206 | /// |
| 207 | /// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory() |
| 208 | /// [release memory]: https://developer.android.com/topic/performance/memory#release |
| 209 | /// |
| 210 | /// ### iOS |
| 211 | /// |
| 212 | /// On iOS, the `MemoryWarning` event is emitted in response to an |
| 213 | /// [`applicationDidReceiveMemoryWarning`] callback. The application must free as much |
| 214 | /// memory as possible or risk being terminated, see [how to respond to memory warnings]. |
| 215 | /// |
| 216 | /// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni |
| 217 | /// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings |
| 218 | /// |
| 219 | /// ### Others |
| 220 | /// |
| 221 | /// - **macOS / Orbital / Wayland / Web / Windows:** Unsupported. |
| 222 | fn memory_warning(&mut self, event_loop: &ActiveEventLoop) { |
| 223 | let _ = event_loop; |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &mut A { |
| 228 | #[inline ] |
| 229 | fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { |
| 230 | (**self).new_events(event_loop, cause); |
| 231 | } |
| 232 | |
| 233 | #[inline ] |
| 234 | fn resumed(&mut self, event_loop: &ActiveEventLoop) { |
| 235 | (**self).resumed(event_loop); |
| 236 | } |
| 237 | |
| 238 | #[inline ] |
| 239 | fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { |
| 240 | (**self).user_event(event_loop, event); |
| 241 | } |
| 242 | |
| 243 | #[inline ] |
| 244 | fn window_event( |
| 245 | &mut self, |
| 246 | event_loop: &ActiveEventLoop, |
| 247 | window_id: WindowId, |
| 248 | event: WindowEvent, |
| 249 | ) { |
| 250 | (**self).window_event(event_loop, window_id, event); |
| 251 | } |
| 252 | |
| 253 | #[inline ] |
| 254 | fn device_event( |
| 255 | &mut self, |
| 256 | event_loop: &ActiveEventLoop, |
| 257 | device_id: DeviceId, |
| 258 | event: DeviceEvent, |
| 259 | ) { |
| 260 | (**self).device_event(event_loop, device_id, event); |
| 261 | } |
| 262 | |
| 263 | #[inline ] |
| 264 | fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { |
| 265 | (**self).about_to_wait(event_loop); |
| 266 | } |
| 267 | |
| 268 | #[inline ] |
| 269 | fn suspended(&mut self, event_loop: &ActiveEventLoop) { |
| 270 | (**self).suspended(event_loop); |
| 271 | } |
| 272 | |
| 273 | #[inline ] |
| 274 | fn exiting(&mut self, event_loop: &ActiveEventLoop) { |
| 275 | (**self).exiting(event_loop); |
| 276 | } |
| 277 | |
| 278 | #[inline ] |
| 279 | fn memory_warning(&mut self, event_loop: &ActiveEventLoop) { |
| 280 | (**self).memory_warning(event_loop); |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Box<A> { |
| 285 | #[inline ] |
| 286 | fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { |
| 287 | (**self).new_events(event_loop, cause); |
| 288 | } |
| 289 | |
| 290 | #[inline ] |
| 291 | fn resumed(&mut self, event_loop: &ActiveEventLoop) { |
| 292 | (**self).resumed(event_loop); |
| 293 | } |
| 294 | |
| 295 | #[inline ] |
| 296 | fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) { |
| 297 | (**self).user_event(event_loop, event); |
| 298 | } |
| 299 | |
| 300 | #[inline ] |
| 301 | fn window_event( |
| 302 | &mut self, |
| 303 | event_loop: &ActiveEventLoop, |
| 304 | window_id: WindowId, |
| 305 | event: WindowEvent, |
| 306 | ) { |
| 307 | (**self).window_event(event_loop, window_id, event); |
| 308 | } |
| 309 | |
| 310 | #[inline ] |
| 311 | fn device_event( |
| 312 | &mut self, |
| 313 | event_loop: &ActiveEventLoop, |
| 314 | device_id: DeviceId, |
| 315 | event: DeviceEvent, |
| 316 | ) { |
| 317 | (**self).device_event(event_loop, device_id, event); |
| 318 | } |
| 319 | |
| 320 | #[inline ] |
| 321 | fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { |
| 322 | (**self).about_to_wait(event_loop); |
| 323 | } |
| 324 | |
| 325 | #[inline ] |
| 326 | fn suspended(&mut self, event_loop: &ActiveEventLoop) { |
| 327 | (**self).suspended(event_loop); |
| 328 | } |
| 329 | |
| 330 | #[inline ] |
| 331 | fn exiting(&mut self, event_loop: &ActiveEventLoop) { |
| 332 | (**self).exiting(event_loop); |
| 333 | } |
| 334 | |
| 335 | #[inline ] |
| 336 | fn memory_warning(&mut self, event_loop: &ActiveEventLoop) { |
| 337 | (**self).memory_warning(event_loop); |
| 338 | } |
| 339 | } |
| 340 | |