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