lib.rs 83 KB

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