use std::eprintln;
use crate::display::DisplayDriver;
use crate::hwcore::addr::PhysAddr;
use crate::screen::Screen;
use rlvgl_core::widget::{Color, Rect};
use std::fs::{File, OpenOptions};
use std::os::unix::io::AsRawFd;
const FBIOGET_VSCREENINFO: libc::c_ulong = 0x4600;
const FBIOGET_FSCREENINFO: libc::c_ulong = 0x4602;
#[repr(C)]
#[derive(Default, Clone, Copy)]
struct FbVarScreeninfo {
xres: u32,
yres: u32,
xres_virtual: u32,
yres_virtual: u32,
xoffset: u32,
yoffset: u32,
bits_per_pixel: u32,
grayscale: u32,
red: FbBitfield,
green: FbBitfield,
blue: FbBitfield,
transp: FbBitfield,
nonstd: u32,
activate: u32,
height: u32,
width: u32,
accel_flags: u32,
pixclock: u32,
left_margin: u32,
right_margin: u32,
upper_margin: u32,
lower_margin: u32,
hsync_len: u32,
vsync_len: u32,
sync: u32,
vmode: u32,
rotate: u32,
colorspace: u32,
reserved: [u32; 4],
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
struct FbBitfield {
offset: u32,
length: u32,
msb_right: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct FbFixScreeninfo {
id: [u8; 16],
smem_start: libc::c_ulong,
smem_len: u32,
kind: u32,
type_aux: u32,
visual: u32,
xpanstep: u16,
ypanstep: u16,
ywrapstep: u16,
line_length: u32,
mmio_start: libc::c_ulong,
mmio_len: u32,
accel: u32,
capabilities: u16,
reserved: [u16; 2],
}
impl Default for FbFixScreeninfo {
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
pub struct LinuxFbdevDisplay {
_file: File,
mmap_ptr: *mut u8,
mmap_len: usize,
phys_addr: Option<PhysAddr>,
width: u32,
height: u32,
stride: u32,
bpp: u32,
}
unsafe impl Send for LinuxFbdevDisplay {}
impl LinuxFbdevDisplay {
pub fn open(path: &str) -> Self {
let file = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.unwrap_or_else(|e| panic!("failed to open {path}: {e}"));
let fd = file.as_raw_fd();
let mut vinfo = FbVarScreeninfo::default();
let mut finfo = FbFixScreeninfo::default();
unsafe {
if libc::ioctl(fd, FBIOGET_VSCREENINFO, &mut vinfo as *mut _) != 0 {
panic!("FBIOGET_VSCREENINFO failed");
}
if libc::ioctl(fd, FBIOGET_FSCREENINFO, &mut finfo as *mut _) != 0 {
panic!("FBIOGET_FSCREENINFO failed");
}
}
let mmap_len = finfo.smem_len as usize;
let mmap_ptr = unsafe {
libc::mmap(
core::ptr::null_mut(),
mmap_len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0,
)
};
if mmap_ptr == libc::MAP_FAILED {
panic!("mmap failed for {path}");
}
eprintln!(
"fbdev: {}x{} @ {}bpp, stride={}, mmap={}K",
vinfo.xres,
vinfo.yres,
vinfo.bits_per_pixel,
finfo.line_length,
mmap_len / 1024,
);
Self {
_file: file,
mmap_ptr: mmap_ptr as *mut u8,
mmap_len,
phys_addr: u32::try_from(finfo.smem_start).ok().map(PhysAddr::new),
width: vinfo.xres,
height: vinfo.yres,
stride: finfo.line_length,
bpp: vinfo.bits_per_pixel,
}
}
pub fn open_default() -> Self {
Self::open("/dev/fb0")
}
pub fn buffer_mut(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.mmap_ptr, self.mmap_len) }
}
pub fn framebuffer_phys(&self) -> Option<PhysAddr> {
self.phys_addr
}
pub fn stride_bytes(&self) -> usize {
self.stride as usize
}
pub fn bits_per_pixel(&self) -> u32 {
self.bpp
}
}
impl Drop for LinuxFbdevDisplay {
fn drop(&mut self) {
unsafe {
libc::munmap(self.mmap_ptr as *mut libc::c_void, self.mmap_len);
}
}
}
impl DisplayDriver for LinuxFbdevDisplay {
fn screen(&self) -> Screen {
Screen::landscape(self.width, self.height)
}
fn flush(&mut self, area: Rect, colors: &[Color]) {
let width = self.width as usize;
let height = self.height as usize;
let stride = self.stride as usize;
let bpp = self.bpp;
let fb = self.buffer_mut();
for row in 0..area.height as usize {
let py = area.y as usize + row;
if py >= height {
break;
}
for col in 0..area.width as usize {
let px = area.x as usize + col;
if px >= width {
break;
}
let color = colors[row * area.width as usize + col];
let offset = py * stride + px * (bpp / 8) as usize;
match bpp {
32 => {
if offset + 3 < fb.len() {
fb[offset] = color.2; fb[offset + 1] = color.1; fb[offset + 2] = color.0; fb[offset + 3] = color.3; }
}
16 => {
if offset + 1 < fb.len() {
let r5 = (color.0 >> 3) as u16;
let g6 = (color.1 >> 2) as u16;
let b5 = (color.2 >> 3) as u16;
let pixel = (b5 << 11) | (g6 << 5) | r5;
fb[offset] = pixel as u8;
fb[offset + 1] = (pixel >> 8) as u8;
}
}
24 if offset + 2 < fb.len() => {
fb[offset] = color.2; fb[offset + 1] = color.1; fb[offset + 2] = color.0; }
_ => {} }
}
}
}
}