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 { // Declare `ffi` first so it is dropped before `fd` ffi: Ptr, fd: T, } impl fmt::Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Device") .field("ptr", &format_args!("{:p}", &self.ffi)) .finish() } } impl Clone for Device { fn clone(&self) -> Device { Device { fd: self.fd.clone(), ffi: self.ffi.clone(), } } } impl AsFd for Device { fn as_fd(&self) -> BorrowedFd { unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*self.ffi)) } } } impl AsRaw for Device { fn as_raw(&self) -> *const ffi::gbm_device { *self.ffi } } impl Deref for Device { type Target = T; fn deref(&self) -> &T { &self.fd } } impl DerefMut for Device { fn deref_mut(&mut self) -> &mut T { &mut self.fd } } impl Device { /// 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> { 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::::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( &self, width: u32, height: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult> { 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( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, usage: BufferObjectFlags, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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( &self, width: u32, height: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult> { 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( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, usage: BufferObjectFlags, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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( &self, buffer: &WlBuffer, usage: BufferObjectFlags, ) -> IoResult> { 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( &self, buffer: EGLImage, usage: BufferObjectFlags, ) -> IoResult> { 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( &self, buffer: BorrowedFd<'_>, width: u32, height: u32, stride: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult> { 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( &self, len: u32, buffers: [Option>; 4], width: u32, height: u32, format: Format, usage: BufferObjectFlags, strides: [i32; 4], offsets: [i32; 4], modifier: Modifier, ) -> IoResult> { 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 DrmDevice for Device {} #[cfg(feature = "drm-support")] impl DrmControlDevice for Device {} #[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 } }