manager.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use crate::{
  5. api::{
  6. assets::Assets,
  7. config::{Config, WindowUrl},
  8. path::{resolve_path, BaseDirectory},
  9. PackageInfo,
  10. },
  11. app::{GlobalMenuEventListener, WindowMenuEvent},
  12. event::{Event, EventHandler, Listeners},
  13. hooks::{InvokeHandler, OnPageLoad, PageLoadPayload},
  14. plugin::PluginStore,
  15. runtime::{
  16. menu::{Menu, MenuId, MenuItem},
  17. private::ParamsBase,
  18. tag::{tags_to_javascript_array, Tag, TagRef, ToJsString},
  19. webview::{
  20. CustomProtocol, FileDropEvent, FileDropHandler, InvokePayload, WebviewRpcHandler,
  21. WindowBuilder,
  22. },
  23. window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent},
  24. Icon, Params, Runtime,
  25. },
  26. App, Context, Invoke, MenuEvent, StateManager, Window,
  27. };
  28. use serde::Serialize;
  29. use serde_json::Value as JsonValue;
  30. use std::borrow::Borrow;
  31. use std::marker::PhantomData;
  32. use std::{
  33. borrow::Cow,
  34. collections::{HashMap, HashSet},
  35. fs::create_dir_all,
  36. sync::{Arc, Mutex, MutexGuard},
  37. };
  38. use uuid::Uuid;
  39. const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
  40. const WINDOW_MOVED_EVENT: &str = "tauri://move";
  41. const WINDOW_CLOSE_REQUESTED_EVENT: &str = "tauri://close-requested";
  42. const WINDOW_DESTROYED_EVENT: &str = "tauri://destroyed";
  43. const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
  44. const WINDOW_BLUR_EVENT: &str = "tauri://blur";
  45. const WINDOW_SCALE_FACTOR_CHANGED_EVENT: &str = "tauri://scale-change";
  46. const MENU_EVENT: &str = "tauri://menu";
  47. /// Parse a string representing an internal tauri event into [`Params::Event`]
  48. ///
  49. /// # Panics
  50. ///
  51. /// This will panic if the `FromStr` implementation of [`Params::Event`] returns an error.
  52. pub(crate) fn tauri_event<Event: Tag>(tauri_event: &str) -> Event {
  53. tauri_event.parse().unwrap_or_else(|_| {
  54. panic!(
  55. "failed to parse internal tauri event into Params::Event: {}",
  56. tauri_event
  57. )
  58. })
  59. }
  60. pub struct InnerWindowManager<P: Params> {
  61. windows: Mutex<HashMap<P::Label, Window<P>>>,
  62. plugins: Mutex<PluginStore<P>>,
  63. listeners: Listeners<P::Event, P::Label>,
  64. pub(crate) state: Arc<StateManager>,
  65. /// The JS message handler.
  66. invoke_handler: Box<InvokeHandler<P>>,
  67. /// The page load hook, invoked when the webview performs a navigation.
  68. on_page_load: Box<OnPageLoad<P>>,
  69. config: Arc<Config>,
  70. assets: Arc<P::Assets>,
  71. default_window_icon: Option<Vec<u8>>,
  72. /// A list of salts that are valid for the current application.
  73. salts: Mutex<HashSet<Uuid>>,
  74. package_info: PackageInfo,
  75. /// The webview protocols protocols available to all windows.
  76. uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
  77. /// The menu set to all windows.
  78. menu: Vec<Menu<P::MenuId>>,
  79. /// Menu event listeners to all windows.
  80. menu_event_listeners: Arc<Vec<GlobalMenuEventListener<P>>>,
  81. menu_ids: HashMap<u32, P::MenuId>,
  82. }
  83. /// A [Zero Sized Type] marker representing a full [`Params`].
  84. ///
  85. /// [Zero Sized Type]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts
  86. pub struct Args<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> {
  87. _event: PhantomData<fn() -> E>,
  88. _label: PhantomData<fn() -> L>,
  89. _menu_id: PhantomData<fn() -> MID>,
  90. _tray_menu_id: PhantomData<fn() -> TID>,
  91. _assets: PhantomData<fn() -> A>,
  92. _runtime: PhantomData<fn() -> R>,
  93. }
  94. impl<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> Default
  95. for Args<E, L, MID, TID, A, R>
  96. {
  97. fn default() -> Self {
  98. Self {
  99. _event: PhantomData,
  100. _label: PhantomData,
  101. _menu_id: PhantomData,
  102. _tray_menu_id: PhantomData,
  103. _assets: PhantomData,
  104. _runtime: PhantomData,
  105. }
  106. }
  107. }
  108. impl<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> ParamsBase
  109. for Args<E, L, MID, TID, A, R>
  110. {
  111. }
  112. impl<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> Params
  113. for Args<E, L, MID, TID, A, R>
  114. {
  115. type Event = E;
  116. type Label = L;
  117. type MenuId = MID;
  118. type SystemTrayMenuId = TID;
  119. type Assets = A;
  120. type Runtime = R;
  121. }
  122. pub struct WindowManager<P: Params> {
  123. pub inner: Arc<InnerWindowManager<P>>,
  124. #[allow(clippy::type_complexity)]
  125. _marker: Args<P::Event, P::Label, P::MenuId, P::SystemTrayMenuId, P::Assets, P::Runtime>,
  126. }
  127. impl<P: Params> Clone for WindowManager<P> {
  128. fn clone(&self) -> Self {
  129. Self {
  130. inner: self.inner.clone(),
  131. _marker: Args::default(),
  132. }
  133. }
  134. }
  135. fn get_menu_ids<I: MenuId>(menu: &[Menu<I>]) -> HashMap<u32, I> {
  136. let mut map = HashMap::new();
  137. for m in menu {
  138. for item in &m.items {
  139. if let MenuItem::Custom(i) = item {
  140. map.insert(i.id_value(), i.id.clone());
  141. }
  142. }
  143. }
  144. map
  145. }
  146. impl<P: Params> WindowManager<P> {
  147. #[allow(clippy::too_many_arguments)]
  148. pub(crate) fn with_handlers(
  149. context: Context<P::Assets>,
  150. plugins: PluginStore<P>,
  151. invoke_handler: Box<InvokeHandler<P>>,
  152. on_page_load: Box<OnPageLoad<P>>,
  153. uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
  154. state: StateManager,
  155. menu: Vec<Menu<P::MenuId>>,
  156. menu_event_listeners: Vec<GlobalMenuEventListener<P>>,
  157. ) -> Self {
  158. let menu_ids = get_menu_ids(&menu);
  159. Self {
  160. inner: Arc::new(InnerWindowManager {
  161. windows: Mutex::default(),
  162. plugins: Mutex::new(plugins),
  163. listeners: Listeners::default(),
  164. state: Arc::new(state),
  165. invoke_handler,
  166. on_page_load,
  167. config: Arc::new(context.config),
  168. assets: context.assets,
  169. default_window_icon: context.default_window_icon,
  170. salts: Mutex::default(),
  171. package_info: context.package_info,
  172. uri_scheme_protocols,
  173. menu,
  174. menu_event_listeners: Arc::new(menu_event_listeners),
  175. menu_ids,
  176. }),
  177. _marker: Args::default(),
  178. }
  179. }
  180. /// Get a locked handle to the windows.
  181. pub(crate) fn windows_lock(&self) -> MutexGuard<'_, HashMap<P::Label, Window<P>>> {
  182. self.inner.windows.lock().expect("poisoned window manager")
  183. }
  184. /// State managed by the application.
  185. pub(crate) fn state(&self) -> Arc<StateManager> {
  186. self.inner.state.clone()
  187. }
  188. /// Get the menu ids mapper.
  189. pub(crate) fn menu_ids(&self) -> HashMap<u32, P::MenuId> {
  190. self.inner.menu_ids.clone()
  191. }
  192. // setup content for dev-server
  193. #[cfg(dev)]
  194. fn get_url(&self) -> String {
  195. if self.inner.config.build.dev_path.starts_with("http") {
  196. self.inner.config.build.dev_path.clone()
  197. } else {
  198. "tauri://localhost".into()
  199. }
  200. }
  201. #[cfg(custom_protocol)]
  202. fn get_url(&self) -> String {
  203. "tauri://localhost".into()
  204. }
  205. fn prepare_pending_window(
  206. &self,
  207. mut pending: PendingWindow<P>,
  208. label: P::Label,
  209. pending_labels: &[P::Label],
  210. ) -> crate::Result<PendingWindow<P>> {
  211. let is_init_global = self.inner.config.build.with_global_tauri;
  212. let plugin_init = self
  213. .inner
  214. .plugins
  215. .lock()
  216. .expect("poisoned plugin store")
  217. .initialization_script();
  218. let mut webview_attributes = pending.webview_attributes
  219. .initialization_script(&self.initialization_script(&plugin_init, is_init_global))
  220. .initialization_script(&format!(
  221. r#"
  222. window.__TAURI__.__windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }});
  223. window.__TAURI__.__currentWindow = {{ label: {current_window_label} }}
  224. "#,
  225. window_labels_array = tags_to_javascript_array(pending_labels)?,
  226. current_window_label = label.to_js_string()?,
  227. ));
  228. if !pending.window_builder.has_icon() {
  229. if let Some(default_window_icon) = &self.inner.default_window_icon {
  230. let icon = Icon::Raw(default_window_icon.clone());
  231. pending.window_builder = pending.window_builder.icon(icon)?;
  232. }
  233. }
  234. if !pending.window_builder.has_menu() {
  235. pending.window_builder = pending.window_builder.menu(self.inner.menu.clone());
  236. }
  237. for (uri_scheme, protocol) in &self.inner.uri_scheme_protocols {
  238. if !webview_attributes.has_uri_scheme_protocol(uri_scheme) {
  239. let protocol = protocol.clone();
  240. webview_attributes = webview_attributes
  241. .register_uri_scheme_protocol(uri_scheme.clone(), move |p| (protocol.protocol)(p));
  242. }
  243. }
  244. if !webview_attributes.has_uri_scheme_protocol("tauri") {
  245. webview_attributes = webview_attributes
  246. .register_uri_scheme_protocol("tauri", self.prepare_uri_scheme_protocol().protocol);
  247. }
  248. let local_app_data = resolve_path(
  249. &self.inner.config,
  250. &self.inner.config.tauri.bundle.identifier,
  251. Some(BaseDirectory::LocalData),
  252. );
  253. if let Ok(user_data_dir) = local_app_data {
  254. // Make sure the directory exist without panic
  255. if create_dir_all(&user_data_dir).is_ok() {
  256. webview_attributes = webview_attributes.data_directory(user_data_dir);
  257. }
  258. }
  259. pending.webview_attributes = webview_attributes;
  260. Ok(pending)
  261. }
  262. fn prepare_rpc_handler(&self) -> WebviewRpcHandler<P> {
  263. let manager = self.clone();
  264. Box::new(move |window, request| {
  265. let window = Window::new(manager.clone(), window);
  266. let command = request.command.clone();
  267. let arg = request
  268. .params
  269. .unwrap()
  270. .as_array_mut()
  271. .unwrap()
  272. .first_mut()
  273. .unwrap_or(&mut JsonValue::Null)
  274. .take();
  275. match serde_json::from_value::<InvokePayload>(arg) {
  276. Ok(message) => {
  277. let _ = window.on_message(command, message);
  278. }
  279. Err(e) => {
  280. let error: crate::Error = e.into();
  281. let _ = window.eval(&format!(
  282. r#"console.error({})"#,
  283. JsonValue::String(error.to_string())
  284. ));
  285. }
  286. }
  287. })
  288. }
  289. fn prepare_uri_scheme_protocol(&self) -> CustomProtocol {
  290. let assets = self.inner.assets.clone();
  291. CustomProtocol {
  292. protocol: Box::new(move |path| {
  293. let mut path = path
  294. .split('?')
  295. // ignore query string
  296. .next()
  297. .unwrap()
  298. .to_string()
  299. .replace("tauri://localhost", "");
  300. if path.ends_with('/') {
  301. path.pop();
  302. }
  303. let path = if path.is_empty() {
  304. // if the url is `tauri://localhost`, we should load `index.html`
  305. "index.html".to_string()
  306. } else {
  307. // skip leading `/`
  308. path.chars().skip(1).collect::<String>()
  309. };
  310. let asset_response = assets
  311. .get(&path)
  312. .ok_or(crate::Error::AssetNotFound(path))
  313. .map(Cow::into_owned);
  314. match asset_response {
  315. Ok(asset) => Ok(asset),
  316. Err(e) => {
  317. #[cfg(debug_assertions)]
  318. eprintln!("{:?}", e); // TODO log::error!
  319. Err(Box::new(e))
  320. }
  321. }
  322. }),
  323. }
  324. }
  325. fn prepare_file_drop(&self) -> FileDropHandler<P> {
  326. let manager = self.clone();
  327. Box::new(move |event, window| {
  328. let manager = manager.clone();
  329. crate::async_runtime::block_on(async move {
  330. let window = Window::new(manager.clone(), window);
  331. let _ = match event {
  332. FileDropEvent::Hovered(paths) => {
  333. window.emit_internal(&tauri_event::<P::Event>("tauri://file-drop"), Some(paths))
  334. }
  335. FileDropEvent::Dropped(paths) => window.emit_internal(
  336. &tauri_event::<P::Event>("tauri://file-drop-hover"),
  337. Some(paths),
  338. ),
  339. FileDropEvent::Cancelled => window.emit_internal(
  340. &tauri_event::<P::Event>("tauri://file-drop-cancelled"),
  341. Some(()),
  342. ),
  343. _ => unimplemented!(),
  344. };
  345. });
  346. true
  347. })
  348. }
  349. fn initialization_script(
  350. &self,
  351. plugin_initialization_script: &str,
  352. with_global_tauri: bool,
  353. ) -> String {
  354. format!(
  355. r#"
  356. {bundle_script}
  357. {core_script}
  358. {event_initialization_script}
  359. if (window.rpc) {{
  360. window.__TAURI__.invoke("__initialized", {{ url: window.location.href }})
  361. }} else {{
  362. window.addEventListener('DOMContentLoaded', function () {{
  363. window.__TAURI__.invoke("__initialized", {{ url: window.location.href }})
  364. }})
  365. }}
  366. {plugin_initialization_script}
  367. "#,
  368. core_script = include_str!("../scripts/core.js"),
  369. bundle_script = if with_global_tauri {
  370. include_str!("../scripts/bundle.js")
  371. } else {
  372. ""
  373. },
  374. event_initialization_script = self.event_initialization_script(),
  375. plugin_initialization_script = plugin_initialization_script
  376. )
  377. }
  378. fn event_initialization_script(&self) -> String {
  379. return format!(
  380. "
  381. window['{queue}'] = [];
  382. window['{function}'] = function (eventData, salt, ignoreQueue) {{
  383. const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
  384. if (!ignoreQueue && listeners.length === 0) {{
  385. window['{queue}'].push({{
  386. eventData: eventData,
  387. salt: salt
  388. }})
  389. }}
  390. if (listeners.length > 0) {{
  391. window.__TAURI__.invoke('tauri', {{
  392. __tauriModule: 'Internal',
  393. message: {{
  394. cmd: 'validateSalt',
  395. salt: salt
  396. }}
  397. }}).then(function (flag) {{
  398. if (flag) {{
  399. for (let i = listeners.length - 1; i >= 0; i--) {{
  400. const listener = listeners[i]
  401. eventData.id = listener.id
  402. listener.handler(eventData)
  403. }}
  404. }}
  405. }})
  406. }}
  407. }}
  408. ",
  409. function = self.inner.listeners.function_name(),
  410. queue = self.inner.listeners.queue_object_name(),
  411. listeners = self.inner.listeners.listeners_object_name()
  412. );
  413. }
  414. }
  415. #[cfg(test)]
  416. mod test {
  417. use super::{Args, WindowManager};
  418. use crate::{generate_context, plugin::PluginStore, StateManager, Wry};
  419. #[test]
  420. fn check_get_url() {
  421. let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate);
  422. let manager: WindowManager<Args<String, String, String, String, _, Wry>> =
  423. WindowManager::with_handlers(
  424. context,
  425. PluginStore::default(),
  426. Box::new(|_| ()),
  427. Box::new(|_, _| ()),
  428. Default::default(),
  429. StateManager::new(),
  430. Vec::new(),
  431. Default::default(),
  432. );
  433. #[cfg(custom_protocol)]
  434. assert_eq!(manager.get_url(), "tauri://localhost");
  435. #[cfg(dev)]
  436. assert_eq!(manager.get_url(), manager.config().build.dev_path);
  437. }
  438. }
  439. impl<P: Params> WindowManager<P> {
  440. pub fn run_invoke_handler(&self, invoke: Invoke<P>) {
  441. (self.inner.invoke_handler)(invoke);
  442. }
  443. pub fn run_on_page_load(&self, window: Window<P>, payload: PageLoadPayload) {
  444. (self.inner.on_page_load)(window.clone(), payload.clone());
  445. self
  446. .inner
  447. .plugins
  448. .lock()
  449. .expect("poisoned plugin store")
  450. .on_page_load(window, payload);
  451. }
  452. pub fn extend_api(&self, invoke: Invoke<P>) {
  453. self
  454. .inner
  455. .plugins
  456. .lock()
  457. .expect("poisoned plugin store")
  458. .extend_api(invoke);
  459. }
  460. pub fn initialize_plugins(&self, app: &App<P>) -> crate::Result<()> {
  461. self
  462. .inner
  463. .plugins
  464. .lock()
  465. .expect("poisoned plugin store")
  466. .initialize(&app, &self.inner.config.plugins)
  467. }
  468. pub fn prepare_window(
  469. &self,
  470. mut pending: PendingWindow<P>,
  471. pending_labels: &[P::Label],
  472. ) -> crate::Result<PendingWindow<P>> {
  473. let (is_local, url) = match &pending.webview_attributes.url {
  474. WindowUrl::App(path) => {
  475. let url = self.get_url();
  476. (
  477. true,
  478. // ignore "index.html" just to simplify the url
  479. if path.to_str() != Some("index.html") {
  480. format!("{}/{}", url, path.to_string_lossy())
  481. } else {
  482. url
  483. },
  484. )
  485. }
  486. WindowUrl::External(url) => (url.as_str().starts_with("tauri://"), url.to_string()),
  487. _ => unimplemented!(),
  488. };
  489. if is_local {
  490. let label = pending.label.clone();
  491. pending = self.prepare_pending_window(pending, label, pending_labels)?;
  492. pending.rpc_handler = Some(self.prepare_rpc_handler());
  493. }
  494. pending.file_drop_handler = Some(self.prepare_file_drop());
  495. pending.url = url;
  496. Ok(pending)
  497. }
  498. pub fn attach_window(&self, window: DetachedWindow<P>) -> Window<P> {
  499. let window = Window::new(self.clone(), window);
  500. let window_ = window.clone();
  501. window.on_window_event(move |event| {
  502. let _ = on_window_event(&window_, event);
  503. });
  504. let window_ = window.clone();
  505. let menu_event_listeners = self.inner.menu_event_listeners.clone();
  506. window.on_menu_event(move |event| {
  507. let _ = on_menu_event(&window_, &event);
  508. for handler in menu_event_listeners.iter() {
  509. handler(WindowMenuEvent {
  510. window: window_.clone(),
  511. menu_item_id: event.menu_item_id.clone(),
  512. });
  513. }
  514. });
  515. // insert the window into our manager
  516. {
  517. self
  518. .windows_lock()
  519. .insert(window.label().clone(), window.clone());
  520. }
  521. // let plugins know that a new window has been added to the manager
  522. {
  523. self
  524. .inner
  525. .plugins
  526. .lock()
  527. .expect("poisoned plugin store")
  528. .created(window.clone());
  529. }
  530. window
  531. }
  532. pub fn emit_filter<E: ?Sized, S, F>(
  533. &self,
  534. event: &E,
  535. payload: Option<S>,
  536. filter: F,
  537. ) -> crate::Result<()>
  538. where
  539. P::Event: Borrow<E>,
  540. E: TagRef<P::Event>,
  541. S: Serialize + Clone,
  542. F: Fn(&Window<P>) -> bool,
  543. {
  544. self
  545. .windows_lock()
  546. .values()
  547. .filter(|&w| filter(w))
  548. .try_for_each(|window| window.emit(event, payload.clone()))
  549. }
  550. pub fn labels(&self) -> HashSet<P::Label> {
  551. self.windows_lock().keys().cloned().collect()
  552. }
  553. pub fn config(&self) -> Arc<Config> {
  554. self.inner.config.clone()
  555. }
  556. pub fn package_info(&self) -> &PackageInfo {
  557. &self.inner.package_info
  558. }
  559. pub fn unlisten(&self, handler_id: EventHandler) {
  560. self.inner.listeners.unlisten(handler_id)
  561. }
  562. pub fn trigger<E: ?Sized>(&self, event: &E, window: Option<P::Label>, data: Option<String>)
  563. where
  564. P::Event: Borrow<E>,
  565. E: TagRef<P::Event>,
  566. {
  567. self.inner.listeners.trigger(event, window, data)
  568. }
  569. pub fn listen<F: Fn(Event) + Send + 'static>(
  570. &self,
  571. event: P::Event,
  572. window: Option<P::Label>,
  573. handler: F,
  574. ) -> EventHandler {
  575. self.inner.listeners.listen(event, window, handler)
  576. }
  577. pub fn once<F: Fn(Event) + Send + 'static>(
  578. &self,
  579. event: P::Event,
  580. window: Option<P::Label>,
  581. handler: F,
  582. ) -> EventHandler {
  583. self.inner.listeners.once(event, window, handler)
  584. }
  585. pub fn event_listeners_object_name(&self) -> String {
  586. self.inner.listeners.listeners_object_name()
  587. }
  588. pub fn event_queue_object_name(&self) -> String {
  589. self.inner.listeners.queue_object_name()
  590. }
  591. pub fn event_emit_function_name(&self) -> String {
  592. self.inner.listeners.function_name()
  593. }
  594. pub fn generate_salt(&self) -> Uuid {
  595. let salt = Uuid::new_v4();
  596. self
  597. .inner
  598. .salts
  599. .lock()
  600. .expect("poisoned salt mutex")
  601. .insert(salt);
  602. salt
  603. }
  604. pub fn verify_salt(&self, salt: String) -> bool {
  605. // flat out ignore any invalid uuids
  606. let uuid: Uuid = match salt.parse() {
  607. Ok(uuid) => uuid,
  608. Err(_) => return false,
  609. };
  610. // HashSet::remove lets us know if the entry was found
  611. self
  612. .inner
  613. .salts
  614. .lock()
  615. .expect("poisoned salt mutex")
  616. .remove(&uuid)
  617. }
  618. pub fn get_window<L: ?Sized>(&self, label: &L) -> Option<Window<P>>
  619. where
  620. P::Label: Borrow<L>,
  621. L: TagRef<P::Label>,
  622. {
  623. self.windows_lock().get(label).cloned()
  624. }
  625. pub fn windows(&self) -> HashMap<P::Label, Window<P>> {
  626. self.windows_lock().clone()
  627. }
  628. }
  629. fn on_window_event<P: Params>(window: &Window<P>, event: &WindowEvent) -> crate::Result<()> {
  630. match event {
  631. WindowEvent::Resized(size) => window.emit(
  632. &WINDOW_RESIZED_EVENT
  633. .parse()
  634. .unwrap_or_else(|_| panic!("unhandled event")),
  635. Some(size),
  636. )?,
  637. WindowEvent::Moved(position) => window.emit(
  638. &WINDOW_MOVED_EVENT
  639. .parse()
  640. .unwrap_or_else(|_| panic!("unhandled event")),
  641. Some(position),
  642. )?,
  643. WindowEvent::CloseRequested => window.emit(
  644. &WINDOW_CLOSE_REQUESTED_EVENT
  645. .parse()
  646. .unwrap_or_else(|_| panic!("unhandled event")),
  647. Some(()),
  648. )?,
  649. WindowEvent::Destroyed => window.emit(
  650. &WINDOW_DESTROYED_EVENT
  651. .parse()
  652. .unwrap_or_else(|_| panic!("unhandled event")),
  653. Some(()),
  654. )?,
  655. WindowEvent::Focused(focused) => window.emit(
  656. &if *focused {
  657. WINDOW_FOCUS_EVENT
  658. .parse()
  659. .unwrap_or_else(|_| panic!("unhandled event"))
  660. } else {
  661. WINDOW_BLUR_EVENT
  662. .parse()
  663. .unwrap_or_else(|_| panic!("unhandled event"))
  664. },
  665. Some(()),
  666. )?,
  667. WindowEvent::ScaleFactorChanged {
  668. scale_factor,
  669. new_inner_size,
  670. ..
  671. } => window.emit(
  672. &WINDOW_SCALE_FACTOR_CHANGED_EVENT
  673. .parse()
  674. .unwrap_or_else(|_| panic!("unhandled event")),
  675. Some(ScaleFactorChanged {
  676. scale_factor: *scale_factor,
  677. size: new_inner_size.clone(),
  678. }),
  679. )?,
  680. _ => unimplemented!(),
  681. }
  682. Ok(())
  683. }
  684. #[derive(Serialize)]
  685. #[serde(rename_all = "camelCase")]
  686. struct ScaleFactorChanged {
  687. scale_factor: f64,
  688. size: PhysicalSize<u32>,
  689. }
  690. fn on_menu_event<P: Params>(window: &Window<P>, event: &MenuEvent<P::MenuId>) -> crate::Result<()> {
  691. window.emit(
  692. &MENU_EVENT
  693. .parse()
  694. .unwrap_or_else(|_| panic!("unhandled event")),
  695. Some(event.menu_item_id.clone()),
  696. )
  697. }