use std::os::fd::{FromRawFd as _, OwnedFd};
use std::sync::Arc;
use crate::device::{DeviceInner, SCANOUT_RENDERING};
use crate::ffi::{self, BoPtr};
use crate::surface::SurfaceInner;
use crate::{GbmDevice, GbmError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(
clippy::exhaustive_enums,
reason = "a closed set: there is no further variant to add"
)]
pub enum MapAccess {
Read,
Write,
ReadWrite,
}
enum Owner {
Allocated,
Surface(Arc<SurfaceInner>),
}
pub struct GbmBuffer {
ptr: BoPtr,
owner: Owner,
device: Arc<DeviceInner>,
}
unsafe impl Send for GbmBuffer {}
unsafe impl Sync for GbmBuffer {}
impl Drop for GbmBuffer {
fn drop(&mut self) {
let gbm = self.device.gbm;
match &self.owner {
Owner::Allocated => unsafe { (gbm.bo_destroy)(self.ptr) },
Owner::Surface(surface) => unsafe {
(gbm.surface_release_buffer)(surface.ptr, self.ptr);
},
}
}
}
impl std::fmt::Debug for GbmBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GbmBuffer")
.field("width", &self.width())
.field("height", &self.height())
.field("modifier", &format_args!("{:#x}", self.modifier()))
.finish_non_exhaustive()
}
}
impl GbmBuffer {
pub(crate) fn locked(ptr: BoPtr, surface: Arc<SurfaceInner>) -> Self {
Self {
ptr,
device: Arc::clone(&surface.device),
owner: Owner::Surface(surface),
}
}
pub fn create(
device: &GbmDevice,
width: u32,
height: u32,
fourcc: u32,
) -> Result<Self, GbmError> {
let inner = &device.inner;
let ptr =
unsafe { (inner.gbm.bo_create)(inner.ptr, width, height, fourcc, SCANOUT_RENDERING) };
if ptr.is_null() {
return Err(GbmError::Allocation(ffi::last_error("gbm_bo_create")));
}
Ok(Self {
ptr,
owner: Owner::Allocated,
device: Arc::clone(inner),
})
}
pub fn create_with_modifiers(
device: &GbmDevice,
width: u32,
height: u32,
fourcc: u32,
modifiers: &[u64],
) -> Result<Self, GbmError> {
if modifiers.is_empty() {
return Self::create(device, width, height, fourcc);
}
let inner = &device.inner;
let Ok(count) = u32::try_from(modifiers.len()) else {
return Self::create(device, width, height, fourcc);
};
let mut ptr = std::ptr::null_mut();
if let Some(create) = inner.gbm.bo_create_with_modifiers2 {
ptr = unsafe {
create(
inner.ptr,
width,
height,
fourcc,
modifiers.as_ptr(),
count,
SCANOUT_RENDERING,
)
};
} else if let Some(create) = inner.gbm.bo_create_with_modifiers {
ptr = unsafe { create(inner.ptr, width, height, fourcc, modifiers.as_ptr(), count) };
}
if ptr.is_null() {
return Self::create(device, width, height, fourcc);
}
Ok(Self {
ptr,
owner: Owner::Allocated,
device: Arc::clone(inner),
})
}
#[must_use]
pub fn width(&self) -> u32 {
unsafe { (self.device.gbm.bo_get_width)(self.ptr) }
}
#[must_use]
pub fn height(&self) -> u32 {
unsafe { (self.device.gbm.bo_get_height)(self.ptr) }
}
#[must_use]
pub fn stride(&self) -> u32 {
unsafe { (self.device.gbm.bo_get_stride)(self.ptr) }
}
#[must_use]
pub fn fourcc(&self) -> u32 {
unsafe { (self.device.gbm.bo_get_format)(self.ptr) }
}
#[must_use]
pub fn modifier(&self) -> u64 {
const INVALID: u64 = (1 << 56) - 1;
self.device.gbm.bo_get_modifier.map_or(INVALID, |get| {
unsafe { get(self.ptr) }
})
}
#[must_use]
pub fn plane_count(&self) -> u32 {
self.device.gbm.bo_get_plane_count.map_or(1, |get| {
let count = unsafe { get(self.ptr) };
u32::try_from(count).unwrap_or(0)
})
}
#[must_use]
pub fn handle(&self) -> u32 {
let handle = unsafe { (self.device.gbm.bo_get_handle)(self.ptr) };
unsafe { handle.u32_ }
}
pub fn export(&self) -> Result<OwnedFd, GbmError> {
let fd = unsafe { (self.device.gbm.bo_get_fd)(self.ptr) };
if fd < 0 {
return Err(GbmError::Allocation(ffi::last_error("gbm_bo_get_fd")));
}
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
#[must_use]
pub fn as_raw(&self) -> *mut std::ffi::c_void {
self.ptr
}
}