use core::{ cell::RefCell, sync::atomic::{AtomicBool, Ordering}, time::Duration, }; use alloc::{ boxed::Box, collections::vec_deque::VecDeque, rc::Rc, string::ToString, sync::Arc, vec::Vec, }; use critical_section::Mutex; use esp_hal::time::Instant; use log::{debug, info}; use slint::{ EventLoopError, PhysicalSize, SharedString, WindowSize, platform::{ EventLoopProxy, Key, WindowEvent, software_renderer::{RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer}, }, }; use xkbcommon::xkb::{self, Keysym}; use crate::proxy::{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 quit_event_loop: Arc, pub events: Arc>>>>, } impl slint::platform::Platform for SlintBackend { fn create_window_adapter( &self, ) -> Result, slint::PlatformError> { 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 new_event_loop_proxy(&self) -> Option> { Some(Box::new(AcidEventLoopProxy { quit_event_loop: self.quit_event_loop.clone(), events: self.events.clone(), })) } 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 */ { let drained_events = critical_section::with(|cs| { self.events .borrow(cs) .borrow_mut() .drain(..) .collect::>() }); for event in drained_events { (event)(); } if let Some(window) = self.window.borrow().clone() { // 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) && 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 ); } } // 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)); 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(()) } } struct AcidEventLoopProxy { pub quit_event_loop: Arc, pub events: Arc>>>>, } 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, ) -> Result<(), EventLoopError> { critical_section::with(|cs| { self.events.borrow(cs).borrow_mut().push_back(event); }); Ok(()) } }