use alloc::vec::Vec;
use crate::{
BlitterRenderer, CpuBlitter, PixelFmt, Surface, display::DisplayDriver, screen::Screen,
};
use rlvgl_core::{
WidgetNode,
event::{Event, Key},
widget::{Color, Rect},
};
use uefi::{
boot,
proto::console::{
gop::{BltOp, BltPixel, BltRegion, GraphicsOutput},
text::{Input, Key as UefiKey, ScanCode},
},
system,
};
#[derive(Default)]
pub struct SyntheticKeyRelease(Option<Key>);
impl SyntheticKeyRelease {
pub fn schedule(&mut self, key: Key) {
self.0 = Some(key);
}
pub fn take(&mut self) -> Option<Key> {
self.0.take()
}
}
pub struct UefiDisplay {
width: usize,
height: usize,
frame: Vec<u8>,
present: Vec<BltPixel>,
present_count: u32,
}
impl UefiDisplay {
pub fn new(gop: &mut GraphicsOutput) -> Self {
let (width, height) = gop.current_mode_info().resolution();
let width = width.max(1);
let height = height.max(1);
Self {
width,
height,
frame: alloc::vec![0; width * height * 4],
present: alloc::vec![BltPixel::from(0xff00_0000); width * height],
present_count: 0,
}
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width as u32, self.height as u32)
}
pub fn frame_mut(&mut self) -> &mut [u8] {
&mut self.frame
}
pub fn clear(&mut self, color: Color) {
let argb = color.to_argb8888().to_le_bytes();
for pixel in self.frame.chunks_exact_mut(4) {
pixel.copy_from_slice(&argb);
}
}
pub fn render(&mut self, root: &WidgetNode) {
let mut blitter = CpuBlitter;
let surface = Surface::new(
&mut self.frame,
self.width * 4,
PixelFmt::Argb8888,
self.width as u32,
self.height as u32,
);
let mut renderer: BlitterRenderer<'_, CpuBlitter, 32> =
BlitterRenderer::new(&mut blitter, surface);
root.draw(&mut renderer);
}
pub fn present(&mut self, gop: &mut GraphicsOutput) -> uefi::Result {
for (index, bytes) in self.frame.chunks_exact(4).enumerate() {
let argb = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
self.present[index] = BltPixel::from(argb);
}
let result = gop.blt(BltOp::BufferToVideo {
buffer: &self.present,
src: BltRegion::Full,
dest: (0, 0),
dims: (self.width, self.height),
});
if result.is_ok() {
self.present_count = self.present_count.wrapping_add(1);
}
result
}
}
impl DisplayDriver for UefiDisplay {
fn screen(&self) -> Screen {
Screen::landscape(self.width as u32, self.height as u32)
}
fn flush(&mut self, area: Rect, colors: &[Color]) {
let width = area.width.max(0) as usize;
let height = area.height.max(0) as usize;
for y in 0..height {
for x in 0..width {
let dst_x = area.x.max(0) as usize + x;
let dst_y = area.y.max(0) as usize + y;
if dst_x >= self.width || dst_y >= self.height {
continue;
}
let dst_index = (dst_y * self.width + dst_x) * 4;
let src_index = y * width + x;
self.frame[dst_index..dst_index + 4]
.copy_from_slice(&colors[src_index].to_argb8888().to_le_bytes());
}
}
}
}
pub struct UefiInput {
pending_release: SyntheticKeyRelease,
}
impl UefiInput {
pub fn new() -> Self {
Self {
pending_release: SyntheticKeyRelease::default(),
}
}
pub fn poll(&mut self) -> uefi::Result<Option<Event>> {
if let Some(key) = self.pending_release.take() {
return Ok(Some(Event::KeyUp { key }));
}
system::with_stdin(|input: &mut Input| -> uefi::Result<Option<Event>> {
match input.read_key()? {
Some(raw) => {
if let Some(key) = map_key(raw) {
self.pending_release.schedule(key.clone());
Ok(Some(Event::KeyDown { key }))
} else {
Ok(None)
}
}
None => Ok(None),
}
})
}
pub fn wait(&mut self) -> uefi::Result<Option<Event>> {
if let Some(key) = self.pending_release.take() {
return Ok(Some(Event::KeyUp { key }));
}
system::with_stdin(|input: &mut Input| -> uefi::Result<Option<Event>> {
if let Some(event) = input.wait_for_key_event() {
let mut events = [event];
boot::wait_for_event(&mut events).map_err(|err| err.status())?;
}
match input.read_key()? {
Some(raw) => {
if let Some(key) = map_key(raw) {
self.pending_release.schedule(key.clone());
Ok(Some(Event::KeyDown { key }))
} else {
Ok(None)
}
}
None => Ok(None),
}
})
}
}
impl Default for UefiInput {
fn default() -> Self {
Self::new()
}
}
fn map_key(key: UefiKey) -> Option<Key> {
match key {
UefiKey::Special(scan) if scan == ScanCode::UP => Some(Key::ArrowUp),
UefiKey::Special(scan) if scan == ScanCode::DOWN => Some(Key::ArrowDown),
UefiKey::Special(scan) if scan == ScanCode::LEFT => Some(Key::ArrowLeft),
UefiKey::Special(scan) if scan == ScanCode::RIGHT => Some(Key::ArrowRight),
UefiKey::Special(scan) if scan == ScanCode::ESCAPE => Some(Key::Escape),
UefiKey::Special(scan) if scan == ScanCode::FUNCTION_1 => Some(Key::Function(1)),
UefiKey::Special(scan) if scan == ScanCode::FUNCTION_2 => Some(Key::Function(2)),
UefiKey::Special(scan) if scan == ScanCode::FUNCTION_3 => Some(Key::Function(3)),
UefiKey::Printable(ch) if ch == '\r' || ch == '\n' => Some(Key::Enter),
UefiKey::Printable(ch) if ch == ' ' => Some(Key::Space),
UefiKey::Printable(ch) => Some(Key::Character(char::from(ch))),
UefiKey::Special(scan) => Some(Key::Other(scan.0 as u32)),
}
}
impl rlvgl_playit::FramebufferReader for UefiDisplay {
fn read_pixel(&self, x: i32, y: i32) -> u32 {
let ux = x as usize;
let uy = y as usize;
if ux >= self.width || uy >= self.height {
return 0;
}
let offset = (uy * self.width + ux) * 4;
u32::from_le_bytes([
self.frame[offset],
self.frame[offset + 1],
self.frame[offset + 2],
self.frame[offset + 3],
])
}
fn read_row(&self, x: i32, y: i32, width: u16, out: &mut [u32]) -> usize {
let ux = x.max(0) as usize;
let uy = y.max(0) as usize;
if uy >= self.height || ux >= self.width {
return 0;
}
let available = (self.width - ux).min(width as usize).min(out.len());
for i in 0..available {
let offset = (uy * self.width + ux + i) * 4;
out[i] = u32::from_le_bytes([
self.frame[offset],
self.frame[offset + 1],
self.frame[offset + 2],
self.frame[offset + 3],
]);
}
available
}
fn present_count(&self) -> u32 {
self.present_count
}
}