acid/firmware/acid-firmware/src/ui/backend.rs

171 lines
6.1 KiB
Rust
Raw Normal View History

2026-02-04 03:14:21 +01:00
use core::{
cell::RefCell,
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
2025-12-31 22:24:26 +01:00
2026-02-04 03:14:21 +01:00
use alloc::{
boxed::Box, collections::vec_deque::VecDeque, rc::Rc, string::ToString, sync::Arc, vec::Vec,
};
use critical_section::Mutex;
2026-01-06 22:52:41 +01:00
use esp_hal::time::Instant;
2026-01-31 15:36:36 +01:00
use log::{debug, info};
2026-02-04 03:14:21 +01:00
use rmk::futures::sink::drain;
2026-01-06 22:52:41 +01:00
use slint::{
2026-02-04 03:14:21 +01:00
EventLoopError, PhysicalSize, SharedString, WindowSize,
2026-01-10 19:21:13 +01:00
platform::{
2026-02-04 03:14:21 +01:00
EventLoopProxy, Key, WindowEvent,
2026-01-10 19:21:13 +01:00
software_renderer::{RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer},
2026-01-06 22:52:41 +01:00
},
};
use xkbcommon::xkb::{self, Keysym};
2026-01-10 19:21:13 +01:00
use crate::keymap::{KEY_MESSAGE_CHANNEL, TryFromKeysym};
2025-12-31 22:24:26 +01:00
use super::window_adapter::SoftwareWindowAdapter;
pub struct FramebufferPtr(pub *mut [Rgb565Pixel]);
unsafe impl Send for FramebufferPtr {}
pub struct SlintBackend {
pub window_size: [u32; 2],
pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>,
pub framebuffer: FramebufferPtr,
2026-02-04 03:14:21 +01:00
pub quit_event_loop: Arc<AtomicBool>,
pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
2025-12-31 22:24:26 +01:00
}
impl slint::platform::Platform for SlintBackend {
2026-01-06 22:52:41 +01:00
fn create_window_adapter(
&self,
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
let renderer = SoftwareRenderer::new_with_repaint_buffer_type(
RepaintBufferType::ReusedBuffer, /* TODO: Implement a swapchain */
);
2025-12-31 22:24:26 +01:00
renderer.set_rendering_rotation(RenderingRotation::Rotate270);
let window = SoftwareWindowAdapter::new(renderer);
// window.set_scale_factor(4.0);
2026-01-06 22:52:41 +01:00
window.set_size(WindowSize::Physical(PhysicalSize::new(
self.window_size[0],
self.window_size[1],
)));
2025-12-31 22:24:26 +01:00
self.window.replace(Some(window.clone()));
Ok(window)
}
fn duration_since_start(&self) -> Duration {
Duration::from_millis(Instant::now().duration_since_epoch().as_millis())
}
2026-02-04 03:14:21 +01:00
fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
Some(Box::new(AcidEventLoopProxy {
quit_event_loop: self.quit_event_loop.clone(),
events: self.events.clone(),
}))
}
2025-12-31 22:24:26 +01:00
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
2026-01-01 01:24:52 +01:00
// Instead of `loop`ing here, we execute a single iteration and handle `loop`ing
// in `crate::run_renderer_task`, where we can make use of `await`.
2026-01-06 22:52:41 +01:00
/* loop */
{
2026-02-04 03:14:21 +01:00
let drained_events = critical_section::with(|cs| {
self.events
.borrow(cs)
.borrow_mut()
.drain(..)
.collect::<Vec<_>>()
});
for event in drained_events {
(event)();
}
2025-12-31 22:24:26 +01:00
if let Some(window) = self.window.borrow().clone() {
2026-01-10 19:21:13 +01:00
// Handle key presses
while let Ok(mut key_message) = KEY_MESSAGE_CHANNEL.try_receive() {
debug!("key_message = {key_message:?}");
if let Some(string) = key_message.string.as_ref()
&& (Keysym::a..=Keysym::z).contains(&key_message.keysym)
2026-02-04 03:14:21 +01:00
&& let &[code] = string.as_bytes()
{
const UNICODE_CTRL_A: char = '\u{1}';
let letter_index_from_keysym =
key_message.keysym.raw().wrapping_sub(Keysym::a.raw());
let letter_index_from_string =
(code as u32).wrapping_sub(UNICODE_CTRL_A as u32);
if letter_index_from_keysym == letter_index_from_string {
key_message.keysym =
Keysym::new(Keysym::a.raw() + letter_index_from_keysym);
// TODO: Avoid allocation
key_message.string =
Some(((b'a' + letter_index_from_keysym as u8) as char).to_string());
info!(
"Translating CTRL-{letter} to {letter}",
letter = (b'A' + letter_index_from_keysym as u8) as char
);
}
2026-02-04 03:14:21 +01:00
}
// let text = key_message.string.map(SharedString::from).or_else(|| {
// Key::try_from_keysym(key_message.keysym).map(SharedString::from)
// });
let text = Key::try_from_keysym(key_message.keysym)
.map(SharedString::from)
.or_else(|| key_message.string.map(SharedString::from));
2026-01-10 19:21:13 +01:00
debug!("text = {text:?}");
2026-01-10 19:21:13 +01:00
if let Some(text) = text {
match key_message.direction {
xkb::KeyDirection::Down => window
.try_dispatch_event(WindowEvent::KeyPressed { text: text.clone() })
.unwrap(),
xkb::KeyDirection::Up => window
.try_dispatch_event(WindowEvent::KeyReleased { text })
.unwrap(),
}
2026-01-10 19:21:13 +01:00
}
}
2025-12-31 22:24:26 +01:00
window.draw_if_needed(|renderer| {
// TODO: Proper synchronization.
let framebuffer = unsafe { &mut *self.framebuffer.0 };
renderer.render(framebuffer, self.window_size[1] as usize);
info!("UI rendered.");
});
}
}
2026-01-01 01:24:52 +01:00
Ok(())
2025-12-31 22:24:26 +01:00
}
}
2026-02-04 03:14:21 +01:00
struct AcidEventLoopProxy {
pub quit_event_loop: Arc<AtomicBool>,
pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
}
impl EventLoopProxy for AcidEventLoopProxy {
fn quit_event_loop(&self) -> Result<(), EventLoopError> {
self.quit_event_loop.store(true, Ordering::SeqCst);
Ok(())
}
fn invoke_from_event_loop(
&self,
event: Box<dyn FnOnce() + Send>,
) -> Result<(), EventLoopError> {
critical_section::with(|cs| {
self.events.borrow(cs).borrow_mut().push_back(event);
});
Ok(())
}
}