linux-drm 0.6.0

Client for the Linux Direct Rendering Manager and Kernel Modesetting APIs.
Documentation
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::ops::{BitAnd, BitOr};

mod atomic;
mod buffer;
mod props;

pub use atomic::*;
pub use buffer::*;
pub use props::*;

macro_rules! id_newtype {
    ($name:ident) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        #[repr(transparent)]
        pub struct $name(pub u32);

        impl $name {
            /// Returns the raw `u32` value of the id.
            #[inline(always)]
            pub const fn to_raw(self) -> u32 {
                self.0
            }

            /// Returns true if the id is zero, representing the absense of an object.
            #[inline(always)]
            pub const fn is_null(self) -> bool {
                self.0 == 0
            }
        }

        impl props::AsRawPropertyValue for $name {
            #[inline(always)]
            fn as_raw_property_value(&self) -> u64 {
                self.0 as u64
            }
        }

        impl props::IntoRawPropertyValue for $name {
            #[inline(always)]
            fn into_raw_property_value(self) -> (u64, Option<Box<dyn core::any::Any>>) {
                (self.0 as u64, None)
            }
        }
    };
}

id_newtype!(FramebufferId);
id_newtype!(CrtcId);
id_newtype!(ConnectorId);
id_newtype!(EncoderId);
id_newtype!(PlaneId);
id_newtype!(BufferObjectId);
id_newtype!(PropertyId);
id_newtype!(BlobId);

#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum ObjectId {
    Crtc(CrtcId),
    Connector(ConnectorId),
    Encoder(EncoderId),
    Mode(u32),
    Property(PropertyId),
    Framebuffer(FramebufferId),
    Blob(BlobId),
    Plane(PlaneId),
}

impl ObjectId {
    pub fn as_raw_type_and_id(self) -> (u32, u32) {
        use crate::ioctl;
        match self {
            ObjectId::Crtc(id) => (ioctl::DRM_MODE_OBJECT_CRTC, id.0),
            ObjectId::Connector(id) => (ioctl::DRM_MODE_OBJECT_CONNECTOR, id.0),
            ObjectId::Encoder(id) => (ioctl::DRM_MODE_OBJECT_ENCODER, id.0),
            ObjectId::Mode(id) => (ioctl::DRM_MODE_OBJECT_MODE, id),
            ObjectId::Property(id) => (ioctl::DRM_MODE_OBJECT_PROPERTY, id.0),
            ObjectId::Framebuffer(id) => (ioctl::DRM_MODE_OBJECT_FB, id.0),
            ObjectId::Blob(id) => (ioctl::DRM_MODE_OBJECT_BLOB, id.0),
            ObjectId::Plane(id) => (ioctl::DRM_MODE_OBJECT_PLANE, id.0),
        }
    }
}

impl From<CrtcId> for ObjectId {
    fn from(value: CrtcId) -> Self {
        Self::Crtc(value)
    }
}

impl From<ConnectorId> for ObjectId {
    fn from(value: ConnectorId) -> Self {
        Self::Connector(value)
    }
}

impl From<EncoderId> for ObjectId {
    fn from(value: EncoderId) -> Self {
        Self::Encoder(value)
    }
}

impl From<PropertyId> for ObjectId {
    fn from(value: PropertyId) -> Self {
        Self::Property(value)
    }
}

impl From<FramebufferId> for ObjectId {
    fn from(value: FramebufferId) -> Self {
        Self::Framebuffer(value)
    }
}

impl From<BlobId> for ObjectId {
    fn from(value: BlobId) -> Self {
        Self::Blob(value)
    }
}

impl From<PlaneId> for ObjectId {
    fn from(value: PlaneId) -> Self {
        Self::Plane(value)
    }
}

#[derive(Debug)]
pub struct CardResources {
    pub fb_ids: Vec<FramebufferId>,
    pub crtc_ids: Vec<CrtcId>,
    pub connector_ids: Vec<ConnectorId>,
    pub encoder_ids: Vec<EncoderId>,
    pub plane_ids: Vec<PlaneId>,
    pub min_width: u32,
    pub max_width: u32,
    pub min_height: u32,
    pub max_height: u32,
}

#[derive(Debug)]
pub struct ConnectorState {
    pub id: ConnectorId,
    pub current_encoder_id: EncoderId,
    pub connector_type: ConnectorType,
    pub connector_type_id: u32,
    pub connection_state: ConnectionState,
    pub width_mm: u32,
    pub height_mm: u32,
    pub subpixel_type: SubpixelType,
    pub modes: Vec<ModeInfo>,
    pub props: Vec<ModeProp>,
    pub available_encoder_ids: Vec<u32>,
}

impl ConnectorState {
    pub fn preferred_mode(&self) -> Option<&ModeInfo> {
        for mode in &self.modes {
            if (mode.typ & crate::ioctl::DRM_MODE_TYPE_PREFERRED) != 0 {
                return Some(mode);
            }
        }
        None
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u32)]
pub enum ConnectionState {
    Connected = 1,
    Disconnected = 2,
    Unknown = 3,
}

impl From<u32> for ConnectionState {
    fn from(value: u32) -> Self {
        match value {
            1 => Self::Connected,
            2 => Self::Disconnected,
            _ => Self::Unknown,
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
pub enum ConnectorType {
    Unknown = 0,
    Vga = 1,
    DviI = 2,
    DviD = 3,
    DviA = 4,
    Composite = 5,
    SVideo = 6,
    Lvds = 7,
    Component = 8,
    NinePinDin = 9,
    DisplayPort = 10,
    HdmiA = 11,
    HdmiB = 12,
    Tv = 13,
    Edp = 14,
    Virtual = 15,
    Dsi = 16,
    Dpi = 17,
    Writeback = 18,
    Spi = 19,
    Usb = 20,
    Other = !0, // Not used by kernel, but used by us if kernel returns something we don't know
}

impl From<u32> for ConnectorType {
    #[inline]
    fn from(value: u32) -> Self {
        if value < 21 {
            // Safety: all values in this range are valid representations
            // of this enum, as described above.
            unsafe { core::mem::transmute(value) }
        } else {
            Self::Other
        }
    }
}

#[derive(Debug)]
pub struct EncoderState {
    pub encoder_id: EncoderId,
    pub encoder_type: u32,
    pub current_crtc_id: CrtcId,
    pub possible_crtcs: u32,
    pub possible_clones: u32,
}

#[derive(Debug)]
pub struct CrtcState {
    pub crtc_id: CrtcId,
    pub fb_id: FramebufferId,
    pub x: u32,
    pub y: u32,
    pub gamma_size: u32,
    pub mode_valid: u32,
    pub mode: ModeInfo,
}

impl From<crate::ioctl::DrmModeCrtc> for CrtcState {
    fn from(value: crate::ioctl::DrmModeCrtc) -> Self {
        Self {
            crtc_id: CrtcId(value.crtc_id),
            fb_id: FramebufferId(value.fb_id),
            x: value.x,
            y: value.y,
            gamma_size: value.gamma_size,
            mode_valid: value.mode_valid,
            mode: value.mode.into(),
        }
    }
}

#[derive(Debug)]
pub struct PlaneState {
    pub id: PlaneId,
    pub crtc_id: CrtcId,
    pub fb_id: FramebufferId,
    pub possible_crtcs: u32,
    pub gamma_size: u32,
}

#[derive(Debug)]
pub struct ModeInfo {
    pub name: Vec<u8>,
    pub clock: u32,
    pub hdisplay: u16,
    pub hsync_start: u16,
    pub hsync_end: u16,
    pub htotal: u16,
    pub hskew: u16,
    pub vdisplay: u16,
    pub vsync_start: u16,
    pub vsync_end: u16,
    pub vtotal: u16,
    pub vscan: u16,
    pub vrefresh: u32,
    pub flags: u32,
    pub typ: u32,
}

impl From<crate::ioctl::DrmModeInfo> for ModeInfo {
    fn from(value: crate::ioctl::DrmModeInfo) -> Self {
        let name = value.name[..].split(|c| *c == 0).next().unwrap();
        let name: &[u8] = unsafe { core::mem::transmute(name) };
        Self {
            name: name.to_vec(),
            clock: value.clock,
            hdisplay: value.hdisplay,
            hsync_start: value.hsync_start,
            hsync_end: value.hsync_end,
            htotal: value.htotal,
            hskew: value.hskew,
            vdisplay: value.vdisplay,
            vsync_start: value.vsync_start,
            vsync_end: value.vsync_end,
            vtotal: value.vtotal,
            vscan: value.vscan,
            vrefresh: value.vrefresh,
            flags: value.flags,
            typ: value.typ,
        }
    }
}

impl From<&ModeInfo> for crate::ioctl::DrmModeInfo {
    fn from(value: &ModeInfo) -> Self {
        let mut name_raw = [0_8; 32];
        let name_len = core::cmp::min(name_raw.len() - 1, value.name.len());
        let name_raw_slice = &mut name_raw[0..name_len];
        name_raw_slice
            .copy_from_slice(unsafe { core::mem::transmute(&value.name.as_slice()[0..name_len]) });
        Self {
            clock: value.clock,
            hdisplay: value.hdisplay,
            hsync_start: value.hsync_start,
            hsync_end: value.hsync_end,
            htotal: value.htotal,
            hskew: value.hskew,
            vdisplay: value.vdisplay,
            vsync_start: value.vsync_start,
            vsync_end: value.vsync_end,
            vtotal: value.vtotal,
            vscan: value.vscan,
            vrefresh: value.vrefresh,
            flags: value.flags,
            typ: value.typ,
            name: name_raw,
        }
    }
}

#[derive(Debug)]
#[repr(u32)]
pub enum SubpixelType {
    Unknown = 1,
    HorizontalRgb = 2,
    HorizontalBgr = 3,
    VerticalRgb = 4,
    VerticalBgr = 5,
    None = 6,
}

impl From<u32> for SubpixelType {
    fn from(value: u32) -> Self {
        match value {
            2 => Self::HorizontalRgb,
            3 => Self::HorizontalBgr,
            4 => Self::VerticalRgb,
            5 => Self::VerticalBgr,
            _ => Self::Unknown,
        }
    }
}

#[derive(Debug)]
#[repr(transparent)]
pub struct PageFlipFlags(u32);

impl PageFlipFlags {
    pub const NONE: Self = Self(0);
    pub const EVENT: Self = Self(crate::ioctl::DRM_MODE_PAGE_FLIP_EVENT);
    pub const ASYNC: Self = Self(crate::ioctl::DRM_MODE_PAGE_FLIP_ASYNC);
}

impl BitOr for PageFlipFlags {
    type Output = Self;

    #[inline(always)]
    fn bitor(self, rhs: Self) -> Self::Output {
        Self(self.0 | rhs.0)
    }
}

impl BitAnd for PageFlipFlags {
    type Output = Self;

    #[inline(always)]
    fn bitand(self, rhs: Self) -> Self::Output {
        Self(self.0 | rhs.0)
    }
}

impl From<PageFlipFlags> for u32 {
    #[inline(always)]
    fn from(value: PageFlipFlags) -> Self {
        value.0
    }
}