use anyhow::{Context, Result};
use drm::{
ClientCapability, Device as DrmDevice,
buffer::Handle as GemHandle,
control::{
AtomicCommitFlags, Device as ControlDevice, PageFlipFlags, PageFlipTarget, ResourceHandles,
atomic::AtomicModeReq, connector, crtc, encoder, framebuffer, plane,
},
};
use std::fs::{File, OpenOptions};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
#[derive(Debug)]
pub struct DrmCard {
file: File,
path: String,
}
impl DrmCard {
pub fn open_first_primary() -> Result<Self> {
for idx in 0..64u32 {
let path = format!("/dev/dri/card{idx}");
match OpenOptions::new().read(true).write(true).open(&path) {
Ok(file) => return Ok(Self { file, path }),
Err(_) => continue,
}
}
Err(anyhow::anyhow!(
"failed to open any /dev/dri/cardN primary node"
))
}
pub fn path(&self) -> &str {
&self.path
}
pub fn raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
pub fn enable_atomic(&self) -> Result<()> {
DrmDevice::set_client_capability(self, ClientCapability::Atomic, true)
.context("enabling DRM atomic client capability")
}
pub fn resource_handles(&self) -> Result<ResourceHandles> {
ControlDevice::resource_handles(self).context("querying DRM resource handles")
}
pub fn plane_handles(&self) -> Result<Vec<plane::Handle>> {
ControlDevice::plane_handles(self).context("querying DRM plane handles")
}
pub fn get_connector(&self, handle: connector::Handle) -> Result<connector::Info> {
ControlDevice::get_connector(self, handle, false).context("querying DRM connector")
}
pub fn get_encoder(&self, handle: encoder::Handle) -> Result<encoder::Info> {
ControlDevice::get_encoder(self, handle).context("querying DRM encoder")
}
pub fn get_crtc(&self, handle: crtc::Handle) -> Result<crtc::Info> {
ControlDevice::get_crtc(self, handle).context("querying DRM crtc")
}
pub fn get_plane(&self, handle: plane::Handle) -> Result<plane::Info> {
ControlDevice::get_plane(self, handle).context("querying DRM plane")
}
pub fn set_crtc(
&self,
crtc: crtc::Handle,
framebuffer: Option<framebuffer::Handle>,
pos: (u32, u32),
conns: &[connector::Handle],
mode: Option<drm::control::Mode>,
) -> Result<()> {
ControlDevice::set_crtc(self, crtc, framebuffer, pos, conns, mode)
.context("setting DRM crtc")
}
#[allow(unused)]
pub fn set_plane(
&self,
plane: plane::Handle,
crtc: crtc::Handle,
framebuffer: Option<framebuffer::Handle>,
flags: u32,
crtc_rect: (i32, i32, u32, u32),
src_rect: (u32, u32, u32, u32),
) -> Result<()> {
ControlDevice::set_plane(self, plane, crtc, framebuffer, flags, crtc_rect, src_rect)
.context("setting DRM plane")
}
pub fn page_flip(
&self,
crtc: crtc::Handle,
framebuffer: framebuffer::Handle,
flags: PageFlipFlags,
target: Option<PageFlipTarget>,
) -> Result<()> {
ControlDevice::page_flip(self, crtc, framebuffer, flags, target).context("page flip")
}
pub fn atomic_commit(&self, flags: AtomicCommitFlags, req: AtomicModeReq) -> Result<()> {
ControlDevice::atomic_commit(self, flags, req).context("atomic commit")
}
pub fn create_property_blob<T>(
&self,
data: &T,
) -> Result<drm::control::property::Value<'static>> {
ControlDevice::create_property_blob(self, data).context("creating property blob")
}
#[allow(unused)]
pub fn destroy_property_blob(&self, blob: u64) -> Result<()> {
ControlDevice::destroy_property_blob(self, blob).context("destroying property blob")
}
pub fn prime_to_gem_handle(&self, prime_fd: RawFd) -> Result<GemHandle> {
assert!(prime_fd >= 0);
let handle = self
.prime_fd_to_buffer(unsafe { BorrowedFd::borrow_raw(prime_fd) })
.context("Fail to import DMA-BUF FD to DRM GEM Handle")?;
Ok(handle)
}
}
impl AsFd for DrmCard {
fn as_fd(&self) -> BorrowedFd<'_> {
self.file.as_fd()
}
}
impl DrmDevice for DrmCard {}
impl ControlDevice for DrmCard {}
pub fn open_first_primary() -> Result<DrmCard> {
DrmCard::open_first_primary().context("opening DRM primary node")
}