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&mut DebugStruct<'_, '_>.debug_struct("Device")
32 .field(name:"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(fd: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> {}
422