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