1use inkview::event::Key;
2use inkview::screen::RGB24;
3use inkview::{screen::Screen, Event};
4use rgb::RGB;
5use slint::platform::{
6 software_renderer::{self as renderer, PhysicalRegion},
7 WindowEvent,
8};
9use std::{
10 cell::RefCell,
11 rc::Rc,
12 sync::mpsc::Receiver,
13 time::{Duration, Instant},
14};
15
16pub struct Backend {
17 screen: RefCell<Screen<'static>>,
18 evts: Receiver<Event>,
19 width: usize,
20 height: usize,
21 window: RefCell<Option<Rc<renderer::MinimalSoftwareWindow>>>,
22 buffer: RefCell<Vec<RGB<u8>>>,
23}
24
25impl Backend {
26 pub fn new(screen: Screen<'static>, evts: Receiver<Event>) -> Self {
27 let width = screen.width();
28 let height = screen.height();
29
30 let buffer = vec![Default::default(); width * height];
31
32 Self {
33 screen: screen.into(),
34 evts,
35 width,
36 height,
37 window: Default::default(),
38 buffer: buffer.into(),
39 }
40 }
41}
42
43fn rect_from_phys(r: PhysicalRegion) -> euclid::Rect<i32, euclid::UnknownUnit> {
44 euclid::Rect::new(
45 euclid::Point2D::new(r.bounding_box_origin().x, r.bounding_box_origin().y),
46 euclid::Size2D::new(
47 r.bounding_box_size().width as i32,
48 r.bounding_box_size().height as i32,
49 ),
50 )
51}
52
53fn scale_from_screen(screen: &Screen) -> f32 {
54 let dpi = screen.dpi() as f32 / 100.0;
55
56 return dpi * screen.scale();
57}
58
59impl slint::platform::Platform for Backend {
60 fn create_window_adapter(
61 &self,
62 ) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
63 let window =
64 renderer::MinimalSoftwareWindow::new(renderer::RepaintBufferType::ReusedBuffer);
65 self.window.replace(Some(window.clone()));
66 Ok(window)
67 }
68
69 fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
70 let scale_factor = scale_from_screen(&*self.screen.borrow());
71
72 let convert_evt = |evt| ink_evt_to_slint(scale_factor, evt);
73
74 slint::Window::set_size(
75 self.window.borrow().as_ref().unwrap().as_ref(),
76 slint::PhysicalSize::new(self.width as u32, self.height as u32)
77 .to_logical(scale_factor),
78 );
79
80 self.window
81 .borrow()
82 .as_ref()
83 .unwrap()
84 .dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor });
85
86 let mut fulfill_dynamic_updates_after: Option<Instant> = None;
88 let mut dynamic_region_to_redraw: Option<euclid::Rect<i32, euclid::UnknownUnit>> = None;
89 let mut accumulated_updates: Option<euclid::Rect<i32, euclid::UnknownUnit>> = None;
90 let mut last_draw_at = Instant::now();
91
92 loop {
93 slint::platform::update_timers_and_animations();
94
95 if let Some(window) = self.window.borrow().clone() {
96 let delay = if window.has_active_animations() {
97 None
98 } else {
99 match (
100 slint::platform::duration_until_next_timer_update(),
101 fulfill_dynamic_updates_after.map(|i| i.duration_since(Instant::now())),
102 ) {
103 (Some(a), Some(b)) => Some(a.min(b)),
104 (Some(a), None) => Some(a),
105 (_, b) => b,
106 }
107 };
108
109 let evt = if let Some(delay) = delay {
110 self.evts.recv_timeout(delay).ok().and_then(convert_evt)
111 } else if window.has_active_animations() {
112 self.evts.try_recv().ok().and_then(convert_evt)
113 } else {
114 self.evts.recv().ok().and_then(convert_evt)
115 };
116
117 if let Some(redraw_region) = dynamic_region_to_redraw {
118 if last_draw_at.elapsed() > Duration::from_millis(200) {
119 dynamic_region_to_redraw = None;
120 fulfill_dynamic_updates_after = None;
121
122 let mut screen = self.screen.borrow_mut();
123 screen.partial_update(
124 redraw_region.origin.x,
125 redraw_region.origin.y,
126 redraw_region.width() as u32,
127 redraw_region.height() as u32,
128 );
129 last_draw_at = Instant::now();
130 }
131 }
132
133 slint::platform::update_timers_and_animations();
134
135 if let Some(evt) = evt {
136 window.dispatch_event(evt);
137 }
138
139 window.draw_if_needed(|renderer| {
140 let mut buffer = self.buffer.borrow_mut();
141 let damage = renderer.render(buffer.as_mut_slice(), self.width);
142 let mut screen = self.screen.borrow_mut();
143
144 for dy in 0..damage.bounding_box_size().height {
145 for dx in 0..damage.bounding_box_size().width {
146 let x = damage.bounding_box_origin().x + dx as i32;
147 let y = damage.bounding_box_origin().y + dy as i32;
148 let idx = y as usize * self.width + x as usize;
149 let c = buffer[idx];
150 screen.draw(x as usize, y as usize, RGB24(c.r, c.g, c.b));
151 }
152 }
153
154 if screen.is_updating() {
157 if let Some(r) = accumulated_updates.as_mut() {
158 *r = r.union(&rect_from_phys(damage.clone()));
159 } else {
160 accumulated_updates = Some(rect_from_phys(damage.clone()));
161 }
162
163 if last_draw_at.elapsed() > Duration::from_millis(20) {
164 let redraw_region = accumulated_updates.take().unwrap();
165 screen.dynamic_update(
166 redraw_region.origin.x,
167 redraw_region.origin.y,
168 redraw_region.width() as u32,
169 redraw_region.height() as u32,
170 );
171 last_draw_at = Instant::now();
172 }
173
174 if let Some(r) = dynamic_region_to_redraw.as_mut() {
175 *r = r.union(&rect_from_phys(damage));
176 } else {
177 dynamic_region_to_redraw = Some(rect_from_phys(damage));
178 }
179
180 fulfill_dynamic_updates_after =
181 Some(Instant::now() + Duration::from_millis(200));
182 } else {
183 screen.partial_update(
184 damage.bounding_box_origin().x,
185 damage.bounding_box_origin().y,
186 damage.bounding_box_size().width,
187 damage.bounding_box_size().height,
188 );
189 last_draw_at = Instant::now();
190 }
191 });
192 }
193 }
194 }
195}
196
197fn ink_key_to_slint(key: Key) -> Option<slint::platform::Key> {
198 match key {
199 Key::Up => Some(slint::platform::Key::UpArrow),
200 Key::Down => Some(slint::platform::Key::DownArrow),
201 Key::Left => Some(slint::platform::Key::LeftArrow),
202 Key::Prev => Some(slint::platform::Key::LeftArrow),
203 Key::Prev2 => Some(slint::platform::Key::LeftArrow),
204 Key::Right => Some(slint::platform::Key::RightArrow),
205 Key::Next => Some(slint::platform::Key::RightArrow),
206 Key::Next2 => Some(slint::platform::Key::RightArrow),
207 Key::Ok => Some(slint::platform::Key::Return),
208 Key::Back => Some(slint::platform::Key::Backspace),
209 Key::Menu => Some(slint::platform::Key::Menu),
210 Key::Home => Some(slint::platform::Key::Home),
211 Key::Plus => Some(slint::platform::Key::PageUp),
212 Key::Minus => Some(slint::platform::Key::PageDown),
213 _ => None,
214 }
215}
216
217fn ink_evt_to_slint(scale_factor: f32, evt: Event) -> Option<WindowEvent> {
218 println!("evt: {:?}", evt);
219 let evt = match evt {
220 Event::PointerDown { x, y } => WindowEvent::PointerPressed {
221 position: slint::PhysicalPosition { x, y }.to_logical(scale_factor),
222 button: slint::platform::PointerEventButton::Left,
223 },
224 Event::PointerMove { x, y } => WindowEvent::PointerMoved {
225 position: slint::PhysicalPosition { x, y }.to_logical(scale_factor),
226 },
227 Event::PointerUp { x, y } => WindowEvent::PointerReleased {
228 position: slint::PhysicalPosition { x, y }.to_logical(scale_factor),
229 button: slint::platform::PointerEventButton::Left,
230 },
231 Event::Foreground { .. } => WindowEvent::WindowActiveChanged(true),
232 Event::Background { .. } => WindowEvent::WindowActiveChanged(false),
233 Event::KeyDown { key } => {
234 if let Some(slint_key) = ink_key_to_slint(key) {
235 WindowEvent::KeyPressed {
236 text: slint_key.into(),
237 }
238 } else {
239 return None;
240 }
241 }
242 Event::KeyRepeat { key } => {
243 if let Some(slint_key) = ink_key_to_slint(key) {
244 WindowEvent::KeyPressRepeated {
245 text: slint_key.into(),
246 }
247 } else {
248 return None;
249 }
250 }
251 Event::KeyUp { key } => {
252 if let Some(slint_key) = ink_key_to_slint(key) {
253 WindowEvent::KeyReleased {
254 text: slint_key.into(),
255 }
256 } else {
257 return None;
258 }
259 }
260 _ => return None,
261 };
262
263 Some(evt)
264}