#![warn(missing_docs)]
extern crate core;
extern crate drm_ffi;
extern crate drm_fourcc;
extern crate nix;
extern crate bytemuck;
pub(crate) mod util;
pub mod buffer;
pub mod control;
use std::ffi::{OsStr, OsString};
use std::os::unix::{
ffi::OsStringExt,
io::{AsFd, AsRawFd},
};
use std::time::Duration;
pub use drm_ffi::result::SystemError;
use util::*;
pub trait Device: AsFd {
fn acquire_master_lock(&self) -> Result<(), SystemError> {
drm_ffi::auth::acquire_master(self.as_fd().as_raw_fd())?;
Ok(())
}
fn release_master_lock(&self) -> Result<(), SystemError> {
drm_ffi::auth::release_master(self.as_fd().as_raw_fd())?;
Ok(())
}
#[deprecated(note = "Consider opening a render node instead.")]
fn generate_auth_token(&self) -> Result<AuthToken, SystemError> {
let token = drm_ffi::auth::get_magic_token(self.as_fd().as_raw_fd())?;
Ok(AuthToken(token.magic))
}
fn authenticate_auth_token(&self, token: AuthToken) -> Result<(), SystemError> {
drm_ffi::auth::auth_magic_token(self.as_fd().as_raw_fd(), token.0)?;
Ok(())
}
fn set_client_capability(
&self,
cap: ClientCapability,
enable: bool,
) -> Result<(), SystemError> {
drm_ffi::set_capability(self.as_fd().as_raw_fd(), cap as u64, enable)?;
Ok(())
}
fn get_bus_id(&self) -> Result<OsString, SystemError> {
let mut buffer = Vec::new();
let _ = drm_ffi::get_bus_id(self.as_fd().as_raw_fd(), Some(&mut buffer))?;
let bus_id = OsString::from_vec(buffer);
Ok(bus_id)
}
fn authenticated(&self) -> Result<bool, SystemError> {
let client = drm_ffi::get_client(self.as_fd().as_raw_fd(), 0)?;
Ok(client.auth == 1)
}
fn get_driver_capability(&self, cap: DriverCapability) -> Result<u64, SystemError> {
let cap = drm_ffi::get_capability(self.as_fd().as_raw_fd(), cap as u64)?;
Ok(cap.value)
}
#[allow(missing_docs)]
fn get_driver(&self) -> Result<Driver, SystemError> {
let mut name = Vec::new();
let mut date = Vec::new();
let mut desc = Vec::new();
let _ = drm_ffi::get_version(
self.as_fd().as_raw_fd(),
Some(&mut name),
Some(&mut date),
Some(&mut desc),
)?;
let name = OsString::from_vec(unsafe { transmute_vec(name) });
let date = OsString::from_vec(unsafe { transmute_vec(date) });
let desc = OsString::from_vec(unsafe { transmute_vec(desc) });
let driver = Driver { name, date, desc };
Ok(driver)
}
fn wait_vblank(
&self,
target_sequence: VblankWaitTarget,
flags: VblankWaitFlags,
high_crtc: u32,
user_data: usize,
) -> Result<VblankWaitReply, SystemError> {
use drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_HIGH_CRTC_MASK;
use drm_ffi::_DRM_VBLANK_HIGH_CRTC_SHIFT;
let high_crtc_mask = _DRM_VBLANK_HIGH_CRTC_MASK >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
if (high_crtc & !high_crtc_mask) != 0 {
return Err(SystemError::InvalidArgument);
}
let (sequence, wait_type) = match target_sequence {
VblankWaitTarget::Absolute(n) => {
(n, drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_ABSOLUTE)
}
VblankWaitTarget::Relative(n) => {
(n, drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_RELATIVE)
}
};
let type_ = wait_type | (high_crtc << _DRM_VBLANK_HIGH_CRTC_SHIFT) | flags.bits();
let reply = drm_ffi::wait_vblank(self.as_fd().as_raw_fd(), type_, sequence, user_data)?;
let time = match (reply.tval_sec, reply.tval_usec) {
(0, 0) => None,
(sec, usec) => Some(Duration::new(sec as u64, (usec * 1000) as u32)),
};
Ok(VblankWaitReply {
frame: reply.sequence,
time,
})
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct AuthToken(u32);
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Driver {
pub name: OsString,
pub date: OsString,
pub desc: OsString,
}
impl Driver {
pub fn name(&self) -> &OsStr {
self.name.as_ref()
}
pub fn date(&self) -> &OsStr {
self.date.as_ref()
}
pub fn description(&self) -> &OsStr {
self.desc.as_ref()
}
}
#[allow(clippy::upper_case_acronyms)]
#[repr(u64)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum DriverCapability {
DumbBuffer = drm_ffi::DRM_CAP_DUMB_BUFFER as u64,
VBlankHighCRTC = drm_ffi::DRM_CAP_VBLANK_HIGH_CRTC as u64,
DumbPreferredDepth = drm_ffi::DRM_CAP_DUMB_PREFERRED_DEPTH as u64,
DumbPreferShadow = drm_ffi::DRM_CAP_DUMB_PREFER_SHADOW as u64,
Prime = drm_ffi::DRM_CAP_PRIME as u64,
MonotonicTimestamp = drm_ffi::DRM_CAP_TIMESTAMP_MONOTONIC as u64,
ASyncPageFlip = drm_ffi::DRM_CAP_ASYNC_PAGE_FLIP as u64,
CursorWidth = drm_ffi::DRM_CAP_CURSOR_WIDTH as u64,
CursorHeight = drm_ffi::DRM_CAP_CURSOR_HEIGHT as u64,
AddFB2Modifiers = drm_ffi::DRM_CAP_ADDFB2_MODIFIERS as u64,
PageFlipTarget = drm_ffi::DRM_CAP_PAGE_FLIP_TARGET as u64,
CRTCInVBlankEvent = drm_ffi::DRM_CAP_CRTC_IN_VBLANK_EVENT as u64,
SyncObj = drm_ffi::DRM_CAP_SYNCOBJ as u64,
}
#[repr(u64)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum ClientCapability {
Stereo3D = drm_ffi::DRM_CLIENT_CAP_STEREO_3D as u64,
UniversalPlanes = drm_ffi::DRM_CLIENT_CAP_UNIVERSAL_PLANES as u64,
Atomic = drm_ffi::DRM_CLIENT_CAP_ATOMIC as u64,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum VblankWaitTarget {
Absolute(u32),
Relative(u32),
}
bitflags::bitflags! {
pub struct VblankWaitFlags : u32 {
const EVENT = drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_EVENT;
const NEXT_ON_MISS = drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_NEXTONMISS;
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct VblankWaitReply {
frame: u32,
time: Option<Duration>,
}
impl VblankWaitReply {
pub fn frame(&self) -> u32 {
self.frame
}
pub fn time(&self) -> Option<Duration> {
self.time
}
}