#![allow(clippy::borrow_as_ptr)] mod framebuffer; mod input; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use evdev::KeyCode; use slint::platform::software_renderer::{MinimalSoftwareWindow, RepaintBufferType, Rgb565Pixel}; use slint::platform::{EventLoopProxy, Platform, WindowEvent}; use slint::{EventLoopError, LogicalPosition, PhysicalSize, PlatformError, WindowSize}; use self::framebuffer::Framebuffer; use self::input::InputState; pub struct Trekstor { window: Rc, queue: Queue, quit_flag: Arc, } impl Trekstor { pub fn new() -> Self { Self::default() } } impl Default for Trekstor { fn default() -> Self { let window = MinimalSoftwareWindow::new(RepaintBufferType::NewBuffer); Self { window, queue: Arc::new(Mutex::new(Vec::new())), quit_flag: Arc::new(AtomicBool::new(false)), } } } type Closure = Box; type Queue = Arc>>; struct TrekstorEventProxy { queue: Queue, quit_flag: Arc, } impl EventLoopProxy for TrekstorEventProxy { fn quit_event_loop(&self) -> Result<(), EventLoopError> { self.quit_flag.store(true, Ordering::SeqCst); Ok(()) } fn invoke_from_event_loop(&self, event: Closure) -> Result<(), EventLoopError> { // Don't queue if the loop is shutting down, the closure would never // run, and any caller blocking on a channel send inside it would hang. if self.quit_flag.load(Ordering::SeqCst) { return Err(EventLoopError::EventLoopTerminated); } self.queue .lock() .expect("event loop closure queue poisoned") .push(event); Ok(()) } } impl Platform for Trekstor { fn create_window_adapter( &self, ) -> Result, slint::PlatformError> { Ok(self.window.clone()) } fn run_event_loop(&self) -> Result<(), slint::PlatformError> { let mut frame_buffer = Framebuffer::open() .map_err(|e| PlatformError::Other(format!("failed to open /dev/fb0: {e}")))?; let mut input = InputState::new(frame_buffer.width, frame_buffer.height); #[allow(clippy::large_stack_arrays)] // causes stack overflow if rgba8 let mut buffer1 = [Rgb565Pixel(0); 614_400]; self.window.set_size(WindowSize::Physical(PhysicalSize::new( frame_buffer.width, frame_buffer.height, ))); loop { input.handle_event(); slint::platform::update_timers_and_animations(); if input.is_just_pressed(KeyCode::BTN_TOUCH) { self.window .try_dispatch_event(WindowEvent::PointerPressed { position: LogicalPosition::new(input.x as f32, input.y as f32), button: slint::platform::PointerEventButton::Left, }) .ok(); } if input.is_just_released(KeyCode::BTN_TOUCH) { self.window .try_dispatch_event(WindowEvent::PointerReleased { position: LogicalPosition::new(input.x as f32, input.y as f32), button: slint::platform::PointerEventButton::Left, }) .ok(); } if input.is_down(KeyCode::BTN_TOUCH) { self.window .try_dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(input.x as f32, input.y as f32), }) .ok(); } if self.quit_flag.load(Ordering::SeqCst) { break; } self.window.draw_if_needed(|renderer| { renderer.render(&mut buffer1, frame_buffer.width as usize); frame_buffer.write_buffer(&buffer1); }); input.finish(); let pending: Vec<_> = self .queue .lock() .expect("event loop closure queue poisoned") .drain(..) .collect(); for c in pending { c(); } } Ok(()) } fn new_event_loop_proxy(&self) -> Option> { Some(Box::new(TrekstorEventProxy { queue: self.queue.clone(), quit_flag: self.quit_flag.clone(), })) } }