ferroui_linuxframebuffer/output/
fb_device.rs1use crate::native_unsafe_methods::{fb_fix_screeninfo, fb_var_screeninfo};
9use std::fs::{File, OpenOptions};
10use std::io;
11use std::os::fd::AsRawFd;
12
13pub struct FbMapping {
16 address: *mut u8,
17 length: usize,
18}
19
20unsafe impl Send for FbMapping {}
25unsafe impl Sync for FbMapping {}
27
28impl FbMapping {
29 fn map(fd: i32, length: usize) -> io::Result<Self> {
31 let address = unsafe {
34 libc::mmap(std::ptr::null_mut(), length, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED, fd, 0)
35 };
36 if address == libc::MAP_FAILED {
37 let error = io::Error::last_os_error();
38 return Err(io::Error::new(error.kind(), format!("Unable to mmap {length} bytes, error {error}")));
39 }
40 Ok(Self { address: address.cast(), length })
41 }
42
43 pub fn address(&self) -> *mut u8 {
44 self.address
45 }
46
47 pub fn len(&self) -> usize {
48 self.length
49 }
50
51 pub fn is_empty(&self) -> bool {
52 self.length == 0
53 }
54
55 pub fn copy_from(&self, source: &[u8]) {
57 let count = source.len().min(self.length);
58 unsafe {
61 std::ptr::copy_nonoverlapping(source.as_ptr(), self.address, count);
62 }
63 }
64
65 pub fn to_vec(&self) -> Vec<u8> {
67 let mut rv = vec![0u8; self.length];
68 unsafe {
71 std::ptr::copy_nonoverlapping(self.address, rv.as_mut_ptr(), self.length);
72 }
73 rv
74 }
75}
76
77impl Drop for FbMapping {
78 fn drop(&mut self) {
79 unsafe {
81 libc::munmap(self.address.cast(), self.length);
82 }
83 }
84}
85
86pub trait IFbDevice: Send {
88 fn get_var_screen_info(&self) -> io::Result<fb_var_screeninfo>;
90
91 fn put_var_screen_info(&mut self, info: &fb_var_screeninfo) -> io::Result<()>;
93
94 fn get_fix_screen_info(&self) -> io::Result<fb_fix_screeninfo>;
96
97 fn map(&self, length: usize) -> io::Result<FbMapping>;
99
100 fn wait_for_vsync(&self);
102}
103
104#[cfg(target_os = "linux")]
106pub struct LinuxFbDevice {
107 fd: std::os::fd::OwnedFd,
108}
109
110#[cfg(target_os = "linux")]
111impl LinuxFbDevice {
112 pub fn open(file_name: &str) -> io::Result<Self> {
114 use std::os::fd::FromRawFd;
115 let fd = crate::native_unsafe_methods::sys::open(file_name, libc::O_RDWR)
116 .map_err(|error| io::Error::new(error.kind(), format!("Error: {error}")))?;
117 Ok(Self { fd: unsafe { std::os::fd::OwnedFd::from_raw_fd(fd) } })
119 }
120}
121
122#[cfg(target_os = "linux")]
123impl IFbDevice for LinuxFbDevice {
124 fn get_var_screen_info(&self) -> io::Result<fb_var_screeninfo> {
125 use crate::native_unsafe_methods::{fb_io_ctl, sys};
126 let mut info = fb_var_screeninfo::default();
127 sys::ioctl_struct(self.fd.as_raw_fd(), fb_io_ctl::FBIOGET_VSCREENINFO, &mut info)
128 .map_err(|error| io::Error::new(error.kind(), format!("FBIOGET_VSCREENINFO error: {error}")))?;
129 Ok(info)
130 }
131
132 fn put_var_screen_info(&mut self, info: &fb_var_screeninfo) -> io::Result<()> {
133 use crate::native_unsafe_methods::{fb_io_ctl, sys};
134 let mut info = *info;
135 sys::ioctl_struct(self.fd.as_raw_fd(), fb_io_ctl::FBIOPUT_VSCREENINFO, &mut info).map(|_| ())
136 }
137
138 fn get_fix_screen_info(&self) -> io::Result<fb_fix_screeninfo> {
139 use crate::native_unsafe_methods::{fb_io_ctl, sys};
140 let mut info = fb_fix_screeninfo::default();
141 sys::ioctl_struct(self.fd.as_raw_fd(), fb_io_ctl::FBIOGET_FSCREENINFO, &mut info)
142 .map_err(|error| io::Error::new(error.kind(), format!("FBIOGET_FSCREENINFO error: {error}")))?;
143 Ok(info)
144 }
145
146 fn map(&self, length: usize) -> io::Result<FbMapping> {
147 FbMapping::map(self.fd.as_raw_fd(), length)
148 }
149
150 fn wait_for_vsync(&self) {
151 use crate::native_unsafe_methods::{fb_io_ctl, sys};
152 sys::ioctl_null(self.fd.as_raw_fd(), fb_io_ctl::FBIO_WAITFORVSYNC);
153 }
154}
155
156pub struct FileFbDevice {
159 file: File,
160 var_info: fb_var_screeninfo,
161 id: [u8; 16],
162}
163
164impl FileFbDevice {
165 pub fn create(path: &str, var_info: fb_var_screeninfo) -> io::Result<Self> {
168 let file = OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path)?;
169 let mut id = [0u8; 16];
170 id[..4].copy_from_slice(b"file");
171 let this = Self { file, var_info, id };
172 this.resize()?;
173 Ok(this)
174 }
175
176 fn line_length(&self) -> u32 {
177 self.var_info.xres * self.var_info.bits_per_pixel.div_ceil(8)
178 }
179
180 fn resize(&self) -> io::Result<()> {
181 self.file.set_len(self.line_length() as u64 * self.var_info.yres as u64)
182 }
183}
184
185impl IFbDevice for FileFbDevice {
186 fn get_var_screen_info(&self) -> io::Result<fb_var_screeninfo> {
187 Ok(self.var_info)
188 }
189
190 fn put_var_screen_info(&mut self, info: &fb_var_screeninfo) -> io::Result<()> {
191 if info.bits_per_pixel != 16 && info.bits_per_pixel != 32 {
192 return Err(io::Error::from(io::ErrorKind::InvalidInput));
193 }
194 self.var_info = *info;
195 self.resize()
196 }
197
198 fn get_fix_screen_info(&self) -> io::Result<fb_fix_screeninfo> {
199 let line_length = self.line_length();
200 Ok(fb_fix_screeninfo {
201 id: self.id,
202 smem_len: line_length * self.var_info.yres,
203 line_length,
204 ..Default::default()
205 })
206 }
207
208 fn map(&self, length: usize) -> io::Result<FbMapping> {
209 FbMapping::map(self.file.as_raw_fd(), length)
210 }
211
212 fn wait_for_vsync(&self) {}
213}