use std::ffi::CStr;
use std::os::fd::{AsFd, AsRawFd as _, OwnedFd};
use std::sync::Arc;
use crate::ffi::{self, DevicePtr, Gbm};
use crate::{GbmError, GbmSurface};
pub(crate) const SCANOUT_RENDERING: u32 = (1 << 0) | (1 << 2);
pub(crate) struct DeviceInner {
pub(crate) gbm: &'static Gbm,
pub(crate) ptr: DevicePtr,
_fd: OwnedFd,
}
unsafe impl Send for DeviceInner {}
unsafe impl Sync for DeviceInner {}
impl Drop for DeviceInner {
fn drop(&mut self) {
unsafe { (self.gbm.device_destroy)(self.ptr) };
}
}
#[derive(Clone)]
pub struct GbmDevice {
pub(crate) inner: Arc<DeviceInner>,
}
impl std::fmt::Debug for GbmDevice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GbmDevice").finish_non_exhaustive()
}
}
impl GbmDevice {
#[cfg(feature = "drmkit-core")]
pub fn new(device: &drmkit_core::Device) -> Result<Self, GbmError> {
Self::from_fd(device)
}
pub fn from_fd(fd: impl AsFd) -> Result<Self, GbmError> {
let gbm = ffi::gbm()?;
let duped = fd.as_fd().try_clone_to_owned().map_err(|e| {
GbmError::Io(rustix::io::Errno::from_io_error(&e).unwrap_or(rustix::io::Errno::MFILE))
})?;
let ptr = unsafe { (gbm.create_device)(duped.as_raw_fd()) };
if ptr.is_null() {
return Err(GbmError::NotGbmCapable(ffi::last_error(
"gbm_create_device",
)));
}
Ok(Self {
inner: Arc::new(DeviceInner {
gbm,
ptr,
_fd: duped,
}),
})
}
pub fn create_surface(
&self,
width: u32,
height: u32,
fourcc: u32,
modifiers: &[u64],
) -> Result<GbmSurface, GbmError> {
let gbm = self.inner.gbm;
let device = self.inner.ptr;
let count = u32::try_from(modifiers.len())
.map_err(|_| GbmError::Allocation("too many modifiers".to_owned()))?;
let mut ptr = std::ptr::null_mut();
if !modifiers.is_empty() {
if let Some(create) = gbm.surface_create_with_modifiers2 {
ptr = unsafe {
create(
device,
width,
height,
fourcc,
modifiers.as_ptr(),
count,
SCANOUT_RENDERING,
)
};
} else if let Some(create) = gbm.surface_create_with_modifiers {
ptr = unsafe { create(device, width, height, fourcc, modifiers.as_ptr(), count) };
}
}
if ptr.is_null() {
ptr = unsafe { (gbm.surface_create)(device, width, height, fourcc, SCANOUT_RENDERING) };
}
if ptr.is_null() {
return Err(GbmError::Allocation(ffi::last_error("gbm_surface_create")));
}
Ok(GbmSurface::adopt(ptr, Arc::clone(&self.inner)))
}
#[must_use]
pub fn backend_name(&self) -> String {
let name = unsafe { (self.inner.gbm.device_get_backend_name)(self.inner.ptr) };
if name.is_null() {
return String::new();
}
unsafe { CStr::from_ptr(name) }
.to_string_lossy()
.into_owned()
}
#[must_use]
pub fn as_raw(&self) -> *mut std::ffi::c_void {
self.inner.ptr
}
}