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