1use std::{
6 collections::HashMap,
7 sync::{
8 Arc, Mutex,
9 mpsc::{self, Receiver, Sender},
10 },
11 time::{Duration, Instant},
12};
13
14use cef::ImplBrowserHost;
15use raw_window_handle::HasWindowHandle;
16use tauri_runtime::{
17 Error, Icon, ProgressBarState, Result, UserAttentionType, UserEvent, WindowDispatch,
18 WindowEventId,
19 dpi::{PhysicalPosition, PhysicalSize, Position, Size},
20 monitor::Monitor,
21 webview::{DetachedWebview, PendingWebview},
22 window::{
23 CursorIcon, DetachedWindow, DetachedWindowWebview, PendingWindow, RawWindow, WindowEvent,
24 WindowId, WindowSizeConstraints,
25 },
26};
27use tauri_utils::{Theme, config::Color};
28use winit::{
29 event_loop::ActiveEventLoop,
30 monitor::{Fullscreen, MonitorHandle},
31 window::{Window as WinitWindow, WindowAttributes, WindowLevel},
32};
33
34use crate::platform::{EventLoopExt, MonitorExt};
35#[cfg(any(
36 windows,
37 target_os = "macos",
38 target_os = "linux",
39 target_os = "dragonfly",
40 target_os = "freebsd",
41 target_os = "netbsd",
42 target_os = "openbsd"
43))]
44use std::marker::PhantomData;
45#[cfg(target_os = "macos")]
46use winit::platform::macos::WindowExtMacOS;
47#[cfg(windows)]
48use winit::platform::windows::WindowExtWindows;
49
50#[cfg(windows)]
51use crate::window_handle::SoftbufferWindowHandle;
52use crate::{
53 cef_impl::{client as browser_client, request_context},
54 runtime::{
55 AfterWindowCreationCallback, CefRuntime, Message, RuntimeContext, WinitCefApp,
56 WinitDragDropState,
57 },
58 webview::{AppWebview, CefWebviewDispatcher, create_webview_detached},
59 window_builder::WindowBuilderWrapper,
60 window_handle::SendRawWindowHandle,
61};
62
63type WindowEventListener = Box<dyn Fn(&WindowEvent) + Send>;
64type WindowEventListeners = Arc<Mutex<HashMap<WindowEventId, WindowEventListener>>>;
65
66#[cfg(any(
67 target_os = "linux",
68 target_os = "dragonfly",
69 target_os = "freebsd",
70 target_os = "netbsd",
71 target_os = "openbsd"
72))]
73pub(crate) struct SendGtkWindow(*mut std::ffi::c_void);
74
75#[cfg(any(
76 target_os = "linux",
77 target_os = "dragonfly",
78 target_os = "freebsd",
79 target_os = "netbsd",
80 target_os = "openbsd"
81))]
82unsafe impl Send for SendGtkWindow {}
83
84#[cfg(any(
85 target_os = "linux",
86 target_os = "dragonfly",
87 target_os = "freebsd",
88 target_os = "netbsd",
89 target_os = "openbsd"
90))]
91pub(crate) struct SendGtkBox(*mut std::ffi::c_void);
92
93#[cfg(any(
94 target_os = "linux",
95 target_os = "dragonfly",
96 target_os = "freebsd",
97 target_os = "netbsd",
98 target_os = "openbsd"
99))]
100unsafe impl Send for SendGtkBox {}
101
102pub(crate) fn tauri_theme_to_winit_theme(theme: Option<Theme>) -> Option<winit::window::Theme> {
103 theme.map(|theme| match theme {
104 Theme::Light => winit::window::Theme::Light,
105 Theme::Dark => winit::window::Theme::Dark,
106 _ => winit::window::Theme::Light,
107 })
108}
109
110pub(crate) fn winit_theme_to_tauri_theme(theme: winit::window::Theme) -> Theme {
111 match theme {
112 winit::window::Theme::Light => Theme::Light,
113 winit::window::Theme::Dark => Theme::Dark,
114 }
115}
116
117fn tauri_resize_direction_to_winit(
118 direction: tauri_runtime::ResizeDirection,
119) -> winit::window::ResizeDirection {
120 match direction {
121 tauri_runtime::ResizeDirection::East => winit::window::ResizeDirection::East,
122 tauri_runtime::ResizeDirection::North => winit::window::ResizeDirection::North,
123 tauri_runtime::ResizeDirection::NorthEast => winit::window::ResizeDirection::NorthEast,
124 tauri_runtime::ResizeDirection::NorthWest => winit::window::ResizeDirection::NorthWest,
125 tauri_runtime::ResizeDirection::South => winit::window::ResizeDirection::South,
126 tauri_runtime::ResizeDirection::SouthEast => winit::window::ResizeDirection::SouthEast,
127 tauri_runtime::ResizeDirection::SouthWest => winit::window::ResizeDirection::SouthWest,
128 tauri_runtime::ResizeDirection::West => winit::window::ResizeDirection::West,
129 }
130}
131
132fn calculate_window_center_position(
133 window_size: PhysicalSize<u32>,
134 monitor: &MonitorHandle,
135) -> PhysicalPosition<i32> {
136 let work_area = monitor.work_area();
137 PhysicalPosition::new(
138 work_area.position.x + ((work_area.size.width as i32 - window_size.width as i32).max(0) / 2),
139 work_area.position.y + ((work_area.size.height as i32 - window_size.height as i32).max(0) / 2),
140 )
141}
142
143fn find_monitor_for_position(
144 monitors: impl Iterator<Item = MonitorHandle>,
145 position: Position,
146) -> Option<MonitorHandle> {
147 monitors.into_iter().find(|monitor| {
148 let Some(monitor_position) = monitor.position() else {
149 return false;
150 };
151 let Some(video_mode) = monitor.current_video_mode() else {
152 return false;
153 };
154
155 let monitor_size = video_mode.size();
156 let position = position.to_physical::<i32>(monitor.scale_factor());
157
158 monitor_position.x <= position.x
159 && position.x < monitor_position.x + monitor_size.width as i32
160 && monitor_position.y <= position.y
161 && position.y < monitor_position.y + monitor_size.height as i32
162 })
163}
164
165fn clamp_surface_size(attrs: &WindowAttributes, scale_factor: f64) -> PhysicalSize<u32> {
166 let mut size = attrs
167 .surface_size
168 .unwrap_or_else(|| PhysicalSize::new(800, 600).into())
169 .to_physical::<u32>(scale_factor);
170
171 if let Some(min_size) = attrs.min_surface_size {
172 let min_size = min_size.to_physical::<u32>(scale_factor);
173 size.width = size.width.max(min_size.width);
174 size.height = size.height.max(min_size.height);
175 }
176
177 if let Some(max_size) = attrs.max_surface_size {
178 let max_size = max_size.to_physical::<u32>(scale_factor);
179 size.width = size.width.min(max_size.width);
180 size.height = size.height.min(max_size.height);
181 }
182
183 size
184}
185
186fn apply_prevent_overflow(
187 attrs: &mut WindowAttributes,
188 window_size: &mut PhysicalSize<u32>,
189 monitor: &MonitorHandle,
190 margin: Size,
191) {
192 let work_area = monitor.work_area();
193 let margin = margin.to_physical::<u32>(monitor.scale_factor());
194 let constraint = PhysicalSize::new(
195 work_area.size.width.saturating_sub(margin.width),
196 work_area.size.height.saturating_sub(margin.height),
197 );
198
199 if window_size.width > constraint.width || window_size.height > constraint.height {
200 window_size.width = window_size.width.min(constraint.width);
201 window_size.height = window_size.height.min(constraint.height);
202 attrs.surface_size = Some((*window_size).into());
203 }
204}
205
206fn prepare_window_attributes(event_loop: &dyn ActiveEventLoop, attrs: &mut AppWindowAttrs) {
207 if !attrs.center && attrs.prevent_overflow.is_none() {
208 return;
209 }
210
211 let monitor = attrs
212 .inner
213 .position
214 .and_then(|position| {
215 let monitors = event_loop.available_monitors();
216 find_monitor_for_position(monitors, position)
217 })
218 .or_else(|| event_loop.primary_monitor());
219
220 let Some(monitor) = monitor else {
221 return;
222 };
223
224 let mut window_size = clamp_surface_size(&attrs.inner, monitor.scale_factor());
228
229 #[allow(unused_mut)]
233 let mut shadow_width: u32 = 0;
234 #[cfg(windows)]
235 if attrs.inner.decorations {
236 use windows::Win32::{
237 Foundation::RECT,
238 UI::WindowsAndMessaging::{AdjustWindowRect, WS_OVERLAPPEDWINDOW},
239 };
240
241 let mut rect = RECT::default();
242 if unsafe { AdjustWindowRect(&mut rect, WS_OVERLAPPEDWINDOW, false) }.is_ok() {
243 shadow_width = (rect.right - rect.left) as u32;
244 window_size.height += (-rect.top) as u32;
247 }
248 }
249
250 if let Some(margin) = attrs.prevent_overflow {
251 apply_prevent_overflow(&mut attrs.inner, &mut window_size, &monitor, margin);
252 }
253
254 if attrs.center {
255 window_size.width += shadow_width;
256 let position = calculate_window_center_position(window_size, &monitor);
257 attrs.inner.position = Some(position.into());
258 }
259}
260
261fn paired_size_constraint(
262 width: Option<tauri_runtime::dpi::PixelUnit>,
263 height: Option<tauri_runtime::dpi::PixelUnit>,
264 unconstrained: u32,
265) -> Option<Size> {
266 match (width, height) {
267 (
268 Some(tauri_runtime::dpi::PixelUnit::Logical(width)),
269 Some(tauri_runtime::dpi::PixelUnit::Logical(height)),
270 ) => Some(Size::Logical(tauri_runtime::dpi::LogicalSize::new(
271 width.into(),
272 height.into(),
273 ))),
274 (
275 Some(tauri_runtime::dpi::PixelUnit::Physical(width)),
276 Some(tauri_runtime::dpi::PixelUnit::Physical(height)),
277 ) => Some(Size::Physical(PhysicalSize::new(
278 width.into(),
279 height.into(),
280 ))),
281 (Some(tauri_runtime::dpi::PixelUnit::Logical(width)), None) => Some(Size::Logical(
282 tauri_runtime::dpi::LogicalSize::new(width.into(), unconstrained as f64),
283 )),
284 (None, Some(tauri_runtime::dpi::PixelUnit::Logical(height))) => Some(Size::Logical(
285 tauri_runtime::dpi::LogicalSize::new(unconstrained as f64, height.into()),
286 )),
287 (Some(tauri_runtime::dpi::PixelUnit::Physical(width)), None) => Some(Size::Physical(
288 PhysicalSize::new(width.into(), unconstrained),
289 )),
290 (None, Some(tauri_runtime::dpi::PixelUnit::Physical(height))) => Some(Size::Physical(
291 PhysicalSize::new(unconstrained, height.into()),
292 )),
293 _ => None,
294 }
295}
296
297pub(crate) fn min_size_constraint(
298 width: Option<tauri_runtime::dpi::PixelUnit>,
299 height: Option<tauri_runtime::dpi::PixelUnit>,
300) -> Option<Size> {
301 paired_size_constraint(width, height, 0)
302}
303
304pub(crate) fn max_size_constraint(
305 width: Option<tauri_runtime::dpi::PixelUnit>,
306 height: Option<tauri_runtime::dpi::PixelUnit>,
307) -> Option<Size> {
308 paired_size_constraint(width, height, u32::MAX)
309}
310
311#[derive(Clone, Copy, Debug)]
315pub(crate) enum FullscreenTarget {
316 CurrentMonitor,
318 MonitorAt(PhysicalPosition<f64>),
320}
321
322pub(crate) enum WindowMessage {
323 AddEventListener(WindowEventId, WindowEventListener),
324 Close,
325 Destroy,
326 ScaleFactor(Sender<Result<f64>>),
327 InnerPosition(Sender<Result<PhysicalPosition<i32>>>),
328 OuterPosition(Sender<Result<PhysicalPosition<i32>>>),
329 InnerSize(Sender<Result<PhysicalSize<u32>>>),
330 OuterSize(Sender<Result<PhysicalSize<u32>>>),
331 IsFullscreen(Sender<Result<bool>>),
332 IsMinimized(Sender<Result<bool>>),
333 IsMaximized(Sender<Result<bool>>),
334 IsFocused(Sender<Result<bool>>),
335 IsDecorated(Sender<Result<bool>>),
336 IsResizable(Sender<Result<bool>>),
337 IsMaximizable(Sender<Result<bool>>),
338 IsMinimizable(Sender<Result<bool>>),
339 IsClosable(Sender<Result<bool>>),
340 IsVisible(Sender<Result<bool>>),
341 IsEnabled(Sender<Result<bool>>),
342 IsAlwaysOnTop(Sender<Result<bool>>),
343 Title(Sender<Result<String>>),
344 CurrentMonitor(Sender<Result<Option<Monitor>>>),
345 PrimaryMonitor(Sender<Result<Option<Monitor>>>),
346 MonitorFromPoint(Sender<Result<Option<Monitor>>>, f64, f64),
347 AvailableMonitors(Sender<Result<Vec<Monitor>>>),
348 #[cfg(any(
349 target_os = "linux",
350 target_os = "dragonfly",
351 target_os = "freebsd",
352 target_os = "netbsd",
353 target_os = "openbsd"
354 ))]
355 GtkWindow(Sender<Result<SendGtkWindow>>),
356 #[cfg(any(
357 target_os = "linux",
358 target_os = "dragonfly",
359 target_os = "freebsd",
360 target_os = "netbsd",
361 target_os = "openbsd"
362 ))]
363 DefaultVBox(Sender<Result<SendGtkBox>>),
364 RawWindowHandle(Sender<Result<SendRawWindowHandle>>),
365 Theme(Sender<Result<Theme>>),
366 Center,
367 RequestUserAttention(Option<UserAttentionType>),
368 SetEnabled(bool),
369 SetResizable(bool),
370 SetMaximizable(bool),
371 SetMinimizable(bool),
372 SetClosable(bool),
373 SetTitle(String),
374 Maximize,
375 Unmaximize,
376 Minimize,
377 Unminimize,
378 Show,
379 Hide,
380 SetDecorations(bool),
381 SetShadow(bool),
382 SetAlwaysOnBottom(bool),
383 SetAlwaysOnTop(bool),
384 SetVisibleOnAllWorkspaces(bool),
385 SetContentProtected(bool),
386 SetSize(Size),
387 SetMinSize(Option<Size>),
388 SetMaxSize(Option<Size>),
389 SetSizeConstraints(WindowSizeConstraints),
390 SetPosition(Position),
391 SetFullscreen(bool),
392 SetFullscreenOnMonitor(PhysicalPosition<f64>),
393 #[cfg(target_os = "macos")]
396 ApplyPendingFullscreen,
397 #[cfg(target_os = "macos")]
398 SetSimpleFullscreen(bool),
399 SetFocus,
400 #[allow(unused)]
402 SetFocusable(bool),
403 SetIcon(Icon<'static>),
404 SetSkipTaskbar(bool),
405 SetCursorGrab(bool),
406 SetCursorVisible(bool),
407 SetCursorIcon(CursorIcon),
408 SetCursorPosition(Position),
409 SetIgnoreCursorEvents(bool),
410 SetProgressBar(ProgressBarState),
411 SetBadgeCount(Option<i64>, Option<String>),
412 SetBadgeLabel(Option<String>),
413 SetOverlayIcon(Option<Icon<'static>>),
414 SetTitleBarStyle(tauri_utils::TitleBarStyle),
415 SetTrafficLightPosition(Position),
416 SetTheme(Option<Theme>),
417 SetBackgroundColor(Option<Color>),
418 StartDragging,
419 StartResizeDragging(tauri_runtime::ResizeDirection),
420}
421
422#[cfg(windows)]
423type SoftbufferSurface = softbuffer::Surface<SoftbufferWindowHandle, SoftbufferWindowHandle>;
424
425#[derive(Clone)]
428pub struct NativeWindowToken(Arc<()>);
429
430impl NativeWindowToken {
431 pub(crate) fn new() -> Self {
432 Self(Arc::new(()))
433 }
434}
435
436impl PartialEq for NativeWindowToken {
437 fn eq(&self, other: &Self) -> bool {
438 Arc::ptr_eq(&self.0, &other.0)
439 }
440}
441
442impl Eq for NativeWindowToken {}
443
444impl std::fmt::Debug for NativeWindowToken {
445 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446 formatter
447 .debug_struct("NativeWindowToken")
448 .finish_non_exhaustive()
449 }
450}
451
452#[cfg(test)]
453mod native_window_identity_tests {
454 use super::NativeWindowToken;
455
456 #[test]
457 fn references_preserve_one_window_and_reject_replacements() {
458 let first = NativeWindowToken::new();
459 let retained_by_webview = first.clone();
460 assert_eq!(first, retained_by_webview);
461 let destination = NativeWindowToken::new();
462 assert_ne!(retained_by_webview, destination);
463 drop(first);
464 assert_ne!(retained_by_webview, NativeWindowToken::new());
465 }
466}
467
468const PENDING_ACTIVATION_TIMEOUT: Duration = Duration::from_secs(5);
471
472pub(crate) struct AppWindow {
473 pub(crate) lifetime: NativeWindowToken,
474 #[allow(unused)]
475 pub(crate) id: WindowId,
476 pub(crate) label: String,
477 #[cfg(windows)]
478 pub(crate) background_surface: Option<SoftbufferSurface>,
479 pub(crate) window: Arc<dyn WinitWindow>,
480 pub(crate) attrs: AppWindowAttrs,
481 pub(crate) children: Vec<AppWebview>,
482 pub(crate) listeners: WindowEventListeners,
483 pub(crate) native_drag_drop: Option<WinitDragDropState>,
484 #[cfg(any(
485 target_os = "linux",
486 target_os = "dragonfly",
487 target_os = "freebsd",
488 target_os = "netbsd",
489 target_os = "openbsd"
490 ))]
491 pub(crate) cef_host: crate::platform::linux::CefX11Host,
494 pub(crate) pending_activation: Option<Instant>,
498 #[cfg(target_os = "macos")]
501 pub(crate) fullscreen_transition:
502 objc2::rc::Retained<crate::platform::macos::FullscreenTransition>,
503}
504
505#[derive(Clone, Debug, Default)]
506pub(crate) struct AppWindowAttrs {
507 pub(crate) inner: WindowAttributes,
508 pub(crate) center: bool,
509 pub(crate) background_color: Option<Color>,
510 pub(crate) prevent_overflow: Option<Size>,
511 #[cfg(target_os = "macos")]
512 pub(crate) traffic_light_position: Option<Position>,
513 #[cfg(any(
514 target_os = "macos",
515 target_os = "linux",
516 target_os = "dragonfly",
517 target_os = "freebsd",
518 target_os = "netbsd",
519 target_os = "openbsd"
520 ))]
521 pub(crate) visible_on_all_workspaces: bool,
522 #[cfg(any(
523 target_os = "linux",
524 target_os = "dragonfly",
525 target_os = "freebsd",
526 target_os = "netbsd",
527 target_os = "openbsd"
528 ))]
529 pub(crate) skip_taskbar: bool,
530 #[cfg(any(
533 target_os = "linux",
534 target_os = "dragonfly",
535 target_os = "freebsd",
536 target_os = "netbsd",
537 target_os = "openbsd"
538 ))]
539 pub(crate) transient_for: Option<gtk::Window>,
540}
541
542impl AppWindow {
543 pub(crate) fn center(&self) {
544 let monitor = self.window.current_monitor();
545 let monitor = monitor.or_else(|| self.window.primary_monitor());
546 let Some(monitor) = monitor else {
547 return;
548 };
549
550 #[allow(unused_mut)]
551 let mut window_size = self.window.outer_size();
552
553 #[cfg(windows)]
557 if self.window.is_decorated()
558 && let Some(visible_height) = self.dwm_visible_frame_height()
559 {
560 window_size.height = visible_height;
561 }
562
563 let position = calculate_window_center_position(window_size, &monitor);
564 self.window.set_outer_position(Position::Physical(position));
565 }
566
567 pub(crate) fn activate(&self) {
576 #[cfg(target_os = "macos")]
577 crate::platform::macos::activate_application();
578
579 #[cfg(any(
580 target_os = "linux",
581 target_os = "dragonfly",
582 target_os = "freebsd",
583 target_os = "netbsd",
584 target_os = "openbsd"
585 ))]
586 self.raise_native();
587
588 self.window.focus_window();
589 }
590
591 pub(crate) fn request_fullscreen(&self, target: Option<FullscreenTarget>) {
599 #[cfg(target_os = "macos")]
600 if self.fullscreen_transition.in_progress() {
601 self.fullscreen_transition.hold(target);
602 return;
603 }
604
605 let fullscreen = match target {
606 None => None,
607 Some(FullscreenTarget::CurrentMonitor) => Some(Fullscreen::Borderless(None)),
608 Some(FullscreenTarget::MonitorAt(position)) => {
609 let monitor = self.window.available_monitors().find(|m| {
611 let pos = m.position().unwrap_or_default();
612 let vm = m.current_video_mode();
613 let size = vm.map(|v| v.size()).unwrap_or_default();
614 position.x >= pos.x as f64
615 && position.x < pos.x as f64 + size.width as f64
616 && position.y >= pos.y as f64
617 && position.y < pos.y as f64 + size.height as f64
618 });
619 let Some(monitor) = monitor else {
620 return;
621 };
622 Some(Fullscreen::Borderless(Some(monitor)))
623 }
624 };
625 self.window.set_fullscreen(fullscreen);
626 }
627
628 pub(crate) fn preferred_theme(&self) -> Option<Theme> {
629 self
630 .attrs
631 .inner
632 .preferred_theme
633 .map(winit_theme_to_tauri_theme)
634 }
635
636 pub(crate) fn resolved_theme(&self, app_wide_theme: Option<Theme>) -> Option<Theme> {
637 self.preferred_theme().or(app_wide_theme)
638 }
639
640 pub(crate) fn safe_surface_size(&self) -> PhysicalSize<u32> {
644 #[cfg(any(
645 target_os = "linux",
646 target_os = "dragonfly",
647 target_os = "freebsd",
648 target_os = "netbsd",
649 target_os = "openbsd"
650 ))]
651 {
652 self.cef_host.size()
653 }
654
655 #[cfg(not(any(
656 target_os = "linux",
657 target_os = "dragonfly",
658 target_os = "freebsd",
659 target_os = "netbsd",
660 target_os = "openbsd"
661 )))]
662 self.window.surface_size()
663 }
664
665 pub(crate) fn set_theme(&mut self, theme: Option<Theme>) {
666 self.attrs.inner.preferred_theme = tauri_theme_to_winit_theme(theme);
667 self.window.set_theme(tauri_theme_to_winit_theme(theme));
668 self.apply_cef_theme(theme);
669 #[cfg(target_os = "macos")]
670 self.reapply_traffic_light_position_after_appearance_change();
671 }
672
673 fn apply_cef_theme(&self, theme: Option<Theme>) {
674 for child in &self.children {
675 let request_context = child.host.request_context();
676 request_context::apply_theme_scheme(request_context.as_ref(), theme);
677 }
678 }
679}
680
681impl<T: UserEvent> WinitCefApp<T> {
682 pub(crate) fn create_window(
683 &mut self,
684 event_loop: &dyn ActiveEventLoop,
685 window_id: WindowId,
686 webview_id: Option<u32>,
687 pending: Box<PendingWindow<T, CefRuntime<T>>>,
688 _after_window_creation: Option<AfterWindowCreationCallback>,
689 ) -> Result<()> {
690 let mut attrs = pending.window_builder.attrs.clone();
691 if attrs.inner.preferred_theme.is_none() {
692 attrs.inner.preferred_theme =
693 tauri_theme_to_winit_theme(*self.context.app_wide_theme.lock().unwrap());
694 }
695 prepare_window_attributes(event_loop, &mut attrs);
696
697 let window = event_loop
698 .create_window(attrs.inner.clone())
699 .map_err(|e| Error::CreateWindow(Box::new(e)))?;
700
701 #[cfg(any(
702 target_os = "linux",
703 target_os = "dragonfly",
704 target_os = "freebsd",
705 target_os = "netbsd",
706 target_os = "openbsd"
707 ))]
708 let cef_host = crate::platform::linux::CefX11Host::new(window.as_ref()).ok_or_else(|| {
709 Error::CreateWindow("failed to create the X11 host for the CEF browser".into())
710 })?;
711
712 let winit_id = window.id();
713 let pending_activation = (attrs.inner.active && attrs.inner.visible)
714 .then(|| Instant::now() + PENDING_ACTIVATION_TIMEOUT);
715 #[cfg(target_os = "macos")]
716 let fullscreen_transition = {
717 let context = self.context.clone();
718 crate::platform::macos::FullscreenTransition::observe(
719 &crate::platform::macos::nswindow(window.as_ref()),
720 move || {
721 let _ = context.send_message(Message::Window {
722 window_id,
723 message: WindowMessage::ApplyPendingFullscreen,
724 });
725 },
726 )
727 };
728 let mut appwindow = AppWindow {
729 lifetime: NativeWindowToken::new(),
730 id: window_id,
731 label: pending.label.clone(),
732 #[cfg(windows)]
733 background_surface: None,
734 window: Arc::from(window),
735 attrs,
736 children: Vec::new(),
737 listeners: Default::default(),
738 native_drag_drop: None,
739 #[cfg(any(
740 target_os = "linux",
741 target_os = "dragonfly",
742 target_os = "freebsd",
743 target_os = "netbsd",
744 target_os = "openbsd"
745 ))]
746 cef_host,
747 pending_activation,
748 #[cfg(target_os = "macos")]
749 fullscreen_transition,
750 };
751
752 #[cfg(target_os = "macos")]
753 {
754 appwindow.apply_movability();
755 appwindow.set_visible_on_all_workspaces(appwindow.attrs.visible_on_all_workspaces);
756 if let Some(position) = &appwindow.attrs.traffic_light_position {
757 appwindow.set_traffic_light_position(position);
758 }
759 }
760
761 #[cfg(any(
762 target_os = "linux",
763 target_os = "dragonfly",
764 target_os = "freebsd",
765 target_os = "netbsd",
766 target_os = "openbsd"
767 ))]
768 {
769 appwindow.set_visible_on_all_workspaces(appwindow.attrs.visible_on_all_workspaces);
770 appwindow.set_skip_taskbar(appwindow.attrs.skip_taskbar);
771 appwindow.apply_transient_for();
772 }
773
774 #[cfg(windows)]
775 appwindow.draw_background_surface();
776
777 #[cfg(not(windows))]
778 if appwindow.attrs.background_color.is_some() {
779 appwindow.set_background_color(appwindow.attrs.background_color);
780 }
781
782 #[cfg(any(
783 target_os = "linux",
784 target_os = "dragonfly",
785 target_os = "freebsd",
786 target_os = "netbsd",
787 target_os = "openbsd"
788 ))]
789 if let Some(after_window_creation) = _after_window_creation {
790 use gtk::glib::translate::ToGlibPtr;
791 use winit::platform::gtk4::WindowExtGtk4;
792
793 let gtk_window = appwindow.window.gtk_window().unwrap();
794 let default_vbox = appwindow.cef_host.default_vbox();
795 after_window_creation(RawWindow {
796 gtk_window: {
797 let ptr: *mut gtk::ffi::GtkApplicationWindow = gtk_window.to_glib_none().0;
798 ptr as *mut std::ffi::c_void
799 },
800 default_vbox: Some({
801 let ptr: *mut gtk::ffi::GtkBox = default_vbox.to_glib_none().0;
802 ptr as *mut std::ffi::c_void
803 }),
804 _marker: &PhantomData,
805 });
806 }
807
808 #[cfg(any(windows, target_os = "macos"))]
809 if let Some(after_window_creation) = _after_window_creation {
810 after_window_creation(RawWindow {
811 #[cfg(windows)]
812 hwnd: appwindow.hwnd().0 as isize,
813 _marker: &PhantomData,
814 });
815 }
816
817 if let (Some(webview_id), Some(webview)) = (webview_id, pending.webview) {
821 Self::build_and_attach_webview(
822 &self.context,
823 &self.scheme_registry,
824 &mut self.state.live_browsers,
825 &mut appwindow,
826 webview_id,
827 browser_client::DragDropEventTarget::Window,
828 webview,
829 )?;
830 }
831
832 self
833 .state
834 .winid_id_to_window_id_map
835 .insert(winit_id, window_id);
836 self.state.windows.insert(window_id, appwindow);
837
838 Ok(())
839 }
840
841 pub(crate) fn apply_pending_activations(&mut self) {
857 let now = Instant::now();
858 for appwindow in self.state.windows.values_mut() {
859 let Some(deadline) = appwindow.pending_activation else {
860 continue;
861 };
862
863 if appwindow.window.is_visible() == Some(false) {
864 if now < deadline {
865 continue;
866 }
867 appwindow.pending_activation = None;
868 continue;
869 }
870
871 appwindow.activate();
872 appwindow.pending_activation = None;
873 }
874 }
875
876 #[cfg(any(
884 target_os = "linux",
885 target_os = "dragonfly",
886 target_os = "freebsd",
887 target_os = "netbsd",
888 target_os = "openbsd"
889 ))]
890 pub(crate) fn apply_pending_host_layouts(&mut self) {
891 for appwindow in self.state.windows.values() {
892 if appwindow.cef_host.take_needs_relayout() {
893 crate::webview::layout_app_window(appwindow);
894 }
895 }
896 }
897
898 pub(crate) fn handle_window_message(
899 &mut self,
900 event_loop: &dyn ActiveEventLoop,
901 window_id: WindowId,
902 message: WindowMessage,
903 ) {
904 match message {
906 WindowMessage::Close => {
907 self.request_window_close(window_id, event_loop);
908 return;
909 }
910 WindowMessage::Destroy => {
911 self.close_window(window_id, event_loop);
912 return;
913 }
914 _ => {}
915 }
916
917 let Some(appwindow) = self.state.windows.get_mut(&window_id) else {
918 return;
919 };
920 let window = &appwindow.window;
921
922 match message {
923 WindowMessage::AddEventListener(id, listener) => {
924 appwindow.listeners.lock().unwrap().insert(id, listener);
925 }
926 WindowMessage::Close | WindowMessage::Destroy => unreachable!("handled before borrowing"),
927 WindowMessage::ScaleFactor(tx) => _ = tx.send(Ok(window.scale_factor())),
928 WindowMessage::InnerSize(tx) => _ = tx.send(Ok(window.surface_size())),
929 WindowMessage::OuterSize(tx) => _ = tx.send(Ok(window.outer_size())),
930 WindowMessage::IsFullscreen(tx) => _ = tx.send(Ok(window.fullscreen().is_some())),
931 WindowMessage::IsMinimized(tx) => _ = tx.send(Ok(window.is_minimized().unwrap_or(false))),
932 WindowMessage::IsMaximized(tx) => _ = tx.send(Ok(window.is_maximized())),
933 WindowMessage::IsFocused(tx) => _ = tx.send(Ok(window.has_focus())),
934 WindowMessage::IsDecorated(tx) => _ = tx.send(Ok(window.is_decorated())),
935 WindowMessage::IsResizable(tx) => _ = tx.send(Ok(window.is_resizable())),
936 WindowMessage::IsMaximizable(tx) => {
937 let is_maximizable = window
938 .enabled_buttons()
939 .contains(winit::window::WindowButtons::MAXIMIZE);
940 let _ = tx.send(Ok(is_maximizable));
941 }
942 WindowMessage::IsMinimizable(tx) => {
943 let is_minimizable = window
944 .enabled_buttons()
945 .contains(winit::window::WindowButtons::MINIMIZE);
946 let _ = tx.send(Ok(is_minimizable));
947 }
948 WindowMessage::IsClosable(tx) => {
949 let is_closable = window
950 .enabled_buttons()
951 .contains(winit::window::WindowButtons::CLOSE);
952 let _ = tx.send(Ok(is_closable));
953 }
954 WindowMessage::IsVisible(tx) => _ = tx.send(Ok(window.is_visible().unwrap_or(true))),
955 WindowMessage::IsEnabled(tx) => _ = tx.send(Ok(appwindow.is_enabled())),
956 WindowMessage::IsAlwaysOnTop(tx) => {
957 let is_on_top = appwindow.attrs.inner.window_level == WindowLevel::AlwaysOnTop;
958 let _ = tx.send(Ok(is_on_top));
959 }
960 WindowMessage::Title(tx) => _ = tx.send(Ok(window.title())),
961 WindowMessage::InnerPosition(tx) => _ = tx.send(Ok(window.surface_position())),
962 WindowMessage::OuterPosition(tx) => {
963 let pos = window
964 .outer_position()
965 .map_err(|_| Error::FailedToGetMonitor);
966 let _ = tx.send(pos);
967 }
968 WindowMessage::CurrentMonitor(tx) => {
969 let current = window.current_monitor();
970 let current = current.map(|m| winit_monitor_to_tauri_monitor(&m));
971 let _ = tx.send(Ok(current));
972 }
973 WindowMessage::PrimaryMonitor(tx) => {
974 let primary = window.primary_monitor();
975 let primary = primary.map(|m| winit_monitor_to_tauri_monitor(&m));
976 let _ = tx.send(Ok(primary));
977 }
978 WindowMessage::MonitorFromPoint(tx, x, y) => {
979 let mut available_monitors = window.available_monitors();
980 let monitor = available_monitors
981 .find(|m| {
982 let pos = m.position().unwrap_or_default();
983 let vm = m.current_video_mode();
984 let size = vm.map(|v| v.size()).unwrap_or_default();
985 x >= pos.x as f64
986 && x <= pos.x as f64 + size.width as f64
987 && y >= pos.y as f64
988 && y <= pos.y as f64 + size.height as f64
989 })
990 .map(|m| winit_monitor_to_tauri_monitor(&m));
991 let _ = tx.send(Ok(monitor));
992 }
993 WindowMessage::AvailableMonitors(tx) => {
994 let monitors = window
995 .available_monitors()
996 .map(|m| winit_monitor_to_tauri_monitor(&m))
997 .collect();
998 let _ = tx.send(Ok(monitors));
999 }
1000 #[cfg(any(
1001 target_os = "linux",
1002 target_os = "dragonfly",
1003 target_os = "freebsd",
1004 target_os = "netbsd",
1005 target_os = "openbsd"
1006 ))]
1007 WindowMessage::GtkWindow(tx) => {
1008 use gtk::glib::translate::ToGlibPtr;
1009 use winit::platform::gtk4::WindowExtGtk4;
1010
1011 let gtk_window = appwindow.window.gtk_window().unwrap();
1012 let ptr: *mut gtk::ffi::GtkApplicationWindow = gtk_window.to_glib_full();
1013 let _ = tx.send(Ok(SendGtkWindow(ptr as *mut std::ffi::c_void)));
1014 }
1015 #[cfg(any(
1016 target_os = "linux",
1017 target_os = "dragonfly",
1018 target_os = "freebsd",
1019 target_os = "netbsd",
1020 target_os = "openbsd"
1021 ))]
1022 WindowMessage::DefaultVBox(tx) => {
1023 use gtk::glib::translate::ToGlibPtr;
1024
1025 let default_vbox = appwindow.cef_host.default_vbox();
1026 let ptr: *mut gtk::ffi::GtkBox = default_vbox.to_glib_full();
1027 let _ = tx.send(Ok(SendGtkBox(ptr as *mut std::ffi::c_void)));
1028 }
1029 WindowMessage::RawWindowHandle(tx) => {
1030 let handle = window.window_handle();
1031 let send_handle = handle
1032 .map(|h| SendRawWindowHandle(h.as_raw()))
1033 .map_err(|_| Error::FailedToSendMessage);
1034 let _ = tx.send(send_handle);
1035 }
1036 WindowMessage::Theme(tx) => {
1037 let theme = window.theme();
1038 let theme = theme.map(winit_theme_to_tauri_theme);
1039 let theme = theme.unwrap_or(Theme::Light);
1040 let _ = tx.send(Ok(theme));
1041 }
1042 WindowMessage::Center => {
1043 appwindow.center();
1044 }
1045 WindowMessage::RequestUserAttention(attention) => {
1046 window.request_user_attention(match attention {
1047 Some(UserAttentionType::Critical) => Some(winit::window::UserAttentionType::Critical),
1048 Some(UserAttentionType::Informational) => {
1049 Some(winit::window::UserAttentionType::Informational)
1050 }
1051 None => None,
1052 })
1053 }
1054 WindowMessage::SetEnabled(value) => appwindow.set_enabled(value),
1055 WindowMessage::SetResizable(value) => window.set_resizable(value),
1056 WindowMessage::SetTitle(title) => window.set_title(&title),
1057 WindowMessage::Maximize => window.set_maximized(true),
1058 WindowMessage::Unmaximize => window.set_maximized(false),
1059 WindowMessage::Minimize => window.set_minimized(true),
1060 WindowMessage::Unminimize => window.set_minimized(false),
1061 WindowMessage::Show => window.set_visible(true),
1062 WindowMessage::Hide => window.set_visible(false),
1063 WindowMessage::SetDecorations(value) => {
1064 window.set_decorations(value);
1065 #[cfg(target_os = "macos")]
1066 appwindow.apply_movability();
1067 }
1068 WindowMessage::SetSize(size) => _ = window.request_surface_size(size),
1069 WindowMessage::SetPosition(position) => window.set_outer_position(position),
1070 WindowMessage::SetFullscreen(value) => {
1071 appwindow.request_fullscreen(value.then_some(FullscreenTarget::CurrentMonitor))
1072 }
1073 WindowMessage::SetFullscreenOnMonitor(position) => {
1074 appwindow.request_fullscreen(Some(FullscreenTarget::MonitorAt(position)))
1075 }
1076 #[cfg(target_os = "macos")]
1077 WindowMessage::ApplyPendingFullscreen => {
1078 if let Some(target) = appwindow.fullscreen_transition.take_pending() {
1079 appwindow.request_fullscreen(target);
1080 }
1081 }
1082 #[cfg(target_os = "macos")]
1083 WindowMessage::SetSimpleFullscreen(value) => {
1084 let changed = window.set_simple_fullscreen(value);
1085 if changed && !value {
1086 appwindow.apply_movability();
1087 }
1088 }
1089 WindowMessage::SetFocus => appwindow.activate(),
1090 WindowMessage::SetMinSize(min_size) => window.set_min_surface_size(min_size),
1091 WindowMessage::SetMaxSize(max_size) => window.set_max_surface_size(max_size),
1092 WindowMessage::SetMaximizable(value) => {
1093 let mut buttons = window.enabled_buttons();
1094 buttons.set(winit::window::WindowButtons::MAXIMIZE, value);
1095 window.set_enabled_buttons(buttons);
1096 }
1097 WindowMessage::SetMinimizable(value) => {
1098 let mut buttons = window.enabled_buttons();
1099 buttons.set(winit::window::WindowButtons::MINIMIZE, value);
1100 window.set_enabled_buttons(buttons);
1101 }
1102 WindowMessage::SetClosable(value) => {
1103 let mut buttons = window.enabled_buttons();
1104 buttons.set(winit::window::WindowButtons::CLOSE, value);
1105 window.set_enabled_buttons(buttons);
1106 }
1107 WindowMessage::SetAlwaysOnBottom(value) => {
1108 let level = match value {
1109 true => WindowLevel::AlwaysOnBottom,
1110 false => WindowLevel::Normal,
1111 };
1112 appwindow.attrs.inner.window_level = level;
1113 window.set_window_level(level);
1114 }
1115 WindowMessage::SetAlwaysOnTop(value) => {
1116 let level = match value {
1117 true => WindowLevel::AlwaysOnTop,
1118 false => WindowLevel::Normal,
1119 };
1120 appwindow.attrs.inner.window_level = level;
1121 window.set_window_level(level);
1122 }
1123 WindowMessage::SetVisibleOnAllWorkspaces(_value) => {
1124 #[cfg(target_os = "macos")]
1125 {
1126 appwindow.attrs.visible_on_all_workspaces = _value;
1127 appwindow.set_visible_on_all_workspaces(_value);
1128 }
1129 #[cfg(any(
1130 target_os = "linux",
1131 target_os = "dragonfly",
1132 target_os = "freebsd",
1133 target_os = "netbsd",
1134 target_os = "openbsd"
1135 ))]
1136 {
1137 appwindow.attrs.visible_on_all_workspaces = _value;
1138 appwindow.set_visible_on_all_workspaces(_value);
1139 }
1140 }
1141 WindowMessage::SetContentProtected(value) => window.set_content_protected(value),
1142 WindowMessage::SetIcon(icon) => {
1143 if let Ok(icon) = super::window::tauri_icon_to_winit_icon(icon) {
1144 window.set_window_icon(Some(icon))
1145 }
1146 }
1147 WindowMessage::SetSkipTaskbar(_value) => {
1148 #[cfg(windows)]
1149 window.set_skip_taskbar(_value);
1150 #[cfg(any(
1151 target_os = "linux",
1152 target_os = "dragonfly",
1153 target_os = "freebsd",
1154 target_os = "netbsd",
1155 target_os = "openbsd"
1156 ))]
1157 appwindow.set_skip_taskbar(_value);
1158 }
1159 WindowMessage::SetShadow(_value) => {
1160 #[cfg(windows)]
1161 window.set_undecorated_shadow(_value);
1162 #[cfg(target_os = "macos")]
1163 window.set_has_shadow(_value);
1164 }
1165 WindowMessage::SetCursorGrab(value) => {
1166 let _ = window.set_cursor_grab(match value {
1167 true => winit::window::CursorGrabMode::Confined,
1168 false => winit::window::CursorGrabMode::None,
1169 });
1170 }
1171 WindowMessage::SetCursorVisible(value) => window.set_cursor_visible(value),
1172 WindowMessage::SetCursorIcon(value) => {
1173 let cursor_icon = tauri_cursor_to_winit_cursor(value);
1174 window.set_cursor(cursor_icon.into())
1175 }
1176 WindowMessage::SetCursorPosition(value) => _ = window.set_cursor_position(value),
1177 WindowMessage::SetIgnoreCursorEvents(value) => _ = window.set_cursor_hittest(!value),
1178
1179 WindowMessage::SetTrafficLightPosition(_position) => {
1180 #[cfg(target_os = "macos")]
1181 {
1182 appwindow.attrs.traffic_light_position = Some(_position);
1183 appwindow.set_traffic_light_position(&_position);
1184 }
1185 }
1186 WindowMessage::SetTitleBarStyle(_style) => {
1187 #[cfg(target_os = "macos")]
1188 appwindow.set_title_bar_style(_style);
1189 }
1190 WindowMessage::SetBackgroundColor(color) => {
1191 appwindow.attrs.background_color = color;
1192 appwindow.set_background_color(color);
1193 }
1194 WindowMessage::SetTheme(theme) => appwindow.set_theme(theme),
1195 WindowMessage::SetBadgeCount(count, desktop_filename) => {
1196 event_loop.set_badge_count(count, desktop_filename)
1197 }
1198 WindowMessage::SetBadgeLabel(label) => event_loop.set_badge_label(label),
1199 WindowMessage::SetOverlayIcon(_icon) => {
1200 #[cfg(windows)]
1201 appwindow.set_overlay_icon(_icon);
1202 }
1203 WindowMessage::StartDragging => _ = window.drag_window(),
1204 WindowMessage::StartResizeDragging(direction) => {
1205 let _ = window.drag_resize_window(tauri_resize_direction_to_winit(direction));
1206 }
1207 WindowMessage::SetSizeConstraints(constraints) => {
1208 let min_size = min_size_constraint(constraints.min_width, constraints.min_height);
1210 let max_size = max_size_constraint(constraints.max_width, constraints.max_height);
1211 window.set_min_surface_size(min_size);
1212 window.set_max_surface_size(max_size);
1213 }
1214
1215 WindowMessage::SetFocusable(_) => {
1216 }
1218 WindowMessage::SetProgressBar(state) => {
1219 #[cfg(target_os = "macos")]
1220 event_loop.set_progress_bar(state);
1221 #[cfg(not(target_os = "macos"))]
1222 appwindow.set_progress_bar(state);
1223 }
1224 }
1225 }
1226}
1227
1228#[derive(Debug, Clone)]
1229pub struct CefWindowDispatcher<T: UserEvent> {
1230 pub(crate) window_id: WindowId,
1231 pub(crate) context: RuntimeContext<T>,
1232}
1233
1234fn getter<T: UserEvent, R>(
1235 context: &RuntimeContext<T>,
1236 message: Message<T>,
1237 receiver: Receiver<Result<R>>,
1238) -> Result<R> {
1239 context.send_message(message)?;
1240 receiver.recv().map_err(|_| Error::FailedToReceiveMessage)?
1241}
1242
1243macro_rules! window_getter {
1244 ($self:ident, $variant:ident) => {{
1245 let (tx, rx) = mpsc::channel();
1246 getter(
1247 &$self.context,
1248 Message::Window {
1249 window_id: $self.window_id,
1250 message: WindowMessage::$variant(tx),
1251 },
1252 rx,
1253 )
1254 }};
1255}
1256
1257impl<T: UserEvent> WindowDispatch<T> for CefWindowDispatcher<T> {
1258 type Runtime = CefRuntime<T>;
1259 type WindowBuilder = WindowBuilderWrapper;
1260
1261 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
1262 self.context.run_on_main_thread(f)
1263 }
1264
1265 fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
1266 let id = self.context.next_window_event_id();
1267 let _ = self.context.send_message(Message::Window {
1268 window_id: self.window_id,
1269 message: WindowMessage::AddEventListener(id, Box::new(f)),
1270 });
1271 id
1272 }
1273
1274 fn scale_factor(&self) -> Result<f64> {
1275 window_getter!(self, ScaleFactor)
1276 }
1277
1278 fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
1279 window_getter!(self, InnerPosition)
1280 }
1281
1282 fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
1283 window_getter!(self, OuterPosition)
1284 }
1285
1286 fn inner_size(&self) -> Result<PhysicalSize<u32>> {
1287 window_getter!(self, InnerSize)
1288 }
1289
1290 fn outer_size(&self) -> Result<PhysicalSize<u32>> {
1291 window_getter!(self, OuterSize)
1292 }
1293
1294 fn is_fullscreen(&self) -> Result<bool> {
1295 window_getter!(self, IsFullscreen)
1296 }
1297
1298 fn is_minimized(&self) -> Result<bool> {
1299 window_getter!(self, IsMinimized)
1300 }
1301
1302 fn is_maximized(&self) -> Result<bool> {
1303 window_getter!(self, IsMaximized)
1304 }
1305
1306 fn is_focused(&self) -> Result<bool> {
1307 window_getter!(self, IsFocused)
1308 }
1309
1310 fn is_decorated(&self) -> Result<bool> {
1311 window_getter!(self, IsDecorated)
1312 }
1313
1314 fn is_resizable(&self) -> Result<bool> {
1315 window_getter!(self, IsResizable)
1316 }
1317
1318 fn is_maximizable(&self) -> Result<bool> {
1319 window_getter!(self, IsMaximizable)
1320 }
1321
1322 fn is_minimizable(&self) -> Result<bool> {
1323 window_getter!(self, IsMinimizable)
1324 }
1325
1326 fn is_closable(&self) -> Result<bool> {
1327 window_getter!(self, IsClosable)
1328 }
1329
1330 fn is_visible(&self) -> Result<bool> {
1331 window_getter!(self, IsVisible)
1332 }
1333
1334 fn is_enabled(&self) -> Result<bool> {
1335 window_getter!(self, IsEnabled)
1336 }
1337
1338 fn is_always_on_top(&self) -> Result<bool> {
1339 window_getter!(self, IsAlwaysOnTop)
1340 }
1341
1342 fn title(&self) -> Result<String> {
1343 window_getter!(self, Title)
1344 }
1345
1346 fn current_monitor(&self) -> Result<Option<Monitor>> {
1347 window_getter!(self, CurrentMonitor)
1348 }
1349
1350 fn primary_monitor(&self) -> Result<Option<Monitor>> {
1351 window_getter!(self, PrimaryMonitor)
1352 }
1353
1354 fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
1355 let (tx, rx) = mpsc::channel();
1356 getter(
1357 &self.context,
1358 Message::Window {
1359 window_id: self.window_id,
1360 message: WindowMessage::MonitorFromPoint(tx, x, y),
1361 },
1362 rx,
1363 )
1364 }
1365
1366 fn available_monitors(&self) -> Result<Vec<Monitor>> {
1367 window_getter!(self, AvailableMonitors)
1368 }
1369
1370 #[cfg(any(
1371 target_os = "linux",
1372 target_os = "dragonfly",
1373 target_os = "freebsd",
1374 target_os = "netbsd",
1375 target_os = "openbsd"
1376 ))]
1377 fn gtk_window(&self) -> Result<*mut std::ffi::c_void> {
1378 window_getter!(self, GtkWindow).map(|gtk_window| gtk_window.0)
1379 }
1380
1381 #[cfg(any(
1382 target_os = "linux",
1383 target_os = "dragonfly",
1384 target_os = "freebsd",
1385 target_os = "netbsd",
1386 target_os = "openbsd"
1387 ))]
1388 fn default_vbox(&self) -> Result<*mut std::ffi::c_void> {
1389 window_getter!(self, DefaultVBox).map(|gtk_box| gtk_box.0)
1390 }
1391
1392 fn window_handle(
1393 &self,
1394 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
1395 let handle: Result<SendRawWindowHandle> = window_getter!(self, RawWindowHandle);
1396 let handle = handle.map_err(|_| raw_window_handle::HandleError::Unavailable)?;
1397 Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.0) })
1398 }
1399
1400 fn theme(&self) -> Result<Theme> {
1401 window_getter!(self, Theme)
1402 }
1403
1404 fn center(&self) -> Result<()> {
1405 self.context.send_message(Message::Window {
1406 window_id: self.window_id,
1407 message: WindowMessage::Center,
1408 })
1409 }
1410
1411 fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
1412 self.context.send_message(Message::Window {
1413 window_id: self.window_id,
1414 message: WindowMessage::RequestUserAttention(request_type),
1415 })
1416 }
1417
1418 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
1419 &mut self,
1420 pending: PendingWindow<T, Self::Runtime>,
1421 after_window_creation: Option<F>,
1422 ) -> Result<DetachedWindow<T, Self::Runtime>> {
1423 create_window_detached(&self.context, pending, after_window_creation)
1424 }
1425
1426 fn create_webview(
1427 &mut self,
1428 pending: PendingWebview<T, Self::Runtime>,
1429 ) -> Result<DetachedWebview<T, Self::Runtime>> {
1430 create_webview_detached(&self.context, self.window_id, pending)
1431 }
1432
1433 fn set_resizable(&self, resizable: bool) -> Result<()> {
1434 self.context.send_message(Message::Window {
1435 window_id: self.window_id,
1436 message: WindowMessage::SetResizable(resizable),
1437 })
1438 }
1439
1440 fn set_enabled(&self, enabled: bool) -> Result<()> {
1441 self.context.send_message(Message::Window {
1442 window_id: self.window_id,
1443 message: WindowMessage::SetEnabled(enabled),
1444 })
1445 }
1446
1447 fn set_maximizable(&self, maximizable: bool) -> Result<()> {
1448 self.context.send_message(Message::Window {
1449 window_id: self.window_id,
1450 message: WindowMessage::SetMaximizable(maximizable),
1451 })
1452 }
1453
1454 fn set_minimizable(&self, minimizable: bool) -> Result<()> {
1455 self.context.send_message(Message::Window {
1456 window_id: self.window_id,
1457 message: WindowMessage::SetMinimizable(minimizable),
1458 })
1459 }
1460
1461 fn set_closable(&self, closable: bool) -> Result<()> {
1462 self.context.send_message(Message::Window {
1463 window_id: self.window_id,
1464 message: WindowMessage::SetClosable(closable),
1465 })
1466 }
1467
1468 fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
1469 self.context.send_message(Message::Window {
1470 window_id: self.window_id,
1471 message: WindowMessage::SetTitle(title.into()),
1472 })
1473 }
1474
1475 fn maximize(&self) -> Result<()> {
1476 self.context.send_message(Message::Window {
1477 window_id: self.window_id,
1478 message: WindowMessage::Maximize,
1479 })
1480 }
1481
1482 fn unmaximize(&self) -> Result<()> {
1483 self.context.send_message(Message::Window {
1484 window_id: self.window_id,
1485 message: WindowMessage::Unmaximize,
1486 })
1487 }
1488
1489 fn minimize(&self) -> Result<()> {
1490 self.context.send_message(Message::Window {
1491 window_id: self.window_id,
1492 message: WindowMessage::Minimize,
1493 })
1494 }
1495
1496 fn unminimize(&self) -> Result<()> {
1497 self.context.send_message(Message::Window {
1498 window_id: self.window_id,
1499 message: WindowMessage::Unminimize,
1500 })
1501 }
1502
1503 fn show(&self) -> Result<()> {
1504 self.context.send_message(Message::Window {
1505 window_id: self.window_id,
1506 message: WindowMessage::Show,
1507 })
1508 }
1509
1510 fn hide(&self) -> Result<()> {
1511 self.context.send_message(Message::Window {
1512 window_id: self.window_id,
1513 message: WindowMessage::Hide,
1514 })
1515 }
1516
1517 fn close(&self) -> Result<()> {
1518 self.context.send_message(Message::Window {
1519 window_id: self.window_id,
1520 message: WindowMessage::Close,
1521 })
1522 }
1523
1524 fn destroy(&self) -> Result<()> {
1525 self.context.send_message(Message::Window {
1526 window_id: self.window_id,
1527 message: WindowMessage::Destroy,
1528 })
1529 }
1530
1531 fn set_decorations(&self, decorations: bool) -> Result<()> {
1532 self.context.send_message(Message::Window {
1533 window_id: self.window_id,
1534 message: WindowMessage::SetDecorations(decorations),
1535 })
1536 }
1537
1538 fn set_shadow(&self, enable: bool) -> Result<()> {
1539 self.context.send_message(Message::Window {
1540 window_id: self.window_id,
1541 message: WindowMessage::SetShadow(enable),
1542 })
1543 }
1544
1545 fn set_always_on_bottom(&self, value: bool) -> Result<()> {
1546 self.context.send_message(Message::Window {
1547 window_id: self.window_id,
1548 message: WindowMessage::SetAlwaysOnBottom(value),
1549 })
1550 }
1551
1552 fn set_always_on_top(&self, value: bool) -> Result<()> {
1553 self.context.send_message(Message::Window {
1554 window_id: self.window_id,
1555 message: WindowMessage::SetAlwaysOnTop(value),
1556 })
1557 }
1558
1559 fn set_visible_on_all_workspaces(&self, value: bool) -> Result<()> {
1560 self.context.send_message(Message::Window {
1561 window_id: self.window_id,
1562 message: WindowMessage::SetVisibleOnAllWorkspaces(value),
1563 })
1564 }
1565
1566 fn set_content_protected(&self, protected: bool) -> Result<()> {
1567 self.context.send_message(Message::Window {
1568 window_id: self.window_id,
1569 message: WindowMessage::SetContentProtected(protected),
1570 })
1571 }
1572
1573 fn set_size(&self, size: Size) -> Result<()> {
1574 self.context.send_message(Message::Window {
1575 window_id: self.window_id,
1576 message: WindowMessage::SetSize(size),
1577 })
1578 }
1579
1580 fn set_min_size(&self, size: Option<Size>) -> Result<()> {
1581 self.context.send_message(Message::Window {
1582 window_id: self.window_id,
1583 message: WindowMessage::SetMinSize(size),
1584 })
1585 }
1586
1587 fn set_max_size(&self, size: Option<Size>) -> Result<()> {
1588 self.context.send_message(Message::Window {
1589 window_id: self.window_id,
1590 message: WindowMessage::SetMaxSize(size),
1591 })
1592 }
1593
1594 fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()> {
1595 self.context.send_message(Message::Window {
1596 window_id: self.window_id,
1597 message: WindowMessage::SetSizeConstraints(constraints),
1598 })
1599 }
1600
1601 fn set_position(&self, position: Position) -> Result<()> {
1602 self.context.send_message(Message::Window {
1603 window_id: self.window_id,
1604 message: WindowMessage::SetPosition(position),
1605 })
1606 }
1607
1608 fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
1609 self.context.send_message(Message::Window {
1610 window_id: self.window_id,
1611 message: WindowMessage::SetFullscreen(fullscreen),
1612 })
1613 }
1614
1615 fn set_fullscreen_on_monitor(&self, position: PhysicalPosition<f64>) -> Result<()> {
1616 self.context.send_message(Message::Window {
1617 window_id: self.window_id,
1618 message: WindowMessage::SetFullscreenOnMonitor(position),
1619 })
1620 }
1621
1622 #[cfg(target_os = "macos")]
1623 fn set_simple_fullscreen(&self, enable: bool) -> Result<()> {
1624 self.context.send_message(Message::Window {
1625 window_id: self.window_id,
1626 message: WindowMessage::SetSimpleFullscreen(enable),
1627 })
1628 }
1629
1630 fn set_focus(&self) -> Result<()> {
1631 self.context.send_message(Message::Window {
1632 window_id: self.window_id,
1633 message: WindowMessage::SetFocus,
1634 })
1635 }
1636
1637 fn set_focusable(&self, focusable: bool) -> Result<()> {
1638 self.context.send_message(Message::Window {
1639 window_id: self.window_id,
1640 message: WindowMessage::SetFocusable(focusable),
1641 })
1642 }
1643
1644 fn set_icon(&self, icon: Icon) -> Result<()> {
1645 self.context.send_message(Message::Window {
1646 window_id: self.window_id,
1647 message: WindowMessage::SetIcon(icon.into_owned()),
1648 })
1649 }
1650
1651 fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
1652 self.context.send_message(Message::Window {
1653 window_id: self.window_id,
1654 message: WindowMessage::SetSkipTaskbar(skip),
1655 })
1656 }
1657
1658 fn set_cursor_grab(&self, grab: bool) -> Result<()> {
1659 self.context.send_message(Message::Window {
1660 window_id: self.window_id,
1661 message: WindowMessage::SetCursorGrab(grab),
1662 })
1663 }
1664
1665 fn set_cursor_visible(&self, visible: bool) -> Result<()> {
1666 self.context.send_message(Message::Window {
1667 window_id: self.window_id,
1668 message: WindowMessage::SetCursorVisible(visible),
1669 })
1670 }
1671
1672 fn set_cursor_icon(&self, icon: CursorIcon) -> Result<()> {
1673 self.context.send_message(Message::Window {
1674 window_id: self.window_id,
1675 message: WindowMessage::SetCursorIcon(icon),
1676 })
1677 }
1678
1679 fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> Result<()> {
1680 self.context.send_message(Message::Window {
1681 window_id: self.window_id,
1682 message: WindowMessage::SetCursorPosition(position.into()),
1683 })
1684 }
1685
1686 fn set_ignore_cursor_events(&self, ignore: bool) -> Result<()> {
1687 self.context.send_message(Message::Window {
1688 window_id: self.window_id,
1689 message: WindowMessage::SetIgnoreCursorEvents(ignore),
1690 })
1691 }
1692
1693 fn start_dragging(&self) -> Result<()> {
1694 self.context.send_message(Message::Window {
1695 window_id: self.window_id,
1696 message: WindowMessage::StartDragging,
1697 })
1698 }
1699
1700 fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> {
1701 self.context.send_message(Message::Window {
1702 window_id: self.window_id,
1703 message: WindowMessage::StartResizeDragging(direction),
1704 })
1705 }
1706
1707 fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
1708 self.context.send_message(Message::Window {
1709 window_id: self.window_id,
1710 message: WindowMessage::SetBadgeCount(count, desktop_filename),
1711 })
1712 }
1713
1714 fn set_badge_label(&self, label: Option<String>) -> Result<()> {
1715 self.context.send_message(Message::Window {
1716 window_id: self.window_id,
1717 message: WindowMessage::SetBadgeLabel(label),
1718 })
1719 }
1720
1721 fn set_overlay_icon(&self, icon: Option<Icon>) -> Result<()> {
1722 self.context.send_message(Message::Window {
1723 window_id: self.window_id,
1724 message: WindowMessage::SetOverlayIcon(icon.map(Icon::into_owned)),
1725 })
1726 }
1727
1728 fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
1729 self.context.send_message(Message::Window {
1730 window_id: self.window_id,
1731 message: WindowMessage::SetProgressBar(progress_state),
1732 })
1733 }
1734
1735 fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
1736 self.context.send_message(Message::Window {
1737 window_id: self.window_id,
1738 message: WindowMessage::SetTitleBarStyle(style),
1739 })
1740 }
1741
1742 fn set_traffic_light_position(&self, position: Position) -> Result<()> {
1743 self.context.send_message(Message::Window {
1744 window_id: self.window_id,
1745 message: WindowMessage::SetTrafficLightPosition(position),
1746 })
1747 }
1748
1749 fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
1750 self.context.send_message(Message::Window {
1751 window_id: self.window_id,
1752 message: WindowMessage::SetTheme(theme),
1753 })
1754 }
1755
1756 fn set_background_color(&self, color: Option<Color>) -> Result<()> {
1757 self.context.send_message(Message::Window {
1758 window_id: self.window_id,
1759 message: WindowMessage::SetBackgroundColor(color),
1760 })
1761 }
1762}
1763
1764pub(crate) fn create_window_detached<T, F>(
1765 context: &RuntimeContext<T>,
1766 pending: PendingWindow<T, CefRuntime<T>>,
1767 after_window_creation: Option<F>,
1768) -> Result<DetachedWindow<T, CefRuntime<T>>>
1769where
1770 T: UserEvent,
1771 F: Fn(RawWindow<'_>) + Send + 'static,
1772{
1773 let label = pending.label.clone();
1774 let window_id = context.next_window_id();
1775 let (webview_id, use_https_scheme, devtools) = pending
1776 .webview
1777 .as_ref()
1778 .map(|w| {
1779 (
1780 Some(context.next_webview_id()),
1781 w.webview_attributes.use_https_scheme,
1782 w.webview_attributes.devtools,
1783 )
1784 })
1785 .unwrap_or((None, false, None));
1786
1787 let (result_tx, result_rx) = mpsc::channel();
1788 context.send_message(Message::CreateWindow {
1789 window_id,
1790 webview_id,
1791 pending: Box::new(pending),
1792 after_window_creation: after_window_creation.map(|f| Box::new(f) as _),
1793 result_tx,
1794 })?;
1795 context.wait_for_creation(result_rx)?;
1798
1799 let webview = webview_id.map(|webview_id| DetachedWindowWebview {
1800 webview: DetachedWebview {
1801 label: label.clone(),
1802 dispatcher: CefWebviewDispatcher {
1803 window_id: Arc::new(Mutex::new(window_id)),
1804 webview_id,
1805 context: context.clone(),
1806 },
1807 },
1808 use_https_scheme,
1809 devtools,
1810 });
1811
1812 Ok(DetachedWindow {
1813 id: window_id,
1814 label,
1815 dispatcher: CefWindowDispatcher {
1816 window_id,
1817 context: context.clone(),
1818 },
1819 webview,
1820 })
1821}
1822
1823pub(crate) fn winit_monitor_to_tauri_monitor(monitor: &winit::monitor::MonitorHandle) -> Monitor {
1824 Monitor {
1825 name: monitor.name().map(|s| s.to_string()),
1826 scale_factor: monitor.scale_factor(),
1827 position: monitor.position().unwrap_or_default(),
1828 size: monitor
1829 .current_video_mode()
1830 .map(|v| v.size())
1831 .unwrap_or_default(),
1832 work_area: monitor.work_area(),
1833 }
1834}
1835
1836pub(crate) fn tauri_icon_to_winit_icon(icon: Icon) -> Result<winit::icon::Icon> {
1837 winit::icon::RgbaIcon::new(icon.rgba.into_owned(), icon.width, icon.height)
1838 .map(Into::into)
1839 .map_err(|e| tauri_runtime::Error::InvalidIcon(e.into()))
1840}
1841
1842fn tauri_cursor_to_winit_cursor(cursor: CursorIcon) -> winit::cursor::CursorIcon {
1843 match cursor {
1844 CursorIcon::Default => winit::cursor::CursorIcon::Default,
1845 CursorIcon::Crosshair => winit::cursor::CursorIcon::Crosshair,
1846 CursorIcon::Hand => winit::cursor::CursorIcon::Grab,
1847 CursorIcon::Arrow => winit::cursor::CursorIcon::Default,
1848 CursorIcon::Move => winit::cursor::CursorIcon::Move,
1849 CursorIcon::Text => winit::cursor::CursorIcon::Text,
1850 CursorIcon::Wait => winit::cursor::CursorIcon::Wait,
1851 CursorIcon::Help => winit::cursor::CursorIcon::Help,
1852 CursorIcon::Progress => winit::cursor::CursorIcon::Progress,
1853 CursorIcon::NotAllowed => winit::cursor::CursorIcon::NotAllowed,
1854 CursorIcon::ContextMenu => winit::cursor::CursorIcon::ContextMenu,
1855 CursorIcon::Cell => winit::cursor::CursorIcon::Cell,
1856 CursorIcon::VerticalText => winit::cursor::CursorIcon::VerticalText,
1857 CursorIcon::Alias => winit::cursor::CursorIcon::Alias,
1858 CursorIcon::Copy => winit::cursor::CursorIcon::Copy,
1859 CursorIcon::NoDrop => winit::cursor::CursorIcon::NoDrop,
1860 CursorIcon::Grab => winit::cursor::CursorIcon::Grab,
1861 CursorIcon::Grabbing => winit::cursor::CursorIcon::Grabbing,
1862 CursorIcon::AllScroll => winit::cursor::CursorIcon::AllScroll,
1863 CursorIcon::ZoomIn => winit::cursor::CursorIcon::ZoomIn,
1864 CursorIcon::ZoomOut => winit::cursor::CursorIcon::ZoomOut,
1865 CursorIcon::EResize => winit::cursor::CursorIcon::EResize,
1866 CursorIcon::NResize => winit::cursor::CursorIcon::NResize,
1867 CursorIcon::NeResize => winit::cursor::CursorIcon::NeResize,
1868 CursorIcon::NwResize => winit::cursor::CursorIcon::NwResize,
1869 CursorIcon::SResize => winit::cursor::CursorIcon::SResize,
1870 CursorIcon::SeResize => winit::cursor::CursorIcon::SeResize,
1871 CursorIcon::SwResize => winit::cursor::CursorIcon::SwResize,
1872 CursorIcon::WResize => winit::cursor::CursorIcon::WResize,
1873 CursorIcon::EwResize => winit::cursor::CursorIcon::EwResize,
1874 CursorIcon::NsResize => winit::cursor::CursorIcon::NsResize,
1875 CursorIcon::NeswResize => winit::cursor::CursorIcon::NeswResize,
1876 CursorIcon::NwseResize => winit::cursor::CursorIcon::NwseResize,
1877 CursorIcon::ColResize => winit::cursor::CursorIcon::ColResize,
1878 CursorIcon::RowResize => winit::cursor::CursorIcon::RowResize,
1879 _ => winit::cursor::CursorIcon::Default,
1880 }
1881}