1 use crate::{AsRaw, BufferObject, BufferObjectFlags, Format, Modifier, Ptr, Surface}; 2 3 use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; 4 5 use std::error; 6 use std::ffi::CStr; 7 use std::fmt; 8 use std::io::{Error as IoError, Result as IoResult}; 9 use std::ops::{Deref, DerefMut}; 10 11 #[cfg(feature = "import-wayland")] 12 use wayland_server::protocol::wl_buffer::WlBuffer; 13 14 #[cfg(feature = "import-egl")] 15 /// An EGLImage handle 16 pub type EGLImage = *mut libc::c_void; 17 18 #[cfg(feature = "drm-support")] 19 use drm::control::Device as DrmControlDevice; 20 #[cfg(feature = "drm-support")] 21 use drm::Device as DrmDevice; 22 23 /// An open GBM device 24 pub struct Device<T: AsFd> { 25 // Declare `ffi` first so it is dropped before `fd` 26 ffi: Ptr<ffi::gbm_device>, 27 fd: T, 28 } 29 30 impl<T: AsFd> fmt::Debug for Device<T> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 f.debug_struct("Device") 33 .field("ptr", &format_args!("{:p}", &self.ffi)) 34 .finish() 35 } 36 } 37 38 impl<T: AsFd + Clone> Clone for Device<T> { clone(&self) -> Device<T>39 fn clone(&self) -> Device<T> { 40 Device { 41 fd: self.fd.clone(), 42 ffi: self.ffi.clone(), 43 } 44 } 45 } 46 47 impl<T: AsFd> AsFd for Device<T> { as_fd(&self) -> BorrowedFd48 fn as_fd(&self) -> BorrowedFd { 49 unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*self.ffi)) } 50 } 51 } 52 53 impl<T: AsFd> AsRaw<ffi::gbm_device> for Device<T> { as_raw(&self) -> *const ffi::gbm_device54 fn as_raw(&self) -> *const ffi::gbm_device { 55 *self.ffi 56 } 57 } 58 59 impl<T: AsFd> Deref for Device<T> { 60 type Target = T; deref(&self) -> &T61 fn deref(&self) -> &T { 62 &self.fd 63 } 64 } 65 66 impl<T: AsFd> DerefMut for Device<T> { deref_mut(&mut self) -> &mut T67 fn deref_mut(&mut self) -> &mut T { 68 &mut self.fd 69 } 70 } 71 72 impl<T: AsFd> Device<T> { 73 /// Open a GBM device from a given open DRM device. 74 /// 75 /// The underlying file descriptor passed in is used by the backend to communicate with 76 /// platform for allocating the memory. For allocations using DRI this would be 77 /// the file descriptor returned when opening a device such as `/dev/dri/card0`. new(fd: T) -> IoResult<Device<T>>78 pub fn new(fd: T) -> IoResult<Device<T>> { 79 let ptr = unsafe { ffi::gbm_create_device(fd.as_fd().as_raw_fd()) }; 80 if ptr.is_null() { 81 Err(IoError::last_os_error()) 82 } else { 83 Ok(Device { 84 fd, 85 ffi: Ptr::<ffi::gbm_device>::new(ptr, |ptr| unsafe { 86 ffi::gbm_device_destroy(ptr) 87 }), 88 }) 89 } 90 } 91 92 /// Get the backend name backend_name(&self) -> &str93 pub fn backend_name(&self) -> &str { 94 unsafe { 95 CStr::from_ptr(ffi::gbm_device_get_backend_name(*self.ffi)) 96 .to_str() 97 .expect("GBM passed invalid utf8 string") 98 } 99 } 100 101 /// Test if a format is supported for a given set of usage flags is_format_supported(&self, format: Format, usage: BufferObjectFlags) -> bool102 pub fn is_format_supported(&self, format: Format, usage: BufferObjectFlags) -> bool { 103 unsafe { ffi::gbm_device_is_format_supported(*self.ffi, format as u32, usage.bits()) != 0 } 104 } 105 106 /// Allocate a new surface object create_surface<U: 'static>( &self, width: u32, height: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult<Surface<U>>107 pub fn create_surface<U: 'static>( 108 &self, 109 width: u32, 110 height: u32, 111 format: Format, 112 usage: BufferObjectFlags, 113 ) -> IoResult<Surface<U>> { 114 let ptr = unsafe { 115 ffi::gbm_surface_create(*self.ffi, width, height, format as u32, usage.bits()) 116 }; 117 if ptr.is_null() { 118 Err(IoError::last_os_error()) 119 } else { 120 Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) }) 121 } 122 } 123 124 /// Allocate a new surface object with explicit modifiers create_surface_with_modifiers<U: 'static>( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator<Item = Modifier>, ) -> IoResult<Surface<U>>125 pub fn create_surface_with_modifiers<U: 'static>( 126 &self, 127 width: u32, 128 height: u32, 129 format: Format, 130 modifiers: impl Iterator<Item = Modifier>, 131 ) -> IoResult<Surface<U>> { 132 let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>(); 133 let ptr = unsafe { 134 ffi::gbm_surface_create_with_modifiers( 135 *self.ffi, 136 width, 137 height, 138 format as u32, 139 mods.as_ptr(), 140 mods.len() as u32, 141 ) 142 }; 143 if ptr.is_null() { 144 Err(IoError::last_os_error()) 145 } else { 146 Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) }) 147 } 148 } 149 150 /// Allocate a new surface object with explicit modifiers and flags create_surface_with_modifiers2<U: 'static>( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator<Item = Modifier>, usage: BufferObjectFlags, ) -> IoResult<Surface<U>>151 pub fn create_surface_with_modifiers2<U: 'static>( 152 &self, 153 width: u32, 154 height: u32, 155 format: Format, 156 modifiers: impl Iterator<Item = Modifier>, 157 usage: BufferObjectFlags, 158 ) -> IoResult<Surface<U>> { 159 let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>(); 160 let ptr = unsafe { 161 ffi::gbm_surface_create_with_modifiers2( 162 *self.ffi, 163 width, 164 height, 165 format as u32, 166 mods.as_ptr(), 167 mods.len() as u32, 168 usage.bits(), 169 ) 170 }; 171 if ptr.is_null() { 172 Err(IoError::last_os_error()) 173 } else { 174 Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) }) 175 } 176 } 177 178 /// Allocate a buffer object for the given dimensions create_buffer_object<U: 'static>( &self, width: u32, height: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult<BufferObject<U>>179 pub fn create_buffer_object<U: 'static>( 180 &self, 181 width: u32, 182 height: u32, 183 format: Format, 184 usage: BufferObjectFlags, 185 ) -> IoResult<BufferObject<U>> { 186 let ptr = 187 unsafe { ffi::gbm_bo_create(*self.ffi, width, height, format as u32, usage.bits()) }; 188 if ptr.is_null() { 189 Err(IoError::last_os_error()) 190 } else { 191 Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) }) 192 } 193 } 194 195 /// Allocate a buffer object for the given dimensions with explicit modifiers create_buffer_object_with_modifiers<U: 'static>( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator<Item = Modifier>, ) -> IoResult<BufferObject<U>>196 pub fn create_buffer_object_with_modifiers<U: 'static>( 197 &self, 198 width: u32, 199 height: u32, 200 format: Format, 201 modifiers: impl Iterator<Item = Modifier>, 202 ) -> IoResult<BufferObject<U>> { 203 let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>(); 204 let ptr = unsafe { 205 ffi::gbm_bo_create_with_modifiers( 206 *self.ffi, 207 width, 208 height, 209 format as u32, 210 mods.as_ptr(), 211 mods.len() as u32, 212 ) 213 }; 214 if ptr.is_null() { 215 Err(IoError::last_os_error()) 216 } else { 217 Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) }) 218 } 219 } 220 221 /// Allocate a buffer object for the given dimensions with explicit modifiers and flags create_buffer_object_with_modifiers2<U: 'static>( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator<Item = Modifier>, usage: BufferObjectFlags, ) -> IoResult<BufferObject<U>>222 pub fn create_buffer_object_with_modifiers2<U: 'static>( 223 &self, 224 width: u32, 225 height: u32, 226 format: Format, 227 modifiers: impl Iterator<Item = Modifier>, 228 usage: BufferObjectFlags, 229 ) -> IoResult<BufferObject<U>> { 230 let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>(); 231 let ptr = unsafe { 232 ffi::gbm_bo_create_with_modifiers2( 233 *self.ffi, 234 width, 235 height, 236 format as u32, 237 mods.as_ptr(), 238 mods.len() as u32, 239 usage.bits(), 240 ) 241 }; 242 if ptr.is_null() { 243 Err(IoError::last_os_error()) 244 } else { 245 Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) }) 246 } 247 } 248 249 /// Create a GBM buffer object from a wayland buffer 250 /// 251 /// This function imports a foreign [`WlBuffer`] object and creates a new GBM 252 /// buffer object for it. 253 /// This enables using the foreign object with a display API such as KMS. 254 /// 255 /// The GBM bo shares the underlying pixels but its life-time is 256 /// independent of the foreign object. 257 #[cfg(feature = "import-wayland")] import_buffer_object_from_wayland<U: 'static>( &self, buffer: &WlBuffer, usage: BufferObjectFlags, ) -> IoResult<BufferObject<U>>258 pub fn import_buffer_object_from_wayland<U: 'static>( 259 &self, 260 buffer: &WlBuffer, 261 usage: BufferObjectFlags, 262 ) -> IoResult<BufferObject<U>> { 263 use wayland_server::Resource; 264 265 let ptr = unsafe { 266 ffi::gbm_bo_import( 267 *self.ffi, 268 ffi::GBM_BO_IMPORT_WL_BUFFER as u32, 269 buffer.id().as_ptr() as *mut _, 270 usage.bits(), 271 ) 272 }; 273 if ptr.is_null() { 274 Err(IoError::last_os_error()) 275 } else { 276 Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) }) 277 } 278 } 279 280 /// Create a GBM buffer object from an egl buffer 281 /// 282 /// This function imports a foreign [`EGLImage`] object and creates a new GBM 283 /// buffer object for it. 284 /// This enables using the foreign object with a display API such as KMS. 285 /// 286 /// The GBM bo shares the underlying pixels but its life-time is 287 /// independent of the foreign object. 288 /// 289 /// # Safety 290 /// 291 /// The given [`EGLImage`] is a raw pointer. Passing null or an invalid [`EGLImage`] will 292 /// cause undefined behavior. 293 #[cfg(feature = "import-egl")] import_buffer_object_from_egl<U: 'static>( &self, buffer: EGLImage, usage: BufferObjectFlags, ) -> IoResult<BufferObject<U>>294 pub unsafe fn import_buffer_object_from_egl<U: 'static>( 295 &self, 296 buffer: EGLImage, 297 usage: BufferObjectFlags, 298 ) -> IoResult<BufferObject<U>> { 299 let ptr = ffi::gbm_bo_import( 300 *self.ffi, 301 ffi::GBM_BO_IMPORT_EGL_IMAGE as u32, 302 buffer, 303 usage.bits(), 304 ); 305 if ptr.is_null() { 306 Err(IoError::last_os_error()) 307 } else { 308 Ok(BufferObject::new(ptr, self.ffi.downgrade())) 309 } 310 } 311 312 /// Create a GBM buffer object from a dma buffer 313 /// 314 /// This function imports a foreign dma buffer from an open file descriptor 315 /// and creates a new GBM buffer object for it. 316 /// This enables using the foreign object with a display API such as KMS. 317 /// 318 /// The GBM bo shares the underlying pixels but its life-time is 319 /// independent of the foreign object. import_buffer_object_from_dma_buf<U: 'static>( &self, buffer: BorrowedFd<'_>, width: u32, height: u32, stride: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult<BufferObject<U>>320 pub fn import_buffer_object_from_dma_buf<U: 'static>( 321 &self, 322 buffer: BorrowedFd<'_>, 323 width: u32, 324 height: u32, 325 stride: u32, 326 format: Format, 327 usage: BufferObjectFlags, 328 ) -> IoResult<BufferObject<U>> { 329 let mut fd_data = ffi::gbm_import_fd_data { 330 fd: buffer.as_raw_fd(), 331 width, 332 height, 333 stride, 334 format: format as u32, 335 }; 336 337 let ptr = unsafe { 338 ffi::gbm_bo_import( 339 *self.ffi, 340 ffi::GBM_BO_IMPORT_FD as u32, 341 &mut fd_data as *mut ffi::gbm_import_fd_data as *mut _, 342 usage.bits(), 343 ) 344 }; 345 if ptr.is_null() { 346 Err(IoError::last_os_error()) 347 } else { 348 Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) }) 349 } 350 } 351 352 /// Create a GBM buffer object from a dma buffer with explicit modifiers 353 /// 354 /// This function imports a foreign dma buffer from an open file descriptor 355 /// and creates a new GBM buffer object for it. 356 /// This enables using the foreign object with a display API such as KMS. 357 /// 358 /// The GBM bo shares the underlying pixels but its life-time is 359 /// independent of the foreign object. 360 #[allow(clippy::too_many_arguments)] 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>>361 pub fn import_buffer_object_from_dma_buf_with_modifiers<U: 'static>( 362 &self, 363 len: u32, 364 buffers: [Option<BorrowedFd<'_>>; 4], 365 width: u32, 366 height: u32, 367 format: Format, 368 usage: BufferObjectFlags, 369 strides: [i32; 4], 370 offsets: [i32; 4], 371 modifier: Modifier, 372 ) -> IoResult<BufferObject<U>> { 373 let fds = buffers.map(|fd| fd.map_or(-1, |x| x.as_raw_fd())); 374 let mut fd_data = ffi::gbm_import_fd_modifier_data { 375 fds, 376 width, 377 height, 378 format: format as u32, 379 strides, 380 offsets, 381 modifier: modifier.into(), 382 num_fds: len, 383 }; 384 385 let ptr = unsafe { 386 ffi::gbm_bo_import( 387 *self.ffi, 388 ffi::GBM_BO_IMPORT_FD_MODIFIER as u32, 389 &mut fd_data as *mut ffi::gbm_import_fd_modifier_data as *mut _, 390 usage.bits(), 391 ) 392 }; 393 if ptr.is_null() { 394 Err(IoError::last_os_error()) 395 } else { 396 Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) }) 397 } 398 } 399 } 400 401 #[cfg(feature = "drm-support")] 402 impl<T: DrmDevice + AsFd> DrmDevice for Device<T> {} 403 404 #[cfg(feature = "drm-support")] 405 impl<T: DrmControlDevice + AsFd> DrmControlDevice for Device<T> {} 406 407 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 408 /// Thrown when the underlying GBM device was already destroyed 409 pub struct DeviceDestroyedError; 410 411 impl fmt::Display for DeviceDestroyedError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result412 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 413 write!(f, "The underlying GBM device was already destroyed") 414 } 415 } 416 417 impl error::Error for DeviceDestroyedError { cause(&self) -> Option<&dyn error::Error>418 fn cause(&self) -> Option<&dyn error::Error> { 419 None 420 } 421 } 422