Skip to main content

inkview_slint/
lib.rs

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        // bad naming, oops
87        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                    // println!("Drawing to: {:?}", damage);
155
156                    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}