i_slint_backend_linuxkms/
fullscreenwindowadapter.rs1use std::cell::Cell;
7use std::pin::Pin;
8use std::rc::Rc;
9
10use i_slint_core::api::{LogicalPosition, PhysicalSize as PhysicalWindowSize};
11use i_slint_core::graphics::{euclid, Image};
12use i_slint_core::item_rendering::ItemRenderer;
13use i_slint_core::lengths::LogicalRect;
14use i_slint_core::platform::WindowEvent;
15use i_slint_core::slice::Slice;
16use i_slint_core::Property;
17use i_slint_core::{platform::PlatformError, window::WindowAdapter};
18
19use crate::display::RenderingRotation;
20
21pub trait FullscreenRenderer {
22 fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer;
23 fn render_and_present(
24 &self,
25 rotation: RenderingRotation,
26 draw_mouse_cursor_callback: &dyn Fn(&mut dyn ItemRenderer),
27 ) -> Result<(), PlatformError>;
28 fn size(&self) -> PhysicalWindowSize;
29}
30
31pub struct FullscreenWindowAdapter {
32 window: i_slint_core::api::Window,
33 renderer: Box<dyn FullscreenRenderer>,
34 redraw_requested: Cell<bool>,
35 rotation: RenderingRotation,
36}
37
38impl WindowAdapter for FullscreenWindowAdapter {
39 fn window(&self) -> &i_slint_core::api::Window {
40 &self.window
41 }
42
43 fn size(&self) -> i_slint_core::api::PhysicalSize {
44 self.rotation.screen_size_to_rotated_window_size(self.renderer.size())
45 }
46
47 fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
48 self.renderer.as_core_renderer()
49 }
50
51 fn request_redraw(&self) {
52 self.redraw_requested.set(true)
53 }
54
55 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
56 if visible {
57 if let Some(scale_factor) =
58 std::env::var("SLINT_SCALE_FACTOR").ok().and_then(|sf| sf.parse().ok())
59 {
60 self.window.try_dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor })?;
61 }
62 }
63 Ok(())
64 }
65}
66
67impl FullscreenWindowAdapter {
68 pub fn new(
69 renderer: Box<dyn FullscreenRenderer>,
70 rotation: RenderingRotation,
71 ) -> Result<Rc<Self>, PlatformError> {
72 let size = renderer.size();
73 let rotation_degrees = rotation.degrees();
74 eprintln!(
75 "Rendering at {}x{}{}",
76 size.width,
77 size.height,
78 if rotation_degrees != 0. {
79 format!(" with {} rotation_degrees rotation", rotation_degrees)
80 } else {
81 String::new()
82 }
83 );
84 Ok(Rc::<FullscreenWindowAdapter>::new_cyclic(|self_weak| FullscreenWindowAdapter {
85 window: i_slint_core::api::Window::new(self_weak.clone()),
86 renderer,
87 redraw_requested: Cell::new(true),
88 rotation,
89 }))
90 }
91
92 pub fn render_if_needed(
93 self: Rc<Self>,
94 mouse_position: Pin<&Property<Option<LogicalPosition>>>,
95 ) -> Result<(), PlatformError> {
96 if self.redraw_requested.replace(false) {
97 self.renderer.render_and_present(self.rotation, &|item_renderer| {
98 if let Some(mouse_position) = mouse_position.get() {
99 let cursor_image = mouse_cursor_image();
100 item_renderer.save_state();
101 item_renderer.translate(
102 i_slint_core::lengths::logical_point_from_api(mouse_position).to_vector(),
103 );
104 item_renderer.draw_image_direct(mouse_cursor_image());
105 item_renderer.restore_state();
106 let cursor_rect = LogicalRect::new(
107 euclid::point2(mouse_position.x, mouse_position.y),
108 euclid::Size2D::from_untyped(cursor_image.size().cast()),
109 );
110 self.renderer.as_core_renderer().mark_dirty_region(cursor_rect.into());
111 }
112 })?;
113 if self.window.has_active_animations() {
118 let self_weak = Rc::downgrade(&self);
119 i_slint_core::timers::Timer::single_shot(
120 std::time::Duration::default(),
121 move || {
122 let Some(this) = self_weak.upgrade() else {
123 return;
124 };
125 this.request_redraw();
126 },
127 )
128 }
129 }
130 Ok(())
131 }
132}
133
134fn mouse_cursor_image() -> Image {
135 let mouse_pointer_svg = i_slint_core::graphics::load_image_from_embedded_data(
136 Slice::from_slice(include_bytes!("mouse-pointer.svg")),
137 Slice::from_slice(b"svg"),
138 );
139 let mouse_pointer_inner: &i_slint_core::graphics::ImageInner = (&mouse_pointer_svg).into();
140 match mouse_pointer_inner {
141 i_slint_core::ImageInner::Svg(svg) => {
142 let pixels = svg.render(None).unwrap();
143 let cache_key = svg.cache_key();
144 let mouse_pointer_pixel_image = i_slint_core::graphics::ImageInner::EmbeddedImage {
145 cache_key: cache_key.clone(),
146 buffer: pixels,
147 };
148 i_slint_core::graphics::cache::replace_cached_image(
149 cache_key,
150 mouse_pointer_pixel_image.clone(),
151 );
152
153 mouse_pointer_pixel_image.into()
154 }
155 cached_image @ _ => cached_image.clone().into(),
156 }
157}