Skip to main content

tauri/
app.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5use crate::{
6  image::Image,
7  ipc::{
8    channel::ChannelDataIpcQueue, CallbackFn, CommandArg, CommandItem, Invoke, InvokeError,
9    InvokeHandler, InvokeResponseBody,
10  },
11  manager::{webview::UriSchemeProtocol, AppManager, Asset},
12  plugin::{Plugin, PluginStore},
13  resources::ResourceTable,
14  runtime::{
15    window::{WebviewEvent as RuntimeWebviewEvent, WindowEvent as RuntimeWindowEvent},
16    ExitRequestedEventAction, RunEvent as RuntimeRunEvent,
17  },
18  sealed::{ManagerBase, RuntimeOrDispatch},
19  utils::{config::Config, Env},
20  webview::PageLoadPayload,
21  Context, DeviceEventFilter, Emitter, EventLoopMessage, EventName, Listener, Manager, Monitor,
22  Runtime, Scopes, StateManager, Theme, Webview, WebviewWindowBuilder, Window,
23};
24
25#[cfg(desktop)]
26use crate::menu::{Menu, MenuEvent};
27#[cfg(all(desktop, feature = "tray-icon"))]
28use crate::tray::{TrayIcon, TrayIconBuilder, TrayIconEvent, TrayIconId};
29use raw_window_handle::HasDisplayHandle;
30use serialize_to_javascript::{default_template, DefaultTemplate, Template};
31use tauri_macros::default_runtime;
32#[cfg(desktop)]
33use tauri_runtime::EventLoopProxy;
34use tauri_runtime::{
35  dpi::{PhysicalPosition, PhysicalSize},
36  window::DragDropEvent,
37  RuntimeInitArgs,
38};
39use tauri_utils::{assets::AssetsIter, PackageInfo};
40
41use std::{
42  borrow::Cow,
43  collections::HashMap,
44  fmt,
45  sync::{atomic, mpsc::Sender, Arc, Mutex, MutexGuard},
46  thread::ThreadId,
47  time::Duration,
48};
49
50use crate::{event::EventId, runtime::RuntimeHandle, Event, EventTarget};
51
52#[cfg(target_os = "macos")]
53use crate::ActivationPolicy;
54
55pub(crate) mod plugin;
56
57#[cfg(desktop)]
58pub(crate) type GlobalMenuEventListener<T> = Box<dyn Fn(&T, crate::menu::MenuEvent) + Send + Sync>;
59#[cfg(all(desktop, feature = "tray-icon"))]
60pub(crate) type GlobalTrayIconEventListener<T> =
61  Box<dyn Fn(&T, crate::tray::TrayIconEvent) + Send + Sync>;
62pub(crate) type GlobalWindowEventListener<R> = Box<dyn Fn(&Window<R>, &WindowEvent) + Send + Sync>;
63pub(crate) type GlobalWebviewEventListener<R> =
64  Box<dyn Fn(&Webview<R>, &WebviewEvent) + Send + Sync>;
65/// A closure that is run when the Tauri application is setting up.
66pub type SetupHook<R> =
67  Box<dyn FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send>;
68/// A closure that is run every time a page starts or finishes loading.
69pub type OnPageLoad<R> = dyn Fn(&Webview<R>, &PageLoadPayload<'_>) + Send + Sync + 'static;
70/// A closure that is run when the web content process terminates.
71#[cfg(any(target_os = "macos", target_os = "ios"))]
72pub type OnWebContentProcessTerminate<R> = dyn Fn(&Webview<R>) + Send + Sync + 'static;
73pub type ChannelInterceptor<R> =
74  Box<dyn Fn(&Webview<R>, CallbackFn, usize, &InvokeResponseBody) -> bool + Send + Sync + 'static>;
75
76/// The exit code on [`RunEvent::ExitRequested`] when [`AppHandle#method.restart`] is called.
77pub const RESTART_EXIT_CODE: i32 = i32::MAX;
78
79/// Api exposed on the `ExitRequested` event.
80#[derive(Debug, Clone)]
81pub struct ExitRequestApi {
82  tx: Sender<ExitRequestedEventAction>,
83  code: Option<i32>,
84}
85
86impl ExitRequestApi {
87  /// Prevents the app from exiting.
88  ///
89  /// **Note:** This is ignored when using [`AppHandle#method.restart`].
90  pub fn prevent_exit(&self) {
91    if self.code != Some(RESTART_EXIT_CODE) {
92      self.tx.send(ExitRequestedEventAction::Prevent).unwrap();
93    }
94  }
95}
96
97/// Api exposed on the `CloseRequested` event.
98#[derive(Debug, Clone)]
99pub struct CloseRequestApi(Sender<bool>);
100
101impl CloseRequestApi {
102  /// Prevents the window from being closed.
103  pub fn prevent_close(&self) {
104    self.0.send(true).unwrap();
105  }
106}
107
108/// An event from a window.
109#[derive(Debug, Clone)]
110#[non_exhaustive]
111pub enum WindowEvent {
112  /// The size of the window has changed. Contains the client area's new dimensions.
113  Resized(PhysicalSize<u32>),
114  /// The position of the window has changed. Contains the window's new position.
115  Moved(PhysicalPosition<i32>),
116  /// The window has been requested to close.
117  #[non_exhaustive]
118  CloseRequested {
119    /// An API modify the behavior of the close requested event.
120    api: CloseRequestApi,
121  },
122  /// The window has been destroyed.
123  Destroyed,
124  /// The window gained or lost focus.
125  ///
126  /// The parameter is true if the window has gained focus, and false if it has lost focus.
127  Focused(bool),
128  /// The window's scale factor has changed.
129  ///
130  /// The following user actions can cause DPI changes:
131  ///
132  /// - Changing the display's resolution.
133  /// - Changing the display's scale factor (e.g. in Control Panel on Windows).
134  /// - Moving the window to a display with a different scale factor.
135  #[non_exhaustive]
136  ScaleFactorChanged {
137    /// The new scale factor.
138    scale_factor: f64,
139    /// The window inner size.
140    new_inner_size: PhysicalSize<u32>,
141  },
142  /// An event associated with the drag and drop action.
143  DragDrop(DragDropEvent),
144  /// The system window theme has changed. Only delivered if the window [`theme`](`crate::window::WindowBuilder#method.theme`) is `None`.
145  ///
146  /// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme.
147  ///
148  /// ## Platform-specific
149  ///
150  /// - **Linux**: Not supported.
151  ThemeChanged(Theme),
152  /// Emitted when the application has been suspended.
153  ///
154  /// ## Platform-specific
155  ///
156  /// - **Android**: This is triggered by `onPause` method of the Activity.
157  /// - **iOS**: This is triggered by `applicationWillResignActive` method of the UIApplicationDelegate.
158  /// - **Linux / macOS / Windows**: Unsupported.
159  #[cfg(mobile)]
160  Suspended,
161  /// Emitted when the application has been resumed.
162  ///
163  /// ## Platform-specific
164  ///
165  /// - **Android**: This is triggered by `onResume` method of the Activity. The first onResume() is ignored to match the iOS implementation, since that is called on activity creation.
166  /// - **iOS**: This is triggered by `applicationWillEnterForeground` method of the UIApplicationDelegate.
167  /// - **Linux / macOS / Windows**: Unsupported.
168  #[cfg(mobile)]
169  Resumed,
170}
171
172impl From<RuntimeWindowEvent> for WindowEvent {
173  fn from(event: RuntimeWindowEvent) -> Self {
174    match event {
175      RuntimeWindowEvent::Resized(size) => Self::Resized(size),
176      RuntimeWindowEvent::Moved(position) => Self::Moved(position),
177      RuntimeWindowEvent::CloseRequested { signal_tx } => Self::CloseRequested {
178        api: CloseRequestApi(signal_tx),
179      },
180      RuntimeWindowEvent::Destroyed => Self::Destroyed,
181      RuntimeWindowEvent::Focused(flag) => Self::Focused(flag),
182      RuntimeWindowEvent::ScaleFactorChanged {
183        scale_factor,
184        new_inner_size,
185      } => Self::ScaleFactorChanged {
186        scale_factor,
187        new_inner_size,
188      },
189      RuntimeWindowEvent::DragDrop(event) => Self::DragDrop(event),
190      RuntimeWindowEvent::ThemeChanged(theme) => Self::ThemeChanged(theme),
191      #[cfg(mobile)]
192      RuntimeWindowEvent::Suspended => Self::Suspended,
193      #[cfg(mobile)]
194      RuntimeWindowEvent::Resumed => Self::Resumed,
195    }
196  }
197}
198
199/// An event from a window.
200#[derive(Debug, Clone)]
201#[non_exhaustive]
202pub enum WebviewEvent {
203  /// An event associated with the drag and drop action.
204  DragDrop(DragDropEvent),
205}
206
207impl From<RuntimeWebviewEvent> for WebviewEvent {
208  fn from(event: RuntimeWebviewEvent) -> Self {
209    match event {
210      RuntimeWebviewEvent::DragDrop(e) => Self::DragDrop(e),
211    }
212  }
213}
214
215/// An application event, triggered from the event loop.
216///
217/// See [`App::run`](crate::App#method.run) for usage examples.
218#[derive(Debug)]
219#[non_exhaustive]
220pub enum RunEvent {
221  /// Event loop is exiting.
222  Exit,
223  /// The app is about to exit
224  #[non_exhaustive]
225  ExitRequested {
226    /// Exit code.
227    /// [`Option::None`] when the exit is requested by user interaction,
228    /// [`Option::Some`] when requested programmatically via [`AppHandle#method.exit`] and [`AppHandle#method.restart`].
229    code: Option<i32>,
230    /// Event API
231    api: ExitRequestApi,
232  },
233  /// An event associated with a window.
234  #[non_exhaustive]
235  WindowEvent {
236    /// The window label.
237    label: String,
238    /// The detailed event.
239    event: WindowEvent,
240  },
241  /// An event associated with a webview.
242  #[non_exhaustive]
243  WebviewEvent {
244    /// The window label.
245    label: String,
246    /// The detailed event.
247    event: WebviewEvent,
248  },
249  /// Application ready.
250  Ready,
251  /// Sent if the event loop is being resumed.
252  Resumed,
253  /// Emitted when all of the event loop's input events have been processed and redraw processing is about to begin.
254  ///
255  /// This event is useful as a place to put your code that should be run after all state-changing events have been handled and you want to do stuff (updating state, performing calculations, etc) that happens as the "main body" of your event loop.
256  MainEventsCleared,
257  /// Emitted when the user wants to open the specified resource with the app.
258  #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
259  #[cfg_attr(
260    docsrs,
261    doc(cfg(any(target_os = "macos", target_os = "ios", target_os = "android")))
262  )]
263  Opened {
264    /// The URL of the resources that is being open.
265    urls: Vec<url::Url>,
266  },
267  /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
268  #[cfg(desktop)]
269  #[cfg_attr(docsrs, doc(cfg(desktop)))]
270  MenuEvent(crate::menu::MenuEvent),
271  /// An event from a tray icon.
272  #[cfg(all(desktop, feature = "tray-icon"))]
273  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
274  TrayIconEvent(crate::tray::TrayIconEvent),
275  /// Emitted when the NSApplicationDelegate's applicationShouldHandleReopen gets called
276  #[non_exhaustive]
277  #[cfg(target_os = "macos")]
278  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
279  Reopen {
280    /// Indicates whether the NSApplication object found any visible windows in your application.
281    has_visible_windows: bool,
282  },
283  /// Emitted when a scene is requested by the system.
284  ///
285  /// This event is emitted when a scene is requested by the system.
286  /// Scenes created by [`Window::new`] are not emitted with this event.
287  /// It is also not emitted for the main scene.
288  #[cfg(target_os = "ios")]
289  SceneRequested {
290    /// Scene that was requested by the system.
291    scene: objc2::rc::Retained<objc2_ui_kit::UIScene>,
292    /// Options that were used to request the scene.
293    ///
294    /// This lets you determine why the scene was requested.
295    options: objc2::rc::Retained<objc2_ui_kit::UISceneConnectionOptions>,
296  },
297}
298
299impl From<EventLoopMessage> for RunEvent {
300  fn from(event: EventLoopMessage) -> Self {
301    match event {
302      #[cfg(desktop)]
303      EventLoopMessage::MenuEvent(e) => Self::MenuEvent(e),
304      #[cfg(all(desktop, feature = "tray-icon"))]
305      EventLoopMessage::TrayIconEvent(e) => Self::TrayIconEvent(e),
306    }
307  }
308}
309
310/// The asset resolver is a helper to access the [`crate::Assets`] interface.
311#[derive(Debug, Clone)]
312pub struct AssetResolver<R: Runtime> {
313  manager: Arc<AppManager<R>>,
314}
315
316impl<R: Runtime> AssetResolver<R> {
317  /// Gets the app asset associated with the given path.
318  ///
319  /// By default it tries to infer your application's URL scheme in production by checking if all webviews
320  /// were configured with [`crate::webview::WebviewBuilder::use_https_scheme`] or `tauri.conf.json > app > windows > useHttpsScheme`.
321  /// If you are resolving an asset for a webview with a more dynamic configuration, see [`AssetResolver::get_for_scheme`].
322  ///
323  /// In production, this resolves to the embedded asset bundled in the app executable
324  /// which contains your frontend assets in [`frontendDist`](https://v2.tauri.app/reference/config/#frontenddist) during build time.
325  ///
326  /// In dev mode, if [`devUrl`](https://v2.tauri.app/reference/config/#devurl) is set, we don't bundle the assets to reduce re-builds,
327  /// and this will fall back to read from `frontendDist` directly.
328  /// Note that the dist directory must exist so you might need to build your frontend assets first.
329  pub fn get(&self, path: String) -> Option<Asset> {
330    let use_https_scheme = self
331      .manager
332      .webviews()
333      .values()
334      .all(|webview| webview.use_https_scheme());
335    self.get_for_scheme(path, use_https_scheme)
336  }
337
338  ///  Same as [`AssetResolver::get`] but resolves the custom protocol scheme based on a parameter.
339  ///
340  /// - `use_https_scheme`: If `true` when using [`Pattern::Isolation`](crate::Pattern::Isolation),
341  ///   the csp header will contain `https://tauri.localhost` instead of `http://tauri.localhost`
342  pub fn get_for_scheme(&self, path: String, use_https_scheme: bool) -> Option<Asset> {
343    #[cfg(dev)]
344    {
345      // We don't bundle the assets when in dev mode and `devUrl` is set, so fall back to read from `frontendDist` directly
346      // TODO: Maybe handle `FrontendDist::Files` as well
347      if let (Some(_), Some(crate::utils::config::FrontendDist::Directory(dist_path))) = (
348        &self.manager.config().build.dev_url,
349        &self.manager.config().build.frontend_dist,
350      ) {
351        let asset_path = std::path::PathBuf::from(&path)
352          .components()
353          .filter(|c| !matches!(c, std::path::Component::RootDir))
354          .collect::<std::path::PathBuf>();
355
356        let asset_path = self
357          .manager
358          .config_parent()
359          .map(|p| p.join(dist_path).join(&asset_path))
360          .unwrap_or_else(|| dist_path.join(&asset_path));
361        return std::fs::read(asset_path).ok().map(|bytes| {
362          let mime_type = crate::utils::mime_type::MimeType::parse(&bytes, &path);
363          Asset {
364            bytes,
365            mime_type,
366            csp_header: None,
367          }
368        });
369      }
370    }
371
372    self.manager.get_asset(path, use_https_scheme).ok()
373  }
374
375  /// Iterate on all assets.
376  pub fn iter(&self) -> Box<AssetsIter<'_>> {
377    self.manager.assets.iter()
378  }
379}
380
381/// A handle to the currently running application.
382///
383/// This type implements [`Manager`] which allows for manipulation of global application items.
384#[default_runtime(crate::Wry, wry)]
385#[derive(Debug)]
386pub struct AppHandle<R: Runtime> {
387  pub(crate) runtime_handle: R::Handle,
388  pub(crate) manager: Arc<AppManager<R>>,
389  event_loop: Arc<Mutex<EventLoop>>,
390}
391
392/// Not the real event loop, only contains the main thread id of the event loop
393#[derive(Debug)]
394struct EventLoop {
395  main_thread_id: ThreadId,
396}
397
398/// APIs specific to the wry runtime.
399#[cfg(feature = "wry")]
400impl AppHandle<crate::Wry> {
401  /// Create a new tao window using a callback. The event loop must be running at this point.
402  pub fn create_tao_window<
403    F: FnOnce() -> (String, tauri_runtime_wry::TaoWindowBuilder) + Send + 'static,
404  >(
405    &self,
406    f: F,
407  ) -> crate::Result<std::sync::Weak<tauri_runtime_wry::Window>> {
408    self.runtime_handle.create_tao_window(f).map_err(Into::into)
409  }
410
411  /// Sends a window message to the event loop.
412  pub fn send_tao_window_event(
413    &self,
414    window_id: tauri_runtime_wry::TaoWindowId,
415    message: tauri_runtime_wry::WindowMessage,
416  ) -> crate::Result<()> {
417    self
418      .runtime_handle
419      .send_event(tauri_runtime_wry::Message::Window(
420        self.runtime_handle.window_id(window_id),
421        message,
422      ))
423      .map_err(Into::into)
424  }
425}
426
427#[cfg(target_vendor = "apple")]
428impl<R: Runtime> AppHandle<R> {
429  /// Fetches all Data Store Identifiers by this app
430  ///
431  /// Needs to be called from Main Thread
432  pub async fn fetch_data_store_identifiers(&self) -> crate::Result<Vec<[u8; 16]>> {
433    let (tx, rx) = tokio::sync::oneshot::channel::<Result<Vec<[u8; 16]>, tauri_runtime::Error>>();
434    let lock: Arc<Mutex<Option<_>>> = Arc::new(Mutex::new(Some(tx)));
435    let runtime_handle = self.runtime_handle.clone();
436
437    self.run_on_main_thread(move || {
438      let cloned_lock = lock.clone();
439      if let Err(err) = runtime_handle.fetch_data_store_identifiers(move |ids| {
440        if let Some(tx) = cloned_lock.lock().unwrap().take() {
441          let _ = tx.send(Ok(ids));
442        }
443      }) {
444        if let Some(tx) = lock.lock().unwrap().take() {
445          let _ = tx.send(Err(err));
446        }
447      }
448    })?;
449
450    rx.await?.map_err(Into::into)
451  }
452  /// Deletes a Data Store of this app
453  ///
454  /// Needs to be called from Main Thread
455  pub async fn remove_data_store(&self, uuid: [u8; 16]) -> crate::Result<()> {
456    let (tx, rx) = tokio::sync::oneshot::channel::<Result<(), tauri_runtime::Error>>();
457    let lock: Arc<Mutex<Option<_>>> = Arc::new(Mutex::new(Some(tx)));
458    let runtime_handle = self.runtime_handle.clone();
459
460    self.run_on_main_thread(move || {
461      let cloned_lock = lock.clone();
462      if let Err(err) = runtime_handle.remove_data_store(uuid, move |result| {
463        if let Some(tx) = cloned_lock.lock().unwrap().take() {
464          let _ = tx.send(result);
465        }
466      }) {
467        if let Some(tx) = lock.lock().unwrap().take() {
468          let _ = tx.send(Err(err));
469        }
470      }
471    })?;
472    rx.await?.map_err(Into::into)
473  }
474}
475
476impl<R: Runtime> Clone for AppHandle<R> {
477  fn clone(&self) -> Self {
478    Self {
479      runtime_handle: self.runtime_handle.clone(),
480      manager: self.manager.clone(),
481      event_loop: self.event_loop.clone(),
482    }
483  }
484}
485
486impl<'de, R: Runtime> CommandArg<'de, R> for AppHandle<R> {
487  /// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`AppHandle`]. This will never fail.
488  fn from_command(command: CommandItem<'de, R>) -> std::result::Result<Self, InvokeError> {
489    Ok(command.message.webview().app_handle)
490  }
491}
492
493impl<R: Runtime> AppHandle<R> {
494  /// Runs the given closure on the main thread.
495  pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
496    self
497      .runtime_handle
498      .run_on_main_thread(f)
499      .map_err(Into::into)
500  }
501
502  /// Adds a Tauri application plugin.
503  /// This function can be used to register a plugin that is loaded dynamically e.g. after login.
504  /// For plugins that are created when the app is started, prefer [`Builder::plugin`].
505  ///
506  /// See [`Builder::plugin`] for more information.
507  ///
508  /// # Examples
509  ///
510  /// ```
511  /// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin}, Runtime};
512  ///
513  /// fn init_plugin<R: Runtime>() -> TauriPlugin<R> {
514  ///   PluginBuilder::new("dummy").build()
515  /// }
516  ///
517  /// tauri::Builder::default()
518  ///   .setup(move |app| {
519  ///     let handle = app.handle().clone();
520  ///     std::thread::spawn(move || {
521  ///       handle.plugin(init_plugin());
522  ///     });
523  ///
524  ///     Ok(())
525  ///   });
526  /// ```
527  pub fn plugin<P: Plugin<R> + 'static>(&self, plugin: P) -> crate::Result<()> {
528    self.plugin_boxed(Box::new(plugin))
529  }
530
531  /// Adds a Tauri application plugin.
532  ///
533  /// This method is similar to [`Self::plugin`],
534  /// but accepts a boxed trait object instead of a generic type.
535  #[cfg_attr(feature = "tracing", tracing::instrument(name = "app::plugin::register", skip(plugin), fields(name = plugin.name())))]
536  pub fn plugin_boxed(&self, mut plugin: Box<dyn Plugin<R>>) -> crate::Result<()> {
537    let mut store = self.manager().plugins.lock().unwrap();
538    store.initialize(&mut plugin, self, &self.config().plugins)?;
539    store.register(plugin);
540
541    Ok(())
542  }
543
544  /// Removes the plugin with the given name.
545  ///
546  /// # Examples
547  ///
548  /// ```
549  /// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin, Plugin}, Runtime};
550  ///
551  /// fn init_plugin<R: Runtime>() -> TauriPlugin<R> {
552  ///   PluginBuilder::new("dummy").build()
553  /// }
554  ///
555  /// let plugin = init_plugin();
556  /// // `.name()` requires the `Plugin` trait import
557  /// let plugin_name = plugin.name();
558  /// tauri::Builder::default()
559  ///   .plugin(plugin)
560  ///   .setup(move |app| {
561  ///     let handle = app.handle().clone();
562  ///     std::thread::spawn(move || {
563  ///       handle.remove_plugin(plugin_name);
564  ///     });
565  ///
566  ///     Ok(())
567  ///   });
568  /// ```
569  pub fn remove_plugin(&self, plugin: &str) -> bool {
570    self.manager().plugins.lock().unwrap().unregister(plugin)
571  }
572
573  /// Exits the app by triggering [`RunEvent::ExitRequested`] and [`RunEvent::Exit`].
574  pub fn exit(&self, exit_code: i32) {
575    if let Err(e) = self.runtime_handle.request_exit(exit_code) {
576      log::error!("failed to exit: {}", e);
577      self.cleanup_before_exit();
578      std::process::exit(exit_code);
579    }
580  }
581
582  /// Restarts the app by triggering [`RunEvent::ExitRequested`] with code [`RESTART_EXIT_CODE`](crate::RESTART_EXIT_CODE) and [`RunEvent::Exit`].
583  ///
584  /// When this function is called on the main thread, we cannot guarantee the delivery of those events,
585  /// so we skip them and directly restart the process.
586  ///
587  /// If you want to trigger them reliably, use [`Self::request_restart`] instead
588  pub fn restart(&self) -> ! {
589    if self.event_loop.lock().unwrap().main_thread_id == std::thread::current().id() {
590      log::debug!("restart triggered on the main thread");
591      self.cleanup_before_exit();
592      crate::process::restart(&self.env());
593    } else {
594      log::debug!("restart triggered from a separate thread");
595      // we're running on a separate thread, so we must trigger the exit request and wait for it to finish
596      self
597        .manager
598        .restart_on_exit
599        .store(true, atomic::Ordering::Relaxed);
600      // We'll be restarting when we receive the next `RuntimeRunEvent::Exit` event in `App::run` if this call succeed
601      match self.runtime_handle.request_exit(RESTART_EXIT_CODE) {
602        Ok(()) => loop {
603          std::thread::sleep(Duration::MAX);
604        },
605        Err(e) => {
606          log::error!("failed to request exit: {e}");
607          self.cleanup_before_exit();
608          crate::process::restart(&self.env());
609        }
610      }
611    }
612  }
613
614  /// Restarts the app by triggering [`RunEvent::ExitRequested`] with code [`RESTART_EXIT_CODE`] and [`RunEvent::Exit`].
615  pub fn request_restart(&self) {
616    self
617      .manager
618      .restart_on_exit
619      .store(true, atomic::Ordering::Relaxed);
620    // We'll be restarting when we receive the next `RuntimeRunEvent::Exit` event in `App::run` if this call succeed
621    if self.runtime_handle.request_exit(RESTART_EXIT_CODE).is_err() {
622      self.cleanup_before_exit();
623      crate::process::restart(&self.env());
624    }
625  }
626
627  /// Sets the activation policy for the application. It is set to `NSApplicationActivationPolicyRegular` by default.
628  ///
629  /// # Examples
630  /// ```,no_run
631  /// tauri::Builder::default()
632  ///   .setup(move |app| {
633  ///     #[cfg(target_os = "macos")]
634  ///     app.handle().set_activation_policy(tauri::ActivationPolicy::Accessory);
635  ///     Ok(())
636  ///   });
637  /// ```
638  #[cfg(target_os = "macos")]
639  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
640  pub fn set_activation_policy(&self, activation_policy: ActivationPolicy) -> crate::Result<()> {
641    self
642      .runtime_handle
643      .set_activation_policy(activation_policy)
644      .map_err(Into::into)
645  }
646
647  /// Sets the dock visibility for the application.
648  ///
649  /// # Examples
650  /// ```,no_run
651  /// tauri::Builder::default()
652  ///   .setup(move |app| {
653  ///     #[cfg(target_os = "macos")]
654  ///     app.handle().set_dock_visibility(false);
655  ///     Ok(())
656  ///   });
657  /// ```
658  #[cfg(target_os = "macos")]
659  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
660  pub fn set_dock_visibility(&self, visible: bool) -> crate::Result<()> {
661    self
662      .runtime_handle
663      .set_dock_visibility(visible)
664      .map_err(Into::into)
665  }
666
667  /// Change the device event filter mode.
668  ///
669  /// See [App::set_device_event_filter] for details.
670  ///
671  /// ## Platform-specific
672  ///
673  /// See [App::set_device_event_filter] for details.
674  pub fn set_device_event_filter(&self, filter: DeviceEventFilter) {
675    self.runtime_handle.set_device_event_filter(filter);
676  }
677
678  /// Whether the application supports multiple windows.
679  #[cfg(target_os = "ios")]
680  pub fn supports_multiple_windows(&self) -> bool {
681    let (tx, rx) = std::sync::mpsc::channel();
682    self.run_on_main_thread(move || unsafe {
683      let mtm = objc2::MainThreadMarker::new().unwrap();
684      let ui_application = objc2_ui_kit::UIApplication::sharedApplication(mtm);
685      tx.send(ui_application.supportsMultipleScenes()).unwrap();
686    });
687    rx.recv().unwrap()
688  }
689}
690
691impl<R: Runtime> Manager<R> for AppHandle<R> {
692  fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
693    self.manager.resources_table()
694  }
695}
696
697impl<R: Runtime> ManagerBase<R> for AppHandle<R> {
698  fn manager(&self) -> &AppManager<R> {
699    &self.manager
700  }
701
702  fn manager_owned(&self) -> Arc<AppManager<R>> {
703    self.manager.clone()
704  }
705
706  fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
707    RuntimeOrDispatch::RuntimeHandle(self.runtime_handle.clone())
708  }
709
710  fn managed_app_handle(&self) -> &AppHandle<R> {
711    self
712  }
713
714  #[cfg(target_os = "android")]
715  fn activity_name(&self) -> Option<crate::Result<String>> {
716    None
717  }
718
719  #[cfg(target_os = "ios")]
720  fn scene_identifier(&self) -> Option<crate::Result<String>> {
721    None
722  }
723}
724
725/// The instance of the currently running application.
726///
727/// This type implements [`Manager`] which allows for manipulation of global application items.
728#[default_runtime(crate::Wry, wry)]
729pub struct App<R: Runtime> {
730  runtime: Option<R>,
731  setup: Option<SetupHook<R>>,
732  manager: Arc<AppManager<R>>,
733  handle: AppHandle<R>,
734  ran_setup: bool,
735}
736
737impl<R: Runtime> fmt::Debug for App<R> {
738  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
739    f.debug_struct("App")
740      .field("runtime", &self.runtime)
741      .field("manager", &self.manager)
742      .field("handle", &self.handle)
743      .finish()
744  }
745}
746
747impl<R: Runtime> Manager<R> for App<R> {
748  fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
749    self.manager.resources_table()
750  }
751}
752
753impl<R: Runtime> ManagerBase<R> for App<R> {
754  fn manager(&self) -> &AppManager<R> {
755    &self.manager
756  }
757
758  fn manager_owned(&self) -> Arc<AppManager<R>> {
759    self.manager.clone()
760  }
761
762  fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
763    if let Some(runtime) = self.runtime.as_ref() {
764      RuntimeOrDispatch::Runtime(runtime)
765    } else {
766      self.handle.runtime()
767    }
768  }
769
770  fn managed_app_handle(&self) -> &AppHandle<R> {
771    self.handle()
772  }
773
774  #[cfg(target_os = "android")]
775  fn activity_name(&self) -> Option<crate::Result<String>> {
776    None
777  }
778
779  #[cfg(target_os = "ios")]
780  fn scene_identifier(&self) -> Option<crate::Result<String>> {
781    None
782  }
783}
784
785/// APIs specific to the wry runtime.
786#[cfg(feature = "wry")]
787impl App<crate::Wry> {
788  /// Adds a [`tauri_runtime_wry::Plugin`] using its [`tauri_runtime_wry::PluginBuilder`].
789  ///
790  /// # Stability
791  ///
792  /// This API is unstable.
793  pub fn wry_plugin<P: tauri_runtime_wry::PluginBuilder<EventLoopMessage> + Send + 'static>(
794    &mut self,
795    plugin: P,
796  ) where
797    <P as tauri_runtime_wry::PluginBuilder<EventLoopMessage>>::Plugin: Send,
798  {
799    self.handle.runtime_handle.plugin(plugin);
800  }
801}
802
803macro_rules! shared_app_impl {
804  ($app: ty) => {
805    impl<R: Runtime> $app {
806      /// Registers a global menu event listener.
807      #[cfg(desktop)]
808      pub fn on_menu_event<F: Fn(&AppHandle<R>, MenuEvent) + Send + Sync + 'static>(
809        &self,
810        handler: F,
811      ) {
812        self.manager.menu.on_menu_event(handler)
813      }
814
815      /// Registers a global tray icon menu event listener.
816      #[cfg(all(desktop, feature = "tray-icon"))]
817      #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
818      pub fn on_tray_icon_event<F: Fn(&AppHandle<R>, TrayIconEvent) + Send + Sync + 'static>(
819        &self,
820        handler: F,
821      ) {
822        self.manager.tray.on_tray_icon_event(handler)
823      }
824
825      /// Gets a tray icon using the provided id.
826      #[cfg(all(desktop, feature = "tray-icon"))]
827      #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
828      pub fn tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
829      where
830        I: ?Sized,
831        TrayIconId: PartialEq<&'a I>,
832      {
833        self.manager.tray.tray_by_id(self.app_handle(), id)
834      }
835
836      /// Removes a tray icon using the provided id from tauri's internal state and returns it.
837      ///
838      /// Note that dropping the returned icon, may cause the tray icon to disappear
839      /// if it wasn't cloned somewhere else or referenced by JS.
840      #[cfg(all(desktop, feature = "tray-icon"))]
841      #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
842      pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
843      where
844        I: ?Sized,
845        TrayIconId: PartialEq<&'a I>,
846      {
847        self.manager.tray.remove_tray_by_id(self.app_handle(), id)
848      }
849
850      /// Gets the app's configuration, defined on the `tauri.conf.json` file.
851      pub fn config(&self) -> &Config {
852        self.manager.config()
853      }
854
855      /// Gets the app's package information.
856      pub fn package_info(&self) -> &PackageInfo {
857        self.manager.package_info()
858      }
859
860      /// The application's asset resolver.
861      pub fn asset_resolver(&self) -> AssetResolver<R> {
862        AssetResolver {
863          manager: self.manager.clone(),
864        }
865      }
866
867      /// Returns the primary monitor of the system.
868      ///
869      /// Returns None if it can't identify any monitor as a primary one.
870      pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
871        Ok(match self.runtime() {
872          RuntimeOrDispatch::Runtime(h) => h.primary_monitor().map(Into::into),
873          RuntimeOrDispatch::RuntimeHandle(h) => h.primary_monitor().map(Into::into),
874          _ => unreachable!(),
875        })
876      }
877
878      /// Returns the monitor that contains the given point.
879      pub fn monitor_from_point(&self, x: f64, y: f64) -> crate::Result<Option<Monitor>> {
880        Ok(match self.runtime() {
881          RuntimeOrDispatch::Runtime(h) => h.monitor_from_point(x, y).map(Into::into),
882          RuntimeOrDispatch::RuntimeHandle(h) => h.monitor_from_point(x, y).map(Into::into),
883          _ => unreachable!(),
884        })
885      }
886
887      /// Returns the list of all the monitors available on the system.
888      pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
889        Ok(match self.runtime() {
890          RuntimeOrDispatch::Runtime(h) => {
891            h.available_monitors().into_iter().map(Into::into).collect()
892          }
893          RuntimeOrDispatch::RuntimeHandle(h) => {
894            h.available_monitors().into_iter().map(Into::into).collect()
895          }
896          _ => unreachable!(),
897        })
898      }
899
900      /// Get the cursor position relative to the top-left hand corner of the desktop.
901      ///
902      /// Note that the top-left hand corner of the desktop is not necessarily the same as the screen.
903      /// If the user uses a desktop with multiple monitors,
904      /// the top-left hand corner of the desktop is the top-left hand corner of the main monitor on Windows and macOS
905      /// or the top-left of the leftmost monitor on X11.
906      ///
907      /// The coordinates can be negative if the top-left hand corner of the window is outside of the visible screen region.
908      pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
909        Ok(match self.runtime() {
910          RuntimeOrDispatch::Runtime(h) => h.cursor_position()?,
911          RuntimeOrDispatch::RuntimeHandle(h) => h.cursor_position()?,
912          _ => unreachable!(),
913        })
914      }
915
916      /// Sets the app theme.
917      ///
918      /// ## Platform-specific
919      ///
920      /// - **iOS / Android:** Unsupported.
921      pub fn set_theme(&self, theme: Option<Theme>) {
922        #[cfg(windows)]
923        for window in self.manager.windows().values() {
924          if let (Some(menu), Ok(hwnd)) = (window.menu(), window.hwnd()) {
925            let raw_hwnd = hwnd.0 as isize;
926            let _ = self.run_on_main_thread(move || {
927              let _ = unsafe {
928                menu.inner().set_theme_for_hwnd(
929                  raw_hwnd,
930                  theme
931                    .map(crate::menu::map_to_menu_theme)
932                    .unwrap_or(muda::MenuTheme::Auto),
933                )
934              };
935            });
936          };
937        }
938        match self.runtime() {
939          RuntimeOrDispatch::Runtime(h) => h.set_theme(theme),
940          RuntimeOrDispatch::RuntimeHandle(h) => h.set_theme(theme),
941          _ => unreachable!(),
942        }
943      }
944
945      /// Returns the default window icon.
946      pub fn default_window_icon(&self) -> Option<&Image<'_>> {
947        self.manager.window.default_icon.as_ref()
948      }
949
950      /// Returns the app-wide menu.
951      #[cfg(desktop)]
952      pub fn menu(&self) -> Option<Menu<R>> {
953        self.manager.menu.menu_lock().clone()
954      }
955
956      /// Sets the app-wide menu and returns the previous one.
957      ///
958      /// If a window was not created with an explicit menu or had one set explicitly,
959      /// this menu will be assigned to it.
960      #[cfg(desktop)]
961      pub fn set_menu(&self, menu: Menu<R>) -> crate::Result<Option<Menu<R>>> {
962        let prev_menu = self.remove_menu()?;
963
964        self.manager.menu.insert_menu_into_stash(&menu);
965
966        self.manager.menu.menu_lock().replace(menu.clone());
967
968        // set it on all windows that don't have one or previously had the app-wide menu
969        #[cfg(not(target_os = "macos"))]
970        {
971          for window in self.manager.windows().values() {
972            let has_app_wide_menu = window.has_app_wide_menu() || window.menu().is_none();
973            if has_app_wide_menu {
974              window.set_menu(menu.clone())?;
975              window.menu_lock().replace(crate::window::WindowMenu {
976                is_app_wide: true,
977                menu: menu.clone(),
978              });
979            }
980          }
981        }
982
983        // set it app-wide for macos
984        #[cfg(target_os = "macos")]
985        {
986          let menu_ = menu.clone();
987          self.run_on_main_thread(move || {
988            let _ = init_app_menu(&menu_);
989          })?;
990        }
991
992        Ok(prev_menu)
993      }
994
995      /// Remove the app-wide menu and returns it.
996      ///
997      /// If a window was not created with an explicit menu or had one set explicitly,
998      /// this will remove the menu from it.
999      #[cfg(desktop)]
1000      pub fn remove_menu(&self) -> crate::Result<Option<Menu<R>>> {
1001        let menu = self.manager.menu.menu_lock().as_ref().cloned();
1002        #[allow(unused_variables)]
1003        if let Some(menu) = menu {
1004          // remove from windows that have the app-wide menu
1005          #[cfg(not(target_os = "macos"))]
1006          {
1007            for window in self.manager.windows().values() {
1008              let has_app_wide_menu = window.has_app_wide_menu();
1009              if has_app_wide_menu {
1010                window.remove_menu()?;
1011                *window.menu_lock() = None;
1012              }
1013            }
1014          }
1015
1016          // remove app-wide for macos
1017          #[cfg(target_os = "macos")]
1018          {
1019            self.run_on_main_thread(move || {
1020              menu.inner().remove_for_nsapp();
1021            })?;
1022          }
1023        }
1024
1025        let prev_menu = self.manager.menu.menu_lock().take();
1026
1027        self
1028          .manager
1029          .remove_menu_from_stash_by_id(prev_menu.as_ref().map(|m| m.id()));
1030
1031        Ok(prev_menu)
1032      }
1033
1034      /// Hides the app-wide menu from windows that have it.
1035      ///
1036      /// If a window was not created with an explicit menu or had one set explicitly,
1037      /// this will hide the menu from it.
1038      #[cfg(desktop)]
1039      pub fn hide_menu(&self) -> crate::Result<()> {
1040        #[cfg(not(target_os = "macos"))]
1041        {
1042          let is_app_menu_set = self.manager.menu.menu_lock().is_some();
1043          if is_app_menu_set {
1044            for window in self.manager.windows().values() {
1045              if window.has_app_wide_menu() {
1046                window.hide_menu()?;
1047              }
1048            }
1049          }
1050        }
1051
1052        Ok(())
1053      }
1054
1055      /// Shows the app-wide menu for windows that have it.
1056      ///
1057      /// If a window was not created with an explicit menu or had one set explicitly,
1058      /// this will show the menu for it.
1059      #[cfg(desktop)]
1060      pub fn show_menu(&self) -> crate::Result<()> {
1061        #[cfg(not(target_os = "macos"))]
1062        {
1063          let is_app_menu_set = self.manager.menu.menu_lock().is_some();
1064          if is_app_menu_set {
1065            for window in self.manager.windows().values() {
1066              if window.has_app_wide_menu() {
1067                window.show_menu()?;
1068              }
1069            }
1070          }
1071        }
1072
1073        Ok(())
1074      }
1075
1076      /// Shows the application, but does not automatically focus it.
1077      #[cfg(target_os = "macos")]
1078      pub fn show(&self) -> crate::Result<()> {
1079        match self.runtime() {
1080          RuntimeOrDispatch::Runtime(r) => r.show(),
1081          RuntimeOrDispatch::RuntimeHandle(h) => h.show()?,
1082          _ => unreachable!(),
1083        }
1084        Ok(())
1085      }
1086
1087      /// Hides the application.
1088      #[cfg(target_os = "macos")]
1089      pub fn hide(&self) -> crate::Result<()> {
1090        match self.runtime() {
1091          RuntimeOrDispatch::Runtime(r) => r.hide(),
1092          RuntimeOrDispatch::RuntimeHandle(h) => h.hide()?,
1093          _ => unreachable!(),
1094        }
1095        Ok(())
1096      }
1097
1098      /// Runs necessary cleanup tasks before exiting the process.
1099      /// **You should always exit the tauri app immediately after this function returns and not use any tauri-related APIs.**
1100      pub fn cleanup_before_exit(&self) {
1101        #[cfg(all(desktop, feature = "tray-icon"))]
1102        self.manager.tray.icons.lock().unwrap().clear();
1103        self.manager.resources_table().clear();
1104        for (_, window) in self.manager.windows() {
1105          window.resources_table().clear();
1106          #[cfg(windows)]
1107          let _ = window.hide();
1108        }
1109        for (_, webview) in self.manager.webviews() {
1110          webview.resources_table().clear();
1111        }
1112      }
1113
1114      /// Gets the invoke key that must be referenced when using [`crate::webview::InvokeRequest`].
1115      ///
1116      /// # Security
1117      ///
1118      /// DO NOT expose this key to third party scripts as might grant access to the backend from external URLs and iframes.
1119      pub fn invoke_key(&self) -> &str {
1120        self.manager.invoke_key()
1121      }
1122
1123      /// Whether the application supports multiple windows.
1124      #[cfg(desktop)]
1125      pub fn supports_multiple_windows(&self) -> bool {
1126        true
1127      }
1128
1129      /// Whether the application supports multiple windows.
1130      #[cfg(target_os = "android")]
1131      pub fn supports_multiple_windows(&self) -> bool {
1132        let runtime_handle = match self.runtime() {
1133          RuntimeOrDispatch::Runtime(runtime) => runtime.handle(),
1134          RuntimeOrDispatch::RuntimeHandle(handle) => handle,
1135          _ => unreachable!(),
1136        };
1137
1138        let (tx, rx) = std::sync::mpsc::channel();
1139
1140        runtime_handle.run_on_android_context(move |env, _activity, _webview| {
1141          let supports = (|| {
1142            let version_class = env.find_class("android/os/Build$VERSION")?;
1143            let sdk = env
1144              .get_static_field(version_class, "SDK_INT", "I")?
1145              .i()
1146              .unwrap_or_default();
1147            crate::Result::Ok(sdk >= 32)
1148          })()
1149          .unwrap_or(false);
1150
1151          let _ = tx.send(supports);
1152        });
1153
1154        rx.recv().unwrap_or(false)
1155      }
1156    }
1157
1158    impl<R: Runtime> Listener<R> for $app {
1159      /// Listen to an event on this app.
1160      ///
1161      /// # Examples
1162      ///
1163      /// ```
1164      /// use tauri::Listener;
1165      ///
1166      /// tauri::Builder::default()
1167      ///   .setup(|app| {
1168      ///     app.listen("component-loaded", move |event| {
1169      ///       println!("window just loaded a component");
1170      ///     });
1171      ///
1172      ///     Ok(())
1173      ///   });
1174      /// ```
1175      fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
1176      where
1177        F: Fn(Event) + Send + 'static,
1178      {
1179        let event = EventName::new(event.into()).unwrap();
1180        self.manager.listen(event, EventTarget::App, handler)
1181      }
1182
1183      /// Listen to an event on this app only once.
1184      ///
1185      /// See [`Self::listen`] for more information.
1186      fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
1187      where
1188        F: FnOnce(Event) + Send + 'static,
1189      {
1190        let event = EventName::new(event.into()).unwrap();
1191        self.manager.once(event, EventTarget::App, handler)
1192      }
1193
1194      /// Unlisten to an event on this app.
1195      ///
1196      /// # Examples
1197      ///
1198      /// ```
1199      /// use tauri::Listener;
1200      ///
1201      /// tauri::Builder::default()
1202      ///   .setup(|app| {
1203      ///     let handler = app.listen("component-loaded", move |event| {
1204      ///       println!("app just loaded a component");
1205      ///     });
1206      ///
1207      ///     // stop listening to the event when you do not need it anymore
1208      ///     app.unlisten(handler);
1209      ///
1210      ///     Ok(())
1211      ///   });
1212      /// ```
1213      fn unlisten(&self, id: EventId) {
1214        self.manager.unlisten(id)
1215      }
1216    }
1217
1218    impl<R: Runtime> Emitter<R> for $app {}
1219  };
1220}
1221
1222shared_app_impl!(App<R>);
1223shared_app_impl!(AppHandle<R>);
1224
1225impl<R: Runtime> App<R> {
1226  #[cfg_attr(
1227    feature = "tracing",
1228    tracing::instrument(name = "app::core_plugins::register")
1229  )]
1230  fn register_core_plugins(&self) -> crate::Result<()> {
1231    self.handle.plugin(crate::path::plugin::init())?;
1232    self.handle.plugin(crate::event::plugin::init(self))?;
1233    self.handle.plugin(crate::window::plugin::init())?;
1234    self.handle.plugin(crate::webview::plugin::init())?;
1235    self.handle.plugin(crate::app::plugin::init())?;
1236    self.handle.plugin(crate::resources::plugin::init())?;
1237    self.handle.plugin(crate::image::plugin::init())?;
1238    #[cfg(desktop)]
1239    self.handle.plugin(crate::menu::plugin::init())?;
1240    #[cfg(all(desktop, feature = "tray-icon"))]
1241    self.handle.plugin(crate::tray::plugin::init())?;
1242    Ok(())
1243  }
1244
1245  /// Runs the given closure on the main thread.
1246  pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
1247    self.app_handle().run_on_main_thread(f)
1248  }
1249
1250  /// Gets a handle to the application instance.
1251  pub fn handle(&self) -> &AppHandle<R> {
1252    &self.handle
1253  }
1254
1255  /// Whether the application supports multiple windows.
1256  #[cfg(target_os = "ios")]
1257  pub fn supports_multiple_windows(&self) -> bool {
1258    unsafe {
1259      let mtm = objc2::MainThreadMarker::new().unwrap();
1260      let ui_application = objc2_ui_kit::UIApplication::sharedApplication(mtm);
1261      ui_application.supportsMultipleScenes()
1262    }
1263  }
1264
1265  /// Sets the activation policy for the application. It is set to `NSApplicationActivationPolicyRegular` by default.
1266  ///
1267  /// # Examples
1268  /// ```,no_run
1269  /// tauri::Builder::default()
1270  ///   .setup(move |app| {
1271  ///     #[cfg(target_os = "macos")]
1272  ///     app.set_activation_policy(tauri::ActivationPolicy::Accessory);
1273  ///     Ok(())
1274  ///   });
1275  /// ```
1276  #[cfg(target_os = "macos")]
1277  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1278  pub fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
1279    if let Some(runtime) = self.runtime.as_mut() {
1280      runtime.set_activation_policy(activation_policy);
1281    } else {
1282      let _ = self.app_handle().set_activation_policy(activation_policy);
1283    }
1284  }
1285
1286  /// Sets the dock visibility for the application.
1287  ///
1288  /// # Examples
1289  /// ```,no_run
1290  /// tauri::Builder::default()
1291  ///   .setup(move |app| {
1292  ///     #[cfg(target_os = "macos")]
1293  ///     app.set_dock_visibility(false);
1294  ///     Ok(())
1295  ///   });
1296  /// ```
1297  #[cfg(target_os = "macos")]
1298  #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1299  pub fn set_dock_visibility(&mut self, visible: bool) {
1300    if let Some(runtime) = self.runtime.as_mut() {
1301      runtime.set_dock_visibility(visible);
1302    } else {
1303      let _ = self.app_handle().set_dock_visibility(visible);
1304    }
1305  }
1306
1307  /// Change the device event filter mode.
1308  ///
1309  /// Since the DeviceEvent capture can lead to high CPU usage for unfocused windows, [`tao`]
1310  /// will ignore them by default for unfocused windows on Windows. This method allows changing
1311  /// the filter to explicitly capture them again.
1312  ///
1313  /// ## Platform-specific
1314  ///
1315  /// - ** Linux / macOS / iOS / Android**: Unsupported.
1316  ///
1317  /// # Examples
1318  /// ```,no_run
1319  /// let mut app = tauri::Builder::default()
1320  ///   // on an actual app, remove the string argument
1321  ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
1322  ///   .expect("error while building tauri application");
1323  /// app.set_device_event_filter(tauri::DeviceEventFilter::Always);
1324  /// app.run(|_app_handle, _event| {});
1325  /// ```
1326  ///
1327  /// [`tao`]: https://crates.io/crates/tao
1328  pub fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {
1329    self
1330      .runtime
1331      .as_mut()
1332      .unwrap()
1333      .set_device_event_filter(filter);
1334  }
1335
1336  /// Runs the application.
1337  ///
1338  /// This function never returns. When the application finishes, the process is exited directly using [`std::process::exit`].
1339  /// See [`run_return`](Self::run_return) if you need to run code after the application event loop exits.
1340  ///
1341  /// # Panics
1342  ///
1343  /// This function will panic if the setup-function supplied in [`Builder::setup`] fails.
1344  ///
1345  /// # Examples
1346  /// ```,no_run
1347  /// let app = tauri::Builder::default()
1348  ///   // on an actual app, remove the string argument
1349  ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
1350  ///   .expect("error while building tauri application");
1351  /// app.run(|_app_handle, event| match event {
1352  ///   tauri::RunEvent::ExitRequested { api, .. } => {
1353  ///     api.prevent_exit();
1354  ///   }
1355  ///   _ => {}
1356  /// });
1357  /// ```
1358  pub fn run<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(mut self, callback: F) {
1359    self.handle.event_loop.lock().unwrap().main_thread_id = std::thread::current().id();
1360
1361    self
1362      .runtime
1363      .take()
1364      .unwrap()
1365      .run(self.make_run_event_loop_callback(callback));
1366  }
1367
1368  /// Runs the application, returning its intended exit code.
1369  ///
1370  /// Note when using [`AppHandle::restart`] and [`AppHandle::request_restart`],
1371  /// this function will handle the restart request, exit and restart the app without returning
1372  ///
1373  /// ## Platform-specific
1374  ///
1375  /// - **iOS**: Unsupported. The application will fallback to [`run`](Self::run).
1376  ///
1377  /// # Panics
1378  ///
1379  /// This function will panic if the setup-function supplied in [`Builder::setup`] fails.
1380  ///
1381  /// # Examples
1382  /// ```,no_run
1383  /// let app = tauri::Builder::default()
1384  ///   // on an actual app, remove the string argument
1385  ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
1386  ///   .expect("error while building tauri application");
1387  /// let exit_code = app
1388  ///   .run_return(|_app_handle, event| match event {
1389  ///     tauri::RunEvent::ExitRequested { api, .. } => {
1390  ///      api.prevent_exit();
1391  ///     }
1392  ///      _ => {}
1393  ///   });
1394  ///
1395  /// std::process::exit(exit_code);
1396  /// ```
1397  pub fn run_return<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(mut self, callback: F) -> i32 {
1398    self.handle.event_loop.lock().unwrap().main_thread_id = std::thread::current().id();
1399
1400    self
1401      .runtime
1402      .take()
1403      .unwrap()
1404      .run_return(self.make_run_event_loop_callback(callback))
1405  }
1406
1407  fn make_run_event_loop_callback<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
1408    mut self,
1409    mut callback: F,
1410  ) -> impl FnMut(RuntimeRunEvent<EventLoopMessage>) {
1411    let app_handle = self.handle().clone();
1412    let manager = self.manager.clone();
1413
1414    move |event| match event {
1415      RuntimeRunEvent::Ready => {
1416        if let Err(e) = setup(&mut self) {
1417          panic!("Failed to setup app: {e}");
1418        }
1419        let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Ready, &manager);
1420        callback(&app_handle, event);
1421      }
1422      RuntimeRunEvent::Exit => {
1423        let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Exit, &manager);
1424        callback(&app_handle, event);
1425        app_handle.cleanup_before_exit();
1426        if self.manager.restart_on_exit.load(atomic::Ordering::Relaxed) {
1427          crate::process::restart(&self.env());
1428        }
1429      }
1430      _ => {
1431        let event = on_event_loop_event(&app_handle, event, &manager);
1432        callback(&app_handle, event);
1433      }
1434    }
1435  }
1436
1437  /// Runs an iteration of the runtime event loop and immediately return.
1438  ///
1439  /// Note that when using this API, app cleanup is not automatically done.
1440  /// The cleanup calls [`App::cleanup_before_exit`] so you may want to call that function before exiting the application.
1441  ///
1442  /// # Examples
1443  /// ```no_run
1444  /// use tauri::Manager;
1445  ///
1446  /// let mut app = tauri::Builder::default()
1447  ///   // on an actual app, remove the string argument
1448  ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
1449  ///   .expect("error while building tauri application");
1450  ///
1451  /// loop {
1452  ///   app.run_iteration(|_app, _event| {});
1453  ///   if app.webview_windows().is_empty() {
1454  ///     app.cleanup_before_exit();
1455  ///     break;
1456  ///   }
1457  /// }
1458  /// ```
1459  #[cfg(desktop)]
1460  #[deprecated(
1461    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."
1462  )]
1463  pub fn run_iteration<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(&mut self, mut callback: F) {
1464    let manager = self.manager.clone();
1465    let app_handle = self.handle().clone();
1466
1467    if !self.ran_setup {
1468      if let Err(e) = setup(self) {
1469        panic!("Failed to setup app: {e}");
1470      }
1471    }
1472
1473    app_handle.event_loop.lock().unwrap().main_thread_id = std::thread::current().id();
1474
1475    self.runtime.as_mut().unwrap().run_iteration(move |event| {
1476      let event = on_event_loop_event(&app_handle, event, &manager);
1477      callback(&app_handle, event);
1478    })
1479  }
1480}
1481
1482/// Builds a Tauri application.
1483///
1484/// # Examples
1485/// ```,no_run
1486/// tauri::Builder::default()
1487///   // on an actual app, remove the string argument
1488///   .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
1489///  .expect("error while running tauri application");
1490/// ```
1491#[allow(clippy::type_complexity)]
1492pub struct Builder<R: Runtime> {
1493  /// A flag indicating that the runtime must be started on an environment that supports the event loop not on the main thread.
1494  #[cfg(any(windows, target_os = "linux"))]
1495  runtime_any_thread: bool,
1496
1497  /// The JS message handler.
1498  invoke_handler: Box<InvokeHandler<R>>,
1499
1500  /// The script that initializes the `window.__TAURI_INTERNALS__.postMessage` function.
1501  pub(crate) invoke_initialization_script: String,
1502
1503  channel_interceptor: Option<ChannelInterceptor<R>>,
1504
1505  /// The setup hook.
1506  setup: SetupHook<R>,
1507
1508  /// Page load hook.
1509  on_page_load: Option<Arc<OnPageLoad<R>>>,
1510
1511  /// Web content process termination hook.
1512  #[cfg(any(target_os = "macos", target_os = "ios"))]
1513  on_web_content_process_terminate: Option<Arc<OnWebContentProcessTerminate<R>>>,
1514
1515  /// All passed plugins
1516  plugins: PluginStore<R>,
1517
1518  /// The webview protocols available to all windows.
1519  uri_scheme_protocols: HashMap<String, Arc<UriSchemeProtocol<R>>>,
1520
1521  /// App state.
1522  state: StateManager,
1523
1524  /// A closure that returns the menu set to all windows.
1525  #[cfg(desktop)]
1526  menu: Option<Box<dyn FnOnce(&AppHandle<R>) -> crate::Result<Menu<R>> + Send>>,
1527
1528  /// Menu event listeners for any menu event.
1529  #[cfg(desktop)]
1530  menu_event_listeners: Vec<GlobalMenuEventListener<AppHandle<R>>>,
1531
1532  /// Tray event listeners for any tray icon event.
1533  #[cfg(all(desktop, feature = "tray-icon"))]
1534  tray_icon_event_listeners: Vec<GlobalTrayIconEventListener<AppHandle<R>>>,
1535
1536  /// Enable macOS default menu creation.
1537  #[allow(unused)]
1538  enable_macos_default_menu: bool,
1539
1540  /// Window event handlers that listens to all windows.
1541  window_event_listeners: Vec<GlobalWindowEventListener<R>>,
1542
1543  /// Webview event handlers that listens to all webviews.
1544  webview_event_listeners: Vec<GlobalWebviewEventListener<R>>,
1545
1546  /// The device event filter.
1547  device_event_filter: DeviceEventFilter,
1548
1549  pub(crate) invoke_key: String,
1550}
1551
1552#[derive(Template)]
1553#[default_template("../scripts/ipc-protocol.js")]
1554pub(crate) struct InvokeInitializationScript<'a> {
1555  /// The function that processes the IPC message.
1556  #[raw]
1557  pub(crate) process_ipc_message_fn: &'a str,
1558  pub(crate) os_name: &'a str,
1559  pub(crate) fetch_channel_data_command: &'a str,
1560  pub(crate) invoke_key: &'a str,
1561}
1562
1563/// Make `Wry` the default `Runtime` for `Builder`
1564#[cfg(feature = "wry")]
1565#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
1566impl Default for Builder<crate::Wry> {
1567  fn default() -> Self {
1568    Self::new()
1569  }
1570}
1571
1572#[cfg(not(feature = "wry"))]
1573#[cfg_attr(docsrs, doc(cfg(not(feature = "wry"))))]
1574impl<R: Runtime> Default for Builder<R> {
1575  fn default() -> Self {
1576    Self::new()
1577  }
1578}
1579
1580impl<R: Runtime> Builder<R> {
1581  /// Creates a new App builder.
1582  pub fn new() -> Self {
1583    let invoke_key = crate::generate_invoke_key().unwrap();
1584
1585    Self {
1586      #[cfg(any(windows, target_os = "linux"))]
1587      runtime_any_thread: false,
1588      setup: Box::new(|_| Ok(())),
1589      invoke_handler: Box::new(|_| false),
1590      invoke_initialization_script: InvokeInitializationScript {
1591        process_ipc_message_fn: crate::manager::webview::PROCESS_IPC_MESSAGE_FN,
1592        os_name: std::env::consts::OS,
1593        fetch_channel_data_command: crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND,
1594        invoke_key: &invoke_key.clone(),
1595      }
1596      .render_default(&Default::default())
1597      .unwrap()
1598      .into_string(),
1599      channel_interceptor: None,
1600      on_page_load: None,
1601      #[cfg(any(target_os = "macos", target_os = "ios"))]
1602      on_web_content_process_terminate: None,
1603      plugins: PluginStore::default(),
1604      uri_scheme_protocols: Default::default(),
1605      state: StateManager::new(),
1606      #[cfg(desktop)]
1607      menu: None,
1608      #[cfg(desktop)]
1609      menu_event_listeners: Vec::new(),
1610      #[cfg(all(desktop, feature = "tray-icon"))]
1611      tray_icon_event_listeners: Vec::new(),
1612      enable_macos_default_menu: true,
1613      window_event_listeners: Vec::new(),
1614      webview_event_listeners: Vec::new(),
1615      device_event_filter: Default::default(),
1616      invoke_key,
1617    }
1618  }
1619}
1620
1621impl<R: Runtime> Builder<R> {
1622  /// Builds a new Tauri application running on any thread, bypassing the main thread requirement.
1623  ///
1624  /// ## Platform-specific
1625  ///
1626  /// - **macOS:** on macOS the application *must* be executed on the main thread, so this function is not exposed.
1627  #[cfg(any(windows, target_os = "linux"))]
1628  #[cfg_attr(docsrs, doc(cfg(any(windows, target_os = "linux"))))]
1629  #[must_use]
1630  pub fn any_thread(mut self) -> Self {
1631    self.runtime_any_thread = true;
1632    self
1633  }
1634
1635  /// Defines the JS message handler callback.
1636  ///
1637  /// # Examples
1638  /// ```
1639  /// #[tauri::command]
1640  /// fn command_1() -> String {
1641  ///   return "hello world".to_string();
1642  /// }
1643  /// tauri::Builder::default()
1644  ///   .invoke_handler(tauri::generate_handler![
1645  ///     command_1,
1646  ///     // etc...
1647  ///   ]);
1648  /// ```
1649  #[must_use]
1650  pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
1651  where
1652    F: Fn(Invoke<R>) -> bool + Send + Sync + 'static,
1653  {
1654    self.invoke_handler = Box::new(invoke_handler);
1655    self
1656  }
1657
1658  /// Defines a custom JS message system.
1659  ///
1660  /// The `initialization_script` is a script that initializes `window.__TAURI_INTERNALS__.postMessage`.
1661  /// That function must take the `(message: object, options: object)` arguments and send it to the backend.
1662  ///
1663  /// Additionally, the script must include a `__INVOKE_KEY__` token that is replaced with a value that must be sent with the IPC payload
1664  /// to check the integrity of the message by the [`crate::WebviewWindow::on_message`] API, e.g.
1665  ///
1666  /// ```js
1667  /// const invokeKey = __INVOKE_KEY__;
1668  /// fetch('my-impl://command', {
1669  ///   headers: {
1670  ///     'Tauri-Invoke-Key': invokeKey,
1671  ///   }
1672  /// })
1673  /// ```
1674  ///
1675  /// Note that the implementation details is up to your implementation.
1676  #[must_use]
1677  pub fn invoke_system(mut self, initialization_script: impl AsRef<str>) -> Self {
1678    self.invoke_initialization_script = initialization_script
1679      .as_ref()
1680      .replace("__INVOKE_KEY__", &format!("\"{}\"", self.invoke_key));
1681    self
1682  }
1683
1684  /// Registers a channel interceptor that can overwrite the default channel implementation.
1685  ///
1686  /// If the event has been consumed, it must return `true`.
1687  ///
1688  /// The channel automatically orders the messages, so the third closure argument represents the message number.
1689  /// The payload expected by the channel receiver is in the form of `{ id: usize, message: T }`.
1690  pub fn channel_interceptor<
1691    F: Fn(&Webview<R>, CallbackFn, usize, &InvokeResponseBody) -> bool + Send + Sync + 'static,
1692  >(
1693    mut self,
1694    interceptor: F,
1695  ) -> Self {
1696    self.channel_interceptor.replace(Box::new(interceptor));
1697    self
1698  }
1699
1700  /// Append a custom initialization script.
1701  ///
1702  /// Allow to append custom initialization script instead of replacing entire invoke system.
1703  ///
1704  /// # Examples
1705  ///
1706  /// ```
1707  /// let custom_script = r#"
1708  /// // A custom call system bridge build on top of tauri invoke system.
1709  /// async function invoke(cmd, args = {}) {
1710  ///   if (!args) args = {};
1711  ///
1712  ///   let prefix = "";
1713  ///
1714  ///   if (args?.__module) {
1715  ///     prefix = `plugin:hybridcall.${args.__module}|`;
1716  ///   }
1717  ///
1718  ///   const command = `${prefix}tauri_${cmd}`;
1719  ///
1720  ///   const invoke = window.__TAURI_INTERNALS__.invoke;
1721  ///
1722  ///   return invoke(command, args).then(result => {
1723  ///     if (window.build.debug) {
1724  ///       console.log(`call: ${command}`);
1725  ///       console.log(`args: ${JSON.stringify(args)}`);
1726  ///       console.log(`return: ${JSON.stringify(result)}`);
1727  ///     }
1728  ///
1729  ///     return result;
1730  ///   });
1731  /// }
1732  /// "#;
1733  ///
1734  /// tauri::Builder::default()
1735  ///   .append_invoke_initialization_script(custom_script);
1736  /// ```
1737  pub fn append_invoke_initialization_script(
1738    mut self,
1739    initialization_script: impl AsRef<str>,
1740  ) -> Self {
1741    self
1742      .invoke_initialization_script
1743      .push_str(initialization_script.as_ref());
1744    self
1745  }
1746
1747  /// Defines the setup hook.
1748  ///
1749  /// # Examples
1750  #[cfg_attr(
1751    feature = "unstable",
1752    doc = r####"
1753```
1754use tauri::Manager;
1755tauri::Builder::default()
1756  .setup(|app| {
1757    let main_window = app.get_webview_window("main").unwrap();
1758    main_window.set_title("Tauri!")?;
1759    Ok(())
1760  });
1761```
1762  "####
1763  )]
1764  #[must_use]
1765  pub fn setup<F>(mut self, setup: F) -> Self
1766  where
1767    F: FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send + 'static,
1768  {
1769    self.setup = Box::new(setup);
1770    self
1771  }
1772
1773  /// Defines the page load hook.
1774  #[must_use]
1775  pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
1776  where
1777    F: Fn(&Webview<R>, &PageLoadPayload<'_>) + Send + Sync + 'static,
1778  {
1779    self.on_page_load.replace(Arc::new(on_page_load));
1780    self
1781  }
1782
1783  /// Defines the web content process termination hook.
1784  ///
1785  /// ## Platform-specific
1786  ///
1787  /// - **Linux / Windows / Android:** Unsupported.
1788  #[cfg(any(target_os = "macos", target_os = "ios"))]
1789  #[must_use]
1790  pub fn on_web_content_process_terminate<F>(mut self, on_web_content_process_terminate: F) -> Self
1791  where
1792    F: Fn(&Webview<R>) + Send + Sync + 'static,
1793  {
1794    self
1795      .on_web_content_process_terminate
1796      .replace(Arc::new(on_web_content_process_terminate));
1797    self
1798  }
1799
1800  /// Adds a Tauri application plugin.
1801  ///
1802  /// A plugin is created using the [`crate::plugin::Builder`] struct.Check its documentation for more information.
1803  ///
1804  /// # Examples
1805  ///
1806  /// ```
1807  /// mod plugin {
1808  ///   use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin}, RunEvent, Runtime};
1809  ///
1810  ///   // this command can be called in the frontend using `invoke('plugin:window|do_something')`.
1811  ///   #[tauri::command]
1812  ///   async fn do_something<R: Runtime>(app: tauri::AppHandle<R>, window: tauri::Window<R>) -> Result<(), String> {
1813  ///     println!("command called");
1814  ///     Ok(())
1815  ///   }
1816  ///   pub fn init<R: Runtime>() -> TauriPlugin<R> {
1817  ///     PluginBuilder::new("window")
1818  ///       .setup(|app, api| {
1819  ///         // initialize the plugin here
1820  ///         Ok(())
1821  ///       })
1822  ///       .on_event(|app, event| {
1823  ///         match event {
1824  ///           RunEvent::Ready => {
1825  ///             println!("app is ready");
1826  ///           }
1827  ///           RunEvent::WindowEvent { label, event, .. } => {
1828  ///             println!("window {} received an event: {:?}", label, event);
1829  ///           }
1830  ///           _ => (),
1831  ///         }
1832  ///       })
1833  ///       .invoke_handler(tauri::generate_handler![do_something])
1834  ///       .build()
1835  ///   }
1836  /// }
1837  ///
1838  /// tauri::Builder::default()
1839  ///   .plugin(plugin::init());
1840  /// ```
1841  #[must_use]
1842  pub fn plugin<P: Plugin<R> + 'static>(self, plugin: P) -> Self {
1843    self.plugin_boxed(Box::new(plugin))
1844  }
1845
1846  /// Adds a Tauri application plugin.
1847  ///
1848  /// This method is similar to [`Self::plugin`],
1849  /// but accepts a boxed trait object instead of a generic type.
1850  #[must_use]
1851  pub fn plugin_boxed(mut self, plugin: Box<dyn Plugin<R>>) -> Self {
1852    self.plugins.register(plugin);
1853    self
1854  }
1855
1856  /// Add `state` to the state managed by the application.
1857  ///
1858  /// This method can be called any number of times as long as each call
1859  /// refers to a different `T`.
1860  ///
1861  /// Managed state can be retrieved by any command handler via the
1862  /// [`crate::State`] guard. In particular, if a value of type `T`
1863  /// is managed by Tauri, adding `State<T>` to the list of arguments in a
1864  /// command handler instructs Tauri to retrieve the managed value.
1865  /// Additionally, [`state`](crate::Manager#method.state) can be used to retrieve the value manually.
1866  ///
1867  /// # Panics
1868  ///
1869  /// Panics if state of type `T` is already being managed.
1870  ///
1871  /// # Mutability
1872  ///
1873  /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
1874  ///
1875  /// ```,no_run
1876  /// use std::{collections::HashMap, sync::Mutex};
1877  /// use tauri::State;
1878  /// // here we use Mutex to achieve interior mutability
1879  /// struct Storage {
1880  ///   store: Mutex<HashMap<u64, String>>,
1881  /// }
1882  /// struct Connection;
1883  /// struct DbConnection {
1884  ///   db: Mutex<Option<Connection>>,
1885  /// }
1886  ///
1887  /// #[tauri::command]
1888  /// fn connect(connection: State<DbConnection>) {
1889  ///   // initialize the connection, mutating the state with interior mutability
1890  ///   *connection.db.lock().unwrap() = Some(Connection {});
1891  /// }
1892  ///
1893  /// #[tauri::command]
1894  /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
1895  ///   // mutate the storage behind the Mutex
1896  ///   storage.store.lock().unwrap().insert(key, value);
1897  /// }
1898  ///
1899  /// tauri::Builder::default()
1900  ///   .manage(Storage { store: Default::default() })
1901  ///   .manage(DbConnection { db: Default::default() })
1902  ///   .invoke_handler(tauri::generate_handler![connect, storage_insert])
1903  ///   // on an actual app, remove the string argument
1904  ///   .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
1905  ///   .expect("error while running tauri application");
1906  /// ```
1907  ///
1908  /// # Examples
1909  ///
1910  /// ```,no_run
1911  /// use tauri::State;
1912  ///
1913  /// struct MyInt(isize);
1914  /// struct MyString(String);
1915  ///
1916  /// #[tauri::command]
1917  /// fn int_command(state: State<MyInt>) -> String {
1918  ///     format!("The stateful int is: {}", state.0)
1919  /// }
1920  ///
1921  /// #[tauri::command]
1922  /// fn string_command<'r>(state: State<'r, MyString>) {
1923  ///     println!("state: {}", state.inner().0);
1924  /// }
1925  ///
1926  /// tauri::Builder::default()
1927  ///   .manage(MyInt(10))
1928  ///   .manage(MyString("Hello, managed state!".to_string()))
1929  ///   .invoke_handler(tauri::generate_handler![int_command, string_command])
1930  ///   // on an actual app, remove the string argument
1931  ///   .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
1932  ///   .expect("error while running tauri application");
1933  /// ```
1934  #[must_use]
1935  pub fn manage<T>(self, state: T) -> Self
1936  where
1937    T: Send + Sync + 'static,
1938  {
1939    let type_name = std::any::type_name::<T>();
1940    assert!(
1941      self.state.set(state),
1942      "state for type '{type_name}' is already being managed",
1943    );
1944    self
1945  }
1946
1947  /// Sets the menu to use on all windows.
1948  ///
1949  /// # Examples
1950  /// ```
1951  /// use tauri::menu::{Menu, MenuItem, PredefinedMenuItem, Submenu};
1952  ///
1953  /// tauri::Builder::default()
1954  ///   .menu(|handle| Menu::with_items(handle, &[
1955  ///     &Submenu::with_items(
1956  ///       handle,
1957  ///       "File",
1958  ///       true,
1959  ///       &[
1960  ///         &PredefinedMenuItem::close_window(handle, None)?,
1961  ///         #[cfg(target_os = "macos")]
1962  ///         &MenuItem::new(handle, "Hello", true, None::<&str>)?,
1963  ///       ],
1964  ///     )?
1965  ///   ]));
1966  /// ```
1967  #[must_use]
1968  #[cfg(desktop)]
1969  pub fn menu<F: FnOnce(&AppHandle<R>) -> crate::Result<Menu<R>> + Send + 'static>(
1970    mut self,
1971    f: F,
1972  ) -> Self {
1973    self.menu.replace(Box::new(f));
1974    self
1975  }
1976
1977  /// Registers an event handler for any menu event.
1978  ///
1979  /// # Examples
1980  /// ```
1981  /// use tauri::menu::*;
1982  ///
1983  /// tauri::Builder::default()
1984  ///   .on_menu_event(|app, event| {
1985  ///      if event.id() == "quit" {
1986  ///        app.exit(0);
1987  ///      }
1988  ///   });
1989  /// ```
1990  #[must_use]
1991  #[cfg(desktop)]
1992  pub fn on_menu_event<F: Fn(&AppHandle<R>, MenuEvent) + Send + Sync + 'static>(
1993    mut self,
1994    f: F,
1995  ) -> Self {
1996    self.menu_event_listeners.push(Box::new(f));
1997    self
1998  }
1999
2000  /// Registers an event handler for any tray icon event.
2001  ///
2002  /// # Examples
2003  /// ```
2004  /// use tauri::Manager;
2005  ///
2006  /// tauri::Builder::default()
2007  ///   .on_tray_icon_event(|app, event| {
2008  ///      let tray = app.tray_by_id(event.id()).expect("can't find tray icon");
2009  ///      let _ = tray.set_visible(false);
2010  ///   });
2011  /// ```
2012  #[must_use]
2013  #[cfg(all(desktop, feature = "tray-icon"))]
2014  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
2015  pub fn on_tray_icon_event<F: Fn(&AppHandle<R>, TrayIconEvent) + Send + Sync + 'static>(
2016    mut self,
2017    f: F,
2018  ) -> Self {
2019    self.tray_icon_event_listeners.push(Box::new(f));
2020    self
2021  }
2022
2023  /// Enable or disable the default menu on macOS. Enabled by default.
2024  ///
2025  /// # Examples
2026  /// ```
2027  /// tauri::Builder::default()
2028  ///   .enable_macos_default_menu(false);
2029  /// ```
2030  #[must_use]
2031  pub fn enable_macos_default_menu(mut self, enable: bool) -> Self {
2032    self.enable_macos_default_menu = enable;
2033    self
2034  }
2035
2036  /// Registers a window event handler for all windows.
2037  ///
2038  /// # Examples
2039  /// ```
2040  /// tauri::Builder::default()
2041  ///   .on_window_event(|window, event| match event {
2042  ///     tauri::WindowEvent::Focused(focused) => {
2043  ///       // hide window whenever it loses focus
2044  ///       if !focused {
2045  ///         window.hide().unwrap();
2046  ///       }
2047  ///     }
2048  ///     _ => {}
2049  ///   });
2050  /// ```
2051  #[must_use]
2052  pub fn on_window_event<F: Fn(&Window<R>, &WindowEvent) + Send + Sync + 'static>(
2053    mut self,
2054    handler: F,
2055  ) -> Self {
2056    self.window_event_listeners.push(Box::new(handler));
2057    self
2058  }
2059
2060  /// Registers a webview event handler for all webviews.
2061  ///
2062  /// # Examples
2063  /// ```
2064  /// tauri::Builder::default()
2065  ///   .on_webview_event(|window, event| match event {
2066  ///     tauri::WebviewEvent::DragDrop(event) => {
2067  ///       println!("{:?}", event);
2068  ///     }
2069  ///     _ => {}
2070  ///   });
2071  /// ```
2072  #[must_use]
2073  pub fn on_webview_event<F: Fn(&Webview<R>, &WebviewEvent) + Send + Sync + 'static>(
2074    mut self,
2075    handler: F,
2076  ) -> Self {
2077    self.webview_event_listeners.push(Box::new(handler));
2078    self
2079  }
2080
2081  /// Registers a URI scheme protocol available to all webviews.
2082  ///
2083  /// Leverages [setURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) on macOS,
2084  /// [AddWebResourceRequestedFilter](https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addwebresourcerequestedfilter?view=webview2-dotnet-1.0.774.44) on Windows
2085  /// and [webkit-web-context-register-uri-scheme](https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebContext.html#webkit-web-context-register-uri-scheme) on Linux.
2086  ///
2087  /// # Arguments
2088  ///
2089  /// * `uri_scheme` The URI scheme to register, such as `example`.
2090  /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes a request and returns a response.
2091  ///
2092  /// # Examples
2093  /// ```
2094  /// tauri::Builder::default()
2095  ///   .register_uri_scheme_protocol("app-files", |_ctx, request| {
2096  ///     // skip leading `/`
2097  ///     if let Ok(data) = std::fs::read(&request.uri().path()[1..]) {
2098  ///       http::Response::builder()
2099  ///         .body(data)
2100  ///         .unwrap()
2101  ///     } else {
2102  ///       http::Response::builder()
2103  ///         .status(http::StatusCode::BAD_REQUEST)
2104  ///         .header(http::header::CONTENT_TYPE, mime::TEXT_PLAIN.essence_str())
2105  ///         .body("failed to read file".as_bytes().to_vec())
2106  ///         .unwrap()
2107  ///     }
2108  ///   });
2109  /// ```
2110  ///
2111  /// # Warning
2112  ///
2113  /// Pages loaded from a custom protocol will have a different Origin on different platforms.
2114  /// Servers which enforce CORS will need to add the exact same Origin header (or `*`) in `Access-Control-Allow-Origin`
2115  /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
2116  /// different Origin headers across platforms:
2117  ///
2118  /// - macOS, iOS and Linux: `<scheme_name>://localhost/<path>` (so it will be `my-scheme://localhost/path/to/page).
2119  /// - Windows and Android: `http://<scheme_name>.localhost/<path>` by default (so it will be `http://my-scheme.localhost/path/to/page`).
2120  ///   To use `https` instead of `http`, use [`super::webview::WebviewBuilder::use_https_scheme`].
2121  #[must_use]
2122  pub fn register_uri_scheme_protocol<
2123    N: Into<String>,
2124    T: Into<Cow<'static, [u8]>>,
2125    H: Fn(UriSchemeContext<'_, R>, http::Request<Vec<u8>>) -> http::Response<T>
2126      + Send
2127      + Sync
2128      + 'static,
2129  >(
2130    mut self,
2131    uri_scheme: N,
2132    protocol_handler: H,
2133  ) -> Self {
2134    self.uri_scheme_protocols.insert(
2135      uri_scheme.into(),
2136      Arc::new(UriSchemeProtocol {
2137        handler: Box::new(move |ctx, request, responder| {
2138          responder.respond(protocol_handler(ctx, request))
2139        }),
2140      }),
2141    );
2142    self
2143  }
2144
2145  /// Similar to [`Self::register_uri_scheme_protocol`] but with an asynchronous responder that allows you
2146  /// to process the request in a separate thread and respond asynchronously.
2147  ///
2148  /// # Arguments
2149  ///
2150  /// * `uri_scheme` The URI scheme to register, such as `example`.
2151  /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
2152  ///
2153  /// # Examples
2154  /// ```
2155  /// tauri::Builder::default()
2156  ///   .register_asynchronous_uri_scheme_protocol("app-files", |_ctx, request, responder| {
2157  ///     // skip leading `/`
2158  ///     let path = request.uri().path()[1..].to_string();
2159  ///     std::thread::spawn(move || {
2160  ///       if let Ok(data) = std::fs::read(path) {
2161  ///         responder.respond(
2162  ///           http::Response::builder()
2163  ///             .body(data)
2164  ///             .unwrap()
2165  ///         );
2166  ///       } else {
2167  ///         responder.respond(
2168  ///           http::Response::builder()
2169  ///             .status(http::StatusCode::BAD_REQUEST)
2170  ///             .header(http::header::CONTENT_TYPE, mime::TEXT_PLAIN.essence_str())
2171  ///             .body("failed to read file".as_bytes().to_vec())
2172  ///             .unwrap()
2173  ///         );
2174  ///       }
2175  ///     });
2176  ///   });
2177  /// ```
2178  ///
2179  /// # Warning
2180  ///
2181  /// Pages loaded from a custom protocol will have a different Origin on different platforms.
2182  /// Servers which enforce CORS will need to add the exact same Origin header (or `*`) in `Access-Control-Allow-Origin`
2183  /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
2184  /// different Origin headers across platforms:
2185  ///
2186  /// - macOS, iOS and Linux: `<scheme_name>://localhost/<path>` (so it will be `my-scheme://localhost/path/to/page).
2187  /// - Windows and Android: `http://<scheme_name>.localhost/<path>` by default (so it will be `http://my-scheme.localhost/path/to/page`).
2188  ///   To use `https` instead of `http`, use [`super::webview::WebviewBuilder::use_https_scheme`].
2189  #[must_use]
2190  pub fn register_asynchronous_uri_scheme_protocol<
2191    N: Into<String>,
2192    H: Fn(UriSchemeContext<'_, R>, http::Request<Vec<u8>>, UriSchemeResponder) + Send + Sync + 'static,
2193  >(
2194    mut self,
2195    uri_scheme: N,
2196    protocol_handler: H,
2197  ) -> Self {
2198    self.uri_scheme_protocols.insert(
2199      uri_scheme.into(),
2200      Arc::new(UriSchemeProtocol {
2201        handler: Box::new(protocol_handler),
2202      }),
2203    );
2204    self
2205  }
2206
2207  /// Change the device event filter mode.
2208  ///
2209  /// Since the DeviceEvent capture can lead to high CPU usage for unfocused windows, [`tao`]
2210  /// will ignore them by default for unfocused windows on Windows. This method allows changing
2211  /// the filter to explicitly capture them again.
2212  ///
2213  /// ## Platform-specific
2214  ///
2215  /// - ** Linux / macOS / iOS / Android**: Unsupported.
2216  ///
2217  /// # Examples
2218  /// ```,no_run
2219  /// tauri::Builder::default()
2220  ///   .device_event_filter(tauri::DeviceEventFilter::Always);
2221  /// ```
2222  ///
2223  /// [`tao`]: https://crates.io/crates/tao
2224  pub fn device_event_filter(mut self, filter: DeviceEventFilter) -> Self {
2225    self.device_event_filter = filter;
2226    self
2227  }
2228
2229  /// Builds the application.
2230  #[allow(clippy::type_complexity, unused_mut)]
2231  #[cfg_attr(
2232    feature = "tracing",
2233    tracing::instrument(name = "app::build", skip_all)
2234  )]
2235  pub fn build(mut self, context: Context<R>) -> crate::Result<App<R>> {
2236    #[cfg(target_os = "macos")]
2237    if self.menu.is_none() && self.enable_macos_default_menu {
2238      self.menu = Some(Box::new(|app_handle| {
2239        crate::menu::Menu::default(app_handle)
2240      }));
2241    }
2242
2243    let manager = Arc::new(AppManager::with_handlers(
2244      context,
2245      self.plugins,
2246      self.invoke_handler,
2247      self.on_page_load,
2248      #[cfg(any(target_os = "macos", target_os = "ios"))]
2249      self.on_web_content_process_terminate,
2250      self.uri_scheme_protocols,
2251      self.state,
2252      #[cfg(desktop)]
2253      self.menu_event_listeners,
2254      #[cfg(all(desktop, feature = "tray-icon"))]
2255      self.tray_icon_event_listeners,
2256      self.window_event_listeners,
2257      self.webview_event_listeners,
2258      #[cfg(desktop)]
2259      HashMap::new(),
2260      self.invoke_initialization_script,
2261      self.channel_interceptor,
2262      self.invoke_key,
2263    ));
2264
2265    #[cfg(any(
2266      target_os = "linux",
2267      target_os = "dragonfly",
2268      target_os = "freebsd",
2269      target_os = "netbsd",
2270      target_os = "openbsd"
2271    ))]
2272    let app_id = if manager.config.app.enable_gtk_app_id {
2273      Some(manager.config.identifier.clone())
2274    } else {
2275      None
2276    };
2277
2278    let runtime_args = RuntimeInitArgs {
2279      #[cfg(any(
2280        target_os = "linux",
2281        target_os = "dragonfly",
2282        target_os = "freebsd",
2283        target_os = "netbsd",
2284        target_os = "openbsd"
2285      ))]
2286      app_id,
2287
2288      #[cfg(windows)]
2289      msg_hook: {
2290        let menus = manager.menu.menus.clone();
2291        Some(Box::new(move |msg| {
2292          use windows::Win32::UI::WindowsAndMessaging::{TranslateAcceleratorW, HACCEL, MSG};
2293          unsafe {
2294            let msg = msg as *const MSG;
2295            for menu in menus.lock().unwrap().values() {
2296              let translated =
2297                TranslateAcceleratorW((*msg).hwnd, HACCEL(menu.inner().haccel() as _), msg);
2298              if translated == 1 {
2299                return true;
2300              }
2301            }
2302
2303            false
2304          }
2305        }))
2306      },
2307    };
2308
2309    // The env var must be set before the Runtime is created so that GetAvailableBrowserVersionString picks it up.
2310    #[cfg(windows)]
2311    {
2312      if let crate::utils::config::WebviewInstallMode::FixedRuntime { path } =
2313        &manager.config.bundle.windows.webview_install_mode
2314      {
2315        if let Some(exe_dir) = crate::utils::platform::current_exe()
2316          .ok()
2317          .and_then(|p| p.parent().map(|p| p.to_path_buf()))
2318        {
2319          std::env::set_var("WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", exe_dir.join(path));
2320        } else {
2321          #[cfg(debug_assertions)]
2322          eprintln!(
2323            "failed to resolve resource directory; fallback to the installed Webview2 runtime."
2324          );
2325        }
2326      }
2327    }
2328
2329    #[cfg(any(windows, target_os = "linux"))]
2330    let mut runtime = if self.runtime_any_thread {
2331      R::new_any_thread(runtime_args)?
2332    } else {
2333      R::new(runtime_args)?
2334    };
2335    #[cfg(not(any(windows, target_os = "linux")))]
2336    let mut runtime = R::new(runtime_args)?;
2337
2338    #[cfg(desktop)]
2339    {
2340      // setup menu event handler
2341      let proxy = runtime.create_proxy();
2342      muda::MenuEvent::set_event_handler(Some(move |e: muda::MenuEvent| {
2343        let _ = proxy.send_event(EventLoopMessage::MenuEvent(e.into()));
2344      }));
2345
2346      // setup tray event handler
2347      #[cfg(feature = "tray-icon")]
2348      {
2349        let proxy = runtime.create_proxy();
2350        tray_icon::TrayIconEvent::set_event_handler(Some(move |e: tray_icon::TrayIconEvent| {
2351          let _ = proxy.send_event(EventLoopMessage::TrayIconEvent(e.into()));
2352        }));
2353      }
2354    }
2355
2356    runtime.set_device_event_filter(self.device_event_filter);
2357
2358    let runtime_handle = runtime.handle();
2359
2360    #[allow(unused_mut)]
2361    let mut app = App {
2362      runtime: Some(runtime),
2363      setup: Some(self.setup),
2364      manager: manager.clone(),
2365      handle: AppHandle {
2366        runtime_handle,
2367        manager,
2368        event_loop: Arc::new(Mutex::new(EventLoop {
2369          main_thread_id: std::thread::current().id(),
2370        })),
2371      },
2372      ran_setup: false,
2373    };
2374
2375    #[cfg(desktop)]
2376    if let Some(menu) = self.menu {
2377      let menu = menu(&app.handle)?;
2378      app
2379        .manager
2380        .menu
2381        .menus_stash_lock()
2382        .insert(menu.id().clone(), menu.clone());
2383
2384      #[cfg(target_os = "macos")]
2385      init_app_menu(&menu)?;
2386
2387      app.manager.menu.menu_lock().replace(menu);
2388    }
2389
2390    app.register_core_plugins()?;
2391
2392    let env = Env::default();
2393    app.manage(env);
2394
2395    app.manage(Scopes {
2396      #[cfg(feature = "protocol-asset")]
2397      asset_protocol: crate::scope::fs::Scope::new(
2398        &app,
2399        &app.config().app.security.asset_protocol.scope,
2400      )?,
2401    });
2402
2403    app.manage(ChannelDataIpcQueue::default());
2404    app.handle.plugin(crate::ipc::channel::plugin())?;
2405
2406    let handle = app.handle();
2407
2408    // initialize default tray icon if defined
2409    #[cfg(all(desktop, feature = "tray-icon"))]
2410    {
2411      let config = app.config();
2412      if let Some(tray_config) = &config.app.tray_icon {
2413        #[allow(deprecated)]
2414        let mut tray =
2415          TrayIconBuilder::with_id(tray_config.id.clone().unwrap_or_else(|| "main".into()))
2416            .icon_as_template(tray_config.icon_as_template)
2417            .menu_on_left_click(tray_config.menu_on_left_click)
2418            .show_menu_on_left_click(tray_config.show_menu_on_left_click);
2419        if let Some(icon) = &app.manager.tray.icon {
2420          tray = tray.icon(icon.clone());
2421        }
2422        if let Some(title) = &tray_config.title {
2423          tray = tray.title(title);
2424        }
2425        if let Some(tooltip) = &tray_config.tooltip {
2426          tray = tray.tooltip(tooltip);
2427        }
2428        tray.build(handle)?;
2429      }
2430    }
2431
2432    app.manager.initialize_plugins(handle)?;
2433
2434    Ok(app)
2435  }
2436
2437  /// Builds the configured application and runs it.
2438  ///
2439  /// This is a shorthand for [`Self::build`] followed by [`App::run`].
2440  /// For more flexibility, consider using those functions manually.
2441  pub fn run(self, context: Context<R>) -> crate::Result<()> {
2442    self.build(context)?.run(|_, _| {});
2443    Ok(())
2444  }
2445}
2446
2447pub(crate) type UriSchemeResponderFn = Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>;
2448
2449/// Async uri scheme protocol responder.
2450pub struct UriSchemeResponder(pub(crate) UriSchemeResponderFn);
2451
2452impl UriSchemeResponder {
2453  /// Resolves the request with the given response.
2454  pub fn respond<T: Into<Cow<'static, [u8]>>>(self, response: http::Response<T>) {
2455    let (parts, body) = response.into_parts();
2456    (self.0)(http::Response::from_parts(parts, body.into()))
2457  }
2458}
2459
2460/// Uri scheme protocol context
2461pub struct UriSchemeContext<'a, R: Runtime> {
2462  pub(crate) app_handle: &'a AppHandle<R>,
2463  pub(crate) webview_label: &'a str,
2464}
2465
2466impl<'a, R: Runtime> UriSchemeContext<'a, R> {
2467  /// Get a reference to an [`AppHandle`].
2468  pub fn app_handle(&self) -> &'a AppHandle<R> {
2469    self.app_handle
2470  }
2471
2472  /// Get the webview label that made the uri scheme request.
2473  pub fn webview_label(&self) -> &'a str {
2474    self.webview_label
2475  }
2476}
2477
2478#[cfg(target_os = "macos")]
2479fn init_app_menu<R: Runtime>(menu: &Menu<R>) -> crate::Result<()> {
2480  menu.inner().init_for_nsapp();
2481
2482  if let Some(window_menu) = menu.get(crate::menu::WINDOW_SUBMENU_ID) {
2483    if let Some(m) = window_menu.as_submenu() {
2484      m.set_as_windows_menu_for_nsapp()?;
2485    }
2486  }
2487  if let Some(help_menu) = menu.get(crate::menu::HELP_SUBMENU_ID) {
2488    if let Some(m) = help_menu.as_submenu() {
2489      m.set_as_help_menu_for_nsapp()?;
2490    }
2491  }
2492
2493  Ok(())
2494}
2495
2496impl<R: Runtime> HasDisplayHandle for AppHandle<R> {
2497  fn display_handle(
2498    &self,
2499  ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
2500    self.runtime_handle.display_handle()
2501  }
2502}
2503
2504impl<R: Runtime> HasDisplayHandle for App<R> {
2505  fn display_handle(
2506    &self,
2507  ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
2508    self.handle.display_handle()
2509  }
2510}
2511
2512#[cfg_attr(feature = "tracing", tracing::instrument(name = "app::setup"))]
2513fn setup<R: Runtime>(app: &mut App<R>) -> crate::Result<()> {
2514  app.ran_setup = true;
2515
2516  for window_config in app.config().app.windows.iter().filter(|w| w.create) {
2517    WebviewWindowBuilder::from_config(app.handle(), window_config)?.build()?;
2518  }
2519
2520  app.manager.assets.setup(app);
2521
2522  if let Some(setup) = app.setup.take() {
2523    (setup)(app).map_err(|e| crate::Error::Setup(e.into()))?;
2524  }
2525
2526  Ok(())
2527}
2528
2529fn on_event_loop_event<R: Runtime>(
2530  app_handle: &AppHandle<R>,
2531  event: RuntimeRunEvent<EventLoopMessage>,
2532  manager: &AppManager<R>,
2533) -> RunEvent {
2534  if let RuntimeRunEvent::WindowEvent {
2535    label,
2536    event: RuntimeWindowEvent::Destroyed,
2537  } = &event
2538  {
2539    manager.on_window_close(label);
2540  }
2541
2542  let event = match event {
2543    RuntimeRunEvent::Exit => RunEvent::Exit,
2544    RuntimeRunEvent::ExitRequested { code, tx } => RunEvent::ExitRequested {
2545      code,
2546      api: ExitRequestApi { tx, code },
2547    },
2548    RuntimeRunEvent::WindowEvent { label, event } => RunEvent::WindowEvent {
2549      label,
2550      event: event.into(),
2551    },
2552    RuntimeRunEvent::WebviewEvent { label, event } => RunEvent::WebviewEvent {
2553      label,
2554      event: event.into(),
2555    },
2556    RuntimeRunEvent::Ready => {
2557      // set the app icon in development
2558      #[cfg(all(dev, target_os = "macos"))]
2559      {
2560        use objc2::{AllocAnyThread, MainThreadMarker};
2561        use objc2_app_kit::{NSApplication, NSImage};
2562        use objc2_foundation::NSData;
2563
2564        if let Some(icon) = app_handle.manager.app_icon.clone() {
2565          // TODO: Enable this check.
2566          let mtm = unsafe { MainThreadMarker::new_unchecked() };
2567          let app = NSApplication::sharedApplication(mtm);
2568          let data = NSData::with_bytes(&icon);
2569          let app_icon = NSImage::initWithData(NSImage::alloc(), &data).expect("creating icon");
2570          unsafe { app.setApplicationIconImage(Some(&app_icon)) };
2571        }
2572      }
2573      RunEvent::Ready
2574    }
2575    RuntimeRunEvent::Resumed => RunEvent::Resumed,
2576    RuntimeRunEvent::MainEventsCleared => RunEvent::MainEventsCleared,
2577    RuntimeRunEvent::UserEvent(t) => {
2578      match t {
2579        #[cfg(desktop)]
2580        EventLoopMessage::MenuEvent(ref e) => {
2581          for listener in &*app_handle
2582            .manager
2583            .menu
2584            .global_event_listeners
2585            .lock()
2586            .unwrap()
2587          {
2588            listener(app_handle, e.clone());
2589          }
2590          for (label, listener) in &*app_handle.manager.menu.event_listeners.lock().unwrap() {
2591            if let Some(w) = app_handle.manager().get_window(label) {
2592              listener(&w, e.clone());
2593            }
2594          }
2595        }
2596        #[cfg(all(desktop, feature = "tray-icon"))]
2597        EventLoopMessage::TrayIconEvent(ref e) => {
2598          for listener in &*app_handle
2599            .manager
2600            .tray
2601            .global_event_listeners
2602            .lock()
2603            .unwrap()
2604          {
2605            listener(app_handle, e.clone());
2606          }
2607
2608          for (id, listener) in &*app_handle.manager.tray.event_listeners.lock().unwrap() {
2609            if e.id() == id {
2610              if let Some(tray) = app_handle.tray_by_id(id) {
2611                listener(&tray, e.clone());
2612              }
2613            }
2614          }
2615        }
2616      }
2617
2618      #[allow(unreachable_code)]
2619      t.into()
2620    }
2621    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
2622    RuntimeRunEvent::Opened { urls } => RunEvent::Opened { urls },
2623    #[cfg(target_os = "macos")]
2624    RuntimeRunEvent::Reopen {
2625      has_visible_windows,
2626    } => RunEvent::Reopen {
2627      has_visible_windows,
2628    },
2629    #[cfg(target_os = "ios")]
2630    RuntimeRunEvent::SceneRequested { scene, options } => {
2631      RunEvent::SceneRequested { scene, options }
2632    }
2633    _ => unimplemented!(),
2634  };
2635
2636  manager
2637    .plugins
2638    .lock()
2639    .expect("poisoned plugin store")
2640    .on_event(app_handle, &event);
2641
2642  event
2643}
2644
2645#[cfg(test)]
2646mod tests {
2647  #[test]
2648  fn is_send_sync() {
2649    crate::test_utils::assert_send::<super::AppHandle>();
2650    crate::test_utils::assert_sync::<super::AppHandle>();
2651
2652    #[cfg(feature = "wry")]
2653    {
2654      crate::test_utils::assert_send::<super::AssetResolver<crate::Wry>>();
2655      crate::test_utils::assert_sync::<super::AssetResolver<crate::Wry>>();
2656    }
2657  }
2658}