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