1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//!Simple linux framebuffer abstraction.
//!Examples can be found [here](https://github.com/Roysten/rust-framebuffer/tree/master/examples).

extern crate libc;
extern crate memmap;

use libc::ioctl;

use std::fmt;
use std::io::Write;
use std::fs::{OpenOptions, File};
use std::os::unix::io::AsRawFd;
use std::error::Error;

use memmap::{Mmap, Protection};

const FBIOGET_VSCREENINFO: libc::c_ulong = 0x4600;
const FBIOPUT_VSCREENINFO: libc::c_ulong = 0x4601;
const FBIOGET_FSCREENINFO: libc::c_ulong = 0x4602;

const KDSETMODE: libc::c_ulong = 0x4B3A;
const KD_TEXT: libc::c_ulong = 0x00;
const KD_GRAPHICS: libc::c_ulong = 0x01;

///Bitfield which is a part of VarScreeninfo.
#[repr(C)]
#[derive(Clone, Debug)]
pub struct Bitfield {
    pub offset: u32,
    pub length: u32,
    pub msb_right: u32,
}

///Struct as defined in /usr/include/linux/fb.h
#[repr(C)]
#[derive(Clone, Debug)]
pub struct VarScreeninfo {
    pub xres: u32,    
    pub yres: u32,
    pub xres_virtual: u32,
    pub yres_virtual: u32,
    pub xoffset: u32,
    pub yoffset: u32,
    pub bits_per_pixel: u32,
    pub grayscale: u32,
    pub red: Bitfield,
    pub green: Bitfield,
    pub blue: Bitfield,
    pub transp: Bitfield,
    pub nonstd: u32,
    pub activate: u32,
    pub height: u32,
    pub width: u32,
    pub accel_flags: u32,
    pub pixclock: u32,
    pub left_margin: u32,
    pub right_margin: u32,
    pub upper_margin: u32,
    pub lower_margin: u32,
    pub hsync_len: u32,
    pub vsync_len: u32,
    pub sync: u32,
    pub vmode: u32,
    pub rotate: u32,
    pub colorspace: u32,
    pub reserved: [u32; 4],
}


///Struct as defined in /usr/include/linux/fb.h Note: type is a keyword in Rust and therefore has been
///changed to fb_type.
#[repr(C)]
#[derive(Clone, Debug)]
pub struct FixScreeninfo {
    pub id: [u8; 16],
    pub smem_start: usize,
    pub smem_len: u32,
    pub fb_type: u32,
    pub type_aux: u32,
    pub visual: u32,
    pub xpanstep: u16,
    pub ypanstep: u16,
    pub ywrapstep: u16,
    pub line_length: u32,
    pub mmio_start: usize,
    pub mmio_len: u32,
    pub accel: u32,
    pub capabilities: u16,
    pub reserved: [u16; 2],
}

impl ::std::default::Default for Bitfield {
    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}

impl ::std::default::Default for VarScreeninfo {
    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}

impl ::std::default::Default for FixScreeninfo {
    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}

///Enum that can be used to set the current KdMode.
pub enum KdMode {
    Graphics = KD_GRAPHICS as isize,
    Text = KD_TEXT as isize,
}

///Kind of errors that can occur when dealing with the Framebuffer.
#[derive(Debug)]
pub enum FramebufferErrorKind {
    IoctlFailed,
    IoError,
}

#[derive(Debug)]
pub struct FramebufferError {
    pub kind: FramebufferErrorKind,
    pub details: String,
}

impl FramebufferError {
    fn new(kind: FramebufferErrorKind, details: &str) -> FramebufferError {
        FramebufferError { kind: kind, details: String::from(details) }
    }
}

impl std::error::Error for FramebufferError {
    fn description(&self) -> &str {
        &self.details
    }
}

impl fmt::Display for FramebufferError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}", self.description())
    }
}

impl std::convert::From<std::io::Error> for FramebufferError {
    fn from(err: std::io::Error) -> FramebufferError {
        FramebufferError::new(FramebufferErrorKind::IoError, err.description())
    }
}

///Struct that should be used to work with the framebuffer. Direct usage of `frame` should not be
///necessary.
pub struct Framebuffer {
    pub device: File,
    pub frame: Mmap,
    pub var_screen_info: VarScreeninfo,
    pub fix_screen_info: FixScreeninfo,
}

impl Framebuffer {
    pub fn new(path_to_device: &str) -> Result<Framebuffer, FramebufferError> {
        let device = try!(OpenOptions::new().read(true).write(true).open(path_to_device));

        let var_screen_info = try!(Framebuffer::get_var_screeninfo(&device));
        let fix_screen_info = try!(Framebuffer::get_fix_screeninfo(&device));

        let frame_length = (fix_screen_info.line_length * var_screen_info.yres) as usize;
        let frame = Mmap::open_with_offset(&device, Protection::ReadWrite, 0, frame_length);
        match frame {
            Ok(frame_result) => 
                Ok(Framebuffer {
                    device: device,
                    frame: frame_result,
                    var_screen_info: var_screen_info,
                    fix_screen_info: fix_screen_info,
                }),
                Err(_) => Err(
                    FramebufferError::new(
                    FramebufferErrorKind::IoError,
                    &format!("Could not map memory! Mem start: {} Mem stop: {}", 0, frame_length))
                ),
        }

    }

    ///Writes a frame to the Framebuffer.
    pub fn write_frame(&mut self, frame: &[u8]) {
        unsafe { self.frame.as_mut_slice() }.write_all(frame).unwrap();
    }

    ///Creates a FixScreeninfo struct and fills it using ioctl.
    pub fn get_fix_screeninfo(device: &File) -> Result<FixScreeninfo, FramebufferError> {
        let mut info: FixScreeninfo = Default::default();
        let result = unsafe { ioctl(device.as_raw_fd(), FBIOGET_FSCREENINFO, &mut info) };
        match result {
            -1 => Err(FramebufferError::new(FramebufferErrorKind::IoctlFailed, "Ioctl returned -1")),
            _ => Ok(info),
        }
    }

    ///Creates a VarScreeninfo struct and fills it using ioctl.
    pub fn get_var_screeninfo(device: &File) -> Result<VarScreeninfo, FramebufferError> {
        let mut info: VarScreeninfo = Default::default();
        let result = unsafe { ioctl(device.as_raw_fd(), FBIOGET_VSCREENINFO, &mut info) };
        match result {
            -1 => Err(FramebufferError::new(FramebufferErrorKind::IoctlFailed, "Ioctl returned -1")),
            _ => Ok(info),
        }
    }

    pub fn put_var_screeninfo(device: &File, screeninfo: &VarScreeninfo) -> Result<i32, FramebufferError> {
        match unsafe { ioctl(device.as_raw_fd(), FBIOPUT_VSCREENINFO, &screeninfo) } {
            -1 => Err(FramebufferError::new(FramebufferErrorKind::IoctlFailed, "Ioctl returned -1")),
            ret => Ok(ret),
        }
    }

    ///Sets the tty graphics mode. Make sure to change it back to KdMode::Text after the program is
    ///done!
    pub fn set_kd_mode(kd_mode: KdMode) -> Result<i32, FramebufferError> {
        match unsafe { ioctl(0, KDSETMODE, kd_mode) } {
            -1 => Err(FramebufferError::new(FramebufferErrorKind::IoctlFailed, "Ioctl returned - 1")),
            ret => Ok(ret),
        }
    }
}