use core::{cell::RefCell, time::Duration}; use alloc::rc::Rc; use esp_hal::time::Instant; use log::{debug, info, warn}; use slint::{ PhysicalSize, SharedString, WindowSize, platform::{ Key, WindowEvent, software_renderer::{RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer}, }, }; use xkbcommon::xkb; use crate::keymap::{KEY_MESSAGE_CHANNEL, TryFromKeysym}; 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>>, pub framebuffer: FramebufferPtr, // pub peripherals: RefCell>, } impl slint::platform::Platform for SlintBackend { fn create_window_adapter( &self, ) -> Result, slint::PlatformError> { // TODO: Custom window adapter impl needs to be implemented, so we can change `rotation` on // `SoftwareRenderer`. let renderer = SoftwareRenderer::new_with_repaint_buffer_type( RepaintBufferType::ReusedBuffer, /* TODO: Implement a swapchain */ ); renderer.set_rendering_rotation(RenderingRotation::Rotate270); let window = SoftwareWindowAdapter::new(renderer); // window.set_scale_factor(4.0); window.set_size(WindowSize::Physical(PhysicalSize::new( self.window_size[0], self.window_size[1], ))); self.window.replace(Some(window.clone())); Ok(window) } fn duration_since_start(&self) -> Duration { Duration::from_millis(Instant::now().duration_since_epoch().as_millis()) } fn run_event_loop(&self) -> Result<(), slint::PlatformError> { // 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`. /* loop */ { if let Some(window) = self.window.borrow().clone() { // Handle key presses while let Ok(key_message) = KEY_MESSAGE_CHANNEL.try_receive() { debug!("key_message = {key_message:?}"); let text = key_message.string.map(SharedString::from).or_else(|| { Key::try_from_keysym(key_message.keysym).map(SharedString::from) }); debug!("text = {text:?}"); 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(), } } } 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."); }); } } Ok(()) } }