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