1 | use crate::{AsRaw, BufferObject, BufferObjectFlags, Format, Modifier, Ptr, Surface}; |
2 | |
3 | use libc::c_void; |
4 | use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; |
5 | |
6 | use std::error; |
7 | use std::ffi::CStr; |
8 | use std::fmt; |
9 | use std::io::{Error as IoError, Result as IoResult}; |
10 | use std::ops::{Deref, DerefMut}; |
11 | use std::os::unix::io::RawFd; |
12 | |
13 | #[cfg (feature = "import-wayland" )] |
14 | use wayland_server::protocol::wl_buffer::WlBuffer; |
15 | |
16 | #[cfg (feature = "import-egl" )] |
17 | /// An EGLImage handle |
18 | pub type EGLImage = *mut c_void; |
19 | |
20 | #[cfg (feature = "drm-support" )] |
21 | use drm::control::Device as DrmControlDevice; |
22 | #[cfg (feature = "drm-support" )] |
23 | use drm::Device as DrmDevice; |
24 | |
25 | /// An open GBM device |
26 | pub 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 | |
32 | impl<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 | |
40 | impl<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 | |
49 | impl<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 | |
55 | impl<T: AsFd> AsRaw<ffi::gbm_device> for Device<T> { |
56 | fn as_raw(&self) -> *const ffi::gbm_device { |
57 | *self.ffi |
58 | } |
59 | } |
60 | |
61 | impl<T: AsFd> Deref for Device<T> { |
62 | type Target = T; |
63 | fn deref(&self) -> &T { |
64 | &self.fd |
65 | } |
66 | } |
67 | |
68 | impl<T: AsFd> DerefMut for Device<T> { |
69 | fn deref_mut(&mut self) -> &mut T { |
70 | &mut self.fd |
71 | } |
72 | } |
73 | |
74 | impl<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" )] |
415 | impl<T: DrmDevice + AsFd> DrmDevice for Device<T> {} |
416 | |
417 | #[cfg (feature = "drm-support" )] |
418 | impl<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 |
422 | pub struct DeviceDestroyedError; |
423 | |
424 | impl 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 | |
430 | impl error::Error for DeviceDestroyedError { |
431 | fn cause(&self) -> Option<&dyn error::Error> { |
432 | None |
433 | } |
434 | } |
435 | |