use {AsRaw, BufferObject, BufferObjectFlags, Format, Surface};
use libc::c_void;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::rc::Rc;
use std::io::{Error as IoError, Result as IoResult};
use std::os::unix::io::{AsRawFd, RawFd};
use std::ops::{Deref, DerefMut};
#[cfg(feature = "import-wayland")]
use wayland_server::Resource;
#[cfg(feature = "import-wayland")]
use wayland_server::protocol::wl_buffer::WlBuffer;
#[cfg(feature = "import-egl")]
pub type EGLImage = *mut c_void;
#[cfg(feature = "drm-support")]
use drm::Device as DrmDevice;
#[cfg(feature = "drm-support")]
use drm::control::Device as DrmControlDevice;
pub struct FdWrapper(RawFd);
impl AsRawFd for FdWrapper {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
pub struct Device<T: AsRawFd + 'static> {
fd: T,
ffi: Rc<*mut ::ffi::gbm_device>,
}
impl<T: AsRawFd + 'static> AsRawFd for Device<T> {
fn as_raw_fd(&self) -> RawFd {
unsafe { ::ffi::gbm_device_get_fd(*self.ffi) }
}
}
impl<T: AsRawFd + 'static> AsRaw<::ffi::gbm_device> for Device<T> {
fn as_raw(&self) -> *const ::ffi::gbm_device {
*self.ffi
}
}
impl<T: AsRawFd + 'static> Deref for Device<T> {
type Target = T;
fn deref(&self) -> &T {
&self.fd
}
}
impl<T: AsRawFd + 'static> DerefMut for Device<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.fd
}
}
impl Device<FdWrapper> {
pub unsafe fn new_from_fd(fd: RawFd) -> IoResult<Device<FdWrapper>> {
let ptr = ::ffi::gbm_create_device(fd);
if ptr.is_null() {
Err(IoError::last_os_error())
} else {
Ok(Device {
fd: FdWrapper(fd),
ffi: Rc::new(ptr),
})
}
}
}
impl<T: AsRawFd + 'static> Device<T> {
pub fn new(fd: T) -> IoResult<Device<T>> {
let ptr = unsafe { ::ffi::gbm_create_device(fd.as_raw_fd()) };
if ptr.is_null() {
Err(IoError::last_os_error())
} else {
Ok(Device {
fd: fd,
ffi: Rc::new(ptr),
})
}
}
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")
}
}
pub fn is_format_supported(&self, format: Format, usage: BufferObjectFlags) -> bool {
unsafe {
::ffi::gbm_device_is_format_supported(
*self.ffi,
format.as_ffi(),
usage.bits(),
) != 0
}
}
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_ffi(),
usage.bits(),
)
};
if ptr.is_null() {
Err(IoError::last_os_error())
} else {
Ok(unsafe { Surface::new(ptr, Rc::downgrade(&self.ffi)) })
}
}
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_ffi(),
usage.bits(),
)
};
if ptr.is_null() {
Err(IoError::last_os_error())
} else {
Ok(unsafe { BufferObject::new(ptr, Rc::downgrade(&self.ffi)) })
}
}
#[cfg(feature = "import-wayland")]
pub fn import_buffer_object_from_wayland<U: 'static>(
&self,
buffer: &WlBuffer,
usage: BufferObjectFlags,
) -> IoResult<BufferObject<U>> {
let ptr = unsafe {
::ffi::gbm_bo_import(
*self.ffi,
::ffi::GBM_BO_IMPORT::WL_BUFFER as u32,
buffer.ptr() as *mut _,
usage.bits(),
)
};
if ptr.is_null() {
Err(IoError::last_os_error())
} else {
Ok(unsafe { BufferObject::new(ptr, Rc::downgrade(&self.ffi)) })
}
}
#[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, Rc::downgrade(&self.ffi)))
}
}
pub fn import_buffer_object_from_dma_buf<U: 'static>(
&self,
buffer: RawFd,
width: u32,
height: u32,
stride: u32,
format: Format,
usage: BufferObjectFlags,
) -> IoResult<BufferObject<U>> {
let mut fd_data = ::ffi::gbm_import_fd_data {
fd: buffer,
width: width,
height: height,
stride: stride,
format: format.as_ffi(),
};
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, Rc::downgrade(&self.ffi)) })
}
}
}
#[cfg(feature = "drm-support")]
impl<T: DrmDevice + AsRawFd + 'static> DrmDevice for Device<T> {}
#[cfg(feature = "drm-support")]
impl<T: DrmControlDevice + AsRawFd + 'static> DrmControlDevice for Device<T> {}
impl<T: AsRawFd + 'static> Drop for Device<T> {
fn drop(&mut self) {
unsafe { ::ffi::gbm_device_destroy(*self.ffi) };
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DeviceDestroyedError;
impl fmt::Display for DeviceDestroyedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use std::error::Error;
write!(f, "{}", self.description())
}
}
impl error::Error for DeviceDestroyedError {
fn description(&self) -> &str {
"The underlying gbm device was already destroyed"
}
fn cause(&self) -> Option<&error::Error> { None }
}