tauri_runtime_wry/
lib.rs

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