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