use drmkit_planes::{PlaneCapabilities, PlaneRegistry, PlaneType};
use crate::CursorError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PlanePath {
AtomicCursor,
AtomicOverlay,
Legacy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct SelectedPlane {
pub plane_id: u32,
pub path: PlanePath,
pub cursor_max_w: u32,
pub cursor_max_h: u32,
pub fourcc: u32,
pub legacy_plane_id: u32,
}
pub const CURSOR_FORMATS: [u32; 4] = [
drmkit_fmt::fourcc::ARGB8888,
drmkit_fmt::fourcc::RGBA8888,
drmkit_fmt::fourcc::ABGR8888,
drmkit_fmt::fourcc::BGRA8888,
];
fn usable(plane: &PlaneCapabilities) -> Option<u32> {
CURSOR_FORMATS
.into_iter()
.find(|fourcc| plane.scans_out_linear(*fourcc))
}
pub fn select_plane(
registry: &PlaneRegistry,
crtc_index: u32,
forced: Option<u32>,
allow_legacy: bool,
prefer_legacy: bool,
bound_elsewhere: &dyn Fn(u32) -> bool,
) -> Result<SelectedPlane, CursorError> {
if let Some(plane_id) = forced {
let plane = registry
.by_id(plane_id)
.filter(|plane| plane.compatible_with_crtc(crtc_index))
.ok_or(CursorError::PlaneUnusable)?;
let fourcc = usable(plane).ok_or(CursorError::PlaneUnusable)?;
return Ok(selected(plane, fourcc));
}
if let Some((plane, fourcc)) = registry
.for_crtc(crtc_index)
.filter(|plane| plane.plane_type == PlaneType::Cursor)
.find_map(|plane| usable(plane).map(|fourcc| (plane, fourcc)))
{
if prefer_legacy && allow_legacy && plane.scans_out_linear(drmkit_fmt::fourcc::ARGB8888) {
return Ok(SelectedPlane {
legacy_plane_id: plane.id,
..legacy()
});
}
return Ok(selected(plane, fourcc));
}
let mut fallback: Option<(&PlaneCapabilities, u32)> = None;
for plane in registry.for_crtc(crtc_index) {
if plane.plane_type != PlaneType::Overlay {
continue;
}
let Some(fourcc) = usable(plane) else {
continue;
};
if !bound_elsewhere(plane.id) {
return Ok(selected(plane, fourcc));
}
fallback.get_or_insert((plane, fourcc));
}
if let Some((plane, fourcc)) = fallback {
return Ok(selected(plane, fourcc));
}
if allow_legacy {
return Ok(legacy());
}
Err(CursorError::NoPlane)
}
const fn legacy() -> SelectedPlane {
SelectedPlane {
plane_id: 0,
path: PlanePath::Legacy,
cursor_max_w: 0,
cursor_max_h: 0,
fourcc: drmkit_fmt::fourcc::ARGB8888,
legacy_plane_id: 0,
}
}
fn selected(plane: &PlaneCapabilities, fourcc: u32) -> SelectedPlane {
let path = if plane.plane_type == PlaneType::Cursor {
PlanePath::AtomicCursor
} else {
PlanePath::AtomicOverlay
};
SelectedPlane {
plane_id: plane.id,
path,
cursor_max_w: plane.cursor_max_w,
cursor_max_h: plane.cursor_max_h,
fourcc,
legacy_plane_id: 0,
}
}