extern crate libc;
#[macro_use]
extern crate bitflags;
extern crate memmap;
#[cfg(feature = "tokio")]
extern crate futures;
#[cfg(feature = "tokio")]
extern crate tokio_core;
#[cfg(feature = "tokio")]
extern crate mio;
mod ioctl_vals;
mod ffi;
mod fourcc;
#[cfg(feature = "tokio")]
pub mod tokio;
pub mod mode;
use ioctl_vals::*;
use mode::*;
#[cfg(feature = "tokio")]
use tokio_core::reactor::Handle;
use libc::ioctl;
use std::ops::{Deref, DerefMut};
use std::fs::{self, File, OpenOptions};
use std::io::BufReader;
use std::io::prelude::*;
use std::io::ErrorKind;
use std::io;
use std::iter::repeat;
use std::mem::{size_of, transmute};
use std::os::raw::*;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
use std::str;
use std::string::FromUtf8Error;
use std::time::Instant;
#[allow(dead_code)]
mod consts {
pub const DRM_EVENT_VBLANK: u32 = 0x01;
pub const DRM_EVENT_FLIP_COMPLETE: u32 = 0x02;
pub const DRM_VBLANK_ABSOLUTE: u32 = 0;
pub const DRM_VBLANK_RELATIVE: u32 = 1;
pub const DRM_VBLANK_HIGH_CRTC_MASK: u32 = 62;
pub const DRM_VBLANK_EVENT: u32 = 67108864;
pub const DRM_VBLANK_FLIP: u32 = 134217728;
pub const DRM_VBLANK_NEXTONMISS: u32 = 268435456;
pub const DRM_VBLANK_SECONDARY: u32 = 536870912;
pub const DRM_VBLANK_SIGNAL: u32 = 1073741824;
pub const DRM_MODE_CURSOR_BO: u32 = 0x01;
pub const DRM_MODE_CURSOR_MOVE: u32 = 0x02;
}
fn check_ioctl_err(ret: c_int) -> io::Result<()> {
if ret < 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
const BUFFER_CAPACITY: usize = 1024;
#[derive(Debug)]
pub struct Device {
fd: BufReader<File>,
}
impl Device {
pub fn cards() -> io::Result<Box<Iterator<Item=PathBuf>>> {
Ok(Box::new({
try!(fs::read_dir("/dev/dri"))
.filter_map(Result::ok)
.map(|e| e.path())
.filter(|p| p.file_name()
.and_then(|f| f.to_str())
.map(|f| f.starts_with("card"))
.unwrap_or(false))
}) as Box<_>)
}
pub fn open<P>(path: P) -> io::Result<Device>
where P: AsRef<Path>,
{
OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map(|f| {
Device {
fd: BufReader::with_capacity(BUFFER_CAPACITY, f),
}
})
}
fn try_clone(&self) -> io::Result<Device> {
Ok(Device {
fd: BufReader::with_capacity(0, self.fd.get_ref().try_clone()?)
})
}
pub fn set_nonblocking(&mut self, nonblocking: bool) -> io::Result<()> {
unsafe {
let old_flags = libc::fcntl(self.as_raw_fd(), libc::F_GETFL);
if old_flags == -1 {
return Err(io::Error::last_os_error());
}
let new_flags = if nonblocking {
old_flags | libc::O_NONBLOCK
} else {
old_flags & !libc::O_NONBLOCK
};
if new_flags != old_flags {
if libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new_flags) == -1 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
}
pub fn first_card() -> io::Result<Device> {
let mut cards: Vec<_> = try!(Device::cards()).collect();
cards.sort();
match cards.first() {
None => Err(io::Error::new(io::ErrorKind::NotFound,
"No cards found in /dev/dri".to_string())),
Some(p) => Device::open(p)
}
}
fn ioctl<T:DrmIoctl>(&self, arg: &mut T) -> io::Result<()> {
loop {
let ret = unsafe {
ioctl(self.as_raw_fd(), T::request(), arg.as_ptr())
};
return match check_ioctl_err(ret) {
Ok(ok) => Ok(ok),
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => Err(e)
}
}
}
pub fn set_master<'a>(&'a mut self) -> io::Result<Master<'a>> {
let ret = unsafe {
ioctl(self.as_raw_fd(), DRM_IOCTL_SET_MASTER, 0)
};
match check_ioctl_err(ret) {
Ok(_) => Ok(Master { dev: self }),
Err(err) => Err(err),
}
}
pub fn version(&self) -> io::Result<Version> {
let mut version: ffi::version = Default::default();
try!(self.ioctl(&mut version));
let mut name: Vec<u8> = repeat(0).take(version.name_len).collect();
version.name = name.as_mut_ptr() as *mut i8;
let mut date: Vec<u8> = repeat(0).take(version.date_len).collect();
version.date = date.as_mut_ptr() as *mut i8;
let mut desc: Vec<u8> = repeat(0).take(version.desc_len).collect();
version.desc = desc.as_mut_ptr() as *mut i8;
try!(self.ioctl(&mut version));
Ok(Version {
major: version.version_major,
minor: version.version_minor,
patchlevel: version.version_patchlevel,
name: String::from_utf8(name).map_err(FromUtf8Error::into_bytes),
date: String::from_utf8(date).map_err(FromUtf8Error::into_bytes),
desc: String::from_utf8(desc).map_err(FromUtf8Error::into_bytes),
})
}
pub fn get_resources(&self) -> io::Result<mode::Resources> {
mode::Resources::get(self)
}
pub fn get<T: mode::Resource>(&self, id: Id<T>) -> io::Result<T> {
T::get(self, id)
}
pub fn get_object_props<T: mode::Resource>(&self, id: Id<T>)
-> io::Result<Vec<(Id<Property>, u64)>>
{
impl DrmIoctl for ffi::mode_obj_get_properties {
fn request() -> c_ulong { DRM_IOCTL_MODE_OBJ_GETPROPERTIES }
}
let mut get_props = ffi::mode_obj_get_properties::default();
get_props.obj_id = id.as_u32();
get_props.obj_type = T::object_type();
self.ioctl(&mut get_props)?;
let mut props = Vec::new();
let mut prop_values = Vec::new();
while get_props.count_props > 0 {
let prev_count = get_props.count_props;
let no_id = unsafe { Id::from_u32(1).unwrap() };
props.resize(get_props.count_props as usize, no_id);
prop_values.resize(get_props.count_props as usize, 0u64);
get_props.props_ptr = props.as_mut_ptr() as usize as u64;
get_props.prop_values_ptr = prop_values.as_mut_ptr() as usize as u64;
self.ioctl(&mut get_props)?;
if get_props.count_props == prev_count {
break;
}
}
Ok(props.into_iter().zip(prop_values.into_iter()).collect())
}
pub fn busid(&self) -> io::Result<String> {
let mut unique = ffi::unique::default();
impl DrmIoctl for ffi::unique {
fn request() -> c_ulong { DRM_IOCTL_GET_UNIQUE }
}
try!(self.ioctl(&mut unique));
if unique.unique_len > 0 {
let mut s: Vec<u8> = repeat(0).collect();
unique.unique = s.as_mut_ptr() as *mut c_char;
try!(self.ioctl(&mut unique));
String::from_utf8(s)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
} else {
Ok(String::new())
}
}
pub fn request_vblank(&self, data: usize, crtc_num: u32) -> io::Result<()> {
let pipe = crtc_num;
let mut seq_type = if pipe > 1 {
((pipe << 1) as u32) & consts::DRM_VBLANK_HIGH_CRTC_MASK
} else if pipe > 0 {
consts::DRM_VBLANK_SECONDARY
} else {
0
};
seq_type |= consts::DRM_VBLANK_RELATIVE;
seq_type |= consts::DRM_VBLANK_EVENT;
impl DrmIoctl for ffi::wait_vblank_request {
fn request() -> c_ulong { DRM_IOCTL_WAIT_VBLANK }
}
let mut req = ffi::wait_vblank_request {
type_: unsafe { transmute(seq_type) },
sequence: 1,
signal: data as c_ulong,
};
self.ioctl(&mut req)
}
pub fn read_event(&mut self) -> io::Result<Event> {
let nbytes;
let result = {
let buf: &[u8] = try!(self.fd.fill_buf());
if buf.len() == 0 {
return Err(io::Error::new(io::ErrorKind::WouldBlock, "No events available"));
}
assert!(buf.len() >= size_of::<ffi::event>(),
"Malformed event from drm device: {} < {}",
buf.len(), size_of::<ffi::event>());
let ev_header: ffi::event = unsafe {
*transmute::<_, *const ffi::event>(buf.as_ptr())
};
if buf.len() < ev_header.length as usize {
nbytes = buf.len();
Err(io::Error::new(ErrorKind::InvalidData, "Short DRM event"))
} else {
nbytes = ev_header.length as usize;
match ev_header.type_ {
consts::DRM_EVENT_VBLANK | consts::DRM_EVENT_FLIP_COMPLETE => {
let ev: ffi::event_vblank = unsafe {
*transmute::<_, *const ffi::event_vblank>(buf.as_ptr())
};
let at: Instant = unsafe {
transmute(libc::timespec { tv_sec: ev.tv_sec as i64,
tv_nsec: ev.tv_usec as i64 * 1000 })
};
match ev.base.type_ {
consts::DRM_EVENT_VBLANK => Ok(Event::VBlank {
seq: ev.sequence,
tv: at,
user: ev.user_data }),
consts::DRM_EVENT_FLIP_COMPLETE => Ok(Event::PageFlip {
seq: ev.sequence,
tv: at,
user: ev.user_data }),
_ => unreachable!(),
}
}
_ => Ok(Event::Unknown),
}
}
};
self.fd.consume(nbytes);
result
}
pub fn capability(&self, cap: Capability) -> io::Result<u64> {
let mut call = ffi::get_cap {
capability: cap as u64,
value: 0,
};
impl DrmIoctl for ffi::get_cap {
fn request() -> c_ulong { DRM_IOCTL_GET_CAP }
}
try!(self.ioctl(&mut call));
Ok(call.value)
}
#[cfg(feature = "tokio")]
pub fn event_stream(self, handle: &Handle) -> io::Result<tokio::EventFuture> {
tokio::EventFuture::new(self, handle)
}
}
#[repr(u64)]
pub enum Capability {
DumbBuffer = 0x1,
VblankHighCrtc = 0x2,
DumbPreferredDepth = 0x3,
DumbPreferShadow = 0x4,
Prime = 0x5,
TimestampMonotonic = 0x6,
AsyncPageFlip = 0x7,
CursorWidth = 0x8,
CursorHeight = 0x9,
Addfb2Modifiers = 0x10,
}
impl AsRawFd for Device {
fn as_raw_fd(&self) -> RawFd {
self.fd.get_ref().as_raw_fd()
}
}
impl IntoRawFd for Device {
fn into_raw_fd(self) -> RawFd {
self.fd.into_inner().into_raw_fd()
}
}
impl FromRawFd for Device {
unsafe fn from_raw_fd(fd: RawFd) -> Device {
Device {
fd: BufReader::with_capacity(BUFFER_CAPACITY, File::from_raw_fd(fd)),
}
}
}
trait DrmIoctl {
fn request() -> c_ulong;
fn as_ptr(&mut self) -> *mut Self {
self as *mut Self
}
}
impl DrmIoctl for ffi::version {
fn request() -> c_ulong { DRM_IOCTL_VERSION }
}
impl DrmIoctl for ffi::mode_cursor {
fn request() -> c_ulong { DRM_IOCTL_MODE_CURSOR }
}
impl DrmIoctl for ffi::mode_cursor2 {
fn request() -> c_ulong { DRM_IOCTL_MODE_CURSOR2 }
}
pub trait GemHandle {
fn bo_handle(&self) -> u32;
fn width(&self) -> u32;
fn height(&self) -> u32;
}
#[derive(Debug)]
pub struct Master<'a> {
dev: &'a mut Device,
}
impl<'a> Deref for Master<'a> {
type Target = Device;
fn deref(&self) -> &Device {
self.dev
}
}
impl<'a> DerefMut for Master<'a> {
fn deref_mut(&mut self) -> &mut Device {
self.dev
}
}
impl<'a> Drop for Master<'a> {
fn drop(&mut self) {
let _ret = unsafe {
ioctl(self.dev.as_raw_fd(), DRM_IOCTL_DROP_MASTER, 0)
};
}
}
impl<'a> Master<'a>
{
pub fn set_cursor<B>(&self, crtc_id: Id<mode::Crtc>, bo: &B) -> io::Result<()>
where B: GemHandle
{
let mut arg = ffi::mode_cursor {
flags: consts::DRM_MODE_CURSOR_BO,
crtc_id: crtc_id.as_u32(),
width: bo.width(),
height: bo.height(),
handle: bo.bo_handle(),
..Default::default()
};
self.ioctl(&mut arg)
}
pub fn set_cursor2<B>(&self, crtc_id: Id<mode::Crtc>, bo: &B,
hot_x: i32, hot_y: i32)
-> io::Result<()>
where B: GemHandle
{
let mut arg = ffi::mode_cursor2 {
flags: consts::DRM_MODE_CURSOR_BO,
crtc_id: crtc_id.as_u32(),
width: bo.width(),
height: bo.height(),
handle: bo.bo_handle(),
hot_x: hot_x, hot_y: hot_y,
..Default::default()
};
self.ioctl(&mut arg)
}
pub fn move_cursor(&self, crtc_id: Id<mode::Crtc>, x: i32, y: i32) -> io::Result<()> {
let mut arg = ffi::mode_cursor {
flags: consts::DRM_MODE_CURSOR_MOVE,
crtc_id: crtc_id.as_u32(),
x: x,
y: y,
..Default::default()
};
self.ioctl(&mut arg)
}
pub fn set_crtc(&self,
crtc_id: Id<mode::Crtc>,
fb: Option<Id<mode::Fb>>,
x: u32, y: u32,
connectors: &[Id<mode::Connector>],
mode: Option<&mode::ModeInfo>)
-> io::Result<()>
{
#[repr(C)]
struct SetCrtc {
set_connectors_ptr: u64,
count_connectors: u32,
crtc_id: u32,
fb_id: u32,
x: u32,
y: u32,
gamma_size: u32,
mode_valid: u32,
mode: mode::ModeInfo,
};
impl DrmIoctl for SetCrtc {
fn request() -> c_ulong { DRM_IOCTL_MODE_SETCRTC }
}
let mut crtc = SetCrtc {
set_connectors_ptr: unsafe { transmute(connectors.as_ptr()) },
count_connectors: connectors.len() as u32,
crtc_id: crtc_id.as_u32(),
fb_id: fb.map(|id| id.as_u32()).unwrap_or(0),
x: x,
y: y,
gamma_size: 0,
mode_valid: if mode.is_some() { 1 } else { 0 },
mode: match mode {
None => unsafe { std::mem::zeroed() },
Some(mode) => { *mode }
}
};
self.dev.ioctl(&mut crtc)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Version {
major: i32,
minor: i32,
patchlevel: i32,
name: Result<String, Vec<u8>>,
date: Result<String, Vec<u8>>,
desc: Result<String, Vec<u8>>
}
impl Version {
pub fn number(&self) -> (u32, u32, u32) {
(self.major as u32, self.minor as u32, self.patchlevel as u32)
}
pub fn name(&self) -> Result<&str, &[u8]> {
self.name.as_ref().map(String::as_str).map_err(Vec::as_slice)
}
pub fn date(&self) -> Result<&str, &[u8]> {
self.date.as_ref().map(String::as_str).map_err(Vec::as_slice)
}
pub fn desc(&self) -> Result<&str, &[u8]> {
self.desc.as_ref().map(String::as_str).map_err(Vec::as_slice)
}
}
#[derive(Copy,Clone,Debug,Eq,PartialEq,Hash)]
pub enum EventType {
VBlank,
FlipComplete,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Event {
Unknown,
VBlank { seq: u32, tv: Instant, user: u64 },
PageFlip { seq: u32, tv: Instant, user: u64 },
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
struct Event_ {
type_: EventType,
user_data: u64,
time: Instant,
sequence: u32,
}
#[allow(dead_code)]
impl Event_
{
pub fn user_data(&self) -> u64 { self.user_data }
pub fn event_type(&self) -> EventType { self.type_ }
pub fn sequence(&self) -> u32 { self.sequence }
pub fn time_val(&self) -> Instant { self.time }
fn read_event(dev: &mut Device) -> io::Result<Event_>
{
const EVENT_BYTES: usize = 32;
let mut buf = [0; EVENT_BYTES];
let n = try!(dev.fd.read(&mut buf[..]));
if n == 0 {
return Err(io::Error::new(io::ErrorKind::WouldBlock, "no events available"));
}
assert_eq!(EVENT_BYTES, n);
let ev: ffi::event_vblank = unsafe {
transmute(buf)
};
let type_ = match ev.base.type_ {
consts::DRM_EVENT_VBLANK => EventType::VBlank,
consts::DRM_EVENT_FLIP_COMPLETE => EventType::FlipComplete,
_ => return Err(io::Error::new(io::ErrorKind::InvalidData,
"Unknow DRM event type")),
};
let at: Instant = unsafe {
transmute(libc::timespec { tv_sec: ev.tv_sec as i64,
tv_nsec: ev.tv_usec as i64 * 1000 })
};
println!("current instant: {:?}", Instant::now());
println!(" evil instant: {:?}", at);
Ok(Event_ {
type_: type_,
user_data: ev.user_data,
time: at,
sequence: ev.sequence,
})
}
}