use crate::native_unsafe_methods::{fb_fix_screeninfo, fb_var_screeninfo};
use std::fs::{File, OpenOptions};
use std::io;
use std::os::fd::AsRawFd;
pub struct FbMapping {
address: *mut u8,
length: usize,
}
unsafe impl Send for FbMapping {}
unsafe impl Sync for FbMapping {}
impl FbMapping {
fn map(fd: i32, length: usize) -> io::Result<Self> {
let address = unsafe {
libc::mmap(std::ptr::null_mut(), length, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED, fd, 0)
};
if address == libc::MAP_FAILED {
let error = io::Error::last_os_error();
return Err(io::Error::new(error.kind(), format!("Unable to mmap {length} bytes, error {error}")));
}
Ok(Self { address: address.cast(), length })
}
pub fn address(&self) -> *mut u8 {
self.address
}
pub fn len(&self) -> usize {
self.length
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
pub fn copy_from(&self, source: &[u8]) {
let count = source.len().min(self.length);
unsafe {
std::ptr::copy_nonoverlapping(source.as_ptr(), self.address, count);
}
}
pub fn to_vec(&self) -> Vec<u8> {
let mut rv = vec![0u8; self.length];
unsafe {
std::ptr::copy_nonoverlapping(self.address, rv.as_mut_ptr(), self.length);
}
rv
}
}
impl Drop for FbMapping {
fn drop(&mut self) {
unsafe {
libc::munmap(self.address.cast(), self.length);
}
}
}
pub trait IFbDevice: Send {
fn get_var_screen_info(&self) -> io::Result<fb_var_screeninfo>;
fn put_var_screen_info(&mut self, info: &fb_var_screeninfo) -> io::Result<()>;
fn get_fix_screen_info(&self) -> io::Result<fb_fix_screeninfo>;
fn map(&self, length: usize) -> io::Result<FbMapping>;
fn wait_for_vsync(&self);
}
#[cfg(target_os = "linux")]
pub struct LinuxFbDevice {
fd: std::os::fd::OwnedFd,
}
#[cfg(target_os = "linux")]
impl LinuxFbDevice {
pub fn open(file_name: &str) -> io::Result<Self> {
use std::os::fd::FromRawFd;
let fd = crate::native_unsafe_methods::sys::open(file_name, libc::O_RDWR)
.map_err(|error| io::Error::new(error.kind(), format!("Error: {error}")))?;
Ok(Self { fd: unsafe { std::os::fd::OwnedFd::from_raw_fd(fd) } })
}
}
#[cfg(target_os = "linux")]
impl IFbDevice for LinuxFbDevice {
fn get_var_screen_info(&self) -> io::Result<fb_var_screeninfo> {
use crate::native_unsafe_methods::{fb_io_ctl, sys};
let mut info = fb_var_screeninfo::default();
sys::ioctl_struct(self.fd.as_raw_fd(), fb_io_ctl::FBIOGET_VSCREENINFO, &mut info)
.map_err(|error| io::Error::new(error.kind(), format!("FBIOGET_VSCREENINFO error: {error}")))?;
Ok(info)
}
fn put_var_screen_info(&mut self, info: &fb_var_screeninfo) -> io::Result<()> {
use crate::native_unsafe_methods::{fb_io_ctl, sys};
let mut info = *info;
sys::ioctl_struct(self.fd.as_raw_fd(), fb_io_ctl::FBIOPUT_VSCREENINFO, &mut info).map(|_| ())
}
fn get_fix_screen_info(&self) -> io::Result<fb_fix_screeninfo> {
use crate::native_unsafe_methods::{fb_io_ctl, sys};
let mut info = fb_fix_screeninfo::default();
sys::ioctl_struct(self.fd.as_raw_fd(), fb_io_ctl::FBIOGET_FSCREENINFO, &mut info)
.map_err(|error| io::Error::new(error.kind(), format!("FBIOGET_FSCREENINFO error: {error}")))?;
Ok(info)
}
fn map(&self, length: usize) -> io::Result<FbMapping> {
FbMapping::map(self.fd.as_raw_fd(), length)
}
fn wait_for_vsync(&self) {
use crate::native_unsafe_methods::{fb_io_ctl, sys};
sys::ioctl_null(self.fd.as_raw_fd(), fb_io_ctl::FBIO_WAITFORVSYNC);
}
}
pub struct FileFbDevice {
file: File,
var_info: fb_var_screeninfo,
id: [u8; 16],
}
impl FileFbDevice {
pub fn create(path: &str, var_info: fb_var_screeninfo) -> io::Result<Self> {
let file = OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path)?;
let mut id = [0u8; 16];
id[..4].copy_from_slice(b"file");
let this = Self { file, var_info, id };
this.resize()?;
Ok(this)
}
fn line_length(&self) -> u32 {
self.var_info.xres * self.var_info.bits_per_pixel.div_ceil(8)
}
fn resize(&self) -> io::Result<()> {
self.file.set_len(self.line_length() as u64 * self.var_info.yres as u64)
}
}
impl IFbDevice for FileFbDevice {
fn get_var_screen_info(&self) -> io::Result<fb_var_screeninfo> {
Ok(self.var_info)
}
fn put_var_screen_info(&mut self, info: &fb_var_screeninfo) -> io::Result<()> {
if info.bits_per_pixel != 16 && info.bits_per_pixel != 32 {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
self.var_info = *info;
self.resize()
}
fn get_fix_screen_info(&self) -> io::Result<fb_fix_screeninfo> {
let line_length = self.line_length();
Ok(fb_fix_screeninfo {
id: self.id,
smem_len: line_length * self.var_info.yres,
line_length,
..Default::default()
})
}
fn map(&self, length: usize) -> io::Result<FbMapping> {
FbMapping::map(self.file.as_raw_fd(), length)
}
fn wait_for_vsync(&self) {}
}