lib.rs 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! The [`wry`] Tauri [`Runtime`].
  5. use tauri_runtime::{
  6. http::{
  7. Request as HttpRequest, RequestParts as HttpRequestParts, Response as HttpResponse,
  8. ResponseParts as HttpResponseParts,
  9. },
  10. menu::{CustomMenuItem, Menu, MenuEntry, MenuHash, MenuId, MenuItem, MenuUpdate},
  11. monitor::Monitor,
  12. webview::{
  13. FileDropEvent, FileDropHandler, RpcRequest, WebviewRpcHandler, WindowBuilder, WindowBuilderBase,
  14. },
  15. window::{
  16. dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
  17. DetachedWindow, PendingWindow, WindowEvent,
  18. },
  19. ClipboardManager, Dispatch, Error, ExitRequestedEventAction, GlobalShortcutManager, Icon, Result,
  20. RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType,
  21. };
  22. use tauri_runtime::window::MenuEvent;
  23. #[cfg(feature = "system-tray")]
  24. use tauri_runtime::{SystemTray, SystemTrayEvent};
  25. #[cfg(windows)]
  26. use webview2_com::{
  27. FocusChangedEventHandler,
  28. Windows::Win32::{Foundation::HWND, System::WinRT::EventRegistrationToken},
  29. };
  30. #[cfg(all(feature = "system-tray", target_os = "macos"))]
  31. use wry::application::platform::macos::{SystemTrayBuilderExtMacOS, SystemTrayExtMacOS};
  32. #[cfg(target_os = "linux")]
  33. use wry::application::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
  34. #[cfg(windows)]
  35. use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
  36. #[cfg(feature = "system-tray")]
  37. use wry::application::system_tray::{SystemTray as WrySystemTray, SystemTrayBuilder};
  38. use tauri_utils::config::WindowConfig;
  39. use uuid::Uuid;
  40. use wry::{
  41. application::{
  42. accelerator::{Accelerator, AcceleratorId},
  43. clipboard::Clipboard,
  44. dpi::{
  45. LogicalPosition as WryLogicalPosition, LogicalSize as WryLogicalSize,
  46. PhysicalPosition as WryPhysicalPosition, PhysicalSize as WryPhysicalSize,
  47. Position as WryPosition, Size as WrySize,
  48. },
  49. event::{Event, StartCause, WindowEvent as WryWindowEvent},
  50. event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget},
  51. global_shortcut::{GlobalShortcut, ShortcutManager as WryShortcutManager},
  52. menu::{
  53. CustomMenuItem as WryCustomMenuItem, MenuBar, MenuId as WryMenuId, MenuItem as WryMenuItem,
  54. MenuItemAttributes as WryMenuItemAttributes, MenuType,
  55. },
  56. monitor::MonitorHandle,
  57. window::{Fullscreen, Icon as WindowIcon, UserAttentionType as WryUserAttentionType},
  58. },
  59. http::{
  60. Request as WryHttpRequest, RequestParts as WryRequestParts, Response as WryHttpResponse,
  61. ResponseParts as WryResponseParts,
  62. },
  63. webview::{
  64. FileDropEvent as WryFileDropEvent, RpcRequest as WryRpcRequest, RpcResponse, WebContext,
  65. WebView, WebViewBuilder,
  66. },
  67. };
  68. pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId};
  69. #[cfg(target_os = "windows")]
  70. use wry::webview::WebviewExtWindows;
  71. #[cfg(target_os = "macos")]
  72. use tauri_runtime::{menu::NativeImage, ActivationPolicy};
  73. #[cfg(target_os = "macos")]
  74. pub use wry::application::platform::macos::{
  75. ActivationPolicy as WryActivationPolicy, CustomMenuItemExtMacOS, EventLoopExtMacOS,
  76. NativeImage as WryNativeImage, WindowExtMacOS,
  77. };
  78. use std::{
  79. collections::{
  80. hash_map::Entry::{Occupied, Vacant},
  81. HashMap,
  82. },
  83. convert::TryFrom,
  84. fmt,
  85. fs::read,
  86. ops::Deref,
  87. path::PathBuf,
  88. sync::{
  89. mpsc::{channel, Sender},
  90. Arc, Mutex, MutexGuard, Weak,
  91. },
  92. thread::{current as current_thread, ThreadId},
  93. };
  94. #[cfg(feature = "system-tray")]
  95. mod system_tray;
  96. #[cfg(feature = "system-tray")]
  97. use system_tray::*;
  98. type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
  99. // window
  100. type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
  101. type WindowEventListenersMap = Arc<Mutex<HashMap<Uuid, WindowEventHandler>>>;
  102. type WindowEventListeners = Arc<Mutex<HashMap<WindowId, WindowEventListenersMap>>>;
  103. // global shortcut
  104. type GlobalShortcutListeners = Arc<Mutex<HashMap<AcceleratorId, Box<dyn Fn() + Send>>>>;
  105. // menu
  106. pub type MenuEventHandler = Box<dyn Fn(&MenuEvent) + Send>;
  107. pub type MenuEventListeners = Arc<Mutex<HashMap<WindowId, WindowMenuEventListeners>>>;
  108. pub type WindowMenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;
  109. macro_rules! getter {
  110. ($self: ident, $rx: expr, $message: expr) => {{
  111. send_user_message(&$self.context, $message)?;
  112. $rx.recv().unwrap()
  113. }};
  114. }
  115. macro_rules! window_getter {
  116. ($self: ident, $message: expr) => {{
  117. let (tx, rx) = channel();
  118. getter!($self, rx, Message::Window($self.window_id, $message(tx)))
  119. }};
  120. }
  121. fn send_user_message(context: &Context, message: Message) -> Result<()> {
  122. if current_thread().id() == context.main_thread_id {
  123. handle_user_message(
  124. &context.main_thread.window_target,
  125. message,
  126. UserMessageContext {
  127. window_event_listeners: &context.window_event_listeners,
  128. global_shortcut_manager: context.main_thread.global_shortcut_manager.clone(),
  129. clipboard_manager: context.main_thread.clipboard_manager.clone(),
  130. menu_event_listeners: &context.menu_event_listeners,
  131. windows: context.main_thread.windows.clone(),
  132. #[cfg(feature = "system-tray")]
  133. tray_context: &context.main_thread.tray_context,
  134. },
  135. &context.main_thread.web_context,
  136. );
  137. Ok(())
  138. } else {
  139. context
  140. .proxy
  141. .send_event(message)
  142. .map_err(|_| Error::FailedToSendMessage)
  143. }
  144. }
  145. #[derive(Clone)]
  146. struct Context {
  147. main_thread_id: ThreadId,
  148. proxy: EventLoopProxy<Message>,
  149. window_event_listeners: WindowEventListeners,
  150. menu_event_listeners: MenuEventListeners,
  151. main_thread: DispatcherMainThreadContext,
  152. }
  153. #[derive(Debug, Clone)]
  154. struct DispatcherMainThreadContext {
  155. window_target: EventLoopWindowTarget<Message>,
  156. web_context: WebContextStore,
  157. global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
  158. clipboard_manager: Arc<Mutex<Clipboard>>,
  159. windows: Arc<Mutex<HashMap<WindowId, WindowWrapper>>>,
  160. #[cfg(feature = "system-tray")]
  161. tray_context: TrayContext,
  162. }
  163. // the main thread context is only used on the main thread
  164. unsafe impl Send for DispatcherMainThreadContext {}
  165. impl fmt::Debug for Context {
  166. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  167. f.debug_struct("Context")
  168. .field("main_thread_id", &self.main_thread_id)
  169. .field("proxy", &self.proxy)
  170. .field("main_thread", &self.main_thread)
  171. .finish()
  172. }
  173. }
  174. struct HttpRequestPartsWrapper(HttpRequestParts);
  175. impl From<HttpRequestPartsWrapper> for HttpRequestParts {
  176. fn from(parts: HttpRequestPartsWrapper) -> Self {
  177. Self {
  178. method: parts.0.method,
  179. uri: parts.0.uri,
  180. headers: parts.0.headers,
  181. }
  182. }
  183. }
  184. impl From<HttpRequestParts> for HttpRequestPartsWrapper {
  185. fn from(request: HttpRequestParts) -> Self {
  186. Self(HttpRequestParts {
  187. method: request.method,
  188. uri: request.uri,
  189. headers: request.headers,
  190. })
  191. }
  192. }
  193. impl From<WryRequestParts> for HttpRequestPartsWrapper {
  194. fn from(request: WryRequestParts) -> Self {
  195. Self(HttpRequestParts {
  196. method: request.method,
  197. uri: request.uri,
  198. headers: request.headers,
  199. })
  200. }
  201. }
  202. struct HttpRequestWrapper(HttpRequest);
  203. impl From<&WryHttpRequest> for HttpRequestWrapper {
  204. fn from(req: &WryHttpRequest) -> Self {
  205. Self(HttpRequest {
  206. body: req.body.clone(),
  207. head: HttpRequestPartsWrapper::from(req.head.clone()).0,
  208. })
  209. }
  210. }
  211. // response
  212. struct HttpResponsePartsWrapper(WryResponseParts);
  213. impl From<HttpResponseParts> for HttpResponsePartsWrapper {
  214. fn from(response: HttpResponseParts) -> Self {
  215. Self(WryResponseParts {
  216. mimetype: response.mimetype,
  217. status: response.status,
  218. version: response.version,
  219. headers: response.headers,
  220. })
  221. }
  222. }
  223. struct HttpResponseWrapper(WryHttpResponse);
  224. impl From<HttpResponse> for HttpResponseWrapper {
  225. fn from(response: HttpResponse) -> Self {
  226. Self(WryHttpResponse {
  227. body: response.body,
  228. head: HttpResponsePartsWrapper::from(response.head).0,
  229. })
  230. }
  231. }
  232. pub struct MenuItemAttributesWrapper<'a>(pub WryMenuItemAttributes<'a>);
  233. impl<'a> From<&'a CustomMenuItem> for MenuItemAttributesWrapper<'a> {
  234. fn from(item: &'a CustomMenuItem) -> Self {
  235. let mut attributes = WryMenuItemAttributes::new(&item.title)
  236. .with_enabled(item.enabled)
  237. .with_selected(item.selected)
  238. .with_id(WryMenuId(item.id));
  239. if let Some(accelerator) = item.keyboard_accelerator.as_ref() {
  240. attributes = attributes.with_accelerators(&accelerator.parse().expect("invalid accelerator"));
  241. }
  242. Self(attributes)
  243. }
  244. }
  245. pub struct MenuItemWrapper(pub WryMenuItem);
  246. impl From<MenuItem> for MenuItemWrapper {
  247. fn from(item: MenuItem) -> Self {
  248. match item {
  249. MenuItem::About(v) => Self(WryMenuItem::About(v)),
  250. MenuItem::Hide => Self(WryMenuItem::Hide),
  251. MenuItem::Services => Self(WryMenuItem::Services),
  252. MenuItem::HideOthers => Self(WryMenuItem::HideOthers),
  253. MenuItem::ShowAll => Self(WryMenuItem::ShowAll),
  254. MenuItem::CloseWindow => Self(WryMenuItem::CloseWindow),
  255. MenuItem::Quit => Self(WryMenuItem::Quit),
  256. MenuItem::Copy => Self(WryMenuItem::Copy),
  257. MenuItem::Cut => Self(WryMenuItem::Cut),
  258. MenuItem::Undo => Self(WryMenuItem::Undo),
  259. MenuItem::Redo => Self(WryMenuItem::Redo),
  260. MenuItem::SelectAll => Self(WryMenuItem::SelectAll),
  261. MenuItem::Paste => Self(WryMenuItem::Paste),
  262. MenuItem::EnterFullScreen => Self(WryMenuItem::EnterFullScreen),
  263. MenuItem::Minimize => Self(WryMenuItem::Minimize),
  264. MenuItem::Zoom => Self(WryMenuItem::Zoom),
  265. MenuItem::Separator => Self(WryMenuItem::Separator),
  266. _ => unimplemented!(),
  267. }
  268. }
  269. }
  270. #[cfg(target_os = "macos")]
  271. pub struct NativeImageWrapper(pub WryNativeImage);
  272. #[cfg(target_os = "macos")]
  273. impl std::fmt::Debug for NativeImageWrapper {
  274. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  275. f.debug_struct("NativeImageWrapper").finish()
  276. }
  277. }
  278. #[cfg(target_os = "macos")]
  279. impl From<NativeImage> for NativeImageWrapper {
  280. fn from(image: NativeImage) -> NativeImageWrapper {
  281. let wry_image = match image {
  282. NativeImage::Add => WryNativeImage::Add,
  283. NativeImage::Advanced => WryNativeImage::Advanced,
  284. NativeImage::Bluetooth => WryNativeImage::Bluetooth,
  285. NativeImage::Bookmarks => WryNativeImage::Bookmarks,
  286. NativeImage::Caution => WryNativeImage::Caution,
  287. NativeImage::ColorPanel => WryNativeImage::ColorPanel,
  288. NativeImage::ColumnView => WryNativeImage::ColumnView,
  289. NativeImage::Computer => WryNativeImage::Computer,
  290. NativeImage::EnterFullScreen => WryNativeImage::EnterFullScreen,
  291. NativeImage::Everyone => WryNativeImage::Everyone,
  292. NativeImage::ExitFullScreen => WryNativeImage::ExitFullScreen,
  293. NativeImage::FlowView => WryNativeImage::FlowView,
  294. NativeImage::Folder => WryNativeImage::Folder,
  295. NativeImage::FolderBurnable => WryNativeImage::FolderBurnable,
  296. NativeImage::FolderSmart => WryNativeImage::FolderSmart,
  297. NativeImage::FollowLinkFreestanding => WryNativeImage::FollowLinkFreestanding,
  298. NativeImage::FontPanel => WryNativeImage::FontPanel,
  299. NativeImage::GoLeft => WryNativeImage::GoLeft,
  300. NativeImage::GoRight => WryNativeImage::GoRight,
  301. NativeImage::Home => WryNativeImage::Home,
  302. NativeImage::IChatTheater => WryNativeImage::IChatTheater,
  303. NativeImage::IconView => WryNativeImage::IconView,
  304. NativeImage::Info => WryNativeImage::Info,
  305. NativeImage::InvalidDataFreestanding => WryNativeImage::InvalidDataFreestanding,
  306. NativeImage::LeftFacingTriangle => WryNativeImage::LeftFacingTriangle,
  307. NativeImage::ListView => WryNativeImage::ListView,
  308. NativeImage::LockLocked => WryNativeImage::LockLocked,
  309. NativeImage::LockUnlocked => WryNativeImage::LockUnlocked,
  310. NativeImage::MenuMixedState => WryNativeImage::MenuMixedState,
  311. NativeImage::MenuOnState => WryNativeImage::MenuOnState,
  312. NativeImage::MobileMe => WryNativeImage::MobileMe,
  313. NativeImage::MultipleDocuments => WryNativeImage::MultipleDocuments,
  314. NativeImage::Network => WryNativeImage::Network,
  315. NativeImage::Path => WryNativeImage::Path,
  316. NativeImage::PreferencesGeneral => WryNativeImage::PreferencesGeneral,
  317. NativeImage::QuickLook => WryNativeImage::QuickLook,
  318. NativeImage::RefreshFreestanding => WryNativeImage::RefreshFreestanding,
  319. NativeImage::Refresh => WryNativeImage::Refresh,
  320. NativeImage::Remove => WryNativeImage::Remove,
  321. NativeImage::RevealFreestanding => WryNativeImage::RevealFreestanding,
  322. NativeImage::RightFacingTriangle => WryNativeImage::RightFacingTriangle,
  323. NativeImage::Share => WryNativeImage::Share,
  324. NativeImage::Slideshow => WryNativeImage::Slideshow,
  325. NativeImage::SmartBadge => WryNativeImage::SmartBadge,
  326. NativeImage::StatusAvailable => WryNativeImage::StatusAvailable,
  327. NativeImage::StatusNone => WryNativeImage::StatusNone,
  328. NativeImage::StatusPartiallyAvailable => WryNativeImage::StatusPartiallyAvailable,
  329. NativeImage::StatusUnavailable => WryNativeImage::StatusUnavailable,
  330. NativeImage::StopProgressFreestanding => WryNativeImage::StopProgressFreestanding,
  331. NativeImage::StopProgress => WryNativeImage::StopProgress,
  332. NativeImage::TrashEmpty => WryNativeImage::TrashEmpty,
  333. NativeImage::TrashFull => WryNativeImage::TrashFull,
  334. NativeImage::User => WryNativeImage::User,
  335. NativeImage::UserAccounts => WryNativeImage::UserAccounts,
  336. NativeImage::UserGroup => WryNativeImage::UserGroup,
  337. NativeImage::UserGuest => WryNativeImage::UserGuest,
  338. };
  339. Self(wry_image)
  340. }
  341. }
  342. #[derive(Debug, Clone)]
  343. pub struct GlobalShortcutWrapper(GlobalShortcut);
  344. unsafe impl Send for GlobalShortcutWrapper {}
  345. /// Wrapper around [`WryShortcutManager`].
  346. #[derive(Clone)]
  347. pub struct GlobalShortcutManagerHandle {
  348. context: Context,
  349. shortcuts: Arc<Mutex<HashMap<String, (AcceleratorId, GlobalShortcutWrapper)>>>,
  350. listeners: GlobalShortcutListeners,
  351. }
  352. impl fmt::Debug for GlobalShortcutManagerHandle {
  353. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  354. f.debug_struct("GlobalShortcutManagerHandle")
  355. .field("context", &self.context)
  356. .field("shortcuts", &self.shortcuts)
  357. .finish()
  358. }
  359. }
  360. impl GlobalShortcutManager for GlobalShortcutManagerHandle {
  361. fn is_registered(&self, accelerator: &str) -> Result<bool> {
  362. let (tx, rx) = channel();
  363. Ok(getter!(
  364. self,
  365. rx,
  366. Message::GlobalShortcut(GlobalShortcutMessage::IsRegistered(
  367. accelerator.parse().expect("invalid accelerator"),
  368. tx
  369. ))
  370. ))
  371. }
  372. fn register<F: Fn() + Send + 'static>(&mut self, accelerator: &str, handler: F) -> Result<()> {
  373. let wry_accelerator: Accelerator = accelerator.parse().expect("invalid accelerator");
  374. let id = wry_accelerator.clone().id();
  375. let (tx, rx) = channel();
  376. let shortcut = getter!(
  377. self,
  378. rx,
  379. Message::GlobalShortcut(GlobalShortcutMessage::Register(wry_accelerator, tx))
  380. )?;
  381. self.listeners.lock().unwrap().insert(id, Box::new(handler));
  382. self
  383. .shortcuts
  384. .lock()
  385. .unwrap()
  386. .insert(accelerator.into(), (id, shortcut));
  387. Ok(())
  388. }
  389. fn unregister_all(&mut self) -> Result<()> {
  390. let (tx, rx) = channel();
  391. getter!(
  392. self,
  393. rx,
  394. Message::GlobalShortcut(GlobalShortcutMessage::UnregisterAll(tx))
  395. )?;
  396. self.listeners.lock().unwrap().clear();
  397. self.shortcuts.lock().unwrap().clear();
  398. Ok(())
  399. }
  400. fn unregister(&mut self, accelerator: &str) -> Result<()> {
  401. if let Some((accelerator_id, shortcut)) = self.shortcuts.lock().unwrap().remove(accelerator) {
  402. let (tx, rx) = channel();
  403. getter!(
  404. self,
  405. rx,
  406. Message::GlobalShortcut(GlobalShortcutMessage::Unregister(shortcut, tx))
  407. )?;
  408. self.listeners.lock().unwrap().remove(&accelerator_id);
  409. }
  410. Ok(())
  411. }
  412. }
  413. #[derive(Debug, Clone)]
  414. pub struct ClipboardManagerWrapper {
  415. context: Context,
  416. }
  417. impl ClipboardManager for ClipboardManagerWrapper {
  418. fn read_text(&self) -> Result<Option<String>> {
  419. let (tx, rx) = channel();
  420. Ok(getter!(
  421. self,
  422. rx,
  423. Message::Clipboard(ClipboardMessage::ReadText(tx))
  424. ))
  425. }
  426. fn write_text<T: Into<String>>(&mut self, text: T) -> Result<()> {
  427. let (tx, rx) = channel();
  428. getter!(
  429. self,
  430. rx,
  431. Message::Clipboard(ClipboardMessage::WriteText(text.into(), tx))
  432. );
  433. Ok(())
  434. }
  435. }
  436. /// Wrapper around a [`wry::application::window::Icon`] that can be created from an [`Icon`].
  437. pub struct WryIcon(WindowIcon);
  438. fn icon_err<E: std::error::Error + Send + 'static>(e: E) -> Error {
  439. Error::InvalidIcon(Box::new(e))
  440. }
  441. impl TryFrom<Icon> for WryIcon {
  442. type Error = Error;
  443. fn try_from(icon: Icon) -> std::result::Result<Self, Self::Error> {
  444. let image_bytes = match icon {
  445. Icon::File(path) => read(path).map_err(icon_err)?,
  446. Icon::Raw(raw) => raw,
  447. _ => unimplemented!(),
  448. };
  449. let extension = infer::get(&image_bytes)
  450. .expect("could not determine icon extension")
  451. .extension();
  452. match extension {
  453. #[cfg(windows)]
  454. "ico" => {
  455. let icon_dir = ico::IconDir::read(std::io::Cursor::new(image_bytes)).map_err(icon_err)?;
  456. let entry = &icon_dir.entries()[0];
  457. let icon = WindowIcon::from_rgba(
  458. entry.decode().map_err(icon_err)?.rgba_data().to_vec(),
  459. entry.width(),
  460. entry.height(),
  461. )
  462. .map_err(icon_err)?;
  463. Ok(Self(icon))
  464. }
  465. #[cfg(target_os = "linux")]
  466. "png" => {
  467. let decoder = png::Decoder::new(std::io::Cursor::new(image_bytes));
  468. let (info, mut reader) = decoder.read_info().map_err(icon_err)?;
  469. let mut buffer = Vec::new();
  470. while let Ok(Some(row)) = reader.next_row() {
  471. buffer.extend(row);
  472. }
  473. let icon = WindowIcon::from_rgba(buffer, info.width, info.height).map_err(icon_err)?;
  474. Ok(Self(icon))
  475. }
  476. _ => panic!(
  477. "image `{}` extension not supported; please file a Tauri feature request",
  478. extension
  479. ),
  480. }
  481. }
  482. }
  483. struct WindowEventWrapper(Option<WindowEvent>);
  484. impl WindowEventWrapper {
  485. fn parse(webview: &WindowHandle, event: &WryWindowEvent) -> Self {
  486. match event {
  487. // resized event from tao doesn't include a reliable size on macOS
  488. // because wry replaces the NSView
  489. WryWindowEvent::Resized(_) => Self(Some(WindowEvent::Resized(
  490. PhysicalSizeWrapper(webview.inner_size()).into(),
  491. ))),
  492. e => e.into(),
  493. }
  494. }
  495. }
  496. impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
  497. fn from(event: &WryWindowEvent<'a>) -> Self {
  498. let event = match event {
  499. WryWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
  500. WryWindowEvent::Moved(position) => {
  501. WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
  502. }
  503. WryWindowEvent::CloseRequested => WindowEvent::CloseRequested,
  504. WryWindowEvent::Destroyed => WindowEvent::Destroyed,
  505. WryWindowEvent::ScaleFactorChanged {
  506. scale_factor,
  507. new_inner_size,
  508. } => WindowEvent::ScaleFactorChanged {
  509. scale_factor: *scale_factor,
  510. new_inner_size: PhysicalSizeWrapper(**new_inner_size).into(),
  511. },
  512. #[cfg(any(target_os = "linux", target_os = "macos"))]
  513. WryWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
  514. _ => return Self(None),
  515. };
  516. Self(Some(event))
  517. }
  518. }
  519. impl From<&WebviewEvent> for WindowEventWrapper {
  520. fn from(event: &WebviewEvent) -> Self {
  521. let event = match event {
  522. WebviewEvent::Focused(focused) => WindowEvent::Focused(*focused),
  523. };
  524. Self(Some(event))
  525. }
  526. }
  527. pub struct MonitorHandleWrapper(MonitorHandle);
  528. impl From<MonitorHandleWrapper> for Monitor {
  529. fn from(monitor: MonitorHandleWrapper) -> Monitor {
  530. Self {
  531. name: monitor.0.name(),
  532. position: PhysicalPositionWrapper(monitor.0.position()).into(),
  533. size: PhysicalSizeWrapper(monitor.0.size()).into(),
  534. scale_factor: monitor.0.scale_factor(),
  535. }
  536. }
  537. }
  538. struct PhysicalPositionWrapper<T>(WryPhysicalPosition<T>);
  539. impl<T> From<PhysicalPositionWrapper<T>> for PhysicalPosition<T> {
  540. fn from(position: PhysicalPositionWrapper<T>) -> Self {
  541. Self {
  542. x: position.0.x,
  543. y: position.0.y,
  544. }
  545. }
  546. }
  547. impl<T> From<PhysicalPosition<T>> for PhysicalPositionWrapper<T> {
  548. fn from(position: PhysicalPosition<T>) -> Self {
  549. Self(WryPhysicalPosition {
  550. x: position.x,
  551. y: position.y,
  552. })
  553. }
  554. }
  555. struct LogicalPositionWrapper<T>(WryLogicalPosition<T>);
  556. impl<T> From<LogicalPosition<T>> for LogicalPositionWrapper<T> {
  557. fn from(position: LogicalPosition<T>) -> Self {
  558. Self(WryLogicalPosition {
  559. x: position.x,
  560. y: position.y,
  561. })
  562. }
  563. }
  564. struct PhysicalSizeWrapper<T>(WryPhysicalSize<T>);
  565. impl<T> From<PhysicalSizeWrapper<T>> for PhysicalSize<T> {
  566. fn from(size: PhysicalSizeWrapper<T>) -> Self {
  567. Self {
  568. width: size.0.width,
  569. height: size.0.height,
  570. }
  571. }
  572. }
  573. impl<T> From<PhysicalSize<T>> for PhysicalSizeWrapper<T> {
  574. fn from(size: PhysicalSize<T>) -> Self {
  575. Self(WryPhysicalSize {
  576. width: size.width,
  577. height: size.height,
  578. })
  579. }
  580. }
  581. struct LogicalSizeWrapper<T>(WryLogicalSize<T>);
  582. impl<T> From<LogicalSize<T>> for LogicalSizeWrapper<T> {
  583. fn from(size: LogicalSize<T>) -> Self {
  584. Self(WryLogicalSize {
  585. width: size.width,
  586. height: size.height,
  587. })
  588. }
  589. }
  590. struct SizeWrapper(WrySize);
  591. impl From<Size> for SizeWrapper {
  592. fn from(size: Size) -> Self {
  593. match size {
  594. Size::Logical(s) => Self(WrySize::Logical(LogicalSizeWrapper::from(s).0)),
  595. Size::Physical(s) => Self(WrySize::Physical(PhysicalSizeWrapper::from(s).0)),
  596. }
  597. }
  598. }
  599. struct PositionWrapper(WryPosition);
  600. impl From<Position> for PositionWrapper {
  601. fn from(position: Position) -> Self {
  602. match position {
  603. Position::Logical(s) => Self(WryPosition::Logical(LogicalPositionWrapper::from(s).0)),
  604. Position::Physical(s) => Self(WryPosition::Physical(PhysicalPositionWrapper::from(s).0)),
  605. }
  606. }
  607. }
  608. #[derive(Debug, Clone)]
  609. pub struct UserAttentionTypeWrapper(WryUserAttentionType);
  610. impl From<UserAttentionType> for UserAttentionTypeWrapper {
  611. fn from(request_type: UserAttentionType) -> UserAttentionTypeWrapper {
  612. let o = match request_type {
  613. UserAttentionType::Critical => WryUserAttentionType::Critical,
  614. UserAttentionType::Informational => WryUserAttentionType::Informational,
  615. };
  616. Self(o)
  617. }
  618. }
  619. #[derive(Debug, Clone, Default)]
  620. pub struct WindowBuilderWrapper {
  621. inner: WryWindowBuilder,
  622. center: bool,
  623. menu: Option<Menu>,
  624. }
  625. // safe since `menu_items` are read only here
  626. unsafe impl Send for WindowBuilderWrapper {}
  627. impl WindowBuilderBase for WindowBuilderWrapper {}
  628. impl WindowBuilder for WindowBuilderWrapper {
  629. fn new() -> Self {
  630. Default::default()
  631. }
  632. fn with_config(config: WindowConfig) -> Self {
  633. let mut window = WindowBuilderWrapper::new()
  634. .title(config.title.to_string())
  635. .inner_size(config.width, config.height)
  636. .visible(config.visible)
  637. .resizable(config.resizable)
  638. .decorations(config.decorations)
  639. .maximized(config.maximized)
  640. .fullscreen(config.fullscreen)
  641. .transparent(config.transparent)
  642. .always_on_top(config.always_on_top)
  643. .skip_taskbar(config.skip_taskbar);
  644. if let (Some(min_width), Some(min_height)) = (config.min_width, config.min_height) {
  645. window = window.min_inner_size(min_width, min_height);
  646. }
  647. if let (Some(max_width), Some(max_height)) = (config.max_width, config.max_height) {
  648. window = window.max_inner_size(max_width, max_height);
  649. }
  650. if let (Some(x), Some(y)) = (config.x, config.y) {
  651. window = window.position(x, y);
  652. }
  653. if config.center {
  654. window = window.center();
  655. }
  656. window
  657. }
  658. fn menu(mut self, menu: Menu) -> Self {
  659. self.menu.replace(menu);
  660. self
  661. }
  662. fn center(mut self) -> Self {
  663. self.center = true;
  664. self
  665. }
  666. fn position(mut self, x: f64, y: f64) -> Self {
  667. self.inner = self.inner.with_position(WryLogicalPosition::new(x, y));
  668. self
  669. }
  670. fn inner_size(mut self, width: f64, height: f64) -> Self {
  671. self.inner = self
  672. .inner
  673. .with_inner_size(WryLogicalSize::new(width, height));
  674. self
  675. }
  676. fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
  677. self.inner = self
  678. .inner
  679. .with_min_inner_size(WryLogicalSize::new(min_width, min_height));
  680. self
  681. }
  682. fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
  683. self.inner = self
  684. .inner
  685. .with_max_inner_size(WryLogicalSize::new(max_width, max_height));
  686. self
  687. }
  688. fn resizable(mut self, resizable: bool) -> Self {
  689. self.inner = self.inner.with_resizable(resizable);
  690. self
  691. }
  692. fn title<S: Into<String>>(mut self, title: S) -> Self {
  693. self.inner = self.inner.with_title(title.into());
  694. self
  695. }
  696. fn fullscreen(mut self, fullscreen: bool) -> Self {
  697. self.inner = if fullscreen {
  698. self
  699. .inner
  700. .with_fullscreen(Some(Fullscreen::Borderless(None)))
  701. } else {
  702. self.inner.with_fullscreen(None)
  703. };
  704. self
  705. }
  706. /// Deprecated since 0.1.4 (noop)
  707. /// Windows is automatically focused when created.
  708. fn focus(self) -> Self {
  709. self
  710. }
  711. fn maximized(mut self, maximized: bool) -> Self {
  712. self.inner = self.inner.with_maximized(maximized);
  713. self
  714. }
  715. fn visible(mut self, visible: bool) -> Self {
  716. self.inner = self.inner.with_visible(visible);
  717. self
  718. }
  719. fn transparent(mut self, transparent: bool) -> Self {
  720. self.inner = self.inner.with_transparent(transparent);
  721. self
  722. }
  723. fn decorations(mut self, decorations: bool) -> Self {
  724. self.inner = self.inner.with_decorations(decorations);
  725. self
  726. }
  727. fn always_on_top(mut self, always_on_top: bool) -> Self {
  728. self.inner = self.inner.with_always_on_top(always_on_top);
  729. self
  730. }
  731. #[cfg(windows)]
  732. fn parent_window(mut self, parent: HWND) -> Self {
  733. self.inner = self.inner.with_parent_window(parent);
  734. self
  735. }
  736. #[cfg(windows)]
  737. fn owner_window(mut self, owner: HWND) -> Self {
  738. self.inner = self.inner.with_owner_window(owner);
  739. self
  740. }
  741. fn icon(mut self, icon: Icon) -> Result<Self> {
  742. self.inner = self
  743. .inner
  744. .with_window_icon(Some(WryIcon::try_from(icon)?.0));
  745. Ok(self)
  746. }
  747. #[cfg(any(target_os = "windows", target_os = "linux"))]
  748. fn skip_taskbar(mut self, skip: bool) -> Self {
  749. self.inner = self.inner.with_skip_taskbar(skip);
  750. self
  751. }
  752. #[cfg(target_os = "macos")]
  753. fn skip_taskbar(self, _skip: bool) -> Self {
  754. self
  755. }
  756. fn has_icon(&self) -> bool {
  757. self.inner.window.window_icon.is_some()
  758. }
  759. fn get_menu(&self) -> Option<&Menu> {
  760. self.menu.as_ref()
  761. }
  762. }
  763. pub struct RpcRequestWrapper(WryRpcRequest);
  764. impl From<RpcRequestWrapper> for RpcRequest {
  765. fn from(request: RpcRequestWrapper) -> Self {
  766. Self {
  767. command: request.0.method,
  768. params: request.0.params,
  769. }
  770. }
  771. }
  772. pub struct FileDropEventWrapper(WryFileDropEvent);
  773. impl From<FileDropEventWrapper> for FileDropEvent {
  774. fn from(event: FileDropEventWrapper) -> Self {
  775. match event.0 {
  776. WryFileDropEvent::Hovered(paths) => FileDropEvent::Hovered(paths),
  777. WryFileDropEvent::Dropped(paths) => FileDropEvent::Dropped(paths),
  778. // default to cancelled
  779. // FIXME(maybe): Add `FileDropEvent::Unknown` event?
  780. _ => FileDropEvent::Cancelled,
  781. }
  782. }
  783. }
  784. #[cfg(target_os = "macos")]
  785. pub struct NSWindow(*mut std::ffi::c_void);
  786. #[cfg(target_os = "macos")]
  787. unsafe impl Send for NSWindow {}
  788. #[cfg(windows)]
  789. pub struct Hwnd(HWND);
  790. #[cfg(windows)]
  791. unsafe impl Send for Hwnd {}
  792. #[cfg(any(
  793. target_os = "linux",
  794. target_os = "dragonfly",
  795. target_os = "freebsd",
  796. target_os = "netbsd",
  797. target_os = "openbsd"
  798. ))]
  799. pub struct GtkWindow(gtk::ApplicationWindow);
  800. #[cfg(any(
  801. target_os = "linux",
  802. target_os = "dragonfly",
  803. target_os = "freebsd",
  804. target_os = "netbsd",
  805. target_os = "openbsd"
  806. ))]
  807. unsafe impl Send for GtkWindow {}
  808. #[derive(Debug, Clone)]
  809. pub enum WindowMessage {
  810. // Getters
  811. ScaleFactor(Sender<f64>),
  812. InnerPosition(Sender<Result<PhysicalPosition<i32>>>),
  813. OuterPosition(Sender<Result<PhysicalPosition<i32>>>),
  814. InnerSize(Sender<PhysicalSize<u32>>),
  815. OuterSize(Sender<PhysicalSize<u32>>),
  816. IsFullscreen(Sender<bool>),
  817. IsMaximized(Sender<bool>),
  818. IsDecorated(Sender<bool>),
  819. IsResizable(Sender<bool>),
  820. IsVisible(Sender<bool>),
  821. IsMenuVisible(Sender<bool>),
  822. CurrentMonitor(Sender<Option<MonitorHandle>>),
  823. PrimaryMonitor(Sender<Option<MonitorHandle>>),
  824. AvailableMonitors(Sender<Vec<MonitorHandle>>),
  825. #[cfg(target_os = "macos")]
  826. NSWindow(Sender<NSWindow>),
  827. #[cfg(windows)]
  828. Hwnd(Sender<Hwnd>),
  829. #[cfg(any(
  830. target_os = "linux",
  831. target_os = "dragonfly",
  832. target_os = "freebsd",
  833. target_os = "netbsd",
  834. target_os = "openbsd"
  835. ))]
  836. GtkWindow(Sender<GtkWindow>),
  837. // Setters
  838. Center(Sender<Result<()>>),
  839. RequestUserAttention(Option<UserAttentionTypeWrapper>),
  840. SetResizable(bool),
  841. SetTitle(String),
  842. Maximize,
  843. Unmaximize,
  844. Minimize,
  845. Unminimize,
  846. ShowMenu,
  847. HideMenu,
  848. Show,
  849. Hide,
  850. Close,
  851. SetDecorations(bool),
  852. SetAlwaysOnTop(bool),
  853. SetSize(Size),
  854. SetMinSize(Option<Size>),
  855. SetMaxSize(Option<Size>),
  856. SetPosition(Position),
  857. SetFullscreen(bool),
  858. SetFocus,
  859. SetIcon(WindowIcon),
  860. SetSkipTaskbar(bool),
  861. DragWindow,
  862. UpdateMenuItem(u16, MenuUpdate),
  863. }
  864. #[derive(Debug, Clone)]
  865. pub enum WebviewMessage {
  866. EvaluateScript(String),
  867. #[allow(dead_code)]
  868. WebviewEvent(WebviewEvent),
  869. Print,
  870. }
  871. #[allow(dead_code)]
  872. #[derive(Debug, Clone)]
  873. pub enum WebviewEvent {
  874. Focused(bool),
  875. }
  876. #[cfg(feature = "system-tray")]
  877. #[derive(Debug, Clone)]
  878. pub enum TrayMessage {
  879. UpdateItem(u16, MenuUpdate),
  880. UpdateMenu(SystemTrayMenu),
  881. UpdateIcon(Icon),
  882. #[cfg(target_os = "macos")]
  883. UpdateIconAsTemplate(bool),
  884. }
  885. #[derive(Debug, Clone)]
  886. pub enum GlobalShortcutMessage {
  887. IsRegistered(Accelerator, Sender<bool>),
  888. Register(Accelerator, Sender<Result<GlobalShortcutWrapper>>),
  889. Unregister(GlobalShortcutWrapper, Sender<Result<()>>),
  890. UnregisterAll(Sender<Result<()>>),
  891. }
  892. #[derive(Debug, Clone)]
  893. pub enum ClipboardMessage {
  894. WriteText(String, Sender<()>),
  895. ReadText(Sender<Option<String>>),
  896. }
  897. pub enum Message {
  898. Task(Box<dyn FnOnce() + Send>),
  899. Window(WindowId, WindowMessage),
  900. Webview(WindowId, WebviewMessage),
  901. #[cfg(feature = "system-tray")]
  902. Tray(TrayMessage),
  903. CreateWebview(
  904. Box<
  905. dyn FnOnce(&EventLoopWindowTarget<Message>, &WebContextStore) -> Result<WindowWrapper> + Send,
  906. >,
  907. Sender<WindowId>,
  908. ),
  909. CreateWindow(
  910. Box<dyn FnOnce() -> (String, WryWindowBuilder) + Send>,
  911. Sender<Result<Weak<Window>>>,
  912. ),
  913. GlobalShortcut(GlobalShortcutMessage),
  914. Clipboard(ClipboardMessage),
  915. }
  916. impl Clone for Message {
  917. fn clone(&self) -> Self {
  918. match self {
  919. Self::Window(i, m) => Self::Window(*i, m.clone()),
  920. Self::Webview(i, m) => Self::Webview(*i, m.clone()),
  921. #[cfg(feature = "system-tray")]
  922. Self::Tray(m) => Self::Tray(m.clone()),
  923. Self::GlobalShortcut(m) => Self::GlobalShortcut(m.clone()),
  924. Self::Clipboard(m) => Self::Clipboard(m.clone()),
  925. _ => unimplemented!(),
  926. }
  927. }
  928. }
  929. /// The Tauri [`Dispatch`] for [`Wry`].
  930. #[derive(Debug, Clone)]
  931. pub struct WryDispatcher {
  932. window_id: WindowId,
  933. context: Context,
  934. }
  935. impl Dispatch for WryDispatcher {
  936. type Runtime = Wry;
  937. type WindowBuilder = WindowBuilderWrapper;
  938. fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
  939. send_user_message(&self.context, Message::Task(Box::new(f)))
  940. }
  941. fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> Uuid {
  942. let id = Uuid::new_v4();
  943. self
  944. .context
  945. .window_event_listeners
  946. .lock()
  947. .unwrap()
  948. .get(&self.window_id)
  949. .unwrap()
  950. .lock()
  951. .unwrap()
  952. .insert(id, Box::new(f));
  953. id
  954. }
  955. fn on_menu_event<F: Fn(&MenuEvent) + Send + 'static>(&self, f: F) -> Uuid {
  956. let id = Uuid::new_v4();
  957. self
  958. .context
  959. .menu_event_listeners
  960. .lock()
  961. .unwrap()
  962. .get(&self.window_id)
  963. .unwrap()
  964. .lock()
  965. .unwrap()
  966. .insert(id, Box::new(f));
  967. id
  968. }
  969. // Getters
  970. fn scale_factor(&self) -> Result<f64> {
  971. Ok(window_getter!(self, WindowMessage::ScaleFactor))
  972. }
  973. fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
  974. window_getter!(self, WindowMessage::InnerPosition)
  975. }
  976. fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
  977. window_getter!(self, WindowMessage::OuterPosition)
  978. }
  979. fn inner_size(&self) -> Result<PhysicalSize<u32>> {
  980. Ok(window_getter!(self, WindowMessage::InnerSize))
  981. }
  982. fn outer_size(&self) -> Result<PhysicalSize<u32>> {
  983. Ok(window_getter!(self, WindowMessage::OuterSize))
  984. }
  985. fn is_fullscreen(&self) -> Result<bool> {
  986. Ok(window_getter!(self, WindowMessage::IsFullscreen))
  987. }
  988. fn is_maximized(&self) -> Result<bool> {
  989. Ok(window_getter!(self, WindowMessage::IsMaximized))
  990. }
  991. /// Gets the window’s current decoration state.
  992. fn is_decorated(&self) -> Result<bool> {
  993. Ok(window_getter!(self, WindowMessage::IsDecorated))
  994. }
  995. /// Gets the window’s current resizable state.
  996. fn is_resizable(&self) -> Result<bool> {
  997. Ok(window_getter!(self, WindowMessage::IsResizable))
  998. }
  999. fn is_visible(&self) -> Result<bool> {
  1000. Ok(window_getter!(self, WindowMessage::IsVisible))
  1001. }
  1002. fn is_menu_visible(&self) -> Result<bool> {
  1003. Ok(window_getter!(self, WindowMessage::IsMenuVisible))
  1004. }
  1005. fn current_monitor(&self) -> Result<Option<Monitor>> {
  1006. Ok(window_getter!(self, WindowMessage::CurrentMonitor).map(|m| MonitorHandleWrapper(m).into()))
  1007. }
  1008. fn primary_monitor(&self) -> Result<Option<Monitor>> {
  1009. Ok(window_getter!(self, WindowMessage::PrimaryMonitor).map(|m| MonitorHandleWrapper(m).into()))
  1010. }
  1011. fn available_monitors(&self) -> Result<Vec<Monitor>> {
  1012. Ok(
  1013. window_getter!(self, WindowMessage::AvailableMonitors)
  1014. .into_iter()
  1015. .map(|m| MonitorHandleWrapper(m).into())
  1016. .collect(),
  1017. )
  1018. }
  1019. #[cfg(target_os = "macos")]
  1020. fn ns_window(&self) -> Result<*mut std::ffi::c_void> {
  1021. Ok(window_getter!(self, WindowMessage::NSWindow).0)
  1022. }
  1023. #[cfg(windows)]
  1024. fn hwnd(&self) -> Result<HWND> {
  1025. Ok(window_getter!(self, WindowMessage::Hwnd).0)
  1026. }
  1027. /// Returns the `ApplicatonWindow` from gtk crate that is used by this window.
  1028. #[cfg(any(
  1029. target_os = "linux",
  1030. target_os = "dragonfly",
  1031. target_os = "freebsd",
  1032. target_os = "netbsd",
  1033. target_os = "openbsd"
  1034. ))]
  1035. fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
  1036. Ok(window_getter!(self, WindowMessage::GtkWindow).0)
  1037. }
  1038. // Setters
  1039. fn center(&self) -> Result<()> {
  1040. window_getter!(self, WindowMessage::Center)
  1041. }
  1042. fn print(&self) -> Result<()> {
  1043. send_user_message(
  1044. &self.context,
  1045. Message::Webview(self.window_id, WebviewMessage::Print),
  1046. )
  1047. }
  1048. fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
  1049. send_user_message(
  1050. &self.context,
  1051. Message::Window(
  1052. self.window_id,
  1053. WindowMessage::RequestUserAttention(request_type.map(Into::into)),
  1054. ),
  1055. )
  1056. }
  1057. // Creates a window by dispatching a message to the event loop.
  1058. // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
  1059. fn create_window(
  1060. &mut self,
  1061. pending: PendingWindow<Self::Runtime>,
  1062. ) -> Result<DetachedWindow<Self::Runtime>> {
  1063. let (tx, rx) = channel();
  1064. let label = pending.label.clone();
  1065. let menu_ids = pending.menu_ids.clone();
  1066. let context = self.context.clone();
  1067. send_user_message(
  1068. &self.context,
  1069. Message::CreateWebview(
  1070. Box::new(move |event_loop, web_context| {
  1071. create_webview(event_loop, web_context, context, pending)
  1072. }),
  1073. tx,
  1074. ),
  1075. )?;
  1076. let window_id = rx.recv().unwrap();
  1077. let dispatcher = WryDispatcher {
  1078. window_id,
  1079. context: self.context.clone(),
  1080. };
  1081. Ok(DetachedWindow {
  1082. label,
  1083. dispatcher,
  1084. menu_ids,
  1085. })
  1086. }
  1087. fn set_resizable(&self, resizable: bool) -> Result<()> {
  1088. send_user_message(
  1089. &self.context,
  1090. Message::Window(self.window_id, WindowMessage::SetResizable(resizable)),
  1091. )
  1092. }
  1093. fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
  1094. send_user_message(
  1095. &self.context,
  1096. Message::Window(self.window_id, WindowMessage::SetTitle(title.into())),
  1097. )
  1098. }
  1099. fn maximize(&self) -> Result<()> {
  1100. send_user_message(
  1101. &self.context,
  1102. Message::Window(self.window_id, WindowMessage::Maximize),
  1103. )
  1104. }
  1105. fn unmaximize(&self) -> Result<()> {
  1106. send_user_message(
  1107. &self.context,
  1108. Message::Window(self.window_id, WindowMessage::Unmaximize),
  1109. )
  1110. }
  1111. fn minimize(&self) -> Result<()> {
  1112. send_user_message(
  1113. &self.context,
  1114. Message::Window(self.window_id, WindowMessage::Minimize),
  1115. )
  1116. }
  1117. fn unminimize(&self) -> Result<()> {
  1118. send_user_message(
  1119. &self.context,
  1120. Message::Window(self.window_id, WindowMessage::Unminimize),
  1121. )
  1122. }
  1123. fn show_menu(&self) -> Result<()> {
  1124. send_user_message(
  1125. &self.context,
  1126. Message::Window(self.window_id, WindowMessage::ShowMenu),
  1127. )
  1128. }
  1129. fn hide_menu(&self) -> Result<()> {
  1130. send_user_message(
  1131. &self.context,
  1132. Message::Window(self.window_id, WindowMessage::HideMenu),
  1133. )
  1134. }
  1135. fn show(&self) -> Result<()> {
  1136. send_user_message(
  1137. &self.context,
  1138. Message::Window(self.window_id, WindowMessage::Show),
  1139. )
  1140. }
  1141. fn hide(&self) -> Result<()> {
  1142. send_user_message(
  1143. &self.context,
  1144. Message::Window(self.window_id, WindowMessage::Hide),
  1145. )
  1146. }
  1147. fn close(&self) -> Result<()> {
  1148. // NOTE: close cannot use the `send_user_message` function because it accesses the event loop callback
  1149. self
  1150. .context
  1151. .proxy
  1152. .send_event(Message::Window(self.window_id, WindowMessage::Close))
  1153. .map_err(|_| Error::FailedToSendMessage)
  1154. }
  1155. fn set_decorations(&self, decorations: bool) -> Result<()> {
  1156. send_user_message(
  1157. &self.context,
  1158. Message::Window(self.window_id, WindowMessage::SetDecorations(decorations)),
  1159. )
  1160. }
  1161. fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
  1162. send_user_message(
  1163. &self.context,
  1164. Message::Window(self.window_id, WindowMessage::SetAlwaysOnTop(always_on_top)),
  1165. )
  1166. }
  1167. fn set_size(&self, size: Size) -> Result<()> {
  1168. send_user_message(
  1169. &self.context,
  1170. Message::Window(self.window_id, WindowMessage::SetSize(size)),
  1171. )
  1172. }
  1173. fn set_min_size(&self, size: Option<Size>) -> Result<()> {
  1174. send_user_message(
  1175. &self.context,
  1176. Message::Window(self.window_id, WindowMessage::SetMinSize(size)),
  1177. )
  1178. }
  1179. fn set_max_size(&self, size: Option<Size>) -> Result<()> {
  1180. send_user_message(
  1181. &self.context,
  1182. Message::Window(self.window_id, WindowMessage::SetMaxSize(size)),
  1183. )
  1184. }
  1185. fn set_position(&self, position: Position) -> Result<()> {
  1186. send_user_message(
  1187. &self.context,
  1188. Message::Window(self.window_id, WindowMessage::SetPosition(position)),
  1189. )
  1190. }
  1191. fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
  1192. send_user_message(
  1193. &self.context,
  1194. Message::Window(self.window_id, WindowMessage::SetFullscreen(fullscreen)),
  1195. )
  1196. }
  1197. fn set_focus(&self) -> Result<()> {
  1198. send_user_message(
  1199. &self.context,
  1200. Message::Window(self.window_id, WindowMessage::SetFocus),
  1201. )
  1202. }
  1203. fn set_icon(&self, icon: Icon) -> Result<()> {
  1204. send_user_message(
  1205. &self.context,
  1206. Message::Window(
  1207. self.window_id,
  1208. WindowMessage::SetIcon(WryIcon::try_from(icon)?.0),
  1209. ),
  1210. )
  1211. }
  1212. fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
  1213. send_user_message(
  1214. &self.context,
  1215. Message::Window(self.window_id, WindowMessage::SetSkipTaskbar(skip)),
  1216. )
  1217. }
  1218. fn start_dragging(&self) -> Result<()> {
  1219. send_user_message(
  1220. &self.context,
  1221. Message::Window(self.window_id, WindowMessage::DragWindow),
  1222. )
  1223. }
  1224. fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
  1225. send_user_message(
  1226. &self.context,
  1227. Message::Webview(
  1228. self.window_id,
  1229. WebviewMessage::EvaluateScript(script.into()),
  1230. ),
  1231. )
  1232. }
  1233. fn update_menu_item(&self, id: u16, update: MenuUpdate) -> Result<()> {
  1234. send_user_message(
  1235. &self.context,
  1236. Message::Window(self.window_id, WindowMessage::UpdateMenuItem(id, update)),
  1237. )
  1238. }
  1239. }
  1240. #[cfg(feature = "system-tray")]
  1241. #[derive(Clone, Default)]
  1242. struct TrayContext {
  1243. tray: Arc<Mutex<Option<Arc<Mutex<WrySystemTray>>>>>,
  1244. listeners: SystemTrayEventListeners,
  1245. items: SystemTrayItems,
  1246. }
  1247. #[cfg(feature = "system-tray")]
  1248. impl fmt::Debug for TrayContext {
  1249. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  1250. f.debug_struct("TrayContext")
  1251. .field("items", &self.items)
  1252. .finish()
  1253. }
  1254. }
  1255. enum WindowHandle {
  1256. Webview(WebView),
  1257. Window(Arc<Window>),
  1258. }
  1259. impl fmt::Debug for WindowHandle {
  1260. fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
  1261. Ok(())
  1262. }
  1263. }
  1264. impl WindowHandle {
  1265. fn window(&self) -> &Window {
  1266. match self {
  1267. Self::Webview(w) => w.window(),
  1268. Self::Window(w) => w,
  1269. }
  1270. }
  1271. fn inner_size(&self) -> WryPhysicalSize<u32> {
  1272. match self {
  1273. WindowHandle::Window(w) => w.inner_size(),
  1274. WindowHandle::Webview(w) => w.inner_size(),
  1275. }
  1276. }
  1277. }
  1278. #[derive(Debug)]
  1279. pub struct WindowWrapper {
  1280. label: String,
  1281. inner: WindowHandle,
  1282. menu_items: Option<HashMap<u16, WryCustomMenuItem>>,
  1283. }
  1284. /// A Tauri [`Runtime`] wrapper around wry.
  1285. pub struct Wry {
  1286. main_thread_id: ThreadId,
  1287. global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
  1288. global_shortcut_manager_handle: GlobalShortcutManagerHandle,
  1289. clipboard_manager: Arc<Mutex<Clipboard>>,
  1290. clipboard_manager_handle: ClipboardManagerWrapper,
  1291. event_loop: EventLoop<Message>,
  1292. windows: Arc<Mutex<HashMap<WindowId, WindowWrapper>>>,
  1293. web_context: WebContextStore,
  1294. window_event_listeners: WindowEventListeners,
  1295. menu_event_listeners: MenuEventListeners,
  1296. #[cfg(feature = "system-tray")]
  1297. tray_context: TrayContext,
  1298. }
  1299. /// A handle to the Wry runtime.
  1300. #[derive(Debug, Clone)]
  1301. pub struct WryHandle {
  1302. context: Context,
  1303. }
  1304. impl WryHandle {
  1305. /// Creates a new tao window using a callback, and returns its window id.
  1306. pub fn create_tao_window<F: FnOnce() -> (String, WryWindowBuilder) + Send + 'static>(
  1307. &self,
  1308. f: F,
  1309. ) -> Result<Weak<Window>> {
  1310. let (tx, rx) = channel();
  1311. send_user_message(&self.context, Message::CreateWindow(Box::new(f), tx))?;
  1312. rx.recv().unwrap()
  1313. }
  1314. /// Send a message to the event loop.
  1315. pub fn send_event(&self, message: Message) -> Result<()> {
  1316. self
  1317. .context
  1318. .proxy
  1319. .send_event(message)
  1320. .map_err(|_| Error::FailedToSendMessage)?;
  1321. Ok(())
  1322. }
  1323. }
  1324. impl RuntimeHandle for WryHandle {
  1325. type Runtime = Wry;
  1326. // Creates a window by dispatching a message to the event loop.
  1327. // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
  1328. fn create_window(
  1329. &self,
  1330. pending: PendingWindow<Self::Runtime>,
  1331. ) -> Result<DetachedWindow<Self::Runtime>> {
  1332. let (tx, rx) = channel();
  1333. let label = pending.label.clone();
  1334. let menu_ids = pending.menu_ids.clone();
  1335. let context = self.context.clone();
  1336. send_user_message(
  1337. &self.context,
  1338. Message::CreateWebview(
  1339. Box::new(move |event_loop, web_context| {
  1340. create_webview(event_loop, web_context, context, pending)
  1341. }),
  1342. tx,
  1343. ),
  1344. )?;
  1345. let window_id = rx.recv().unwrap();
  1346. let dispatcher = WryDispatcher {
  1347. window_id,
  1348. context: self.context.clone(),
  1349. };
  1350. Ok(DetachedWindow {
  1351. label,
  1352. dispatcher,
  1353. menu_ids,
  1354. })
  1355. }
  1356. fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
  1357. send_user_message(&self.context, Message::Task(Box::new(f)))
  1358. }
  1359. #[cfg(all(windows, feature = "system-tray"))]
  1360. /// Deprecated. (not needed anymore)
  1361. fn remove_system_tray(&self) -> Result<()> {
  1362. Ok(())
  1363. }
  1364. }
  1365. impl Runtime for Wry {
  1366. type Dispatcher = WryDispatcher;
  1367. type Handle = WryHandle;
  1368. type GlobalShortcutManager = GlobalShortcutManagerHandle;
  1369. type ClipboardManager = ClipboardManagerWrapper;
  1370. #[cfg(feature = "system-tray")]
  1371. type TrayHandler = SystemTrayHandle;
  1372. fn new() -> Result<Self> {
  1373. let event_loop = EventLoop::<Message>::with_user_event();
  1374. let proxy = event_loop.create_proxy();
  1375. let main_thread_id = current_thread().id();
  1376. let web_context = WebContextStore::default();
  1377. let global_shortcut_manager = Arc::new(Mutex::new(WryShortcutManager::new(&event_loop)));
  1378. let clipboard_manager = Arc::new(Mutex::new(Clipboard::new()));
  1379. let windows = Arc::new(Mutex::new(HashMap::default()));
  1380. let window_event_listeners = WindowEventListeners::default();
  1381. let menu_event_listeners = MenuEventListeners::default();
  1382. #[cfg(feature = "system-tray")]
  1383. let tray_context = TrayContext::default();
  1384. let event_loop_context = Context {
  1385. main_thread_id,
  1386. proxy,
  1387. window_event_listeners: window_event_listeners.clone(),
  1388. menu_event_listeners: menu_event_listeners.clone(),
  1389. main_thread: DispatcherMainThreadContext {
  1390. window_target: event_loop.deref().clone(),
  1391. web_context: web_context.clone(),
  1392. global_shortcut_manager: global_shortcut_manager.clone(),
  1393. clipboard_manager: clipboard_manager.clone(),
  1394. windows: windows.clone(),
  1395. #[cfg(feature = "system-tray")]
  1396. tray_context: tray_context.clone(),
  1397. },
  1398. };
  1399. let global_shortcut_listeners = GlobalShortcutListeners::default();
  1400. let clipboard_manager_handle = ClipboardManagerWrapper {
  1401. context: event_loop_context.clone(),
  1402. };
  1403. Ok(Self {
  1404. main_thread_id,
  1405. global_shortcut_manager,
  1406. global_shortcut_manager_handle: GlobalShortcutManagerHandle {
  1407. context: event_loop_context,
  1408. shortcuts: Default::default(),
  1409. listeners: global_shortcut_listeners,
  1410. },
  1411. clipboard_manager,
  1412. clipboard_manager_handle,
  1413. event_loop,
  1414. windows,
  1415. web_context,
  1416. window_event_listeners,
  1417. menu_event_listeners,
  1418. #[cfg(feature = "system-tray")]
  1419. tray_context,
  1420. })
  1421. }
  1422. fn handle(&self) -> Self::Handle {
  1423. WryHandle {
  1424. context: Context {
  1425. main_thread_id: self.main_thread_id,
  1426. proxy: self.event_loop.create_proxy(),
  1427. window_event_listeners: self.window_event_listeners.clone(),
  1428. menu_event_listeners: self.menu_event_listeners.clone(),
  1429. main_thread: DispatcherMainThreadContext {
  1430. window_target: self.event_loop.deref().clone(),
  1431. web_context: self.web_context.clone(),
  1432. global_shortcut_manager: self.global_shortcut_manager.clone(),
  1433. clipboard_manager: self.clipboard_manager.clone(),
  1434. windows: self.windows.clone(),
  1435. #[cfg(feature = "system-tray")]
  1436. tray_context: self.tray_context.clone(),
  1437. },
  1438. },
  1439. }
  1440. }
  1441. fn global_shortcut_manager(&self) -> Self::GlobalShortcutManager {
  1442. self.global_shortcut_manager_handle.clone()
  1443. }
  1444. fn clipboard_manager(&self) -> Self::ClipboardManager {
  1445. self.clipboard_manager_handle.clone()
  1446. }
  1447. fn create_window(&self, pending: PendingWindow<Self>) -> Result<DetachedWindow<Self>> {
  1448. let label = pending.label.clone();
  1449. let menu_ids = pending.menu_ids.clone();
  1450. let proxy = self.event_loop.create_proxy();
  1451. let webview = create_webview(
  1452. &self.event_loop,
  1453. &self.web_context,
  1454. Context {
  1455. main_thread_id: self.main_thread_id,
  1456. proxy: proxy.clone(),
  1457. window_event_listeners: self.window_event_listeners.clone(),
  1458. menu_event_listeners: self.menu_event_listeners.clone(),
  1459. main_thread: DispatcherMainThreadContext {
  1460. window_target: self.event_loop.deref().clone(),
  1461. web_context: self.web_context.clone(),
  1462. global_shortcut_manager: self.global_shortcut_manager.clone(),
  1463. clipboard_manager: self.clipboard_manager.clone(),
  1464. windows: self.windows.clone(),
  1465. #[cfg(feature = "system-tray")]
  1466. tray_context: self.tray_context.clone(),
  1467. },
  1468. },
  1469. pending,
  1470. )?;
  1471. #[cfg(target_os = "windows")]
  1472. {
  1473. let id = webview.inner.window().id();
  1474. if let WindowHandle::Webview(ref webview) = webview.inner {
  1475. if let Some(controller) = webview.controller() {
  1476. let proxy = self.event_loop.create_proxy();
  1477. let mut token = EventRegistrationToken::default();
  1478. unsafe {
  1479. controller.add_GotFocus(
  1480. FocusChangedEventHandler::create(Box::new(move |_, _| {
  1481. let _ = proxy.send_event(Message::Webview(
  1482. id,
  1483. WebviewMessage::WebviewEvent(WebviewEvent::Focused(true)),
  1484. ));
  1485. Ok(())
  1486. })),
  1487. &mut token,
  1488. )
  1489. }
  1490. .unwrap();
  1491. let proxy = self.event_loop.create_proxy();
  1492. unsafe {
  1493. controller.add_LostFocus(
  1494. FocusChangedEventHandler::create(Box::new(move |_, _| {
  1495. let _ = proxy.send_event(Message::Webview(
  1496. id,
  1497. WebviewMessage::WebviewEvent(WebviewEvent::Focused(false)),
  1498. ));
  1499. Ok(())
  1500. })),
  1501. &mut token,
  1502. )
  1503. }
  1504. .unwrap();
  1505. }
  1506. }
  1507. }
  1508. let dispatcher = WryDispatcher {
  1509. window_id: webview.inner.window().id(),
  1510. context: Context {
  1511. main_thread_id: self.main_thread_id,
  1512. proxy,
  1513. window_event_listeners: self.window_event_listeners.clone(),
  1514. menu_event_listeners: self.menu_event_listeners.clone(),
  1515. main_thread: DispatcherMainThreadContext {
  1516. window_target: self.event_loop.deref().clone(),
  1517. web_context: self.web_context.clone(),
  1518. global_shortcut_manager: self.global_shortcut_manager.clone(),
  1519. clipboard_manager: self.clipboard_manager.clone(),
  1520. windows: self.windows.clone(),
  1521. #[cfg(feature = "system-tray")]
  1522. tray_context: self.tray_context.clone(),
  1523. },
  1524. },
  1525. };
  1526. self
  1527. .windows
  1528. .lock()
  1529. .unwrap()
  1530. .insert(webview.inner.window().id(), webview);
  1531. Ok(DetachedWindow {
  1532. label,
  1533. dispatcher,
  1534. menu_ids,
  1535. })
  1536. }
  1537. #[cfg(feature = "system-tray")]
  1538. fn system_tray(&self, system_tray: SystemTray) -> Result<Self::TrayHandler> {
  1539. let icon = system_tray
  1540. .icon
  1541. .expect("tray icon not set")
  1542. .into_tray_icon();
  1543. let mut items = HashMap::new();
  1544. #[cfg(target_os = "macos")]
  1545. let tray = SystemTrayBuilder::new(
  1546. icon,
  1547. system_tray
  1548. .menu
  1549. .map(|menu| to_wry_context_menu(&mut items, menu)),
  1550. )
  1551. .with_icon_as_template(system_tray.icon_as_template)
  1552. .build(&self.event_loop)
  1553. .map_err(|e| Error::SystemTray(Box::new(e)))?;
  1554. #[cfg(not(target_os = "macos"))]
  1555. let tray = SystemTrayBuilder::new(
  1556. icon,
  1557. system_tray
  1558. .menu
  1559. .map(|menu| to_wry_context_menu(&mut items, menu)),
  1560. )
  1561. .build(&self.event_loop)
  1562. .map_err(|e| Error::SystemTray(Box::new(e)))?;
  1563. *self.tray_context.items.lock().unwrap() = items;
  1564. *self.tray_context.tray.lock().unwrap() = Some(Arc::new(Mutex::new(tray)));
  1565. Ok(SystemTrayHandle {
  1566. proxy: self.event_loop.create_proxy(),
  1567. })
  1568. }
  1569. #[cfg(feature = "system-tray")]
  1570. fn on_system_tray_event<F: Fn(&SystemTrayEvent) + Send + 'static>(&mut self, f: F) -> Uuid {
  1571. let id = Uuid::new_v4();
  1572. self
  1573. .tray_context
  1574. .listeners
  1575. .lock()
  1576. .unwrap()
  1577. .insert(id, Box::new(f));
  1578. id
  1579. }
  1580. #[cfg(target_os = "macos")]
  1581. fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
  1582. self
  1583. .event_loop
  1584. .set_activation_policy(match activation_policy {
  1585. ActivationPolicy::Regular => WryActivationPolicy::Regular,
  1586. ActivationPolicy::Accessory => WryActivationPolicy::Accessory,
  1587. ActivationPolicy::Prohibited => WryActivationPolicy::Prohibited,
  1588. _ => unimplemented!(),
  1589. });
  1590. }
  1591. #[cfg(any(target_os = "windows", target_os = "macos"))]
  1592. fn run_iteration<F: FnMut(RunEvent) + 'static>(&mut self, mut callback: F) -> RunIteration {
  1593. use wry::application::platform::run_return::EventLoopExtRunReturn;
  1594. let windows = self.windows.clone();
  1595. let web_context = &self.web_context;
  1596. let window_event_listeners = self.window_event_listeners.clone();
  1597. let menu_event_listeners = self.menu_event_listeners.clone();
  1598. #[cfg(feature = "system-tray")]
  1599. let tray_context = self.tray_context.clone();
  1600. let global_shortcut_manager = self.global_shortcut_manager.clone();
  1601. let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone();
  1602. let clipboard_manager = self.clipboard_manager.clone();
  1603. let mut iteration = RunIteration::default();
  1604. self
  1605. .event_loop
  1606. .run_return(|event, event_loop, control_flow| {
  1607. if let Event::MainEventsCleared = &event {
  1608. *control_flow = ControlFlow::Exit;
  1609. }
  1610. iteration = handle_event_loop(
  1611. event,
  1612. event_loop,
  1613. control_flow,
  1614. EventLoopIterationContext {
  1615. callback: &mut callback,
  1616. windows: windows.clone(),
  1617. window_event_listeners: &window_event_listeners,
  1618. global_shortcut_manager: global_shortcut_manager.clone(),
  1619. global_shortcut_manager_handle: &global_shortcut_manager_handle,
  1620. clipboard_manager: clipboard_manager.clone(),
  1621. menu_event_listeners: &menu_event_listeners,
  1622. #[cfg(feature = "system-tray")]
  1623. tray_context: &tray_context,
  1624. },
  1625. web_context,
  1626. );
  1627. });
  1628. iteration
  1629. }
  1630. fn run<F: FnMut(RunEvent) + 'static>(self, mut callback: F) {
  1631. let windows = self.windows.clone();
  1632. let web_context = self.web_context;
  1633. let window_event_listeners = self.window_event_listeners.clone();
  1634. let menu_event_listeners = self.menu_event_listeners.clone();
  1635. #[cfg(feature = "system-tray")]
  1636. let tray_context = self.tray_context;
  1637. let global_shortcut_manager = self.global_shortcut_manager.clone();
  1638. let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone();
  1639. let clipboard_manager = self.clipboard_manager.clone();
  1640. self.event_loop.run(move |event, event_loop, control_flow| {
  1641. handle_event_loop(
  1642. event,
  1643. event_loop,
  1644. control_flow,
  1645. EventLoopIterationContext {
  1646. callback: &mut callback,
  1647. windows: windows.clone(),
  1648. window_event_listeners: &window_event_listeners,
  1649. global_shortcut_manager: global_shortcut_manager.clone(),
  1650. global_shortcut_manager_handle: &global_shortcut_manager_handle,
  1651. clipboard_manager: clipboard_manager.clone(),
  1652. menu_event_listeners: &menu_event_listeners,
  1653. #[cfg(feature = "system-tray")]
  1654. tray_context: &tray_context,
  1655. },
  1656. &web_context,
  1657. );
  1658. })
  1659. }
  1660. }
  1661. struct EventLoopIterationContext<'a> {
  1662. callback: &'a mut (dyn FnMut(RunEvent) + 'static),
  1663. windows: Arc<Mutex<HashMap<WindowId, WindowWrapper>>>,
  1664. window_event_listeners: &'a WindowEventListeners,
  1665. global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
  1666. global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle,
  1667. clipboard_manager: Arc<Mutex<Clipboard>>,
  1668. menu_event_listeners: &'a MenuEventListeners,
  1669. #[cfg(feature = "system-tray")]
  1670. tray_context: &'a TrayContext,
  1671. }
  1672. struct UserMessageContext<'a> {
  1673. window_event_listeners: &'a WindowEventListeners,
  1674. global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
  1675. clipboard_manager: Arc<Mutex<Clipboard>>,
  1676. menu_event_listeners: &'a MenuEventListeners,
  1677. windows: Arc<Mutex<HashMap<WindowId, WindowWrapper>>>,
  1678. #[cfg(feature = "system-tray")]
  1679. tray_context: &'a TrayContext,
  1680. }
  1681. fn handle_user_message(
  1682. event_loop: &EventLoopWindowTarget<Message>,
  1683. message: Message,
  1684. context: UserMessageContext<'_>,
  1685. web_context: &WebContextStore,
  1686. ) -> RunIteration {
  1687. let UserMessageContext {
  1688. window_event_listeners,
  1689. menu_event_listeners,
  1690. global_shortcut_manager,
  1691. clipboard_manager,
  1692. windows,
  1693. #[cfg(feature = "system-tray")]
  1694. tray_context,
  1695. } = context;
  1696. match message {
  1697. Message::Task(task) => task(),
  1698. Message::Window(id, window_message) => {
  1699. if let Some(webview) = windows
  1700. .lock()
  1701. .expect("poisoned webview collection")
  1702. .get_mut(&id)
  1703. {
  1704. let window = webview.inner.window();
  1705. match window_message {
  1706. // Getters
  1707. WindowMessage::ScaleFactor(tx) => tx.send(window.scale_factor()).unwrap(),
  1708. WindowMessage::InnerPosition(tx) => tx
  1709. .send(
  1710. window
  1711. .inner_position()
  1712. .map(|p| PhysicalPositionWrapper(p).into())
  1713. .map_err(|_| Error::FailedToSendMessage),
  1714. )
  1715. .unwrap(),
  1716. WindowMessage::OuterPosition(tx) => tx
  1717. .send(
  1718. window
  1719. .outer_position()
  1720. .map(|p| PhysicalPositionWrapper(p).into())
  1721. .map_err(|_| Error::FailedToSendMessage),
  1722. )
  1723. .unwrap(),
  1724. WindowMessage::InnerSize(tx) => tx
  1725. .send(PhysicalSizeWrapper(webview.inner.inner_size()).into())
  1726. .unwrap(),
  1727. WindowMessage::OuterSize(tx) => tx
  1728. .send(PhysicalSizeWrapper(window.outer_size()).into())
  1729. .unwrap(),
  1730. WindowMessage::IsFullscreen(tx) => tx.send(window.fullscreen().is_some()).unwrap(),
  1731. WindowMessage::IsMaximized(tx) => tx.send(window.is_maximized()).unwrap(),
  1732. WindowMessage::IsDecorated(tx) => tx.send(window.is_decorated()).unwrap(),
  1733. WindowMessage::IsResizable(tx) => tx.send(window.is_resizable()).unwrap(),
  1734. WindowMessage::IsVisible(tx) => tx.send(window.is_visible()).unwrap(),
  1735. WindowMessage::IsMenuVisible(tx) => tx.send(window.is_menu_visible()).unwrap(),
  1736. WindowMessage::CurrentMonitor(tx) => tx.send(window.current_monitor()).unwrap(),
  1737. WindowMessage::PrimaryMonitor(tx) => tx.send(window.primary_monitor()).unwrap(),
  1738. WindowMessage::AvailableMonitors(tx) => {
  1739. tx.send(window.available_monitors().collect()).unwrap()
  1740. }
  1741. #[cfg(target_os = "macos")]
  1742. WindowMessage::NSWindow(tx) => tx.send(NSWindow(window.ns_window())).unwrap(),
  1743. #[cfg(windows)]
  1744. WindowMessage::Hwnd(tx) => tx.send(Hwnd(HWND(window.hwnd() as _))).unwrap(),
  1745. #[cfg(any(
  1746. target_os = "linux",
  1747. target_os = "dragonfly",
  1748. target_os = "freebsd",
  1749. target_os = "netbsd",
  1750. target_os = "openbsd"
  1751. ))]
  1752. WindowMessage::GtkWindow(tx) => tx.send(GtkWindow(window.gtk_window().clone())).unwrap(),
  1753. // Setters
  1754. WindowMessage::Center(tx) => {
  1755. tx.send(center_window(window, webview.inner.inner_size()))
  1756. .unwrap();
  1757. }
  1758. WindowMessage::RequestUserAttention(request_type) => {
  1759. window.request_user_attention(request_type.map(|r| r.0));
  1760. }
  1761. WindowMessage::SetResizable(resizable) => window.set_resizable(resizable),
  1762. WindowMessage::SetTitle(title) => window.set_title(&title),
  1763. WindowMessage::Maximize => window.set_maximized(true),
  1764. WindowMessage::Unmaximize => window.set_maximized(false),
  1765. WindowMessage::Minimize => window.set_minimized(true),
  1766. WindowMessage::Unminimize => window.set_minimized(false),
  1767. WindowMessage::ShowMenu => window.show_menu(),
  1768. WindowMessage::HideMenu => window.hide_menu(),
  1769. WindowMessage::Show => window.set_visible(true),
  1770. WindowMessage::Hide => window.set_visible(false),
  1771. WindowMessage::Close => panic!("cannot handle `WindowMessage::Close` on the main thread"),
  1772. WindowMessage::SetDecorations(decorations) => window.set_decorations(decorations),
  1773. WindowMessage::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top),
  1774. WindowMessage::SetSize(size) => {
  1775. window.set_inner_size(SizeWrapper::from(size).0);
  1776. }
  1777. WindowMessage::SetMinSize(size) => {
  1778. window.set_min_inner_size(size.map(|s| SizeWrapper::from(s).0));
  1779. }
  1780. WindowMessage::SetMaxSize(size) => {
  1781. window.set_max_inner_size(size.map(|s| SizeWrapper::from(s).0));
  1782. }
  1783. WindowMessage::SetPosition(position) => {
  1784. window.set_outer_position(PositionWrapper::from(position).0)
  1785. }
  1786. WindowMessage::SetFullscreen(fullscreen) => {
  1787. if fullscreen {
  1788. window.set_fullscreen(Some(Fullscreen::Borderless(None)))
  1789. } else {
  1790. window.set_fullscreen(None)
  1791. }
  1792. }
  1793. WindowMessage::SetFocus => {
  1794. window.set_focus();
  1795. }
  1796. WindowMessage::SetIcon(icon) => {
  1797. window.set_window_icon(Some(icon));
  1798. }
  1799. WindowMessage::SetSkipTaskbar(_skip) => {
  1800. #[cfg(any(target_os = "windows", target_os = "linux"))]
  1801. window.set_skip_taskbar(_skip);
  1802. }
  1803. WindowMessage::DragWindow => {
  1804. let _ = window.drag_window();
  1805. }
  1806. WindowMessage::UpdateMenuItem(id, update) => {
  1807. if let Some(menu_items) = webview.menu_items.as_mut() {
  1808. let item = menu_items.get_mut(&id).expect("menu item not found");
  1809. match update {
  1810. MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
  1811. MenuUpdate::SetTitle(title) => item.set_title(&title),
  1812. MenuUpdate::SetSelected(selected) => item.set_selected(selected),
  1813. #[cfg(target_os = "macos")]
  1814. MenuUpdate::SetNativeImage(image) => {
  1815. item.set_native_image(NativeImageWrapper::from(image).0)
  1816. }
  1817. }
  1818. }
  1819. }
  1820. }
  1821. }
  1822. }
  1823. Message::Webview(id, webview_message) => match webview_message {
  1824. WebviewMessage::EvaluateScript(script) => {
  1825. if let Some(WindowHandle::Webview(webview)) = windows
  1826. .lock()
  1827. .expect("poisoned webview collection")
  1828. .get(&id)
  1829. .map(|w| &w.inner)
  1830. {
  1831. if let Err(e) = webview.evaluate_script(&script) {
  1832. eprintln!("{}", e);
  1833. }
  1834. }
  1835. }
  1836. WebviewMessage::Print => {
  1837. if let Some(WindowHandle::Webview(webview)) = windows
  1838. .lock()
  1839. .expect("poisoned webview collection")
  1840. .get(&id)
  1841. .map(|w| &w.inner)
  1842. {
  1843. let _ = webview.print();
  1844. }
  1845. }
  1846. WebviewMessage::WebviewEvent(event) => {
  1847. if let Some(event) = WindowEventWrapper::from(&event).0 {
  1848. for handler in window_event_listeners
  1849. .lock()
  1850. .unwrap()
  1851. .get(&id)
  1852. .unwrap()
  1853. .lock()
  1854. .unwrap()
  1855. .values()
  1856. {
  1857. handler(&event);
  1858. }
  1859. }
  1860. }
  1861. },
  1862. Message::CreateWebview(handler, sender) => match handler(event_loop, web_context) {
  1863. Ok(webview) => {
  1864. let window_id = webview.inner.window().id();
  1865. windows
  1866. .lock()
  1867. .expect("poisoned webview collection")
  1868. .insert(window_id, webview);
  1869. sender.send(window_id).unwrap();
  1870. }
  1871. Err(e) => {
  1872. eprintln!("{}", e);
  1873. }
  1874. },
  1875. Message::CreateWindow(handler, sender) => {
  1876. let (label, builder) = handler();
  1877. if let Ok(window) = builder.build(event_loop) {
  1878. let window_id = window.id();
  1879. window_event_listeners
  1880. .lock()
  1881. .unwrap()
  1882. .insert(window.id(), WindowEventListenersMap::default());
  1883. menu_event_listeners
  1884. .lock()
  1885. .unwrap()
  1886. .insert(window.id(), WindowMenuEventListeners::default());
  1887. let w = Arc::new(window);
  1888. windows.lock().expect("poisoned webview collection").insert(
  1889. window_id,
  1890. WindowWrapper {
  1891. label,
  1892. inner: WindowHandle::Window(w.clone()),
  1893. menu_items: Default::default(),
  1894. },
  1895. );
  1896. sender.send(Ok(Arc::downgrade(&w))).unwrap();
  1897. } else {
  1898. sender.send(Err(Error::CreateWindow)).unwrap();
  1899. }
  1900. }
  1901. #[cfg(feature = "system-tray")]
  1902. Message::Tray(tray_message) => match tray_message {
  1903. TrayMessage::UpdateItem(menu_id, update) => {
  1904. let mut tray = tray_context.items.as_ref().lock().unwrap();
  1905. let item = tray.get_mut(&menu_id).expect("menu item not found");
  1906. match update {
  1907. MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
  1908. MenuUpdate::SetTitle(title) => item.set_title(&title),
  1909. MenuUpdate::SetSelected(selected) => item.set_selected(selected),
  1910. #[cfg(target_os = "macos")]
  1911. MenuUpdate::SetNativeImage(image) => {
  1912. item.set_native_image(NativeImageWrapper::from(image).0)
  1913. }
  1914. }
  1915. }
  1916. TrayMessage::UpdateMenu(menu) => {
  1917. if let Some(tray) = &*tray_context.tray.lock().unwrap() {
  1918. let mut items = HashMap::new();
  1919. tray
  1920. .lock()
  1921. .unwrap()
  1922. .set_menu(&to_wry_context_menu(&mut items, menu));
  1923. *tray_context.items.lock().unwrap() = items;
  1924. }
  1925. }
  1926. TrayMessage::UpdateIcon(icon) => {
  1927. if let Some(tray) = &*tray_context.tray.lock().unwrap() {
  1928. tray.lock().unwrap().set_icon(icon.into_tray_icon());
  1929. }
  1930. }
  1931. #[cfg(target_os = "macos")]
  1932. TrayMessage::UpdateIconAsTemplate(is_template) => {
  1933. if let Some(tray) = &*tray_context.tray.lock().unwrap() {
  1934. tray.lock().unwrap().set_icon_as_template(is_template);
  1935. }
  1936. }
  1937. },
  1938. Message::GlobalShortcut(message) => match message {
  1939. GlobalShortcutMessage::IsRegistered(accelerator, tx) => tx
  1940. .send(
  1941. global_shortcut_manager
  1942. .lock()
  1943. .unwrap()
  1944. .is_registered(&accelerator),
  1945. )
  1946. .unwrap(),
  1947. GlobalShortcutMessage::Register(accelerator, tx) => tx
  1948. .send(
  1949. global_shortcut_manager
  1950. .lock()
  1951. .unwrap()
  1952. .register(accelerator)
  1953. .map(GlobalShortcutWrapper)
  1954. .map_err(|e| Error::GlobalShortcut(Box::new(e))),
  1955. )
  1956. .unwrap(),
  1957. GlobalShortcutMessage::Unregister(shortcut, tx) => tx
  1958. .send(
  1959. global_shortcut_manager
  1960. .lock()
  1961. .unwrap()
  1962. .unregister(shortcut.0)
  1963. .map_err(|e| Error::GlobalShortcut(Box::new(e))),
  1964. )
  1965. .unwrap(),
  1966. GlobalShortcutMessage::UnregisterAll(tx) => tx
  1967. .send(
  1968. global_shortcut_manager
  1969. .lock()
  1970. .unwrap()
  1971. .unregister_all()
  1972. .map_err(|e| Error::GlobalShortcut(Box::new(e))),
  1973. )
  1974. .unwrap(),
  1975. },
  1976. Message::Clipboard(message) => match message {
  1977. ClipboardMessage::WriteText(text, tx) => {
  1978. clipboard_manager.lock().unwrap().write_text(text);
  1979. tx.send(()).unwrap();
  1980. }
  1981. ClipboardMessage::ReadText(tx) => tx
  1982. .send(clipboard_manager.lock().unwrap().read_text())
  1983. .unwrap(),
  1984. },
  1985. }
  1986. let it = RunIteration {
  1987. window_count: windows.lock().expect("poisoned webview collection").len(),
  1988. };
  1989. it
  1990. }
  1991. fn handle_event_loop(
  1992. event: Event<Message>,
  1993. event_loop: &EventLoopWindowTarget<Message>,
  1994. control_flow: &mut ControlFlow,
  1995. context: EventLoopIterationContext<'_>,
  1996. web_context: &WebContextStore,
  1997. ) -> RunIteration {
  1998. let EventLoopIterationContext {
  1999. callback,
  2000. windows,
  2001. window_event_listeners,
  2002. global_shortcut_manager,
  2003. global_shortcut_manager_handle,
  2004. clipboard_manager,
  2005. menu_event_listeners,
  2006. #[cfg(feature = "system-tray")]
  2007. tray_context,
  2008. } = context;
  2009. if *control_flow == ControlFlow::Exit {
  2010. return RunIteration {
  2011. window_count: windows.lock().expect("poisoned webview collection").len(),
  2012. };
  2013. }
  2014. *control_flow = ControlFlow::Wait;
  2015. match event {
  2016. Event::NewEvents(StartCause::Init) => {
  2017. callback(RunEvent::Ready);
  2018. }
  2019. Event::NewEvents(StartCause::Poll) => {
  2020. callback(RunEvent::Resumed);
  2021. }
  2022. Event::MainEventsCleared => {
  2023. callback(RunEvent::MainEventsCleared);
  2024. }
  2025. Event::GlobalShortcutEvent(accelerator_id) => {
  2026. for (id, handler) in &*global_shortcut_manager_handle.listeners.lock().unwrap() {
  2027. if accelerator_id == *id {
  2028. handler();
  2029. }
  2030. }
  2031. }
  2032. Event::MenuEvent {
  2033. window_id,
  2034. menu_id,
  2035. origin: MenuType::MenuBar,
  2036. ..
  2037. } => {
  2038. let window_id = window_id.unwrap(); // always Some on MenuBar event
  2039. let event = MenuEvent {
  2040. menu_item_id: menu_id.0,
  2041. };
  2042. let listeners = menu_event_listeners.lock().unwrap();
  2043. let window_menu_event_listeners = listeners.get(&window_id).cloned().unwrap_or_default();
  2044. for handler in window_menu_event_listeners.lock().unwrap().values() {
  2045. handler(&event);
  2046. }
  2047. }
  2048. #[cfg(feature = "system-tray")]
  2049. Event::MenuEvent {
  2050. window_id: _,
  2051. menu_id,
  2052. origin: MenuType::ContextMenu,
  2053. ..
  2054. } => {
  2055. let event = SystemTrayEvent::MenuItemClick(menu_id.0);
  2056. for handler in tray_context.listeners.lock().unwrap().values() {
  2057. handler(&event);
  2058. }
  2059. }
  2060. #[cfg(feature = "system-tray")]
  2061. Event::TrayEvent {
  2062. bounds,
  2063. event,
  2064. position: _cursor_position,
  2065. ..
  2066. } => {
  2067. let (position, size) = (
  2068. PhysicalPositionWrapper(bounds.position).into(),
  2069. PhysicalSizeWrapper(bounds.size).into(),
  2070. );
  2071. let event = match event {
  2072. TrayEvent::RightClick => SystemTrayEvent::RightClick { position, size },
  2073. TrayEvent::DoubleClick => SystemTrayEvent::DoubleClick { position, size },
  2074. // default to left click
  2075. _ => SystemTrayEvent::LeftClick { position, size },
  2076. };
  2077. for handler in tray_context.listeners.lock().unwrap().values() {
  2078. handler(&event);
  2079. }
  2080. }
  2081. Event::WindowEvent {
  2082. event, window_id, ..
  2083. } => {
  2084. // NOTE(amrbashir): we handle this event here instead of `match` statement below because
  2085. // we want to focus the webview as soon as possible, especially on windows.
  2086. if event == WryWindowEvent::Focused(true) {
  2087. if let Some(WindowHandle::Webview(webview)) = windows
  2088. .lock()
  2089. .expect("poisoned webview collection")
  2090. .get(&window_id)
  2091. .map(|w| &w.inner)
  2092. {
  2093. webview.focus();
  2094. }
  2095. }
  2096. {
  2097. let windows_lock = windows.lock().expect("poisoned webview collection");
  2098. if let Some(window_handle) = windows_lock.get(&window_id).map(|w| &w.inner) {
  2099. if let Some(event) = WindowEventWrapper::parse(window_handle, &event).0 {
  2100. drop(windows_lock);
  2101. for handler in window_event_listeners
  2102. .lock()
  2103. .unwrap()
  2104. .get(&window_id)
  2105. .unwrap()
  2106. .lock()
  2107. .unwrap()
  2108. .values()
  2109. {
  2110. handler(&event);
  2111. }
  2112. }
  2113. }
  2114. }
  2115. match event {
  2116. WryWindowEvent::CloseRequested => {
  2117. let (tx, rx) = channel();
  2118. let windows_guard = windows.lock().expect("poisoned webview collection");
  2119. if let Some(w) = windows_guard.get(&window_id) {
  2120. let label = w.label.clone();
  2121. drop(windows_guard);
  2122. callback(RunEvent::CloseRequested {
  2123. label,
  2124. signal_tx: tx,
  2125. });
  2126. if let Ok(true) = rx.try_recv() {
  2127. } else {
  2128. on_window_close(
  2129. callback,
  2130. window_id,
  2131. windows.lock().expect("poisoned webview collection"),
  2132. control_flow,
  2133. #[cfg(target_os = "linux")]
  2134. window_event_listeners,
  2135. menu_event_listeners.clone(),
  2136. );
  2137. }
  2138. }
  2139. }
  2140. WryWindowEvent::Resized(_) => {
  2141. if let Some(WindowHandle::Webview(webview)) = windows
  2142. .lock()
  2143. .expect("poisoned webview collection")
  2144. .get(&window_id)
  2145. .map(|w| &w.inner)
  2146. {
  2147. if let Err(e) = webview.resize() {
  2148. eprintln!("{}", e);
  2149. }
  2150. }
  2151. }
  2152. _ => {}
  2153. }
  2154. }
  2155. Event::UserEvent(message) => {
  2156. if let Message::Window(id, WindowMessage::Close) = message {
  2157. on_window_close(
  2158. callback,
  2159. id,
  2160. windows.lock().expect("poisoned webview collection"),
  2161. control_flow,
  2162. #[cfg(target_os = "linux")]
  2163. window_event_listeners,
  2164. menu_event_listeners.clone(),
  2165. );
  2166. } else {
  2167. return handle_user_message(
  2168. event_loop,
  2169. message,
  2170. UserMessageContext {
  2171. window_event_listeners,
  2172. global_shortcut_manager,
  2173. clipboard_manager,
  2174. menu_event_listeners,
  2175. windows,
  2176. #[cfg(feature = "system-tray")]
  2177. tray_context,
  2178. },
  2179. web_context,
  2180. );
  2181. }
  2182. }
  2183. _ => (),
  2184. }
  2185. let it = RunIteration {
  2186. window_count: windows.lock().expect("poisoned webview collection").len(),
  2187. };
  2188. it
  2189. }
  2190. fn on_window_close<'a>(
  2191. callback: &'a mut (dyn FnMut(RunEvent) + 'static),
  2192. window_id: WindowId,
  2193. mut windows: MutexGuard<'a, HashMap<WindowId, WindowWrapper>>,
  2194. control_flow: &mut ControlFlow,
  2195. #[cfg(target_os = "linux")] window_event_listeners: &WindowEventListeners,
  2196. menu_event_listeners: MenuEventListeners,
  2197. ) {
  2198. if let Some(webview) = windows.remove(&window_id) {
  2199. let is_empty = windows.is_empty();
  2200. drop(windows);
  2201. menu_event_listeners.lock().unwrap().remove(&window_id);
  2202. callback(RunEvent::WindowClose(webview.label.clone()));
  2203. if is_empty {
  2204. let (tx, rx) = channel();
  2205. callback(RunEvent::ExitRequested {
  2206. window_label: webview.label,
  2207. tx,
  2208. });
  2209. let recv = rx.try_recv();
  2210. let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
  2211. if !should_prevent {
  2212. *control_flow = ControlFlow::Exit;
  2213. callback(RunEvent::Exit);
  2214. }
  2215. }
  2216. }
  2217. // TODO: tao does not fire the destroyed event properly
  2218. #[cfg(target_os = "linux")]
  2219. {
  2220. for handler in window_event_listeners
  2221. .lock()
  2222. .unwrap()
  2223. .get(&window_id)
  2224. .unwrap()
  2225. .lock()
  2226. .unwrap()
  2227. .values()
  2228. {
  2229. handler(&WindowEvent::Destroyed);
  2230. }
  2231. }
  2232. }
  2233. fn center_window(window: &Window, window_size: WryPhysicalSize<u32>) -> Result<()> {
  2234. if let Some(monitor) = window.current_monitor() {
  2235. let screen_size = monitor.size();
  2236. let x = (screen_size.width - window_size.width) / 2;
  2237. let y = (screen_size.height - window_size.height) / 2;
  2238. window.set_outer_position(WryPhysicalPosition::new(x, y));
  2239. Ok(())
  2240. } else {
  2241. Err(Error::FailedToGetMonitor)
  2242. }
  2243. }
  2244. fn to_wry_menu(
  2245. custom_menu_items: &mut HashMap<MenuHash, WryCustomMenuItem>,
  2246. menu: Menu,
  2247. ) -> MenuBar {
  2248. let mut wry_menu = MenuBar::new();
  2249. for item in menu.items {
  2250. match item {
  2251. MenuEntry::CustomItem(c) => {
  2252. let mut attributes = MenuItemAttributesWrapper::from(&c).0;
  2253. attributes = attributes.with_id(WryMenuId(c.id));
  2254. #[allow(unused_mut)]
  2255. let mut item = wry_menu.add_item(attributes);
  2256. #[cfg(target_os = "macos")]
  2257. if let Some(native_image) = c.native_image {
  2258. item.set_native_image(NativeImageWrapper::from(native_image).0);
  2259. }
  2260. custom_menu_items.insert(c.id, item);
  2261. }
  2262. MenuEntry::NativeItem(i) => {
  2263. wry_menu.add_native_item(MenuItemWrapper::from(i).0);
  2264. }
  2265. MenuEntry::Submenu(submenu) => {
  2266. wry_menu.add_submenu(
  2267. &submenu.title,
  2268. submenu.enabled,
  2269. to_wry_menu(custom_menu_items, submenu.inner),
  2270. );
  2271. }
  2272. }
  2273. }
  2274. wry_menu
  2275. }
  2276. fn create_webview(
  2277. event_loop: &EventLoopWindowTarget<Message>,
  2278. web_context: &WebContextStore,
  2279. context: Context,
  2280. pending: PendingWindow<Wry>,
  2281. ) -> Result<WindowWrapper> {
  2282. #[allow(unused_mut)]
  2283. let PendingWindow {
  2284. webview_attributes,
  2285. uri_scheme_protocols,
  2286. mut window_builder,
  2287. rpc_handler,
  2288. file_drop_handler,
  2289. label,
  2290. url,
  2291. menu_ids,
  2292. ..
  2293. } = pending;
  2294. let is_window_transparent = window_builder.inner.window.transparent;
  2295. let menu_items = if let Some(menu) = window_builder.menu {
  2296. let mut menu_items = HashMap::new();
  2297. let menu = to_wry_menu(&mut menu_items, menu);
  2298. window_builder.inner = window_builder.inner.with_menu(menu);
  2299. Some(menu_items)
  2300. } else {
  2301. None
  2302. };
  2303. let window = window_builder.inner.build(event_loop).unwrap();
  2304. context
  2305. .window_event_listeners
  2306. .lock()
  2307. .unwrap()
  2308. .insert(window.id(), WindowEventListenersMap::default());
  2309. context
  2310. .menu_event_listeners
  2311. .lock()
  2312. .unwrap()
  2313. .insert(window.id(), WindowMenuEventListeners::default());
  2314. if window_builder.center {
  2315. let _ = center_window(&window, window.inner_size());
  2316. }
  2317. let mut webview_builder = WebViewBuilder::new(window)
  2318. .map_err(|e| Error::CreateWebview(Box::new(e)))?
  2319. .with_url(&url)
  2320. .unwrap() // safe to unwrap because we validate the URL beforehand
  2321. .with_transparent(is_window_transparent);
  2322. if let Some(handler) = rpc_handler {
  2323. webview_builder = webview_builder.with_rpc_handler(create_rpc_handler(
  2324. context.clone(),
  2325. label.clone(),
  2326. menu_ids.clone(),
  2327. handler,
  2328. ));
  2329. }
  2330. if let Some(handler) = file_drop_handler {
  2331. webview_builder = webview_builder.with_file_drop_handler(create_file_drop_handler(
  2332. context,
  2333. label.clone(),
  2334. menu_ids,
  2335. handler,
  2336. ));
  2337. }
  2338. for (scheme, protocol) in uri_scheme_protocols {
  2339. webview_builder = webview_builder.with_custom_protocol(scheme, move |wry_request| {
  2340. protocol(&HttpRequestWrapper::from(wry_request).0)
  2341. .map(|tauri_response| HttpResponseWrapper::from(tauri_response).0)
  2342. .map_err(|_| wry::Error::InitScriptError)
  2343. });
  2344. }
  2345. for script in webview_attributes.initialization_scripts {
  2346. webview_builder = webview_builder.with_initialization_script(&script);
  2347. }
  2348. let mut web_context = web_context.lock().expect("poisoned WebContext store");
  2349. let is_first_context = web_context.is_empty();
  2350. let automation_enabled = std::env::var("TAURI_AUTOMATION").as_deref() == Ok("true");
  2351. let web_context = match web_context.entry(
  2352. // force a unique WebContext when automation is false;
  2353. // the context must be stored on the HashMap because it must outlive the WebView on macOS
  2354. if automation_enabled {
  2355. webview_attributes.data_directory.clone()
  2356. } else {
  2357. // random unique key
  2358. Some(Uuid::new_v4().to_hyphenated().to_string().into())
  2359. },
  2360. ) {
  2361. Occupied(occupied) => occupied.into_mut(),
  2362. Vacant(vacant) => {
  2363. let mut web_context = WebContext::new(webview_attributes.data_directory);
  2364. web_context.set_allows_automation(if automation_enabled {
  2365. is_first_context
  2366. } else {
  2367. false
  2368. });
  2369. vacant.insert(web_context)
  2370. }
  2371. };
  2372. let webview = webview_builder
  2373. .with_web_context(web_context)
  2374. .build()
  2375. .map_err(|e| Error::CreateWebview(Box::new(e)))?;
  2376. Ok(WindowWrapper {
  2377. label,
  2378. inner: WindowHandle::Webview(webview),
  2379. menu_items,
  2380. })
  2381. }
  2382. /// Create a wry rpc handler from a tauri rpc handler.
  2383. fn create_rpc_handler(
  2384. context: Context,
  2385. label: String,
  2386. menu_ids: HashMap<MenuHash, MenuId>,
  2387. handler: WebviewRpcHandler<Wry>,
  2388. ) -> Box<dyn Fn(&Window, WryRpcRequest) -> Option<RpcResponse> + 'static> {
  2389. Box::new(move |window, request| {
  2390. handler(
  2391. DetachedWindow {
  2392. dispatcher: WryDispatcher {
  2393. window_id: window.id(),
  2394. context: context.clone(),
  2395. },
  2396. label: label.clone(),
  2397. menu_ids: menu_ids.clone(),
  2398. },
  2399. RpcRequestWrapper(request).into(),
  2400. );
  2401. None
  2402. })
  2403. }
  2404. /// Create a wry file drop handler from a tauri file drop handler.
  2405. fn create_file_drop_handler(
  2406. context: Context,
  2407. label: String,
  2408. menu_ids: HashMap<MenuHash, MenuId>,
  2409. handler: FileDropHandler<Wry>,
  2410. ) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
  2411. Box::new(move |window, event| {
  2412. handler(
  2413. FileDropEventWrapper(event).into(),
  2414. DetachedWindow {
  2415. dispatcher: WryDispatcher {
  2416. window_id: window.id(),
  2417. context: context.clone(),
  2418. },
  2419. label: label.clone(),
  2420. menu_ids: menu_ids.clone(),
  2421. },
  2422. )
  2423. })
  2424. }