diff --git a/.changes/introduce-run-return.md b/.changes/introduce-run-return.md new file mode 100644 index 000000000000..23ec9e36d12a --- /dev/null +++ b/.changes/introduce-run-return.md @@ -0,0 +1,8 @@ +--- +tauri: 'minor:feat' +tauri-runtime: 'minor:feat' +tauri-runtime-wry: 'minor:feat' +--- + +Add `App::run_return` function. Contrary to `App::run`, this will **not** exit the thread but instead return the requested exit-code. This allows the host app to perform further cleanup after Tauri has exited. +The `App::run_iteration` function is deprecated as part of this because calling it in a loop - as suggested by the name - will cause a busy-loop. diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 7e8013d6a743..e5e260d70a6a 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -2830,39 +2830,45 @@ impl Runtime for Wry { }); } - fn run) + 'static>(self, mut callback: F) { - let windows = self.context.main_thread.windows.clone(); - let window_id_map = self.context.window_id_map.clone(); - let web_context = self.context.main_thread.web_context; - let plugins = self.context.plugins.clone(); + fn run) + 'static>(self, callback: F) { + let event_handler = make_event_handler(&self, callback); - #[cfg(feature = "tracing")] - let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone(); - let proxy = self.event_loop.create_proxy(); + self.event_loop.run(event_handler) + } - self.event_loop.run(move |event, event_loop, control_flow| { - for p in plugins.lock().unwrap().iter_mut() { - let prevent_default = p.on_event( - &event, - event_loop, - &proxy, - control_flow, - EventLoopIterationContext { - callback: &mut callback, - window_id_map: window_id_map.clone(), - windows: windows.clone(), - #[cfg(feature = "tracing")] - active_tracing_spans: active_tracing_spans.clone(), - }, - &web_context, - ); - if prevent_default { - return; - } - } - handle_event_loop( - event, + #[cfg(desktop)] + fn run_return) + 'static>(mut self, callback: F) -> i32 { + use tao::platform::run_return::EventLoopExtRunReturn; + + let event_handler = make_event_handler(&self, callback); + + self.event_loop.run_return(event_handler) + } +} + +fn make_event_handler( + runtime: &Wry, + mut callback: F, +) -> impl FnMut(Event<'_, Message>, &EventLoopWindowTarget>, &mut ControlFlow) +where + T: UserEvent, + F: FnMut(RunEvent) + 'static, +{ + let windows = runtime.context.main_thread.windows.clone(); + let window_id_map = runtime.context.window_id_map.clone(); + let web_context = runtime.context.main_thread.web_context.clone(); + let plugins = runtime.context.plugins.clone(); + + #[cfg(feature = "tracing")] + let active_tracing_spans = runtime.context.main_thread.active_tracing_spans.clone(); + let proxy = runtime.event_loop.create_proxy(); + + move |event, event_loop, control_flow| { + for p in plugins.lock().unwrap().iter_mut() { + let prevent_default = p.on_event( + &event, event_loop, + &proxy, control_flow, EventLoopIterationContext { callback: &mut callback, @@ -2871,8 +2877,24 @@ impl Runtime for Wry { #[cfg(feature = "tracing")] active_tracing_spans: active_tracing_spans.clone(), }, + &web_context, ); - }) + if prevent_default { + return; + } + } + handle_event_loop( + event, + event_loop, + control_flow, + EventLoopIterationContext { + callback: &mut callback, + window_id_map: window_id_map.clone(), + windows: windows.clone(), + #[cfg(feature = "tracing")] + active_tracing_spans: active_tracing_spans.clone(), + }, + ); } } diff --git a/crates/tauri-runtime/src/lib.rs b/crates/tauri-runtime/src/lib.rs index 8c93a19a0907..a6f0d7a0bbf1 100644 --- a/crates/tauri-runtime/src/lib.rs +++ b/crates/tauri-runtime/src/lib.rs @@ -437,6 +437,10 @@ pub trait Runtime: Debug + Sized + 'static { #[cfg(desktop)] fn run_iteration) + 'static>(&mut self, callback: F); + /// Equivalent to [`Runtime::run`] but returns the exit code instead of exiting the process. + #[cfg(desktop)] + fn run_return) + 'static>(self, callback: F) -> i32; + /// Run the webview runtime. fn run) + 'static>(self, callback: F); } diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index 412d12ba2819..5440e3f341f7 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -225,8 +225,8 @@ name = "multiwindow" path = "../../examples/multiwindow/main.rs" [[example]] -name = "run-iteration" -path = "../../examples/run-iteration/main.rs" +name = "run-return" +path = "../../examples/run-return/main.rs" [[example]] name = "splashscreen" diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index 57fc7a00ac56..0c9f9e67275b 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -1092,6 +1092,47 @@ impl App { }); } + /// Runs the application, returning the exit code to use. + /// + /// # Examples + /// ```,no_run + /// let app = tauri::Builder::default() + /// // on an actual app, remove the string argument + /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")) + /// .expect("error while building tauri application"); + /// let exit_code = app + /// .run_return(|_app_handle, event| match event { + /// tauri::RunEvent::ExitRequested { api, .. } => { + /// api.prevent_exit(); + /// } + /// _ => {} + /// }) + /// .unwrap(); + /// + /// std::process::exit(exit_code); + /// ``` + #[cfg(desktop)] + pub fn run_return, RunEvent) + 'static>( + mut self, + mut callback: F, + ) -> std::result::Result> { + let manager = self.manager.clone(); + let app_handle = self.handle().clone(); + + if !self.ran_setup { + setup(&mut self)?; + } + + let exit_code = self.runtime.take().unwrap().run_return(move |event| { + let event = on_event_loop_event(&app_handle, event, &manager); + callback(&app_handle, event); + }); + + self.cleanup_before_exit(); + + Ok(exit_code) + } + /// Runs an iteration of the runtime event loop and immediately return. /// /// Note that when using this API, app cleanup is not automatically done. @@ -1115,6 +1156,9 @@ impl App { /// } /// ``` #[cfg(desktop)] + #[deprecated( + note = "When called in a loop (as suggested by the name), this function will busy-loop. To re-gain control of control flow after the app has exited, use `App::run_return` instead." + )] pub fn run_iteration, RunEvent) + 'static>(&mut self, mut callback: F) { let manager = self.manager.clone(); let app_handle = self.handle().clone(); diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index 2d45794762ca..e5f0fa9a9e1f 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -1188,6 +1188,13 @@ impl Runtime for MockRuntime { ))] fn run_iteration)>(&mut self, callback: F) {} + #[cfg(desktop)] + fn run_return) + 'static>(self, callback: F) -> i32 { + self.run(callback); + + 0 + } + fn run) + 'static>(self, mut callback: F) { self.is_running.store(true, Ordering::Relaxed); callback(RunEvent::Ready); diff --git a/examples/run-iteration/README.md b/examples/run-return/README.md similarity index 100% rename from examples/run-iteration/README.md rename to examples/run-return/README.md diff --git a/examples/run-iteration/index.html b/examples/run-return/index.html similarity index 100% rename from examples/run-iteration/index.html rename to examples/run-return/index.html diff --git a/examples/run-iteration/main.rs b/examples/run-return/main.rs similarity index 56% rename from examples/run-iteration/main.rs rename to examples/run-return/main.rs index 666de49fe5c9..df3d1fd0781c 100644 --- a/examples/run-iteration/main.rs +++ b/examples/run-return/main.rs @@ -4,23 +4,20 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tauri::Manager; - fn main() { - let mut app = tauri::Builder::default() + let app = tauri::Builder::default() .build(tauri::generate_context!( - "../../examples/run-iteration/tauri.conf.json" + "../../examples/run-return/tauri.conf.json" )) .expect("error while building tauri application"); - loop { - app.run_iteration(|_app, _event| { + let exit_code = app + .run_return(|_app, _event| { //println!("{:?}", _event); - }); + }) + .expect("unreachable, we haven't provided a setup fn"); + + println!("I run after exit"); - if app.webview_windows().is_empty() { - app.cleanup_before_exit(); - break; - } - } + std::process::exit(exit_code); } diff --git a/examples/run-iteration/tauri.conf.json b/examples/run-return/tauri.conf.json similarity index 95% rename from examples/run-iteration/tauri.conf.json rename to examples/run-return/tauri.conf.json index 80851c7aeeaf..fb3eaf94d7c0 100644 --- a/examples/run-iteration/tauri.conf.json +++ b/examples/run-return/tauri.conf.json @@ -1,6 +1,6 @@ { "$schema": "../../crates/tauri-schema-generator/schemas/config.schema.json", - "productName": "RunIteration", + "productName": "RunReturn", "version": "0.1.0", "identifier": "com.tauri.dev", "build": {