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