app.rs 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use crate::{
  5. command::{CommandArg, CommandItem},
  6. ipc::{
  7. channel::ChannelDataIpcQueue, CallbackFn, Invoke, InvokeError, InvokeHandler, InvokeResponder,
  8. InvokeResponse,
  9. },
  10. manager::{Asset, UriSchemeProtocol, WindowManager},
  11. plugin::{Plugin, PluginStore},
  12. runtime::{
  13. webview::WebviewAttributes,
  14. window::{PendingWindow, WindowEvent as RuntimeWindowEvent},
  15. ExitRequestedEventAction, RunEvent as RuntimeRunEvent,
  16. },
  17. scope,
  18. sealed::{ManagerBase, RuntimeOrDispatch},
  19. utils::config::Config,
  20. utils::{assets::Assets, Env},
  21. Context, DeviceEventFilter, EventLoopMessage, Icon, Manager, Monitor, Runtime, Scopes,
  22. StateManager, Theme, Window,
  23. };
  24. #[cfg(desktop)]
  25. use crate::menu::{Menu, MenuEvent};
  26. #[cfg(all(desktop, feature = "tray-icon"))]
  27. use crate::tray::{TrayIcon, TrayIconBuilder, TrayIconEvent, TrayIconId};
  28. #[cfg(desktop)]
  29. use crate::window::WindowMenu;
  30. use raw_window_handle::HasRawDisplayHandle;
  31. use serde::Deserialize;
  32. use serialize_to_javascript::{default_template, DefaultTemplate, Template};
  33. use tauri_macros::default_runtime;
  34. #[cfg(desktop)]
  35. use tauri_runtime::EventLoopProxy;
  36. use tauri_runtime::{
  37. window::{
  38. dpi::{PhysicalPosition, PhysicalSize},
  39. FileDropEvent,
  40. },
  41. RuntimeInitArgs,
  42. };
  43. use tauri_utils::PackageInfo;
  44. use std::{
  45. borrow::Cow,
  46. collections::HashMap,
  47. fmt,
  48. sync::{mpsc::Sender, Arc, Weak},
  49. };
  50. use crate::runtime::RuntimeHandle;
  51. #[cfg(target_os = "macos")]
  52. use crate::ActivationPolicy;
  53. pub(crate) mod plugin;
  54. #[cfg(desktop)]
  55. pub(crate) type GlobalMenuEventListener<T> = Box<dyn Fn(&T, crate::menu::MenuEvent) + Send + Sync>;
  56. #[cfg(all(desktop, feature = "tray-icon"))]
  57. pub(crate) type GlobalTrayIconEventListener<T> =
  58. Box<dyn Fn(&T, crate::tray::TrayIconEvent) + Send + Sync>;
  59. pub(crate) type GlobalWindowEventListener<R> = Box<dyn Fn(GlobalWindowEvent<R>) + Send + Sync>;
  60. /// A closure that is run when the Tauri application is setting up.
  61. pub type SetupHook<R> =
  62. Box<dyn FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send>;
  63. /// A closure that is run once every time a window is created and loaded.
  64. pub type OnPageLoad<R> = dyn Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static;
  65. /// The payload for the [`OnPageLoad`] hook.
  66. #[derive(Debug, Clone, Deserialize)]
  67. pub struct PageLoadPayload {
  68. url: String,
  69. }
  70. impl PageLoadPayload {
  71. /// The page URL.
  72. pub fn url(&self) -> &str {
  73. &self.url
  74. }
  75. }
  76. /// Api exposed on the `ExitRequested` event.
  77. #[derive(Debug)]
  78. pub struct ExitRequestApi(Sender<ExitRequestedEventAction>);
  79. impl ExitRequestApi {
  80. /// Prevents the app from exiting
  81. pub fn prevent_exit(&self) {
  82. self.0.send(ExitRequestedEventAction::Prevent).unwrap();
  83. }
  84. }
  85. /// Api exposed on the `CloseRequested` event.
  86. #[derive(Debug, Clone)]
  87. pub struct CloseRequestApi(Sender<bool>);
  88. impl CloseRequestApi {
  89. /// Prevents the window from being closed.
  90. pub fn prevent_close(&self) {
  91. self.0.send(true).unwrap();
  92. }
  93. }
  94. /// An event from a window.
  95. #[derive(Debug, Clone)]
  96. #[non_exhaustive]
  97. pub enum WindowEvent {
  98. /// The size of the window has changed. Contains the client area's new dimensions.
  99. Resized(PhysicalSize<u32>),
  100. /// The position of the window has changed. Contains the window's new position.
  101. Moved(PhysicalPosition<i32>),
  102. /// The window has been requested to close.
  103. #[non_exhaustive]
  104. CloseRequested {
  105. /// An API modify the behavior of the close requested event.
  106. api: CloseRequestApi,
  107. },
  108. /// The window has been destroyed.
  109. Destroyed,
  110. /// The window gained or lost focus.
  111. ///
  112. /// The parameter is true if the window has gained focus, and false if it has lost focus.
  113. Focused(bool),
  114. /// The window's scale factor has changed.
  115. ///
  116. /// The following user actions can cause DPI changes:
  117. ///
  118. /// - Changing the display's resolution.
  119. /// - Changing the display's scale factor (e.g. in Control Panel on Windows).
  120. /// - Moving the window to a display with a different scale factor.
  121. #[non_exhaustive]
  122. ScaleFactorChanged {
  123. /// The new scale factor.
  124. scale_factor: f64,
  125. /// The window inner size.
  126. new_inner_size: PhysicalSize<u32>,
  127. },
  128. /// An event associated with the file drop action.
  129. FileDrop(FileDropEvent),
  130. /// The system window theme has changed. Only delivered if the window [`theme`](`crate::window::WindowBuilder#method.theme`) is `None`.
  131. ///
  132. /// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme.
  133. ///
  134. /// ## Platform-specific
  135. ///
  136. /// - **Linux**: Not supported.
  137. ThemeChanged(Theme),
  138. }
  139. impl From<RuntimeWindowEvent> for WindowEvent {
  140. fn from(event: RuntimeWindowEvent) -> Self {
  141. match event {
  142. RuntimeWindowEvent::Resized(size) => Self::Resized(size),
  143. RuntimeWindowEvent::Moved(position) => Self::Moved(position),
  144. RuntimeWindowEvent::CloseRequested { signal_tx } => Self::CloseRequested {
  145. api: CloseRequestApi(signal_tx),
  146. },
  147. RuntimeWindowEvent::Destroyed => Self::Destroyed,
  148. RuntimeWindowEvent::Focused(flag) => Self::Focused(flag),
  149. RuntimeWindowEvent::ScaleFactorChanged {
  150. scale_factor,
  151. new_inner_size,
  152. } => Self::ScaleFactorChanged {
  153. scale_factor,
  154. new_inner_size,
  155. },
  156. RuntimeWindowEvent::FileDrop(event) => Self::FileDrop(event),
  157. RuntimeWindowEvent::ThemeChanged(theme) => Self::ThemeChanged(theme),
  158. }
  159. }
  160. }
  161. /// An application event, triggered from the event loop.
  162. ///
  163. /// See [`App::run`](crate::App#method.run) for usage examples.
  164. #[derive(Debug)]
  165. #[non_exhaustive]
  166. pub enum RunEvent {
  167. /// Event loop is exiting.
  168. Exit,
  169. /// The app is about to exit
  170. #[non_exhaustive]
  171. ExitRequested {
  172. /// Event API
  173. api: ExitRequestApi,
  174. },
  175. /// An event associated with a window.
  176. #[non_exhaustive]
  177. WindowEvent {
  178. /// The window label.
  179. label: String,
  180. /// The detailed event.
  181. event: WindowEvent,
  182. },
  183. /// Application ready.
  184. Ready,
  185. /// Sent if the event loop is being resumed.
  186. Resumed,
  187. /// Emitted when all of the event loop’s input events have been processed and redraw processing is about to begin.
  188. ///
  189. /// 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.
  190. MainEventsCleared,
  191. /// Emitted when the user wants to open the specified resource with the app.
  192. #[cfg(any(target_os = "macos", target_os = "ios"))]
  193. #[cfg_attr(doc_cfg, doc(cfg(any(target_os = "macos", feature = "ios"))))]
  194. Opened {
  195. /// The URL of the resources that is being open.
  196. urls: Vec<url::Url>,
  197. },
  198. /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
  199. #[cfg(desktop)]
  200. #[cfg_attr(doc_cfg, doc(cfg(desktop)))]
  201. MenuEvent(crate::menu::MenuEvent),
  202. /// An event from a tray icon.
  203. #[cfg(all(desktop, feature = "tray-icon"))]
  204. #[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
  205. TrayIconEvent(crate::tray::TrayIconEvent),
  206. }
  207. impl From<EventLoopMessage> for RunEvent {
  208. fn from(event: EventLoopMessage) -> Self {
  209. match event {
  210. #[cfg(desktop)]
  211. EventLoopMessage::MenuEvent(e) => Self::MenuEvent(e),
  212. #[cfg(all(desktop, feature = "tray-icon"))]
  213. EventLoopMessage::TrayIconEvent(e) => Self::TrayIconEvent(e),
  214. }
  215. }
  216. }
  217. /// A window event that was triggered on the specified window.
  218. #[default_runtime(crate::Wry, wry)]
  219. #[derive(Debug)]
  220. pub struct GlobalWindowEvent<R: Runtime> {
  221. pub(crate) event: WindowEvent,
  222. pub(crate) window: Window<R>,
  223. }
  224. impl<R: Runtime> GlobalWindowEvent<R> {
  225. /// The event payload.
  226. pub fn event(&self) -> &WindowEvent {
  227. &self.event
  228. }
  229. /// The window that the event belongs to.
  230. pub fn window(&self) -> &Window<R> {
  231. &self.window
  232. }
  233. }
  234. /// The asset resolver is a helper to access the [`tauri_utils::assets::Assets`] interface.
  235. #[derive(Debug, Clone)]
  236. pub struct AssetResolver<R: Runtime> {
  237. manager: WindowManager<R>,
  238. }
  239. impl<R: Runtime> AssetResolver<R> {
  240. /// Gets the app asset associated with the given path.
  241. pub fn get(&self, path: String) -> Option<Asset> {
  242. self.manager.get_asset(path).ok()
  243. }
  244. }
  245. /// A handle to the currently running application.
  246. ///
  247. /// This type implements [`Manager`] which allows for manipulation of global application items.
  248. #[default_runtime(crate::Wry, wry)]
  249. #[derive(Debug)]
  250. pub struct AppHandle<R: Runtime> {
  251. pub(crate) runtime_handle: R::Handle,
  252. pub(crate) manager: WindowManager<R>,
  253. }
  254. /// APIs specific to the wry runtime.
  255. #[cfg(feature = "wry")]
  256. impl AppHandle<crate::Wry> {
  257. /// Create a new tao window using a callback. The event loop must be running at this point.
  258. pub fn create_tao_window<
  259. F: FnOnce() -> (String, tauri_runtime_wry::WryWindowBuilder) + Send + 'static,
  260. >(
  261. &self,
  262. f: F,
  263. ) -> crate::Result<Weak<tauri_runtime_wry::Window>> {
  264. self.runtime_handle.create_tao_window(f).map_err(Into::into)
  265. }
  266. /// Sends a window message to the event loop.
  267. pub fn send_tao_window_event(
  268. &self,
  269. window_id: tauri_runtime_wry::WindowId,
  270. message: tauri_runtime_wry::WindowMessage,
  271. ) -> crate::Result<()> {
  272. self
  273. .runtime_handle
  274. .send_event(tauri_runtime_wry::Message::Window(
  275. self.runtime_handle.window_id(window_id),
  276. message,
  277. ))
  278. .map_err(Into::into)
  279. }
  280. }
  281. impl<R: Runtime> Clone for AppHandle<R> {
  282. fn clone(&self) -> Self {
  283. Self {
  284. runtime_handle: self.runtime_handle.clone(),
  285. manager: self.manager.clone(),
  286. }
  287. }
  288. }
  289. impl<'de, R: Runtime> CommandArg<'de, R> for AppHandle<R> {
  290. /// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`AppHandle`]. This will never fail.
  291. fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
  292. Ok(command.message.window().app_handle)
  293. }
  294. }
  295. impl<R: Runtime> AppHandle<R> {
  296. /// Runs the given closure on the main thread.
  297. pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
  298. self
  299. .runtime_handle
  300. .run_on_main_thread(f)
  301. .map_err(Into::into)
  302. }
  303. /// Adds a Tauri application plugin.
  304. /// This function can be used to register a plugin that is loaded dynamically e.g. after login.
  305. /// For plugins that are created when the app is started, prefer [`Builder::plugin`].
  306. ///
  307. /// See [`Builder::plugin`] for more information.
  308. ///
  309. /// # Examples
  310. ///
  311. /// ```
  312. /// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin}, Runtime};
  313. ///
  314. /// fn init_plugin<R: Runtime>() -> TauriPlugin<R> {
  315. /// PluginBuilder::new("dummy").build()
  316. /// }
  317. ///
  318. /// tauri::Builder::default()
  319. /// .setup(move |app| {
  320. /// let handle = app.handle().clone();
  321. /// std::thread::spawn(move || {
  322. /// handle.plugin(init_plugin());
  323. /// });
  324. ///
  325. /// Ok(())
  326. /// });
  327. /// ```
  328. pub fn plugin<P: Plugin<R> + 'static>(&self, mut plugin: P) -> crate::Result<()> {
  329. plugin
  330. .initialize(
  331. self,
  332. self
  333. .config()
  334. .plugins
  335. .0
  336. .get(plugin.name())
  337. .cloned()
  338. .unwrap_or_default(),
  339. )
  340. .map_err(|e| crate::Error::PluginInitialization(plugin.name().to_string(), e.to_string()))?;
  341. self
  342. .manager()
  343. .inner
  344. .plugins
  345. .lock()
  346. .unwrap()
  347. .register(plugin);
  348. Ok(())
  349. }
  350. /// Removes the plugin with the given name.
  351. ///
  352. /// # Examples
  353. ///
  354. /// ```
  355. /// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin, Plugin}, Runtime};
  356. ///
  357. /// fn init_plugin<R: Runtime>() -> TauriPlugin<R> {
  358. /// PluginBuilder::new("dummy").build()
  359. /// }
  360. ///
  361. /// let plugin = init_plugin();
  362. /// // `.name()` requires the `PLugin` trait import
  363. /// let plugin_name = plugin.name();
  364. /// tauri::Builder::default()
  365. /// .plugin(plugin)
  366. /// .setup(move |app| {
  367. /// let handle = app.handle().clone();
  368. /// std::thread::spawn(move || {
  369. /// handle.remove_plugin(plugin_name);
  370. /// });
  371. ///
  372. /// Ok(())
  373. /// });
  374. /// ```
  375. pub fn remove_plugin(&self, plugin: &'static str) -> bool {
  376. self
  377. .manager()
  378. .inner
  379. .plugins
  380. .lock()
  381. .unwrap()
  382. .unregister(plugin)
  383. }
  384. /// Exits the app. This is the same as [`std::process::exit`], but it performs cleanup on this application.
  385. pub fn exit(&self, exit_code: i32) {
  386. self.cleanup_before_exit();
  387. std::process::exit(exit_code);
  388. }
  389. /// Restarts the app. This is the same as [`crate::process::restart`], but it performs cleanup on this application.
  390. pub fn restart(&self) {
  391. self.cleanup_before_exit();
  392. crate::process::restart(&self.env());
  393. }
  394. }
  395. impl<R: Runtime> Manager<R> for AppHandle<R> {}
  396. impl<R: Runtime> ManagerBase<R> for AppHandle<R> {
  397. fn manager(&self) -> &WindowManager<R> {
  398. &self.manager
  399. }
  400. fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
  401. RuntimeOrDispatch::RuntimeHandle(self.runtime_handle.clone())
  402. }
  403. fn managed_app_handle(&self) -> &AppHandle<R> {
  404. self
  405. }
  406. }
  407. /// The instance of the currently running application.
  408. ///
  409. /// This type implements [`Manager`] which allows for manipulation of global application items.
  410. #[default_runtime(crate::Wry, wry)]
  411. pub struct App<R: Runtime> {
  412. runtime: Option<R>,
  413. pending_windows: Option<Vec<PendingWindow<EventLoopMessage, R>>>,
  414. setup: Option<SetupHook<R>>,
  415. manager: WindowManager<R>,
  416. handle: AppHandle<R>,
  417. }
  418. impl<R: Runtime> fmt::Debug for App<R> {
  419. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  420. f.debug_struct("App")
  421. .field("runtime", &self.runtime)
  422. .field("manager", &self.manager)
  423. .field("handle", &self.handle)
  424. .finish()
  425. }
  426. }
  427. impl<R: Runtime> Manager<R> for App<R> {}
  428. impl<R: Runtime> ManagerBase<R> for App<R> {
  429. fn manager(&self) -> &WindowManager<R> {
  430. &self.manager
  431. }
  432. fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
  433. if let Some(runtime) = self.runtime.as_ref() {
  434. RuntimeOrDispatch::Runtime(runtime)
  435. } else {
  436. self.handle.runtime()
  437. }
  438. }
  439. fn managed_app_handle(&self) -> &AppHandle<R> {
  440. self.handle()
  441. }
  442. }
  443. /// APIs specific to the wry runtime.
  444. #[cfg(feature = "wry")]
  445. impl App<crate::Wry> {
  446. /// Adds a [`tauri_runtime_wry::Plugin`] using its [`tauri_runtime_wry::PluginBuilder`].
  447. ///
  448. /// # Stability
  449. ///
  450. /// This API is unstable.
  451. pub fn wry_plugin<P: tauri_runtime_wry::PluginBuilder<EventLoopMessage> + Send + 'static>(
  452. &mut self,
  453. plugin: P,
  454. ) where
  455. <P as tauri_runtime_wry::PluginBuilder<EventLoopMessage>>::Plugin: Send,
  456. {
  457. self.handle.runtime_handle.plugin(plugin);
  458. }
  459. }
  460. macro_rules! shared_app_impl {
  461. ($app: ty) => {
  462. impl<R: Runtime> $app {
  463. /// Registers a global menu event listener.
  464. #[cfg(desktop)]
  465. pub fn on_menu_event<F: Fn(&AppHandle<R>, MenuEvent) + Send + Sync + 'static>(
  466. &self,
  467. handler: F,
  468. ) {
  469. self
  470. .manager
  471. .inner
  472. .menu_event_listeners
  473. .lock()
  474. .unwrap()
  475. .push(Box::new(handler));
  476. }
  477. /// Registers a global tray icon menu event listener.
  478. #[cfg(all(desktop, feature = "tray-icon"))]
  479. #[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
  480. pub fn on_tray_icon_event<F: Fn(&AppHandle<R>, TrayIconEvent) + Send + Sync + 'static>(
  481. &self,
  482. handler: F,
  483. ) {
  484. self
  485. .manager
  486. .inner
  487. .global_tray_event_listeners
  488. .lock()
  489. .unwrap()
  490. .push(Box::new(handler));
  491. }
  492. /// Gets the first tray icon registered,
  493. /// usually the one configured in the Tauri configuration file.
  494. #[cfg(all(desktop, feature = "tray-icon"))]
  495. #[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
  496. pub fn tray(&self) -> Option<TrayIcon<R>> {
  497. self
  498. .manager
  499. .inner
  500. .tray_icons
  501. .lock()
  502. .unwrap()
  503. .first()
  504. .cloned()
  505. }
  506. /// Removes the first tray icon registerd, usually the one configured in
  507. /// tauri config file, from tauri's internal state and returns it.
  508. ///
  509. /// Note that dropping the returned icon, will cause the tray icon to disappear.
  510. #[cfg(all(desktop, feature = "tray-icon"))]
  511. #[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
  512. pub fn remove_tray(&self) -> Option<TrayIcon<R>> {
  513. let mut tray_icons = self.manager.inner.tray_icons.lock().unwrap();
  514. if !tray_icons.is_empty() {
  515. return Some(tray_icons.swap_remove(0));
  516. }
  517. None
  518. }
  519. /// Gets a tray icon using the provided id.
  520. #[cfg(all(desktop, feature = "tray-icon"))]
  521. #[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
  522. pub fn tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
  523. where
  524. I: ?Sized,
  525. TrayIconId: PartialEq<&'a I>,
  526. {
  527. self
  528. .manager
  529. .inner
  530. .tray_icons
  531. .lock()
  532. .unwrap()
  533. .iter()
  534. .find(|t| t.id() == &id)
  535. .cloned()
  536. }
  537. /// Removes a tray icon using the provided id from tauri's internal state and returns it.
  538. ///
  539. /// Note that dropping the returned icon, will cause the tray icon to disappear.
  540. #[cfg(all(desktop, feature = "tray-icon"))]
  541. #[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
  542. pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
  543. where
  544. I: ?Sized,
  545. TrayIconId: PartialEq<&'a I>,
  546. {
  547. let mut tray_icons = self.manager.inner.tray_icons.lock().unwrap();
  548. let idx = tray_icons.iter().position(|t| t.id() == &id);
  549. if let Some(idx) = idx {
  550. return Some(tray_icons.swap_remove(idx));
  551. }
  552. None
  553. }
  554. /// Gets the app's configuration, defined on the `tauri.conf.json` file.
  555. pub fn config(&self) -> Arc<Config> {
  556. self.manager.config()
  557. }
  558. /// Gets the app's package information.
  559. pub fn package_info(&self) -> &PackageInfo {
  560. self.manager.package_info()
  561. }
  562. /// The application's asset resolver.
  563. pub fn asset_resolver(&self) -> AssetResolver<R> {
  564. AssetResolver {
  565. manager: self.manager.clone(),
  566. }
  567. }
  568. /// Returns the primary monitor of the system.
  569. ///
  570. /// Returns None if it can't identify any monitor as a primary one.
  571. pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
  572. Ok(match self.runtime() {
  573. RuntimeOrDispatch::Runtime(h) => h.primary_monitor().map(Into::into),
  574. RuntimeOrDispatch::RuntimeHandle(h) => h.primary_monitor().map(Into::into),
  575. _ => unreachable!(),
  576. })
  577. }
  578. /// Returns the list of all the monitors available on the system.
  579. pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
  580. Ok(match self.runtime() {
  581. RuntimeOrDispatch::Runtime(h) => {
  582. h.available_monitors().into_iter().map(Into::into).collect()
  583. }
  584. RuntimeOrDispatch::RuntimeHandle(h) => {
  585. h.available_monitors().into_iter().map(Into::into).collect()
  586. }
  587. _ => unreachable!(),
  588. })
  589. }
  590. /// Returns the default window icon.
  591. pub fn default_window_icon(&self) -> Option<&Icon> {
  592. self.manager.inner.default_window_icon.as_ref()
  593. }
  594. /// Returns the app-wide menu.
  595. #[cfg(desktop)]
  596. pub fn menu(&self) -> Option<Menu<R>> {
  597. self.manager.menu_lock().clone()
  598. }
  599. /// Sets the app-wide menu and returns the previous one.
  600. ///
  601. /// If a window was not created with an explicit menu or had one set explicitly,
  602. /// this menu will be assigned to it.
  603. #[cfg(desktop)]
  604. pub fn set_menu(&self, menu: Menu<R>) -> crate::Result<Option<Menu<R>>> {
  605. let prev_menu = self.remove_menu()?;
  606. self.manager.insert_menu_into_stash(&menu);
  607. self.manager.menu_lock().replace(menu.clone());
  608. // set it on all windows that don't have one or previously had the app-wide menu
  609. #[cfg(not(target_os = "macos"))]
  610. {
  611. for window in self.manager.windows().values() {
  612. let has_app_wide_menu = window.has_app_wide_menu() || window.menu().is_none();
  613. if has_app_wide_menu {
  614. window.set_menu(menu.clone())?;
  615. window.menu_lock().replace(WindowMenu {
  616. is_app_wide: true,
  617. menu: menu.clone(),
  618. });
  619. }
  620. }
  621. }
  622. // set it app-wide for macos
  623. #[cfg(target_os = "macos")]
  624. {
  625. let menu_ = menu.clone();
  626. self.run_on_main_thread(move || {
  627. let _ = init_app_menu(&menu_);
  628. })?;
  629. }
  630. Ok(prev_menu)
  631. }
  632. /// Remove the app-wide menu and returns it.
  633. ///
  634. /// If a window was not created with an explicit menu or had one set explicitly,
  635. /// this will remove the menu from it.
  636. #[cfg(desktop)]
  637. pub fn remove_menu(&self) -> crate::Result<Option<Menu<R>>> {
  638. let menu = self.manager.menu_lock().as_ref().cloned();
  639. #[allow(unused_variables)]
  640. if let Some(menu) = menu {
  641. // remove from windows that have the app-wide menu
  642. #[cfg(not(target_os = "macos"))]
  643. {
  644. for window in self.manager.windows().values() {
  645. let has_app_wide_menu = window.has_app_wide_menu();
  646. if has_app_wide_menu {
  647. window.remove_menu()?;
  648. *window.menu_lock() = None;
  649. }
  650. }
  651. }
  652. // remove app-wide for macos
  653. #[cfg(target_os = "macos")]
  654. {
  655. self.run_on_main_thread(move || {
  656. menu.inner().remove_for_nsapp();
  657. })?;
  658. }
  659. }
  660. let prev_menu = self.manager.menu_lock().take();
  661. self
  662. .manager
  663. .remove_menu_from_stash_by_id(prev_menu.as_ref().map(|m| m.id()));
  664. Ok(prev_menu)
  665. }
  666. /// Hides the app-wide menu from windows that have it.
  667. ///
  668. /// If a window was not created with an explicit menu or had one set explicitly,
  669. /// this will hide the menu from it.
  670. #[cfg(desktop)]
  671. pub fn hide_menu(&self) -> crate::Result<()> {
  672. #[cfg(not(target_os = "macos"))]
  673. {
  674. let is_app_menu_set = self.manager.menu_lock().is_some();
  675. if is_app_menu_set {
  676. for window in self.manager.windows().values() {
  677. if window.has_app_wide_menu() {
  678. window.hide_menu()?;
  679. }
  680. }
  681. }
  682. }
  683. Ok(())
  684. }
  685. /// Shows the app-wide menu for windows that have it.
  686. ///
  687. /// If a window was not created with an explicit menu or had one set explicitly,
  688. /// this will show the menu for it.
  689. #[cfg(desktop)]
  690. pub fn show_menu(&self) -> crate::Result<()> {
  691. #[cfg(not(target_os = "macos"))]
  692. {
  693. let is_app_menu_set = self.manager.menu_lock().is_some();
  694. if is_app_menu_set {
  695. for window in self.manager.windows().values() {
  696. if window.has_app_wide_menu() {
  697. window.show_menu()?;
  698. }
  699. }
  700. }
  701. }
  702. Ok(())
  703. }
  704. /// Shows the application, but does not automatically focus it.
  705. #[cfg(target_os = "macos")]
  706. pub fn show(&self) -> crate::Result<()> {
  707. match self.runtime() {
  708. RuntimeOrDispatch::Runtime(r) => r.show(),
  709. RuntimeOrDispatch::RuntimeHandle(h) => h.show()?,
  710. _ => unreachable!(),
  711. }
  712. Ok(())
  713. }
  714. /// Hides the application.
  715. #[cfg(target_os = "macos")]
  716. pub fn hide(&self) -> crate::Result<()> {
  717. match self.runtime() {
  718. RuntimeOrDispatch::Runtime(r) => r.hide(),
  719. RuntimeOrDispatch::RuntimeHandle(h) => h.hide()?,
  720. _ => unreachable!(),
  721. }
  722. Ok(())
  723. }
  724. /// Runs necessary cleanup tasks before exiting the process.
  725. /// **You should always exit the tauri app immediately after this function returns and not use any tauri-related APIs.**
  726. pub fn cleanup_before_exit(&self) {
  727. #[cfg(all(desktop, feature = "tray-icon"))]
  728. self.manager.inner.tray_icons.lock().unwrap().clear()
  729. }
  730. }
  731. };
  732. }
  733. shared_app_impl!(App<R>);
  734. shared_app_impl!(AppHandle<R>);
  735. impl<R: Runtime> App<R> {
  736. fn register_core_plugins(&self) -> crate::Result<()> {
  737. self.handle.plugin(crate::path::plugin::init())?;
  738. self.handle.plugin(crate::event::plugin::init())?;
  739. self.handle.plugin(crate::window::plugin::init())?;
  740. self.handle.plugin(crate::app::plugin::init())?;
  741. Ok(())
  742. }
  743. /// Runs the given closure on the main thread.
  744. pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
  745. self.app_handle().run_on_main_thread(f)
  746. }
  747. /// Gets a handle to the application instance.
  748. pub fn handle(&self) -> &AppHandle<R> {
  749. &self.handle
  750. }
  751. /// Sets the activation policy for the application. It is set to `NSApplicationActivationPolicyRegular` by default.
  752. ///
  753. /// # Examples
  754. /// ```,no_run
  755. /// let mut app = tauri::Builder::default()
  756. /// // on an actual app, remove the string argument
  757. /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  758. /// .expect("error while building tauri application");
  759. /// #[cfg(target_os = "macos")]
  760. /// app.set_activation_policy(tauri::ActivationPolicy::Accessory);
  761. /// app.run(|_app_handle, _event| {});
  762. /// ```
  763. #[cfg(target_os = "macos")]
  764. #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
  765. pub fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
  766. self
  767. .runtime
  768. .as_mut()
  769. .unwrap()
  770. .set_activation_policy(activation_policy);
  771. }
  772. /// Change the device event filter mode.
  773. ///
  774. /// Since the DeviceEvent capture can lead to high CPU usage for unfocused windows, [`tao`]
  775. /// will ignore them by default for unfocused windows on Windows. This method allows changing
  776. /// the filter to explicitly capture them again.
  777. ///
  778. /// ## Platform-specific
  779. ///
  780. /// - ** Linux / macOS / iOS / Android**: Unsupported.
  781. ///
  782. /// # Examples
  783. /// ```,no_run
  784. /// let mut app = tauri::Builder::default()
  785. /// // on an actual app, remove the string argument
  786. /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  787. /// .expect("error while building tauri application");
  788. /// app.set_device_event_filter(tauri::DeviceEventFilter::Always);
  789. /// app.run(|_app_handle, _event| {});
  790. /// ```
  791. ///
  792. /// [`tao`]: https://crates.io/crates/tao
  793. pub fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {
  794. self
  795. .runtime
  796. .as_mut()
  797. .unwrap()
  798. .set_device_event_filter(filter);
  799. }
  800. /// Runs the application.
  801. ///
  802. /// # Examples
  803. /// ```,no_run
  804. /// let app = tauri::Builder::default()
  805. /// // on an actual app, remove the string argument
  806. /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  807. /// .expect("error while building tauri application");
  808. /// app.run(|_app_handle, event| match event {
  809. /// tauri::RunEvent::ExitRequested { api, .. } => {
  810. /// api.prevent_exit();
  811. /// }
  812. /// _ => {}
  813. /// });
  814. /// ```
  815. pub fn run<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(mut self, mut callback: F) {
  816. let app_handle = self.handle().clone();
  817. let manager = self.manager.clone();
  818. self.runtime.take().unwrap().run(move |event| match event {
  819. RuntimeRunEvent::Ready => {
  820. if let Err(e) = setup(&mut self) {
  821. panic!("Failed to setup app: {e}");
  822. }
  823. on_event_loop_event(
  824. &app_handle,
  825. RuntimeRunEvent::Ready,
  826. &manager,
  827. Some(&mut callback),
  828. );
  829. }
  830. RuntimeRunEvent::Exit => {
  831. on_event_loop_event(
  832. &app_handle,
  833. RuntimeRunEvent::Exit,
  834. &manager,
  835. Some(&mut callback),
  836. );
  837. app_handle.cleanup_before_exit();
  838. }
  839. _ => {
  840. on_event_loop_event(&app_handle, event, &manager, Some(&mut callback));
  841. }
  842. });
  843. }
  844. /// Runs a iteration of the runtime event loop and immediately return.
  845. ///
  846. /// Note that when using this API, app cleanup is not automatically done.
  847. /// The cleanup calls [`App::cleanup_before_exit`] so you may want to call that function before exiting the application.
  848. ///
  849. /// # Examples
  850. /// ```no_run
  851. /// let mut app = tauri::Builder::default()
  852. /// // on an actual app, remove the string argument
  853. /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  854. /// .expect("error while building tauri application");
  855. /// loop {
  856. /// let iteration = app.run_iteration();
  857. /// if iteration.window_count == 0 {
  858. /// app.cleanup_before_exit();
  859. /// break;
  860. /// }
  861. /// }
  862. /// ```
  863. #[cfg(desktop)]
  864. pub fn run_iteration(&mut self) -> crate::runtime::RunIteration {
  865. let manager = self.manager.clone();
  866. let app_handle = self.handle().clone();
  867. self.runtime.as_mut().unwrap().run_iteration(move |event| {
  868. on_event_loop_event(
  869. &app_handle,
  870. event,
  871. &manager,
  872. Option::<&mut Box<dyn FnMut(&AppHandle<R>, RunEvent)>>::None,
  873. )
  874. })
  875. }
  876. }
  877. /// Builds a Tauri application.
  878. ///
  879. /// # Examples
  880. /// ```,no_run
  881. /// tauri::Builder::default()
  882. /// // on an actual app, remove the string argument
  883. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  884. /// .expect("error while running tauri application");
  885. /// ```
  886. #[allow(clippy::type_complexity)]
  887. pub struct Builder<R: Runtime> {
  888. /// A flag indicating that the runtime must be started on an environment that supports the event loop not on the main thread.
  889. #[cfg(any(windows, target_os = "linux"))]
  890. runtime_any_thread: bool,
  891. /// The JS message handler.
  892. invoke_handler: Box<InvokeHandler<R>>,
  893. /// The JS message responder.
  894. invoke_responder: Option<Arc<InvokeResponder<R>>>,
  895. /// The script that initializes the `window.__TAURI_INTERNALS__.postMessage` function.
  896. invoke_initialization_script: String,
  897. /// The setup hook.
  898. setup: SetupHook<R>,
  899. /// Page load hook.
  900. on_page_load: Box<OnPageLoad<R>>,
  901. /// windows to create when starting up.
  902. pending_windows: Vec<PendingWindow<EventLoopMessage, R>>,
  903. /// All passed plugins
  904. plugins: PluginStore<R>,
  905. /// The webview protocols available to all windows.
  906. uri_scheme_protocols: HashMap<String, Arc<UriSchemeProtocol<R>>>,
  907. /// App state.
  908. state: StateManager,
  909. /// A closure that returns the menu set to all windows.
  910. #[cfg(desktop)]
  911. menu: Option<Box<dyn FnOnce(&AppHandle<R>) -> crate::Result<Menu<R>> + Send>>,
  912. /// Enable macOS default menu creation.
  913. #[allow(unused)]
  914. enable_macos_default_menu: bool,
  915. /// Window event handlers that listens to all windows.
  916. window_event_listeners: Vec<GlobalWindowEventListener<R>>,
  917. /// The device event filter.
  918. device_event_filter: DeviceEventFilter,
  919. }
  920. #[derive(Template)]
  921. #[default_template("../scripts/ipc-protocol.js")]
  922. struct InvokeInitializationScript<'a> {
  923. /// The function that processes the IPC message.
  924. #[raw]
  925. process_ipc_message_fn: &'a str,
  926. os_name: &'a str,
  927. fetch_channel_data_command: &'a str,
  928. use_custom_protocol: bool,
  929. }
  930. impl<R: Runtime> Builder<R> {
  931. /// Creates a new App builder.
  932. pub fn new() -> Self {
  933. Self {
  934. #[cfg(any(windows, target_os = "linux"))]
  935. runtime_any_thread: false,
  936. setup: Box::new(|_| Ok(())),
  937. invoke_handler: Box::new(|_| false),
  938. invoke_responder: None,
  939. invoke_initialization_script: InvokeInitializationScript {
  940. process_ipc_message_fn: crate::manager::PROCESS_IPC_MESSAGE_FN,
  941. os_name: std::env::consts::OS,
  942. fetch_channel_data_command: crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND,
  943. use_custom_protocol: cfg!(ipc_custom_protocol),
  944. }
  945. .render_default(&Default::default())
  946. .unwrap()
  947. .into_string(),
  948. on_page_load: Box::new(|_, _| ()),
  949. pending_windows: Default::default(),
  950. plugins: PluginStore::default(),
  951. uri_scheme_protocols: Default::default(),
  952. state: StateManager::new(),
  953. #[cfg(desktop)]
  954. menu: None,
  955. enable_macos_default_menu: true,
  956. window_event_listeners: Vec::new(),
  957. device_event_filter: Default::default(),
  958. }
  959. }
  960. /// Builds a new Tauri application running on any thread, bypassing the main thread requirement.
  961. ///
  962. /// ## Platform-specific
  963. ///
  964. /// - **macOS:** on macOS the application *must* be executed on the main thread, so this function is not exposed.
  965. #[cfg(any(windows, target_os = "linux"))]
  966. #[cfg_attr(doc_cfg, doc(cfg(any(windows, target_os = "linux"))))]
  967. #[must_use]
  968. pub fn any_thread(mut self) -> Self {
  969. self.runtime_any_thread = true;
  970. self
  971. }
  972. /// Defines the JS message handler callback.
  973. ///
  974. /// # Examples
  975. /// ```
  976. /// #[tauri::command]
  977. /// fn command_1() -> String {
  978. /// return "hello world".to_string();
  979. /// }
  980. /// tauri::Builder::default()
  981. /// .invoke_handler(tauri::generate_handler![
  982. /// command_1,
  983. /// // etc...
  984. /// ]);
  985. /// ```
  986. #[must_use]
  987. pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
  988. where
  989. F: Fn(Invoke<R>) -> bool + Send + Sync + 'static,
  990. {
  991. self.invoke_handler = Box::new(invoke_handler);
  992. self
  993. }
  994. /// Defines a custom JS message system.
  995. ///
  996. /// The `responder` is a function that will be called when a command has been executed and must send a response to the JS layer.
  997. ///
  998. /// The `initialization_script` is a script that initializes `window.__TAURI_INTERNALS__.postMessage`.
  999. /// That function must take the `(message: object, options: object)` arguments and send it to the backend.
  1000. #[must_use]
  1001. pub fn invoke_system<F>(mut self, initialization_script: String, responder: F) -> Self
  1002. where
  1003. F: Fn(&Window<R>, &str, &InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static,
  1004. {
  1005. self.invoke_initialization_script = initialization_script;
  1006. self.invoke_responder.replace(Arc::new(responder));
  1007. self
  1008. }
  1009. /// Defines the setup hook.
  1010. ///
  1011. /// # Examples
  1012. /// ```
  1013. /// use tauri::Manager;
  1014. /// tauri::Builder::default()
  1015. /// .setup(|app| {
  1016. /// let main_window = app.get_window("main").unwrap();
  1017. /// main_window.set_title("Tauri!");
  1018. /// Ok(())
  1019. /// });
  1020. /// ```
  1021. #[must_use]
  1022. pub fn setup<F>(mut self, setup: F) -> Self
  1023. where
  1024. F: FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send + 'static,
  1025. {
  1026. self.setup = Box::new(setup);
  1027. self
  1028. }
  1029. /// Defines the page load hook.
  1030. #[must_use]
  1031. pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
  1032. where
  1033. F: Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static,
  1034. {
  1035. self.on_page_load = Box::new(on_page_load);
  1036. self
  1037. }
  1038. /// Adds a Tauri application plugin.
  1039. ///
  1040. /// A plugin is created using the [`crate::plugin::Builder`] struct.Check its documentation for more information.
  1041. ///
  1042. /// # Examples
  1043. ///
  1044. /// ```
  1045. /// mod plugin {
  1046. /// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin}, RunEvent, Runtime};
  1047. ///
  1048. /// // this command can be called in the frontend using `invoke('plugin:window|do_something')`.
  1049. /// #[tauri::command]
  1050. /// async fn do_something<R: Runtime>(app: tauri::AppHandle<R>, window: tauri::Window<R>) -> Result<(), String> {
  1051. /// println!("command called");
  1052. /// Ok(())
  1053. /// }
  1054. /// pub fn init<R: Runtime>() -> TauriPlugin<R> {
  1055. /// PluginBuilder::new("window")
  1056. /// .setup(|app, api| {
  1057. /// // initialize the plugin here
  1058. /// Ok(())
  1059. /// })
  1060. /// .on_event(|app, event| {
  1061. /// match event {
  1062. /// RunEvent::Ready => {
  1063. /// println!("app is ready");
  1064. /// }
  1065. /// RunEvent::WindowEvent { label, event, .. } => {
  1066. /// println!("window {} received an event: {:?}", label, event);
  1067. /// }
  1068. /// _ => (),
  1069. /// }
  1070. /// })
  1071. /// .invoke_handler(tauri::generate_handler![do_something])
  1072. /// .build()
  1073. /// }
  1074. /// }
  1075. ///
  1076. /// tauri::Builder::default()
  1077. /// .plugin(plugin::init());
  1078. /// ```
  1079. #[must_use]
  1080. pub fn plugin<P: Plugin<R> + 'static>(mut self, plugin: P) -> Self {
  1081. self.plugins.register(plugin);
  1082. self
  1083. }
  1084. /// Add `state` to the state managed by the application.
  1085. ///
  1086. /// This method can be called any number of times as long as each call
  1087. /// refers to a different `T`.
  1088. ///
  1089. /// Managed state can be retrieved by any command handler via the
  1090. /// [`State`](crate::State) guard. In particular, if a value of type `T`
  1091. /// is managed by Tauri, adding `State<T>` to the list of arguments in a
  1092. /// command handler instructs Tauri to retrieve the managed value.
  1093. /// Additionally, [`state`](crate::Manager#method.state) can be used to retrieve the value manually.
  1094. ///
  1095. /// # Panics
  1096. ///
  1097. /// Panics if state of type `T` is already being managed.
  1098. ///
  1099. /// # Mutability
  1100. ///
  1101. /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
  1102. ///
  1103. /// ```,no_run
  1104. /// use std::{collections::HashMap, sync::Mutex};
  1105. /// use tauri::State;
  1106. /// // here we use Mutex to achieve interior mutability
  1107. /// struct Storage {
  1108. /// store: Mutex<HashMap<u64, String>>,
  1109. /// }
  1110. /// struct Connection;
  1111. /// struct DbConnection {
  1112. /// db: Mutex<Option<Connection>>,
  1113. /// }
  1114. ///
  1115. /// #[tauri::command]
  1116. /// fn connect(connection: State<DbConnection>) {
  1117. /// // initialize the connection, mutating the state with interior mutability
  1118. /// *connection.db.lock().unwrap() = Some(Connection {});
  1119. /// }
  1120. ///
  1121. /// #[tauri::command]
  1122. /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
  1123. /// // mutate the storage behind the Mutex
  1124. /// storage.store.lock().unwrap().insert(key, value);
  1125. /// }
  1126. ///
  1127. /// tauri::Builder::default()
  1128. /// .manage(Storage { store: Default::default() })
  1129. /// .manage(DbConnection { db: Default::default() })
  1130. /// .invoke_handler(tauri::generate_handler![connect, storage_insert])
  1131. /// // on an actual app, remove the string argument
  1132. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  1133. /// .expect("error while running tauri application");
  1134. /// ```
  1135. ///
  1136. /// # Examples
  1137. ///
  1138. /// ```,no_run
  1139. /// use tauri::State;
  1140. ///
  1141. /// struct MyInt(isize);
  1142. /// struct MyString(String);
  1143. ///
  1144. /// #[tauri::command]
  1145. /// fn int_command(state: State<MyInt>) -> String {
  1146. /// format!("The stateful int is: {}", state.0)
  1147. /// }
  1148. ///
  1149. /// #[tauri::command]
  1150. /// fn string_command<'r>(state: State<'r, MyString>) {
  1151. /// println!("state: {}", state.inner().0);
  1152. /// }
  1153. ///
  1154. /// tauri::Builder::default()
  1155. /// .manage(MyInt(10))
  1156. /// .manage(MyString("Hello, managed state!".to_string()))
  1157. /// .invoke_handler(tauri::generate_handler![int_command, string_command])
  1158. /// // on an actual app, remove the string argument
  1159. /// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
  1160. /// .expect("error while running tauri application");
  1161. /// ```
  1162. #[must_use]
  1163. pub fn manage<T>(self, state: T) -> Self
  1164. where
  1165. T: Send + Sync + 'static,
  1166. {
  1167. let type_name = std::any::type_name::<T>();
  1168. assert!(
  1169. self.state.set(state),
  1170. "state for type '{type_name}' is already being managed",
  1171. );
  1172. self
  1173. }
  1174. /// Sets the menu to use on all windows.
  1175. ///
  1176. /// # Examples
  1177. /// ```
  1178. /// use tauri::menu::{Menu, MenuItem, PredefinedMenuItem, Submenu};
  1179. ///
  1180. /// tauri::Builder::default()
  1181. /// .menu(|handle| Menu::with_items(handle, &[
  1182. /// &Submenu::with_items(
  1183. /// handle,
  1184. /// "File",
  1185. /// true,
  1186. /// &[
  1187. /// &PredefinedMenuItem::close_window(handle, None),
  1188. /// #[cfg(target_os = "macos")]
  1189. /// &MenuItem::new(handle, "Hello", true, None),
  1190. /// ],
  1191. /// )?
  1192. /// ]));
  1193. /// ```
  1194. #[must_use]
  1195. #[cfg(desktop)]
  1196. pub fn menu<F: FnOnce(&AppHandle<R>) -> crate::Result<Menu<R>> + Send + 'static>(
  1197. mut self,
  1198. f: F,
  1199. ) -> Self {
  1200. self.menu.replace(Box::new(f));
  1201. self
  1202. }
  1203. /// Enable or disable the default menu on macOS. Enabled by default.
  1204. ///
  1205. /// # Examples
  1206. /// ```
  1207. /// tauri::Builder::default()
  1208. /// .enable_macos_default_menu(false);
  1209. /// ```
  1210. #[must_use]
  1211. pub fn enable_macos_default_menu(mut self, enable: bool) -> Self {
  1212. self.enable_macos_default_menu = enable;
  1213. self
  1214. }
  1215. /// Registers a window event handler for all windows.
  1216. ///
  1217. /// # Examples
  1218. /// ```
  1219. /// tauri::Builder::default()
  1220. /// .on_window_event(|event| match event.event() {
  1221. /// tauri::WindowEvent::Focused(focused) => {
  1222. /// // hide window whenever it loses focus
  1223. /// if !focused {
  1224. /// event.window().hide().unwrap();
  1225. /// }
  1226. /// }
  1227. /// _ => {}
  1228. /// });
  1229. /// ```
  1230. #[must_use]
  1231. pub fn on_window_event<F: Fn(GlobalWindowEvent<R>) + Send + Sync + 'static>(
  1232. mut self,
  1233. handler: F,
  1234. ) -> Self {
  1235. self.window_event_listeners.push(Box::new(handler));
  1236. self
  1237. }
  1238. /// Registers a URI scheme protocol available to all webviews.
  1239. /// Leverages [setURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) on macOS,
  1240. /// [AddWebResourceRequestedFilter](https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addwebresourcerequestedfilter?view=webview2-dotnet-1.0.774.44) on Windows
  1241. /// and [webkit-web-context-register-uri-scheme](https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebContext.html#webkit-web-context-register-uri-scheme) on Linux.
  1242. ///
  1243. /// # Arguments
  1244. ///
  1245. /// * `uri_scheme` The URI scheme to register, such as `example`.
  1246. /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes a request and returns a response.
  1247. ///
  1248. /// # Examples
  1249. /// ```
  1250. /// tauri::Builder::default()
  1251. /// .register_uri_scheme_protocol("app-files", |_app, request| {
  1252. /// // skip leading `/`
  1253. /// if let Ok(data) = std::fs::read(&request.uri().path()[1..]) {
  1254. /// http::Response::builder()
  1255. /// .body(data)
  1256. /// .unwrap()
  1257. /// } else {
  1258. /// http::Response::builder()
  1259. /// .status(http::StatusCode::BAD_REQUEST)
  1260. /// .header(http::header::CONTENT_TYPE, mime::TEXT_PLAIN.essence_str())
  1261. /// .body("failed to read file".as_bytes().to_vec())
  1262. /// .unwrap()
  1263. /// }
  1264. /// });
  1265. /// ```
  1266. #[must_use]
  1267. pub fn register_uri_scheme_protocol<
  1268. N: Into<String>,
  1269. T: Into<Cow<'static, [u8]>>,
  1270. H: Fn(&AppHandle<R>, http::Request<Vec<u8>>) -> http::Response<T> + Send + Sync + 'static,
  1271. >(
  1272. mut self,
  1273. uri_scheme: N,
  1274. protocol: H,
  1275. ) -> Self {
  1276. self.uri_scheme_protocols.insert(
  1277. uri_scheme.into(),
  1278. Arc::new(UriSchemeProtocol {
  1279. protocol: Box::new(move |app, request, responder| {
  1280. responder.respond(protocol(app, request))
  1281. }),
  1282. }),
  1283. );
  1284. self
  1285. }
  1286. /// Similar to [`Self::register_uri_scheme_protocol`] but with an asynchronous responder that allows you
  1287. /// to process the request in a separate thread and respond asynchronously.
  1288. ///
  1289. /// # Arguments
  1290. ///
  1291. /// * `uri_scheme` The URI scheme to register, such as `example`.
  1292. /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
  1293. ///
  1294. /// # Examples
  1295. /// ```
  1296. /// tauri::Builder::default()
  1297. /// .register_asynchronous_uri_scheme_protocol("app-files", |_app, request, responder| {
  1298. /// // skip leading `/`
  1299. /// let path = request.uri().path()[1..].to_string();
  1300. /// std::thread::spawn(move || {
  1301. /// if let Ok(data) = std::fs::read(path) {
  1302. /// responder.respond(
  1303. /// http::Response::builder()
  1304. /// .body(data)
  1305. /// .unwrap()
  1306. /// );
  1307. /// } else {
  1308. /// responder.respond(
  1309. /// http::Response::builder()
  1310. /// .status(http::StatusCode::BAD_REQUEST)
  1311. /// .header(http::header::CONTENT_TYPE, mime::TEXT_PLAIN.essence_str())
  1312. /// .body("failed to read file".as_bytes().to_vec())
  1313. /// .unwrap()
  1314. /// );
  1315. /// }
  1316. /// });
  1317. /// });
  1318. /// ```
  1319. #[must_use]
  1320. pub fn register_asynchronous_uri_scheme_protocol<
  1321. N: Into<String>,
  1322. H: Fn(&AppHandle<R>, http::Request<Vec<u8>>, UriSchemeResponder) + Send + Sync + 'static,
  1323. >(
  1324. mut self,
  1325. uri_scheme: N,
  1326. protocol: H,
  1327. ) -> Self {
  1328. self.uri_scheme_protocols.insert(
  1329. uri_scheme.into(),
  1330. Arc::new(UriSchemeProtocol {
  1331. protocol: Box::new(protocol),
  1332. }),
  1333. );
  1334. self
  1335. }
  1336. /// Change the device event filter mode.
  1337. ///
  1338. /// Since the DeviceEvent capture can lead to high CPU usage for unfocused windows, [`tao`]
  1339. /// will ignore them by default for unfocused windows on Windows. This method allows changing
  1340. /// the filter to explicitly capture them again.
  1341. ///
  1342. /// ## Platform-specific
  1343. ///
  1344. /// - ** Linux / macOS / iOS / Android**: Unsupported.
  1345. ///
  1346. /// # Examples
  1347. /// ```,no_run
  1348. /// tauri::Builder::default()
  1349. /// .device_event_filter(tauri::DeviceEventFilter::Always);
  1350. /// ```
  1351. ///
  1352. /// [`tao`]: https://crates.io/crates/tao
  1353. pub fn device_event_filter(mut self, filter: DeviceEventFilter) -> Self {
  1354. self.device_event_filter = filter;
  1355. self
  1356. }
  1357. /// Builds the application.
  1358. #[allow(clippy::type_complexity)]
  1359. pub fn build<A: Assets>(mut self, context: Context<A>) -> crate::Result<App<R>> {
  1360. #[cfg(target_os = "macos")]
  1361. if self.menu.is_none() && self.enable_macos_default_menu {
  1362. self.menu = Some(Box::new(|app_handle| {
  1363. crate::menu::Menu::default(app_handle)
  1364. }));
  1365. }
  1366. let manager = WindowManager::with_handlers(
  1367. context,
  1368. self.plugins,
  1369. self.invoke_handler,
  1370. self.on_page_load,
  1371. self.uri_scheme_protocols,
  1372. self.state,
  1373. self.window_event_listeners,
  1374. #[cfg(desktop)]
  1375. HashMap::new(),
  1376. (self.invoke_responder, self.invoke_initialization_script),
  1377. );
  1378. // set up all the windows defined in the config
  1379. for config in manager.config().tauri.windows.clone() {
  1380. let label = config.label.clone();
  1381. let webview_attributes = WebviewAttributes::from(&config);
  1382. self.pending_windows.push(PendingWindow::with_config(
  1383. config,
  1384. webview_attributes,
  1385. label,
  1386. )?);
  1387. }
  1388. let runtime_args = RuntimeInitArgs {
  1389. #[cfg(windows)]
  1390. msg_hook: {
  1391. let menus = manager.inner.menus.clone();
  1392. Some(Box::new(move |msg| {
  1393. use windows::Win32::UI::WindowsAndMessaging::{TranslateAcceleratorW, HACCEL, MSG};
  1394. unsafe {
  1395. let msg = msg as *const MSG;
  1396. for menu in menus.lock().unwrap().values() {
  1397. let translated =
  1398. TranslateAcceleratorW((*msg).hwnd, HACCEL(menu.inner().haccel()), msg);
  1399. if translated == 1 {
  1400. return true;
  1401. }
  1402. }
  1403. false
  1404. }
  1405. }))
  1406. },
  1407. };
  1408. #[cfg(any(windows, target_os = "linux"))]
  1409. let mut runtime = if self.runtime_any_thread {
  1410. R::new_any_thread(runtime_args)?
  1411. } else {
  1412. R::new(runtime_args)?
  1413. };
  1414. #[cfg(not(any(windows, target_os = "linux")))]
  1415. let mut runtime = R::new(runtime_args)?;
  1416. #[cfg(desktop)]
  1417. {
  1418. // setup menu event handler
  1419. let proxy = runtime.create_proxy();
  1420. muda::MenuEvent::set_event_handler(Some(move |e: muda::MenuEvent| {
  1421. let _ = proxy.send_event(EventLoopMessage::MenuEvent(e.into()));
  1422. }));
  1423. // setup tray event handler
  1424. #[cfg(feature = "tray-icon")]
  1425. {
  1426. let proxy = runtime.create_proxy();
  1427. tray_icon::TrayIconEvent::set_event_handler(Some(move |e: tray_icon::TrayIconEvent| {
  1428. let _ = proxy.send_event(EventLoopMessage::TrayIconEvent(e.into()));
  1429. }));
  1430. }
  1431. }
  1432. runtime.set_device_event_filter(self.device_event_filter);
  1433. let runtime_handle = runtime.handle();
  1434. #[allow(unused_mut)]
  1435. let mut app = App {
  1436. runtime: Some(runtime),
  1437. pending_windows: Some(self.pending_windows),
  1438. setup: Some(self.setup),
  1439. manager: manager.clone(),
  1440. handle: AppHandle {
  1441. runtime_handle,
  1442. manager,
  1443. },
  1444. };
  1445. #[cfg(desktop)]
  1446. if let Some(menu) = self.menu {
  1447. let menu = menu(&app.handle)?;
  1448. app
  1449. .manager
  1450. .menus_stash_lock()
  1451. .insert(menu.id().clone(), menu.clone());
  1452. #[cfg(target_os = "macos")]
  1453. init_app_menu(&menu)?;
  1454. app.manager.menu_lock().replace(menu);
  1455. }
  1456. app.register_core_plugins()?;
  1457. let env = Env::default();
  1458. app.manage(env);
  1459. app.manage(Scopes {
  1460. ipc: scope::ipc::Scope::new(&app.config()),
  1461. #[cfg(feature = "protocol-asset")]
  1462. asset_protocol: scope::fs::Scope::for_fs_api(
  1463. &app,
  1464. &app.config().tauri.security.asset_protocol.scope,
  1465. )?,
  1466. });
  1467. app.manage(ChannelDataIpcQueue::default());
  1468. app.handle.plugin(crate::ipc::channel::plugin())?;
  1469. #[cfg(windows)]
  1470. {
  1471. if let crate::utils::config::WebviewInstallMode::FixedRuntime { path } = &app
  1472. .manager
  1473. .config()
  1474. .tauri
  1475. .bundle
  1476. .windows
  1477. .webview_install_mode
  1478. {
  1479. if let Ok(resource_dir) = app.path().resource_dir() {
  1480. std::env::set_var(
  1481. "WEBVIEW2_BROWSER_EXECUTABLE_FOLDER",
  1482. resource_dir.join(path),
  1483. );
  1484. } else {
  1485. #[cfg(debug_assertions)]
  1486. eprintln!(
  1487. "failed to resolve resource directory; fallback to the installed Webview2 runtime."
  1488. );
  1489. }
  1490. }
  1491. }
  1492. let handle = app.handle();
  1493. // initialize default tray icon if defined
  1494. #[cfg(all(desktop, feature = "tray-icon"))]
  1495. {
  1496. let config = app.config();
  1497. if let Some(tray_config) = &config.tauri.tray_icon {
  1498. let mut tray =
  1499. TrayIconBuilder::with_id(tray_config.id.clone().unwrap_or_else(|| "main".into()))
  1500. .icon_as_template(tray_config.icon_as_template)
  1501. .menu_on_left_click(tray_config.menu_on_left_click);
  1502. if let Some(icon) = &app.manager.inner.tray_icon {
  1503. tray = tray.icon(icon.clone());
  1504. }
  1505. if let Some(title) = &tray_config.title {
  1506. tray = tray.title(title);
  1507. }
  1508. if let Some(tooltip) = &tray_config.tooltip {
  1509. tray = tray.tooltip(tooltip);
  1510. }
  1511. let tray = tray.build(handle)?;
  1512. app.manager.inner.tray_icons.lock().unwrap().push(tray);
  1513. }
  1514. }
  1515. app.manager.initialize_plugins(handle)?;
  1516. Ok(app)
  1517. }
  1518. /// Runs the configured Tauri application.
  1519. pub fn run<A: Assets>(self, context: Context<A>) -> crate::Result<()> {
  1520. self.build(context)?.run(|_, _| {});
  1521. Ok(())
  1522. }
  1523. }
  1524. pub(crate) type UriSchemeResponderFn = Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>;
  1525. pub struct UriSchemeResponder(pub(crate) UriSchemeResponderFn);
  1526. impl UriSchemeResponder {
  1527. /// Resolves the request with the given response.
  1528. pub fn respond<T: Into<Cow<'static, [u8]>>>(self, response: http::Response<T>) {
  1529. let (parts, body) = response.into_parts();
  1530. (self.0)(http::Response::from_parts(parts, body.into()))
  1531. }
  1532. }
  1533. #[cfg(target_os = "macos")]
  1534. fn init_app_menu<R: Runtime>(menu: &Menu<R>) -> crate::Result<()> {
  1535. menu.inner().init_for_nsapp();
  1536. if let Some(window_menu) = menu.get(crate::menu::WINDOW_SUBMENU_ID) {
  1537. if let Some(m) = window_menu.as_submenu() {
  1538. m.set_as_windows_menu_for_nsapp()?;
  1539. }
  1540. }
  1541. if let Some(help_menu) = menu.get(crate::menu::HELP_SUBMENU_ID) {
  1542. if let Some(m) = help_menu.as_submenu() {
  1543. m.set_as_help_menu_for_nsapp()?;
  1544. }
  1545. }
  1546. Ok(())
  1547. }
  1548. unsafe impl<R: Runtime> HasRawDisplayHandle for AppHandle<R> {
  1549. fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
  1550. self.runtime_handle.raw_display_handle()
  1551. }
  1552. }
  1553. unsafe impl<R: Runtime> HasRawDisplayHandle for App<R> {
  1554. fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
  1555. self.handle.raw_display_handle()
  1556. }
  1557. }
  1558. fn setup<R: Runtime>(app: &mut App<R>) -> crate::Result<()> {
  1559. let pending_windows = app.pending_windows.take();
  1560. if let Some(pending_windows) = pending_windows {
  1561. let window_labels = pending_windows
  1562. .iter()
  1563. .map(|p| p.label.clone())
  1564. .collect::<Vec<_>>();
  1565. let app_handle = app.handle();
  1566. let manager = app.manager();
  1567. for pending in pending_windows {
  1568. let pending = manager.prepare_window(app_handle.clone(), pending, &window_labels)?;
  1569. #[cfg(desktop)]
  1570. let window_menu = app.manager.menu_lock().as_ref().map(|m| WindowMenu {
  1571. is_app_wide: true,
  1572. menu: m.clone(),
  1573. });
  1574. #[cfg(desktop)]
  1575. let handler = manager.prepare_window_menu_creation_handler(window_menu.as_ref());
  1576. #[cfg(not(desktop))]
  1577. #[allow(clippy::type_complexity)]
  1578. let handler: Option<Box<dyn Fn(tauri_runtime::window::RawWindow<'_>) + Send>> = None;
  1579. let window_effects = pending.webview_attributes.window_effects.clone();
  1580. let detached = if let RuntimeOrDispatch::RuntimeHandle(runtime) = app_handle.runtime() {
  1581. runtime.create_window(pending, handler)?
  1582. } else {
  1583. // the AppHandle's runtime is always RuntimeOrDispatch::RuntimeHandle
  1584. unreachable!()
  1585. };
  1586. let window = manager.attach_window(
  1587. app_handle.clone(),
  1588. detached,
  1589. #[cfg(desktop)]
  1590. None,
  1591. );
  1592. if let Some(effects) = window_effects {
  1593. crate::vibrancy::set_window_effects(&window, Some(effects))?;
  1594. }
  1595. }
  1596. }
  1597. if let Some(setup) = app.setup.take() {
  1598. (setup)(app).map_err(|e| crate::Error::Setup(e.into()))?;
  1599. }
  1600. Ok(())
  1601. }
  1602. fn on_event_loop_event<R: Runtime, F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
  1603. app_handle: &AppHandle<R>,
  1604. event: RuntimeRunEvent<EventLoopMessage>,
  1605. manager: &WindowManager<R>,
  1606. callback: Option<&mut F>,
  1607. ) {
  1608. if let RuntimeRunEvent::WindowEvent {
  1609. label,
  1610. event: RuntimeWindowEvent::Destroyed,
  1611. } = &event
  1612. {
  1613. manager.on_window_close(label);
  1614. }
  1615. let event = match event {
  1616. RuntimeRunEvent::Exit => RunEvent::Exit,
  1617. RuntimeRunEvent::ExitRequested { tx } => RunEvent::ExitRequested {
  1618. api: ExitRequestApi(tx),
  1619. },
  1620. RuntimeRunEvent::WindowEvent { label, event } => RunEvent::WindowEvent {
  1621. label,
  1622. event: event.into(),
  1623. },
  1624. RuntimeRunEvent::Ready => {
  1625. // set the app icon in development
  1626. #[cfg(all(dev, target_os = "macos"))]
  1627. unsafe {
  1628. use cocoa::{
  1629. appkit::NSImage,
  1630. base::{id, nil},
  1631. foundation::NSData,
  1632. };
  1633. use objc::*;
  1634. if let Some(icon) = app_handle.manager.inner.app_icon.clone() {
  1635. let ns_app: id = msg_send![class!(NSApplication), sharedApplication];
  1636. let data = NSData::dataWithBytes_length_(
  1637. nil,
  1638. icon.as_ptr() as *const std::os::raw::c_void,
  1639. icon.len() as u64,
  1640. );
  1641. let app_icon = NSImage::initWithData_(NSImage::alloc(nil), data);
  1642. let _: () = msg_send![ns_app, setApplicationIconImage: app_icon];
  1643. }
  1644. }
  1645. RunEvent::Ready
  1646. }
  1647. RuntimeRunEvent::Resumed => RunEvent::Resumed,
  1648. RuntimeRunEvent::MainEventsCleared => RunEvent::MainEventsCleared,
  1649. RuntimeRunEvent::UserEvent(t) => {
  1650. match t {
  1651. #[cfg(desktop)]
  1652. EventLoopMessage::MenuEvent(ref e) => {
  1653. for listener in &*app_handle
  1654. .manager
  1655. .inner
  1656. .menu_event_listeners
  1657. .lock()
  1658. .unwrap()
  1659. {
  1660. listener(app_handle, e.clone());
  1661. }
  1662. for (label, listener) in &*app_handle
  1663. .manager
  1664. .inner
  1665. .window_menu_event_listeners
  1666. .lock()
  1667. .unwrap()
  1668. {
  1669. if let Some(w) = app_handle.get_window(label) {
  1670. listener(&w, e.clone());
  1671. }
  1672. }
  1673. }
  1674. #[cfg(all(desktop, feature = "tray-icon"))]
  1675. EventLoopMessage::TrayIconEvent(ref e) => {
  1676. for listener in &*app_handle
  1677. .manager
  1678. .inner
  1679. .global_tray_event_listeners
  1680. .lock()
  1681. .unwrap()
  1682. {
  1683. listener(app_handle, e.clone());
  1684. }
  1685. for (id, listener) in &*app_handle
  1686. .manager
  1687. .inner
  1688. .tray_event_listeners
  1689. .lock()
  1690. .unwrap()
  1691. {
  1692. if e.id == id {
  1693. if let Some(tray) = app_handle.tray_by_id(id) {
  1694. listener(&tray, e.clone());
  1695. }
  1696. }
  1697. }
  1698. }
  1699. }
  1700. #[allow(unreachable_code)]
  1701. t.into()
  1702. }
  1703. #[cfg(any(target_os = "macos", target_os = "ios"))]
  1704. RuntimeRunEvent::Opened { urls } => RunEvent::Opened { urls },
  1705. _ => unimplemented!(),
  1706. };
  1707. manager
  1708. .inner
  1709. .plugins
  1710. .lock()
  1711. .expect("poisoned plugin store")
  1712. .on_event(app_handle, &event);
  1713. if let Some(c) = callback {
  1714. c(app_handle, event);
  1715. }
  1716. }
  1717. /// Make `Wry` the default `Runtime` for `Builder`
  1718. #[cfg(feature = "wry")]
  1719. #[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
  1720. impl Default for Builder<crate::Wry> {
  1721. fn default() -> Self {
  1722. Self::new()
  1723. }
  1724. }
  1725. #[cfg(test)]
  1726. mod tests {
  1727. #[test]
  1728. fn is_send_sync() {
  1729. crate::test_utils::assert_send::<super::AppHandle>();
  1730. crate::test_utils::assert_sync::<super::AppHandle>();
  1731. #[cfg(feature = "wry")]
  1732. {
  1733. crate::test_utils::assert_send::<super::AssetResolver<crate::Wry>>();
  1734. crate::test_utils::assert_sync::<super::AssetResolver<crate::Wry>>();
  1735. }
  1736. }
  1737. }