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