Files
dashboard/trekstor/src/lib.rs
2026-08-15 22:46:22 +02:00

154 lines
4.5 KiB
Rust

#![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<MinimalSoftwareWindow>,
queue: Queue,
quit_flag: Arc<AtomicBool>,
}
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<dyn FnOnce() + Send>;
type Queue = Arc<Mutex<Vec<Closure>>>;
struct TrekstorEventProxy {
queue: Queue,
quit_flag: Arc<AtomicBool>,
}
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<Rc<dyn slint::platform::WindowAdapter>, 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<Box<dyn EventLoopProxy>> {
Some(Box::new(TrekstorEventProxy {
queue: self.queue.clone(),
quit_flag: self.quit_flag.clone(),
}))
}
}