lib.rs 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
  5. //!
  6. //! Tauri is a framework for building tiny, blazing fast binaries for all major desktop platforms.
  7. //! Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface.
  8. //! The backend of the application is a rust-sourced binary with an API that the front-end can interact with.
  9. //!
  10. //! # Cargo features
  11. //!
  12. //! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that can be enabled or disabled:
  13. //!
  14. //! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime.
  15. //! - **common-controls-v6** *(enabled by default)*: Enables [Common Controls v6](https://learn.microsoft.com/en-us/windows/win32/controls/common-control-versions) support on Windows, mainly for the predefined `about` menu item.
  16. //! - **unstable**: Enables unstable features. Be careful, it might introduce breaking changes in future minor releases.
  17. //! - **tracing**: Enables [`tracing`](https://docs.rs/tracing/latest/tracing) for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers.
  18. //! - **test**: Enables the [`mod@test`] module exposing unit test helpers.
  19. //! - **objc-exception**: Wrap each msg_send! in a @try/@catch and panics if an exception is caught, preventing Objective-C from unwinding into Rust.
  20. //! - **linux-ipc-protocol**: Use custom protocol for faster IPC on Linux. Requires webkit2gtk v2.40 or above.
  21. //! - **linux-libxdo**: Enables linking to libxdo which enables Cut, Copy, Paste and SelectAll menu items to work on Linux.
  22. //! - **isolation**: Enables the isolation pattern. Enabled by default if the `app > security > pattern > use` config option is set to `isolation` on the `tauri.conf.json` file.
  23. //! - **custom-protocol**: Feature managed by the Tauri CLI. When enabled, Tauri assumes a production environment instead of a development one.
  24. //! - **devtools**: Enables the developer tools (Web inspector) and [`window::Window#method.open_devtools`]. Enabled by default on debug builds.
  25. //! On macOS it uses private APIs, so you can't enable it if your app will be published to the App Store.
  26. //! - **native-tls**: Provides TLS support to connect over HTTPS.
  27. //! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL.
  28. //! - **rustls-tls**: Provides TLS support to connect over HTTPS using rustls.
  29. //! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website).
  30. //! - **tray-icon**: Enables application tray icon APIs. Enabled by default if the `trayIcon` config is defined on the `tauri.conf.json` file.
  31. //! - **macos-private-api**: Enables features only available in **macOS**'s private APIs, currently the `transparent` window functionality and the `fullScreenEnabled` preference setting to `true`. Enabled by default if the `tauri > macosPrivateApi` config flag is set to `true` on the `tauri.conf.json` file.
  32. //! - **webview-data-url**: Enables usage of data URLs on the webview.
  33. //! - **compression** *(enabled by default): Enables asset compression. You should only disable this if you want faster compile times in release builds - it produces larger binaries.
  34. //! - **config-json5**: Adds support to JSON5 format for `tauri.conf.json`.
  35. //! - **config-toml**: Adds support to TOML format for the configuration `Tauri.toml`.
  36. //! - **icon-ico**: Adds support to set `.ico` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants.
  37. //! - **icon-png**: Adds support to set `.png` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants.
  38. //! - **macos-proxy**: Adds support for [`WebviewBuilder::proxy_url`] on macOS. Requires macOS 14+.
  39. //!
  40. //! ## Cargo allowlist features
  41. //!
  42. //! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that enables commands for Tauri's API package.
  43. //! These features are automatically enabled by the Tauri CLI based on the `allowlist` configuration under `tauri.conf.json`.
  44. //!
  45. //! ### Protocol allowlist
  46. //!
  47. //! - **protocol-asset**: Enables the `asset` custom protocol.
  48. #![doc(
  49. html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
  50. html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
  51. )]
  52. #![warn(missing_docs, rust_2018_idioms)]
  53. #![cfg_attr(docsrs, feature(doc_cfg))]
  54. /// Setups the binding that initializes an iOS plugin.
  55. #[cfg(target_os = "ios")]
  56. #[macro_export]
  57. macro_rules! ios_plugin_binding {
  58. ($fn_name: ident) => {
  59. tauri::swift_rs::swift!(fn $fn_name() -> *const ::std::ffi::c_void);
  60. }
  61. }
  62. #[cfg(target_os = "ios")]
  63. #[doc(hidden)]
  64. pub use cocoa;
  65. #[cfg(target_os = "macos")]
  66. #[doc(hidden)]
  67. pub use embed_plist;
  68. pub use error::{Error, Result};
  69. pub use resources::{Resource, ResourceId, ResourceTable};
  70. #[cfg(target_os = "ios")]
  71. #[doc(hidden)]
  72. pub use swift_rs;
  73. #[cfg(mobile)]
  74. pub use tauri_macros::mobile_entry_point;
  75. pub use tauri_macros::{command, generate_handler};
  76. pub(crate) mod app;
  77. pub mod async_runtime;
  78. mod error;
  79. mod event;
  80. pub mod ipc;
  81. mod manager;
  82. mod pattern;
  83. pub mod plugin;
  84. pub(crate) mod protocol;
  85. mod resources;
  86. mod vibrancy;
  87. pub mod webview;
  88. pub mod window;
  89. use tauri_runtime as runtime;
  90. #[cfg(target_os = "ios")]
  91. mod ios;
  92. #[cfg(desktop)]
  93. pub mod menu;
  94. /// Path APIs.
  95. pub mod path;
  96. pub mod process;
  97. /// The allowlist scopes.
  98. pub mod scope;
  99. mod state;
  100. #[cfg(all(desktop, feature = "tray-icon"))]
  101. #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
  102. pub mod tray;
  103. pub use tauri_utils as utils;
  104. pub use http;
  105. /// A Tauri [`Runtime`] wrapper around wry.
  106. #[cfg(feature = "wry")]
  107. #[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
  108. pub type Wry = tauri_runtime_wry::Wry<EventLoopMessage>;
  109. /// A Tauri [`RuntimeHandle`] wrapper around wry.
  110. #[cfg(feature = "wry")]
  111. #[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
  112. pub type WryHandle = tauri_runtime_wry::WryHandle<EventLoopMessage>;
  113. #[cfg(all(feature = "wry", target_os = "android"))]
  114. #[cfg_attr(docsrs, doc(cfg(all(feature = "wry", target_os = "android"))))]
  115. #[doc(hidden)]
  116. #[macro_export]
  117. macro_rules! android_binding {
  118. ($domain:ident, $package:ident, $main: ident, $wry: path) => {
  119. use $wry::{
  120. android_setup,
  121. prelude::{JClass, JNIEnv, JString},
  122. };
  123. ::tauri::wry::android_binding!($domain, $package, $wry);
  124. ::tauri::tao::android_binding!(
  125. $domain,
  126. $package,
  127. WryActivity,
  128. android_setup,
  129. $main,
  130. ::tauri::tao
  131. );
  132. ::tauri::tao::platform::android::prelude::android_fn!(
  133. app_tauri,
  134. plugin,
  135. PluginManager,
  136. handlePluginResponse,
  137. [i32, JString, JString],
  138. );
  139. ::tauri::tao::platform::android::prelude::android_fn!(
  140. app_tauri,
  141. plugin,
  142. PluginManager,
  143. sendChannelData,
  144. [i64, JString],
  145. );
  146. // this function is a glue between PluginManager.kt > handlePluginResponse and Rust
  147. #[allow(non_snake_case)]
  148. pub fn handlePluginResponse(
  149. mut env: JNIEnv,
  150. _: JClass,
  151. id: i32,
  152. success: JString,
  153. error: JString,
  154. ) {
  155. ::tauri::handle_android_plugin_response(&mut env, id, success, error);
  156. }
  157. // this function is a glue between PluginManager.kt > sendChannelData and Rust
  158. #[allow(non_snake_case)]
  159. pub fn sendChannelData(mut env: JNIEnv, _: JClass, id: i64, data: JString) {
  160. ::tauri::send_channel_data(&mut env, id, data);
  161. }
  162. };
  163. }
  164. #[cfg(all(feature = "wry", target_os = "android"))]
  165. #[doc(hidden)]
  166. pub use plugin::mobile::{handle_android_plugin_response, send_channel_data};
  167. #[cfg(all(feature = "wry", target_os = "android"))]
  168. #[doc(hidden)]
  169. pub use tauri_runtime_wry::{tao, wry};
  170. /// A task to run on the main thread.
  171. pub type SyncTask = Box<dyn FnOnce() + Send>;
  172. use serde::{Deserialize, Serialize};
  173. use std::{
  174. collections::HashMap,
  175. fmt::{self, Debug},
  176. sync::MutexGuard,
  177. };
  178. use utils::acl::resolved::Resolved;
  179. #[cfg(feature = "wry")]
  180. #[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
  181. pub use tauri_runtime_wry::webview_version;
  182. #[cfg(target_os = "macos")]
  183. #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
  184. pub use runtime::ActivationPolicy;
  185. #[cfg(target_os = "macos")]
  186. pub use self::utils::TitleBarStyle;
  187. pub use self::event::{Event, EventId, EventTarget};
  188. pub use {
  189. self::app::{
  190. App, AppHandle, AssetResolver, Builder, CloseRequestApi, RunEvent, WebviewEvent, WindowEvent,
  191. },
  192. self::manager::Asset,
  193. self::runtime::{
  194. webview::WebviewAttributes,
  195. window::{
  196. dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
  197. CursorIcon, FileDropEvent,
  198. },
  199. DeviceEventFilter, UserAttentionType,
  200. },
  201. self::state::{State, StateManager},
  202. self::utils::{
  203. assets::Assets,
  204. config::{Config, WebviewUrl},
  205. Env, PackageInfo, Theme,
  206. },
  207. self::webview::{Webview, WebviewWindow, WebviewWindowBuilder},
  208. self::window::{Monitor, Window},
  209. scope::*,
  210. };
  211. #[cfg(feature = "unstable")]
  212. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
  213. pub use {self::webview::WebviewBuilder, self::window::WindowBuilder};
  214. /// The Tauri version.
  215. pub const VERSION: &str = env!("CARGO_PKG_VERSION");
  216. #[cfg(target_os = "ios")]
  217. #[doc(hidden)]
  218. pub fn log_stdout() {
  219. use std::{
  220. ffi::CString,
  221. fs::File,
  222. io::{BufRead, BufReader},
  223. os::unix::prelude::*,
  224. thread,
  225. };
  226. let mut logpipe: [RawFd; 2] = Default::default();
  227. unsafe {
  228. libc::pipe(logpipe.as_mut_ptr());
  229. libc::dup2(logpipe[1], libc::STDOUT_FILENO);
  230. libc::dup2(logpipe[1], libc::STDERR_FILENO);
  231. }
  232. thread::spawn(move || unsafe {
  233. let file = File::from_raw_fd(logpipe[0]);
  234. let mut reader = BufReader::new(file);
  235. let mut buffer = String::new();
  236. loop {
  237. buffer.clear();
  238. if let Ok(len) = reader.read_line(&mut buffer) {
  239. if len == 0 {
  240. break;
  241. } else if let Ok(msg) = CString::new(buffer.as_bytes())
  242. .map_err(|_| ())
  243. .and_then(|c| c.into_string().map_err(|_| ()))
  244. {
  245. log::info!("{}", msg);
  246. }
  247. }
  248. }
  249. });
  250. }
  251. /// The user event type.
  252. #[derive(Debug, Clone)]
  253. pub enum EventLoopMessage {
  254. /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
  255. #[cfg(desktop)]
  256. MenuEvent(menu::MenuEvent),
  257. /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
  258. #[cfg(all(desktop, feature = "tray-icon"))]
  259. #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
  260. TrayIconEvent(tray::TrayIconEvent),
  261. }
  262. /// The webview runtime interface. A wrapper around [`runtime::Runtime`] with the proper user event type associated.
  263. pub trait Runtime: runtime::Runtime<EventLoopMessage> {}
  264. /// The webview runtime handle. A wrapper arond [`runtime::RuntimeHandle`] with the proper user event type associated.
  265. pub trait RuntimeHandle: runtime::RuntimeHandle<EventLoopMessage> {}
  266. impl<W: runtime::Runtime<EventLoopMessage>> Runtime for W {}
  267. impl<R: runtime::RuntimeHandle<EventLoopMessage>> RuntimeHandle for R {}
  268. /// Reads the config file at compile time and generates a [`Context`] based on its content.
  269. ///
  270. /// The default config file path is a `tauri.conf.json` file inside the Cargo manifest directory of
  271. /// the crate being built.
  272. ///
  273. /// # Custom Config Path
  274. ///
  275. /// You may pass a string literal to this macro to specify a custom path for the Tauri config file.
  276. /// If the path is relative, it will be search for relative to the Cargo manifest of the compiling
  277. /// crate.
  278. ///
  279. /// # Note
  280. ///
  281. /// This macro should not be called if you are using [`tauri-build`] to generate the context from
  282. /// inside your build script as it will just cause excess computations that will be discarded. Use
  283. /// either the [`tauri-build`] method or this macro - not both.
  284. ///
  285. /// [`tauri-build`]: https://docs.rs/tauri-build
  286. pub use tauri_macros::generate_context;
  287. /// Include a [`Context`] that was generated by [`tauri-build`] inside your build script.
  288. ///
  289. /// You should either use [`tauri-build`] and this macro to include the compile time generated code,
  290. /// or [`generate_context!`]. Do not use both at the same time, as they generate the same code and
  291. /// will cause excess computations that will be discarded.
  292. ///
  293. /// [`tauri-build`]: https://docs.rs/tauri-build
  294. #[macro_export]
  295. macro_rules! tauri_build_context {
  296. () => {
  297. include!(concat!(env!("OUT_DIR"), "/tauri-build-context.rs"))
  298. };
  299. }
  300. pub use pattern::Pattern;
  301. /// A icon definition.
  302. #[derive(Debug, Clone)]
  303. #[non_exhaustive]
  304. pub enum Icon {
  305. /// Icon from file path.
  306. #[cfg(any(feature = "icon-ico", feature = "icon-png"))]
  307. #[cfg_attr(docsrs, doc(cfg(any(feature = "icon-ico", feature = "icon-png"))))]
  308. File(std::path::PathBuf),
  309. /// Icon from raw RGBA bytes. Width and height is parsed at runtime.
  310. #[cfg(any(feature = "icon-ico", feature = "icon-png"))]
  311. #[cfg_attr(docsrs, doc(cfg(any(feature = "icon-ico", feature = "icon-png"))))]
  312. Raw(Vec<u8>),
  313. /// Icon from raw RGBA bytes.
  314. Rgba {
  315. /// RGBA bytes of the icon image.
  316. rgba: Vec<u8>,
  317. /// Icon width.
  318. width: u32,
  319. /// Icon height.
  320. height: u32,
  321. },
  322. }
  323. impl TryFrom<Icon> for runtime::Icon {
  324. type Error = Error;
  325. fn try_from(icon: Icon) -> Result<Self> {
  326. #[allow(irrefutable_let_patterns)]
  327. if let Icon::Rgba {
  328. rgba,
  329. width,
  330. height,
  331. } = icon
  332. {
  333. Ok(Self {
  334. rgba,
  335. width,
  336. height,
  337. })
  338. } else {
  339. #[cfg(not(any(feature = "icon-ico", feature = "icon-png")))]
  340. panic!("unexpected Icon variant");
  341. #[cfg(any(feature = "icon-ico", feature = "icon-png"))]
  342. {
  343. let bytes = match icon {
  344. Icon::File(p) => std::fs::read(p)?,
  345. Icon::Raw(r) => r,
  346. Icon::Rgba { .. } => unreachable!(),
  347. };
  348. let extension = infer::get(&bytes)
  349. .expect("could not determine icon extension")
  350. .extension();
  351. match extension {
  352. #[cfg(feature = "icon-ico")]
  353. "ico" => {
  354. let icon_dir = ico::IconDir::read(std::io::Cursor::new(bytes))?;
  355. let entry = &icon_dir.entries()[0];
  356. Ok(Self {
  357. rgba: entry.decode()?.rgba_data().to_vec(),
  358. width: entry.width(),
  359. height: entry.height(),
  360. })
  361. }
  362. #[cfg(feature = "icon-png")]
  363. "png" => {
  364. let decoder = png::Decoder::new(std::io::Cursor::new(bytes));
  365. let mut reader = decoder.read_info()?;
  366. let mut buffer = Vec::new();
  367. while let Ok(Some(row)) = reader.next_row() {
  368. buffer.extend(row.data());
  369. }
  370. Ok(Self {
  371. rgba: buffer,
  372. width: reader.info().width,
  373. height: reader.info().height,
  374. })
  375. }
  376. _ => panic!(
  377. "image `{extension}` extension not supported; please file a Tauri feature request. `png` or `ico` icons are supported with the `icon-png` and `icon-ico` feature flags"
  378. ),
  379. }
  380. }
  381. }
  382. }
  383. }
  384. /// User supplied data required inside of a Tauri application.
  385. ///
  386. /// # Stability
  387. /// This is the output of the [`generate_context`] macro, and is not considered part of the stable API.
  388. /// Unless you know what you are doing and are prepared for this type to have breaking changes, do not create it yourself.
  389. pub struct Context<A: Assets> {
  390. pub(crate) config: Config,
  391. pub(crate) assets: Box<A>,
  392. pub(crate) default_window_icon: Option<Icon>,
  393. pub(crate) app_icon: Option<Vec<u8>>,
  394. #[cfg(all(desktop, feature = "tray-icon"))]
  395. pub(crate) tray_icon: Option<Icon>,
  396. pub(crate) package_info: PackageInfo,
  397. pub(crate) _info_plist: (),
  398. pub(crate) pattern: Pattern,
  399. pub(crate) resolved_acl: Resolved,
  400. }
  401. impl<A: Assets> fmt::Debug for Context<A> {
  402. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  403. let mut d = f.debug_struct("Context");
  404. d.field("config", &self.config)
  405. .field("default_window_icon", &self.default_window_icon)
  406. .field("app_icon", &self.app_icon)
  407. .field("package_info", &self.package_info)
  408. .field("pattern", &self.pattern);
  409. #[cfg(all(desktop, feature = "tray-icon"))]
  410. d.field("tray_icon", &self.tray_icon);
  411. d.finish()
  412. }
  413. }
  414. impl<A: Assets> Context<A> {
  415. /// The config the application was prepared with.
  416. #[inline(always)]
  417. pub fn config(&self) -> &Config {
  418. &self.config
  419. }
  420. /// A mutable reference to the config the application was prepared with.
  421. #[inline(always)]
  422. pub fn config_mut(&mut self) -> &mut Config {
  423. &mut self.config
  424. }
  425. /// The assets to be served directly by Tauri.
  426. #[inline(always)]
  427. pub fn assets(&self) -> &A {
  428. &self.assets
  429. }
  430. /// A mutable reference to the assets to be served directly by Tauri.
  431. #[inline(always)]
  432. pub fn assets_mut(&mut self) -> &mut A {
  433. &mut self.assets
  434. }
  435. /// The default window icon Tauri should use when creating windows.
  436. #[inline(always)]
  437. pub fn default_window_icon(&self) -> Option<&Icon> {
  438. self.default_window_icon.as_ref()
  439. }
  440. /// A mutable reference to the default window icon Tauri should use when creating windows.
  441. #[inline(always)]
  442. pub fn default_window_icon_mut(&mut self) -> &mut Option<Icon> {
  443. &mut self.default_window_icon
  444. }
  445. /// The icon to use on the system tray UI.
  446. #[cfg(all(desktop, feature = "tray-icon"))]
  447. #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
  448. #[inline(always)]
  449. pub fn tray_icon(&self) -> Option<&Icon> {
  450. self.tray_icon.as_ref()
  451. }
  452. /// A mutable reference to the icon to use on the tray icon.
  453. #[cfg(all(desktop, feature = "tray-icon"))]
  454. #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
  455. #[inline(always)]
  456. pub fn tray_icon_mut(&mut self) -> &mut Option<Icon> {
  457. &mut self.tray_icon
  458. }
  459. /// Package information.
  460. #[inline(always)]
  461. pub fn package_info(&self) -> &PackageInfo {
  462. &self.package_info
  463. }
  464. /// A mutable reference to the package information.
  465. #[inline(always)]
  466. pub fn package_info_mut(&mut self) -> &mut PackageInfo {
  467. &mut self.package_info
  468. }
  469. /// The application pattern.
  470. #[inline(always)]
  471. pub fn pattern(&self) -> &Pattern {
  472. &self.pattern
  473. }
  474. /// A mutable reference to the resolved ACL.
  475. ///
  476. /// # Stability
  477. ///
  478. /// This API is unstable.
  479. #[doc(hidden)]
  480. #[inline(always)]
  481. pub fn resolved_acl(&mut self) -> &mut Resolved {
  482. &mut self.resolved_acl
  483. }
  484. /// Create a new [`Context`] from the minimal required items.
  485. #[inline(always)]
  486. #[allow(clippy::too_many_arguments)]
  487. pub fn new(
  488. config: Config,
  489. assets: Box<A>,
  490. default_window_icon: Option<Icon>,
  491. app_icon: Option<Vec<u8>>,
  492. package_info: PackageInfo,
  493. info_plist: (),
  494. pattern: Pattern,
  495. resolved_acl: Resolved,
  496. ) -> Self {
  497. Self {
  498. config,
  499. assets,
  500. default_window_icon,
  501. app_icon,
  502. #[cfg(all(desktop, feature = "tray-icon"))]
  503. tray_icon: None,
  504. package_info,
  505. _info_plist: info_plist,
  506. pattern,
  507. resolved_acl,
  508. }
  509. }
  510. /// Sets the app tray icon.
  511. #[cfg(all(desktop, feature = "tray-icon"))]
  512. #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
  513. #[inline(always)]
  514. pub fn set_tray_icon(&mut self, icon: Icon) {
  515. self.tray_icon.replace(icon);
  516. }
  517. /// Sets the app shell scope.
  518. #[cfg(shell_scope)]
  519. #[inline(always)]
  520. pub fn set_shell_scope(&mut self, scope: scope::ShellScopeConfig) {
  521. self.shell_scope = scope;
  522. }
  523. }
  524. // TODO: expand these docs
  525. /// Manages a running application.
  526. pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
  527. /// The application handle associated with this manager.
  528. fn app_handle(&self) -> &AppHandle<R> {
  529. self.managed_app_handle()
  530. }
  531. /// The [`Config`] the manager was created with.
  532. fn config(&self) -> &Config {
  533. self.manager().config()
  534. }
  535. /// The [`PackageInfo`] the manager was created with.
  536. fn package_info(&self) -> &PackageInfo {
  537. self.manager().package_info()
  538. }
  539. /// Listen to an emitted event to any [target](EventTarget).
  540. ///
  541. /// # Examples
  542. /// ```
  543. /// use tauri::Manager;
  544. ///
  545. /// #[tauri::command]
  546. /// fn synchronize(window: tauri::Window) {
  547. /// // emits the synchronized event to all windows
  548. /// window.emit("synchronized", ());
  549. /// }
  550. ///
  551. /// tauri::Builder::default()
  552. /// .setup(|app| {
  553. /// app.listen_any("synchronized", |event| {
  554. /// println!("app is in sync");
  555. /// });
  556. /// Ok(())
  557. /// })
  558. /// .invoke_handler(tauri::generate_handler![synchronize]);
  559. /// ```
  560. fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
  561. where
  562. F: Fn(Event) + Send + 'static,
  563. {
  564. self
  565. .manager()
  566. .listen(event.into(), EventTarget::Any, handler)
  567. }
  568. /// Remove an event listener.
  569. ///
  570. /// # Examples
  571. /// ```
  572. /// use tauri::Manager;
  573. ///
  574. /// tauri::Builder::default()
  575. /// .setup(|app| {
  576. /// let handle = app.handle().clone();
  577. /// let handler = app.listen_any("ready", move |event| {
  578. /// println!("app is ready");
  579. ///
  580. /// // we no longer need to listen to the event
  581. /// // we also could have used `app.once_global` instead
  582. /// handle.unlisten(event.id());
  583. /// });
  584. ///
  585. /// // stop listening to the event when you do not need it anymore
  586. /// app.unlisten(handler);
  587. ///
  588. ///
  589. /// Ok(())
  590. /// });
  591. /// ```
  592. fn unlisten(&self, id: EventId) {
  593. self.manager().unlisten(id)
  594. }
  595. /// Listens once to an emitted event to any [target](EventTarget) .
  596. ///
  597. /// See [`Self::listen_any`] for more information.
  598. fn once_any<F>(&self, event: impl Into<String>, handler: F)
  599. where
  600. F: FnOnce(Event) + Send + 'static,
  601. {
  602. self.manager().once(event.into(), EventTarget::Any, handler)
  603. }
  604. /// Emits an event to all [targets](EventTarget).
  605. ///
  606. /// # Examples
  607. /// ```
  608. /// use tauri::Manager;
  609. ///
  610. /// #[tauri::command]
  611. /// fn synchronize(app: tauri::AppHandle) {
  612. /// // emits the synchronized event to all webviews
  613. /// app.emit("synchronized", ());
  614. /// }
  615. /// ```
  616. #[cfg_attr(
  617. feature = "tracing",
  618. tracing::instrument("app::emit", skip(self, payload))
  619. )]
  620. fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
  621. self.manager().emit(event, payload)
  622. }
  623. /// Emits an event to all [targets](EventTarget) matching the given target.
  624. ///
  625. /// # Examples
  626. /// ```
  627. /// use tauri::{Manager, EventTarget};
  628. ///
  629. /// #[tauri::command]
  630. /// fn download(app: tauri::AppHandle) {
  631. /// for i in 1..100 {
  632. /// std::thread::sleep(std::time::Duration::from_millis(150));
  633. /// // emit a download progress event to all listeners
  634. /// app.emit_to(EventTarget::any(), "download-progress", i);
  635. /// // emit an event to listeners that used App::listen or AppHandle::listen
  636. /// app.emit_to(EventTarget::app(), "download-progress", i);
  637. /// // emit an event to any webview/window/webviewWindow matching the given label
  638. /// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
  639. /// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
  640. /// // emit an event to listeners that used WebviewWindow::listen
  641. /// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
  642. /// }
  643. /// }
  644. /// ```
  645. #[cfg_attr(
  646. feature = "tracing",
  647. tracing::instrument("app::emit::to", skip(self, target, payload), fields(target))
  648. )]
  649. fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
  650. where
  651. I: Into<EventTarget>,
  652. S: Serialize + Clone,
  653. {
  654. let target = target.into();
  655. #[cfg(feature = "tracing")]
  656. tracing::Span::current().record("target", format!("{target:?}"));
  657. match target {
  658. // if targeting all, emit to all using emit without filter
  659. EventTarget::Any => self.manager().emit(event, payload),
  660. // if targeting any label, emit using emit_filter and filter labels
  661. EventTarget::AnyLabel {
  662. label: target_label,
  663. } => self.manager().emit_filter(event, payload, |t| match t {
  664. EventTarget::Window { label }
  665. | EventTarget::Webview { label }
  666. | EventTarget::WebviewWindow { label } => label == &target_label,
  667. _ => false,
  668. }),
  669. // otherwise match same target
  670. _ => self.manager().emit_filter(event, payload, |t| t == &target),
  671. }
  672. }
  673. /// Emits an event to all [targets](EventTarget) based on the given filter.
  674. ///
  675. /// # Examples
  676. /// ```
  677. /// use tauri::{Manager, EventTarget};
  678. ///
  679. /// #[tauri::command]
  680. /// fn download(app: tauri::AppHandle) {
  681. /// for i in 1..100 {
  682. /// std::thread::sleep(std::time::Duration::from_millis(150));
  683. /// // emit a download progress event to the updater window
  684. /// app.emit_filter("download-progress", i, |t| match t {
  685. /// EventTarget::WebviewWindow { label } => label == "main",
  686. /// _ => false,
  687. /// });
  688. /// }
  689. /// }
  690. /// ```
  691. #[cfg_attr(
  692. feature = "tracing",
  693. tracing::instrument("app::emit::filter", skip(self, payload, filter))
  694. )]
  695. fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
  696. where
  697. S: Serialize + Clone,
  698. F: Fn(&EventTarget) -> bool,
  699. {
  700. self.manager().emit_filter(event, payload, filter)
  701. }
  702. /// Fetch a single window from the manager.
  703. #[cfg(feature = "unstable")]
  704. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
  705. fn get_window(&self, label: &str) -> Option<Window<R>> {
  706. self.manager().get_window(label)
  707. }
  708. /// Fetch the focused window. Returns `None` if there is not any focused window.
  709. #[cfg(feature = "unstable")]
  710. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
  711. fn get_focused_window(&self) -> Option<Window<R>> {
  712. self.manager().get_focused_window()
  713. }
  714. /// Fetch all managed windows.
  715. #[cfg(feature = "unstable")]
  716. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
  717. fn windows(&self) -> HashMap<String, Window<R>> {
  718. self.manager().windows()
  719. }
  720. /// Fetch a single webview from the manager.
  721. #[cfg(feature = "unstable")]
  722. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
  723. fn get_webview(&self, label: &str) -> Option<Webview<R>> {
  724. self.manager().get_webview(label)
  725. }
  726. /// Fetch all managed webviews.
  727. #[cfg(feature = "unstable")]
  728. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
  729. fn webviews(&self) -> HashMap<String, Webview<R>> {
  730. self.manager().webviews()
  731. }
  732. /// Fetch a single webview window from the manager.
  733. fn get_webview_window(&self, label: &str) -> Option<WebviewWindow<R>> {
  734. self.manager().get_webview(label).and_then(|webview| {
  735. if webview.window().is_webview_window {
  736. Some(WebviewWindow { webview })
  737. } else {
  738. None
  739. }
  740. })
  741. }
  742. /// Fetch all managed webview windows.
  743. fn webview_windows(&self) -> HashMap<String, WebviewWindow<R>> {
  744. self
  745. .manager()
  746. .webviews()
  747. .into_iter()
  748. .filter_map(|(label, webview)| {
  749. if webview.window().is_webview_window {
  750. Some((label, WebviewWindow { webview }))
  751. } else {
  752. None
  753. }
  754. })
  755. .collect::<HashMap<_, _>>()
  756. }
  757. /// Add `state` to the state managed by the application.
  758. ///
  759. /// If the state for the `T` type has previously been set, the state is unchanged and false is returned. Otherwise true is returned.
  760. ///
  761. /// Managed state can be retrieved by any command handler via the
  762. /// [`State`] guard. In particular, if a value of type `T`
  763. /// is managed by Tauri, adding `State<T>` to the list of arguments in a
  764. /// command handler instructs Tauri to retrieve the managed value.
  765. /// Additionally, [`state`](Self#method.state) can be used to retrieve the value manually.
  766. ///
  767. /// # Mutability
  768. ///
  769. /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
  770. ///
  771. /// ```rust,no_run
  772. /// use std::{collections::HashMap, sync::Mutex};
  773. /// use tauri::State;
  774. /// // here we use Mutex to achieve interior mutability
  775. /// struct Storage {
  776. /// store: Mutex<HashMap<u64, String>>,
  777. /// }
  778. /// struct Connection;
  779. /// struct DbConnection {
  780. /// db: Mutex<Option<Connection>>,
  781. /// }
  782. ///
  783. /// #[tauri::command]
  784. /// fn connect(connection: State<DbConnection>) {
  785. /// // initialize the connection, mutating the state with interior mutability
  786. /// *connection.db.lock().unwrap() = Some(Connection {});
  787. /// }
  788. ///
  789. /// #[tauri::command]
  790. /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
  791. /// // mutate the storage behind the Mutex
  792. /// storage.store.lock().unwrap().insert(key, value);
  793. /// }
  794. ///
  795. /// tauri::Builder::default()
  796. /// .manage(Storage { store: Default::default() })
  797. /// .manage(DbConnection { db: Default::default() })
  798. /// .invoke_handler(tauri::generate_handler![connect, storage_insert])
  799. /// // on an actual app, remove the string argument
  800. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  801. /// .expect("error while running tauri application");
  802. /// ```
  803. ///
  804. /// # Examples
  805. ///
  806. /// ```rust,no_run
  807. /// use tauri::{Manager, State};
  808. ///
  809. /// struct MyInt(isize);
  810. /// struct MyString(String);
  811. ///
  812. /// #[tauri::command]
  813. /// fn int_command(state: State<MyInt>) -> String {
  814. /// format!("The stateful int is: {}", state.0)
  815. /// }
  816. ///
  817. /// #[tauri::command]
  818. /// fn string_command<'r>(state: State<'r, MyString>) {
  819. /// println!("state: {}", state.inner().0);
  820. /// }
  821. ///
  822. /// tauri::Builder::default()
  823. /// .setup(|app| {
  824. /// app.manage(MyInt(0));
  825. /// app.manage(MyString("tauri".into()));
  826. /// // `MyInt` is already managed, so `manage()` returns false
  827. /// assert!(!app.manage(MyInt(1)));
  828. /// // read the `MyInt` managed state with the turbofish syntax
  829. /// let int = app.state::<MyInt>();
  830. /// assert_eq!(int.0, 0);
  831. /// // read the `MyString` managed state with the `State` guard
  832. /// let val: State<MyString> = app.state();
  833. /// assert_eq!(val.0, "tauri");
  834. /// Ok(())
  835. /// })
  836. /// .invoke_handler(tauri::generate_handler![int_command, string_command])
  837. /// // on an actual app, remove the string argument
  838. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  839. /// .expect("error while running tauri application");
  840. /// ```
  841. fn manage<T>(&self, state: T) -> bool
  842. where
  843. T: Send + Sync + 'static,
  844. {
  845. self.manager().state().set(state)
  846. }
  847. /// Retrieves the managed state for the type `T`.
  848. ///
  849. /// # Panics
  850. ///
  851. /// Panics if the state for the type `T` has not been previously [managed](Self::manage).
  852. /// Use [try_state](Self::try_state) for a non-panicking version.
  853. fn state<T>(&self) -> State<'_, T>
  854. where
  855. T: Send + Sync + 'static,
  856. {
  857. self
  858. .manager()
  859. .state
  860. .try_get()
  861. .expect("state() called before manage() for given type")
  862. }
  863. /// Attempts to retrieve the managed state for the type `T`.
  864. ///
  865. /// Returns `Some` if the state has previously been [managed](Self::manage). Otherwise returns `None`.
  866. fn try_state<T>(&self) -> Option<State<'_, T>>
  867. where
  868. T: Send + Sync + 'static,
  869. {
  870. self.manager().state.try_get()
  871. }
  872. /// Get a reference to the resources table.
  873. fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
  874. self.manager().resources_table()
  875. }
  876. /// Gets the managed [`Env`].
  877. fn env(&self) -> Env {
  878. self.state::<Env>().inner().clone()
  879. }
  880. /// Gets the scope for the asset protocol.
  881. #[cfg(feature = "protocol-asset")]
  882. fn asset_protocol_scope(&self) -> scope::fs::Scope {
  883. self.state::<Scopes>().inner().asset_protocol.clone()
  884. }
  885. /// The path resolver.
  886. fn path(&self) -> &crate::path::PathResolver<R> {
  887. self.state::<crate::path::PathResolver<R>>().inner()
  888. }
  889. }
  890. /// Prevent implementation details from leaking out of the [`Manager`] trait.
  891. pub(crate) mod sealed {
  892. use super::Runtime;
  893. use crate::{app::AppHandle, manager::AppManager};
  894. use std::sync::Arc;
  895. /// A running [`Runtime`] or a dispatcher to it.
  896. pub enum RuntimeOrDispatch<'r, R: Runtime> {
  897. /// Reference to the running [`Runtime`].
  898. Runtime(&'r R),
  899. /// Handle to the running [`Runtime`].
  900. RuntimeHandle(R::Handle),
  901. /// A dispatcher to the running [`Runtime`].
  902. Dispatch(R::WindowDispatcher),
  903. }
  904. /// Managed handle to the application runtime.
  905. pub trait ManagerBase<R: Runtime> {
  906. fn manager(&self) -> &AppManager<R>;
  907. fn manager_owned(&self) -> Arc<AppManager<R>>;
  908. fn runtime(&self) -> RuntimeOrDispatch<'_, R>;
  909. fn managed_app_handle(&self) -> &AppHandle<R>;
  910. }
  911. }
  912. #[derive(Deserialize)]
  913. #[serde(untagged)]
  914. pub(crate) enum IconDto {
  915. #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
  916. File(std::path::PathBuf),
  917. #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
  918. Raw(Vec<u8>),
  919. Rgba {
  920. rgba: Vec<u8>,
  921. width: u32,
  922. height: u32,
  923. },
  924. }
  925. impl From<IconDto> for Icon {
  926. fn from(icon: IconDto) -> Self {
  927. match icon {
  928. #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
  929. IconDto::File(path) => Self::File(path),
  930. #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
  931. IconDto::Raw(raw) => Self::Raw(raw),
  932. IconDto::Rgba {
  933. rgba,
  934. width,
  935. height,
  936. } => Self::Rgba {
  937. rgba,
  938. width,
  939. height,
  940. },
  941. }
  942. }
  943. }
  944. #[allow(unused)]
  945. macro_rules! run_main_thread {
  946. ($handle:ident, $ex:expr) => {{
  947. use std::sync::mpsc::channel;
  948. let (tx, rx) = channel();
  949. let task = move || {
  950. let f = $ex;
  951. let _ = tx.send(f());
  952. };
  953. $handle
  954. .run_on_main_thread(task)
  955. .and_then(|_| rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage))
  956. }};
  957. }
  958. #[allow(unused)]
  959. pub(crate) use run_main_thread;
  960. #[cfg(any(test, feature = "test"))]
  961. #[cfg_attr(docsrs, doc(cfg(feature = "test")))]
  962. pub mod test;
  963. #[cfg(test)]
  964. mod tests {
  965. use cargo_toml::Manifest;
  966. use std::{env::var, fs::read_to_string, path::PathBuf, sync::OnceLock};
  967. static MANIFEST: OnceLock<Manifest> = OnceLock::new();
  968. const CHECKED_FEATURES: &str = include_str!(concat!(env!("OUT_DIR"), "/checked_features"));
  969. fn get_manifest() -> &'static Manifest {
  970. MANIFEST.get_or_init(|| {
  971. let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
  972. Manifest::from_path(manifest_dir.join("Cargo.toml")).expect("failed to parse Cargo manifest")
  973. })
  974. }
  975. #[test]
  976. fn features_are_documented() {
  977. let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
  978. let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs");
  979. for f in get_manifest().features.keys() {
  980. if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{f}**"))) {
  981. panic!("Feature {f} is not documented");
  982. }
  983. }
  984. }
  985. #[test]
  986. fn aliased_features_exist() {
  987. let checked_features = CHECKED_FEATURES.split(',');
  988. let manifest = get_manifest();
  989. for checked_feature in checked_features {
  990. if !manifest.features.iter().any(|(f, _)| f == checked_feature) {
  991. panic!(
  992. "Feature {checked_feature} was checked in the alias build step but it does not exist in core/tauri/Cargo.toml"
  993. );
  994. }
  995. }
  996. }
  997. }
  998. #[cfg(test)]
  999. mod test_utils {
  1000. use proptest::prelude::*;
  1001. pub fn assert_send<T: Send>() {}
  1002. pub fn assert_sync<T: Sync>() {}
  1003. #[allow(dead_code)]
  1004. pub fn assert_not_allowlist_error<T>(res: anyhow::Result<T>) {
  1005. if let Err(e) = res {
  1006. assert!(!e.to_string().contains("not on the allowlist"));
  1007. }
  1008. }
  1009. proptest! {
  1010. #![proptest_config(ProptestConfig::with_cases(10000))]
  1011. #[test]
  1012. // check to see if spawn executes a function.
  1013. fn check_spawn_task(task in "[a-z]+") {
  1014. // create dummy task function
  1015. let dummy_task = async move {
  1016. format!("{task}-run-dummy-task");
  1017. };
  1018. // call spawn
  1019. crate::async_runtime::spawn(dummy_task);
  1020. }
  1021. }
  1022. }