gbm/
device.rs

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