summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buffer_object.rs711
-rw-r--r--src/device.rs421
-rw-r--r--src/lib.rs209
-rw-r--r--src/surface.rs125
4 files changed, 1466 insertions, 0 deletions
diff --git a/src/buffer_object.rs b/src/buffer_object.rs
new file mode 100644
index 0000000..514ee8e
--- /dev/null
+++ b/src/buffer_object.rs
@@ -0,0 +1,711 @@
+use crate::{AsRaw, Device, DeviceDestroyedError, Format, Modifier, Ptr, WeakPtr};
+
+#[cfg(feature = "drm-support")]
+use drm::buffer::{Buffer as DrmBuffer, Handle, PlanarBuffer as DrmPlanarBuffer};
+use std::os::unix::io::{AsFd, BorrowedFd, FromRawFd, OwnedFd};
+
+use std::error;
+use std::fmt;
+use std::io::{Error as IoError, Result as IoResult};
+use std::marker::PhantomData;
+use std::ops::{Deref, DerefMut};
+use std::ptr;
+use std::slice;
+
+/// A GBM buffer object
+pub struct BufferObject<T: 'static> {
+ pub(crate) ffi: Ptr<ffi::gbm_bo>,
+ pub(crate) _device: WeakPtr<ffi::gbm_device>,
+ pub(crate) _userdata: PhantomData<T>,
+}
+
+impl<T> fmt::Debug for BufferObject<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("BufferObject")
+ .field("ptr", &format_args!("{:p}", self.ffi))
+ .field("device", &format_args!("{:p}", &self._device))
+ .field("width", &self.width().unwrap_or(0))
+ .field("height", &self.height().unwrap_or(0))
+ .field("offsets", &self.offsets())
+ .field("stride", &self.stride().unwrap_or(0))
+ .field("format", &self.format().ok())
+ .field("modifier", &self.modifier().ok())
+ .finish()
+ }
+}
+
+bitflags! {
+ /// Flags to indicate the intended use for the buffer - these are passed into
+ /// [`Device::create_buffer_object()`].
+ ///
+ /// Use [`Device::is_format_supported()`] to check if the combination of format
+ /// and use flags are supported
+ pub struct BufferObjectFlags: u32 {
+ /// Buffer is going to be presented to the screen using an API such as KMS
+ const SCANOUT = ffi::gbm_bo_flags::GBM_BO_USE_SCANOUT as u32;
+ /// Buffer is going to be used as cursor
+ const CURSOR = ffi::gbm_bo_flags::GBM_BO_USE_CURSOR as u32;
+ /// Buffer is going to be used as cursor (deprecated)
+ #[deprecated = "Use CURSOR instead"]
+ const CURSOR_64X64 = ffi::gbm_bo_flags::GBM_BO_USE_CURSOR_64X64 as u32;
+ /// Buffer is to be used for rendering - for example it is going to be used
+ /// as the storage for a color buffer
+ const RENDERING = ffi::gbm_bo_flags::GBM_BO_USE_RENDERING as u32;
+ /// Buffer can be used for [`BufferObject::write()`]. This is guaranteed to work
+ /// with [`Self::CURSOR`], but may not work for other combinations.
+ const WRITE = ffi::gbm_bo_flags::GBM_BO_USE_WRITE as u32;
+ /// Buffer is linear, i.e. not tiled.
+ const LINEAR = ffi::gbm_bo_flags::GBM_BO_USE_LINEAR as u32;
+ /// Buffer is protected
+ const PROTECTED = ffi::gbm_bo_flags::GBM_BO_USE_PROTECTED as u32;
+ }
+}
+
+/// Abstraction representing the handle to a buffer allocated by the manager
+pub type BufferObjectHandle = ffi::gbm_bo_handle;
+
+enum BORef<'a, T: 'static> {
+ Ref(&'a BufferObject<T>),
+ Mut(&'a mut BufferObject<T>),
+}
+
+/// A mapped buffer object
+pub struct MappedBufferObject<'a, T: 'static> {
+ bo: BORef<'a, T>,
+ buffer: &'a mut [u8],
+ data: *mut ::libc::c_void,
+ stride: u32,
+ height: u32,
+ width: u32,
+ x: u32,
+ y: u32,
+}
+
+impl<'a, T> fmt::Debug for MappedBufferObject<'a, T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("MappedBufferObject")
+ .field(
+ "mode",
+ &match self.bo {
+ BORef::Ref(_) => format_args!("read"),
+ BORef::Mut(_) => format_args!("write"),
+ },
+ )
+ .field(
+ "buffer",
+ match &self.bo {
+ BORef::Ref(bo) => *bo,
+ BORef::Mut(bo) => *bo,
+ },
+ )
+ .finish()
+ }
+}
+
+impl<'a, T: 'static> MappedBufferObject<'a, T> {
+ /// Get the stride of the buffer object
+ ///
+ /// This is calculated by the backend when it does the allocation of the buffer.
+ pub fn stride(&self) -> u32 {
+ self.stride
+ }
+
+ /// The height of the mapped region for the buffer
+ pub fn height(&self) -> u32 {
+ self.height
+ }
+
+ /// The width of the mapped region for the buffer
+ pub fn width(&self) -> u32 {
+ self.width
+ }
+
+ /// The X (top left origin) starting position of the mapped region for the buffer
+ pub fn x(&self) -> u32 {
+ self.x
+ }
+
+ /// The Y (top left origin) starting position of the mapped region for the buffer
+ pub fn y(&self) -> u32 {
+ self.y
+ }
+
+ /// Access to the underlying image buffer
+ pub fn buffer(&self) -> &[u8] {
+ self.buffer
+ }
+
+ /// Mutable access to the underlying image buffer
+ pub fn buffer_mut(&mut self) -> &mut [u8] {
+ self.buffer
+ }
+}
+
+impl<'a, T: 'static> Deref for MappedBufferObject<'a, T> {
+ type Target = BufferObject<T>;
+ fn deref(&self) -> &BufferObject<T> {
+ match &self.bo {
+ BORef::Ref(bo) => bo,
+ BORef::Mut(bo) => bo,
+ }
+ }
+}
+
+impl<'a, T: 'static> DerefMut for MappedBufferObject<'a, T> {
+ fn deref_mut(&mut self) -> &mut BufferObject<T> {
+ match &mut self.bo {
+ BORef::Ref(_) => unreachable!(),
+ BORef::Mut(bo) => bo,
+ }
+ }
+}
+
+impl<'a, T: 'static> Drop for MappedBufferObject<'a, T> {
+ fn drop(&mut self) {
+ let ffi = match &self.bo {
+ BORef::Ref(bo) => &bo.ffi,
+ BORef::Mut(bo) => &bo.ffi,
+ };
+ unsafe { ffi::gbm_bo_unmap(**ffi, self.data) }
+ }
+}
+
+unsafe extern "C" fn destroy<T: 'static>(_: *mut ffi::gbm_bo, ptr: *mut ::libc::c_void) {
+ let ptr = ptr as *mut T;
+ if !ptr.is_null() {
+ let _ = Box::from_raw(ptr);
+ }
+}
+
+impl<T: 'static> BufferObject<T> {
+ /// Get the width of the buffer object
+ pub fn width(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_width(*self.ffi) })
+ }
+
+ /// Get the height of the buffer object
+ pub fn height(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_height(*self.ffi) })
+ }
+
+ /// Get the stride of the buffer object
+ pub fn stride(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_stride(*self.ffi) })
+ }
+
+ /// Get the stride of the buffer object
+ pub fn stride_for_plane(&self, plane: i32) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_stride_for_plane(*self.ffi, plane) })
+ }
+
+ /// Get the format of the buffer object
+ pub fn format(&self) -> Result<Format, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(
+ Format::try_from(unsafe { ffi::gbm_bo_get_format(*self.ffi) })
+ .expect("libgbm returned invalid buffer format"),
+ )
+ }
+
+ /// Get the bits per pixel of the buffer object
+ pub fn bpp(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_bpp(*self.ffi) })
+ }
+
+ /// Get the offset for a plane of the buffer object
+ pub fn offset(&self, plane: i32) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_offset(*self.ffi, plane) })
+ }
+
+ /// Get the plane count of the buffer object
+ pub fn plane_count(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_plane_count(*self.ffi) as u32 })
+ }
+
+ /// Get the modifier of the buffer object
+ pub fn modifier(&self) -> Result<Modifier, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(Modifier::from(unsafe {
+ ffi::gbm_bo_get_modifier(*self.ffi)
+ }))
+ }
+
+ /// Get a DMA-BUF file descriptor for the buffer object
+ ///
+ /// This function creates a DMA-BUF (also known as PRIME) file descriptor
+ /// handle for the buffer object. Each call to [`Self::fd()`] returns a new
+ /// file descriptor and the caller is responsible for closing the file
+ /// descriptor.
+ pub fn fd(&self) -> Result<OwnedFd, FdError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ unsafe {
+ let fd = ffi::gbm_bo_get_fd(*self.ffi);
+
+ if fd == -1 {
+ return Err(InvalidFdError.into());
+ }
+
+ Ok(OwnedFd::from_raw_fd(fd))
+ }
+ }
+
+ /// Get the file descriptor of the gbm device of this buffer object
+ pub fn device_fd(&self) -> Result<BorrowedFd, DeviceDestroyedError> {
+ let device_ptr = self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*device_ptr)) })
+ }
+
+ /// Get the handle of the buffer object
+ ///
+ /// This is stored in the platform generic union [`BufferObjectHandle`] type. However
+ /// the format of this handle is platform specific.
+ pub fn handle(&self) -> Result<BufferObjectHandle, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_handle(*self.ffi) })
+ }
+
+ /// Get a DMA-BUF file descriptor for a plane of the buffer object
+ ///
+ /// This function creates a DMA-BUF (also known as PRIME) file descriptor
+ /// handle for a plane of the buffer object. Each call to [`Self::fd_for_plane()`]
+ /// returns a new file descriptor and the caller is responsible for closing
+ /// the file descriptor.
+ pub fn fd_for_plane(&self, plane: i32) -> Result<OwnedFd, FdError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ unsafe {
+ let fd = ffi::gbm_bo_get_fd_for_plane(*self.ffi, plane);
+
+ if fd == -1 {
+ return Err(InvalidFdError.into());
+ }
+
+ Ok(OwnedFd::from_raw_fd(fd))
+ }
+ }
+
+ /// Get the handle of a plane of the buffer object
+ ///
+ /// This is stored in the platform generic union [`BufferObjectHandle`] type. However
+ /// the format of this handle is platform specific.
+ pub fn handle_for_plane(&self, plane: i32) -> Result<BufferObjectHandle, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_handle_for_plane(*self.ffi, plane) })
+ }
+
+ /// Map a region of a GBM buffer object for cpu access
+ ///
+ /// This function maps a region of a GBM bo for cpu read access.
+ pub fn map<'a, D, F, S>(
+ &'a self,
+ device: &Device<D>,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ f: F,
+ ) -> Result<IoResult<S>, WrongDeviceError>
+ where
+ D: AsFd + 'static,
+ F: FnOnce(&MappedBufferObject<'a, T>) -> S,
+ {
+ let device_ref = self._device.upgrade().ok_or(WrongDeviceError)?;
+ if *device_ref != device.as_raw_mut() {
+ // not matching
+ return Err(WrongDeviceError);
+ }
+
+ unsafe {
+ let mut data: *mut ::libc::c_void = ptr::null_mut();
+ let mut stride = 0;
+ let ptr = ffi::gbm_bo_map(
+ *self.ffi,
+ x,
+ y,
+ width,
+ height,
+ ffi::gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ as u32,
+ &mut stride as *mut _,
+ &mut data as *mut _,
+ );
+
+ if ptr.is_null() {
+ Ok(Err(IoError::last_os_error()))
+ } else {
+ Ok(Ok(f(&MappedBufferObject {
+ bo: BORef::Ref(self),
+ buffer: slice::from_raw_parts_mut(ptr as *mut _, (height * stride) as usize),
+ data,
+ stride,
+ height,
+ width,
+ x,
+ y,
+ })))
+ }
+ }
+ }
+
+ /// Map a region of a GBM buffer object for cpu access
+ ///
+ /// This function maps a region of a GBM bo for cpu read/write access.
+ pub fn map_mut<'a, D, F, S>(
+ &'a mut self,
+ device: &Device<D>,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ f: F,
+ ) -> Result<IoResult<S>, WrongDeviceError>
+ where
+ D: AsFd + 'static,
+ F: FnOnce(&mut MappedBufferObject<'a, T>) -> S,
+ {
+ let device_ref = self._device.upgrade().ok_or(WrongDeviceError)?;
+ if *device_ref != device.as_raw_mut() {
+ // not matching
+ return Err(WrongDeviceError);
+ }
+
+ unsafe {
+ let mut data: *mut ::libc::c_void = ptr::null_mut();
+ let mut stride = 0;
+ let ptr = ffi::gbm_bo_map(
+ *self.ffi,
+ x,
+ y,
+ width,
+ height,
+ ffi::gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ_WRITE as u32,
+ &mut stride as *mut _,
+ &mut data as *mut _,
+ );
+
+ if ptr.is_null() {
+ Ok(Err(IoError::last_os_error()))
+ } else {
+ Ok(Ok(f(&mut MappedBufferObject {
+ bo: BORef::Mut(self),
+ buffer: slice::from_raw_parts_mut(ptr as *mut _, (height * stride) as usize),
+ data,
+ stride,
+ height,
+ width,
+ x,
+ y,
+ })))
+ }
+ }
+ }
+
+ /// Write data into the buffer object
+ ///
+ /// If the buffer object was created with the [`BufferObjectFlags::WRITE`] flag,
+ /// this function can be used to write data into the buffer object. The
+ /// data is copied directly into the object and it's the responsibility
+ /// of the caller to make sure the data represents valid pixel data,
+ /// according to the width, height, stride and format of the buffer object.
+ pub fn write(&mut self, buffer: &[u8]) -> Result<IoResult<()>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let result =
+ unsafe { ffi::gbm_bo_write(*self.ffi, buffer.as_ptr() as *const _, buffer.len() as _) };
+ if result != 0 {
+ Ok(Err(IoError::last_os_error()))
+ } else {
+ Ok(Ok(()))
+ }
+ }
+
+ /// Sets the userdata of the buffer object.
+ ///
+ /// If previously userdata was set, it is returned.
+ pub fn set_userdata(&mut self, userdata: T) -> Result<Option<T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let old = self.take_userdata();
+
+ let boxed = Box::new(userdata);
+ unsafe {
+ ffi::gbm_bo_set_user_data(
+ *self.ffi,
+ Box::into_raw(boxed) as *mut _,
+ Some(destroy::<T>),
+ );
+ }
+
+ old
+ }
+
+ /// Clears the set userdata of the buffer object.
+ pub fn clear_userdata(&mut self) -> Result<(), DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let _ = self.take_userdata();
+ Ok(())
+ }
+
+ /// Returns a reference to set userdata, if any.
+ pub fn userdata(&self) -> Result<Option<&T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+ if raw.is_null() {
+ Ok(None)
+ } else {
+ unsafe { Ok(Some(&*(raw as *mut T))) }
+ }
+ }
+
+ /// Returns a mutable reference to set userdata, if any.
+ pub fn userdata_mut(&mut self) -> Result<Option<&mut T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+ if raw.is_null() {
+ Ok(None)
+ } else {
+ unsafe { Ok(Some(&mut *(raw as *mut T))) }
+ }
+ }
+
+ /// Takes ownership of previously set userdata, if any.
+ ///
+ /// This removes the userdata from the buffer object.
+ pub fn take_userdata(&mut self) -> Result<Option<T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+ if raw.is_null() {
+ Ok(None)
+ } else {
+ unsafe {
+ let boxed = Box::from_raw(raw as *mut T);
+ ffi::gbm_bo_set_user_data(*self.ffi, ptr::null_mut(), None);
+ Ok(Some(*boxed))
+ }
+ }
+ }
+
+ pub(crate) unsafe fn new(
+ ffi: *mut ffi::gbm_bo,
+ device: WeakPtr<ffi::gbm_device>,
+ ) -> BufferObject<T> {
+ BufferObject {
+ ffi: Ptr::<ffi::gbm_bo>::new(ffi, |ptr| ffi::gbm_bo_destroy(ptr)),
+ _device: device,
+ _userdata: PhantomData,
+ }
+ }
+
+ fn offsets(&self) -> [u32; 4] {
+ let num = self
+ .plane_count()
+ .expect("GbmDevice does not exist anymore");
+ [
+ BufferObject::<T>::offset(self, 0).unwrap(),
+ if num > 1 {
+ BufferObject::<T>::offset(self, 1).unwrap()
+ } else {
+ 0
+ },
+ if num > 2 {
+ BufferObject::<T>::offset(self, 2).unwrap()
+ } else {
+ 0
+ },
+ if num > 3 {
+ BufferObject::<T>::offset(self, 3).unwrap()
+ } else {
+ 0
+ },
+ ]
+ }
+}
+
+impl<T: 'static> AsRaw<ffi::gbm_bo> for BufferObject<T> {
+ fn as_raw(&self) -> *const ffi::gbm_bo {
+ *self.ffi
+ }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: 'static> DrmBuffer for BufferObject<T> {
+ fn size(&self) -> (u32, u32) {
+ (
+ self.width().expect("GbmDevice does not exist anymore"),
+ self.height().expect("GbmDevice does not exist anymore"),
+ )
+ }
+
+ fn format(&self) -> Format {
+ BufferObject::<T>::format(self).expect("GbmDevice does not exist anymore")
+ }
+
+ fn pitch(&self) -> u32 {
+ self.stride().expect("GbmDevice does not exist anymore")
+ }
+
+ fn handle(&self) -> Handle {
+ use std::num::NonZeroU32;
+ unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ self.handle()
+ .expect("GbmDevice does not exist anymore")
+ .u32_,
+ ))
+ }
+ }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: 'static> DrmPlanarBuffer for BufferObject<T> {
+ fn size(&self) -> (u32, u32) {
+ (
+ self.width().expect("GbmDevice does not exist anymore"),
+ self.height().expect("GbmDevice does not exist anymore"),
+ )
+ }
+ fn format(&self) -> Format {
+ BufferObject::<T>::format(self).expect("GbmDevice does not exist anymore")
+ }
+ fn modifier(&self) -> Option<Modifier> {
+ Some(BufferObject::<T>::modifier(self).expect("GbmDevice does not exist anymore"))
+ }
+ fn pitches(&self) -> [u32; 4] {
+ let num = self
+ .plane_count()
+ .expect("GbmDevice does not exist anymore");
+ [
+ BufferObject::<T>::stride_for_plane(self, 0).unwrap(),
+ if num > 1 {
+ BufferObject::<T>::stride_for_plane(self, 1).unwrap()
+ } else {
+ 0
+ },
+ if num > 2 {
+ BufferObject::<T>::stride_for_plane(self, 2).unwrap()
+ } else {
+ 0
+ },
+ if num > 3 {
+ BufferObject::<T>::stride_for_plane(self, 3).unwrap()
+ } else {
+ 0
+ },
+ ]
+ }
+ fn handles(&self) -> [Option<Handle>; 4] {
+ use std::num::NonZeroU32;
+ let num = self
+ .plane_count()
+ .expect("GbmDevice does not exist anymore");
+ [
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 0).unwrap().u32_,
+ ))
+ }),
+ if num > 1 {
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 1).unwrap().u32_,
+ ))
+ })
+ } else {
+ None
+ },
+ if num > 2 {
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 2).unwrap().u32_,
+ ))
+ })
+ } else {
+ None
+ },
+ if num > 3 {
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 3).unwrap().u32_,
+ ))
+ })
+ } else {
+ None
+ },
+ ]
+ }
+ fn offsets(&self) -> [u32; 4] {
+ self.offsets()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// Thrown when the GBM device does not belong to the buffer object
+pub struct WrongDeviceError;
+
+impl fmt::Display for WrongDeviceError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "The gbm device specified is not the one this buffer object belongs to"
+ )
+ }
+}
+
+impl error::Error for WrongDeviceError {}
+
+/// Thrown when the fd is invalid
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct InvalidFdError;
+
+impl fmt::Display for InvalidFdError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "The returned fd is invalid")
+ }
+}
+
+impl error::Error for InvalidFdError {}
+
+/// Thrown when an error occurs during getting a bo fd
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FdError {
+ /// The underlying device has been destroyed
+ DeviceDestroyed(DeviceDestroyedError),
+ /// The operation returned an invalid fd
+ InvalidFd(InvalidFdError),
+}
+
+impl From<DeviceDestroyedError> for FdError {
+ fn from(err: DeviceDestroyedError) -> Self {
+ FdError::DeviceDestroyed(err)
+ }
+}
+
+impl From<InvalidFdError> for FdError {
+ fn from(err: InvalidFdError) -> Self {
+ FdError::InvalidFd(err)
+ }
+}
+
+impl fmt::Display for FdError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ FdError::DeviceDestroyed(err) => err.fmt(f),
+ FdError::InvalidFd(err) => err.fmt(f),
+ }
+ }
+}
+
+impl error::Error for FdError {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ FdError::DeviceDestroyed(err) => Some(err),
+ FdError::InvalidFd(err) => Some(err),
+ }
+ }
+}
diff --git a/src/device.rs b/src/device.rs
new file mode 100644
index 0000000..b5aa670
--- /dev/null
+++ b/src/device.rs
@@ -0,0 +1,421 @@
+use crate::{AsRaw, BufferObject, BufferObjectFlags, Format, Modifier, Ptr, Surface};
+
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
+
+use std::error;
+use std::ffi::CStr;
+use std::fmt;
+use std::io::{Error as IoError, Result as IoResult};
+use std::ops::{Deref, DerefMut};
+
+#[cfg(feature = "import-wayland")]
+use wayland_server::protocol::wl_buffer::WlBuffer;
+
+#[cfg(feature = "import-egl")]
+/// An EGLImage handle
+pub type EGLImage = *mut libc::c_void;
+
+#[cfg(feature = "drm-support")]
+use drm::control::Device as DrmControlDevice;
+#[cfg(feature = "drm-support")]
+use drm::Device as DrmDevice;
+
+/// An open GBM device
+pub struct Device<T: AsFd> {
+ // Declare `ffi` first so it is dropped before `fd`
+ ffi: Ptr<ffi::gbm_device>,
+ fd: T,
+}
+
+impl<T: AsFd> fmt::Debug for Device<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Device")
+ .field("ptr", &format_args!("{:p}", &self.ffi))
+ .finish()
+ }
+}
+
+impl<T: AsFd + Clone> Clone for Device<T> {
+ fn clone(&self) -> Device<T> {
+ Device {
+ fd: self.fd.clone(),
+ ffi: self.ffi.clone(),
+ }
+ }
+}
+
+impl<T: AsFd> AsFd for Device<T> {
+ fn as_fd(&self) -> BorrowedFd {
+ unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*self.ffi)) }
+ }
+}
+
+impl<T: AsFd> AsRaw<ffi::gbm_device> for Device<T> {
+ fn as_raw(&self) -> *const ffi::gbm_device {
+ *self.ffi
+ }
+}
+
+impl<T: AsFd> Deref for Device<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.fd
+ }
+}
+
+impl<T: AsFd> DerefMut for Device<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.fd
+ }
+}
+
+impl<T: AsFd> Device<T> {
+ /// Open a GBM device from a given open DRM device.
+ ///
+ /// The underlying file descriptor passed in is used by the backend to communicate with
+ /// platform for allocating the memory. For allocations using DRI this would be
+ /// the file descriptor returned when opening a device such as `/dev/dri/card0`.
+ pub fn new(fd: T) -> IoResult<Device<T>> {
+ let ptr = unsafe { ffi::gbm_create_device(fd.as_fd().as_raw_fd()) };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(Device {
+ fd,
+ ffi: Ptr::<ffi::gbm_device>::new(ptr, |ptr| unsafe {
+ ffi::gbm_device_destroy(ptr)
+ }),
+ })
+ }
+ }
+
+ /// Get the backend name
+ pub fn backend_name(&self) -> &str {
+ unsafe {
+ CStr::from_ptr(ffi::gbm_device_get_backend_name(*self.ffi))
+ .to_str()
+ .expect("GBM passed invalid utf8 string")
+ }
+ }
+
+ /// Test if a format is supported for a given set of usage flags
+ pub fn is_format_supported(&self, format: Format, usage: BufferObjectFlags) -> bool {
+ unsafe { ffi::gbm_device_is_format_supported(*self.ffi, format as u32, usage.bits()) != 0 }
+ }
+
+ /// Allocate a new surface object
+ pub fn create_surface<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ ) -> IoResult<Surface<U>> {
+ let ptr = unsafe {
+ ffi::gbm_surface_create(*self.ffi, width, height, format as u32, usage.bits())
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a new surface object with explicit modifiers
+ pub fn create_surface_with_modifiers<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ ) -> IoResult<Surface<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_surface_create_with_modifiers(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a new surface object with explicit modifiers and flags
+ pub fn create_surface_with_modifiers2<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ usage: BufferObjectFlags,
+ ) -> IoResult<Surface<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_surface_create_with_modifiers2(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a buffer object for the given dimensions
+ pub fn create_buffer_object<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let ptr =
+ unsafe { ffi::gbm_bo_create(*self.ffi, width, height, format as u32, usage.bits()) };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a buffer object for the given dimensions with explicit modifiers
+ pub fn create_buffer_object_with_modifiers<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ ) -> IoResult<BufferObject<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_bo_create_with_modifiers(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a buffer object for the given dimensions with explicit modifiers and flags
+ pub fn create_buffer_object_with_modifiers2<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_bo_create_with_modifiers2(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Create a GBM buffer object from a wayland buffer
+ ///
+ /// This function imports a foreign [`WlBuffer`] object and creates a new GBM
+ /// buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ #[cfg(feature = "import-wayland")]
+ pub fn import_buffer_object_from_wayland<U: 'static>(
+ &self,
+ buffer: &WlBuffer,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ use wayland_server::Resource;
+
+ let ptr = unsafe {
+ ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_WL_BUFFER as u32,
+ buffer.id().as_ptr() as *mut _,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Create a GBM buffer object from an egl buffer
+ ///
+ /// This function imports a foreign [`EGLImage`] object and creates a new GBM
+ /// buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ ///
+ /// # Safety
+ ///
+ /// The given [`EGLImage`] is a raw pointer. Passing null or an invalid [`EGLImage`] will
+ /// cause undefined behavior.
+ #[cfg(feature = "import-egl")]
+ pub unsafe fn import_buffer_object_from_egl<U: 'static>(
+ &self,
+ buffer: EGLImage,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let ptr = ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_EGL_IMAGE as u32,
+ buffer,
+ usage.bits(),
+ );
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(BufferObject::new(ptr, self.ffi.downgrade()))
+ }
+ }
+
+ /// Create a GBM buffer object from a dma buffer
+ ///
+ /// This function imports a foreign dma buffer from an open file descriptor
+ /// and creates a new GBM buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ pub fn import_buffer_object_from_dma_buf<U: 'static>(
+ &self,
+ buffer: BorrowedFd<'_>,
+ width: u32,
+ height: u32,
+ stride: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let mut fd_data = ffi::gbm_import_fd_data {
+ fd: buffer.as_raw_fd(),
+ width,
+ height,
+ stride,
+ format: format as u32,
+ };
+
+ let ptr = unsafe {
+ ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_FD as u32,
+ &mut fd_data as *mut ffi::gbm_import_fd_data as *mut _,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Create a GBM buffer object from a dma buffer with explicit modifiers
+ ///
+ /// This function imports a foreign dma buffer from an open file descriptor
+ /// and creates a new GBM buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ #[allow(clippy::too_many_arguments)]
+ pub fn import_buffer_object_from_dma_buf_with_modifiers<U: 'static>(
+ &self,
+ len: u32,
+ buffers: [Option<BorrowedFd<'_>>; 4],
+ width: u32,
+ height: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ strides: [i32; 4],
+ offsets: [i32; 4],
+ modifier: Modifier,
+ ) -> IoResult<BufferObject<U>> {
+ let fds = buffers.map(|fd| fd.map_or(-1, |x| x.as_raw_fd()));
+ let mut fd_data = ffi::gbm_import_fd_modifier_data {
+ fds,
+ width,
+ height,
+ format: format as u32,
+ strides,
+ offsets,
+ modifier: modifier.into(),
+ num_fds: len,
+ };
+
+ let ptr = unsafe {
+ ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_FD_MODIFIER as u32,
+ &mut fd_data as *mut ffi::gbm_import_fd_modifier_data as *mut _,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: DrmDevice + AsFd> DrmDevice for Device<T> {}
+
+#[cfg(feature = "drm-support")]
+impl<T: DrmControlDevice + AsFd> DrmControlDevice for Device<T> {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// Thrown when the underlying GBM device was already destroyed
+pub struct DeviceDestroyedError;
+
+impl fmt::Display for DeviceDestroyedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "The underlying GBM device was already destroyed")
+ }
+}
+
+impl error::Error for DeviceDestroyedError {
+ fn cause(&self) -> Option<&dyn error::Error> {
+ None
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..7d0c8cf
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,209 @@
+//! # Safe `libgbm` bindings for [rust](https://www.rust-lang.org)
+//!
+//! The Generic Buffer Manager
+//!
+//! This module provides an abstraction that the caller can use to request a
+//! buffer from the underlying memory management system for the platform.
+//!
+//! This allows the creation of portable code whilst still allowing access to
+//! the underlying memory manager.
+//!
+//! This library is best used in combination with [`drm-rs`](https://github.com/Smithay/drm-rs),
+//! provided through the `drm-support` feature.
+//!
+//! ## Example
+//!
+//! ```rust,no_run
+//! # extern crate drm;
+//! # extern crate gbm;
+//! # use drm::control::connector::Info as ConnectorInfo;
+//! # use drm::control::Mode;
+//! use drm::control::{self, crtc, framebuffer};
+//! use gbm::{BufferObjectFlags, Device, Format};
+//!
+//! # use std::fs::{File, OpenOptions};
+//! # use std::os::unix::io::{AsFd, BorrowedFd};
+//! #
+//! # use drm::control::Device as ControlDevice;
+//! # use drm::Device as BasicDevice;
+//! # struct Card(File);
+//! #
+//! # impl AsFd for Card {
+//! # fn as_fd(&self) -> BorrowedFd {
+//! # self.0.as_fd()
+//! # }
+//! # }
+//! #
+//! # impl BasicDevice for Card {}
+//! # impl ControlDevice for Card {}
+//! #
+//! # fn init_drm_device() -> Card {
+//! # let mut options = OpenOptions::new();
+//! # options.read(true);
+//! # options.write(true);
+//! # let file = options.open("/dev/dri/card0").unwrap();
+//! # Card(file)
+//! # }
+//! # fn main() {
+//! // ... init your drm device ...
+//! let drm = init_drm_device();
+//!
+//! // init a GBM device
+//! let gbm = Device::new(drm).unwrap();
+//!
+//! // create a 4x4 buffer
+//! let mut bo = gbm
+//! .create_buffer_object::<()>(
+//! 1280,
+//! 720,
+//! Format::Argb8888,
+//! BufferObjectFlags::SCANOUT | BufferObjectFlags::WRITE,
+//! )
+//! .unwrap();
+//! // write something to it (usually use import or egl rendering instead)
+//! let buffer = {
+//! let mut buffer = Vec::new();
+//! for i in 0..1280 {
+//! for _ in 0..720 {
+//! buffer.push(if i % 2 == 0 { 0 } else { 255 });
+//! }
+//! }
+//! buffer
+//! };
+//! bo.write(&buffer).unwrap();
+//!
+//! // create a framebuffer from our buffer
+//! let fb = gbm.add_framebuffer(&bo, 32, 32).unwrap();
+//!
+//! # let res_handles = gbm.resource_handles().unwrap();
+//! # let con = *res_handles.connectors().iter().next().unwrap();
+//! # let crtc_handle = *res_handles.crtcs().iter().next().unwrap();
+//! # let connector_info: ConnectorInfo = gbm.get_connector(con, false).unwrap();
+//! # let mode: Mode = connector_info.modes()[0];
+//! #
+//! // display it (and get a crtc, mode and connector before)
+//! gbm.set_crtc(crtc_handle, Some(fb), (0, 0), &[con], Some(mode))
+//! .unwrap();
+//! # }
+//! ```
+#![warn(missing_debug_implementations)]
+#![deny(missing_docs)]
+
+extern crate gbm_sys as ffi;
+extern crate libc;
+
+#[cfg(feature = "import-wayland")]
+extern crate wayland_server;
+
+#[cfg(feature = "drm-support")]
+extern crate drm;
+
+extern crate drm_fourcc;
+
+#[macro_use]
+extern crate bitflags;
+
+mod buffer_object;
+mod device;
+mod surface;
+
+pub use self::buffer_object::*;
+pub use self::device::*;
+pub use self::surface::*;
+pub use drm_fourcc::{DrmFourcc as Format, DrmModifier as Modifier};
+
+use std::fmt;
+use std::sync::{Arc, Weak};
+
+/// Trait for types that allow to obtain the underlying raw libinput pointer.
+pub trait AsRaw<T> {
+ /// Receive a raw pointer representing this type.
+ fn as_raw(&self) -> *const T;
+
+ #[doc(hidden)]
+ fn as_raw_mut(&self) -> *mut T {
+ self.as_raw() as *mut _
+ }
+}
+
+struct PtrDrop<T>(*mut T, Option<Box<dyn FnOnce(*mut T) + Send + 'static>>);
+
+impl<T> Drop for PtrDrop<T> {
+ fn drop(&mut self) {
+ (self.1.take().unwrap())(self.0);
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct Ptr<T>(Arc<PtrDrop<T>>);
+// SAFETY: The types used with Ptr in this crate are all Send (namely gbm_device, gbm_surface and gbm_bo).
+// The type is private and can thus not be used unsoundly by other crates.
+unsafe impl<T> Send for Ptr<T> {}
+
+impl<T> Ptr<T> {
+ fn new<F: FnOnce(*mut T) + Send + 'static>(ptr: *mut T, destructor: F) -> Ptr<T> {
+ Ptr(Arc::new(PtrDrop(ptr, Some(Box::new(destructor)))))
+ }
+
+ fn downgrade(&self) -> WeakPtr<T> {
+ WeakPtr(Arc::downgrade(&self.0))
+ }
+}
+
+impl<T> std::ops::Deref for Ptr<T> {
+ type Target = *mut T;
+
+ fn deref(&self) -> &Self::Target {
+ &(self.0).0
+ }
+}
+
+impl<T> fmt::Pointer for Ptr<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ fmt::Pointer::fmt(&self.0 .0, f)
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct WeakPtr<T>(Weak<PtrDrop<T>>);
+
+impl<T> WeakPtr<T> {
+ fn upgrade(&self) -> Option<Ptr<T>> {
+ self.0.upgrade().map(Ptr)
+ }
+}
+
+impl<T> fmt::Pointer for WeakPtr<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self.upgrade() {
+ Some(x) => fmt::Pointer::fmt(&x, f),
+ None => fmt::Pointer::fmt(&std::ptr::null::<T>(), f),
+ }
+ }
+}
+
+unsafe impl<T> Send for WeakPtr<T> where Ptr<T>: Send {}
+
+#[cfg(test)]
+mod test {
+ use std::os::unix::io::OwnedFd;
+
+ fn is_send<T: Send>() {}
+
+ #[test]
+ fn device_is_send() {
+ is_send::<super::Device<std::fs::File>>();
+ is_send::<super::Device<OwnedFd>>();
+ }
+
+ #[test]
+ fn surface_is_send() {
+ is_send::<super::Surface<std::fs::File>>();
+ is_send::<super::Surface<OwnedFd>>();
+ }
+
+ #[test]
+ fn unmapped_bo_is_send() {
+ is_send::<super::BufferObject<()>>();
+ }
+}
diff --git a/src/surface.rs b/src/surface.rs
new file mode 100644
index 0000000..f8040d2
--- /dev/null
+++ b/src/surface.rs
@@ -0,0 +1,125 @@
+use crate::{AsRaw, BufferObject, DeviceDestroyedError, Ptr, WeakPtr};
+use std::error;
+use std::fmt;
+use std::marker::PhantomData;
+
+/// A GBM rendering surface
+pub struct Surface<T: 'static> {
+ ffi: Ptr<ffi::gbm_surface>,
+ _device: WeakPtr<ffi::gbm_device>,
+ _bo_userdata: PhantomData<T>,
+}
+
+impl<T: 'static> fmt::Debug for Surface<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Surface")
+ .field("ptr", &format_args!("{:p}", &self.ffi))
+ .field("device", &format_args!("{:p}", &self._device))
+ .finish()
+ }
+}
+
+/// Errors that may happen when locking the front buffer
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FrontBufferError {
+ /// No free buffers are currently available
+ NoFreeBuffers,
+ /// An unknown error happened
+ Unknown,
+ /// Device was already released
+ Destroyed(DeviceDestroyedError),
+}
+
+impl fmt::Display for FrontBufferError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ FrontBufferError::NoFreeBuffers => write!(f, "No free buffers remaining"),
+ FrontBufferError::Unknown => write!(f, "Unknown error"),
+ FrontBufferError::Destroyed(ref err) => write!(f, "Buffer was destroyed: {}", err),
+ }
+ }
+}
+
+impl error::Error for FrontBufferError {
+ fn cause(&self) -> Option<&dyn error::Error> {
+ match *self {
+ FrontBufferError::Destroyed(ref err) => Some(err),
+ _ => None,
+ }
+ }
+}
+
+impl<T: 'static> Surface<T> {
+ /// Return whether or not a surface has free (non-locked) buffers
+ ///
+ /// Before starting a new frame, the surface must have a buffer
+ /// available for rendering. Initially, a GBM surface will have a free
+ /// buffer, but after one or more buffers
+ /// [have been locked](Self::lock_front_buffer()),
+ /// the application must check for a free buffer before rendering.
+ pub fn has_free_buffers(&self) -> bool {
+ let device = self._device.upgrade();
+ if device.is_some() {
+ unsafe { ffi::gbm_surface_has_free_buffers(*self.ffi) != 0 }
+ } else {
+ false
+ }
+ }
+
+ /// Lock the surface's current front buffer
+ ///
+ /// Locks rendering to the surface's current front buffer and returns
+ /// a handle to the underlying [`BufferObject`].
+ ///
+ /// If an error occurs a [`FrontBufferError`] is returned.
+ ///
+ /// # Safety
+ /// This function must be called exactly once after calling
+ /// `eglSwapBuffers`. Calling it before any `eglSwapBuffers` has happened
+ /// on the surface or two or more times after `eglSwapBuffers` is an
+ /// error and may cause undefined behavior.
+ pub unsafe fn lock_front_buffer(&self) -> Result<BufferObject<T>, FrontBufferError> {
+ let device = self._device.upgrade();
+ if device.is_some() {
+ if ffi::gbm_surface_has_free_buffers(*self.ffi) != 0 {
+ let buffer_ptr = ffi::gbm_surface_lock_front_buffer(*self.ffi);
+ if !buffer_ptr.is_null() {
+ let surface_ptr = self.ffi.downgrade();
+ let buffer = BufferObject {
+ ffi: Ptr::new(buffer_ptr, move |ptr| {
+ if let Some(surface) = surface_ptr.upgrade() {
+ ffi::gbm_surface_release_buffer(*surface, ptr);
+ }
+ }),
+ _device: self._device.clone(),
+ _userdata: std::marker::PhantomData,
+ };
+ Ok(buffer)
+ } else {
+ Err(FrontBufferError::Unknown)
+ }
+ } else {
+ Err(FrontBufferError::NoFreeBuffers)
+ }
+ } else {
+ Err(FrontBufferError::Destroyed(DeviceDestroyedError))
+ }
+ }
+
+ pub(crate) unsafe fn new(
+ ffi: *mut ffi::gbm_surface,
+ device: WeakPtr<ffi::gbm_device>,
+ ) -> Surface<T> {
+ Surface {
+ ffi: Ptr::new(ffi, |ptr| ffi::gbm_surface_destroy(ptr)),
+ _device: device,
+ _bo_userdata: PhantomData,
+ }
+ }
+}
+
+impl<T: 'static> AsRaw<ffi::gbm_surface> for Surface<T> {
+ fn as_raw(&self) -> *const ffi::gbm_surface {
+ *self.ffi
+ }
+}