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