Skip to main content

edgefirst_gbm/
device.rs

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