lib.rs 98 KB

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