app.rs 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. #[cfg(feature = "system-tray")]
  5. pub(crate) mod tray;
  6. use crate::{
  7. api::ipc::CallbackFn,
  8. command::{CommandArg, CommandItem},
  9. hooks::{
  10. window_invoke_responder, InvokeHandler, InvokeResponder, OnPageLoad, PageLoadPayload, SetupHook,
  11. },
  12. manager::{Asset, CustomProtocol, WindowManager},
  13. plugin::{Plugin, PluginStore},
  14. runtime::{
  15. http::{Request as HttpRequest, Response as HttpResponse},
  16. webview::{WebviewAttributes, WindowBuilder as _},
  17. window::{PendingWindow, WindowEvent},
  18. Dispatch, ExitRequestedEventAction, RunEvent as RuntimeRunEvent, Runtime,
  19. },
  20. scope::FsScope,
  21. sealed::{ManagerBase, RuntimeOrDispatch},
  22. utils::config::{Config, WindowUrl},
  23. utils::{assets::Assets, Env},
  24. Context, Invoke, InvokeError, InvokeResponse, Manager, Scopes, StateManager, Window,
  25. };
  26. #[cfg(shell_scope)]
  27. use crate::scope::ShellScope;
  28. use tauri_macros::default_runtime;
  29. use tauri_utils::PackageInfo;
  30. use std::{
  31. collections::HashMap,
  32. path::PathBuf,
  33. sync::{mpsc::Sender, Arc, Weak},
  34. };
  35. use crate::runtime::menu::{Menu, MenuId, MenuIdRef};
  36. use crate::runtime::RuntimeHandle;
  37. #[cfg(feature = "system-tray")]
  38. use crate::runtime::{Icon, SystemTrayEvent as RuntimeSystemTrayEvent};
  39. #[cfg(feature = "updater")]
  40. use crate::updater;
  41. #[cfg(target_os = "macos")]
  42. use crate::ActivationPolicy;
  43. pub(crate) type GlobalMenuEventListener<R> = Box<dyn Fn(WindowMenuEvent<R>) + Send + Sync>;
  44. pub(crate) type GlobalWindowEventListener<R> = Box<dyn Fn(GlobalWindowEvent<R>) + Send + Sync>;
  45. #[cfg(feature = "system-tray")]
  46. type SystemTrayEventListener<R> = Box<dyn Fn(&AppHandle<R>, tray::SystemTrayEvent) + Send + Sync>;
  47. /// Api exposed on the `ExitRequested` event.
  48. #[derive(Debug)]
  49. pub struct ExitRequestApi(Sender<ExitRequestedEventAction>);
  50. impl ExitRequestApi {
  51. /// Prevents the app from exiting
  52. pub fn prevent_exit(&self) {
  53. self.0.send(ExitRequestedEventAction::Prevent).unwrap();
  54. }
  55. }
  56. /// Api exposed on the `CloseRequested` event.
  57. #[derive(Debug)]
  58. pub struct CloseRequestApi(Sender<bool>);
  59. impl CloseRequestApi {
  60. /// Prevents the window from being closed.
  61. pub fn prevent_close(&self) {
  62. self.0.send(true).unwrap();
  63. }
  64. }
  65. /// An application event, triggered from the event loop.
  66. #[derive(Debug)]
  67. #[non_exhaustive]
  68. pub enum RunEvent {
  69. /// Event loop is exiting.
  70. Exit,
  71. /// The app is about to exit
  72. #[non_exhaustive]
  73. ExitRequested {
  74. /// The label of the window that requested the exit.
  75. /// It is the last window managed by tauri.
  76. window_label: String,
  77. /// Event API
  78. api: ExitRequestApi,
  79. },
  80. /// Window close was requested by the user.
  81. #[non_exhaustive]
  82. CloseRequested {
  83. /// The window label.
  84. label: String,
  85. /// Event API.
  86. api: CloseRequestApi,
  87. },
  88. /// Window closed.
  89. WindowClosed(String),
  90. /// Application ready.
  91. Ready,
  92. /// Sent if the event loop is being resumed.
  93. Resumed,
  94. /// Emitted when all of the event loop’s input events have been processed and redraw processing is about to begin.
  95. ///
  96. /// 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.
  97. MainEventsCleared,
  98. }
  99. /// A menu event that was triggered on a window.
  100. #[default_runtime(crate::Wry, wry)]
  101. #[derive(Debug)]
  102. pub struct WindowMenuEvent<R: Runtime> {
  103. pub(crate) menu_item_id: MenuId,
  104. pub(crate) window: Window<R>,
  105. }
  106. impl<R: Runtime> WindowMenuEvent<R> {
  107. /// The menu item id.
  108. pub fn menu_item_id(&self) -> MenuIdRef<'_> {
  109. &self.menu_item_id
  110. }
  111. /// The window that the menu belongs to.
  112. pub fn window(&self) -> &Window<R> {
  113. &self.window
  114. }
  115. }
  116. /// A window event that was triggered on the specified window.
  117. #[default_runtime(crate::Wry, wry)]
  118. #[derive(Debug)]
  119. pub struct GlobalWindowEvent<R: Runtime> {
  120. pub(crate) event: WindowEvent,
  121. pub(crate) window: Window<R>,
  122. }
  123. impl<R: Runtime> GlobalWindowEvent<R> {
  124. /// The event payload.
  125. pub fn event(&self) -> &WindowEvent {
  126. &self.event
  127. }
  128. /// The window that the menu belongs to.
  129. pub fn window(&self) -> &Window<R> {
  130. &self.window
  131. }
  132. }
  133. /// The path resolver is a helper for the application-specific [`crate::api::path`] APIs.
  134. #[derive(Debug, Clone)]
  135. pub struct PathResolver {
  136. env: Env,
  137. config: Arc<Config>,
  138. package_info: PackageInfo,
  139. }
  140. impl PathResolver {
  141. /// Returns the path to the resource directory of this app.
  142. pub fn resource_dir(&self) -> Option<PathBuf> {
  143. crate::api::path::resource_dir(&self.package_info, &self.env)
  144. }
  145. /// Returns the path to the suggested directory for your app config files.
  146. pub fn app_dir(&self) -> Option<PathBuf> {
  147. crate::api::path::app_dir(&self.config)
  148. }
  149. /// Returns the path to the suggested log directory.
  150. pub fn log_dir(&self) -> Option<PathBuf> {
  151. crate::api::path::log_dir(&self.config)
  152. }
  153. }
  154. /// The asset resolver is a helper to access the [`tauri_utils::assets::Assets`] interface.
  155. #[derive(Debug, Clone)]
  156. pub struct AssetResolver<R: Runtime> {
  157. manager: WindowManager<R>,
  158. }
  159. impl<R: Runtime> AssetResolver<R> {
  160. /// Gets the app asset associated with the given path.
  161. pub fn get(&self, path: String) -> Option<Asset> {
  162. self.manager.get_asset(path).ok()
  163. }
  164. }
  165. /// A handle to the currently running application.
  166. ///
  167. /// This type implements [`Manager`] which allows for manipulation of global application items.
  168. #[default_runtime(crate::Wry, wry)]
  169. #[derive(Debug)]
  170. pub struct AppHandle<R: Runtime> {
  171. runtime_handle: R::Handle,
  172. manager: WindowManager<R>,
  173. global_shortcut_manager: R::GlobalShortcutManager,
  174. clipboard_manager: R::ClipboardManager,
  175. #[cfg(feature = "system-tray")]
  176. tray_handle: Option<tray::SystemTrayHandle<R>>,
  177. }
  178. #[cfg(feature = "wry")]
  179. impl AppHandle<crate::Wry> {
  180. /// Create a new tao window using a callback. The event loop must be running at this point.
  181. pub fn create_tao_window<
  182. F: FnOnce() -> (String, tauri_runtime_wry::WryWindowBuilder) + Send + 'static,
  183. >(
  184. &self,
  185. f: F,
  186. ) -> crate::Result<Weak<tauri_runtime_wry::Window>> {
  187. self.runtime_handle.create_tao_window(f).map_err(Into::into)
  188. }
  189. /// Sends a window message to the event loop.
  190. pub fn send_tao_window_event(
  191. &self,
  192. window_id: tauri_runtime_wry::WindowId,
  193. message: tauri_runtime_wry::WindowMessage,
  194. ) -> crate::Result<()> {
  195. self
  196. .runtime_handle
  197. .send_event(tauri_runtime_wry::Message::Window(window_id, message))
  198. .map_err(Into::into)
  199. }
  200. }
  201. impl<R: Runtime> Clone for AppHandle<R> {
  202. fn clone(&self) -> Self {
  203. Self {
  204. runtime_handle: self.runtime_handle.clone(),
  205. manager: self.manager.clone(),
  206. global_shortcut_manager: self.global_shortcut_manager.clone(),
  207. clipboard_manager: self.clipboard_manager.clone(),
  208. #[cfg(feature = "system-tray")]
  209. tray_handle: self.tray_handle.clone(),
  210. }
  211. }
  212. }
  213. impl<'de, R: Runtime> CommandArg<'de, R> for AppHandle<R> {
  214. /// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`AppHandle`]. This will never fail.
  215. fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
  216. Ok(command.message.window().app_handle)
  217. }
  218. }
  219. impl<R: Runtime> AppHandle<R> {
  220. /// Runs the given closure on the main thread.
  221. pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
  222. self
  223. .runtime_handle
  224. .run_on_main_thread(f)
  225. .map_err(Into::into)
  226. }
  227. /// Removes the system tray.
  228. #[cfg(all(windows, feature = "system-tray"))]
  229. #[cfg_attr(doc_cfg, doc(cfg(all(windows, feature = "system-tray"))))]
  230. fn remove_system_tray(&self) -> crate::Result<()> {
  231. self.runtime_handle.remove_system_tray().map_err(Into::into)
  232. }
  233. /// Adds a plugin to the runtime.
  234. pub fn plugin<P: Plugin<R> + 'static>(&self, mut plugin: P) -> crate::Result<()> {
  235. plugin
  236. .initialize(
  237. self,
  238. self
  239. .config()
  240. .plugins
  241. .0
  242. .get(plugin.name())
  243. .cloned()
  244. .unwrap_or_default(),
  245. )
  246. .map_err(|e| crate::Error::PluginInitialization(plugin.name().to_string(), e.to_string()))?;
  247. self
  248. .manager()
  249. .inner
  250. .plugins
  251. .lock()
  252. .unwrap()
  253. .register(plugin);
  254. Ok(())
  255. }
  256. /// Exits the app
  257. pub fn exit(&self, exit_code: i32) {
  258. self.cleanup_before_exit();
  259. std::process::exit(exit_code);
  260. }
  261. /// Runs necessary cleanup tasks before exiting the process
  262. fn cleanup_before_exit(&self) {
  263. #[cfg(shell_execute)]
  264. {
  265. crate::api::process::kill_children();
  266. }
  267. #[cfg(all(windows, feature = "system-tray"))]
  268. {
  269. let _ = self.remove_system_tray();
  270. }
  271. }
  272. }
  273. impl<R: Runtime> Manager<R> for AppHandle<R> {}
  274. impl<R: Runtime> ManagerBase<R> for AppHandle<R> {
  275. fn manager(&self) -> &WindowManager<R> {
  276. &self.manager
  277. }
  278. fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
  279. RuntimeOrDispatch::RuntimeHandle(self.runtime_handle.clone())
  280. }
  281. fn app_handle(&self) -> AppHandle<R> {
  282. self.clone()
  283. }
  284. }
  285. /// The instance of the currently running application.
  286. ///
  287. /// This type implements [`Manager`] which allows for manipulation of global application items.
  288. #[default_runtime(crate::Wry, wry)]
  289. #[derive(Debug)]
  290. pub struct App<R: Runtime> {
  291. runtime: Option<R>,
  292. manager: WindowManager<R>,
  293. global_shortcut_manager: R::GlobalShortcutManager,
  294. clipboard_manager: R::ClipboardManager,
  295. #[cfg(feature = "system-tray")]
  296. tray_handle: Option<tray::SystemTrayHandle<R>>,
  297. handle: AppHandle<R>,
  298. }
  299. impl<R: Runtime> Manager<R> for App<R> {}
  300. impl<R: Runtime> ManagerBase<R> for App<R> {
  301. fn manager(&self) -> &WindowManager<R> {
  302. &self.manager
  303. }
  304. fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
  305. RuntimeOrDispatch::Runtime(self.runtime.as_ref().unwrap())
  306. }
  307. fn app_handle(&self) -> AppHandle<R> {
  308. self.handle()
  309. }
  310. }
  311. macro_rules! shared_app_impl {
  312. ($app: ty) => {
  313. impl<R: Runtime> $app {
  314. /// Creates a new webview window.
  315. ///
  316. /// Data URLs are only supported with the `window-data-url` feature flag.
  317. ///
  318. /// See [`Self::window_builder`] for an API with extended functionality.
  319. #[deprecated(
  320. since = "1.0.0-rc.4",
  321. note = "The `window_builder` function offers an easier API with extended functionality"
  322. )]
  323. pub fn create_window<F>(
  324. &self,
  325. label: impl Into<String>,
  326. url: WindowUrl,
  327. setup: F,
  328. ) -> crate::Result<Window<R>>
  329. where
  330. F: FnOnce(
  331. <R::Dispatcher as Dispatch>::WindowBuilder,
  332. WebviewAttributes,
  333. ) -> (
  334. <R::Dispatcher as Dispatch>::WindowBuilder,
  335. WebviewAttributes,
  336. ),
  337. {
  338. let (window_builder, webview_attributes) = setup(
  339. <R::Dispatcher as Dispatch>::WindowBuilder::new(),
  340. WebviewAttributes::new(url),
  341. );
  342. self.create_new_window(PendingWindow::new(
  343. window_builder,
  344. webview_attributes,
  345. label,
  346. )?)
  347. }
  348. #[cfg(feature = "system-tray")]
  349. #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
  350. /// Gets a handle handle to the system tray.
  351. pub fn tray_handle(&self) -> tray::SystemTrayHandle<R> {
  352. self
  353. .tray_handle
  354. .clone()
  355. .expect("tray not configured; use the `Builder#system_tray` API first.")
  356. }
  357. /// The path resolver for the application.
  358. pub fn path_resolver(&self) -> PathResolver {
  359. PathResolver {
  360. env: self.state::<Env>().inner().clone(),
  361. config: self.manager.config(),
  362. package_info: self.manager.package_info().clone(),
  363. }
  364. }
  365. /// Gets a copy of the global shortcut manager instance.
  366. pub fn global_shortcut_manager(&self) -> R::GlobalShortcutManager {
  367. self.global_shortcut_manager.clone()
  368. }
  369. /// Gets a copy of the clipboard manager instance.
  370. pub fn clipboard_manager(&self) -> R::ClipboardManager {
  371. self.clipboard_manager.clone()
  372. }
  373. /// Gets the app's configuration, defined on the `tauri.conf.json` file.
  374. pub fn config(&self) -> Arc<Config> {
  375. self.manager.config()
  376. }
  377. /// Gets the app's package information.
  378. pub fn package_info(&self) -> &PackageInfo {
  379. self.manager.package_info()
  380. }
  381. /// The application's asset resolver.
  382. pub fn asset_resolver(&self) -> AssetResolver<R> {
  383. AssetResolver {
  384. manager: self.manager.clone(),
  385. }
  386. }
  387. }
  388. };
  389. }
  390. shared_app_impl!(App<R>);
  391. shared_app_impl!(AppHandle<R>);
  392. impl<R: Runtime> App<R> {
  393. /// Gets a handle to the application instance.
  394. pub fn handle(&self) -> AppHandle<R> {
  395. self.handle.clone()
  396. }
  397. /// Sets the activation policy for the application. It is set to `NSApplicationActivationPolicyRegular` by default.
  398. ///
  399. /// # Examples
  400. /// ```rust,no_run
  401. /// let mut app = tauri::Builder::default()
  402. /// // on an actual app, remove the string argument
  403. /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  404. /// .expect("error while building tauri application");
  405. /// #[cfg(target_os = "macos")]
  406. /// app.set_activation_policy(tauri::ActivationPolicy::Accessory);
  407. /// app.run(|_app_handle, _event| {});
  408. /// ```
  409. #[cfg(target_os = "macos")]
  410. #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
  411. pub fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
  412. self
  413. .runtime
  414. .as_mut()
  415. .unwrap()
  416. .set_activation_policy(activation_policy);
  417. }
  418. /// Runs the application.
  419. ///
  420. /// # Examples
  421. /// ```rust,no_run
  422. /// let app = tauri::Builder::default()
  423. /// // on an actual app, remove the string argument
  424. /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  425. /// .expect("error while building tauri application");
  426. /// app.run(|_app_handle, event| match event {
  427. /// tauri::RunEvent::ExitRequested { api, .. } => {
  428. /// api.prevent_exit();
  429. /// }
  430. /// _ => {}
  431. /// });
  432. /// ```
  433. pub fn run<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(mut self, mut callback: F) {
  434. let app_handle = self.handle();
  435. let manager = self.manager.clone();
  436. self.runtime.take().unwrap().run(move |event| match event {
  437. RuntimeRunEvent::Exit => {
  438. app_handle.cleanup_before_exit();
  439. on_event_loop_event(
  440. &app_handle,
  441. RuntimeRunEvent::Exit,
  442. &manager,
  443. Some(&mut callback),
  444. );
  445. }
  446. _ => {
  447. on_event_loop_event(&app_handle, event, &manager, Some(&mut callback));
  448. }
  449. });
  450. }
  451. /// Runs a iteration of the runtime event loop and immediately return.
  452. ///
  453. /// Note that when using this API, app cleanup is not automatically done.
  454. /// The cleanup calls [`crate::api::process::kill_children`] so you may want to call that function before exiting the application.
  455. /// Additionally, the cleanup calls [AppHandle#remove_system_tray](`AppHandle#method.remove_system_tray`) (Windows only).
  456. ///
  457. /// # Examples
  458. /// ```rust,no_run
  459. /// let mut app = tauri::Builder::default()
  460. /// // on an actual app, remove the string argument
  461. /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  462. /// .expect("error while building tauri application");
  463. /// loop {
  464. /// let iteration = app.run_iteration();
  465. /// if iteration.window_count == 0 {
  466. /// break;
  467. /// }
  468. /// }
  469. /// ```
  470. pub fn run_iteration(&mut self) -> crate::runtime::RunIteration {
  471. let manager = self.manager.clone();
  472. let app_handle = self.handle();
  473. self.runtime.as_mut().unwrap().run_iteration(move |event| {
  474. on_event_loop_event(
  475. &app_handle,
  476. event,
  477. &manager,
  478. Option::<&mut Box<dyn FnMut(&AppHandle<R>, RunEvent)>>::None,
  479. )
  480. })
  481. }
  482. }
  483. #[cfg(feature = "updater")]
  484. impl<R: Runtime> App<R> {
  485. /// Runs the updater hook with built-in dialog.
  486. fn run_updater_dialog(&self, window: Window<R>) {
  487. let updater_config = self.manager.config().tauri.updater.clone();
  488. let package_info = self.manager.package_info().clone();
  489. crate::async_runtime::spawn(async move {
  490. updater::check_update_with_dialog(updater_config, package_info, window).await
  491. });
  492. }
  493. /// Listen updater events when dialog are disabled.
  494. fn listen_updater_events(&self, window: Window<R>) {
  495. let updater_config = self.manager.config().tauri.updater.clone();
  496. updater::listener(updater_config, self.manager.package_info().clone(), &window);
  497. }
  498. fn run_updater(&self, main_window: Option<Window<R>>) {
  499. if let Some(main_window) = main_window {
  500. let event_window = main_window.clone();
  501. let updater_config = self.manager.config().tauri.updater.clone();
  502. // check if updater is active or not
  503. if updater_config.dialog && updater_config.active {
  504. // if updater dialog is enabled spawn a new task
  505. self.run_updater_dialog(main_window.clone());
  506. let config = self.manager.config().tauri.updater.clone();
  507. let package_info = self.manager.package_info().clone();
  508. // When dialog is enabled, if user want to recheck
  509. // if an update is available after first start
  510. // invoke the Event `tauri://update` from JS or rust side.
  511. main_window.listen(updater::EVENT_CHECK_UPDATE, move |_msg| {
  512. let window = event_window.clone();
  513. let package_info = package_info.clone();
  514. let config = config.clone();
  515. // re-spawn task inside tokyo to launch the download
  516. // we don't need to emit anything as everything is handled
  517. // by the process (user is asked to restart at the end)
  518. // and it's handled by the updater
  519. crate::async_runtime::spawn(async move {
  520. updater::check_update_with_dialog(config, package_info, window).await
  521. });
  522. });
  523. } else if updater_config.active {
  524. // we only listen for `tauri://update`
  525. // once we receive the call, we check if an update is available or not
  526. // if there is a new update we emit `tauri://update-available` with details
  527. // this is the user responsabilities to display dialog and ask if user want to install
  528. // to install the update you need to invoke the Event `tauri://update-install`
  529. self.listen_updater_events(main_window);
  530. }
  531. }
  532. }
  533. }
  534. /// Builds a Tauri application.
  535. ///
  536. /// # Examples
  537. /// ```rust,no_run
  538. /// tauri::Builder::default()
  539. /// // on an actual app, remove the string argument
  540. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  541. /// .expect("error while running tauri application");
  542. /// ```
  543. #[allow(clippy::type_complexity)]
  544. pub struct Builder<R: Runtime> {
  545. /// A flag indicating that the runtime must be started on an environment that supports the event loop not on the main thread.
  546. #[cfg(any(windows, target_os = "linux"))]
  547. #[cfg_attr(doc_cfg, doc(any(windows, target_os = "linux")))]
  548. runtime_any_thread: bool,
  549. /// The JS message handler.
  550. invoke_handler: Box<InvokeHandler<R>>,
  551. /// The JS message responder.
  552. invoke_responder: Arc<InvokeResponder<R>>,
  553. /// The script that initializes the `window.__TAURI_POST_MESSAGE__` function.
  554. invoke_initialization_script: String,
  555. /// The setup hook.
  556. setup: SetupHook<R>,
  557. /// Page load hook.
  558. on_page_load: Box<OnPageLoad<R>>,
  559. /// windows to create when starting up.
  560. pending_windows: Vec<PendingWindow<R>>,
  561. /// All passed plugins
  562. plugins: PluginStore<R>,
  563. /// The webview protocols available to all windows.
  564. uri_scheme_protocols: HashMap<String, Arc<CustomProtocol<R>>>,
  565. /// App state.
  566. state: StateManager,
  567. /// The menu set to all windows.
  568. menu: Option<Menu>,
  569. /// Menu event handlers that listens to all windows.
  570. menu_event_listeners: Vec<GlobalMenuEventListener<R>>,
  571. /// Window event handlers that listens to all windows.
  572. window_event_listeners: Vec<GlobalWindowEventListener<R>>,
  573. /// The app system tray.
  574. #[cfg(feature = "system-tray")]
  575. system_tray: Option<tray::SystemTray>,
  576. /// System tray event handlers.
  577. #[cfg(feature = "system-tray")]
  578. system_tray_event_listeners: Vec<SystemTrayEventListener<R>>,
  579. }
  580. impl<R: Runtime> Builder<R> {
  581. /// Creates a new App builder.
  582. pub fn new() -> Self {
  583. Self {
  584. #[cfg(any(windows, target_os = "linux"))]
  585. runtime_any_thread: false,
  586. setup: Box::new(|_| Ok(())),
  587. invoke_handler: Box::new(|_| ()),
  588. invoke_responder: Arc::new(window_invoke_responder),
  589. invoke_initialization_script:
  590. "Object.defineProperty(window, '__TAURI_POST_MESSAGE__', { value: (message) => window.ipc.postMessage(JSON.stringify(message)) })".into(),
  591. on_page_load: Box::new(|_, _| ()),
  592. pending_windows: Default::default(),
  593. plugins: PluginStore::default(),
  594. uri_scheme_protocols: Default::default(),
  595. state: StateManager::new(),
  596. menu: None,
  597. menu_event_listeners: Vec::new(),
  598. window_event_listeners: Vec::new(),
  599. #[cfg(feature = "system-tray")]
  600. system_tray: None,
  601. #[cfg(feature = "system-tray")]
  602. system_tray_event_listeners: Vec::new(),
  603. }
  604. }
  605. /// Builds a new Tauri application running on any thread, bypassing the main thread requirement.
  606. ///
  607. /// ## Platform-specific
  608. ///
  609. /// - **macOS**: on macOS the application *must* be executed on the main thread, so this function is not exposed.
  610. #[cfg(any(windows, target_os = "linux"))]
  611. #[cfg_attr(doc_cfg, doc(any(windows, target_os = "linux")))]
  612. #[must_use]
  613. pub fn any_thread(mut self) -> Self {
  614. self.runtime_any_thread = true;
  615. self
  616. }
  617. /// Defines the JS message handler callback.
  618. ///
  619. /// # Examples
  620. /// ```rust,no_run
  621. /// #[tauri::command]
  622. /// fn command_1() -> String {
  623. /// return "hello world".to_string();
  624. /// }
  625. /// tauri::Builder::default()
  626. /// .invoke_handler(tauri::generate_handler![
  627. /// command_1,
  628. /// // etc...
  629. /// ]);
  630. /// ```
  631. #[must_use]
  632. pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
  633. where
  634. F: Fn(Invoke<R>) + Send + Sync + 'static,
  635. {
  636. self.invoke_handler = Box::new(invoke_handler);
  637. self
  638. }
  639. /// Defines a custom JS message system.
  640. ///
  641. /// The `responder` is a function that will be called when a command has been executed and must send a response to the JS layer.
  642. ///
  643. /// The `initialization_script` is a script that initializes `window.__TAURI_POST_MESSAGE__`.
  644. /// That function must take the `message: object` argument and send it to the backend.
  645. #[must_use]
  646. pub fn invoke_system<F>(mut self, initialization_script: String, responder: F) -> Self
  647. where
  648. F: Fn(Window<R>, InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static,
  649. {
  650. self.invoke_initialization_script = initialization_script;
  651. self.invoke_responder = Arc::new(responder);
  652. self
  653. }
  654. /// Defines the setup hook.
  655. ///
  656. /// # Examples
  657. /// ```rust,no_run
  658. /// use tauri::Manager;
  659. /// tauri::Builder::default()
  660. /// .setup(|app| {
  661. /// let main_window = app.get_window("main").unwrap();
  662. #[cfg_attr(
  663. feature = "dialog",
  664. doc = r#" tauri::api::dialog::blocking::message(Some(&main_window), "Hello", "Welcome back!");"#
  665. )]
  666. /// Ok(())
  667. /// });
  668. /// ```
  669. #[must_use]
  670. pub fn setup<F>(mut self, setup: F) -> Self
  671. where
  672. F: FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error + Send>> + Send + 'static,
  673. {
  674. self.setup = Box::new(setup);
  675. self
  676. }
  677. /// Defines the page load hook.
  678. #[must_use]
  679. pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
  680. where
  681. F: Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static,
  682. {
  683. self.on_page_load = Box::new(on_page_load);
  684. self
  685. }
  686. /// Adds a plugin to the runtime.
  687. #[must_use]
  688. pub fn plugin<P: Plugin<R> + 'static>(mut self, plugin: P) -> Self {
  689. self.plugins.register(plugin);
  690. self
  691. }
  692. /// Add `state` to the state managed by the application.
  693. ///
  694. /// This method can be called any number of times as long as each call
  695. /// refers to a different `T`.
  696. ///
  697. /// Managed state can be retrieved by any request handler via the
  698. /// [`State`](crate::State) request guard. In particular, if a value of type `T`
  699. /// is managed by Tauri, adding `State<T>` to the list of arguments in a
  700. /// request handler instructs Tauri to retrieve the managed value.
  701. ///
  702. /// # Panics
  703. ///
  704. /// Panics if state of type `T` is already being managed.
  705. ///
  706. /// # Mutability
  707. ///
  708. /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
  709. ///
  710. /// ```rust,no_run
  711. /// use std::{collections::HashMap, sync::Mutex};
  712. /// use tauri::State;
  713. /// // here we use Mutex to achieve interior mutability
  714. /// struct Storage(Mutex<HashMap<u64, String>>);
  715. /// struct Connection;
  716. /// struct DbConnection(Mutex<Option<Connection>>);
  717. ///
  718. /// #[tauri::command]
  719. /// fn connect(connection: State<DbConnection>) {
  720. /// // initialize the connection, mutating the state with interior mutability
  721. /// *connection.0.lock().unwrap() = Some(Connection {});
  722. /// }
  723. ///
  724. /// #[tauri::command]
  725. /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
  726. /// // mutate the storage behind the Mutex
  727. /// storage.0.lock().unwrap().insert(key, value);
  728. /// }
  729. ///
  730. /// tauri::Builder::default()
  731. /// .manage(Storage(Default::default()))
  732. /// .manage(DbConnection(Default::default()))
  733. /// .invoke_handler(tauri::generate_handler![connect, storage_insert])
  734. /// // on an actual app, remove the string argument
  735. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  736. /// .expect("error while running tauri application");
  737. /// ```
  738. ///
  739. /// # Examples
  740. ///
  741. /// ```rust,no_run
  742. /// use tauri::State;
  743. ///
  744. /// struct MyInt(isize);
  745. /// struct MyString(String);
  746. ///
  747. /// #[tauri::command]
  748. /// fn int_command(state: State<MyInt>) -> String {
  749. /// format!("The stateful int is: {}", state.0)
  750. /// }
  751. ///
  752. /// #[tauri::command]
  753. /// fn string_command<'r>(state: State<'r, MyString>) {
  754. /// println!("state: {}", state.inner().0);
  755. /// }
  756. ///
  757. /// tauri::Builder::default()
  758. /// .manage(MyInt(10))
  759. /// .manage(MyString("Hello, managed state!".to_string()))
  760. /// .invoke_handler(tauri::generate_handler![int_command, string_command])
  761. /// // on an actual app, remove the string argument
  762. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  763. /// .expect("error while running tauri application");
  764. /// ```
  765. #[must_use]
  766. pub fn manage<T>(self, state: T) -> Self
  767. where
  768. T: Send + Sync + 'static,
  769. {
  770. let type_name = std::any::type_name::<T>();
  771. assert!(
  772. self.state.set(state),
  773. "state for type '{}' is already being managed",
  774. type_name
  775. );
  776. self
  777. }
  778. /// Creates a new webview window.
  779. ///
  780. /// # Examples
  781. /// ```rust,no_run
  782. /// use tauri::WindowBuilder;
  783. /// tauri::Builder::default()
  784. /// .create_window("main", tauri::WindowUrl::default(), |win, webview| {
  785. /// let win = win
  786. /// .title("My Main Window")
  787. /// .resizable(true)
  788. /// .inner_size(800.0, 550.0)
  789. /// .min_inner_size(400.0, 200.0);
  790. /// return (win, webview);
  791. /// });
  792. /// ```
  793. pub fn create_window<F>(
  794. mut self,
  795. label: impl Into<String>,
  796. url: WindowUrl,
  797. setup: F,
  798. ) -> crate::Result<Self>
  799. where
  800. F: FnOnce(
  801. <R::Dispatcher as Dispatch>::WindowBuilder,
  802. WebviewAttributes,
  803. ) -> (
  804. <R::Dispatcher as Dispatch>::WindowBuilder,
  805. WebviewAttributes,
  806. ),
  807. {
  808. let (window_builder, webview_attributes) = setup(
  809. <R::Dispatcher as Dispatch>::WindowBuilder::new(),
  810. WebviewAttributes::new(url),
  811. );
  812. self.pending_windows.push(PendingWindow::new(
  813. window_builder,
  814. webview_attributes,
  815. label,
  816. )?);
  817. Ok(self)
  818. }
  819. /// Adds the icon configured on `tauri.conf.json` to the system tray with the specified menu items.
  820. #[cfg(feature = "system-tray")]
  821. #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
  822. #[must_use]
  823. pub fn system_tray(mut self, system_tray: tray::SystemTray) -> Self {
  824. self.system_tray.replace(system_tray);
  825. self
  826. }
  827. /// Sets the menu to use on all windows.
  828. ///
  829. /// # Examples
  830. /// ```rust,no_run
  831. /// use tauri::{MenuEntry, Submenu, MenuItem, Menu, CustomMenuItem};
  832. ///
  833. /// tauri::Builder::default()
  834. /// .menu(Menu::with_items([
  835. /// MenuEntry::Submenu(Submenu::new(
  836. /// "File",
  837. /// Menu::with_items([
  838. /// MenuItem::CloseWindow.into(),
  839. /// #[cfg(target_os = "macos")]
  840. /// CustomMenuItem::new("hello", "Hello").into(),
  841. /// ]),
  842. /// )),
  843. /// ]));
  844. /// ```
  845. #[must_use]
  846. pub fn menu(mut self, menu: Menu) -> Self {
  847. self.menu.replace(menu);
  848. self
  849. }
  850. /// Registers a menu event handler for all windows.
  851. ///
  852. /// # Examples
  853. /// ```rust,no_run
  854. /// use tauri::{Menu, MenuEntry, Submenu, CustomMenuItem, api, Manager};
  855. /// tauri::Builder::default()
  856. /// .menu(Menu::with_items([
  857. /// MenuEntry::Submenu(Submenu::new(
  858. /// "File",
  859. /// Menu::with_items([
  860. /// CustomMenuItem::new("New", "New").into(),
  861. /// CustomMenuItem::new("Learn More", "Learn More").into(),
  862. /// ]),
  863. /// )),
  864. /// ]))
  865. /// .on_menu_event(|event| {
  866. /// match event.menu_item_id() {
  867. /// "Learn More" => {
  868. /// // open in browser (requires the `shell-open-api` feature)
  869. #[cfg_attr(
  870. feature = "shell-open-api",
  871. doc = r#" api::shell::open(&event.window().shell_scope(), "https://github.com/tauri-apps/tauri".to_string(), None).unwrap();"#
  872. )]
  873. /// }
  874. /// id => {
  875. /// // do something with other events
  876. /// println!("got menu event: {}", id);
  877. /// }
  878. /// }
  879. /// });
  880. /// ```
  881. #[must_use]
  882. pub fn on_menu_event<F: Fn(WindowMenuEvent<R>) + Send + Sync + 'static>(
  883. mut self,
  884. handler: F,
  885. ) -> Self {
  886. self.menu_event_listeners.push(Box::new(handler));
  887. self
  888. }
  889. /// Registers a window event handler for all windows.
  890. ///
  891. /// # Examples
  892. /// ```rust,no_run
  893. /// tauri::Builder::default()
  894. /// .on_window_event(|event| match event.event() {
  895. /// tauri::WindowEvent::Focused(focused) => {
  896. /// // hide window whenever it loses focus
  897. /// if !focused {
  898. /// event.window().hide().unwrap();
  899. /// }
  900. /// }
  901. /// _ => {}
  902. /// });
  903. /// ```
  904. #[must_use]
  905. pub fn on_window_event<F: Fn(GlobalWindowEvent<R>) + Send + Sync + 'static>(
  906. mut self,
  907. handler: F,
  908. ) -> Self {
  909. self.window_event_listeners.push(Box::new(handler));
  910. self
  911. }
  912. /// Registers a system tray event handler.
  913. ///
  914. /// # Examples
  915. /// ```rust,no_run
  916. /// use tauri::Manager;
  917. /// tauri::Builder::default()
  918. /// .on_system_tray_event(|app, event| match event {
  919. /// // show window with id "main" when the tray is left clicked
  920. /// tauri::SystemTrayEvent::LeftClick { .. } => {
  921. /// let window = app.get_window("main").unwrap();
  922. /// window.show().unwrap();
  923. /// window.set_focus().unwrap();
  924. /// }
  925. /// _ => {}
  926. /// });
  927. /// ```
  928. #[cfg(feature = "system-tray")]
  929. #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
  930. #[must_use]
  931. pub fn on_system_tray_event<
  932. F: Fn(&AppHandle<R>, tray::SystemTrayEvent) + Send + Sync + 'static,
  933. >(
  934. mut self,
  935. handler: F,
  936. ) -> Self {
  937. self.system_tray_event_listeners.push(Box::new(handler));
  938. self
  939. }
  940. /// Registers a URI scheme protocol available to all webviews.
  941. /// Leverages [setURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) on macOS,
  942. /// [AddWebResourceRequestedFilter](https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addwebresourcerequestedfilter?view=webview2-dotnet-1.0.774.44) on Windows
  943. /// and [webkit-web-context-register-uri-scheme](https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebContext.html#webkit-web-context-register-uri-scheme) on Linux.
  944. ///
  945. /// # Arguments
  946. ///
  947. /// * `uri_scheme` The URI scheme to register, such as `example`.
  948. /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
  949. #[must_use]
  950. pub fn register_uri_scheme_protocol<
  951. N: Into<String>,
  952. H: Fn(&AppHandle<R>, &HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>>
  953. + Send
  954. + Sync
  955. + 'static,
  956. >(
  957. mut self,
  958. uri_scheme: N,
  959. protocol: H,
  960. ) -> Self {
  961. self.uri_scheme_protocols.insert(
  962. uri_scheme.into(),
  963. Arc::new(CustomProtocol {
  964. protocol: Box::new(protocol),
  965. }),
  966. );
  967. self
  968. }
  969. /// Builds the application.
  970. #[allow(clippy::type_complexity)]
  971. pub fn build<A: Assets>(mut self, context: Context<A>) -> crate::Result<App<R>> {
  972. #[cfg(feature = "system-tray")]
  973. let system_tray_icon = {
  974. let icon = context.system_tray_icon.clone();
  975. // check the icon format if the system tray is configured
  976. if self.system_tray.is_some() {
  977. use std::io::{Error, ErrorKind};
  978. #[cfg(target_os = "linux")]
  979. if let Some(Icon::Raw(_)) = icon {
  980. return Err(crate::Error::InvalidIcon(Box::new(Error::new(
  981. ErrorKind::InvalidInput,
  982. "system tray icons on linux must be a file path",
  983. ))));
  984. }
  985. #[cfg(not(target_os = "linux"))]
  986. if let Some(Icon::File(_)) = icon {
  987. return Err(crate::Error::InvalidIcon(Box::new(Error::new(
  988. ErrorKind::InvalidInput,
  989. "system tray icons on non-linux platforms must be the raw bytes",
  990. ))));
  991. }
  992. }
  993. icon
  994. };
  995. #[cfg(all(feature = "system-tray", target_os = "macos"))]
  996. let system_tray_icon_as_template = context
  997. .config
  998. .tauri
  999. .system_tray
  1000. .as_ref()
  1001. .map(|t| t.icon_as_template)
  1002. .unwrap_or_default();
  1003. #[cfg(shell_scope)]
  1004. let shell_scope = context.shell_scope.clone();
  1005. let manager = WindowManager::with_handlers(
  1006. context,
  1007. self.plugins,
  1008. self.invoke_handler,
  1009. self.on_page_load,
  1010. self.uri_scheme_protocols,
  1011. self.state,
  1012. self.window_event_listeners,
  1013. (self.menu, self.menu_event_listeners),
  1014. (self.invoke_responder, self.invoke_initialization_script),
  1015. );
  1016. // set up all the windows defined in the config
  1017. for config in manager.config().tauri.windows.clone() {
  1018. let url = config.url.clone();
  1019. let label = config.label.clone();
  1020. let file_drop_enabled = config.file_drop_enabled;
  1021. let mut webview_attributes = WebviewAttributes::new(url);
  1022. if !file_drop_enabled {
  1023. webview_attributes = webview_attributes.disable_file_drop_handler();
  1024. }
  1025. self.pending_windows.push(PendingWindow::with_config(
  1026. config,
  1027. webview_attributes,
  1028. label,
  1029. )?);
  1030. }
  1031. #[cfg(any(windows, target_os = "linux"))]
  1032. let runtime = if self.runtime_any_thread {
  1033. R::new_any_thread()?
  1034. } else {
  1035. R::new()?
  1036. };
  1037. #[cfg(not(any(windows, target_os = "linux")))]
  1038. let runtime = R::new()?;
  1039. let runtime_handle = runtime.handle();
  1040. let global_shortcut_manager = runtime.global_shortcut_manager();
  1041. let clipboard_manager = runtime.clipboard_manager();
  1042. let mut app = App {
  1043. runtime: Some(runtime),
  1044. manager: manager.clone(),
  1045. global_shortcut_manager: global_shortcut_manager.clone(),
  1046. clipboard_manager: clipboard_manager.clone(),
  1047. #[cfg(feature = "system-tray")]
  1048. tray_handle: None,
  1049. handle: AppHandle {
  1050. runtime_handle,
  1051. manager,
  1052. global_shortcut_manager,
  1053. clipboard_manager,
  1054. #[cfg(feature = "system-tray")]
  1055. tray_handle: None,
  1056. },
  1057. };
  1058. let env = Env::default();
  1059. app.manage(Scopes {
  1060. fs: FsScope::for_fs_api(
  1061. &app.manager.config(),
  1062. app.package_info(),
  1063. &env,
  1064. &app.config().tauri.allowlist.fs.scope,
  1065. ),
  1066. #[cfg(protocol_asset)]
  1067. asset_protocol: FsScope::for_fs_api(
  1068. &app.manager.config(),
  1069. app.package_info(),
  1070. &env,
  1071. &app.config().tauri.allowlist.protocol.asset_scope,
  1072. ),
  1073. #[cfg(http_request)]
  1074. http: crate::scope::HttpScope::for_http_api(&app.config().tauri.allowlist.http.scope),
  1075. #[cfg(shell_scope)]
  1076. shell: ShellScope::new(shell_scope),
  1077. });
  1078. app.manage(env);
  1079. #[cfg(windows)]
  1080. {
  1081. if let Some(w) = &app
  1082. .manager
  1083. .config()
  1084. .tauri
  1085. .bundle
  1086. .windows
  1087. .webview_fixed_runtime_path
  1088. {
  1089. if let Some(resource_dir) = app.path_resolver().resource_dir() {
  1090. std::env::set_var("WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", resource_dir.join(w));
  1091. } else {
  1092. #[cfg(debug_assertions)]
  1093. eprintln!(
  1094. "failed to resolve resource directory; fallback to the installed Webview2 runtime."
  1095. );
  1096. }
  1097. }
  1098. }
  1099. #[cfg(feature = "system-tray")]
  1100. if let Some(system_tray) = self.system_tray {
  1101. let mut ids = HashMap::new();
  1102. if let Some(menu) = system_tray.menu() {
  1103. tray::get_menu_ids(&mut ids, menu);
  1104. }
  1105. let mut tray = tray::SystemTray::new();
  1106. if let Some(menu) = system_tray.menu {
  1107. tray = tray.with_menu(menu);
  1108. }
  1109. #[cfg(not(target_os = "macos"))]
  1110. let tray_handler = app
  1111. .runtime
  1112. .as_ref()
  1113. .unwrap()
  1114. .system_tray(
  1115. tray.with_icon(
  1116. system_tray
  1117. .icon
  1118. .or(system_tray_icon)
  1119. .expect("tray icon not found; please configure it on tauri.conf.json"),
  1120. ),
  1121. )
  1122. .expect("failed to run tray");
  1123. #[cfg(target_os = "macos")]
  1124. let tray_handler = app
  1125. .runtime
  1126. .as_ref()
  1127. .unwrap()
  1128. .system_tray(
  1129. tray
  1130. .with_icon(
  1131. system_tray
  1132. .icon
  1133. .or(system_tray_icon)
  1134. .expect("tray icon not found; please configure it on tauri.conf.json"),
  1135. )
  1136. .with_icon_as_template(system_tray_icon_as_template),
  1137. )
  1138. .expect("failed to run tray");
  1139. let tray_handle = tray::SystemTrayHandle {
  1140. ids: Arc::new(std::sync::Mutex::new(ids)),
  1141. inner: tray_handler,
  1142. };
  1143. let ids = tray_handle.ids.clone();
  1144. app.tray_handle.replace(tray_handle.clone());
  1145. app.handle.tray_handle.replace(tray_handle);
  1146. for listener in self.system_tray_event_listeners {
  1147. let app_handle = app.handle();
  1148. let ids = ids.clone();
  1149. let listener = Arc::new(std::sync::Mutex::new(listener));
  1150. app
  1151. .runtime
  1152. .as_mut()
  1153. .unwrap()
  1154. .on_system_tray_event(move |event| {
  1155. let app_handle = app_handle.clone();
  1156. let event = match event {
  1157. RuntimeSystemTrayEvent::MenuItemClick(id) => tray::SystemTrayEvent::MenuItemClick {
  1158. id: ids.lock().unwrap().get(id).unwrap().clone(),
  1159. },
  1160. RuntimeSystemTrayEvent::LeftClick { position, size } => {
  1161. tray::SystemTrayEvent::LeftClick {
  1162. position: *position,
  1163. size: *size,
  1164. }
  1165. }
  1166. RuntimeSystemTrayEvent::RightClick { position, size } => {
  1167. tray::SystemTrayEvent::RightClick {
  1168. position: *position,
  1169. size: *size,
  1170. }
  1171. }
  1172. RuntimeSystemTrayEvent::DoubleClick { position, size } => {
  1173. tray::SystemTrayEvent::DoubleClick {
  1174. position: *position,
  1175. size: *size,
  1176. }
  1177. }
  1178. };
  1179. let listener = listener.clone();
  1180. listener.lock().unwrap()(&app_handle, event);
  1181. });
  1182. }
  1183. }
  1184. app.manager.initialize_plugins(&app.handle())?;
  1185. let window_labels = self
  1186. .pending_windows
  1187. .iter()
  1188. .map(|p| p.label.clone())
  1189. .collect::<Vec<_>>();
  1190. #[cfg(feature = "updater")]
  1191. let mut main_window = None;
  1192. for pending in self.pending_windows {
  1193. let pending = app
  1194. .manager
  1195. .prepare_window(app.handle.clone(), pending, &window_labels)?;
  1196. let detached = app.runtime.as_ref().unwrap().create_window(pending)?;
  1197. let _window = app.manager.attach_window(app.handle(), detached);
  1198. #[cfg(feature = "updater")]
  1199. if main_window.is_none() {
  1200. main_window = Some(_window);
  1201. }
  1202. }
  1203. (self.setup)(&mut app).map_err(|e| crate::Error::Setup(e))?;
  1204. #[cfg(feature = "updater")]
  1205. app.run_updater(main_window);
  1206. Ok(app)
  1207. }
  1208. /// Runs the configured Tauri application.
  1209. pub fn run<A: Assets>(self, context: Context<A>) -> crate::Result<()> {
  1210. self.build(context)?.run(|_, _| {});
  1211. Ok(())
  1212. }
  1213. }
  1214. fn on_event_loop_event<R: Runtime, F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
  1215. app_handle: &AppHandle<R>,
  1216. event: RuntimeRunEvent,
  1217. manager: &WindowManager<R>,
  1218. callback: Option<&mut F>,
  1219. ) {
  1220. if let RuntimeRunEvent::WindowClose(label) = &event {
  1221. manager.on_window_close(label);
  1222. }
  1223. let event = match event {
  1224. RuntimeRunEvent::Exit => RunEvent::Exit,
  1225. RuntimeRunEvent::ExitRequested { window_label, tx } => RunEvent::ExitRequested {
  1226. window_label,
  1227. api: ExitRequestApi(tx),
  1228. },
  1229. RuntimeRunEvent::CloseRequested { label, signal_tx } => RunEvent::CloseRequested {
  1230. label,
  1231. api: CloseRequestApi(signal_tx),
  1232. },
  1233. RuntimeRunEvent::WindowClose(label) => RunEvent::WindowClosed(label),
  1234. RuntimeRunEvent::Ready => RunEvent::Ready,
  1235. RuntimeRunEvent::Resumed => RunEvent::Resumed,
  1236. RuntimeRunEvent::MainEventsCleared => RunEvent::MainEventsCleared,
  1237. _ => unimplemented!(),
  1238. };
  1239. manager
  1240. .inner
  1241. .plugins
  1242. .lock()
  1243. .expect("poisoned plugin store")
  1244. .on_event(app_handle, &event);
  1245. if let Some(c) = callback {
  1246. c(app_handle, event);
  1247. }
  1248. }
  1249. /// Make `Wry` the default `Runtime` for `Builder`
  1250. #[cfg(feature = "wry")]
  1251. #[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
  1252. impl Default for Builder<crate::Wry> {
  1253. fn default() -> Self {
  1254. Self::new()
  1255. }
  1256. }
  1257. #[cfg(test)]
  1258. mod tests {
  1259. #[test]
  1260. fn is_send_sync() {
  1261. crate::test_utils::assert_send::<super::AppHandle>();
  1262. crate::test_utils::assert_sync::<super::AppHandle>();
  1263. #[cfg(feature = "wry")]
  1264. {
  1265. crate::test_utils::assert_send::<super::AssetResolver<crate::Wry>>();
  1266. crate::test_utils::assert_sync::<super::AssetResolver<crate::Wry>>();
  1267. }
  1268. crate::test_utils::assert_send::<super::PathResolver>();
  1269. crate::test_utils::assert_sync::<super::PathResolver>();
  1270. }
  1271. }