framebuffer/
lib.rs

1//!Simple linux framebuffer abstraction.
2//!Examples can be found [here](https://github.com/Roysten/rust-framebuffer/tree/master/examples).
3
4extern 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///Bitfield which is a part of VarScreeninfo.
27#[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///Struct as defined in /usr/include/linux/fb.h
36#[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///Struct as defined in /usr/include/linux/fb.h Note: type is a keyword in Rust and therefore has been
71///changed to fb_type.
72#[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
110///Enum that can be used to set the current KdMode.
111pub enum KdMode {
112    Graphics = KD_GRAPHICS as isize,
113    Text = KD_TEXT as isize,
114}
115
116///Kind of errors that can occur when dealing with the Framebuffer.
117#[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///Struct that should be used to work with the framebuffer. Direct usage of `frame` should not be
157///necessary.
158#[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    ///Writes a frame to the Framebuffer.
193    pub fn write_frame(&mut self, frame: &[u8]) {
194        self.frame[..].copy_from_slice(&frame[..]);
195    }
196
197    ///Reads a frame from the framebuffer.
198    pub fn read_frame(&self) -> &[u8] {
199        &self.frame[..]
200    }
201
202    ///Creates a FixScreeninfo struct and fills it using ioctl.
203    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    ///Creates a VarScreeninfo struct and fills it using ioctl.
216    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    ///Sets the tty graphics mode. Make sure to change it back to KdMode::Text after the program is
255    ///done!
256    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    /// Allows setting tty mode from non-terminal session by explicitly specifying device name
267    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}