diff --git a/CHANGELOG.md b/CHANGELOG.md index 228c399d..124f7235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,9 @@ And please only add new entries to the top of this list, right below the `# Unre - On X11, add a `with_embedded_parent_window` function to the window builder to allow embedding a window into another window. - On iOS, add force data to touch events when using the Apple Pencil. - On Android, add force data to touch events. +- On iOS, send events `WindowEvent::Occluded(false)`, `WindowEvent::Occluded(true)` when application enters/leaves foreground. +- on iOS, add event `Event::MemoryWarning`. +- On Android, add event `Event::MemoryWarning`. # 0.29.0-beta.0 diff --git a/src/event.rs b/src/event.rs index f41e69eb..963fdb09 100644 --- a/src/event.rs +++ b/src/event.rs @@ -225,6 +225,32 @@ pub enum Event { /// This is irreversible - if this event is emitted, it is guaranteed to be the last event that /// gets emitted. You generally want to treat this as a "do on quit" event. LoopExiting, + + /// Emitted when the application has received a memory warning. + /// + /// ## Platform-specific + /// + /// ### Android + /// + /// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application + /// must [release memory] or risk being killed. + /// + /// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory() + /// [release memory]: https://developer.android.com/topic/performance/memory#release + /// + /// ### iOS + /// + /// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`] + /// callback. The application must free as much memory as possible or risk being terminated, see + /// [how to respond to memory warnings]. + /// + /// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni + /// [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 + /// + /// ### Others + /// + /// - **macOS / Wayland / Windows / Orbital:** Unsupported. + MemoryWarning, } impl Event { @@ -240,6 +266,7 @@ impl Event { LoopExiting => Ok(LoopExiting), Suspended => Ok(Suspended), Resumed => Ok(Resumed), + MemoryWarning => Ok(MemoryWarning), } } } @@ -531,10 +558,23 @@ pub enum WindowEvent { /// This is different to window visibility as it depends on whether the window is closed, /// minimised, set invisible, or fully occluded by another window. /// - /// Platform-specific behavior: + /// ## Platform-specific + /// + /// ### iOS + /// + /// On iOS, the `Occluded(false)` event is emitted in response to an [`applicationWillEnterForeground`] + /// callback which means the application should start preparing its data. The `Occluded(true)` event is + /// emitted in response to an [`applicationDidEnterBackground`] callback which means the application + /// should free resources (according to the [iOS application lifecycle]). + /// + /// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground + /// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground + /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle + /// + /// ### Others /// /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. - /// - **iOS / Android / Wayland / Windows / Orbital:** Unsupported. + /// - **Android / Wayland / Windows / Orbital:** Unsupported. /// /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 62b9bf52..10d431c2 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -272,10 +272,7 @@ impl EventLoop { } } MainEvent::LowMemory => { - // XXX: how to forward this state to applications? - // It seems like ideally winit should support lifecycle and - // low-memory events, especially for mobile platforms. - warn!("TODO: handle Android LowMemory notification"); + callback(event::Event::MemoryWarning, self.window_target()); } MainEvent::Start => { // XXX: how to forward this state to applications? diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index f84efd3f..206e8f50 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -525,10 +525,14 @@ declare_class!( } #[method(applicationWillEnterForeground:)] - fn will_enter_foreground(&self, _application: &UIApplication) {} + fn will_enter_foreground(&self, application: &UIApplication) { + self.send_occluded_event_for_all_windows(application, false); + } #[method(applicationDidEnterBackground:)] - fn did_enter_background(&self, _application: &UIApplication) {} + fn did_enter_background(&self, application: &UIApplication) { + self.send_occluded_event_for_all_windows(application, true); + } #[method(applicationWillTerminate:)] fn will_terminate(&self, application: &UIApplication) { @@ -551,5 +555,33 @@ declare_class!( app_state::handle_nonuser_events(mtm, events); app_state::terminated(mtm); } + + #[method(applicationDidReceiveMemoryWarning:)] + fn did_receive_memory_warning(&self, _application: &UIApplication) { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning)) + } } ); + +impl WinitApplicationDelegate { + fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) { + let mut events = Vec::new(); + for window in application.windows().iter() { + if window.is_kind_of::() { + // SAFETY: We just checked that the window is a `winit` window + let window = unsafe { + let ptr: *const UIWindow = window; + let ptr: *const WinitUIWindow = ptr.cast(); + &*ptr + }; + events.push(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: RootWindowId(window.id()), + event: WindowEvent::Occluded(occluded), + })); + } + } + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_events(mtm, events); + } +}