1#![doc = include_str!("README.md")]
5#![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")]
6#![warn(missing_docs)]
7
8extern crate alloc;
9
10use event_loop::{CustomEvent, EventLoopState};
11use i_slint_core::api::EventLoopError;
12use i_slint_core::graphics::RequestedGraphicsAPI;
13use i_slint_core::platform::{EventLoopProxy, PlatformError};
14use i_slint_core::window::WindowAdapter;
15use renderer::WinitCompatibleRenderer;
16use std::cell::RefCell;
17use std::collections::HashMap;
18use std::rc::Rc;
19use std::rc::Weak;
20use std::sync::Arc;
21use winit::event_loop::ActiveEventLoop;
22
23#[cfg(not(target_arch = "wasm32"))]
24mod clipboard;
25mod drag_resize_window;
26mod winitwindowadapter;
27use winitwindowadapter::*;
28pub(crate) mod event_loop;
29mod frame_throttle;
30
31pub use winit;
33
34#[non_exhaustive]
38#[derive(Debug)]
39pub struct SlintEvent(CustomEvent);
40
41#[i_slint_core_macros::slint_doc]
42pub type EventLoopBuilder = winit::event_loop::EventLoopBuilder<SlintEvent>;
47
48pub enum EventResult {
51 Propagate,
53 PreventDefault,
55}
56
57mod renderer {
58 use std::sync::Arc;
59
60 use i_slint_core::platform::PlatformError;
61 use winit::event_loop::ActiveEventLoop;
62
63 pub trait WinitCompatibleRenderer {
64 fn render(&self, window: &i_slint_core::api::Window) -> Result<(), PlatformError>;
65
66 fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer;
67 fn occluded(&self, _: bool) {}
69
70 fn suspend(&self) -> Result<(), PlatformError>;
71
72 fn resume(
74 &self,
75 active_event_loop: &ActiveEventLoop,
76 window_attributes: winit::window::WindowAttributes,
77 ) -> Result<Arc<winit::window::Window>, PlatformError>;
78 }
79
80 #[cfg(any(
81 all(feature = "renderer-femtovg", supports_opengl),
82 feature = "renderer-femtovg-wgpu"
83 ))]
84 pub(crate) mod femtovg;
85 #[cfg(enable_skia_renderer)]
86 pub(crate) mod skia;
87
88 #[cfg(feature = "renderer-software")]
89 pub(crate) mod sw;
90}
91
92#[cfg(enable_accesskit)]
93mod accesskit;
94#[cfg(muda)]
95mod muda;
96#[cfg(not(use_winit_theme))]
97mod xdg_color_scheme;
98
99#[cfg(target_arch = "wasm32")]
100pub(crate) mod wasm_input_helper;
101
102cfg_if::cfg_if! {
103 if #[cfg(enable_femtovg_renderer)] {
104 const DEFAULT_RENDERER_NAME: &str = "FemtoVG";
105 } else if #[cfg(enable_skia_renderer)] {
106 const DEFAULT_RENDERER_NAME: &'static str = "Skia";
107 } else if #[cfg(feature = "renderer-software")] {
108 const DEFAULT_RENDERER_NAME: &'static str = "Software";
109 } else {
110 compile_error!("Please select a feature to build with the winit backend: `renderer-femtovg`, `renderer-skia`, `renderer-skia-opengl`, `renderer-skia-vulkan` or `renderer-software`");
111 }
112}
113
114fn default_renderer_factory(
115 shared_backend_data: &Rc<SharedBackendData>,
116) -> Result<Box<dyn WinitCompatibleRenderer>, PlatformError> {
117 cfg_if::cfg_if! {
118 if #[cfg(enable_skia_renderer)] {
119 renderer::skia::WinitSkiaRenderer::new_suspended(shared_backend_data)
120 } else if #[cfg(feature = "renderer-femtovg-wgpu")] {
121 renderer::femtovg::WGPUFemtoVGRenderer::new_suspended(shared_backend_data)
122 } else if #[cfg(all(feature = "renderer-femtovg", supports_opengl))] {
123 renderer::femtovg::GlutinFemtoVGRenderer::new_suspended(shared_backend_data)
124 } else if #[cfg(feature = "renderer-software")] {
125 renderer::sw::WinitSoftwareRenderer::new_suspended(shared_backend_data)
126 } else {
127 compile_error!("Please select a feature to build with the winit backend: `renderer-femtovg`, `renderer-skia`, `renderer-skia-opengl`, `renderer-skia-vulkan` or `renderer-software`");
128 }
129 }
130}
131
132fn try_create_window_with_fallback_renderer(
133 shared_backend_data: &Rc<SharedBackendData>,
134 attrs: winit::window::WindowAttributes,
135 _proxy: &winit::event_loop::EventLoopProxy<SlintEvent>,
136 #[cfg(all(muda, target_os = "macos"))] muda_enable_default_menu_bar: bool,
137) -> Option<Rc<WinitWindowAdapter>> {
138 [
139 #[cfg(any(
140 feature = "renderer-skia",
141 feature = "renderer-skia-opengl",
142 feature = "renderer-skia-vulkan"
143 ))]
144 renderer::skia::WinitSkiaRenderer::new_suspended,
145 #[cfg(feature = "renderer-femtovg-wgpu")]
146 renderer::femtovg::WGPUFemtoVGRenderer::new_suspended,
147 #[cfg(all(
148 feature = "renderer-femtovg",
149 supports_opengl,
150 not(feature = "renderer-femtovg-wgpu")
151 ))]
152 renderer::femtovg::GlutinFemtoVGRenderer::new_suspended,
153 #[cfg(feature = "renderer-software")]
154 renderer::sw::WinitSoftwareRenderer::new_suspended,
155 ]
156 .into_iter()
157 .find_map(|renderer_factory| {
158 Some(WinitWindowAdapter::new(
159 shared_backend_data.clone(),
160 renderer_factory(&shared_backend_data).ok()?,
161 attrs.clone(),
162 #[cfg(any(enable_accesskit, muda))]
163 _proxy.clone(),
164 #[cfg(all(muda, target_os = "macos"))]
165 muda_enable_default_menu_bar,
166 ))
167 })
168}
169
170#[doc(hidden)]
171pub type NativeWidgets = ();
172#[doc(hidden)]
173pub type NativeGlobals = ();
174#[doc(hidden)]
175pub const HAS_NATIVE_STYLE: bool = false;
176#[doc(hidden)]
177pub mod native_widgets {}
178
179#[allow(unused_variables)]
187pub trait CustomApplicationHandler {
188 fn resumed(&mut self, _event_loop: &ActiveEventLoop) -> EventResult {
190 EventResult::Propagate
191 }
192
193 fn window_event(
195 &mut self,
196 event_loop: &ActiveEventLoop,
197 window_id: winit::window::WindowId,
198 winit_window: Option<&winit::window::Window>,
199 slint_window: Option<&i_slint_core::api::Window>,
200 event: &winit::event::WindowEvent,
201 ) -> EventResult {
202 EventResult::Propagate
203 }
204
205 fn new_events(
207 &mut self,
208 event_loop: &ActiveEventLoop,
209 cause: winit::event::StartCause,
210 ) -> EventResult {
211 EventResult::Propagate
212 }
213
214 fn device_event(
216 &mut self,
217 event_loop: &ActiveEventLoop,
218 device_id: winit::event::DeviceId,
219 event: winit::event::DeviceEvent,
220 ) -> EventResult {
221 EventResult::Propagate
222 }
223
224 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) -> EventResult {
226 EventResult::Propagate
227 }
228
229 fn suspended(&mut self, event_loop: &ActiveEventLoop) -> EventResult {
231 EventResult::Propagate
232 }
233
234 fn exiting(&mut self, event_loop: &ActiveEventLoop) -> EventResult {
236 EventResult::Propagate
237 }
238
239 fn memory_warning(&mut self, event_loop: &ActiveEventLoop) -> EventResult {
241 EventResult::Propagate
242 }
243}
244
245pub struct BackendBuilder {
249 allow_fallback: bool,
250 requested_graphics_api: Option<RequestedGraphicsAPI>,
251 window_attributes_hook:
252 Option<Box<dyn Fn(winit::window::WindowAttributes) -> winit::window::WindowAttributes>>,
253 renderer_name: Option<String>,
254 event_loop_builder: Option<EventLoopBuilder>,
255 #[cfg(all(muda, target_os = "macos"))]
256 muda_enable_default_menu_bar_bar: bool,
257 #[cfg(target_family = "wasm")]
258 spawn_event_loop: bool,
259 custom_application_handler: Option<Box<dyn CustomApplicationHandler>>,
260}
261
262impl BackendBuilder {
263 #[must_use]
265 pub fn request_graphics_api(mut self, graphics_api: RequestedGraphicsAPI) -> Self {
266 self.requested_graphics_api = Some(graphics_api);
267 self
268 }
269
270 #[must_use]
273 pub fn with_renderer_name(mut self, name: impl Into<String>) -> Self {
274 self.renderer_name = Some(name.into());
275 self
276 }
277
278 #[must_use]
292 pub fn with_window_attributes_hook(
293 mut self,
294 hook: impl Fn(winit::window::WindowAttributes) -> winit::window::WindowAttributes + 'static,
295 ) -> Self {
296 self.window_attributes_hook = Some(Box::new(hook));
297 self
298 }
299
300 #[must_use]
303 pub fn with_event_loop_builder(mut self, event_loop_builder: EventLoopBuilder) -> Self {
304 self.event_loop_builder = Some(event_loop_builder);
305 self
306 }
307
308 #[must_use]
314 #[cfg(all(muda, target_os = "macos"))]
315 pub fn with_default_menu_bar(mut self, enable: bool) -> Self {
316 self.muda_enable_default_menu_bar_bar = enable;
317 self
318 }
319
320 #[cfg(target_family = "wasm")]
321 pub fn with_spawn_event_loop(mut self, enable: bool) -> Self {
324 self.spawn_event_loop = enable;
325 self
326 }
327
328 #[must_use]
333 pub fn with_custom_application_handler(
334 mut self,
335 handler: Box<dyn CustomApplicationHandler + 'static>,
336 ) -> Self {
337 self.custom_application_handler = Some(handler);
338 self
339 }
340
341 pub fn build(self) -> Result<Backend, PlatformError> {
354 #[allow(unused_mut)]
355 let mut event_loop_builder =
356 self.event_loop_builder.unwrap_or_else(winit::event_loop::EventLoop::with_user_event);
357
358 #[cfg(all(feature = "muda", target_os = "macos"))]
361 winit::platform::macos::EventLoopBuilderExtMacOS::with_default_menu(
362 &mut event_loop_builder,
363 false,
364 );
365
366 let shared_data = Rc::new(SharedBackendData::new(
369 event_loop_builder,
370 self.requested_graphics_api.clone(),
371 )?);
372
373 let renderer_factory_fn = match (
374 self.renderer_name.as_deref(),
375 self.requested_graphics_api.as_ref(),
376 ) {
377 #[cfg(all(feature = "renderer-femtovg", supports_opengl))]
378 (Some("gl"), maybe_graphics_api) | (Some("femtovg"), maybe_graphics_api) => {
379 if let Some(api) = maybe_graphics_api {
381 i_slint_core::graphics::RequestedOpenGLVersion::try_from(api)?;
382 }
383 renderer::femtovg::GlutinFemtoVGRenderer::new_suspended
384 }
385 #[cfg(feature = "renderer-femtovg-wgpu")]
386 (Some("femtovg-wgpu"), maybe_graphics_api) => {
387 if !maybe_graphics_api.is_some_and(|_api| {
388 #[cfg(feature = "unstable-wgpu-27")]
389 if matches!(_api, RequestedGraphicsAPI::WGPU27(..)) {
390 return true;
391 }
392 false
393 }) {
394 return Err(
395 "The FemtoVG WGPU renderer only supports the WGPU27 graphics API selection"
396 .into(),
397 );
398 }
399 renderer::femtovg::WGPUFemtoVGRenderer::new_suspended
400 }
401 #[cfg(enable_skia_renderer)]
402 (Some("skia"), maybe_graphics_api) => {
403 renderer::skia::WinitSkiaRenderer::factory_for_graphics_api(maybe_graphics_api)?
404 }
405 #[cfg(all(enable_skia_renderer, supports_opengl))]
406 (Some("skia-opengl"), maybe_graphics_api @ _) => {
407 if let Some(api) = maybe_graphics_api {
409 i_slint_core::graphics::RequestedOpenGLVersion::try_from(api)?;
410 }
411 renderer::skia::WinitSkiaRenderer::new_opengl_suspended
412 }
413 #[cfg(all(
414 enable_skia_renderer,
415 any(feature = "unstable-wgpu-26", feature = "unstable-wgpu-27")
416 ))]
417 (Some("skia-wgpu"), maybe_graphics_api @ _) => {
418 if let Some(factory) = maybe_graphics_api.map_or_else(
419 || {
420 let result;
421 cfg_if::cfg_if!(
422 if #[cfg(feature = "unstable-wgpu-27")]
423 {
424 result = Some(
425 renderer::skia::WinitSkiaRenderer::new_wgpu_27_suspended
426 as RendererFactoryFn,
427 );
428 } else {
429 result = Some(
430 renderer::skia::WinitSkiaRenderer::new_wgpu_26_suspended
431 as RendererFactoryFn,
432 );
433 }
434 );
435 result
436 },
437 |api| {
438 #[cfg(feature = "unstable-wgpu-26")]
439 if matches!(api, RequestedGraphicsAPI::WGPU26(..)) {
440 return Some(
441 renderer::skia::WinitSkiaRenderer::new_wgpu_26_suspended
442 as RendererFactoryFn,
443 );
444 }
445 #[cfg(feature = "unstable-wgpu-27")]
446 if matches!(api, RequestedGraphicsAPI::WGPU27(..)) {
447 return Some(
448 renderer::skia::WinitSkiaRenderer::new_wgpu_27_suspended
449 as RendererFactoryFn,
450 );
451 }
452 None
453 },
454 ) {
455 factory
456 } else {
457 return Err(
458 format!("Skia with WGPU doesn't support non-WGPU graphics API").into()
459 );
460 }
461 }
462 #[cfg(all(enable_skia_renderer, not(target_os = "android")))]
463 (Some("skia-software"), None) => {
464 renderer::skia::WinitSkiaRenderer::new_software_suspended
465 }
466 #[cfg(feature = "renderer-software")]
467 (Some("sw"), None) | (Some("software"), None) => {
468 renderer::sw::WinitSoftwareRenderer::new_suspended
469 }
470 (None, None) => default_renderer_factory,
471 (Some(renderer_name), _) => {
472 if self.allow_fallback {
473 eprintln!(
474 "slint winit: unrecognized renderer {renderer_name}, falling back to {DEFAULT_RENDERER_NAME}"
475 );
476 default_renderer_factory
477 } else {
478 return Err(PlatformError::NoPlatform);
479 }
480 }
481 #[cfg(feature = "unstable-wgpu-26")]
482 (None, Some(RequestedGraphicsAPI::WGPU26(..))) => {
483 renderer::skia::WinitSkiaRenderer::new_wgpu_26_suspended
484 }
485 #[cfg(feature = "unstable-wgpu-27")]
486 (None, Some(RequestedGraphicsAPI::WGPU27(..))) => {
487 cfg_if::cfg_if! {
488 if #[cfg(enable_skia_renderer)] {
489 renderer::skia::WinitSkiaRenderer::new_wgpu_27_suspended
490 } else {
491 renderer::femtovg::WGPUFemtoVGRenderer::new_suspended
492 }
493 }
494 }
495 (None, Some(_requested_graphics_api)) => {
496 cfg_if::cfg_if! {
497 if #[cfg(enable_skia_renderer)] {
498 renderer::skia::WinitSkiaRenderer::factory_for_graphics_api(Some(_requested_graphics_api))?
499 } else if #[cfg(all(feature = "renderer-femtovg", supports_opengl))] {
500 i_slint_core::graphics::RequestedOpenGLVersion::try_from(_requested_graphics_api)?;
502 renderer::femtovg::GlutinFemtoVGRenderer::new_suspended
503 } else {
504 return Err(format!("Graphics API use requested by the compile-time enabled renderers don't support that").into())
505 }
506 }
507 }
508 };
509
510 Ok(Backend {
511 renderer_factory_fn,
512 event_loop_state: Default::default(),
513 window_attributes_hook: self.window_attributes_hook,
514 shared_data,
515 #[cfg(all(muda, target_os = "macos"))]
516 muda_enable_default_menu_bar_bar: self.muda_enable_default_menu_bar_bar,
517 #[cfg(target_family = "wasm")]
518 spawn_event_loop: self.spawn_event_loop,
519 custom_application_handler: self.custom_application_handler.into(),
520 })
521 }
522}
523
524pub(crate) struct SharedBackendData {
525 _requested_graphics_api: Option<RequestedGraphicsAPI>,
526 #[cfg(enable_skia_renderer)]
527 skia_context: i_slint_renderer_skia::SkiaSharedContext,
528 active_windows: RefCell<HashMap<winit::window::WindowId, Weak<WinitWindowAdapter>>>,
529 inactive_windows: RefCell<Vec<Weak<WinitWindowAdapter>>>,
532 #[cfg(not(target_arch = "wasm32"))]
533 clipboard: std::cell::RefCell<clipboard::ClipboardPair>,
534 not_running_event_loop: RefCell<Option<winit::event_loop::EventLoop<SlintEvent>>>,
535 event_loop_proxy: winit::event_loop::EventLoopProxy<SlintEvent>,
536 is_wayland: bool,
537}
538
539impl SharedBackendData {
540 fn new(
541 mut builder: EventLoopBuilder,
542 requested_graphics_api: Option<RequestedGraphicsAPI>,
543 ) -> Result<Self, PlatformError> {
544 #[cfg(not(target_arch = "wasm32"))]
545 use raw_window_handle::HasDisplayHandle;
546
547 #[cfg(all(unix, not(target_vendor = "apple")))]
548 {
549 #[cfg(feature = "wayland")]
550 {
551 use winit::platform::wayland::EventLoopBuilderExtWayland;
552 builder.with_any_thread(true);
553 }
554 #[cfg(feature = "x11")]
555 {
556 use winit::platform::x11::EventLoopBuilderExtX11;
557 builder.with_any_thread(true);
558
559 #[cfg(feature = "wayland")]
563 if std::fs::metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok()
564 || std::fs::metadata("/run/WSL").is_ok()
565 {
566 builder.with_x11();
567 }
568 }
569 }
570 #[cfg(target_family = "windows")]
571 {
572 use winit::platform::windows::EventLoopBuilderExtWindows;
573 builder.with_any_thread(true);
574 }
575
576 let event_loop =
577 builder.build().map_err(|e| format!("Error initializing winit event loop: {e}"))?;
578
579 cfg_if::cfg_if! {
580 if #[cfg(all(unix, not(target_vendor = "apple"), feature = "wayland"))] {
581 use winit::platform::wayland::EventLoopExtWayland;
582 let is_wayland = event_loop.is_wayland();
583 } else {
584 let is_wayland = false;
585 }
586 }
587
588 let event_loop_proxy = event_loop.create_proxy();
589 #[cfg(not(target_arch = "wasm32"))]
590 let clipboard = crate::clipboard::create_clipboard(
591 &event_loop
592 .display_handle()
593 .map_err(|display_err| PlatformError::OtherError(display_err.into()))?,
594 );
595 Ok(Self {
596 _requested_graphics_api: requested_graphics_api,
597 #[cfg(enable_skia_renderer)]
598 skia_context: i_slint_renderer_skia::SkiaSharedContext::default(),
599 active_windows: Default::default(),
600 inactive_windows: Default::default(),
601 #[cfg(not(target_arch = "wasm32"))]
602 clipboard: RefCell::new(clipboard),
603 not_running_event_loop: RefCell::new(Some(event_loop)),
604 event_loop_proxy,
605 is_wayland,
606 })
607 }
608
609 pub fn register_window(&self, id: winit::window::WindowId, window: Rc<WinitWindowAdapter>) {
610 self.active_windows.borrow_mut().insert(id, Rc::downgrade(&window));
611 }
612
613 pub fn register_inactive_window(&self, window: Rc<WinitWindowAdapter>) {
614 let window = Rc::downgrade(&window);
615 let mut inactive_windows = self.inactive_windows.borrow_mut();
616 if !inactive_windows.iter().any(|w| Weak::ptr_eq(w, &window)) {
617 inactive_windows.push(window);
618 }
619 }
620
621 pub fn unregister_window(&self, id: Option<winit::window::WindowId>) {
622 if let Some(id) = id {
623 self.active_windows.borrow_mut().remove(&id);
624 } else {
625 self.inactive_windows
627 .borrow_mut()
628 .retain(|inactive_weak_window| inactive_weak_window.strong_count() > 0)
629 }
630 }
631
632 pub fn create_inactive_windows(
633 &self,
634 event_loop: &winit::event_loop::ActiveEventLoop,
635 ) -> Result<(), PlatformError> {
636 let mut inactive_windows = self.inactive_windows.take();
637 let mut result = Ok(());
638 while let Some(window_weak) = inactive_windows.pop() {
639 if let Some(err) = window_weak.upgrade().and_then(|w| w.ensure_window(event_loop).err())
640 {
641 result = Err(err);
642 break;
643 }
644 }
645 self.inactive_windows.borrow_mut().extend(inactive_windows);
646 result
647 }
648
649 pub fn window_by_id(&self, id: winit::window::WindowId) -> Option<Rc<WinitWindowAdapter>> {
650 self.active_windows.borrow().get(&id).and_then(|weakref| weakref.upgrade())
651 }
652}
653
654type RendererFactoryFn =
655 fn(&Rc<SharedBackendData>) -> Result<Box<dyn WinitCompatibleRenderer>, PlatformError>;
656
657#[i_slint_core_macros::slint_doc]
658pub struct Backend {
667 renderer_factory_fn: RendererFactoryFn,
668 event_loop_state: RefCell<Option<crate::event_loop::EventLoopState>>,
669 shared_data: Rc<SharedBackendData>,
670 custom_application_handler: RefCell<Option<Box<dyn crate::CustomApplicationHandler>>>,
671
672 pub window_attributes_hook:
686 Option<Box<dyn Fn(winit::window::WindowAttributes) -> winit::window::WindowAttributes>>,
687
688 #[cfg(all(muda, target_os = "macos"))]
689 muda_enable_default_menu_bar_bar: bool,
690
691 #[cfg(target_family = "wasm")]
692 spawn_event_loop: bool,
693}
694
695impl Backend {
696 #[i_slint_core_macros::slint_doc]
697 pub fn new() -> Result<Self, PlatformError> {
701 Self::builder().build()
702 }
703
704 #[i_slint_core_macros::slint_doc]
705 pub fn new_with_renderer_by_name(renderer_name: Option<&str>) -> Result<Self, PlatformError> {
711 let mut builder = Self::builder();
712 if let Some(name) = renderer_name {
713 builder = builder.with_renderer_name(name.to_string());
714 }
715 builder.build()
716 }
717
718 pub fn builder() -> BackendBuilder {
721 BackendBuilder {
722 allow_fallback: true,
723 requested_graphics_api: None,
724 window_attributes_hook: None,
725 renderer_name: None,
726 event_loop_builder: None,
727 #[cfg(all(muda, target_os = "macos"))]
728 muda_enable_default_menu_bar_bar: true,
729 #[cfg(target_family = "wasm")]
730 spawn_event_loop: false,
731 custom_application_handler: None,
732 }
733 }
734}
735
736impl i_slint_core::platform::Platform for Backend {
737 fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
738 let mut attrs = WinitWindowAdapter::window_attributes()?;
739
740 if let Some(hook) = &self.window_attributes_hook {
741 attrs = hook(attrs);
742 }
743
744 let adapter = (self.renderer_factory_fn)(&self.shared_data).map_or_else(
745 |e| {
746 try_create_window_with_fallback_renderer(
747 &self.shared_data,
748 attrs.clone(),
749 &self.shared_data.event_loop_proxy.clone(),
750 #[cfg(all(muda, target_os = "macos"))]
751 self.muda_enable_default_menu_bar_bar,
752 )
753 .ok_or_else(|| format!("Winit backend failed to find a suitable renderer: {e}"))
754 },
755 |renderer| {
756 Ok(WinitWindowAdapter::new(
757 self.shared_data.clone(),
758 renderer,
759 attrs.clone(),
760 #[cfg(any(enable_accesskit, muda))]
761 self.shared_data.event_loop_proxy.clone(),
762 #[cfg(all(muda, target_os = "macos"))]
763 self.muda_enable_default_menu_bar_bar,
764 ))
765 },
766 )?;
767 Ok(adapter)
768 }
769
770 fn run_event_loop(&self) -> Result<(), PlatformError> {
771 let loop_state = self.event_loop_state.borrow_mut().take().unwrap_or_else(|| {
772 EventLoopState::new(self.shared_data.clone(), self.custom_application_handler.take())
773 });
774 #[cfg(target_family = "wasm")]
775 {
776 if self.spawn_event_loop {
777 return loop_state.spawn();
778 }
779 }
780 let new_state = loop_state.run()?;
781 *self.event_loop_state.borrow_mut() = Some(new_state);
782 Ok(())
783 }
784
785 #[cfg(all(not(target_arch = "wasm32"), not(ios_and_friends)))]
786 fn process_events(
787 &self,
788 timeout: core::time::Duration,
789 _: i_slint_core::InternalToken,
790 ) -> Result<core::ops::ControlFlow<()>, PlatformError> {
791 let loop_state = self.event_loop_state.borrow_mut().take().unwrap_or_else(|| {
792 EventLoopState::new(self.shared_data.clone(), self.custom_application_handler.take())
793 });
794 let (new_state, status) = loop_state.pump_events(Some(timeout))?;
795 *self.event_loop_state.borrow_mut() = Some(new_state);
796 match status {
797 winit::platform::pump_events::PumpStatus::Continue => {
798 Ok(core::ops::ControlFlow::Continue(()))
799 }
800 winit::platform::pump_events::PumpStatus::Exit(code) => {
801 if code == 0 {
802 Ok(core::ops::ControlFlow::Break(()))
803 } else {
804 Err(format!("Event loop exited with non-zero code {code}").into())
805 }
806 }
807 }
808 }
809
810 fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
811 struct Proxy(winit::event_loop::EventLoopProxy<SlintEvent>);
812 impl EventLoopProxy for Proxy {
813 fn quit_event_loop(&self) -> Result<(), EventLoopError> {
814 self.0
815 .send_event(SlintEvent(CustomEvent::Exit))
816 .map_err(|_| EventLoopError::EventLoopTerminated)
817 }
818
819 fn invoke_from_event_loop(
820 &self,
821 event: Box<dyn FnOnce() + Send>,
822 ) -> Result<(), EventLoopError> {
823 #[cfg(target_arch = "wasm32")]
833 self.0
834 .send_event(SlintEvent(CustomEvent::WakeEventLoopWorkaround))
835 .map_err(|_| EventLoopError::EventLoopTerminated)?;
836
837 self.0
838 .send_event(SlintEvent(CustomEvent::UserEvent(event)))
839 .map_err(|_| EventLoopError::EventLoopTerminated)
840 }
841 }
842 Some(Box::new(Proxy(self.shared_data.event_loop_proxy.clone())))
843 }
844
845 #[cfg(target_arch = "wasm32")]
846 fn set_clipboard_text(&self, text: &str, clipboard: i_slint_core::platform::Clipboard) {
847 crate::wasm_input_helper::set_clipboard_text(text.into(), clipboard);
848 }
849
850 #[cfg(not(target_arch = "wasm32"))]
851 fn set_clipboard_text(&self, text: &str, clipboard: i_slint_core::platform::Clipboard) {
852 let mut pair = self.shared_data.clipboard.borrow_mut();
853 if let Some(clipboard) = clipboard::select_clipboard(&mut pair, clipboard) {
854 clipboard.set_contents(text.into()).ok();
855 }
856 }
857
858 #[cfg(target_arch = "wasm32")]
859 fn clipboard_text(&self, clipboard: i_slint_core::platform::Clipboard) -> Option<String> {
860 crate::wasm_input_helper::get_clipboard_text(clipboard)
861 }
862
863 #[cfg(not(target_arch = "wasm32"))]
864 fn clipboard_text(&self, clipboard: i_slint_core::platform::Clipboard) -> Option<String> {
865 let mut pair = self.shared_data.clipboard.borrow_mut();
866 clipboard::select_clipboard(&mut pair, clipboard).and_then(|c| c.get_contents().ok())
867 }
868}
869
870mod private {
871 pub trait WinitWindowAccessorSealed {}
872}
873
874#[i_slint_core_macros::slint_doc]
875pub trait WinitWindowAccessor: private::WinitWindowAccessorSealed {
888 fn has_winit_window(&self) -> bool;
891 fn with_winit_window<T>(&self, callback: impl FnOnce(&winit::window::Window) -> T)
894 -> Option<T>;
895 fn on_winit_window_event(
903 &self,
904 callback: impl FnMut(&i_slint_core::api::Window, &winit::event::WindowEvent) -> EventResult
905 + 'static,
906 );
907
908 fn winit_window(
951 &self,
952 ) -> impl std::future::Future<Output = Result<Arc<winit::window::Window>, PlatformError>>;
953}
954
955impl WinitWindowAccessor for i_slint_core::api::Window {
956 fn has_winit_window(&self) -> bool {
957 i_slint_core::window::WindowInner::from_pub(self)
958 .window_adapter()
959 .internal(i_slint_core::InternalToken)
960 .and_then(|wa| wa.as_any().downcast_ref::<WinitWindowAdapter>())
961 .is_some_and(|adapter| adapter.winit_window().is_some())
962 }
963
964 fn with_winit_window<T>(
965 &self,
966 callback: impl FnOnce(&winit::window::Window) -> T,
967 ) -> Option<T> {
968 i_slint_core::window::WindowInner::from_pub(self)
969 .window_adapter()
970 .internal(i_slint_core::InternalToken)
971 .and_then(|wa| wa.as_any().downcast_ref::<WinitWindowAdapter>())
972 .and_then(|adapter| adapter.winit_window().map(|w| callback(&w)))
973 }
974
975 fn winit_window(
976 &self,
977 ) -> impl std::future::Future<Output = Result<Arc<winit::window::Window>, PlatformError>> {
978 Box::pin(async move {
979 let adapter_weak = i_slint_core::window::WindowInner::from_pub(self)
980 .window_adapter()
981 .internal(i_slint_core::InternalToken)
982 .and_then(|wa| wa.as_any().downcast_ref::<WinitWindowAdapter>())
983 .map(|wa| wa.self_weak.clone())
984 .ok_or_else(|| {
985 PlatformError::OtherError(
986 format!("Slint window is not backed by a Winit window adapter").into(),
987 )
988 })?;
989 WinitWindowAdapter::async_winit_window(adapter_weak).await
990 })
991 }
992
993 fn on_winit_window_event(
994 &self,
995 mut callback: impl FnMut(&i_slint_core::api::Window, &winit::event::WindowEvent) -> EventResult
996 + 'static,
997 ) {
998 if let Some(adapter) = i_slint_core::window::WindowInner::from_pub(self)
999 .window_adapter()
1000 .internal(i_slint_core::InternalToken)
1001 .and_then(|wa| wa.as_any().downcast_ref::<WinitWindowAdapter>())
1002 {
1003 adapter
1004 .window_event_filter
1005 .set(Some(Box::new(move |window, event| callback(window, event))));
1006 }
1007 }
1008}
1009
1010impl private::WinitWindowAccessorSealed for i_slint_core::api::Window {}
1011
1012#[cfg(test)]
1013mod testui {
1014 slint::slint! {
1015 export component App inherits Window {
1016 Text { text: "Ok"; }
1017 }
1018 }
1019}
1020
1021#[cfg(not(any(target_arch = "wasm32", target_vendor = "apple")))]
1023#[test]
1024fn test_window_accessor_and_rwh() {
1025 slint::platform::set_platform(Box::new(crate::Backend::new().unwrap())).unwrap();
1026
1027 use testui::*;
1028
1029 slint::spawn_local(async move {
1030 let app = App::new().unwrap();
1031 let slint_window = app.window();
1032
1033 assert!(!slint_window.has_winit_window());
1034
1035 app.show().unwrap();
1038
1039 let result = slint_window.winit_window().await;
1040 assert!(result.is_ok(), "Failed to get winit window: {:?}", result.err());
1041 assert!(slint_window.has_winit_window());
1042 let handle = slint_window.window_handle();
1043 use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
1044 assert!(handle.window_handle().is_ok());
1045 assert!(handle.display_handle().is_ok());
1046 slint::quit_event_loop().unwrap();
1047 })
1048 .unwrap();
1049
1050 slint::run_event_loop().unwrap();
1051}