1extern crate libc;
5extern crate memmap;
6
7use libc::ioctl;
8
9use std::fmt;
10use std::fs::{File, OpenOptions};
11use std::os::unix::io::AsRawFd;
12use std::path::Path;
13
14use errno::errno;
15use memmap::{MmapMut, MmapOptions};
16
17const FBIOGET_VSCREENINFO: libc::c_ulong = 0x4600;
18const FBIOPUT_VSCREENINFO: libc::c_ulong = 0x4601;
19const FBIOGET_FSCREENINFO: libc::c_ulong = 0x4602;
20const FBIOPAN_DISPLAY: libc::c_ulong = 0x4606;
21
22const KDSETMODE: libc::c_ulong = 0x4B3A;
23const KD_TEXT: libc::c_ulong = 0x00;
24const KD_GRAPHICS: libc::c_ulong = 0x01;
25
26#[repr(C)]
28#[derive(Clone, Debug)]
29pub struct Bitfield {
30 pub offset: u32,
31 pub length: u32,
32 pub msb_right: u32,
33}
34
35#[repr(C)]
37#[derive(Clone, Debug)]
38pub struct VarScreeninfo {
39 pub xres: u32,
40 pub yres: u32,
41 pub xres_virtual: u32,
42 pub yres_virtual: u32,
43 pub xoffset: u32,
44 pub yoffset: u32,
45 pub bits_per_pixel: u32,
46 pub grayscale: u32,
47 pub red: Bitfield,
48 pub green: Bitfield,
49 pub blue: Bitfield,
50 pub transp: Bitfield,
51 pub nonstd: u32,
52 pub activate: u32,
53 pub height: u32,
54 pub width: u32,
55 pub accel_flags: u32,
56 pub pixclock: u32,
57 pub left_margin: u32,
58 pub right_margin: u32,
59 pub upper_margin: u32,
60 pub lower_margin: u32,
61 pub hsync_len: u32,
62 pub vsync_len: u32,
63 pub sync: u32,
64 pub vmode: u32,
65 pub rotate: u32,
66 pub colorspace: u32,
67 pub reserved: [u32; 4],
68}
69
70#[repr(C)]
73#[derive(Clone, Debug)]
74pub struct FixScreeninfo {
75 pub id: [u8; 16],
76 pub smem_start: usize,
77 pub smem_len: u32,
78 pub fb_type: u32,
79 pub type_aux: u32,
80 pub visual: u32,
81 pub xpanstep: u16,
82 pub ypanstep: u16,
83 pub ywrapstep: u16,
84 pub line_length: u32,
85 pub mmio_start: usize,
86 pub mmio_len: u32,
87 pub accel: u32,
88 pub capabilities: u16,
89 pub reserved: [u16; 2],
90}
91
92impl ::std::default::Default for Bitfield {
93 fn default() -> Self {
94 unsafe { ::std::mem::zeroed() }
95 }
96}
97
98impl ::std::default::Default for VarScreeninfo {
99 fn default() -> Self {
100 unsafe { ::std::mem::zeroed() }
101 }
102}
103
104impl ::std::default::Default for FixScreeninfo {
105 fn default() -> Self {
106 unsafe { ::std::mem::zeroed() }
107 }
108}
109
110pub enum KdMode {
112 Graphics = KD_GRAPHICS as isize,
113 Text = KD_TEXT as isize,
114}
115
116#[derive(Debug)]
118pub enum FramebufferErrorKind {
119 IoctlFailed,
120 IoError,
121}
122
123#[derive(Debug)]
124pub struct FramebufferError {
125 pub kind: FramebufferErrorKind,
126 pub details: String,
127}
128
129impl FramebufferError {
130 fn new(kind: FramebufferErrorKind, details: &str) -> FramebufferError {
131 FramebufferError {
132 kind,
133 details: String::from(details),
134 }
135 }
136}
137
138impl std::error::Error for FramebufferError {
139 fn description(&self) -> &str {
140 &self.details
141 }
142}
143
144impl fmt::Display for FramebufferError {
145 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
146 write!(fmt, "{}", self.details)
147 }
148}
149
150impl std::convert::From<std::io::Error> for FramebufferError {
151 fn from(err: std::io::Error) -> FramebufferError {
152 FramebufferError::new(FramebufferErrorKind::IoError, &err.to_string())
153 }
154}
155
156#[derive(Debug)]
159pub struct Framebuffer {
160 pub device: File,
161 pub frame: MmapMut,
162 pub var_screen_info: VarScreeninfo,
163 pub fix_screen_info: FixScreeninfo,
164}
165
166impl Framebuffer {
167 pub fn new<P: AsRef<Path>>(path_to_device: P) -> Result<Framebuffer, FramebufferError> {
168 let device = OpenOptions::new()
169 .read(true)
170 .write(true)
171 .open(path_to_device)?;
172
173 let var_screen_info = Framebuffer::get_var_screeninfo(&device)?;
174 let fix_screen_info = Framebuffer::get_fix_screeninfo(&device)?;
175
176 let frame_length = (fix_screen_info.line_length * var_screen_info.yres_virtual) as usize;
177 let frame = unsafe { MmapOptions::new().len(frame_length).map_mut(&device) };
178 match frame {
179 Ok(frame_result) => Ok(Framebuffer {
180 device,
181 frame: frame_result,
182 var_screen_info,
183 fix_screen_info,
184 }),
185 Err(_) => Err(FramebufferError::new(
186 FramebufferErrorKind::IoError,
187 &format!("Failed to map memory (offset: {} len: {})", 0, frame_length),
188 )),
189 }
190 }
191
192 pub fn write_frame(&mut self, frame: &[u8]) {
194 self.frame[..].copy_from_slice(&frame[..]);
195 }
196
197 pub fn read_frame(&self) -> &[u8] {
199 &self.frame[..]
200 }
201
202 pub fn get_fix_screeninfo(device: &File) -> Result<FixScreeninfo, FramebufferError> {
204 let mut info: FixScreeninfo = Default::default();
205 let result = unsafe { ioctl(device.as_raw_fd(), FBIOGET_FSCREENINFO as _, &mut info) };
206 match result {
207 -1 => Err(FramebufferError::new(
208 FramebufferErrorKind::IoctlFailed,
209 &format!("Ioctl returned -1: {}", errno()),
210 )),
211 _ => Ok(info),
212 }
213 }
214
215 pub fn get_var_screeninfo(device: &File) -> Result<VarScreeninfo, FramebufferError> {
217 let mut info: VarScreeninfo = Default::default();
218 let result = unsafe { ioctl(device.as_raw_fd(), FBIOGET_VSCREENINFO as _, &mut info) };
219 match result {
220 -1 => Err(FramebufferError::new(
221 FramebufferErrorKind::IoctlFailed,
222 &format!("Ioctl returned -1: {}", errno()),
223 )),
224 _ => Ok(info),
225 }
226 }
227
228 pub fn put_var_screeninfo(
229 device: &File,
230 screeninfo: &VarScreeninfo,
231 ) -> Result<i32, FramebufferError> {
232 match unsafe { ioctl(device.as_raw_fd(), FBIOPUT_VSCREENINFO as _, screeninfo) } {
233 -1 => Err(FramebufferError::new(
234 FramebufferErrorKind::IoctlFailed,
235 &format!("Ioctl returned -1: {}", errno()),
236 )),
237 ret => Ok(ret),
238 }
239 }
240
241 pub fn pan_display(
242 device: &File,
243 screeninfo: &VarScreeninfo,
244 ) -> Result<i32, FramebufferError> {
245 match unsafe { ioctl(device.as_raw_fd(), FBIOPAN_DISPLAY as _, screeninfo) } {
246 -1 => Err(FramebufferError::new(
247 FramebufferErrorKind::IoctlFailed,
248 &format!("Ioctl returned -1: {}", errno()),
249 )),
250 ret => Ok(ret),
251 }
252 }
253
254 pub fn set_kd_mode(kd_mode: KdMode) -> Result<i32, FramebufferError> {
257 match unsafe { ioctl(0, KDSETMODE as _, kd_mode) } {
258 -1 => Err(FramebufferError::new(
259 FramebufferErrorKind::IoctlFailed,
260 &format!("Ioctl returned -1: {}", errno()),
261 )),
262 ret => Ok(ret),
263 }
264 }
265
266 pub fn set_kd_mode_ex<P: AsRef<Path>>(
268 path_to_device: P,
269 kd_mode: KdMode,
270 ) -> Result<i32, FramebufferError> {
271 let device = OpenOptions::new()
272 .read(true)
273 .write(true)
274 .open(path_to_device)?;
275
276 match unsafe { ioctl(device.as_raw_fd(), KDSETMODE as _, kd_mode) } {
277 -1 => Err(FramebufferError::new(
278 FramebufferErrorKind::IoctlFailed,
279 &format!("Ioctl returned -1: {}", errno()),
280 )),
281 ret => Ok(ret),
282 }
283 }
284}