use iPoint;
use buffer;
use control::{self, ResourceHandle, ResourceInfo};
use result::*;
use ffi;
use control::framebuffer::Handle as FBHandle;
use control::connector::Handle as ConHandle;
use std::any::Any;
use std::boxed::Box;
use std::io::Read;
use std::mem;
use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, From, Into)]
pub struct Handle(control::RawHandle);
impl ResourceHandle for Handle {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Info {
handle: Handle,
position: (u32, u32),
mode: Option<control::Mode>,
fb: control::framebuffer::Handle,
gamma_length: u32,
}
impl Info {
pub fn position(&self) -> (u32, u32) {
self.position
}
pub fn mode(&self) -> Option<control::Mode> {
self.mode.clone()
}
pub fn fb(&self) -> FBHandle {
self.fb
}
}
impl control::property::LoadProperties for Handle {
const TYPE: u32 = ffi::DRM_MODE_OBJECT_CRTC;
}
impl ResourceInfo for Info {
type Handle = Handle;
fn load_from_device<T>(device: &T, handle: Handle) -> Result<Self>
where
T: control::Device,
{
let crtc = {
let mut raw: ffi::drm_mode_crtc = Default::default();
raw.crtc_id = handle.0;
unsafe {
try!(ffi::ioctl_mode_getcrtc(device.as_raw_fd(), &mut raw));
}
Self {
handle: handle,
position: (raw.x, raw.y),
mode: if raw.mode_valid != 0 {
Some(control::Mode {
mode: raw.mode.clone(),
})
} else {
None
},
fb: control::framebuffer::Handle::from(raw.fb_id),
gamma_length: raw.gamma_size,
}
};
Ok(crtc)
}
fn handle(&self) -> Self::Handle {
self.handle
}
}
pub fn set<T>(
device: &T,
handle: Handle,
fb: FBHandle,
cons: &[ConHandle],
position: (u32, u32),
mode: Option<control::Mode>,
) -> Result<()>
where
T: control::Device,
{
let mut raw: ffi::drm_mode_crtc = Default::default();
raw.x = position.0;
raw.y = position.1;
raw.crtc_id = handle.into();
raw.fb_id = fb.into();
raw.set_connectors_ptr = cons.as_ptr() as u64;
raw.count_connectors = cons.len() as u32;
match mode {
Some(m) => {
raw.mode = m.mode;
raw.mode_valid = 1;
}
_ => (),
};
unsafe {
try!(ffi::ioctl_mode_setcrtc(device.as_raw_fd(), &mut raw));
}
Ok(())
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PageFlipFlags {
PageFlipEvent = ffi::DRM_MODE_PAGE_FLIP_EVENT,
PageFlipAsync = ffi::DRM_MODE_PAGE_FLIP_ASYNC,
}
pub fn page_flip<T>(device: &T, handle: Handle, fb: FBHandle, flags: &[PageFlipFlags]) -> Result<()>
where
T: control::Device,
{
let mut raw: ffi::drm_mode_crtc_page_flip = Default::default();
raw.fb_id = fb.into();
raw.crtc_id = handle.into();
raw.flags = flags.into_iter().fold(0, |val, flag| val | *flag as u32);
raw.user_data = handle.0 as u64;
unsafe {
try!(ffi::ioctl_mode_page_flip(device.as_raw_fd(), &mut raw));
}
Ok(())
}
pub struct Events {
event_buf: [u8; 1024],
amount: usize,
i: usize,
}
pub enum Event {
Vblank(VblankEvent),
PageFlip(PageFlipEvent),
Unknown(Vec<u8>),
}
pub struct VblankEvent {
pub frame: u32,
pub duration: Duration,
pub crtc: Handle,
}
pub struct PageFlipEvent {
pub frame: u32,
pub duration: Duration,
pub crtc: Handle,
}
impl Iterator for Events {
type Item = Event;
fn next(&mut self) -> Option<Event> {
if self.amount > 0 && self.i < self.amount {
let event = unsafe {
&*(self.event_buf.as_ptr().offset(self.i as isize) as *const ffi::drm_event)
};
self.i += event.length as usize;
match event.type_ {
x if x == ffi::DRM_EVENT_VBLANK => {
let vblank_event: &ffi::drm_event_vblank =
unsafe { mem::transmute(event) };
Some(Event::Vblank(VblankEvent {
frame: vblank_event.sequence,
duration: Duration::new(
vblank_event.tv_sec as u64,
vblank_event.tv_usec * 100,
),
crtc: Handle::from(vblank_event.user_data as u32),
}))
}
x if x == ffi::DRM_EVENT_FLIP_COMPLETE => {
let vblank_event: &ffi::drm_event_vblank =
unsafe { mem::transmute(event) };
Some(Event::PageFlip(PageFlipEvent {
frame: vblank_event.sequence,
duration: Duration::new(
vblank_event.tv_sec as u64,
vblank_event.tv_usec * 1000,
),
crtc: Handle::from(if vblank_event.crtc_id != 0 {
vblank_event.crtc_id
} else {
vblank_event.user_data as u32
}),
}))
}
_ => Some(Event::Unknown(
self.event_buf[self.i - (event.length as usize)..self.i].to_vec(),
)),
}
} else {
None
}
}
}
pub fn receive_events<T>(device: &T) -> Result<Events>
where
T: control::Device,
{
struct DeviceWrapper<'a, T: control::Device + 'a>(&'a T);
impl<'a, T: control::Device> Read for DeviceWrapper<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> ::std::io::Result<usize> {
::nix::unistd::read(self.0.as_raw_fd(), buf).map_err(|err| match err {
::nix::Error::Sys(_) => ::std::io::Error::last_os_error(),
err => ::std::io::Error::new(::std::io::ErrorKind::Other, err),
})
}
}
let mut wrapper = DeviceWrapper(device);
let mut event_buf: [u8; 1024] = [0; 1024];
let amount = try!(wrapper.read(&mut event_buf));
Ok(Events {
event_buf,
amount,
i: 0,
})
}
pub fn set_cursor<T, B>(device: &T, handle: Handle, buffer: &B) -> Result<()>
where
T: control::Device,
B: buffer::Buffer + ?Sized,
{
let dimensions = buffer.size();
let mut raw: ffi::drm_mode_cursor = Default::default();
raw.flags = ffi::DRM_MODE_CURSOR_BO;
raw.crtc_id = handle.into();
raw.width = dimensions.0;
raw.height = dimensions.1;
raw.handle = buffer.handle().into();
unsafe {
try!(ffi::ioctl_mode_cursor(device.as_raw_fd(), &mut raw));
}
Ok(())
}
pub fn set_cursor2<T, B>(device: &T, handle: Handle, buffer: &B, hotspot: iPoint) -> Result<()>
where
T: control::Device,
B: buffer::Buffer + ?Sized,
{
let dimensions = buffer.size();
let mut raw: ffi::drm_mode_cursor2 = Default::default();
raw.flags = ffi::DRM_MODE_CURSOR_BO;
raw.crtc_id = handle.into();
raw.width = dimensions.0;
raw.height = dimensions.1;
raw.handle = buffer.handle().into();
raw.hot_x = hotspot.0;
raw.hot_y = hotspot.1;
unsafe {
try!(ffi::ioctl_mode_cursor2(device.as_raw_fd(), &mut raw));
}
Ok(())
}
pub fn move_cursor<T>(device: &T, handle: Handle, to: iPoint) -> Result<()>
where
T: control::Device,
{
let mut raw: ffi::drm_mode_cursor = Default::default();
raw.flags = ffi::DRM_MODE_CURSOR_MOVE;
raw.crtc_id = handle.into();
raw.x = to.0;
raw.y = to.1;
unsafe {
try!(ffi::ioctl_mode_cursor(device.as_raw_fd(), &mut raw));
}
Ok(())
}
pub fn clear_cursor<T>(device: &T, handle: Handle) -> Result<()>
where
T: control::Device,
{
let mut raw: ffi::drm_mode_cursor = Default::default();
raw.flags = ffi::DRM_MODE_CURSOR_BO;
raw.crtc_id = handle.into();
raw.width = 0;
raw.height = 0;
raw.handle = 0;
unsafe {
try!(ffi::ioctl_mode_cursor(device.as_raw_fd(), &mut raw));
}
Ok(())
}
#[derive(Debug, Clone)]
pub struct GammaRamp {
pub red: Box<[u16]>,
pub green: Box<[u16]>,
pub blue: Box<[u16]>,
}
pub fn gamma<T>(device: &T, handle: Handle) -> Result<GammaRamp>
where
T: control::Device,
{
let info = Info::load_from_device(device, handle)?;
let mut raw: ffi::drm_mode_crtc_lut = Default::default();
raw.crtc_id = handle.into();
raw.gamma_size = info.gamma_length;
let red = ffi_buf!(raw.red, info.gamma_length as usize);
let green = ffi_buf!(raw.green, info.gamma_length as usize);
let blue = ffi_buf!(raw.blue, info.gamma_length as usize);
unsafe {
try!(ffi::ioctl_mode_getgamma(device.as_raw_fd(), &mut raw));
}
Ok(GammaRamp {
red: red.into_boxed_slice(),
green: green.into_boxed_slice(),
blue: blue.into_boxed_slice(),
})
}
pub fn set_gamma<T>(device: &T, handle: Handle, mut gamma: GammaRamp) -> Result<()>
where
T: control::Device,
{
let info = Info::load_from_device(device, handle)?;
if gamma.red.len() as u32 != info.gamma_length {
return Err(Error::from_kind(ErrorKind::InvalidGammaSize(
gamma.red.len(),
info.gamma_length,
)));
}
if gamma.green.len() as u32 != info.gamma_length {
return Err(Error::from_kind(
ErrorKind::InvalidGammaSize(gamma.green.len(), info.gamma_length).into(),
));
}
if gamma.blue.len() as u32 != info.gamma_length {
return Err(Error::from_kind(
ErrorKind::InvalidGammaSize(gamma.blue.len(), info.gamma_length).into(),
));
}
let mut raw: ffi::drm_mode_crtc_lut = Default::default();
raw.crtc_id = handle.into();
raw.gamma_size = info.gamma_length;
raw.red = gamma.red.as_mut_ptr() as u64;
raw.green = gamma.green.as_mut_ptr() as u64;
raw.blue = gamma.blue.as_mut_ptr() as u64;
unsafe {
try!(ffi::ioctl_mode_setgamma(device.as_raw_fd(), &mut raw));
}
Ok(())
}