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 | |