diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 8e932367..877245e2 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -366,6 +366,20 @@ impl WinitApp for GlowWinitApp<'_> { .and_then(|r| r.glutin.borrow().window_from_viewport.get(&id).copied()) } + fn save(&mut self) { + log::debug!("WinitApp::save called"); + if let Some(running) = self.running.as_mut() { + profiling::function_scope!(); + + // This is used because of the "save on suspend" logic on Android. Once the application is suspended, there is no window associated to it, which was causing panics when `.window().expect()` was used. + let window_opt = running.glutin.borrow().window_opt(ViewportId::ROOT); + + running + .integration + .save(running.app.as_mut(), window_opt.as_deref()); + } + } + fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { profiling::function_scope!(); @@ -413,7 +427,7 @@ impl WinitApp for GlowWinitApp<'_> { if let Some(running) = &mut self.running { running.glutin.borrow_mut().on_suspend()?; } - Ok(EventResult::Wait) + Ok(EventResult::Save) } fn device_event( @@ -1214,10 +1228,12 @@ impl GlutinWindowContext { .expect("viewport doesn't exist") } + fn window_opt(&self, viewport_id: ViewportId) -> Option> { + self.viewport(viewport_id).window.clone() + } + fn window(&self, viewport_id: ViewportId) -> Arc { - self.viewport(viewport_id) - .window - .clone() + self.window_opt(viewport_id) .expect("winit window doesn't exist") } diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index e328877a..fb02ac43 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -89,6 +89,7 @@ impl WinitAppWrapper { event_result: Result, ) { let mut exit = false; + let mut save = false; log::trace!("event_result: {event_result:?}"); @@ -126,6 +127,10 @@ impl WinitAppWrapper { ); Ok(event_result) } + EventResult::Save => { + save = true; + Ok(event_result) + } EventResult::Exit => { exit = true; Ok(event_result) @@ -139,6 +144,11 @@ impl WinitAppWrapper { self.return_result = Err(err); }; + if save { + log::debug!("Received an EventResult::Save - saving app state"); + self.winit_app.save(); + } + if exit { if self.run_and_return { log::debug!("Asking to exit event loop…"); diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index f93386f8..0e0fcbf5 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -355,6 +355,13 @@ impl WinitApp for WgpuWinitApp<'_> { ) } + fn save(&mut self) { + log::debug!("WinitApp::save called"); + if let Some(running) = self.running.as_mut() { + running.save(); + } + } + fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { running.save_and_destroy(); @@ -415,7 +422,7 @@ impl WinitApp for WgpuWinitApp<'_> { fn suspended(&mut self, _: &ActiveEventLoop) -> crate::Result { #[cfg(target_os = "android")] self.drop_window()?; - Ok(EventResult::Wait) + Ok(EventResult::Save) } fn device_event( @@ -488,13 +495,23 @@ impl WinitApp for WgpuWinitApp<'_> { } impl WgpuWinitRunning<'_> { + /// Saves the application state + fn save(&mut self) { + let shared = self.shared.borrow(); + // This is done because of the "save on suspend" logic on Android. Once the application is suspended, there is no window associated to it. + let window = if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) + { + window.as_deref() + } else { + None + }; + self.integration.save(self.app.as_mut(), window); + } + fn save_and_destroy(&mut self) { profiling::function_scope!(); - let mut shared = self.shared.borrow_mut(); - if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) { - self.integration.save(self.app.as_mut(), window.as_deref()); - } + self.save(); #[cfg(feature = "glow")] self.app.on_exit(None); @@ -502,6 +519,7 @@ impl WgpuWinitRunning<'_> { #[cfg(not(feature = "glow"))] self.app.on_exit(); + let mut shared = self.shared.borrow_mut(); shared.painter.destroy(); } diff --git a/crates/eframe/src/native/winit_integration.rs b/crates/eframe/src/native/winit_integration.rs index 2b6c54a6..d85443f3 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -70,6 +70,8 @@ pub trait WinitApp { fn window_id_from_viewport_id(&self, id: ViewportId) -> Option; + fn save(&mut self); + fn save_and_destroy(&mut self); fn run_ui_and_paint( @@ -119,6 +121,9 @@ pub enum EventResult { RepaintAt(WindowId, Instant), + /// Causes a save of the client state when the persistence feature is enabled. + Save, + Exit, }