1#![doc(
11 html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
12 html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
13)]
14
15use self::monitor::MonitorExt;
16use http::Request;
17#[cfg(target_os = "macos")]
18use objc2::ClassType;
19use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
20
21#[cfg(windows)]
22use tauri_runtime::webview::ScrollBarStyle;
23use tauri_runtime::{
24 Cookie, DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon,
25 ProgressBarState, ProgressBarStatus, Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs,
26 UserAttentionType, UserEvent, WebviewDispatch, WebviewEventId, WindowDispatch, WindowEventId,
27 dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
28 monitor::Monitor,
29 webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler},
30 window::{
31 CursorIcon, DetachedWindow, DetachedWindowWebview, DragDropEvent, PendingWindow, RawWindow,
32 WebviewEvent, WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, WindowSizeConstraints,
33 },
34};
35
36#[cfg(target_vendor = "apple")]
37use objc2::rc::Retained;
38#[cfg(target_os = "android")]
39use tao::platform::android::{WindowBuilderExtAndroid, WindowExtAndroid};
40#[cfg(target_os = "macos")]
41use tao::platform::macos::{EventLoopWindowTargetExtMacOS, WindowBuilderExtMacOS};
42#[cfg(any(
43 target_os = "linux",
44 target_os = "dragonfly",
45 target_os = "freebsd",
46 target_os = "netbsd",
47 target_os = "openbsd"
48))]
49use tao::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
50#[cfg(windows)]
51use tao::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
52#[cfg(windows)]
53use webview2_com::{
54 ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler,
55 Microsoft::Web::WebView2::Win32::ICoreWebView2Controller,
56};
57#[cfg(windows)]
58use windows::Win32::Foundation::HWND;
59#[cfg(target_os = "ios")]
60use wry::WebViewBuilderExtIos;
61#[cfg(target_os = "macos")]
62use wry::WebViewBuilderExtMacos;
63#[cfg(windows)]
64use wry::WebViewBuilderExtWindows;
65#[cfg(target_vendor = "apple")]
66use wry::{WebViewBuilderExtDarwin, WebViewExtDarwin};
67
68use tao::{
69 event::{Event, StartCause, WindowEvent as TaoWindowEvent},
70 event_loop::{
71 ControlFlow, DeviceEventFilter as TaoDeviceEventFilter, EventLoop, EventLoopBuilder,
72 EventLoopProxy as TaoEventLoopProxy, EventLoopWindowTarget,
73 },
74 monitor::MonitorHandle,
75 window::{
76 CursorIcon as TaoCursorIcon, Fullscreen, Icon as TaoWindowIcon,
77 ProgressBarState as TaoProgressBarState, ProgressState as TaoProgressState, Theme as TaoTheme,
78 UserAttentionType as TaoUserAttentionType,
79 },
80};
81#[cfg(target_os = "macos")]
82use tauri_utils::TitleBarStyle;
83use tauri_utils::config::PreventOverflowConfig;
84use tauri_utils::{
85 Theme,
86 config::{Color, WindowConfig},
87};
88use url::Url;
89#[cfg(windows)]
90use wry::ScrollBarStyle as WryScrollBarStyle;
91use wry::{
92 DragDropEvent as WryDragDropEvent, ProxyConfig, ProxyEndpoint, WebContext as WryWebContext,
93 WebView, WebViewBuilder,
94};
95
96pub use tao;
97pub use tao::window::{Window, WindowBuilder as TaoWindowBuilder, WindowId as TaoWindowId};
98pub use wry;
99pub use wry::webview_version;
100
101#[cfg(windows)]
102use wry::WebViewExtWindows;
103#[cfg(target_os = "android")]
104use wry::{
105 WebViewBuilderExtAndroid, WebViewExtAndroid,
106 prelude::{dispatch, find_class},
107};
108#[cfg(not(any(
109 target_os = "windows",
110 target_os = "macos",
111 target_os = "ios",
112 target_os = "android"
113)))]
114use wry::{WebViewBuilderExtUnix, WebViewExtUnix};
115
116#[cfg(target_os = "ios")]
117pub use tao::platform::ios::{WindowBuilderExtIOS, WindowExtIOS};
118#[cfg(target_os = "macos")]
119pub use tao::platform::macos::{
120 ActivationPolicy as TaoActivationPolicy, EventLoopExtMacOS, WindowExtMacOS,
121};
122#[cfg(target_os = "macos")]
123use tauri_runtime::ActivationPolicy;
124
125use std::{
126 cell::RefCell,
127 collections::{
128 BTreeMap, HashMap, HashSet,
129 hash_map::Entry::{Occupied, Vacant},
130 },
131 fmt,
132 ops::Deref,
133 path::PathBuf,
134 rc::Rc,
135 sync::{
136 Arc, Mutex, Weak,
137 atomic::{AtomicBool, AtomicU32, Ordering},
138 mpsc::{Sender, channel},
139 },
140 thread::{ThreadId, current as current_thread},
141};
142
143pub type WebviewId = u32;
144type IpcHandler = dyn Fn(Request<String>) + 'static;
145
146#[cfg(not(debug_assertions))]
147mod dialog;
148mod monitor;
149#[cfg(any(
150 windows,
151 target_os = "linux",
152 target_os = "dragonfly",
153 target_os = "freebsd",
154 target_os = "netbsd",
155 target_os = "openbsd"
156))]
157mod undecorated_resizing;
158mod util;
159mod webview;
160mod webview_permissions;
161mod window;
162
163pub use webview::Webview;
164use window::WindowExt as _;
165
166#[derive(Debug)]
167pub struct WebContext {
168 pub inner: WryWebContext,
169 pub referenced_by_webviews: HashSet<String>,
170 pub registered_custom_protocols: HashSet<String>,
173}
174
175pub type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
176pub type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
178pub type WindowEventListeners = Arc<Mutex<HashMap<WindowEventId, WindowEventHandler>>>;
179pub type WebviewEventHandler = Box<dyn Fn(&WebviewEvent) + Send>;
180pub type WebviewEventListeners = Arc<Mutex<HashMap<WebviewEventId, WebviewEventHandler>>>;
181
182#[derive(Debug, Clone, Default)]
183pub struct WindowIdStore(Arc<Mutex<HashMap<TaoWindowId, WindowId>>>);
184
185impl WindowIdStore {
186 pub fn insert(&self, w: TaoWindowId, id: WindowId) {
187 self.0.lock().unwrap().insert(w, id);
188 }
189
190 pub fn get(&self, w: &TaoWindowId) -> Option<WindowId> {
191 self.0.lock().unwrap().get(w).copied()
192 }
193}
194
195#[macro_export]
196macro_rules! getter {
197 ($self: ident, $rx: expr, $message: expr) => {{
198 $self.context.send_user_message($message)?;
199 $rx
200 .recv()
201 .map_err(|_| $crate::Error::FailedToReceiveMessage)
202 }};
203}
204
205macro_rules! window_getter {
206 ($self: ident, $message: expr) => {{
207 let (tx, rx) = channel();
208 getter!($self, rx, Message::Window($self.window_id, $message(tx)))
209 }};
210}
211
212macro_rules! event_loop_window_getter {
213 ($self: ident, $message: expr) => {{
214 let (tx, rx) = channel();
215 getter!($self, rx, Message::EventLoopWindowTarget($message(tx)))
216 }};
217}
218
219macro_rules! webview_getter {
220 ($self: ident, $message: expr) => {{
221 let (tx, rx) = channel();
222 getter!(
223 $self,
224 rx,
225 Message::Webview(
226 *$self.window_id.lock().unwrap(),
227 $self.webview_id,
228 $message(tx)
229 )
230 )
231 }};
232}
233
234#[derive(Clone)]
235pub struct Context<T: UserEvent> {
236 pub window_id_map: WindowIdStore,
237 main_thread_id: ThreadId,
238 pub proxy: TaoEventLoopProxy<Message<T>>,
239 main_thread: DispatcherMainThreadContext<T>,
240 plugins: Arc<Mutex<Vec<Box<dyn Plugin<T> + Send>>>>,
241 next_window_id: Arc<AtomicU32>,
242 next_webview_id: Arc<AtomicU32>,
243 next_window_event_id: Arc<AtomicU32>,
244 next_webview_event_id: Arc<AtomicU32>,
245 webview_runtime_installed: bool,
246}
247
248unsafe impl<T: UserEvent> Send for Context<T> {}
249unsafe impl<T: UserEvent> Sync for Context<T> {}
250
251impl<T: UserEvent> Context<T> {
252 pub fn run_threaded<R, F>(&self, f: F) -> R
253 where
254 F: FnOnce(Option<&DispatcherMainThreadContext<T>>) -> R,
255 {
256 f(if current_thread().id() == self.main_thread_id {
257 Some(&self.main_thread)
258 } else {
259 None
260 })
261 }
262
263 fn send_user_message(&self, message: Message<T>) -> Result<()> {
264 if current_thread().id() == self.main_thread_id {
265 handle_user_message(
266 &self.main_thread.window_target,
267 message,
268 UserMessageContext {
269 window_id_map: &self.window_id_map,
270 windows: &self.main_thread.windows,
271 },
272 );
273 Ok(())
274 } else {
275 self
276 .proxy
277 .send_event(message)
278 .map_err(|_| Error::FailedToSendMessage)
279 }
280 }
281
282 fn next_window_id(&self) -> WindowId {
283 self.next_window_id.fetch_add(1, Ordering::Relaxed).into()
284 }
285
286 fn next_webview_id(&self) -> WebviewId {
287 self.next_webview_id.fetch_add(1, Ordering::Relaxed)
288 }
289
290 fn next_window_event_id(&self) -> u32 {
291 self.next_window_event_id.fetch_add(1, Ordering::Relaxed)
292 }
293
294 fn next_webview_event_id(&self) -> u32 {
295 self.next_webview_event_id.fetch_add(1, Ordering::Relaxed)
296 }
297}
298
299impl<T: UserEvent> Context<T> {
300 fn create_window<F: Fn(RawWindow) + Send + 'static>(
301 &self,
302 pending: PendingWindow<T, Wry<T>>,
303 after_window_creation: Option<F>,
304 ) -> Result<DetachedWindow<T, Wry<T>>> {
305 let label = pending.label.clone();
306 let context = self.clone();
307 let window_id = self.next_window_id();
308 let (webview_id, use_https_scheme) = pending
309 .webview
310 .as_ref()
311 .map(|w| {
312 (
313 Some(context.next_webview_id()),
314 w.webview_attributes.use_https_scheme,
315 )
316 })
317 .unwrap_or((None, false));
318
319 let (tx, rx) = channel();
320 self.send_user_message(Message::CreateWindow(
321 window_id,
322 Box::new(move |event_loop| {
323 create_window(
324 window_id,
325 webview_id.unwrap_or_default(),
326 event_loop,
327 &context,
328 pending,
329 after_window_creation,
330 )
331 }),
332 tx,
333 ))?;
334 rx.recv()
335 .map_err(|_| crate::Error::FailedToReceiveMessage)??;
336
337 let dispatcher = WryWindowDispatcher {
338 window_id,
339 context: self.clone(),
340 };
341
342 let detached_webview = webview_id.map(|id| {
343 let webview = DetachedWebview {
344 label: label.clone(),
345 dispatcher: WryWebviewDispatcher {
346 window_id: Arc::new(Mutex::new(window_id)),
347 webview_id: id,
348 context: self.clone(),
349 },
350 };
351 DetachedWindowWebview {
352 webview,
353 use_https_scheme,
354 }
355 });
356
357 Ok(DetachedWindow {
358 id: window_id,
359 label,
360 dispatcher,
361 webview: detached_webview,
362 })
363 }
364
365 fn create_webview(
366 &self,
367 window_id: WindowId,
368 pending: PendingWebview<T, Wry<T>>,
369 ) -> Result<DetachedWebview<T, Wry<T>>> {
370 let label = pending.label.clone();
371 let context = self.clone();
372
373 let webview_id = self.next_webview_id();
374
375 let window_id_wrapper = Arc::new(Mutex::new(window_id));
376 let window_id_wrapper_ = window_id_wrapper.clone();
377
378 let (tx, rx) = channel();
379 self.send_user_message(Message::CreateWebview(
380 window_id,
381 Box::new(move |window, _options| {
382 create_webview(
383 WebviewKind::WindowChild,
384 window,
385 window_id_wrapper_,
386 webview_id,
387 &context,
388 pending,
389 #[cfg(windows)]
390 _options.focused_webview,
391 )
392 }),
393 tx,
394 ))?;
395 rx.recv()
396 .map_err(|_| crate::Error::FailedToReceiveMessage)??;
397
398 let dispatcher = WryWebviewDispatcher {
399 window_id: window_id_wrapper,
400 webview_id,
401 context: self.clone(),
402 };
403
404 Ok(DetachedWebview { label, dispatcher })
405 }
406}
407
408#[cfg(feature = "tracing")]
409#[derive(Clone, Default)]
410pub struct ActiveTraceSpanStore(Rc<RefCell<Vec<ActiveTracingSpan>>>);
411
412#[cfg(feature = "tracing")]
416impl fmt::Debug for ActiveTraceSpanStore {
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 f.debug_struct("ActiveTraceSpanStore")
419 .finish_non_exhaustive()
420 }
421}
422
423#[cfg(feature = "tracing")]
424impl ActiveTraceSpanStore {
425 pub fn remove_window_draw(&self) {
426 self
427 .0
428 .borrow_mut()
429 .retain(|t| !matches!(t, ActiveTracingSpan::WindowDraw { id: _, span: _ }));
430 }
431}
432
433#[cfg(feature = "tracing")]
434#[derive(Debug)]
435pub enum ActiveTracingSpan {
436 WindowDraw {
437 id: TaoWindowId,
438 span: tracing::span::EnteredSpan,
439 },
440}
441
442pub struct WindowsStore(pub RefCell<BTreeMap<WindowId, WindowWrapper>>);
443
444impl fmt::Debug for WindowsStore {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 f.debug_struct("WindowsStore").finish_non_exhaustive()
450 }
451}
452
453#[derive(Debug, Clone)]
454pub struct DispatcherMainThreadContext<T: UserEvent> {
455 pub window_target: EventLoopWindowTarget<Message<T>>,
456 pub web_context: WebContextStore,
457 pub windows: Arc<WindowsStore>,
459 #[cfg(feature = "tracing")]
460 pub active_tracing_spans: ActiveTraceSpanStore,
461}
462
463impl<T: UserEvent> fmt::Debug for Context<T> {
464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465 f.debug_struct("Context")
466 .field("main_thread_id", &self.main_thread_id)
467 .field("proxy", &self.proxy)
468 .field("main_thread", &self.main_thread)
469 .finish()
470 }
471}
472
473pub struct DeviceEventFilterWrapper(pub TaoDeviceEventFilter);
474
475impl From<DeviceEventFilter> for DeviceEventFilterWrapper {
476 fn from(item: DeviceEventFilter) -> Self {
477 match item {
478 DeviceEventFilter::Always => Self(TaoDeviceEventFilter::Always),
479 DeviceEventFilter::Never => Self(TaoDeviceEventFilter::Never),
480 DeviceEventFilter::Unfocused => Self(TaoDeviceEventFilter::Unfocused),
481 }
482 }
483}
484
485pub struct RectWrapper(pub wry::Rect);
486impl From<tauri_runtime::dpi::Rect> for RectWrapper {
487 fn from(value: tauri_runtime::dpi::Rect) -> Self {
488 RectWrapper(wry::Rect {
489 position: value.position,
490 size: value.size,
491 })
492 }
493}
494
495pub struct TaoIcon(pub TaoWindowIcon);
497
498impl TryFrom<Icon<'_>> for TaoIcon {
499 type Error = Error;
500 fn try_from(icon: Icon<'_>) -> std::result::Result<Self, Self::Error> {
501 TaoWindowIcon::from_rgba(icon.rgba.to_vec(), icon.width, icon.height)
502 .map(Self)
503 .map_err(|e| Error::InvalidIcon(Box::new(e)))
504 }
505}
506
507pub struct WindowEventWrapper(pub Option<WindowEvent>);
508
509impl WindowEventWrapper {
510 fn map_from_tao(event: &TaoWindowEvent<'_>, #[cfg(windows)] window: &WindowWrapper) -> Self {
511 let event = match event {
512 TaoWindowEvent::Resized(size) => WindowEvent::Resized(*size),
513 TaoWindowEvent::Moved(position) => WindowEvent::Moved(*position),
514 TaoWindowEvent::Destroyed => WindowEvent::Destroyed,
515 TaoWindowEvent::ScaleFactorChanged {
516 scale_factor,
517 new_inner_size,
518 } => WindowEvent::ScaleFactorChanged {
519 scale_factor: *scale_factor,
520 new_inner_size: **new_inner_size,
521 },
522 TaoWindowEvent::Focused(focused) => {
523 #[cfg(not(windows))]
524 return Self(Some(WindowEvent::Focused(*focused)));
525 #[cfg(windows)]
529 if window.has_children.load(Ordering::Relaxed) {
530 if !*focused {
531 return Self(None);
533 }
534
535 let mut focused_webview = window.focused_webview.lock().unwrap();
536 if let FocusState::Blured {
537 last_focused_webview_label,
538 } = &*focused_webview
539 {
540 let should_focus_webview =
541 last_focused_webview_label
542 .as_deref()
543 .and_then(|last_focused_webview_label| {
544 window
545 .webviews
546 .iter()
547 .find(|w| w.label == last_focused_webview_label)
548 });
549 *focused_webview = FocusState::WindowFocused;
550 if let Some(should_focus_webview) = should_focus_webview {
551 drop(focused_webview);
552 let _ = should_focus_webview.focus();
553 }
554 WindowEvent::Focused(true)
555 } else {
556 return Self(None);
558 }
559 } else if window.webviews.is_empty() {
560 WindowEvent::Focused(*focused)
562 } else {
563 return Self(None);
566 }
567 }
568 TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
569 #[cfg(mobile)]
570 TaoWindowEvent::Suspended => WindowEvent::Suspended,
571 #[cfg(mobile)]
572 TaoWindowEvent::Resumed => WindowEvent::Resumed,
573 _ => return Self(None),
574 };
575 Self(Some(event))
576 }
577
578 fn parse(window: &WindowWrapper, event: &TaoWindowEvent<'_>) -> Self {
579 match event {
580 TaoWindowEvent::Resized(_) => {
583 if let Some(w) = &window.inner {
584 let size = inner_size(
585 w,
586 &window.webviews,
587 window.has_children.load(Ordering::Relaxed),
588 );
589 Self(Some(WindowEvent::Resized(size)))
590 } else {
591 Self(None)
592 }
593 }
594 e => Self::map_from_tao(
595 e,
596 #[cfg(windows)]
597 window,
598 ),
599 }
600 }
601}
602
603pub fn map_theme(theme: &TaoTheme) -> Theme {
604 match theme {
605 TaoTheme::Light => Theme::Light,
606 TaoTheme::Dark => Theme::Dark,
607 _ => Theme::Light,
608 }
609}
610
611#[cfg(target_os = "macos")]
612fn tao_activation_policy(activation_policy: ActivationPolicy) -> TaoActivationPolicy {
613 match activation_policy {
614 ActivationPolicy::Regular => TaoActivationPolicy::Regular,
615 ActivationPolicy::Accessory => TaoActivationPolicy::Accessory,
616 ActivationPolicy::Prohibited => TaoActivationPolicy::Prohibited,
617 _ => unimplemented!(),
618 }
619}
620
621pub struct MonitorHandleWrapper(pub MonitorHandle);
622
623impl From<MonitorHandleWrapper> for Monitor {
624 fn from(monitor: MonitorHandleWrapper) -> Monitor {
625 Self {
626 name: monitor.0.name(),
627 position: monitor.0.position(),
628 size: monitor.0.size(),
629 work_area: monitor.0.work_area(),
630 scale_factor: monitor.0.scale_factor(),
631 }
632 }
633}
634
635fn find_monitor_for_position(
636 monitors: impl Iterator<Item = MonitorHandle>,
637 window_position: Position,
638) -> Option<MonitorHandle> {
639 monitors.into_iter().find(|m| {
640 let monitor_pos = m.position();
641 let monitor_size = m.size();
642
643 let window_position = window_position.to_physical::<i32>(m.scale_factor());
645
646 monitor_pos.x <= window_position.x
647 && window_position.x < monitor_pos.x + monitor_size.width as i32
648 && monitor_pos.y <= window_position.y
649 && window_position.y < monitor_pos.y + monitor_size.height as i32
650 })
651}
652
653#[derive(Debug, Clone)]
654pub struct UserAttentionTypeWrapper(pub TaoUserAttentionType);
655
656impl From<UserAttentionType> for UserAttentionTypeWrapper {
657 fn from(request_type: UserAttentionType) -> Self {
658 let o = match request_type {
659 UserAttentionType::Critical => TaoUserAttentionType::Critical,
660 UserAttentionType::Informational => TaoUserAttentionType::Informational,
661 };
662 Self(o)
663 }
664}
665
666#[derive(Debug)]
667pub struct CursorIconWrapper(pub TaoCursorIcon);
668
669impl From<CursorIcon> for CursorIconWrapper {
670 fn from(icon: CursorIcon) -> Self {
671 use CursorIcon::*;
672 let i = match icon {
673 Default => TaoCursorIcon::Default,
674 Crosshair => TaoCursorIcon::Crosshair,
675 Hand => TaoCursorIcon::Hand,
676 Arrow => TaoCursorIcon::Arrow,
677 Move => TaoCursorIcon::Move,
678 Text => TaoCursorIcon::Text,
679 Wait => TaoCursorIcon::Wait,
680 Help => TaoCursorIcon::Help,
681 Progress => TaoCursorIcon::Progress,
682 NotAllowed => TaoCursorIcon::NotAllowed,
683 ContextMenu => TaoCursorIcon::ContextMenu,
684 Cell => TaoCursorIcon::Cell,
685 VerticalText => TaoCursorIcon::VerticalText,
686 Alias => TaoCursorIcon::Alias,
687 Copy => TaoCursorIcon::Copy,
688 NoDrop => TaoCursorIcon::NoDrop,
689 Grab => TaoCursorIcon::Grab,
690 Grabbing => TaoCursorIcon::Grabbing,
691 AllScroll => TaoCursorIcon::AllScroll,
692 ZoomIn => TaoCursorIcon::ZoomIn,
693 ZoomOut => TaoCursorIcon::ZoomOut,
694 EResize => TaoCursorIcon::EResize,
695 NResize => TaoCursorIcon::NResize,
696 NeResize => TaoCursorIcon::NeResize,
697 NwResize => TaoCursorIcon::NwResize,
698 SResize => TaoCursorIcon::SResize,
699 SeResize => TaoCursorIcon::SeResize,
700 SwResize => TaoCursorIcon::SwResize,
701 WResize => TaoCursorIcon::WResize,
702 EwResize => TaoCursorIcon::EwResize,
703 NsResize => TaoCursorIcon::NsResize,
704 NeswResize => TaoCursorIcon::NeswResize,
705 NwseResize => TaoCursorIcon::NwseResize,
706 ColResize => TaoCursorIcon::ColResize,
707 RowResize => TaoCursorIcon::RowResize,
708 _ => TaoCursorIcon::Default,
709 };
710 Self(i)
711 }
712}
713
714pub struct ProgressStateWrapper(pub TaoProgressState);
715
716impl From<ProgressBarStatus> for ProgressStateWrapper {
717 fn from(status: ProgressBarStatus) -> Self {
718 let state = match status {
719 ProgressBarStatus::None => TaoProgressState::None,
720 ProgressBarStatus::Normal => TaoProgressState::Normal,
721 ProgressBarStatus::Indeterminate => TaoProgressState::Indeterminate,
722 ProgressBarStatus::Paused => TaoProgressState::Paused,
723 ProgressBarStatus::Error => TaoProgressState::Error,
724 };
725 Self(state)
726 }
727}
728
729pub struct ProgressBarStateWrapper(pub TaoProgressBarState);
730
731impl From<ProgressBarState> for ProgressBarStateWrapper {
732 fn from(progress_state: ProgressBarState) -> Self {
733 Self(TaoProgressBarState {
734 progress: progress_state.progress,
735 state: progress_state
736 .status
737 .map(|state| ProgressStateWrapper::from(state).0),
738 desktop_filename: progress_state.desktop_filename,
739 })
740 }
741}
742
743#[derive(Clone, Default)]
744pub struct WindowBuilderWrapper {
745 inner: TaoWindowBuilder,
746 center: bool,
747 prevent_overflow: Option<Size>,
748 #[cfg(target_os = "macos")]
749 tabbing_identifier: Option<String>,
750}
751
752impl std::fmt::Debug for WindowBuilderWrapper {
753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754 let mut s = f.debug_struct("WindowBuilderWrapper");
755 s.field("inner", &self.inner)
756 .field("center", &self.center)
757 .field("prevent_overflow", &self.prevent_overflow);
758 #[cfg(target_os = "macos")]
759 {
760 s.field("tabbing_identifier", &self.tabbing_identifier);
761 }
762 s.finish()
763 }
764}
765
766#[allow(clippy::non_send_fields_in_send_ty)]
768unsafe impl Send for WindowBuilderWrapper {}
769
770impl WindowBuilderBase for WindowBuilderWrapper {}
771impl WindowBuilder for WindowBuilderWrapper {
772 fn new() -> Self {
773 #[allow(unused_mut)]
774 let mut builder = Self::default().focused(true);
775
776 #[cfg(target_os = "macos")]
777 {
778 builder = builder.title_bar_style(TitleBarStyle::Visible);
784 }
785
786 builder = builder.title("Tauri App");
787
788 #[cfg(windows)]
789 {
790 builder = builder.window_classname("Tauri Window");
791 }
792
793 builder
794 }
795
796 fn with_config(config: &WindowConfig) -> Self {
797 let mut window = WindowBuilderWrapper::new();
798
799 #[cfg(target_os = "macos")]
800 {
801 window = window
802 .hidden_title(config.hidden_title)
803 .title_bar_style(config.title_bar_style);
804 if let Some(identifier) = &config.tabbing_identifier {
805 window = window.tabbing_identifier(identifier);
806 }
807 if let Some(position) = &config.traffic_light_position {
808 window = window.traffic_light_position(tauri_runtime::dpi::LogicalPosition::new(
809 position.x, position.y,
810 ));
811 }
812 }
813
814 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
815 {
816 window = window.transparent(config.transparent);
817 }
818 #[cfg(all(
819 target_os = "macos",
820 not(feature = "macos-private-api"),
821 debug_assertions
822 ))]
823 if config.transparent {
824 eprintln!(
825 "The window is set to be transparent but the `macos-private-api` is not enabled.
826 This can be enabled via the `tauri.macOSPrivateApi` configuration property <https://v2.tauri.app/reference/config/#macosprivateapi>
827 ");
828 }
829
830 #[cfg(any(
831 target_os = "linux",
832 target_os = "dragonfly",
833 target_os = "freebsd",
834 target_os = "netbsd",
835 target_os = "openbsd"
836 ))]
837 {
838 window.inner = window.inner.with_cursor_moved_event(false);
840 }
841
842 #[cfg(target_os = "android")]
843 {
844 if let Some(activity_name) = &config.activity_name {
845 window.inner = window.inner.with_activity_name(activity_name.clone());
846 }
847 if let Some(activity_name) = &config.created_by_activity_name {
848 window.inner = window
849 .inner
850 .with_created_by_activity_name(activity_name.clone());
851 }
852 }
853
854 #[cfg(target_os = "ios")]
855 {
856 if let Some(scene_identifier) = &config.requested_by_scene_identifier {
857 window.inner = window
858 .inner
859 .with_requesting_scene_identifier(scene_identifier.clone());
860 }
861 }
862
863 #[cfg(not(any(target_os = "ios", target_os = "android")))]
865 {
866 window = window.inner_size(config.width, config.height);
867 }
868
869 window = window
870 .title(config.title.to_string())
871 .focused(config.focus)
872 .focusable(config.focusable)
873 .visible(config.visible)
874 .resizable(config.resizable)
875 .fullscreen(config.fullscreen)
876 .decorations(config.decorations)
877 .maximized(config.maximized)
878 .always_on_bottom(config.always_on_bottom)
879 .always_on_top(config.always_on_top)
880 .visible_on_all_workspaces(config.visible_on_all_workspaces)
881 .content_protected(config.content_protected)
882 .skip_taskbar(config.skip_taskbar)
883 .theme(config.theme)
884 .no_redirection_bitmap(config.no_redirection_bitmap)
885 .closable(config.closable)
886 .maximizable(config.maximizable)
887 .minimizable(config.minimizable)
888 .shadow(config.shadow);
889
890 let mut constraints = WindowSizeConstraints::default();
891
892 if let Some(min_width) = config.min_width {
893 constraints.min_width = Some(tao::dpi::LogicalUnit::new(min_width).into());
894 }
895 if let Some(min_height) = config.min_height {
896 constraints.min_height = Some(tao::dpi::LogicalUnit::new(min_height).into());
897 }
898 if let Some(max_width) = config.max_width {
899 constraints.max_width = Some(tao::dpi::LogicalUnit::new(max_width).into());
900 }
901 if let Some(max_height) = config.max_height {
902 constraints.max_height = Some(tao::dpi::LogicalUnit::new(max_height).into());
903 }
904 if let Some(color) = config.background_color {
905 window = window.background_color(color);
906 }
907 window = window.inner_size_constraints(constraints);
908
909 if let (Some(x), Some(y)) = (config.x, config.y) {
910 window = window.position(x, y);
911 }
912
913 if config.center {
914 window = window.center();
915 }
916
917 if let Some(window_classname) = &config.window_classname {
918 window = window.window_classname(window_classname);
919 }
920
921 if let Some(prevent_overflow) = &config.prevent_overflow {
922 window = match prevent_overflow {
923 PreventOverflowConfig::Enable(true) => window.prevent_overflow(),
924 PreventOverflowConfig::Margin(margin) => {
925 window.prevent_overflow_with_margin(PhysicalSize::new(margin.width, margin.height).into())
926 }
927 _ => window,
928 };
929 }
930
931 window
932 }
933
934 fn center(mut self) -> Self {
935 self.center = true;
936 self
937 }
938
939 fn position(mut self, x: f64, y: f64) -> Self {
940 self.inner = self.inner.with_position(LogicalPosition::new(x, y));
941 self
942 }
943
944 fn inner_size(mut self, width: f64, height: f64) -> Self {
945 self.inner = self.inner.with_inner_size(LogicalSize::new(width, height));
946 self
947 }
948
949 fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
950 self.inner = self
951 .inner
952 .with_min_inner_size(LogicalSize::new(min_width, min_height));
953 self
954 }
955
956 fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
957 self.inner = self
958 .inner
959 .with_max_inner_size(LogicalSize::new(max_width, max_height));
960 self
961 }
962
963 fn inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self {
964 self.inner.window.inner_size_constraints = tao::window::WindowSizeConstraints {
965 min_width: constraints.min_width,
966 min_height: constraints.min_height,
967 max_width: constraints.max_width,
968 max_height: constraints.max_height,
969 };
970 self
971 }
972
973 fn prevent_overflow(mut self) -> Self {
979 self
980 .prevent_overflow
981 .replace(PhysicalSize::new(0, 0).into());
982 self
983 }
984
985 fn prevent_overflow_with_margin(mut self, margin: Size) -> Self {
992 self.prevent_overflow.replace(margin);
993 self
994 }
995
996 fn resizable(mut self, resizable: bool) -> Self {
997 self.inner = self.inner.with_resizable(resizable);
998 self
999 }
1000
1001 fn maximizable(mut self, maximizable: bool) -> Self {
1002 self.inner = self.inner.with_maximizable(maximizable);
1003 self
1004 }
1005
1006 fn minimizable(mut self, minimizable: bool) -> Self {
1007 self.inner = self.inner.with_minimizable(minimizable);
1008 self
1009 }
1010
1011 fn closable(mut self, closable: bool) -> Self {
1012 self.inner = self.inner.with_closable(closable);
1013 self
1014 }
1015
1016 fn title<S: Into<String>>(mut self, title: S) -> Self {
1017 self.inner = self.inner.with_title(title.into());
1018 self
1019 }
1020
1021 fn fullscreen(mut self, fullscreen: bool) -> Self {
1022 self.inner = if fullscreen {
1023 self
1024 .inner
1025 .with_fullscreen(Some(Fullscreen::Borderless(None)))
1026 } else {
1027 self.inner.with_fullscreen(None)
1028 };
1029 self
1030 }
1031
1032 fn focused(mut self, focused: bool) -> Self {
1033 self.inner = self.inner.with_focused(focused);
1034 self
1035 }
1036
1037 fn focusable(mut self, focusable: bool) -> Self {
1038 self.inner = self.inner.with_focusable(focusable);
1039 self
1040 }
1041
1042 fn maximized(mut self, maximized: bool) -> Self {
1043 self.inner = self.inner.with_maximized(maximized);
1044 self
1045 }
1046
1047 fn visible(mut self, visible: bool) -> Self {
1048 self.inner = self.inner.with_visible(visible);
1049 self
1050 }
1051
1052 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
1053 fn transparent(mut self, transparent: bool) -> Self {
1054 self.inner = self.inner.with_transparent(transparent);
1055 self
1056 }
1057
1058 fn decorations(mut self, decorations: bool) -> Self {
1059 self.inner = self.inner.with_decorations(decorations);
1060 self
1061 }
1062
1063 fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
1064 self.inner = self.inner.with_always_on_bottom(always_on_bottom);
1065 self
1066 }
1067
1068 fn always_on_top(mut self, always_on_top: bool) -> Self {
1069 self.inner = self.inner.with_always_on_top(always_on_top);
1070 self
1071 }
1072
1073 fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self {
1074 self.inner = self
1075 .inner
1076 .with_visible_on_all_workspaces(visible_on_all_workspaces);
1077 self
1078 }
1079
1080 fn content_protected(mut self, protected: bool) -> Self {
1081 self.inner = self.inner.with_content_protection(protected);
1082 self
1083 }
1084
1085 fn shadow(#[allow(unused_mut)] mut self, _enable: bool) -> Self {
1086 #[cfg(windows)]
1087 {
1088 self.inner = self.inner.with_undecorated_shadow(_enable);
1089 }
1090 #[cfg(target_os = "macos")]
1091 {
1092 self.inner = self.inner.with_has_shadow(_enable);
1093 }
1094 self
1095 }
1096
1097 #[cfg(windows)]
1098 fn owner(mut self, owner: HWND) -> Self {
1099 self.inner = self.inner.with_owner_window(owner.0 as _);
1100 self
1101 }
1102
1103 #[cfg(windows)]
1104 fn parent(mut self, parent: HWND) -> Self {
1105 self.inner = self.inner.with_parent_window(parent.0 as _);
1106 self
1107 }
1108
1109 #[cfg(target_os = "macos")]
1110 fn parent(mut self, parent: *mut std::ffi::c_void) -> Self {
1111 self.inner = self.inner.with_parent_window(parent);
1112 self
1113 }
1114
1115 #[cfg(any(
1116 target_os = "linux",
1117 target_os = "dragonfly",
1118 target_os = "freebsd",
1119 target_os = "netbsd",
1120 target_os = "openbsd"
1121 ))]
1122 fn transient_for(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
1123 self.inner = self.inner.with_transient_for(parent);
1124 self
1125 }
1126
1127 #[cfg(windows)]
1128 fn drag_and_drop(mut self, enabled: bool) -> Self {
1129 self.inner = self.inner.with_drag_and_drop(enabled);
1130 self
1131 }
1132
1133 #[cfg(target_os = "macos")]
1134 fn title_bar_style(mut self, style: TitleBarStyle) -> Self {
1135 match style {
1136 TitleBarStyle::Visible => {
1137 self.inner = self.inner.with_titlebar_transparent(false);
1138 self.inner = self.inner.with_fullsize_content_view(true);
1140 }
1141 TitleBarStyle::Transparent => {
1142 self.inner = self.inner.with_titlebar_transparent(true);
1143 self.inner = self.inner.with_fullsize_content_view(false);
1144 }
1145 TitleBarStyle::Overlay => {
1146 self.inner = self.inner.with_titlebar_transparent(true);
1147 self.inner = self.inner.with_fullsize_content_view(true);
1148 }
1149 unknown => {
1150 #[cfg(feature = "tracing")]
1151 tracing::warn!("unknown title bar style applied: {unknown}");
1152
1153 #[cfg(not(feature = "tracing"))]
1154 eprintln!("unknown title bar style applied: {unknown}");
1155 }
1156 }
1157 self
1158 }
1159
1160 #[cfg(target_os = "macos")]
1161 fn traffic_light_position<P: Into<Position>>(mut self, position: P) -> Self {
1162 self.inner = self.inner.with_traffic_light_inset(position.into());
1163 self
1164 }
1165
1166 #[cfg(target_os = "macos")]
1167 fn hidden_title(mut self, hidden: bool) -> Self {
1168 self.inner = self.inner.with_title_hidden(hidden);
1169 self
1170 }
1171
1172 #[cfg(target_os = "macos")]
1173 fn tabbing_identifier(mut self, identifier: &str) -> Self {
1174 self.inner = self.inner.with_tabbing_identifier(identifier);
1175 self.tabbing_identifier.replace(identifier.into());
1176 self
1177 }
1178
1179 fn icon(mut self, icon: Icon) -> Result<Self> {
1180 self.inner = self
1181 .inner
1182 .with_window_icon(Some(TaoIcon::try_from(icon)?.0));
1183 Ok(self)
1184 }
1185
1186 fn background_color(mut self, color: Color) -> Self {
1187 self.inner = self.inner.with_background_color(color.into());
1188 self
1189 }
1190
1191 #[cfg(any(
1192 windows,
1193 target_os = "linux",
1194 target_os = "dragonfly",
1195 target_os = "freebsd",
1196 target_os = "netbsd",
1197 target_os = "openbsd"
1198 ))]
1199 fn skip_taskbar(mut self, skip: bool) -> Self {
1200 self.inner = self.inner.with_skip_taskbar(skip);
1201 self
1202 }
1203
1204 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
1205 fn skip_taskbar(self, _skip: bool) -> Self {
1206 self
1207 }
1208
1209 fn theme(mut self, theme: Option<Theme>) -> Self {
1210 self.inner = self.inner.with_theme(if let Some(t) = theme {
1211 match t {
1212 Theme::Dark => Some(TaoTheme::Dark),
1213 _ => Some(TaoTheme::Light),
1214 }
1215 } else {
1216 None
1217 });
1218
1219 self
1220 }
1221
1222 fn has_icon(&self) -> bool {
1223 self.inner.window.window_icon.is_some()
1224 }
1225
1226 fn get_theme(&self) -> Option<Theme> {
1227 self.inner.window.preferred_theme.map(|theme| match theme {
1228 TaoTheme::Dark => Theme::Dark,
1229 _ => Theme::Light,
1230 })
1231 }
1232
1233 #[cfg(windows)]
1234 fn window_classname<S: Into<String>>(mut self, window_classname: S) -> Self {
1235 self.inner = self.inner.with_window_classname(window_classname);
1236 self
1237 }
1238 #[cfg(not(windows))]
1239 fn window_classname<S: Into<String>>(self, _window_classname: S) -> Self {
1240 self
1241 }
1242
1243 fn no_redirection_bitmap(#[allow(unused_mut)] mut self, _enable: bool) -> Self {
1244 #[cfg(windows)]
1245 {
1246 self.inner = self.inner.with_no_redirection_bitmap(_enable);
1247 }
1248 self
1249 }
1250
1251 #[cfg(target_os = "android")]
1252 fn activity_name<S: Into<String>>(mut self, class_name: S) -> Self {
1253 self.inner = self.inner.with_activity_name(class_name.into());
1254 self
1255 }
1256
1257 #[cfg(target_os = "android")]
1258 fn created_by_activity_name<S: Into<String>>(mut self, class_name: S) -> Self {
1259 self.inner = self.inner.with_created_by_activity_name(class_name.into());
1260 self
1261 }
1262
1263 #[cfg(target_os = "ios")]
1264 fn requested_by_scene_identifier<S: Into<String>>(mut self, identifier: S) -> Self {
1265 self.inner = self
1266 .inner
1267 .with_requesting_scene_identifier(identifier.into());
1268 self
1269 }
1270}
1271
1272#[cfg(any(
1273 target_os = "linux",
1274 target_os = "dragonfly",
1275 target_os = "freebsd",
1276 target_os = "netbsd",
1277 target_os = "openbsd"
1278))]
1279pub struct GtkWindow(pub gtk::ApplicationWindow);
1280#[cfg(any(
1281 target_os = "linux",
1282 target_os = "dragonfly",
1283 target_os = "freebsd",
1284 target_os = "netbsd",
1285 target_os = "openbsd"
1286))]
1287#[allow(clippy::non_send_fields_in_send_ty)]
1288unsafe impl Send for GtkWindow {}
1289
1290#[cfg(any(
1291 target_os = "linux",
1292 target_os = "dragonfly",
1293 target_os = "freebsd",
1294 target_os = "netbsd",
1295 target_os = "openbsd"
1296))]
1297pub struct GtkBox(pub gtk::Box);
1298#[cfg(any(
1299 target_os = "linux",
1300 target_os = "dragonfly",
1301 target_os = "freebsd",
1302 target_os = "netbsd",
1303 target_os = "openbsd"
1304))]
1305#[allow(clippy::non_send_fields_in_send_ty)]
1306unsafe impl Send for GtkBox {}
1307
1308pub struct SendRawWindowHandle(pub raw_window_handle::RawWindowHandle);
1309unsafe impl Send for SendRawWindowHandle {}
1310
1311pub enum ApplicationMessage {
1312 #[cfg(target_os = "macos")]
1313 Show,
1314 #[cfg(target_os = "macos")]
1315 Hide,
1316 #[cfg(any(target_os = "macos", target_os = "ios"))]
1317 FetchDataStoreIdentifiers(Box<dyn FnOnce(Vec<[u8; 16]>) + Send + 'static>),
1318 #[cfg(any(target_os = "macos", target_os = "ios"))]
1319 RemoveDataStore([u8; 16], Box<dyn FnOnce(Result<()>) + Send + 'static>),
1320}
1321
1322pub enum WindowMessage {
1323 AddEventListener(WindowEventId, Box<dyn Fn(&WindowEvent) + Send>),
1324 ScaleFactor(Sender<f64>),
1326 InnerPosition(Sender<Result<PhysicalPosition<i32>>>),
1327 OuterPosition(Sender<Result<PhysicalPosition<i32>>>),
1328 InnerSize(Sender<PhysicalSize<u32>>),
1329 OuterSize(Sender<PhysicalSize<u32>>),
1330 IsFullscreen(Sender<bool>),
1331 IsMinimized(Sender<bool>),
1332 IsMaximized(Sender<bool>),
1333 IsFocused(Sender<bool>),
1334 IsDecorated(Sender<bool>),
1335 IsResizable(Sender<bool>),
1336 IsMaximizable(Sender<bool>),
1337 IsMinimizable(Sender<bool>),
1338 IsClosable(Sender<bool>),
1339 IsVisible(Sender<bool>),
1340 Title(Sender<String>),
1341 CurrentMonitor(Sender<Option<Monitor>>),
1344 PrimaryMonitor(Sender<Option<Monitor>>),
1345 MonitorFromPoint(Sender<Option<Monitor>>, (f64, f64)),
1346 AvailableMonitors(Sender<Vec<Monitor>>),
1347 #[cfg(any(
1348 target_os = "linux",
1349 target_os = "dragonfly",
1350 target_os = "freebsd",
1351 target_os = "netbsd",
1352 target_os = "openbsd"
1353 ))]
1354 GtkWindow(Sender<GtkWindow>),
1355 #[cfg(any(
1356 target_os = "linux",
1357 target_os = "dragonfly",
1358 target_os = "freebsd",
1359 target_os = "netbsd",
1360 target_os = "openbsd"
1361 ))]
1362 GtkBox(Sender<GtkBox>),
1363 #[cfg(target_os = "android")]
1364 ActivityName(Sender<String>),
1365 #[cfg(target_os = "ios")]
1366 SceneIdentifier(Sender<String>),
1367 RawWindowHandle(Sender<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>>),
1368 Theme(Sender<Theme>),
1369 IsEnabled(Sender<bool>),
1370 IsAlwaysOnTop(Sender<bool>),
1371 Center,
1373 RequestUserAttention(Option<UserAttentionTypeWrapper>),
1374 SetEnabled(bool),
1375 SetResizable(bool),
1376 SetMaximizable(bool),
1377 SetMinimizable(bool),
1378 SetClosable(bool),
1379 SetTitle(String),
1380 Maximize,
1381 Unmaximize,
1382 Minimize,
1383 Unminimize,
1384 Show,
1385 Hide,
1386 Close,
1387 Destroy,
1388 SetDecorations(bool),
1389 SetShadow(bool),
1390 SetAlwaysOnBottom(bool),
1391 SetAlwaysOnTop(bool),
1392 SetVisibleOnAllWorkspaces(bool),
1393 SetContentProtected(bool),
1394 SetSize(Size),
1395 SetMinSize(Option<Size>),
1396 SetMaxSize(Option<Size>),
1397 SetSizeConstraints(WindowSizeConstraints),
1398 SetPosition(Position),
1399 SetFullscreen(bool),
1400 SetFullscreenOnMonitor(PhysicalPosition<f64>),
1401 #[cfg(target_os = "macos")]
1402 SetSimpleFullscreen(bool),
1403 SetFocus,
1404 SetFocusable(bool),
1405 SetIcon(TaoWindowIcon),
1406 SetSkipTaskbar(bool),
1407 SetCursorGrab(bool),
1408 SetCursorVisible(bool),
1409 SetCursorIcon(CursorIcon),
1410 SetCursorPosition(Position),
1411 SetIgnoreCursorEvents(bool),
1412 SetBadgeCount(Option<i64>, Option<String>),
1413 SetBadgeLabel(Option<String>),
1414 SetOverlayIcon(Option<TaoIcon>),
1415 SetProgressBar(ProgressBarState),
1416 SetTitleBarStyle(tauri_utils::TitleBarStyle),
1417 SetTrafficLightPosition(Position),
1418 SetTheme(Option<Theme>),
1419 SetBackgroundColor(Option<Color>),
1420 DragWindow,
1421 ResizeDragWindow(tauri_runtime::ResizeDirection),
1422 RequestRedraw,
1423}
1424
1425#[derive(Debug, Clone)]
1426pub enum SynthesizedWindowEvent {
1427 Focused(bool),
1428 DragDrop(DragDropEvent),
1429}
1430
1431impl From<SynthesizedWindowEvent> for WindowEventWrapper {
1432 fn from(event: SynthesizedWindowEvent) -> Self {
1433 let event = match event {
1434 SynthesizedWindowEvent::Focused(focused) => WindowEvent::Focused(focused),
1435 SynthesizedWindowEvent::DragDrop(event) => WindowEvent::DragDrop(event),
1436 };
1437 Self(Some(event))
1438 }
1439}
1440
1441pub enum WebviewMessage {
1442 AddEventListener(WebviewEventId, Box<dyn Fn(&WebviewEvent) + Send>),
1443 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1444 EvaluateScript(String),
1445 #[cfg(all(feature = "tracing", not(target_os = "android")))]
1446 EvaluateScript(String, Sender<()>, tracing::Span),
1447 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1448 EvaluateScriptWithCallback(String, Box<dyn Fn(String) + Send + 'static>),
1449 #[cfg(all(feature = "tracing", not(target_os = "android")))]
1450 EvaluateScriptWithCallback(
1451 String,
1452 Box<dyn Fn(String) + Send + 'static>,
1453 Sender<()>,
1454 tracing::Span,
1455 ),
1456 CookiesForUrl(Url, Sender<Result<Vec<tauri_runtime::Cookie<'static>>>>),
1457 Cookies(Sender<Result<Vec<tauri_runtime::Cookie<'static>>>>),
1458 SetCookie(tauri_runtime::Cookie<'static>),
1459 DeleteCookie(tauri_runtime::Cookie<'static>),
1460 WebviewEvent(WebviewEvent),
1461 SynthesizedWindowEvent(SynthesizedWindowEvent),
1462 Navigate(Url),
1463 Reload,
1464 Print,
1465 Close,
1466 Show,
1467 Hide,
1468 SetPosition(Position),
1469 SetSize(Size),
1470 SetBounds(tauri_runtime::dpi::Rect),
1471 SetFocus,
1472 Reparent(WindowId, Sender<Result<()>>),
1473 SetAutoResize(bool),
1474 SetZoom(f64),
1475 SetBackgroundColor(Option<Color>),
1476 ClearAllBrowsingData,
1477 Url(Sender<Result<String>>),
1479 Bounds(Sender<Result<tauri_runtime::dpi::Rect>>),
1480 Position(Sender<Result<PhysicalPosition<i32>>>),
1481 Size(Sender<Result<PhysicalSize<u32>>>),
1482 WithWebview(Box<dyn FnOnce(Webview) + Send>),
1483 #[cfg(any(debug_assertions, feature = "devtools"))]
1485 OpenDevTools,
1486 #[cfg(any(debug_assertions, feature = "devtools"))]
1487 CloseDevTools,
1488 #[cfg(any(debug_assertions, feature = "devtools"))]
1489 IsDevToolsOpen(Sender<bool>),
1490}
1491
1492pub enum EventLoopWindowTargetMessage {
1493 CursorPosition(Sender<Result<PhysicalPosition<f64>>>),
1494 PrimaryMonitor(Sender<Option<Monitor>>),
1496 MonitorFromPoint(Sender<Option<Monitor>>, (f64, f64)),
1497 AvailableMonitors(Sender<Vec<Monitor>>),
1498 SetTheme(Option<Theme>),
1499 SetDeviceEventFilter(DeviceEventFilter),
1500}
1501
1502pub type CreateWindowClosure<T> =
1503 Box<dyn FnOnce(&EventLoopWindowTarget<Message<T>>) -> Result<WindowWrapper> + Send>;
1504
1505pub type CreateWebviewClosure =
1506 Box<dyn FnOnce(&Window, CreateWebviewOptions) -> Result<WebviewWrapper> + Send>;
1507
1508pub struct CreateWebviewOptions {
1509 #[cfg(windows)]
1510 pub focused_webview: Arc<Mutex<FocusState>>,
1511}
1512
1513pub enum Message<T: 'static> {
1514 Task(Box<dyn FnOnce() + Send>),
1515 #[cfg(target_os = "macos")]
1516 SetActivationPolicy(ActivationPolicy),
1517 #[cfg(target_os = "macos")]
1518 SetDockVisibility(bool),
1519 RequestExit(i32),
1520 Application(ApplicationMessage),
1521 Window(WindowId, WindowMessage),
1522 Webview(WindowId, WebviewId, WebviewMessage),
1523 EventLoopWindowTarget(EventLoopWindowTargetMessage),
1524 CreateWebview(WindowId, CreateWebviewClosure, Sender<Result<()>>),
1525 CreateWindow(WindowId, CreateWindowClosure<T>, Sender<Result<()>>),
1526 CreateRawWindow(
1527 WindowId,
1528 Box<dyn FnOnce() -> (String, TaoWindowBuilder) + Send>,
1529 Sender<Result<Weak<Window>>>,
1530 ),
1531 UserEvent(T),
1532}
1533
1534impl<T: UserEvent> Clone for Message<T> {
1535 fn clone(&self) -> Self {
1536 match self {
1537 Self::UserEvent(t) => Self::UserEvent(t.clone()),
1538 _ => unimplemented!(),
1539 }
1540 }
1541}
1542
1543#[derive(Debug, Clone)]
1545pub struct WryWebviewDispatcher<T: UserEvent> {
1546 window_id: Arc<Mutex<WindowId>>,
1547 webview_id: WebviewId,
1548 context: Context<T>,
1549}
1550
1551impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
1552 type Runtime = Wry<T>;
1553
1554 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
1555 self.context.send_user_message(Message::Task(Box::new(f)))
1556 }
1557
1558 fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
1559 let id = self.context.next_webview_event_id();
1560 let _ = self.context.proxy.send_event(Message::Webview(
1561 *self.window_id.lock().unwrap(),
1562 self.webview_id,
1563 WebviewMessage::AddEventListener(id, Box::new(f)),
1564 ));
1565 id
1566 }
1567
1568 fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> {
1569 self.context.send_user_message(Message::Webview(
1570 *self.window_id.lock().unwrap(),
1571 self.webview_id,
1572 WebviewMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))),
1573 ))
1574 }
1575
1576 #[cfg(any(debug_assertions, feature = "devtools"))]
1577 fn open_devtools(&self) {
1578 let _ = self.context.send_user_message(Message::Webview(
1579 *self.window_id.lock().unwrap(),
1580 self.webview_id,
1581 WebviewMessage::OpenDevTools,
1582 ));
1583 }
1584
1585 #[cfg(any(debug_assertions, feature = "devtools"))]
1586 fn close_devtools(&self) {
1587 let _ = self.context.send_user_message(Message::Webview(
1588 *self.window_id.lock().unwrap(),
1589 self.webview_id,
1590 WebviewMessage::CloseDevTools,
1591 ));
1592 }
1593
1594 #[cfg(any(debug_assertions, feature = "devtools"))]
1596 fn is_devtools_open(&self) -> Result<bool> {
1597 webview_getter!(self, WebviewMessage::IsDevToolsOpen)
1598 }
1599
1600 fn url(&self) -> Result<String> {
1603 webview_getter!(self, WebviewMessage::Url)?
1604 }
1605
1606 fn bounds(&self) -> Result<tauri_runtime::dpi::Rect> {
1607 webview_getter!(self, WebviewMessage::Bounds)?
1608 }
1609
1610 fn position(&self) -> Result<PhysicalPosition<i32>> {
1611 webview_getter!(self, WebviewMessage::Position)?
1612 }
1613
1614 fn size(&self) -> Result<PhysicalSize<u32>> {
1615 webview_getter!(self, WebviewMessage::Size)?
1616 }
1617
1618 fn navigate(&self, url: Url) -> Result<()> {
1621 self.context.send_user_message(Message::Webview(
1622 *self.window_id.lock().unwrap(),
1623 self.webview_id,
1624 WebviewMessage::Navigate(url),
1625 ))
1626 }
1627
1628 fn reload(&self) -> Result<()> {
1629 self.context.send_user_message(Message::Webview(
1630 *self.window_id.lock().unwrap(),
1631 self.webview_id,
1632 WebviewMessage::Reload,
1633 ))
1634 }
1635
1636 fn print(&self) -> Result<()> {
1637 self.context.send_user_message(Message::Webview(
1638 *self.window_id.lock().unwrap(),
1639 self.webview_id,
1640 WebviewMessage::Print,
1641 ))
1642 }
1643
1644 fn close(&self) -> Result<()> {
1645 self.context.send_user_message(Message::Webview(
1646 *self.window_id.lock().unwrap(),
1647 self.webview_id,
1648 WebviewMessage::Close,
1649 ))
1650 }
1651
1652 fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> {
1653 self.context.send_user_message(Message::Webview(
1654 *self.window_id.lock().unwrap(),
1655 self.webview_id,
1656 WebviewMessage::SetBounds(bounds),
1657 ))
1658 }
1659
1660 fn set_size(&self, size: Size) -> Result<()> {
1661 self.context.send_user_message(Message::Webview(
1662 *self.window_id.lock().unwrap(),
1663 self.webview_id,
1664 WebviewMessage::SetSize(size),
1665 ))
1666 }
1667
1668 fn set_position(&self, position: Position) -> Result<()> {
1669 self.context.send_user_message(Message::Webview(
1670 *self.window_id.lock().unwrap(),
1671 self.webview_id,
1672 WebviewMessage::SetPosition(position),
1673 ))
1674 }
1675
1676 fn set_focus(&self) -> Result<()> {
1677 self.context.send_user_message(Message::Webview(
1678 *self.window_id.lock().unwrap(),
1679 self.webview_id,
1680 WebviewMessage::SetFocus,
1681 ))
1682 }
1683
1684 fn reparent(&self, window_id: WindowId) -> Result<()> {
1685 let mut current_window_id = self.window_id.lock().unwrap();
1686 let (tx, rx) = channel();
1687 self.context.send_user_message(Message::Webview(
1688 *current_window_id,
1689 self.webview_id,
1690 WebviewMessage::Reparent(window_id, tx),
1691 ))?;
1692
1693 rx.recv().unwrap()?;
1694
1695 *current_window_id = window_id;
1696 Ok(())
1697 }
1698
1699 fn cookies_for_url(&self, url: Url) -> Result<Vec<Cookie<'static>>> {
1700 let (tx, rx) = channel();
1701 self.context.send_user_message(Message::Webview(
1702 *self.window_id.lock().unwrap(),
1703 self.webview_id,
1704 WebviewMessage::CookiesForUrl(url, tx),
1705 ))?;
1706
1707 rx.recv().unwrap()
1708 }
1709
1710 fn cookies(&self) -> Result<Vec<Cookie<'static>>> {
1711 webview_getter!(self, WebviewMessage::Cookies)?
1712 }
1713
1714 fn set_cookie(&self, cookie: Cookie<'_>) -> Result<()> {
1715 self.context.send_user_message(Message::Webview(
1716 *self.window_id.lock().unwrap(),
1717 self.webview_id,
1718 WebviewMessage::SetCookie(cookie.into_owned()),
1719 ))?;
1720 Ok(())
1721 }
1722
1723 fn delete_cookie(&self, cookie: Cookie<'_>) -> Result<()> {
1724 self.context.send_user_message(Message::Webview(
1725 *self.window_id.lock().unwrap(),
1726 self.webview_id,
1727 WebviewMessage::DeleteCookie(cookie.into_owned()),
1728 ))?;
1729 Ok(())
1730 }
1731
1732 fn set_auto_resize(&self, auto_resize: bool) -> Result<()> {
1733 self.context.send_user_message(Message::Webview(
1734 *self.window_id.lock().unwrap(),
1735 self.webview_id,
1736 WebviewMessage::SetAutoResize(auto_resize),
1737 ))
1738 }
1739
1740 #[cfg(all(feature = "tracing", not(target_os = "android")))]
1741 fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
1742 let (tx, rx) = channel();
1744 getter!(
1745 self,
1746 rx,
1747 Message::Webview(
1748 *self.window_id.lock().unwrap(),
1749 self.webview_id,
1750 WebviewMessage::EvaluateScript(script.into(), tx, tracing::Span::current()),
1751 )
1752 )
1753 }
1754
1755 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1756 fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
1757 self.context.send_user_message(Message::Webview(
1758 *self.window_id.lock().unwrap(),
1759 self.webview_id,
1760 WebviewMessage::EvaluateScript(script.into()),
1761 ))
1762 }
1763
1764 #[cfg(all(feature = "tracing", not(target_os = "android")))]
1765 fn eval_script_with_callback<S: Into<String>>(
1766 &self,
1767 script: S,
1768 callback: impl Fn(String) + Send + 'static,
1769 ) -> Result<()> {
1770 let (tx, rx) = channel();
1772 getter!(
1773 self,
1774 rx,
1775 Message::Webview(
1776 *self.window_id.lock().unwrap(),
1777 self.webview_id,
1778 WebviewMessage::EvaluateScriptWithCallback(
1779 script.into(),
1780 Box::new(callback),
1781 tx,
1782 tracing::Span::current(),
1783 ),
1784 )
1785 )
1786 }
1787
1788 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1789 fn eval_script_with_callback<S: Into<String>>(
1790 &self,
1791 script: S,
1792 callback: impl Fn(String) + Send + 'static,
1793 ) -> Result<()> {
1794 self.context.send_user_message(Message::Webview(
1795 *self.window_id.lock().unwrap(),
1796 self.webview_id,
1797 WebviewMessage::EvaluateScriptWithCallback(script.into(), Box::new(callback)),
1798 ))
1799 }
1800
1801 fn set_zoom(&self, scale_factor: f64) -> Result<()> {
1802 self.context.send_user_message(Message::Webview(
1803 *self.window_id.lock().unwrap(),
1804 self.webview_id,
1805 WebviewMessage::SetZoom(scale_factor),
1806 ))
1807 }
1808
1809 fn clear_all_browsing_data(&self) -> Result<()> {
1810 self.context.send_user_message(Message::Webview(
1811 *self.window_id.lock().unwrap(),
1812 self.webview_id,
1813 WebviewMessage::ClearAllBrowsingData,
1814 ))
1815 }
1816
1817 fn hide(&self) -> Result<()> {
1818 self.context.send_user_message(Message::Webview(
1819 *self.window_id.lock().unwrap(),
1820 self.webview_id,
1821 WebviewMessage::Hide,
1822 ))
1823 }
1824
1825 fn show(&self) -> Result<()> {
1826 self.context.send_user_message(Message::Webview(
1827 *self.window_id.lock().unwrap(),
1828 self.webview_id,
1829 WebviewMessage::Show,
1830 ))
1831 }
1832
1833 fn set_background_color(&self, color: Option<Color>) -> Result<()> {
1834 self.context.send_user_message(Message::Webview(
1835 *self.window_id.lock().unwrap(),
1836 self.webview_id,
1837 WebviewMessage::SetBackgroundColor(color),
1838 ))
1839 }
1840}
1841
1842#[derive(Debug, Clone)]
1844pub struct WryWindowDispatcher<T: UserEvent> {
1845 window_id: WindowId,
1846 context: Context<T>,
1847}
1848
1849#[allow(clippy::non_send_fields_in_send_ty)]
1851unsafe impl<T: UserEvent> Sync for WryWindowDispatcher<T> {}
1852
1853fn get_raw_window_handle<T: UserEvent>(
1854 dispatcher: &WryWindowDispatcher<T>,
1855) -> Result<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>> {
1856 window_getter!(dispatcher, WindowMessage::RawWindowHandle)
1857}
1858
1859impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
1860 type Runtime = Wry<T>;
1861 type WindowBuilder = WindowBuilderWrapper;
1862
1863 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
1864 self.context.send_user_message(Message::Task(Box::new(f)))
1865 }
1866
1867 fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
1868 let id = self.context.next_window_event_id();
1869 let _ = self.context.proxy.send_event(Message::Window(
1870 self.window_id,
1871 WindowMessage::AddEventListener(id, Box::new(f)),
1872 ));
1873 id
1874 }
1875
1876 fn scale_factor(&self) -> Result<f64> {
1879 window_getter!(self, WindowMessage::ScaleFactor)
1880 }
1881
1882 fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
1883 window_getter!(self, WindowMessage::InnerPosition)?
1884 }
1885
1886 fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
1887 window_getter!(self, WindowMessage::OuterPosition)?
1888 }
1889
1890 fn inner_size(&self) -> Result<PhysicalSize<u32>> {
1891 window_getter!(self, WindowMessage::InnerSize)
1892 }
1893
1894 fn outer_size(&self) -> Result<PhysicalSize<u32>> {
1895 window_getter!(self, WindowMessage::OuterSize)
1896 }
1897
1898 fn is_fullscreen(&self) -> Result<bool> {
1899 window_getter!(self, WindowMessage::IsFullscreen)
1900 }
1901
1902 fn is_minimized(&self) -> Result<bool> {
1903 window_getter!(self, WindowMessage::IsMinimized)
1904 }
1905
1906 fn is_maximized(&self) -> Result<bool> {
1907 window_getter!(self, WindowMessage::IsMaximized)
1908 }
1909
1910 fn is_focused(&self) -> Result<bool> {
1911 window_getter!(self, WindowMessage::IsFocused)
1912 }
1913
1914 fn is_decorated(&self) -> Result<bool> {
1916 window_getter!(self, WindowMessage::IsDecorated)
1917 }
1918
1919 fn is_resizable(&self) -> Result<bool> {
1921 window_getter!(self, WindowMessage::IsResizable)
1922 }
1923
1924 fn is_maximizable(&self) -> Result<bool> {
1926 window_getter!(self, WindowMessage::IsMaximizable)
1927 }
1928
1929 fn is_minimizable(&self) -> Result<bool> {
1931 window_getter!(self, WindowMessage::IsMinimizable)
1932 }
1933
1934 fn is_closable(&self) -> Result<bool> {
1936 window_getter!(self, WindowMessage::IsClosable)
1937 }
1938
1939 fn is_visible(&self) -> Result<bool> {
1940 window_getter!(self, WindowMessage::IsVisible)
1941 }
1942
1943 fn title(&self) -> Result<String> {
1944 window_getter!(self, WindowMessage::Title)
1945 }
1946
1947 fn current_monitor(&self) -> Result<Option<Monitor>> {
1948 window_getter!(self, WindowMessage::CurrentMonitor)
1949 }
1950
1951 fn primary_monitor(&self) -> Result<Option<Monitor>> {
1952 window_getter!(self, WindowMessage::PrimaryMonitor)
1953 }
1954
1955 fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
1956 let (tx, rx) = channel();
1957
1958 let _ = self.context.send_user_message(Message::Window(
1959 self.window_id,
1960 WindowMessage::MonitorFromPoint(tx, (x, y)),
1961 ));
1962
1963 rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage)
1964 }
1965
1966 fn available_monitors(&self) -> Result<Vec<Monitor>> {
1967 window_getter!(self, WindowMessage::AvailableMonitors)
1968 }
1969
1970 fn theme(&self) -> Result<Theme> {
1971 window_getter!(self, WindowMessage::Theme)
1972 }
1973
1974 fn is_enabled(&self) -> Result<bool> {
1975 window_getter!(self, WindowMessage::IsEnabled)
1976 }
1977
1978 fn is_always_on_top(&self) -> Result<bool> {
1979 window_getter!(self, WindowMessage::IsAlwaysOnTop)
1980 }
1981
1982 #[cfg(any(
1983 target_os = "linux",
1984 target_os = "dragonfly",
1985 target_os = "freebsd",
1986 target_os = "netbsd",
1987 target_os = "openbsd"
1988 ))]
1989 fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
1990 window_getter!(self, WindowMessage::GtkWindow).map(|w| w.0)
1991 }
1992
1993 #[cfg(any(
1994 target_os = "linux",
1995 target_os = "dragonfly",
1996 target_os = "freebsd",
1997 target_os = "netbsd",
1998 target_os = "openbsd"
1999 ))]
2000 fn default_vbox(&self) -> Result<gtk::Box> {
2001 window_getter!(self, WindowMessage::GtkBox).map(|w| w.0)
2002 }
2003
2004 #[cfg(target_os = "android")]
2006 fn activity_name(&self) -> Result<String> {
2007 window_getter!(self, WindowMessage::ActivityName)
2008 }
2009
2010 #[cfg(target_os = "ios")]
2012 fn scene_identifier(&self) -> Result<String> {
2013 window_getter!(self, WindowMessage::SceneIdentifier)
2014 }
2015
2016 fn window_handle(
2017 &self,
2018 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
2019 get_raw_window_handle(self)
2020 .map_err(|_| raw_window_handle::HandleError::Unavailable)
2021 .and_then(|r| r.map(|h| unsafe { raw_window_handle::WindowHandle::borrow_raw(h.0) }))
2022 }
2023
2024 fn center(&self) -> Result<()> {
2027 self
2028 .context
2029 .send_user_message(Message::Window(self.window_id, WindowMessage::Center))
2030 }
2031
2032 fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
2033 self.context.send_user_message(Message::Window(
2034 self.window_id,
2035 WindowMessage::RequestUserAttention(request_type.map(Into::into)),
2036 ))
2037 }
2038
2039 fn create_window<F: Fn(RawWindow) + Send + 'static>(
2042 &mut self,
2043 pending: PendingWindow<T, Self::Runtime>,
2044 after_window_creation: Option<F>,
2045 ) -> Result<DetachedWindow<T, Self::Runtime>> {
2046 self.context.create_window(pending, after_window_creation)
2047 }
2048
2049 fn create_webview(
2052 &mut self,
2053 pending: PendingWebview<T, Self::Runtime>,
2054 ) -> Result<DetachedWebview<T, Self::Runtime>> {
2055 self.context.create_webview(self.window_id, pending)
2056 }
2057
2058 fn set_resizable(&self, resizable: bool) -> Result<()> {
2059 self.context.send_user_message(Message::Window(
2060 self.window_id,
2061 WindowMessage::SetResizable(resizable),
2062 ))
2063 }
2064
2065 fn set_enabled(&self, enabled: bool) -> Result<()> {
2066 self.context.send_user_message(Message::Window(
2067 self.window_id,
2068 WindowMessage::SetEnabled(enabled),
2069 ))
2070 }
2071
2072 fn set_maximizable(&self, maximizable: bool) -> Result<()> {
2073 self.context.send_user_message(Message::Window(
2074 self.window_id,
2075 WindowMessage::SetMaximizable(maximizable),
2076 ))
2077 }
2078
2079 fn set_minimizable(&self, minimizable: bool) -> Result<()> {
2080 self.context.send_user_message(Message::Window(
2081 self.window_id,
2082 WindowMessage::SetMinimizable(minimizable),
2083 ))
2084 }
2085
2086 fn set_closable(&self, closable: bool) -> Result<()> {
2087 self.context.send_user_message(Message::Window(
2088 self.window_id,
2089 WindowMessage::SetClosable(closable),
2090 ))
2091 }
2092
2093 fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
2094 self.context.send_user_message(Message::Window(
2095 self.window_id,
2096 WindowMessage::SetTitle(title.into()),
2097 ))
2098 }
2099
2100 fn maximize(&self) -> Result<()> {
2101 self
2102 .context
2103 .send_user_message(Message::Window(self.window_id, WindowMessage::Maximize))
2104 }
2105
2106 fn unmaximize(&self) -> Result<()> {
2107 self
2108 .context
2109 .send_user_message(Message::Window(self.window_id, WindowMessage::Unmaximize))
2110 }
2111
2112 fn minimize(&self) -> Result<()> {
2113 self
2114 .context
2115 .send_user_message(Message::Window(self.window_id, WindowMessage::Minimize))
2116 }
2117
2118 fn unminimize(&self) -> Result<()> {
2119 self
2120 .context
2121 .send_user_message(Message::Window(self.window_id, WindowMessage::Unminimize))
2122 }
2123
2124 fn show(&self) -> Result<()> {
2125 self
2126 .context
2127 .send_user_message(Message::Window(self.window_id, WindowMessage::Show))
2128 }
2129
2130 fn hide(&self) -> Result<()> {
2131 self
2132 .context
2133 .send_user_message(Message::Window(self.window_id, WindowMessage::Hide))
2134 }
2135
2136 fn close(&self) -> Result<()> {
2137 self
2139 .context
2140 .proxy
2141 .send_event(Message::Window(self.window_id, WindowMessage::Close))
2142 .map_err(|_| Error::FailedToSendMessage)
2143 }
2144
2145 fn destroy(&self) -> Result<()> {
2146 self
2148 .context
2149 .proxy
2150 .send_event(Message::Window(self.window_id, WindowMessage::Destroy))
2151 .map_err(|_| Error::FailedToSendMessage)
2152 }
2153
2154 fn set_decorations(&self, decorations: bool) -> Result<()> {
2155 self.context.send_user_message(Message::Window(
2156 self.window_id,
2157 WindowMessage::SetDecorations(decorations),
2158 ))
2159 }
2160
2161 fn set_shadow(&self, enable: bool) -> Result<()> {
2162 self.context.send_user_message(Message::Window(
2163 self.window_id,
2164 WindowMessage::SetShadow(enable),
2165 ))
2166 }
2167
2168 fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> {
2169 self.context.send_user_message(Message::Window(
2170 self.window_id,
2171 WindowMessage::SetAlwaysOnBottom(always_on_bottom),
2172 ))
2173 }
2174
2175 fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
2176 self.context.send_user_message(Message::Window(
2177 self.window_id,
2178 WindowMessage::SetAlwaysOnTop(always_on_top),
2179 ))
2180 }
2181
2182 fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> {
2183 self.context.send_user_message(Message::Window(
2184 self.window_id,
2185 WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces),
2186 ))
2187 }
2188
2189 fn set_content_protected(&self, protected: bool) -> Result<()> {
2190 self.context.send_user_message(Message::Window(
2191 self.window_id,
2192 WindowMessage::SetContentProtected(protected),
2193 ))
2194 }
2195
2196 fn set_size(&self, size: Size) -> Result<()> {
2197 self.context.send_user_message(Message::Window(
2198 self.window_id,
2199 WindowMessage::SetSize(size),
2200 ))
2201 }
2202
2203 fn set_min_size(&self, size: Option<Size>) -> Result<()> {
2204 self.context.send_user_message(Message::Window(
2205 self.window_id,
2206 WindowMessage::SetMinSize(size),
2207 ))
2208 }
2209
2210 fn set_max_size(&self, size: Option<Size>) -> Result<()> {
2211 self.context.send_user_message(Message::Window(
2212 self.window_id,
2213 WindowMessage::SetMaxSize(size),
2214 ))
2215 }
2216
2217 fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()> {
2218 self.context.send_user_message(Message::Window(
2219 self.window_id,
2220 WindowMessage::SetSizeConstraints(constraints),
2221 ))
2222 }
2223
2224 fn set_position(&self, position: Position) -> Result<()> {
2225 self.context.send_user_message(Message::Window(
2226 self.window_id,
2227 WindowMessage::SetPosition(position),
2228 ))
2229 }
2230
2231 fn set_fullscreen_on_monitor(&self, position: PhysicalPosition<f64>) -> Result<()> {
2232 self.context.send_user_message(Message::Window(
2233 self.window_id,
2234 WindowMessage::SetFullscreenOnMonitor(position),
2235 ))
2236 }
2237
2238 fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
2239 self.context.send_user_message(Message::Window(
2240 self.window_id,
2241 WindowMessage::SetFullscreen(fullscreen),
2242 ))
2243 }
2244
2245 #[cfg(target_os = "macos")]
2246 fn set_simple_fullscreen(&self, enable: bool) -> Result<()> {
2247 self.context.send_user_message(Message::Window(
2248 self.window_id,
2249 WindowMessage::SetSimpleFullscreen(enable),
2250 ))
2251 }
2252
2253 fn set_focus(&self) -> Result<()> {
2254 self
2255 .context
2256 .send_user_message(Message::Window(self.window_id, WindowMessage::SetFocus))
2257 }
2258
2259 fn set_focusable(&self, focusable: bool) -> Result<()> {
2260 self.context.send_user_message(Message::Window(
2261 self.window_id,
2262 WindowMessage::SetFocusable(focusable),
2263 ))
2264 }
2265
2266 fn set_icon(&self, icon: Icon) -> Result<()> {
2267 self.context.send_user_message(Message::Window(
2268 self.window_id,
2269 WindowMessage::SetIcon(TaoIcon::try_from(icon)?.0),
2270 ))
2271 }
2272
2273 fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
2274 self.context.send_user_message(Message::Window(
2275 self.window_id,
2276 WindowMessage::SetSkipTaskbar(skip),
2277 ))
2278 }
2279
2280 fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
2281 self.context.send_user_message(Message::Window(
2282 self.window_id,
2283 WindowMessage::SetCursorGrab(grab),
2284 ))
2285 }
2286
2287 fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
2288 self.context.send_user_message(Message::Window(
2289 self.window_id,
2290 WindowMessage::SetCursorVisible(visible),
2291 ))
2292 }
2293
2294 fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
2295 self.context.send_user_message(Message::Window(
2296 self.window_id,
2297 WindowMessage::SetCursorIcon(icon),
2298 ))
2299 }
2300
2301 fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2302 self.context.send_user_message(Message::Window(
2303 self.window_id,
2304 WindowMessage::SetCursorPosition(position.into()),
2305 ))
2306 }
2307
2308 fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
2309 self.context.send_user_message(Message::Window(
2310 self.window_id,
2311 WindowMessage::SetIgnoreCursorEvents(ignore),
2312 ))
2313 }
2314
2315 fn start_dragging(&self) -> Result<()> {
2316 self
2317 .context
2318 .send_user_message(Message::Window(self.window_id, WindowMessage::DragWindow))
2319 }
2320
2321 fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> {
2322 self.context.send_user_message(Message::Window(
2323 self.window_id,
2324 WindowMessage::ResizeDragWindow(direction),
2325 ))
2326 }
2327
2328 fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
2329 self.context.send_user_message(Message::Window(
2330 self.window_id,
2331 WindowMessage::SetBadgeCount(count, desktop_filename),
2332 ))
2333 }
2334
2335 fn set_badge_label(&self, label: Option<String>) -> Result<()> {
2336 self.context.send_user_message(Message::Window(
2337 self.window_id,
2338 WindowMessage::SetBadgeLabel(label),
2339 ))
2340 }
2341
2342 fn set_overlay_icon(&self, icon: Option<Icon>) -> Result<()> {
2343 let icon: Result<Option<TaoIcon>> = icon.map_or(Ok(None), |x| Ok(Some(TaoIcon::try_from(x)?)));
2344
2345 self.context.send_user_message(Message::Window(
2346 self.window_id,
2347 WindowMessage::SetOverlayIcon(icon?),
2348 ))
2349 }
2350
2351 fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
2352 self.context.send_user_message(Message::Window(
2353 self.window_id,
2354 WindowMessage::SetProgressBar(progress_state),
2355 ))
2356 }
2357
2358 fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
2359 self.context.send_user_message(Message::Window(
2360 self.window_id,
2361 WindowMessage::SetTitleBarStyle(style),
2362 ))
2363 }
2364
2365 fn set_traffic_light_position(&self, position: Position) -> Result<()> {
2366 self.context.send_user_message(Message::Window(
2367 self.window_id,
2368 WindowMessage::SetTrafficLightPosition(position),
2369 ))
2370 }
2371
2372 fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
2373 self.context.send_user_message(Message::Window(
2374 self.window_id,
2375 WindowMessage::SetTheme(theme),
2376 ))
2377 }
2378
2379 fn set_background_color(&self, color: Option<Color>) -> Result<()> {
2380 self.context.send_user_message(Message::Window(
2381 self.window_id,
2382 WindowMessage::SetBackgroundColor(color),
2383 ))
2384 }
2385}
2386
2387#[derive(Clone)]
2388pub struct WebviewWrapper {
2389 label: String,
2390 id: WebviewId,
2391 inner: Rc<WebView>,
2392 context_store: WebContextStore,
2393 webview_event_listeners: WebviewEventListeners,
2394 context_key: Option<PathBuf>,
2396 bounds: Arc<Mutex<Option<WebviewBounds>>>,
2397}
2398
2399impl Deref for WebviewWrapper {
2400 type Target = WebView;
2401
2402 #[inline(always)]
2403 fn deref(&self) -> &Self::Target {
2404 &self.inner
2405 }
2406}
2407
2408impl Drop for WebviewWrapper {
2409 fn drop(&mut self) {
2410 if Rc::get_mut(&mut self.inner).is_some() {
2411 let mut context_store = self.context_store.lock().unwrap();
2412
2413 if let Some(web_context) = context_store.get_mut(&self.context_key) {
2414 web_context.referenced_by_webviews.remove(&self.label);
2415
2416 #[cfg(not(any(
2422 target_os = "linux",
2423 target_os = "dragonfly",
2424 target_os = "freebsd",
2425 target_os = "netbsd",
2426 target_os = "openbsd"
2427 )))]
2428 if web_context.referenced_by_webviews.is_empty() {
2429 context_store.remove(&self.context_key);
2430 }
2431 }
2432 }
2433 }
2434}
2435
2436#[cfg(windows)]
2437#[derive(Debug)]
2438pub enum FocusState {
2439 WindowFocused,
2440 WebviewFocused {
2441 webview_label: String,
2442 },
2443 Blured {
2444 last_focused_webview_label: Option<String>,
2445 },
2446}
2447
2448#[cfg(windows)]
2449impl Default for FocusState {
2450 fn default() -> Self {
2451 Self::Blured {
2452 last_focused_webview_label: None,
2453 }
2454 }
2455}
2456
2457pub struct WindowWrapper {
2458 label: String,
2459 inner: Option<Arc<Window>>,
2460 has_children: AtomicBool,
2463 webviews: Vec<WebviewWrapper>,
2464 window_event_listeners: WindowEventListeners,
2465 #[cfg(windows)]
2466 background_color: Option<tao::window::RGBA>,
2467 #[cfg(windows)]
2468 is_window_transparent: bool,
2469 #[cfg(windows)]
2470 surface: Option<softbuffer::Surface<Arc<Window>, Arc<Window>>>,
2471 #[cfg(windows)]
2472 focused_webview: Arc<Mutex<FocusState>>,
2473}
2474
2475impl WindowWrapper {
2476 pub fn label(&self) -> &str {
2477 &self.label
2478 }
2479}
2480
2481impl fmt::Debug for WindowWrapper {
2482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2483 f.debug_struct("WindowWrapper")
2484 .field("label", &self.label)
2485 .field("inner", &self.inner)
2486 .finish()
2487 }
2488}
2489
2490#[derive(Debug, Clone)]
2491pub struct EventProxy<T: UserEvent>(TaoEventLoopProxy<Message<T>>);
2492
2493#[cfg(target_os = "ios")]
2494#[allow(clippy::non_send_fields_in_send_ty)]
2495unsafe impl<T: UserEvent> Sync for EventProxy<T> {}
2496
2497impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
2498 fn send_event(&self, event: T) -> Result<()> {
2499 self
2500 .0
2501 .send_event(Message::UserEvent(event))
2502 .map_err(|_| Error::EventLoopClosed)
2503 }
2504}
2505
2506pub trait PluginBuilder<T: UserEvent> {
2507 type Plugin: Plugin<T>;
2508 fn build(self, context: Context<T>) -> Self::Plugin;
2509}
2510
2511pub trait Plugin<T: UserEvent> {
2512 fn on_event(
2513 &mut self,
2514 event: &Event<Message<T>>,
2515 event_loop: &EventLoopWindowTarget<Message<T>>,
2516 proxy: &TaoEventLoopProxy<Message<T>>,
2517 control_flow: &mut ControlFlow,
2518 context: EventLoopIterationContext<'_, T>,
2519 web_context: &WebContextStore,
2520 ) -> bool;
2521}
2522
2523pub struct Wry<T: UserEvent> {
2525 context: Context<T>,
2526 event_loop: EventLoop<Message<T>>,
2527}
2528
2529impl<T: UserEvent> fmt::Debug for Wry<T> {
2530 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2531 f.debug_struct("Wry")
2532 .field("main_thread_id", &self.context.main_thread_id)
2533 .field("event_loop", &self.event_loop)
2534 .field("windows", &self.context.main_thread.windows)
2535 .field("web_context", &self.context.main_thread.web_context)
2536 .finish()
2537 }
2538}
2539
2540#[derive(Debug, Clone)]
2542pub struct WryHandle<T: UserEvent> {
2543 context: Context<T>,
2544}
2545
2546#[allow(clippy::non_send_fields_in_send_ty)]
2548unsafe impl<T: UserEvent> Sync for WryHandle<T> {}
2549
2550impl<T: UserEvent> WryHandle<T> {
2551 pub fn create_tao_window<F: FnOnce() -> (String, TaoWindowBuilder) + Send + 'static>(
2553 &self,
2554 f: F,
2555 ) -> Result<Weak<Window>> {
2556 let id = self.context.next_window_id();
2557 let (tx, rx) = channel();
2558 self
2559 .context
2560 .send_user_message(Message::CreateRawWindow(id, Box::new(f), tx))?;
2561 rx.recv().unwrap()
2562 }
2563
2564 pub fn window_id(&self, window_id: TaoWindowId) -> WindowId {
2566 self.context.window_id_map.get(&window_id).unwrap()
2567 }
2568
2569 pub fn send_event(&self, message: Message<T>) -> Result<()> {
2571 self
2572 .context
2573 .proxy
2574 .send_event(message)
2575 .map_err(|_| Error::FailedToSendMessage)?;
2576 Ok(())
2577 }
2578
2579 pub fn plugin<P: PluginBuilder<T> + 'static>(&mut self, plugin: P)
2580 where
2581 <P as PluginBuilder<T>>::Plugin: Send,
2582 {
2583 self
2584 .context
2585 .plugins
2586 .lock()
2587 .unwrap()
2588 .push(Box::new(plugin.build(self.context.clone())));
2589 }
2590}
2591
2592impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
2593 type Runtime = Wry<T>;
2594
2595 fn create_proxy(&self) -> EventProxy<T> {
2596 EventProxy(self.context.proxy.clone())
2597 }
2598
2599 #[cfg(target_os = "macos")]
2600 fn set_activation_policy(&self, activation_policy: ActivationPolicy) -> Result<()> {
2601 self
2602 .context
2603 .send_user_message(Message::SetActivationPolicy(activation_policy))
2604 }
2605
2606 #[cfg(target_os = "macos")]
2607 fn set_dock_visibility(&self, visible: bool) -> Result<()> {
2608 self
2609 .context
2610 .send_user_message(Message::SetDockVisibility(visible))
2611 }
2612
2613 fn request_exit(&self, code: i32) -> Result<()> {
2614 self
2616 .context
2617 .proxy
2618 .send_event(Message::RequestExit(code))
2619 .map_err(|_| Error::FailedToSendMessage)
2620 }
2621
2622 fn create_window<F: Fn(RawWindow) + Send + 'static>(
2625 &self,
2626 pending: PendingWindow<T, Self::Runtime>,
2627 after_window_creation: Option<F>,
2628 ) -> Result<DetachedWindow<T, Self::Runtime>> {
2629 self.context.create_window(pending, after_window_creation)
2630 }
2631
2632 fn create_webview(
2635 &self,
2636 window_id: WindowId,
2637 pending: PendingWebview<T, Self::Runtime>,
2638 ) -> Result<DetachedWebview<T, Self::Runtime>> {
2639 self.context.create_webview(window_id, pending)
2640 }
2641
2642 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
2643 self.context.send_user_message(Message::Task(Box::new(f)))
2644 }
2645
2646 fn display_handle(
2647 &self,
2648 ) -> std::result::Result<DisplayHandle<'_>, raw_window_handle::HandleError> {
2649 self.context.main_thread.window_target.display_handle()
2650 }
2651
2652 fn primary_monitor(&self) -> Result<Option<Monitor>> {
2653 event_loop_window_getter!(self, EventLoopWindowTargetMessage::PrimaryMonitor)
2654 }
2655
2656 fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
2657 let (tx, rx) = channel();
2658 self
2659 .context
2660 .send_user_message(Message::EventLoopWindowTarget(
2661 EventLoopWindowTargetMessage::MonitorFromPoint(tx, (x, y)),
2662 ))?;
2663 Ok(rx.recv().unwrap())
2664 }
2665
2666 fn available_monitors(&self) -> Result<Vec<Monitor>> {
2667 event_loop_window_getter!(self, EventLoopWindowTargetMessage::AvailableMonitors)
2668 }
2669
2670 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
2671 event_loop_window_getter!(self, EventLoopWindowTargetMessage::CursorPosition)?
2672 .map_err(|_| Error::FailedToGetCursorPosition)
2673 }
2674
2675 fn set_theme(&self, theme: Option<Theme>) {
2676 let _ = self
2677 .context
2678 .send_user_message(Message::EventLoopWindowTarget(
2679 EventLoopWindowTargetMessage::SetTheme(theme),
2680 ));
2681 }
2682
2683 #[cfg(target_os = "macos")]
2684 fn show(&self) -> tauri_runtime::Result<()> {
2685 self
2686 .context
2687 .send_user_message(Message::Application(ApplicationMessage::Show))
2688 }
2689
2690 #[cfg(target_os = "macos")]
2691 fn hide(&self) -> tauri_runtime::Result<()> {
2692 self
2693 .context
2694 .send_user_message(Message::Application(ApplicationMessage::Hide))
2695 }
2696
2697 fn set_device_event_filter(&self, filter: DeviceEventFilter) {
2698 let _ = self
2699 .context
2700 .send_user_message(Message::EventLoopWindowTarget(
2701 EventLoopWindowTargetMessage::SetDeviceEventFilter(filter),
2702 ));
2703 }
2704
2705 #[cfg(target_os = "android")]
2706 fn find_class<'a>(
2707 &self,
2708 env: &mut jni::JNIEnv<'a>,
2709 activity: &jni::objects::JObject<'_>,
2710 name: impl Into<String>,
2711 ) -> std::result::Result<jni::objects::JClass<'a>, jni::errors::Error> {
2712 find_class(env, activity, name.into())
2713 }
2714
2715 #[cfg(target_os = "android")]
2716 fn run_on_android_context<F>(&self, f: F)
2717 where
2718 F: FnOnce(&mut jni::JNIEnv<'_>, &jni::objects::JObject<'_>, &jni::objects::JObject<'_>)
2719 + Send
2720 + 'static,
2721 {
2722 dispatch(f)
2723 }
2724
2725 #[cfg(any(target_os = "macos", target_os = "ios"))]
2726 fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
2727 &self,
2728 cb: F,
2729 ) -> Result<()> {
2730 self.context.send_user_message(Message::Application(
2731 ApplicationMessage::FetchDataStoreIdentifiers(Box::new(cb)),
2732 ))
2733 }
2734
2735 #[cfg(any(target_os = "macos", target_os = "ios"))]
2736 fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
2737 &self,
2738 uuid: [u8; 16],
2739 cb: F,
2740 ) -> Result<()> {
2741 self
2742 .context
2743 .send_user_message(Message::Application(ApplicationMessage::RemoveDataStore(
2744 uuid,
2745 Box::new(cb),
2746 )))
2747 }
2748}
2749
2750impl<T: UserEvent> Wry<T> {
2751 fn init_with_builder(
2752 mut event_loop_builder: EventLoopBuilder<Message<T>>,
2753 #[allow(unused_variables)] args: RuntimeInitArgs,
2754 ) -> Result<Self> {
2755 #[cfg(windows)]
2756 if let Some(hook) = args.msg_hook {
2757 use tao::platform::windows::EventLoopBuilderExtWindows;
2758 event_loop_builder.with_msg_hook(hook);
2759 }
2760
2761 #[cfg(any(
2762 target_os = "linux",
2763 target_os = "dragonfly",
2764 target_os = "freebsd",
2765 target_os = "netbsd",
2766 target_os = "openbsd"
2767 ))]
2768 if let Some(app_id) = args.app_id {
2769 use tao::platform::unix::EventLoopBuilderExtUnix;
2770 event_loop_builder.with_app_id(app_id);
2771 }
2772 Self::init(event_loop_builder.build())
2773 }
2774
2775 fn init(event_loop: EventLoop<Message<T>>) -> Result<Self> {
2776 let main_thread_id = current_thread().id();
2777 let web_context = WebContextStore::default();
2778
2779 #[allow(clippy::arc_with_non_send_sync)]
2780 let windows = Arc::new(WindowsStore(RefCell::new(BTreeMap::default())));
2781 let window_id_map = WindowIdStore::default();
2782
2783 let context = Context {
2784 window_id_map,
2785 main_thread_id,
2786 proxy: event_loop.create_proxy(),
2787 main_thread: DispatcherMainThreadContext {
2788 window_target: event_loop.deref().clone(),
2789 web_context,
2790 windows,
2791 #[cfg(feature = "tracing")]
2792 active_tracing_spans: Default::default(),
2793 },
2794 plugins: Default::default(),
2795 next_window_id: Default::default(),
2796 next_webview_id: Default::default(),
2797 next_window_event_id: Default::default(),
2798 next_webview_event_id: Default::default(),
2799 webview_runtime_installed: wry::webview_version().is_ok(),
2800 };
2801
2802 Ok(Self {
2803 context,
2804 event_loop,
2805 })
2806 }
2807}
2808
2809impl<T: UserEvent> Runtime<T> for Wry<T> {
2810 type WindowDispatcher = WryWindowDispatcher<T>;
2811 type WebviewDispatcher = WryWebviewDispatcher<T>;
2812 type Handle = WryHandle<T>;
2813
2814 type EventLoopProxy = EventProxy<T>;
2815
2816 fn new(args: RuntimeInitArgs) -> Result<Self> {
2817 Self::init_with_builder(EventLoopBuilder::<Message<T>>::with_user_event(), args)
2818 }
2819 #[cfg(any(
2820 target_os = "linux",
2821 target_os = "dragonfly",
2822 target_os = "freebsd",
2823 target_os = "netbsd",
2824 target_os = "openbsd"
2825 ))]
2826 fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
2827 use tao::platform::unix::EventLoopBuilderExtUnix;
2828 let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
2829 event_loop_builder.with_any_thread(true);
2830 Self::init_with_builder(event_loop_builder, args)
2831 }
2832
2833 #[cfg(windows)]
2834 fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
2835 use tao::platform::windows::EventLoopBuilderExtWindows;
2836 let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
2837 event_loop_builder.with_any_thread(true);
2838 Self::init_with_builder(event_loop_builder, args)
2839 }
2840
2841 fn create_proxy(&self) -> EventProxy<T> {
2842 EventProxy(self.event_loop.create_proxy())
2843 }
2844
2845 fn handle(&self) -> Self::Handle {
2846 WryHandle {
2847 context: self.context.clone(),
2848 }
2849 }
2850
2851 fn create_window<F: Fn(RawWindow) + Send + 'static>(
2852 &self,
2853 pending: PendingWindow<T, Self>,
2854 after_window_creation: Option<F>,
2855 ) -> Result<DetachedWindow<T, Self>> {
2856 let label = pending.label.clone();
2857 let window_id = self.context.next_window_id();
2858 let (webview_id, use_https_scheme) = pending
2859 .webview
2860 .as_ref()
2861 .map(|w| {
2862 (
2863 Some(self.context.next_webview_id()),
2864 w.webview_attributes.use_https_scheme,
2865 )
2866 })
2867 .unwrap_or((None, false));
2868
2869 let window = create_window(
2870 window_id,
2871 webview_id.unwrap_or_default(),
2872 &self.event_loop,
2873 &self.context,
2874 pending,
2875 after_window_creation,
2876 )?;
2877
2878 let dispatcher = WryWindowDispatcher {
2879 window_id,
2880 context: self.context.clone(),
2881 };
2882
2883 self
2884 .context
2885 .main_thread
2886 .windows
2887 .0
2888 .borrow_mut()
2889 .insert(window_id, window);
2890
2891 let detached_webview = webview_id.map(|id| {
2892 let webview = DetachedWebview {
2893 label: label.clone(),
2894 dispatcher: WryWebviewDispatcher {
2895 window_id: Arc::new(Mutex::new(window_id)),
2896 webview_id: id,
2897 context: self.context.clone(),
2898 },
2899 };
2900 DetachedWindowWebview {
2901 webview,
2902 use_https_scheme,
2903 }
2904 });
2905
2906 Ok(DetachedWindow {
2907 id: window_id,
2908 label,
2909 dispatcher,
2910 webview: detached_webview,
2911 })
2912 }
2913
2914 fn create_webview(
2915 &self,
2916 window_id: WindowId,
2917 pending: PendingWebview<T, Self>,
2918 ) -> Result<DetachedWebview<T, Self>> {
2919 let label = pending.label.clone();
2920
2921 let window = self
2922 .context
2923 .main_thread
2924 .windows
2925 .0
2926 .borrow()
2927 .get(&window_id)
2928 .map(|w| {
2929 (
2930 w.inner.clone(),
2931 CreateWebviewOptions {
2932 #[cfg(windows)]
2933 focused_webview: w.focused_webview.clone(),
2934 },
2935 )
2936 });
2937 if let Some((Some(window), _options)) = window {
2938 let window_id_wrapper = Arc::new(Mutex::new(window_id));
2939
2940 let webview_id = self.context.next_webview_id();
2941
2942 let webview = create_webview(
2943 WebviewKind::WindowChild,
2944 &window,
2945 window_id_wrapper.clone(),
2946 webview_id,
2947 &self.context,
2948 pending,
2949 #[cfg(windows)]
2950 _options.focused_webview,
2951 )?;
2952
2953 if let Some(w) = self
2954 .context
2955 .main_thread
2956 .windows
2957 .0
2958 .borrow_mut()
2959 .get_mut(&window_id)
2960 {
2961 w.webviews.push(webview);
2962 w.has_children.store(true, Ordering::Relaxed);
2963 }
2964
2965 let dispatcher = WryWebviewDispatcher {
2966 window_id: window_id_wrapper,
2967 webview_id,
2968 context: self.context.clone(),
2969 };
2970
2971 Ok(DetachedWebview { label, dispatcher })
2972 } else {
2973 Err(Error::WindowNotFound)
2974 }
2975 }
2976
2977 fn primary_monitor(&self) -> Option<Monitor> {
2978 self
2979 .context
2980 .main_thread
2981 .window_target
2982 .primary_monitor()
2983 .map(|m| MonitorHandleWrapper(m).into())
2984 }
2985
2986 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
2987 self
2988 .context
2989 .main_thread
2990 .window_target
2991 .monitor_from_point(x, y)
2992 .map(|m| MonitorHandleWrapper(m).into())
2993 }
2994
2995 fn available_monitors(&self) -> Vec<Monitor> {
2996 self
2997 .context
2998 .main_thread
2999 .window_target
3000 .available_monitors()
3001 .map(|m| MonitorHandleWrapper(m).into())
3002 .collect()
3003 }
3004
3005 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
3006 self
3007 .context
3008 .main_thread
3009 .window_target
3010 .cursor_position()
3011 .map_err(|_| Error::FailedToGetCursorPosition)
3012 }
3013
3014 fn set_theme(&self, theme: Option<Theme>) {
3015 self.event_loop.set_theme(to_tao_theme(theme));
3016 }
3017
3018 #[cfg(target_os = "macos")]
3019 fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
3020 self
3021 .event_loop
3022 .set_activation_policy(tao_activation_policy(activation_policy));
3023 }
3024
3025 #[cfg(target_os = "macos")]
3026 fn set_activate_ignoring_other_apps(&mut self, ignore: bool) {
3027 self.event_loop.set_activate_ignoring_other_apps(ignore);
3028 }
3029
3030 #[cfg(target_os = "macos")]
3031 fn set_dock_visibility(&mut self, visible: bool) {
3032 self.event_loop.set_dock_visibility(visible);
3033 }
3034
3035 #[cfg(target_os = "macos")]
3036 fn show(&self) {
3037 self.event_loop.show_application();
3038 }
3039
3040 #[cfg(target_os = "macos")]
3041 fn hide(&self) {
3042 self.event_loop.hide_application();
3043 }
3044
3045 fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {
3046 self
3047 .event_loop
3048 .set_device_event_filter(DeviceEventFilterWrapper::from(filter).0);
3049 }
3050
3051 #[cfg(desktop)]
3052 fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, mut callback: F) {
3053 use tao::platform::run_return::EventLoopExtRunReturn;
3054 let windows = &self.context.main_thread.windows;
3055 let window_id_map = &self.context.window_id_map;
3056 let web_context = &self.context.main_thread.web_context;
3057 let plugins = &self.context.plugins;
3058
3059 #[cfg(feature = "tracing")]
3060 let active_tracing_spans = &self.context.main_thread.active_tracing_spans;
3061
3062 let proxy = self.event_loop.create_proxy();
3063
3064 self
3065 .event_loop
3066 .run_return(|event, event_loop, control_flow| {
3067 *control_flow = ControlFlow::Wait;
3068 if let Event::MainEventsCleared = &event {
3069 *control_flow = ControlFlow::Exit;
3070 }
3071
3072 for p in plugins.lock().unwrap().iter_mut() {
3073 let prevent_default = p.on_event(
3074 &event,
3075 event_loop,
3076 &proxy,
3077 control_flow,
3078 EventLoopIterationContext {
3079 callback: &mut callback,
3080 window_id_map,
3081 windows,
3082 #[cfg(feature = "tracing")]
3083 active_tracing_spans,
3084 },
3085 web_context,
3086 );
3087 if prevent_default {
3088 return;
3089 }
3090 }
3091
3092 handle_event_loop(
3093 event,
3094 event_loop,
3095 control_flow,
3096 EventLoopIterationContext {
3097 callback: &mut callback,
3098 windows,
3099 window_id_map,
3100 #[cfg(feature = "tracing")]
3101 active_tracing_spans,
3102 },
3103 );
3104 });
3105 }
3106
3107 fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) {
3108 let event_handler = make_event_handler(self.context, callback);
3109 self.event_loop.run(event_handler)
3110 }
3111
3112 #[cfg(not(target_os = "ios"))]
3113 fn run_return<F: FnMut(RunEvent<T>) + 'static>(mut self, callback: F) -> i32 {
3114 use tao::platform::run_return::EventLoopExtRunReturn;
3115
3116 let event_handler = make_event_handler(self.context, callback);
3117 self.event_loop.run_return(event_handler)
3118 }
3119
3120 #[cfg(target_os = "ios")]
3121 fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
3122 self.run(callback);
3123 0
3124 }
3125}
3126
3127fn make_event_handler<T: UserEvent, F: FnMut(RunEvent<T>) + 'static>(
3128 context: Context<T>,
3129 mut callback: F,
3130) -> impl FnMut(Event<'_, Message<T>>, &EventLoopWindowTarget<Message<T>>, &mut ControlFlow) {
3131 let windows = context.main_thread.windows;
3132 let window_id_map = context.window_id_map;
3133 let web_context = context.main_thread.web_context;
3134 let plugins = context.plugins;
3135
3136 #[cfg(feature = "tracing")]
3137 let active_tracing_spans = context.main_thread.active_tracing_spans;
3138 let proxy = context.proxy;
3139
3140 move |event, event_loop, control_flow| {
3141 for p in plugins.lock().unwrap().iter_mut() {
3142 let prevent_default = p.on_event(
3143 &event,
3144 event_loop,
3145 &proxy,
3146 control_flow,
3147 EventLoopIterationContext {
3148 callback: &mut callback,
3149 window_id_map: &window_id_map,
3150 windows: &windows,
3151 #[cfg(feature = "tracing")]
3152 active_tracing_spans: &active_tracing_spans,
3153 },
3154 &web_context,
3155 );
3156 if prevent_default {
3157 return;
3158 }
3159 }
3160 handle_event_loop(
3161 event,
3162 event_loop,
3163 control_flow,
3164 EventLoopIterationContext {
3165 callback: &mut callback,
3166 window_id_map: &window_id_map,
3167 windows: &windows,
3168 #[cfg(feature = "tracing")]
3169 active_tracing_spans: &active_tracing_spans,
3170 },
3171 );
3172 }
3173}
3174
3175pub struct EventLoopIterationContext<'a, T: UserEvent> {
3176 pub callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
3177 pub window_id_map: &'a WindowIdStore,
3178 pub windows: &'a WindowsStore,
3179 #[cfg(feature = "tracing")]
3180 pub active_tracing_spans: &'a ActiveTraceSpanStore,
3181}
3182
3183struct UserMessageContext<'a> {
3184 windows: &'a WindowsStore,
3185 window_id_map: &'a WindowIdStore,
3186}
3187
3188fn handle_user_message<T: UserEvent>(
3189 event_loop: &EventLoopWindowTarget<Message<T>>,
3190 message: Message<T>,
3191 context: UserMessageContext,
3192) {
3193 let UserMessageContext {
3194 window_id_map,
3195 windows,
3196 } = context;
3197 match message {
3198 Message::Task(task) => task(),
3199 #[cfg(target_os = "macos")]
3200 Message::SetActivationPolicy(activation_policy) => {
3201 event_loop.set_activation_policy_at_runtime(tao_activation_policy(activation_policy))
3202 }
3203 #[cfg(target_os = "macos")]
3204 Message::SetDockVisibility(visible) => event_loop.set_dock_visibility(visible),
3205 Message::RequestExit(_code) => panic!("cannot handle RequestExit on the main thread"),
3206 Message::Application(application_message) => match application_message {
3207 #[cfg(target_os = "macos")]
3208 ApplicationMessage::Show => {
3209 event_loop.show_application();
3210 }
3211 #[cfg(target_os = "macos")]
3212 ApplicationMessage::Hide => {
3213 event_loop.hide_application();
3214 }
3215 #[cfg(any(target_os = "macos", target_os = "ios"))]
3216 ApplicationMessage::FetchDataStoreIdentifiers(cb) => {
3217 if let Err(e) = WebView::fetch_data_store_identifiers(cb) {
3218 log::error!("failed to fetch data store identifiers: {e}");
3221 }
3222 }
3223 #[cfg(any(target_os = "macos", target_os = "ios"))]
3224 ApplicationMessage::RemoveDataStore(uuid, cb) => {
3225 WebView::remove_data_store(&uuid, move |res| {
3226 cb(res.map_err(|_| Error::FailedToRemoveDataStore))
3227 })
3228 }
3229 },
3230 Message::Window(id, window_message) => {
3231 let w = windows.0.borrow().get(&id).map(|w| {
3232 #[cfg(windows)]
3233 let focused_webview = w.focused_webview.clone();
3234 #[cfg(not(windows))]
3235 let focused_webview = ();
3236 (
3237 w.inner.clone(),
3238 w.webviews.clone(),
3239 w.has_children.load(Ordering::Relaxed),
3240 w.window_event_listeners.clone(),
3241 focused_webview,
3242 )
3243 });
3244 if let Some((
3245 Some(window),
3246 webviews,
3247 has_children,
3248 window_event_listeners,
3249 _focused_webview,
3250 )) = w
3251 {
3252 match window_message {
3253 WindowMessage::AddEventListener(id, listener) => {
3254 window_event_listeners.lock().unwrap().insert(id, listener);
3255 }
3256
3257 WindowMessage::ScaleFactor(tx) => tx.send(window.scale_factor()).unwrap(),
3259 WindowMessage::InnerPosition(tx) => tx
3260 .send(
3261 window
3262 .inner_position()
3263 .map_err(|_| Error::FailedToSendMessage),
3264 )
3265 .unwrap(),
3266 WindowMessage::OuterPosition(tx) => tx
3267 .send(
3268 window
3269 .outer_position()
3270 .map_err(|_| Error::FailedToSendMessage),
3271 )
3272 .unwrap(),
3273 WindowMessage::InnerSize(tx) => tx
3274 .send(inner_size(&window, &webviews, has_children))
3275 .unwrap(),
3276 WindowMessage::OuterSize(tx) => tx.send(window.outer_size()).unwrap(),
3277 WindowMessage::IsFullscreen(tx) => tx.send(window.fullscreen().is_some()).unwrap(),
3278 WindowMessage::IsMinimized(tx) => tx.send(window.is_minimized()).unwrap(),
3279 WindowMessage::IsMaximized(tx) => tx.send(window.is_maximized()).unwrap(),
3280 #[cfg(not(windows))]
3281 WindowMessage::IsFocused(tx) => tx.send(window.is_focused()).unwrap(),
3282 #[cfg(windows)]
3283 WindowMessage::IsFocused(tx) => {
3284 let focused = if has_children {
3285 matches!(
3288 *_focused_webview.lock().unwrap(),
3289 FocusState::WindowFocused | FocusState::WebviewFocused { .. }
3290 )
3291 } else {
3292 window.is_focused()
3293 };
3294 tx.send(focused).unwrap()
3295 }
3296 WindowMessage::IsDecorated(tx) => tx.send(window.is_decorated()).unwrap(),
3297 WindowMessage::IsResizable(tx) => tx.send(window.is_resizable()).unwrap(),
3298 WindowMessage::IsMaximizable(tx) => tx.send(window.is_maximizable()).unwrap(),
3299 WindowMessage::IsMinimizable(tx) => tx.send(window.is_minimizable()).unwrap(),
3300 WindowMessage::IsClosable(tx) => tx.send(window.is_closable()).unwrap(),
3301 WindowMessage::IsVisible(tx) => tx.send(window.is_visible()).unwrap(),
3302 WindowMessage::Title(tx) => tx.send(window.title()).unwrap(),
3303 WindowMessage::CurrentMonitor(tx) => tx
3304 .send(
3305 window
3306 .current_monitor()
3307 .map(|m| MonitorHandleWrapper(m).into()),
3308 )
3309 .unwrap(),
3310 WindowMessage::PrimaryMonitor(tx) => tx
3311 .send(
3312 window
3313 .primary_monitor()
3314 .map(|m| MonitorHandleWrapper(m).into()),
3315 )
3316 .unwrap(),
3317 WindowMessage::MonitorFromPoint(tx, (x, y)) => tx
3318 .send(
3319 window
3320 .monitor_from_point(x, y)
3321 .map(|m| MonitorHandleWrapper(m).into()),
3322 )
3323 .unwrap(),
3324 WindowMessage::AvailableMonitors(tx) => tx
3325 .send(
3326 window
3327 .available_monitors()
3328 .map(|m| MonitorHandleWrapper(m).into())
3329 .collect(),
3330 )
3331 .unwrap(),
3332 #[cfg(any(
3333 target_os = "linux",
3334 target_os = "dragonfly",
3335 target_os = "freebsd",
3336 target_os = "netbsd",
3337 target_os = "openbsd"
3338 ))]
3339 WindowMessage::GtkWindow(tx) => tx.send(GtkWindow(window.gtk_window().clone())).unwrap(),
3340 #[cfg(any(
3341 target_os = "linux",
3342 target_os = "dragonfly",
3343 target_os = "freebsd",
3344 target_os = "netbsd",
3345 target_os = "openbsd"
3346 ))]
3347 WindowMessage::GtkBox(tx) => tx
3348 .send(GtkBox(window.default_vbox().unwrap().clone()))
3349 .unwrap(),
3350 #[cfg(target_os = "android")]
3351 WindowMessage::ActivityName(tx) => {
3352 tx.send(window.activity_name()).unwrap();
3353 }
3354 #[cfg(target_os = "ios")]
3355 WindowMessage::SceneIdentifier(tx) => {
3356 tx.send(window.scene_identifier()).unwrap();
3357 }
3358 WindowMessage::RawWindowHandle(tx) => tx
3359 .send(
3360 window
3361 .window_handle()
3362 .map(|h| SendRawWindowHandle(h.as_raw())),
3363 )
3364 .unwrap(),
3365 WindowMessage::Theme(tx) => {
3366 tx.send(map_theme(&window.theme())).unwrap();
3367 }
3368 WindowMessage::IsEnabled(tx) => tx.send(window.is_enabled()).unwrap(),
3369 WindowMessage::IsAlwaysOnTop(tx) => tx.send(window.is_always_on_top()).unwrap(),
3370 WindowMessage::Center => window.center(),
3372 WindowMessage::RequestUserAttention(request_type) => {
3373 window.request_user_attention(request_type.map(|r| r.0));
3374 }
3375 WindowMessage::SetResizable(resizable) => {
3376 window.set_resizable(resizable);
3377 #[cfg(windows)]
3378 if !resizable {
3379 undecorated_resizing::detach_resize_handler(window.hwnd());
3380 } else if !window.is_decorated() {
3381 undecorated_resizing::attach_resize_handler(
3382 window.hwnd(),
3383 window.has_undecorated_shadow(),
3384 );
3385 }
3386 }
3387 WindowMessage::SetMaximizable(maximizable) => window.set_maximizable(maximizable),
3388 WindowMessage::SetMinimizable(minimizable) => window.set_minimizable(minimizable),
3389 WindowMessage::SetClosable(closable) => window.set_closable(closable),
3390 WindowMessage::SetTitle(title) => window.set_title(&title),
3391 WindowMessage::Maximize => window.set_maximized(true),
3392 WindowMessage::Unmaximize => window.set_maximized(false),
3393 WindowMessage::Minimize => window.set_minimized(true),
3394 WindowMessage::Unminimize => window.set_minimized(false),
3395 WindowMessage::SetEnabled(enabled) => window.set_enabled(enabled),
3396 WindowMessage::Show => window.set_visible(true),
3397 WindowMessage::Hide => window.set_visible(false),
3398 WindowMessage::Close => {
3399 panic!("cannot handle `WindowMessage::Close` on the main thread")
3400 }
3401 WindowMessage::Destroy => {
3402 panic!("cannot handle `WindowMessage::Destroy` on the main thread")
3403 }
3404 WindowMessage::SetDecorations(decorations) => {
3405 window.set_decorations(decorations);
3406 #[cfg(windows)]
3407 if decorations {
3408 undecorated_resizing::detach_resize_handler(window.hwnd());
3409 } else if window.is_resizable() {
3410 undecorated_resizing::attach_resize_handler(
3411 window.hwnd(),
3412 window.has_undecorated_shadow(),
3413 );
3414 }
3415 }
3416 WindowMessage::SetShadow(_enable) => {
3417 #[cfg(windows)]
3418 {
3419 window.set_undecorated_shadow(_enable);
3420 undecorated_resizing::update_drag_hwnd_rgn_for_undecorated(window.hwnd(), _enable);
3421 }
3422 #[cfg(target_os = "macos")]
3423 window.set_has_shadow(_enable);
3424 }
3425 WindowMessage::SetAlwaysOnBottom(always_on_bottom) => {
3426 window.set_always_on_bottom(always_on_bottom)
3427 }
3428 WindowMessage::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top),
3429 WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces) => {
3430 window.set_visible_on_all_workspaces(visible_on_all_workspaces)
3431 }
3432 WindowMessage::SetContentProtected(protected) => window.set_content_protection(protected),
3433 WindowMessage::SetSize(size) => {
3434 window.set_inner_size(size);
3435 }
3436 WindowMessage::SetMinSize(size) => {
3437 window.set_min_inner_size(size);
3438 }
3439 WindowMessage::SetMaxSize(size) => {
3440 window.set_max_inner_size(size);
3441 }
3442 WindowMessage::SetSizeConstraints(constraints) => {
3443 window.set_inner_size_constraints(tao::window::WindowSizeConstraints {
3444 min_width: constraints.min_width,
3445 min_height: constraints.min_height,
3446 max_width: constraints.max_width,
3447 max_height: constraints.max_height,
3448 });
3449 }
3450 WindowMessage::SetPosition(position) => window.set_outer_position(position),
3451 WindowMessage::SetFullscreen(fullscreen) => {
3452 if fullscreen {
3453 window.set_fullscreen(Some(Fullscreen::Borderless(None)))
3454 } else {
3455 window.set_fullscreen(None)
3456 }
3457 }
3458 WindowMessage::SetFullscreenOnMonitor(position) => {
3459 if let Some(monitor) =
3462 find_monitor_for_position(window.available_monitors(), position.into())
3463 {
3464 window.set_fullscreen(Some(Fullscreen::Borderless(Some(monitor))))
3465 }
3466 }
3467
3468 #[cfg(target_os = "macos")]
3469 WindowMessage::SetSimpleFullscreen(enable) => {
3470 window.set_simple_fullscreen(enable);
3471 }
3472
3473 WindowMessage::SetFocus => {
3474 window.set_focus();
3475 }
3476 WindowMessage::SetFocusable(focusable) => {
3477 window.set_focusable(focusable);
3478 }
3479 WindowMessage::SetIcon(icon) => {
3480 window.set_window_icon(Some(icon));
3481 }
3482 #[allow(unused_variables)]
3483 WindowMessage::SetSkipTaskbar(skip) => {
3484 #[cfg(any(
3485 windows,
3486 target_os = "linux",
3487 target_os = "dragonfly",
3488 target_os = "freebsd",
3489 target_os = "netbsd",
3490 target_os = "openbsd"
3491 ))]
3492 let _ = window.set_skip_taskbar(skip);
3493 }
3494 WindowMessage::SetCursorGrab(grab) => {
3495 let _ = window.set_cursor_grab(grab);
3496 }
3497 WindowMessage::SetCursorVisible(visible) => {
3498 window.set_cursor_visible(visible);
3499 }
3500 WindowMessage::SetCursorIcon(icon) => {
3501 window.set_cursor_icon(CursorIconWrapper::from(icon).0);
3502 }
3503 WindowMessage::SetCursorPosition(position) => {
3504 let _ = window.set_cursor_position(position);
3505 }
3506 WindowMessage::SetIgnoreCursorEvents(ignore) => {
3507 let _ = window.set_ignore_cursor_events(ignore);
3508 }
3509 WindowMessage::DragWindow => {
3510 let _ = window.drag_window();
3511 }
3512 WindowMessage::ResizeDragWindow(direction) => {
3513 let _ = window.drag_resize_window(match direction {
3514 tauri_runtime::ResizeDirection::East => tao::window::ResizeDirection::East,
3515 tauri_runtime::ResizeDirection::North => tao::window::ResizeDirection::North,
3516 tauri_runtime::ResizeDirection::NorthEast => tao::window::ResizeDirection::NorthEast,
3517 tauri_runtime::ResizeDirection::NorthWest => tao::window::ResizeDirection::NorthWest,
3518 tauri_runtime::ResizeDirection::South => tao::window::ResizeDirection::South,
3519 tauri_runtime::ResizeDirection::SouthEast => tao::window::ResizeDirection::SouthEast,
3520 tauri_runtime::ResizeDirection::SouthWest => tao::window::ResizeDirection::SouthWest,
3521 tauri_runtime::ResizeDirection::West => tao::window::ResizeDirection::West,
3522 });
3523 }
3524 WindowMessage::RequestRedraw => {
3525 window.request_redraw();
3526 }
3527 WindowMessage::SetBadgeCount(_count, _desktop_filename) => {
3528 #[cfg(target_os = "ios")]
3529 window.set_badge_count(
3530 _count.map_or(0, |x| x.clamp(i32::MIN as i64, i32::MAX as i64) as i32),
3531 );
3532
3533 #[cfg(target_os = "macos")]
3534 window.set_badge_label(_count.map(|x| x.to_string()));
3535
3536 #[cfg(any(
3537 target_os = "linux",
3538 target_os = "dragonfly",
3539 target_os = "freebsd",
3540 target_os = "netbsd",
3541 target_os = "openbsd"
3542 ))]
3543 window.set_badge_count(_count, _desktop_filename);
3544 }
3545 WindowMessage::SetBadgeLabel(_label) => {
3546 #[cfg(target_os = "macos")]
3547 window.set_badge_label(_label);
3548 }
3549 WindowMessage::SetOverlayIcon(_icon) => {
3550 #[cfg(windows)]
3551 window.set_overlay_icon(_icon.map(|x| x.0).as_ref());
3552 }
3553 WindowMessage::SetProgressBar(progress_state) => {
3554 window.set_progress_bar(ProgressBarStateWrapper::from(progress_state).0);
3555 }
3556 WindowMessage::SetTitleBarStyle(_style) => {
3557 #[cfg(target_os = "macos")]
3558 match _style {
3559 TitleBarStyle::Visible => {
3560 window.set_titlebar_transparent(false);
3561 window.set_fullsize_content_view(true);
3562 }
3563 TitleBarStyle::Transparent => {
3564 window.set_titlebar_transparent(true);
3565 window.set_fullsize_content_view(false);
3566 }
3567 TitleBarStyle::Overlay => {
3568 window.set_titlebar_transparent(true);
3569 window.set_fullsize_content_view(true);
3570 }
3571 unknown => {
3572 #[cfg(feature = "tracing")]
3573 tracing::warn!("unknown title bar style applied: {unknown}");
3574
3575 #[cfg(not(feature = "tracing"))]
3576 eprintln!("unknown title bar style applied: {unknown}");
3577 }
3578 };
3579 }
3580 WindowMessage::SetTrafficLightPosition(_position) => {
3581 #[cfg(target_os = "macos")]
3582 window.set_traffic_light_inset(_position);
3583 }
3584 WindowMessage::SetTheme(theme) => {
3585 window.set_theme(to_tao_theme(theme));
3586 }
3587 WindowMessage::SetBackgroundColor(color) => {
3588 window.set_background_color(color.map(Into::into))
3589 }
3590 }
3591 }
3592 }
3593 Message::Webview(window_id, webview_id, webview_message) => {
3594 #[cfg(any(
3595 target_os = "macos",
3596 windows,
3597 target_os = "linux",
3598 target_os = "dragonfly",
3599 target_os = "freebsd",
3600 target_os = "netbsd",
3601 target_os = "openbsd"
3602 ))]
3603 if let WebviewMessage::Reparent(new_parent_window_id, tx) = webview_message {
3604 let webview_handle = windows.0.borrow_mut().get_mut(&window_id).and_then(|w| {
3605 w.webviews
3606 .iter()
3607 .position(|w| w.id == webview_id)
3608 .map(|webview_index| w.webviews.remove(webview_index))
3609 });
3610
3611 if let Some(webview) = webview_handle {
3612 if let Some((Some(new_parent_window), new_parent_window_webviews)) = windows
3613 .0
3614 .borrow_mut()
3615 .get_mut(&new_parent_window_id)
3616 .map(|w| (w.inner.clone(), &mut w.webviews))
3617 {
3618 #[cfg(target_os = "macos")]
3619 let reparent_result = {
3620 use wry::WebViewExtMacOS;
3621 webview.inner.reparent(new_parent_window.ns_window() as _)
3622 };
3623 #[cfg(windows)]
3624 let reparent_result = { webview.inner.reparent(new_parent_window.hwnd()) };
3625
3626 #[cfg(any(
3627 target_os = "linux",
3628 target_os = "dragonfly",
3629 target_os = "freebsd",
3630 target_os = "netbsd",
3631 target_os = "openbsd"
3632 ))]
3633 let reparent_result = {
3634 if let Some(container) = new_parent_window.default_vbox() {
3635 webview.inner.reparent(container)
3636 } else {
3637 Err(wry::Error::MessageSender)
3638 }
3639 };
3640
3641 match reparent_result {
3642 Ok(_) => {
3643 new_parent_window_webviews.push(webview);
3644 tx.send(Ok(())).unwrap();
3645 }
3646 Err(e) => {
3647 log::error!("failed to reparent webview: {e}");
3648 tx.send(Err(Error::FailedToSendMessage)).unwrap();
3649 }
3650 }
3651 }
3652 } else {
3653 tx.send(Err(Error::FailedToSendMessage)).unwrap();
3654 }
3655
3656 return;
3657 }
3658
3659 let webview_handle = windows.0.borrow().get(&window_id).map(|w| {
3660 (
3661 w.inner.clone(),
3662 w.webviews.iter().find(|w| w.id == webview_id).cloned(),
3663 )
3664 });
3665 if let Some((Some(window), Some(webview))) = webview_handle {
3666 match webview_message {
3667 WebviewMessage::WebviewEvent(_) => { }
3668 WebviewMessage::SynthesizedWindowEvent(_) => { }
3669 WebviewMessage::Reparent(_window_id, _tx) => { }
3670 WebviewMessage::AddEventListener(id, listener) => {
3671 webview
3672 .webview_event_listeners
3673 .lock()
3674 .unwrap()
3675 .insert(id, listener);
3676 }
3677
3678 #[cfg(all(feature = "tracing", not(target_os = "android")))]
3679 WebviewMessage::EvaluateScript(script, tx, span) => {
3680 let _span = span.entered();
3681 if let Err(e) = webview.evaluate_script(&script) {
3682 log::error!("{e}");
3683 }
3684 tx.send(()).unwrap();
3685 }
3686 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
3687 WebviewMessage::EvaluateScript(script) => {
3688 if let Err(e) = webview.evaluate_script(&script) {
3689 log::error!("{e}");
3690 }
3691 }
3692 #[cfg(all(feature = "tracing", not(target_os = "android")))]
3693 WebviewMessage::EvaluateScriptWithCallback(script, callback, tx, span) => {
3694 let _span = span.entered();
3695 if let Err(e) = webview.evaluate_script_with_callback(&script, callback) {
3696 log::error!("{e}");
3697 }
3698 tx.send(()).unwrap();
3699 }
3700 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
3701 WebviewMessage::EvaluateScriptWithCallback(script, callback) => {
3702 if let Err(e) = webview.evaluate_script_with_callback(&script, callback) {
3703 log::error!("{e}");
3704 }
3705 }
3706 WebviewMessage::Navigate(url) => {
3707 if let Err(e) = webview.load_url(url.as_str()) {
3708 log::error!("failed to navigate to url {}: {}", url, e);
3709 }
3710 }
3711 WebviewMessage::Reload => {
3712 if let Err(e) = webview.reload() {
3713 log::error!("failed to reload: {e}");
3714 }
3715 }
3716 WebviewMessage::Show => {
3717 if let Err(e) = webview.set_visible(true) {
3718 log::error!("failed to change webview visibility: {e}");
3719 }
3720 }
3721 WebviewMessage::Hide => {
3722 if let Err(e) = webview.set_visible(false) {
3723 log::error!("failed to change webview visibility: {e}");
3724 }
3725 }
3726 WebviewMessage::Print => {
3727 let _ = webview.print();
3728 }
3729 WebviewMessage::Close => {
3730 #[allow(unknown_lints, clippy::manual_inspect)]
3731 windows.0.borrow_mut().get_mut(&window_id).map(|window| {
3732 if let Some(i) = window.webviews.iter().position(|w| w.id == webview.id) {
3733 window.webviews.remove(i);
3734 }
3735 window
3736 });
3737 }
3738 WebviewMessage::SetBounds(bounds) => {
3739 let bounds: RectWrapper = bounds.into();
3740 let bounds = bounds.0;
3741
3742 if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3743 let scale_factor = window.scale_factor();
3744 let size = bounds.size.to_logical::<f32>(scale_factor);
3745 let position = bounds.position.to_logical::<f32>(scale_factor);
3746 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3747 b.width_rate = size.width / window_size.width;
3748 b.height_rate = size.height / window_size.height;
3749 b.x_rate = position.x / window_size.width;
3750 b.y_rate = position.y / window_size.height;
3751 }
3752
3753 if let Err(e) = webview.set_bounds(bounds) {
3754 log::error!("failed to set webview size: {e}");
3755 }
3756 }
3757 WebviewMessage::SetSize(size) => match webview.bounds() {
3758 Ok(mut bounds) => {
3759 bounds.size = size;
3760
3761 let scale_factor = window.scale_factor();
3762 let size = size.to_logical::<f32>(scale_factor);
3763
3764 if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3765 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3766 b.width_rate = size.width / window_size.width;
3767 b.height_rate = size.height / window_size.height;
3768 }
3769
3770 if let Err(e) = webview.set_bounds(bounds) {
3771 log::error!("failed to set webview size: {e}");
3772 }
3773 }
3774 Err(e) => {
3775 log::error!("failed to get webview bounds: {e}");
3776 }
3777 },
3778 WebviewMessage::SetPosition(position) => match webview.bounds() {
3779 Ok(mut bounds) => {
3780 bounds.position = position;
3781
3782 let scale_factor = window.scale_factor();
3783 let position = position.to_logical::<f32>(scale_factor);
3784
3785 if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3786 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3787 b.x_rate = position.x / window_size.width;
3788 b.y_rate = position.y / window_size.height;
3789 }
3790
3791 if let Err(e) = webview.set_bounds(bounds) {
3792 log::error!("failed to set webview position: {e}");
3793 }
3794 }
3795 Err(e) => {
3796 log::error!("failed to get webview bounds: {e}");
3797 }
3798 },
3799 WebviewMessage::SetZoom(scale_factor) => {
3800 if let Err(e) = webview.zoom(scale_factor) {
3801 log::error!("failed to set webview zoom: {e}");
3802 }
3803 }
3804 WebviewMessage::SetBackgroundColor(color) => {
3805 if let Err(e) =
3806 webview.set_background_color(color.map(Into::into).unwrap_or((255, 255, 255, 255)))
3807 {
3808 log::error!("failed to set webview background color: {e}");
3809 }
3810 }
3811 WebviewMessage::ClearAllBrowsingData => {
3812 if let Err(e) = webview.clear_all_browsing_data() {
3813 log::error!("failed to clear webview browsing data: {e}");
3814 }
3815 }
3816 WebviewMessage::Url(tx) => {
3818 tx.send(
3819 webview
3820 .url()
3821 .map(|u| u.parse().expect("invalid webview URL"))
3822 .map_err(|_| Error::FailedToSendMessage),
3823 )
3824 .unwrap();
3825 }
3826
3827 WebviewMessage::Cookies(tx) => {
3828 tx.send(webview.cookies().map_err(|_| Error::FailedToSendMessage))
3829 .unwrap();
3830 }
3831
3832 WebviewMessage::SetCookie(cookie) => {
3833 if let Err(e) = webview.set_cookie(&cookie) {
3834 log::error!("failed to set webview cookie: {e}");
3835 }
3836 }
3837
3838 WebviewMessage::DeleteCookie(cookie) => {
3839 if let Err(e) = webview.delete_cookie(&cookie) {
3840 log::error!("failed to delete webview cookie: {e}");
3841 }
3842 }
3843
3844 WebviewMessage::CookiesForUrl(url, tx) => {
3845 let webview_cookies = webview
3846 .cookies_for_url(url.as_str())
3847 .map_err(|_| Error::FailedToSendMessage);
3848 tx.send(webview_cookies).unwrap();
3849 }
3850
3851 WebviewMessage::Bounds(tx) => {
3852 tx.send(
3853 webview
3854 .bounds()
3855 .map(|bounds| tauri_runtime::dpi::Rect {
3856 size: bounds.size,
3857 position: bounds.position,
3858 })
3859 .map_err(|_| Error::FailedToSendMessage),
3860 )
3861 .unwrap();
3862 }
3863 WebviewMessage::Position(tx) => {
3864 tx.send(
3865 webview
3866 .bounds()
3867 .map(|bounds| bounds.position.to_physical(window.scale_factor()))
3868 .map_err(|_| Error::FailedToSendMessage),
3869 )
3870 .unwrap();
3871 }
3872 WebviewMessage::Size(tx) => {
3873 tx.send(
3874 webview
3875 .bounds()
3876 .map(|bounds| bounds.size.to_physical(window.scale_factor()))
3877 .map_err(|_| Error::FailedToSendMessage),
3878 )
3879 .unwrap();
3880 }
3881 WebviewMessage::SetFocus => {
3882 if let Err(e) = webview.focus() {
3883 log::error!("failed to focus webview: {e}");
3884 }
3885 }
3886 WebviewMessage::SetAutoResize(auto_resize) => match webview.bounds() {
3887 Ok(bounds) => {
3888 let scale_factor = window.scale_factor();
3889 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3890 *webview.bounds.lock().unwrap() = if auto_resize {
3891 let size = bounds.size.to_logical::<f32>(scale_factor);
3892 let position = bounds.position.to_logical::<f32>(scale_factor);
3893 Some(WebviewBounds {
3894 x_rate: position.x / window_size.width,
3895 y_rate: position.y / window_size.height,
3896 width_rate: size.width / window_size.width,
3897 height_rate: size.height / window_size.height,
3898 })
3899 } else {
3900 None
3901 };
3902 }
3903 Err(e) => {
3904 log::error!("failed to get webview bounds: {e}");
3905 }
3906 },
3907 WebviewMessage::WithWebview(f) => {
3908 #[cfg(any(
3909 target_os = "linux",
3910 target_os = "dragonfly",
3911 target_os = "freebsd",
3912 target_os = "netbsd",
3913 target_os = "openbsd"
3914 ))]
3915 {
3916 f(webview.webview());
3917 }
3918 #[cfg(target_os = "macos")]
3919 {
3920 use wry::WebViewExtMacOS;
3921 let platform_webview = webview.webview();
3922 let manager = webview.manager();
3923 let ns_window = webview.ns_window();
3924 f(Webview {
3925 webview: Retained::as_ptr(&platform_webview).cast_mut() as *mut std::ffi::c_void,
3926 manager: Retained::as_ptr(&manager).cast_mut() as *mut std::ffi::c_void,
3927 ns_window: Retained::as_ptr(&ns_window).cast_mut() as *mut std::ffi::c_void,
3928 });
3929 }
3930 #[cfg(target_os = "ios")]
3931 {
3932 use wry::WebViewExtIOS;
3933 let platform_webview = webview.inner.webview();
3934 let manager = webview.inner.manager();
3935
3936 f(Webview {
3937 webview: Retained::as_ptr(&platform_webview).cast_mut() as *mut std::ffi::c_void,
3938 manager: Retained::as_ptr(&manager).cast_mut() as *mut std::ffi::c_void,
3939 view_controller: window.ui_view_controller(),
3940 });
3941 }
3942 #[cfg(windows)]
3943 {
3944 f(Webview {
3945 controller: webview.controller(),
3946 environment: webview.environment(),
3947 });
3948 }
3949 #[cfg(target_os = "android")]
3950 {
3951 f(webview.handle())
3952 }
3953 }
3954 #[cfg(any(debug_assertions, feature = "devtools"))]
3955 WebviewMessage::OpenDevTools => {
3956 webview.open_devtools();
3957 }
3958 #[cfg(any(debug_assertions, feature = "devtools"))]
3959 WebviewMessage::CloseDevTools => {
3960 webview.close_devtools();
3961 }
3962 #[cfg(any(debug_assertions, feature = "devtools"))]
3963 WebviewMessage::IsDevToolsOpen(tx) => {
3964 tx.send(webview.is_devtools_open()).unwrap();
3965 }
3966 }
3967 }
3968 }
3969 Message::CreateWebview(window_id, handler, sender) => {
3970 let window = windows.0.borrow().get(&window_id).map(|w| {
3971 (
3972 w.inner.clone(),
3973 CreateWebviewOptions {
3974 #[cfg(windows)]
3975 focused_webview: w.focused_webview.clone(),
3976 },
3977 )
3978 });
3979 if let Some((Some(window), options)) = window {
3980 match handler(&window, options) {
3981 Ok(webview) => {
3982 if let Some(w) = windows.0.borrow_mut().get_mut(&window_id) {
3983 w.webviews.push(webview);
3984 w.has_children.store(true, Ordering::Relaxed);
3985 }
3986 sender.send(Ok(())).unwrap();
3988 }
3989 Err(e) => {
3990 sender.send(Err(e)).unwrap();
3992 }
3993 }
3994 }
3995 }
3996 Message::CreateWindow(window_id, handler, sender) => match handler(event_loop) {
3997 Ok(webview) => {
3998 windows.0.borrow_mut().insert(window_id, webview);
3999 sender.send(Ok(())).unwrap();
4001 }
4002 Err(e) => {
4003 sender.send(Err(e)).unwrap();
4005 }
4006 },
4007 Message::CreateRawWindow(window_id, handler, sender) => {
4008 let (label, builder) = handler();
4009
4010 #[cfg(windows)]
4011 let background_color = builder.window.background_color;
4012 #[cfg(windows)]
4013 let is_window_transparent = builder.window.transparent;
4014
4015 if let Ok(window) = builder.build(event_loop) {
4016 window_id_map.insert(window.id(), window_id);
4017
4018 let window = Arc::new(window);
4019
4020 #[cfg(windows)]
4021 let surface = if is_window_transparent {
4022 if let Ok(context) = softbuffer::Context::new(window.clone()) {
4023 if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
4024 window.draw_surface(&mut surface, background_color);
4025 Some(surface)
4026 } else {
4027 None
4028 }
4029 } else {
4030 None
4031 }
4032 } else {
4033 None
4034 };
4035
4036 windows.0.borrow_mut().insert(
4037 window_id,
4038 WindowWrapper {
4039 label,
4040 has_children: AtomicBool::new(false),
4041 inner: Some(window.clone()),
4042 window_event_listeners: Default::default(),
4043 webviews: Vec::new(),
4044 #[cfg(windows)]
4045 background_color,
4046 #[cfg(windows)]
4047 is_window_transparent,
4048 #[cfg(windows)]
4049 surface,
4050 #[cfg(windows)]
4051 focused_webview: Default::default(),
4052 },
4053 );
4054 sender.send(Ok(Arc::downgrade(&window))).unwrap();
4055 } else {
4056 sender.send(Err(Error::CreateWindow)).unwrap();
4057 }
4058 }
4059
4060 Message::UserEvent(_) => (),
4061 Message::EventLoopWindowTarget(message) => match message {
4062 EventLoopWindowTargetMessage::CursorPosition(sender) => {
4063 let pos = event_loop
4064 .cursor_position()
4065 .map_err(|_| Error::FailedToSendMessage);
4066 sender.send(pos).unwrap();
4067 }
4068 EventLoopWindowTargetMessage::PrimaryMonitor(sender) => {
4069 sender
4070 .send(
4071 event_loop
4072 .primary_monitor()
4073 .map(|m| MonitorHandleWrapper(m).into()),
4074 )
4075 .unwrap();
4076 }
4077 EventLoopWindowTargetMessage::MonitorFromPoint(sender, (x, y)) => {
4078 sender
4079 .send(
4080 event_loop
4081 .monitor_from_point(x, y)
4082 .map(|m| MonitorHandleWrapper(m).into()),
4083 )
4084 .unwrap();
4085 }
4086 EventLoopWindowTargetMessage::AvailableMonitors(sender) => {
4087 sender
4088 .send(
4089 event_loop
4090 .available_monitors()
4091 .map(|m| MonitorHandleWrapper(m).into())
4092 .collect(),
4093 )
4094 .unwrap();
4095 }
4096 EventLoopWindowTargetMessage::SetTheme(theme) => {
4097 event_loop.set_theme(to_tao_theme(theme));
4098 #[cfg(target_os = "macos")]
4104 for window in windows.0.borrow().values() {
4105 if let Some(inner) = &window.inner {
4106 inner.set_theme(to_tao_theme(theme));
4107 }
4108 }
4109 }
4110 EventLoopWindowTargetMessage::SetDeviceEventFilter(filter) => {
4111 event_loop.set_device_event_filter(DeviceEventFilterWrapper::from(filter).0);
4112 }
4113 },
4114 }
4115}
4116
4117fn handle_event_loop<T: UserEvent>(
4118 event: Event<'_, Message<T>>,
4119 event_loop: &EventLoopWindowTarget<Message<T>>,
4120 control_flow: &mut ControlFlow,
4121 context: EventLoopIterationContext<'_, T>,
4122) {
4123 let EventLoopIterationContext {
4124 callback,
4125 window_id_map,
4126 windows,
4127 #[cfg(feature = "tracing")]
4128 active_tracing_spans,
4129 } = context;
4130 if *control_flow != ControlFlow::Exit {
4131 *control_flow = ControlFlow::Wait;
4132 }
4133
4134 match event {
4135 Event::NewEvents(StartCause::Init) => {
4136 callback(RunEvent::Ready);
4137 }
4138
4139 Event::NewEvents(StartCause::Poll) => {
4140 callback(RunEvent::Resumed);
4141 }
4142
4143 Event::MainEventsCleared => {
4144 callback(RunEvent::MainEventsCleared);
4145 }
4146
4147 Event::LoopDestroyed => {
4148 callback(RunEvent::Exit);
4149 }
4150
4151 #[cfg(windows)]
4152 Event::RedrawRequested(id) => {
4153 if let Some(window_id) = window_id_map.get(&id) {
4154 let mut windows_ref = windows.0.borrow_mut();
4155 if let Some(window) = windows_ref.get_mut(&window_id) {
4156 if window.is_window_transparent {
4157 let background_color = window.background_color;
4158 if let Some(surface) = &mut window.surface {
4159 if let Some(window) = &window.inner {
4160 window.draw_surface(surface, background_color);
4161 }
4162 }
4163 }
4164 }
4165 }
4166 }
4167
4168 #[cfg(feature = "tracing")]
4169 Event::RedrawEventsCleared => {
4170 active_tracing_spans.remove_window_draw();
4171 }
4172
4173 Event::UserEvent(Message::Webview(
4174 window_id,
4175 webview_id,
4176 WebviewMessage::WebviewEvent(event),
4177 )) => {
4178 let windows_ref = windows.0.borrow();
4179 if let Some(window) = windows_ref.get(&window_id) {
4180 if let Some(webview) = window.webviews.iter().find(|w| w.id == webview_id) {
4181 let label = webview.label.clone();
4182 let webview_event_listeners = webview.webview_event_listeners.clone();
4183
4184 drop(windows_ref);
4185
4186 callback(RunEvent::WebviewEvent {
4187 label,
4188 event: event.clone(),
4189 });
4190 let listeners = webview_event_listeners.lock().unwrap();
4191 let handlers = listeners.values();
4192 for handler in handlers {
4193 handler(&event);
4194 }
4195 }
4196 }
4197 }
4198
4199 Event::UserEvent(Message::Webview(
4200 window_id,
4201 _webview_id,
4202 WebviewMessage::SynthesizedWindowEvent(event),
4203 )) => {
4204 if let Some(event) = WindowEventWrapper::from(event).0 {
4205 let windows_ref = windows.0.borrow();
4206 let window = windows_ref.get(&window_id);
4207 if let Some(window) = window {
4208 let label = window.label.clone();
4209 let window_event_listeners = window.window_event_listeners.clone();
4210
4211 drop(windows_ref);
4212
4213 callback(RunEvent::WindowEvent {
4214 label,
4215 event: event.clone(),
4216 });
4217
4218 let listeners = window_event_listeners.lock().unwrap();
4219 let handlers = listeners.values();
4220 for handler in handlers {
4221 handler(&event);
4222 }
4223 }
4224 }
4225 }
4226
4227 Event::WindowEvent {
4228 event, window_id, ..
4229 } => {
4230 if let Some(window_id) = window_id_map.get(&window_id) {
4231 {
4232 let windows_ref = windows.0.borrow();
4233 if let Some(window) = windows_ref.get(&window_id) {
4234 if let Some(event) = WindowEventWrapper::parse(window, &event).0 {
4235 let label = window.label.clone();
4236 let window_event_listeners = window.window_event_listeners.clone();
4237
4238 drop(windows_ref);
4239
4240 callback(RunEvent::WindowEvent {
4241 label,
4242 event: event.clone(),
4243 });
4244 let listeners = window_event_listeners.lock().unwrap();
4245 let handlers = listeners.values();
4246 for handler in handlers {
4247 handler(&event);
4248 }
4249 }
4250 }
4251 }
4252
4253 match event {
4254 #[cfg(windows)]
4255 TaoWindowEvent::ThemeChanged(theme) => {
4256 if let Some(window) = windows.0.borrow().get(&window_id) {
4257 for webview in &window.webviews {
4258 let theme = match theme {
4259 TaoTheme::Dark => wry::Theme::Dark,
4260 TaoTheme::Light => wry::Theme::Light,
4261 _ => wry::Theme::Light,
4262 };
4263 if let Err(e) = webview.set_theme(theme) {
4264 log::error!("failed to set theme: {e}");
4265 }
4266 }
4267 }
4268 }
4269 TaoWindowEvent::CloseRequested => {
4270 on_close_requested(callback, window_id, windows);
4271 }
4272 TaoWindowEvent::Destroyed => {
4273 let removed = windows.0.borrow_mut().remove(&window_id).is_some();
4274 if removed {
4275 let is_empty = windows.0.borrow().is_empty();
4276 if is_empty {
4277 let (tx, rx) = channel();
4278 callback(RunEvent::ExitRequested { code: None, tx });
4279
4280 let recv = rx.try_recv();
4281 let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
4282
4283 if !should_prevent {
4284 *control_flow = ControlFlow::Exit;
4285 }
4286 }
4287 }
4288 }
4289 TaoWindowEvent::Resized(size) => {
4290 if let Some((Some(window), webviews)) = windows
4291 .0
4292 .borrow()
4293 .get(&window_id)
4294 .map(|w| (w.inner.clone(), w.webviews.clone()))
4295 {
4296 let size = size.to_logical::<f32>(window.scale_factor());
4297 for webview in webviews {
4298 if let Some(b) = &*webview.bounds.lock().unwrap() {
4299 if let Err(e) = webview.set_bounds(wry::Rect {
4300 position: LogicalPosition::new(size.width * b.x_rate, size.height * b.y_rate)
4301 .into(),
4302 size: LogicalSize::new(size.width * b.width_rate, size.height * b.height_rate)
4303 .into(),
4304 }) {
4305 log::error!("failed to autoresize webview: {e}");
4306 }
4307 }
4308 }
4309 }
4310 }
4311 _ => {}
4312 }
4313 }
4314 }
4315 Event::UserEvent(message) => match message {
4316 Message::RequestExit(code) => {
4317 let (tx, rx) = channel();
4318 callback(RunEvent::ExitRequested {
4319 code: Some(code),
4320 tx,
4321 });
4322
4323 let recv = rx.try_recv();
4324 let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
4325
4326 if !should_prevent {
4327 *control_flow = ControlFlow::ExitWithCode(code);
4328 }
4329 }
4330 Message::Window(id, WindowMessage::Close) => {
4331 on_close_requested(callback, id, windows);
4332 }
4333 Message::Window(id, WindowMessage::Destroy) => {
4334 on_window_close(id, windows);
4335 }
4336 Message::UserEvent(t) => callback(RunEvent::UserEvent(t)),
4337 message => {
4338 handle_user_message(
4339 event_loop,
4340 message,
4341 UserMessageContext {
4342 window_id_map,
4343 windows,
4344 },
4345 );
4346 }
4347 },
4348 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
4349 Event::Opened { urls } => {
4350 callback(RunEvent::Opened { urls });
4351 }
4352 #[cfg(target_os = "macos")]
4353 Event::Reopen {
4354 has_visible_windows,
4355 ..
4356 } => callback(RunEvent::Reopen {
4357 has_visible_windows,
4358 }),
4359 #[cfg(target_os = "ios")]
4360 Event::SceneRequested { scene, options } => {
4361 callback(RunEvent::SceneRequested { scene, options });
4362 }
4363 _ => (),
4364 }
4365}
4366
4367fn on_close_requested<'a, T: UserEvent>(
4368 callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
4369 window_id: WindowId,
4370 windows: &WindowsStore,
4371) {
4372 let (tx, rx) = channel();
4373 let windows_ref = windows.0.borrow();
4374 if let Some(w) = windows_ref.get(&window_id) {
4375 let label = w.label.clone();
4376 let window_event_listeners = w.window_event_listeners.clone();
4377
4378 drop(windows_ref);
4379
4380 let listeners = window_event_listeners.lock().unwrap();
4381 let handlers = listeners.values();
4382 for handler in handlers {
4383 handler(&WindowEvent::CloseRequested {
4384 signal_tx: tx.clone(),
4385 });
4386 }
4387 callback(RunEvent::WindowEvent {
4388 label,
4389 event: WindowEvent::CloseRequested { signal_tx: tx },
4390 });
4391 if let Ok(true) = rx.try_recv() {
4392 } else {
4393 on_window_close(window_id, windows);
4394 }
4395 }
4396}
4397
4398fn on_window_close(window_id: WindowId, windows: &WindowsStore) {
4399 if let Some(window_wrapper) = windows.0.borrow_mut().get_mut(&window_id) {
4400 window_wrapper.inner = None;
4401 #[cfg(windows)]
4402 window_wrapper.surface.take();
4403 }
4404}
4405
4406fn parse_proxy_url(url: &Url) -> Result<ProxyConfig> {
4407 let host = url.host().map(|h| h.to_string()).unwrap_or_default();
4408 let port = url.port().map(|p| p.to_string()).unwrap_or_default();
4409
4410 if url.scheme() == "http" {
4411 let config = ProxyConfig::Http(ProxyEndpoint { host, port });
4412
4413 Ok(config)
4414 } else if url.scheme() == "socks5" {
4415 let config = ProxyConfig::Socks5(ProxyEndpoint { host, port });
4416
4417 Ok(config)
4418 } else {
4419 Err(Error::InvalidProxyUrl)
4420 }
4421}
4422
4423fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
4424 window_id: WindowId,
4425 webview_id: u32,
4426 event_loop: &EventLoopWindowTarget<Message<T>>,
4427 context: &Context<T>,
4428 pending: PendingWindow<T, Wry<T>>,
4429 after_window_creation: Option<F>,
4430) -> Result<WindowWrapper> {
4431 #[allow(unused_mut)]
4432 let PendingWindow {
4433 mut window_builder,
4434 label,
4435 webview,
4436 } = pending;
4437
4438 #[cfg(feature = "tracing")]
4439 let _webview_create_span = tracing::debug_span!("wry::webview::create").entered();
4440 #[cfg(feature = "tracing")]
4441 let window_draw_span = tracing::debug_span!("wry::window::draw").entered();
4442 #[cfg(feature = "tracing")]
4443 let window_create_span =
4444 tracing::debug_span!(parent: &window_draw_span, "wry::window::create").entered();
4445
4446 let window_event_listeners = WindowEventListeners::default();
4447
4448 #[cfg(windows)]
4449 let background_color = window_builder.inner.window.background_color;
4450 #[cfg(windows)]
4451 let is_window_transparent = window_builder.inner.window.transparent;
4452
4453 #[cfg(target_os = "macos")]
4454 {
4455 if window_builder.tabbing_identifier.is_none()
4456 || window_builder.inner.window.transparent
4457 || !window_builder.inner.window.decorations
4458 {
4459 window_builder.inner = window_builder.inner.with_automatic_window_tabbing(false);
4460 }
4461 }
4462
4463 #[cfg(desktop)]
4464 if window_builder.prevent_overflow.is_some() || window_builder.center {
4465 let monitor = if let Some(window_position) = &window_builder.inner.window.position {
4466 find_monitor_for_position(event_loop.available_monitors(), *window_position)
4467 } else {
4468 event_loop.primary_monitor()
4469 };
4470 if let Some(monitor) = monitor {
4471 let scale_factor = monitor.scale_factor();
4472 let desired_size = window_builder
4473 .inner
4474 .window
4475 .inner_size
4476 .unwrap_or_else(|| PhysicalSize::new(800, 600).into());
4477 let mut inner_size = window_builder
4478 .inner
4479 .window
4480 .inner_size_constraints
4481 .clamp(desired_size, scale_factor)
4482 .to_physical::<u32>(scale_factor);
4483 let mut window_size = inner_size;
4484 #[allow(unused_mut)]
4485 let mut shadow_width = 0;
4488 #[cfg(windows)]
4489 if window_builder.inner.window.decorations {
4490 use windows::Win32::UI::WindowsAndMessaging::{AdjustWindowRect, WS_OVERLAPPEDWINDOW};
4491 let mut rect = windows::Win32::Foundation::RECT::default();
4492 let result = unsafe { AdjustWindowRect(&mut rect, WS_OVERLAPPEDWINDOW, false) };
4493 if result.is_ok() {
4494 shadow_width = (rect.right - rect.left) as u32;
4495 window_size.height += -rect.top as u32;
4497 }
4498 }
4499
4500 if let Some(margin) = window_builder.prevent_overflow {
4501 let work_area = monitor.work_area();
4502 let margin = margin.to_physical::<u32>(scale_factor);
4503 let constraint = PhysicalSize::new(
4504 work_area.size.width - margin.width,
4505 work_area.size.height - margin.height,
4506 );
4507 if window_size.width > constraint.width || window_size.height > constraint.height {
4508 if window_size.width > constraint.width {
4509 inner_size.width = inner_size
4510 .width
4511 .saturating_sub(window_size.width - constraint.width);
4512 window_size.width = constraint.width;
4513 }
4514 if window_size.height > constraint.height {
4515 inner_size.height = inner_size
4516 .height
4517 .saturating_sub(window_size.height - constraint.height);
4518 window_size.height = constraint.height;
4519 }
4520 window_builder.inner.window.inner_size = Some(inner_size.into());
4521 }
4522 }
4523
4524 if window_builder.center {
4525 window_size.width += shadow_width;
4526 let position = window::calculate_window_center_position(window_size, monitor);
4527 let logical_position = position.to_logical::<f64>(scale_factor);
4528 window_builder = window_builder.position(logical_position.x, logical_position.y);
4529 }
4530 }
4531 };
4532
4533 #[cfg(any(target_os = "macos", target_os = "linux"))]
4534 let (initial_position, is_fullscreen) = (
4535 window_builder.inner.window.position,
4536 window_builder.inner.window.fullscreen.is_some(),
4537 );
4538
4539 #[cfg(any(target_os = "macos", target_os = "linux"))]
4542 if let (true, Some(position)) = (is_fullscreen, initial_position) {
4543 if let Some(target_monitor) =
4544 find_monitor_for_position(event_loop.available_monitors(), position)
4545 {
4546 window_builder.inner.window.fullscreen = Some(Fullscreen::Borderless(Some(target_monitor)));
4547 }
4548 }
4549
4550 let window = window_builder
4551 .inner
4552 .build(event_loop)
4553 .inspect_err(|e| log::error!("Error creating window: {e:?}"))
4554 .map_err(|_| Error::CreateWindow)?;
4555
4556 #[cfg(target_os = "macos")]
4559 if !is_fullscreen {
4560 if let Some(position) = initial_position {
4561 window.set_outer_position(position);
4562 }
4563 }
4564
4565 #[cfg(feature = "tracing")]
4566 {
4567 drop(window_create_span);
4568
4569 context
4570 .main_thread
4571 .active_tracing_spans
4572 .0
4573 .borrow_mut()
4574 .push(ActiveTracingSpan::WindowDraw {
4575 id: window.id(),
4576 span: window_draw_span,
4577 });
4578 }
4579
4580 context.window_id_map.insert(window.id(), window_id);
4581
4582 if let Some(handler) = after_window_creation {
4583 let raw = RawWindow {
4584 #[cfg(windows)]
4585 hwnd: window.hwnd(),
4586 #[cfg(any(
4587 target_os = "linux",
4588 target_os = "dragonfly",
4589 target_os = "freebsd",
4590 target_os = "netbsd",
4591 target_os = "openbsd"
4592 ))]
4593 gtk_window: window.gtk_window(),
4594 #[cfg(any(
4595 target_os = "linux",
4596 target_os = "dragonfly",
4597 target_os = "freebsd",
4598 target_os = "netbsd",
4599 target_os = "openbsd"
4600 ))]
4601 default_vbox: window.default_vbox(),
4602 _marker: &std::marker::PhantomData,
4603 };
4604 handler(raw);
4605 }
4606
4607 let mut webviews = Vec::new();
4608
4609 #[cfg(windows)]
4610 let focused_webview = Arc::new(Mutex::new(FocusState::default()));
4611
4612 #[cfg(feature = "unstable")]
4613 let has_children = webview.is_some();
4614 #[cfg(not(feature = "unstable"))]
4615 let has_children = false;
4616
4617 if let Some(webview) = webview {
4618 webviews.push(create_webview(
4619 #[cfg(feature = "unstable")]
4620 WebviewKind::WindowChild,
4621 #[cfg(not(feature = "unstable"))]
4622 WebviewKind::WindowContent,
4623 &window,
4624 Arc::new(Mutex::new(window_id)),
4625 webview_id,
4626 context,
4627 webview,
4628 #[cfg(windows)]
4629 focused_webview.clone(),
4630 )?);
4631 }
4632
4633 let window = Arc::new(window);
4634
4635 #[cfg(windows)]
4636 let surface = if is_window_transparent {
4637 if let Ok(context) = softbuffer::Context::new(window.clone()) {
4638 if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
4639 window.draw_surface(&mut surface, background_color);
4640 Some(surface)
4641 } else {
4642 None
4643 }
4644 } else {
4645 None
4646 }
4647 } else {
4648 None
4649 };
4650
4651 Ok(WindowWrapper {
4652 label,
4653 has_children: AtomicBool::new(has_children),
4654 inner: Some(window),
4655 webviews,
4656 window_event_listeners,
4657 #[cfg(windows)]
4658 background_color,
4659 #[cfg(windows)]
4660 is_window_transparent,
4661 #[cfg(windows)]
4662 surface,
4663 #[cfg(windows)]
4664 focused_webview,
4665 })
4666}
4667
4668#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
4670enum WebviewKind {
4671 WindowContent,
4673 WindowChild,
4675}
4676
4677#[derive(Debug, Clone)]
4678struct WebviewBounds {
4679 x_rate: f32,
4680 y_rate: f32,
4681 width_rate: f32,
4682 height_rate: f32,
4683}
4684
4685fn create_webview<T: UserEvent>(
4686 kind: WebviewKind,
4687 window: &Window,
4688 window_id: Arc<Mutex<WindowId>>,
4689 id: WebviewId,
4690 context: &Context<T>,
4691 pending: PendingWebview<T, Wry<T>>,
4692 #[cfg(windows)] focused_webview: Arc<Mutex<FocusState>>,
4693) -> Result<WebviewWrapper> {
4694 if !context.webview_runtime_installed {
4695 #[cfg(all(not(debug_assertions), windows))]
4696 dialog::error(
4697 r#"Could not find the WebView2 Runtime.
4698
4699Make sure it is installed or download it from <A href="https://developer.microsoft.com/en-us/microsoft-edge/webview2">https://developer.microsoft.com/en-us/microsoft-edge/webview2</A>
4700
4701You may have it installed on another user account, but it is not available for this one.
4702"#,
4703 );
4704
4705 if cfg!(target_os = "macos") {
4706 log::warn!("WebKit webview runtime not found, attempting to create webview anyway.");
4707 } else {
4708 return Err(Error::WebviewRuntimeNotInstalled);
4709 }
4710 }
4711
4712 #[allow(unused_mut)]
4713 let PendingWebview {
4714 webview_attributes,
4715 uri_scheme_protocols,
4716 label,
4717 ipc_handler,
4718 url,
4719 ..
4720 } = pending;
4721
4722 let mut web_context = context
4723 .main_thread
4724 .web_context
4725 .lock()
4726 .expect("poisoned WebContext store");
4727 let is_first_context = web_context.is_empty();
4728 let automation_enabled = std::env::var("TAURI_WEBVIEW_AUTOMATION").as_deref() == Ok("true");
4730 let web_context_key = webview_attributes.data_directory;
4731 let entry = web_context.entry(web_context_key.clone());
4732 let web_context = match entry {
4733 Occupied(occupied) => {
4734 let occupied = occupied.into_mut();
4735 occupied.referenced_by_webviews.insert(label.clone());
4736 occupied
4737 }
4738 Vacant(vacant) => {
4739 let mut web_context = WryWebContext::new(web_context_key.clone());
4740 web_context.set_allows_automation(if automation_enabled {
4741 is_first_context
4742 } else {
4743 false
4744 });
4745 vacant.insert(WebContext {
4746 inner: web_context,
4747 referenced_by_webviews: [label.clone()].into(),
4748 registered_custom_protocols: HashSet::new(),
4749 })
4750 }
4751 };
4752
4753 let mut webview_builder = WebViewBuilder::new_with_web_context(&mut web_context.inner)
4754 .with_id(&label)
4755 .with_focused(webview_attributes.focus)
4756 .with_transparent(webview_attributes.transparent)
4757 .with_accept_first_mouse(webview_attributes.accept_first_mouse)
4758 .with_incognito(webview_attributes.incognito)
4759 .with_clipboard(webview_attributes.clipboard)
4760 .with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled)
4761 .with_general_autofill_enabled(webview_attributes.general_autofill_enabled);
4762
4763 if url != "about:blank" {
4764 webview_builder = webview_builder.with_url(&url);
4765 }
4766
4767 #[cfg(target_os = "macos")]
4768 if let Some(webview_configuration) = webview_attributes.webview_configuration {
4769 webview_builder = webview_builder.with_webview_configuration(webview_configuration);
4770 }
4771
4772 #[cfg(any(target_os = "windows", target_os = "android"))]
4773 {
4774 webview_builder = webview_builder.with_https_scheme(webview_attributes.use_https_scheme);
4775 }
4776
4777 if let Some(background_throttling) = webview_attributes.background_throttling {
4778 webview_builder = webview_builder.with_background_throttling(match background_throttling {
4779 tauri_utils::config::BackgroundThrottlingPolicy::Disabled => {
4780 wry::BackgroundThrottlingPolicy::Disabled
4781 }
4782 tauri_utils::config::BackgroundThrottlingPolicy::Suspend => {
4783 wry::BackgroundThrottlingPolicy::Suspend
4784 }
4785 tauri_utils::config::BackgroundThrottlingPolicy::Throttle => {
4786 wry::BackgroundThrottlingPolicy::Throttle
4787 }
4788 });
4789 }
4790
4791 if webview_attributes.javascript_disabled {
4792 webview_builder = webview_builder.with_javascript_disabled();
4793 }
4794
4795 if let Some(color) = webview_attributes.background_color {
4796 webview_builder = webview_builder.with_background_color(color.into());
4797 }
4798
4799 if webview_attributes.drag_drop_handler_enabled {
4800 let proxy = context.proxy.clone();
4801 let window_id_ = window_id.clone();
4802 webview_builder = webview_builder.with_drag_drop_handler(move |event| {
4803 let event = match event {
4804 WryDragDropEvent::Enter {
4805 paths,
4806 position: (x, y),
4807 } => DragDropEvent::Enter {
4808 paths,
4809 position: PhysicalPosition::new(x as _, y as _),
4810 },
4811 WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::Over {
4812 position: PhysicalPosition::new(x as _, y as _),
4813 },
4814 WryDragDropEvent::Drop {
4815 paths,
4816 position: (x, y),
4817 } => DragDropEvent::Drop {
4818 paths,
4819 position: PhysicalPosition::new(x as _, y as _),
4820 },
4821 WryDragDropEvent::Leave => DragDropEvent::Leave,
4822 _ => unimplemented!(),
4823 };
4824
4825 let message = if kind == WebviewKind::WindowContent {
4826 WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::DragDrop(event))
4827 } else {
4828 WebviewMessage::WebviewEvent(WebviewEvent::DragDrop(event))
4829 };
4830
4831 let _ = proxy.send_event(Message::Webview(*window_id_.lock().unwrap(), id, message));
4832 true
4833 });
4834 }
4835
4836 if let Some(navigation_handler) = pending.navigation_handler {
4837 webview_builder = webview_builder.with_navigation_handler(move |url| {
4838 url
4839 .parse()
4840 .map(|url| navigation_handler(&url))
4841 .unwrap_or(true)
4842 });
4843 }
4844
4845 if let Some(new_window_handler) = pending.new_window_handler {
4846 #[cfg(desktop)]
4847 let context = context.clone();
4848 webview_builder = webview_builder.with_new_window_req_handler(move |url, features| {
4849 let Ok(url) = url.parse() else {
4850 return wry::NewWindowResponse::Deny;
4851 };
4852 let response = new_window_handler(
4853 url,
4854 tauri_runtime::webview::NewWindowFeatures::new(
4855 features.size,
4856 features.position,
4857 tauri_runtime::webview::NewWindowOpener {
4858 #[cfg(desktop)]
4859 webview: features.opener.webview,
4860 #[cfg(windows)]
4861 environment: features.opener.environment,
4862 #[cfg(target_os = "macos")]
4863 target_configuration: features.opener.target_configuration,
4864 },
4865 ),
4866 );
4867 match response {
4868 tauri_runtime::webview::NewWindowResponse::Allow => wry::NewWindowResponse::Allow,
4869 #[cfg(desktop)]
4870 tauri_runtime::webview::NewWindowResponse::Create { window_id } => {
4871 let windows = &context.main_thread.windows.0;
4872 let webview = windows
4873 .borrow()
4874 .get(&window_id)
4875 .unwrap()
4876 .webviews
4877 .first()
4878 .unwrap()
4879 .clone();
4880
4881 #[cfg(desktop)]
4882 wry::NewWindowResponse::Create {
4883 #[cfg(target_os = "macos")]
4884 webview: wry::WebViewExtMacOS::webview(&*webview).as_super().into(),
4885 #[cfg(any(
4886 target_os = "linux",
4887 target_os = "dragonfly",
4888 target_os = "freebsd",
4889 target_os = "netbsd",
4890 target_os = "openbsd",
4891 ))]
4892 webview: webview.webview(),
4893 #[cfg(windows)]
4894 webview: webview.webview(),
4895 }
4896 }
4897 tauri_runtime::webview::NewWindowResponse::Deny => wry::NewWindowResponse::Deny,
4898 }
4899 });
4900 }
4901
4902 if let Some(document_title_changed_handler) = pending.document_title_changed_handler {
4903 webview_builder =
4904 webview_builder.with_document_title_changed_handler(document_title_changed_handler)
4905 }
4906
4907 if let Some(permission_request_handler) = pending.permission_request_handler {
4908 webview_builder = webview_builder.with_permission_handler(move |kind| {
4909 let kind = webview_permissions::from_wry_permission_kind(kind);
4910 let response = permission_request_handler(kind);
4911 webview_permissions::to_wry_permission_response(response)
4912 });
4913 }
4914
4915 let webview_bounds = if let Some(bounds) = webview_attributes.bounds {
4916 let bounds: RectWrapper = bounds.into();
4917 let bounds = bounds.0;
4918
4919 let scale_factor = window.scale_factor();
4920 let position = bounds.position.to_logical::<f32>(scale_factor);
4921 let size = bounds.size.to_logical::<f32>(scale_factor);
4922
4923 webview_builder = webview_builder.with_bounds(bounds);
4924
4925 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
4926
4927 if webview_attributes.auto_resize {
4928 Some(WebviewBounds {
4929 x_rate: position.x / window_size.width,
4930 y_rate: position.y / window_size.height,
4931 width_rate: size.width / window_size.width,
4932 height_rate: size.height / window_size.height,
4933 })
4934 } else {
4935 None
4936 }
4937 } else {
4938 #[cfg(feature = "unstable")]
4939 {
4940 webview_builder = webview_builder.with_bounds(wry::Rect {
4941 position: LogicalPosition::new(0, 0).into(),
4942 size: window.inner_size().into(),
4943 });
4944 Some(WebviewBounds {
4945 x_rate: 0.,
4946 y_rate: 0.,
4947 width_rate: 1.,
4948 height_rate: 1.,
4949 })
4950 }
4951 #[cfg(not(feature = "unstable"))]
4952 None
4953 };
4954
4955 if let Some(download_handler) = pending.download_handler {
4956 let download_handler_ = download_handler.clone();
4957 webview_builder = webview_builder.with_download_started_handler(move |url, path| {
4958 if let Ok(url) = url.parse() {
4959 download_handler_(DownloadEvent::Requested {
4960 url,
4961 destination: path,
4962 })
4963 } else {
4964 false
4965 }
4966 });
4967 webview_builder = webview_builder.with_download_completed_handler(move |url, path, success| {
4968 if let Ok(url) = url.parse() {
4969 download_handler(DownloadEvent::Finished { url, path, success });
4970 }
4971 });
4972 }
4973
4974 if let Some(page_load_handler) = pending.on_page_load_handler {
4975 webview_builder = webview_builder.with_on_page_load_handler(move |event, url| {
4976 if let Ok(url) = url.parse() {
4977 page_load_handler(
4978 url,
4979 match event {
4980 wry::PageLoadEvent::Started => tauri_runtime::webview::PageLoadEvent::Started,
4981 wry::PageLoadEvent::Finished => tauri_runtime::webview::PageLoadEvent::Finished,
4982 },
4983 )
4984 };
4985 });
4986 }
4987
4988 if let Some(user_agent) = webview_attributes.user_agent {
4989 webview_builder = webview_builder.with_user_agent(&user_agent);
4990 }
4991
4992 if let Some(proxy_url) = webview_attributes.proxy_url {
4993 let config = parse_proxy_url(&proxy_url)?;
4994
4995 webview_builder = webview_builder.with_proxy_config(config);
4996 }
4997
4998 #[cfg(windows)]
4999 {
5000 if let Some(additional_browser_args) = webview_attributes.additional_browser_args {
5001 webview_builder = webview_builder.with_additional_browser_args(&additional_browser_args);
5002 }
5003
5004 if let Some(environment) = webview_attributes.environment {
5005 webview_builder = webview_builder.with_environment(environment);
5006 }
5007
5008 webview_builder = webview_builder.with_theme(match window.theme() {
5009 TaoTheme::Dark => wry::Theme::Dark,
5010 TaoTheme::Light => wry::Theme::Light,
5011 _ => wry::Theme::Light,
5012 });
5013
5014 webview_builder =
5015 webview_builder.with_scroll_bar_style(match webview_attributes.scroll_bar_style {
5016 ScrollBarStyle::Default => WryScrollBarStyle::Default,
5017 ScrollBarStyle::FluentOverlay => WryScrollBarStyle::FluentOverlay,
5018 _ => unreachable!(),
5019 });
5020 }
5021
5022 #[cfg(windows)]
5023 {
5024 webview_builder = webview_builder
5025 .with_browser_extensions_enabled(webview_attributes.browser_extensions_enabled);
5026 }
5027
5028 #[cfg(any(
5029 windows,
5030 target_os = "linux",
5031 target_os = "dragonfly",
5032 target_os = "freebsd",
5033 target_os = "netbsd",
5034 target_os = "openbsd"
5035 ))]
5036 {
5037 if let Some(path) = &webview_attributes.extensions_path {
5038 webview_builder = webview_builder.with_extensions_path(path);
5039 }
5040 }
5041
5042 #[cfg(any(
5043 target_os = "linux",
5044 target_os = "dragonfly",
5045 target_os = "freebsd",
5046 target_os = "netbsd",
5047 target_os = "openbsd"
5048 ))]
5049 {
5050 if let Some(related_view) = webview_attributes.related_view {
5051 webview_builder = webview_builder.with_related_view(related_view);
5052 }
5053 }
5054
5055 #[cfg(any(target_os = "macos", target_os = "ios"))]
5056 {
5057 if let Some(data_store_identifier) = &webview_attributes.data_store_identifier {
5058 webview_builder = webview_builder.with_data_store_identifier(*data_store_identifier);
5059 }
5060
5061 webview_builder =
5062 webview_builder.with_allow_link_preview(webview_attributes.allow_link_preview);
5063
5064 if let Some(on_web_content_process_terminate_handler) =
5065 pending.on_web_content_process_terminate_handler
5066 {
5067 webview_builder = webview_builder
5068 .with_on_web_content_process_terminate_handler(on_web_content_process_terminate_handler);
5069 } else {
5070 log::debug!("web content process terminated");
5071 let context_ = context.clone();
5072 let window_id_ = window_id.clone();
5073 webview_builder = webview_builder.with_on_web_content_process_terminate_handler(move || {
5074 if let Ok(windows) = &context_.main_thread.windows.0.try_borrow() {
5075 if let Some(window) = windows.get(&*window_id_.lock().unwrap()) {
5076 if let Some(webview) = window.webviews.iter().find(|w| w.id == id) {
5077 match webview.reload() {
5078 Ok(_) => log::debug!("webview reloaded"),
5079 Err(e) => log::error!("failed to reload webview: {e}"),
5080 }
5081 } else {
5082 log::error!("failed to find webview")
5083 }
5084 } else {
5085 log::error!("failed to get window")
5086 }
5087 } else {
5088 log::error!("failed to borrow windows")
5089 }
5090 });
5091 }
5092 }
5093
5094 #[cfg(target_os = "ios")]
5095 {
5096 webview_builder = webview_builder.with_limit_navigations_to_app_bound_domains(
5097 webview_attributes.limit_navigations_to_app_bound_domains,
5098 );
5099
5100 if let Some(input_accessory_view_builder) = webview_attributes.input_accessory_view_builder {
5101 webview_builder = webview_builder
5102 .with_input_accessory_view_builder(move |webview| input_accessory_view_builder.0(webview));
5103 }
5104 }
5105
5106 #[cfg(target_os = "macos")]
5107 {
5108 if let Some(position) = &webview_attributes.traffic_light_position {
5109 webview_builder = webview_builder.with_traffic_light_inset(*position);
5110 }
5111 }
5112
5113 #[cfg(windows)]
5114 let window_id_for_ipc = window_id.clone();
5115 #[cfg(not(windows))]
5116 let window_id_for_ipc = window_id;
5117 webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
5118 window_id_for_ipc,
5119 id,
5120 context.clone(),
5121 label.clone(),
5122 ipc_handler,
5123 ));
5124
5125 for script in webview_attributes.initialization_scripts {
5126 webview_builder = webview_builder
5127 .with_initialization_script_for_main_only(script.script, script.for_main_frame_only);
5128 }
5129
5130 for (scheme, protocol) in uri_scheme_protocols {
5131 #[cfg(any(
5134 target_os = "linux",
5135 target_os = "dragonfly",
5136 target_os = "freebsd",
5137 target_os = "netbsd",
5138 target_os = "openbsd"
5139 ))]
5140 {
5141 if web_context.registered_custom_protocols.contains(&scheme) {
5142 continue;
5143 }
5144
5145 web_context
5146 .registered_custom_protocols
5147 .insert(scheme.clone());
5148 }
5149
5150 webview_builder = webview_builder.with_asynchronous_custom_protocol(
5151 scheme,
5152 move |webview_id, request, responder| {
5153 protocol(
5154 webview_id,
5155 request,
5156 Box::new(move |response| responder.respond(response)),
5157 )
5158 },
5159 );
5160 }
5161
5162 #[cfg(any(debug_assertions, feature = "devtools"))]
5163 {
5164 webview_builder = webview_builder.with_devtools(webview_attributes.devtools.unwrap_or(true));
5165 }
5166
5167 #[cfg(target_os = "android")]
5168 {
5169 if let Some(on_webview_created) = pending.on_webview_created {
5170 webview_builder = webview_builder.on_webview_created(move |ctx| {
5171 on_webview_created(tauri_runtime::webview::CreationContext {
5172 env: ctx.env,
5173 activity: ctx.activity,
5174 webview: ctx.webview,
5175 })
5176 });
5177 }
5178 }
5179
5180 let webview = match kind {
5181 #[cfg(not(any(
5182 target_os = "windows",
5183 target_os = "macos",
5184 target_os = "ios",
5185 target_os = "android"
5186 )))]
5187 WebviewKind::WindowChild => {
5188 let vbox = window.default_vbox().unwrap();
5190 webview_builder.build_gtk(vbox)
5191 }
5192 #[cfg(any(
5193 target_os = "windows",
5194 target_os = "macos",
5195 target_os = "ios",
5196 target_os = "android"
5197 ))]
5198 WebviewKind::WindowChild => webview_builder.build_as_child(&window),
5199 WebviewKind::WindowContent => {
5200 #[cfg(any(
5201 target_os = "windows",
5202 target_os = "macos",
5203 target_os = "ios",
5204 target_os = "android"
5205 ))]
5206 let builder = webview_builder.build(&window);
5207 #[cfg(not(any(
5208 target_os = "windows",
5209 target_os = "macos",
5210 target_os = "ios",
5211 target_os = "android"
5212 )))]
5213 let builder = {
5214 let vbox = window.default_vbox().unwrap();
5215 webview_builder.build_gtk(vbox)
5216 };
5217 builder
5218 }
5219 }
5220 .map_err(|e| Error::CreateWebview(Box::new(e)))?;
5221
5222 if kind == WebviewKind::WindowContent {
5223 #[cfg(any(
5224 target_os = "linux",
5225 target_os = "dragonfly",
5226 target_os = "freebsd",
5227 target_os = "netbsd",
5228 target_os = "openbsd"
5229 ))]
5230 undecorated_resizing::attach_resize_handler(&webview);
5231 #[cfg(windows)]
5232 if window.is_resizable() && !window.is_decorated() {
5233 undecorated_resizing::attach_resize_handler(window.hwnd(), window.has_undecorated_shadow());
5234 }
5235 }
5236
5237 #[cfg(windows)]
5238 {
5239 let controller = webview.controller();
5240 let mut token = 0;
5241
5242 add_focus_change_listeners(
5243 window_id.clone(),
5244 id,
5245 context.proxy.clone(),
5246 focused_webview,
5247 label.clone(),
5248 &controller,
5249 &mut token,
5250 );
5251
5252 if let Ok(webview) = unsafe { controller.CoreWebView2() } {
5253 let proxy_clone = context.proxy.clone();
5254 unsafe {
5255 let _ = webview.add_ContainsFullScreenElementChanged(
5256 &ContainsFullScreenElementChangedEventHandler::create(Box::new(move |sender, _| {
5257 let mut contains_fullscreen_element = windows::core::BOOL::default();
5258 sender
5259 .ok_or_else(windows::core::Error::empty)?
5260 .ContainsFullScreenElement(&mut contains_fullscreen_element)?;
5261 let _ = proxy_clone.send_event(Message::Window(
5262 *window_id.lock().unwrap(),
5263 WindowMessage::SetFullscreen(contains_fullscreen_element.as_bool()),
5264 ));
5265 Ok(())
5266 })),
5267 &mut token,
5268 );
5269 }
5270 }
5271 }
5272
5273 Ok(WebviewWrapper {
5274 label,
5275 id,
5276 inner: Rc::new(webview),
5277 context_store: context.main_thread.web_context.clone(),
5278 webview_event_listeners: Default::default(),
5279 context_key: if automation_enabled {
5280 None
5281 } else {
5282 web_context_key
5283 },
5284 bounds: Arc::new(Mutex::new(webview_bounds)),
5285 })
5286}
5287
5288fn create_ipc_handler<T: UserEvent>(
5290 window_id: Arc<Mutex<WindowId>>,
5291 webview_id: WebviewId,
5292 context: Context<T>,
5293 label: String,
5294 ipc_handler: Option<WebviewIpcHandler<T, Wry<T>>>,
5295) -> Box<IpcHandler> {
5296 Box::new(move |request| {
5297 if let Some(handler) = &ipc_handler {
5298 handler(
5299 DetachedWebview {
5300 label: label.clone(),
5301 dispatcher: WryWebviewDispatcher {
5302 window_id: window_id.clone(),
5303 webview_id,
5304 context: context.clone(),
5305 },
5306 },
5307 request,
5308 );
5309 }
5310 })
5311}
5312
5313#[cfg(target_os = "macos")]
5314fn inner_size(
5315 window: &Window,
5316 webviews: &[WebviewWrapper],
5317 has_children: bool,
5318) -> PhysicalSize<u32> {
5319 if !has_children && !webviews.is_empty() {
5320 use wry::WebViewExtMacOS;
5321 let webview = webviews.first().unwrap();
5322 let view = unsafe { Retained::cast_unchecked::<objc2_app_kit::NSView>(webview.webview()) };
5323 let view_frame = view.frame();
5324 let logical: LogicalSize<f64> = (view_frame.size.width, view_frame.size.height).into();
5325 return logical.to_physical(window.scale_factor());
5326 }
5327
5328 window.inner_size()
5329}
5330
5331#[cfg(not(target_os = "macos"))]
5332#[allow(unused_variables)]
5333fn inner_size(
5334 window: &Window,
5335 webviews: &[WebviewWrapper],
5336 has_children: bool,
5337) -> PhysicalSize<u32> {
5338 window.inner_size()
5339}
5340
5341fn to_tao_theme(theme: Option<Theme>) -> Option<TaoTheme> {
5342 match theme {
5343 Some(Theme::Light) => Some(TaoTheme::Light),
5344 Some(Theme::Dark) => Some(TaoTheme::Dark),
5345 _ => None,
5346 }
5347}
5348
5349#[cfg(windows)]
5352fn add_focus_change_listeners<T: UserEvent>(
5353 window_id: Arc<Mutex<WindowId>>,
5354 id: u32,
5355 proxy: TaoEventLoopProxy<Message<T>>,
5356 focused_webview: Arc<Mutex<FocusState>>,
5357 label: String,
5358 controller: &ICoreWebView2Controller,
5359 token: &mut i64,
5360) {
5361 let label_ = label.clone();
5362 let window_id_ = window_id.clone();
5363 let proxy_clone = proxy.clone();
5364 let focused_webview_ = focused_webview.clone();
5365 if let Err(error) = unsafe {
5366 controller.add_GotFocus(
5367 &FocusChangedEventHandler::create(Box::new(move |_, _| {
5368 let mut focused_webview = focused_webview_.lock().unwrap();
5369 let already_focused = matches!(
5372 *focused_webview,
5373 FocusState::WindowFocused | FocusState::WebviewFocused { .. }
5374 );
5375 *focused_webview = FocusState::WebviewFocused {
5376 webview_label: label_.clone(),
5377 };
5378
5379 if !already_focused {
5380 let _ = proxy_clone.send_event(Message::Webview(
5381 *window_id_.lock().unwrap(),
5382 id,
5383 WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)),
5384 ));
5385 }
5386 Ok(())
5387 })),
5388 token,
5389 )
5390 } {
5391 log::error!(
5392 "Failed to attach WebView2 `add_GotFocus` handler, `WindowEvent::Focused` will not be sent: {error}"
5393 );
5394 return;
5395 }
5396
5397 if let Err(error) = unsafe {
5398 controller.add_LostFocus(
5399 &FocusChangedEventHandler::create(Box::new(move |_, _| {
5400 let mut focused_webview = focused_webview.lock().unwrap();
5401 if let FocusState::WebviewFocused { ref webview_label } = *focused_webview {
5409 let lost_window_focus = webview_label == &label;
5410 if lost_window_focus {
5411 *focused_webview = FocusState::Blured {
5413 last_focused_webview_label: Some(label.clone()),
5414 };
5415 let _ = proxy.send_event(Message::Webview(
5416 *window_id.lock().unwrap(),
5417 id,
5418 WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)),
5419 ));
5420 }
5421 }
5422
5423 Ok(())
5424 })),
5425 token,
5426 )
5427 } {
5428 log::error!(
5429 "Failed to attach WebView2 `add_LostFocus` handler, `WindowEvent::Focused` will not be sent: {error}"
5430 );
5431 }
5432}