yuno 0.2.0

Multimedia UI layout and rendering framework powered by Skia.
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")
}