Skip to main content

edgefirst_gbm/
lib.rs

1//! # Safe `libgbm` bindings for [rust](https://www.rust-lang.org)
2//!
3//! The Generic Buffer Manager
4//!
5//! This module provides an abstraction that the caller can use to request a
6//! buffer from the underlying memory management system for the platform.
7//!
8//! This allows the creation of portable code whilst still allowing access to
9//! the underlying memory manager.
10//!
11//! This library is best used in combination with [`drm-rs`](https://github.com/Smithay/drm-rs),
12//! provided through the `drm-support` feature.
13//!
14//! ## Example
15//!
16//! ```rust,no_run
17//! # extern crate drm;
18//! # extern crate gbm;
19//! # use drm::control::connector::Info as ConnectorInfo;
20//! # use drm::control::Mode;
21//! use drm::control::{self, crtc, framebuffer};
22//! use gbm::{BufferObjectFlags, Device, Format};
23//!
24//! # use std::fs::{File, OpenOptions};
25//! # use std::os::unix::io::{AsFd, BorrowedFd};
26//! #
27//! # use drm::control::Device as ControlDevice;
28//! # use drm::Device as BasicDevice;
29//! # struct Card(File);
30//! #
31//! # impl AsFd for Card {
32//! #     fn as_fd(&self) -> BorrowedFd {
33//! #         self.0.as_fd()
34//! #     }
35//! # }
36//! #
37//! # impl BasicDevice for Card {}
38//! # impl ControlDevice for Card {}
39//! #
40//! # fn init_drm_device() -> Card {
41//! #     let mut options = OpenOptions::new();
42//! #     options.read(true);
43//! #     options.write(true);
44//! #     let file = options.open("/dev/dri/card0").unwrap();
45//! #     Card(file)
46//! # }
47//! # fn main() {
48//! // ... init your drm device ...
49//! let drm = init_drm_device();
50//!
51//! // init a GBM device
52//! let gbm = Device::new(drm).unwrap();
53//!
54//! // create a 4x4 buffer
55//! let mut bo = gbm
56//!     .create_buffer_object::<()>(
57//!         1280,
58//!         720,
59//!         Format::Argb8888,
60//!         BufferObjectFlags::SCANOUT | BufferObjectFlags::WRITE,
61//!     )
62//!     .unwrap();
63//! // write something to it (usually use import or egl rendering instead)
64//! let buffer = {
65//!     let mut buffer = Vec::new();
66//!     for i in 0..1280 {
67//!         for _ in 0..720 {
68//!             buffer.push(if i % 2 == 0 { 0 } else { 255 });
69//!         }
70//!     }
71//!     buffer
72//! };
73//! bo.write(&buffer).unwrap();
74//!
75//! // create a framebuffer from our buffer
76//! let fb = gbm.add_framebuffer(&bo, 32, 32).unwrap();
77//!
78//! # let res_handles = gbm.resource_handles().unwrap();
79//! # let con = *res_handles.connectors().iter().next().unwrap();
80//! # let crtc_handle = *res_handles.crtcs().iter().next().unwrap();
81//! # let connector_info: ConnectorInfo = gbm.get_connector(con, false).unwrap();
82//! # let mode: Mode = connector_info.modes()[0];
83//! #
84//! // display it (and get a crtc, mode and connector before)
85//! gbm.set_crtc(crtc_handle, Some(fb), (0, 0), &[con], Some(mode))
86//!     .unwrap();
87//! # }
88//! ```
89#![warn(missing_debug_implementations)]
90#![deny(missing_docs)]
91
92extern crate gbm_sys as ffi;
93
94#[cfg(feature = "import-wayland")]
95pub use wayland_server;
96
97#[cfg(feature = "drm-support")]
98pub use drm;
99
100mod buffer_object;
101mod device;
102mod surface;
103
104pub use self::{buffer_object::*, device::*, surface::*};
105pub use drm_fourcc::{DrmFourcc as Format, DrmModifier as Modifier};
106
107use std::{fmt, sync::Arc};
108
109#[cfg(feature = "dynamic")]
110static LIB: std::sync::OnceLock<ffi::gbm> = std::sync::OnceLock::new();
111
112struct Gbm {
113    #[cfg(feature = "dynamic")]
114    lib: &'static ffi::gbm,
115}
116
117#[cfg(feature = "dynamic")]
118const LIBGBM_NAMES: &[&str] = &["libgbm.so", "libgbm.so.1", "libgbm.so.2"];
119
120#[cfg(feature = "dynamic")]
121fn get_or_init_lib() -> Result<&'static ffi::gbm, std::io::Error> {
122    if let Some(gbm) = LIB.get() {
123        return Ok(gbm);
124    }
125
126    let mut last_err = None;
127    for name in LIBGBM_NAMES {
128        match unsafe { ffi::gbm::new(*name) } {
129            Ok(l) => {
130                LIB.get_or_init(|| l);
131                return Ok(LIB.get().unwrap());
132            }
133            Err(e) => {
134                last_err = Some(e);
135            }
136        }
137    }
138
139    Err(std::io::Error::other(last_err.unwrap()))
140}
141
142#[allow(clippy::too_many_arguments)]
143#[allow(dead_code)]
144impl Gbm {
145    fn new() -> std::io::Result<Self> {
146        #[cfg(not(feature = "dynamic"))]
147        return Ok(Self {});
148        #[cfg(feature = "dynamic")]
149        Ok(Self {
150            lib: get_or_init_lib()?,
151        })
152    }
153
154    unsafe fn gbm_device_get_fd(&self, gbm: *mut ffi::gbm_device) -> libc::c_int {
155        #[cfg(not(feature = "dynamic"))]
156        return ffi::gbm_device_get_fd(gbm);
157        #[cfg(feature = "dynamic")]
158        return self.lib.gbm_device_get_fd(gbm);
159    }
160
161    unsafe fn gbm_device_get_backend_name(&self, gbm: *mut ffi::gbm_device) -> *const libc::c_char {
162        #[cfg(not(feature = "dynamic"))]
163        return ffi::gbm_device_get_backend_name(gbm);
164        #[cfg(feature = "dynamic")]
165        return self.lib.gbm_device_get_backend_name(gbm);
166    }
167
168    unsafe fn gbm_device_is_format_supported(
169        &self,
170        gbm: *mut ffi::gbm_device,
171        format: u32,
172        flags: u32,
173    ) -> libc::c_int {
174        #[cfg(not(feature = "dynamic"))]
175        return ffi::gbm_device_is_format_supported(gbm, format, flags);
176        #[cfg(feature = "dynamic")]
177        return self.lib.gbm_device_is_format_supported(gbm, format, flags);
178    }
179
180    unsafe fn gbm_device_get_format_modifier_plane_count(
181        &self,
182        gbm: *mut ffi::gbm_device,
183        format: u32,
184        modifier: u64,
185    ) -> libc::c_int {
186        #[cfg(not(feature = "dynamic"))]
187        return ffi::gbm_device_get_format_modifier_plane_count(gbm, format, modifier);
188        #[cfg(feature = "dynamic")]
189        return self
190            .lib
191            .gbm_device_get_format_modifier_plane_count(gbm, format, modifier);
192    }
193
194    unsafe fn gbm_device_destroy(&self, gbm: *mut ffi::gbm_device) {
195        #[cfg(not(feature = "dynamic"))]
196        ffi::gbm_device_destroy(gbm);
197        #[cfg(feature = "dynamic")]
198        self.lib.gbm_device_destroy(gbm);
199    }
200
201    unsafe fn gbm_create_device(&self, fd: libc::c_int) -> *mut ffi::gbm_device {
202        #[cfg(not(feature = "dynamic"))]
203        return ffi::gbm_create_device(fd);
204        #[cfg(feature = "dynamic")]
205        return self.lib.gbm_create_device(fd);
206    }
207
208    unsafe fn gbm_bo_create(
209        &self,
210        gbm: *mut ffi::gbm_device,
211        width: u32,
212        height: u32,
213        format: u32,
214        flags: u32,
215    ) -> *mut ffi::gbm_bo {
216        #[cfg(not(feature = "dynamic"))]
217        return ffi::gbm_bo_create(gbm, width, height, format, flags);
218        #[cfg(feature = "dynamic")]
219        return self.lib.gbm_bo_create(gbm, width, height, format, flags);
220    }
221
222    unsafe fn gbm_bo_create_with_modifiers(
223        &self,
224        gbm: *mut ffi::gbm_device,
225        width: u32,
226        height: u32,
227        format: u32,
228        modifiers: *const u64,
229        count: libc::c_uint,
230    ) -> *mut ffi::gbm_bo {
231        #[cfg(not(feature = "dynamic"))]
232        return ffi::gbm_bo_create_with_modifiers(gbm, width, height, format, modifiers, count);
233        #[cfg(feature = "dynamic")]
234        return self
235            .lib
236            .gbm_bo_create_with_modifiers(gbm, width, height, format, modifiers, count);
237    }
238
239    unsafe fn gbm_bo_create_with_modifiers2(
240        &self,
241        gbm: *mut ffi::gbm_device,
242        width: u32,
243        height: u32,
244        format: u32,
245        modifiers: *const u64,
246        count: libc::c_uint,
247        flags: u32,
248    ) -> *mut ffi::gbm_bo {
249        #[cfg(not(feature = "dynamic"))]
250        return ffi::gbm_bo_create_with_modifiers2(
251            gbm, width, height, format, modifiers, count, flags,
252        );
253        #[cfg(feature = "dynamic")]
254        return self
255            .lib
256            .gbm_bo_create_with_modifiers2(gbm, width, height, format, modifiers, count, flags);
257    }
258
259    unsafe fn gbm_bo_import(
260        &self,
261        gbm: *mut ffi::gbm_device,
262        type_: u32,
263        buffer: *mut libc::c_void,
264        flags: u32,
265    ) -> *mut ffi::gbm_bo {
266        #[cfg(not(feature = "dynamic"))]
267        return ffi::gbm_bo_import(gbm, type_, buffer, flags);
268        #[cfg(feature = "dynamic")]
269        return self.lib.gbm_bo_import(gbm, type_, buffer, flags);
270    }
271
272    unsafe fn gbm_bo_map(
273        &self,
274        bo: *mut ffi::gbm_bo,
275        x: u32,
276        y: u32,
277        width: u32,
278        height: u32,
279        flags: u32,
280        stride: *mut u32,
281        map_data: *mut *mut libc::c_void,
282    ) -> *mut libc::c_void {
283        #[cfg(not(feature = "dynamic"))]
284        return ffi::gbm_bo_map(bo, x, y, width, height, flags, stride, map_data);
285        #[cfg(feature = "dynamic")]
286        return self
287            .lib
288            .gbm_bo_map(bo, x, y, width, height, flags, stride, map_data);
289    }
290
291    unsafe fn gbm_bo_unmap(&self, bo: *mut ffi::gbm_bo, map_data: *mut libc::c_void) {
292        #[cfg(not(feature = "dynamic"))]
293        return ffi::gbm_bo_unmap(bo, map_data);
294        #[cfg(feature = "dynamic")]
295        return self.lib.gbm_bo_unmap(bo, map_data);
296    }
297
298    unsafe fn gbm_bo_get_width(&self, bo: *mut ffi::gbm_bo) -> u32 {
299        #[cfg(not(feature = "dynamic"))]
300        return ffi::gbm_bo_get_width(bo);
301        #[cfg(feature = "dynamic")]
302        return self.lib.gbm_bo_get_width(bo);
303    }
304
305    unsafe fn gbm_bo_get_height(&self, bo: *mut ffi::gbm_bo) -> u32 {
306        #[cfg(not(feature = "dynamic"))]
307        return ffi::gbm_bo_get_height(bo);
308        #[cfg(feature = "dynamic")]
309        return self.lib.gbm_bo_get_height(bo);
310    }
311
312    unsafe fn gbm_bo_get_stride(&self, bo: *mut ffi::gbm_bo) -> u32 {
313        #[cfg(not(feature = "dynamic"))]
314        return ffi::gbm_bo_get_stride(bo);
315        #[cfg(feature = "dynamic")]
316        return self.lib.gbm_bo_get_stride(bo);
317    }
318
319    unsafe fn gbm_bo_get_stride_for_plane(&self, bo: *mut ffi::gbm_bo, plane: libc::c_int) -> u32 {
320        #[cfg(not(feature = "dynamic"))]
321        return ffi::gbm_bo_get_stride_for_plane(bo, plane);
322        #[cfg(feature = "dynamic")]
323        return self.lib.gbm_bo_get_stride_for_plane(bo, plane);
324    }
325
326    unsafe fn gbm_bo_get_format(&self, bo: *mut ffi::gbm_bo) -> u32 {
327        #[cfg(not(feature = "dynamic"))]
328        return ffi::gbm_bo_get_format(bo);
329        #[cfg(feature = "dynamic")]
330        return self.lib.gbm_bo_get_format(bo);
331    }
332
333    unsafe fn gbm_bo_get_bpp(&self, bo: *mut ffi::gbm_bo) -> u32 {
334        #[cfg(not(feature = "dynamic"))]
335        return ffi::gbm_bo_get_bpp(bo);
336        #[cfg(feature = "dynamic")]
337        return self.lib.gbm_bo_get_bpp(bo);
338    }
339
340    unsafe fn gbm_bo_get_offset(&self, bo: *mut ffi::gbm_bo, plane: libc::c_int) -> u32 {
341        #[cfg(not(feature = "dynamic"))]
342        return ffi::gbm_bo_get_offset(bo, plane);
343        #[cfg(feature = "dynamic")]
344        return self.lib.gbm_bo_get_offset(bo, plane);
345    }
346
347    unsafe fn gbm_bo_get_device(&self, bo: *mut ffi::gbm_bo) -> *mut ffi::gbm_device {
348        #[cfg(not(feature = "dynamic"))]
349        return ffi::gbm_bo_get_device(bo);
350        #[cfg(feature = "dynamic")]
351        return self.lib.gbm_bo_get_device(bo);
352    }
353
354    unsafe fn gbm_bo_get_handle(&self, bo: *mut ffi::gbm_bo) -> ffi::gbm_bo_handle {
355        #[cfg(not(feature = "dynamic"))]
356        return ffi::gbm_bo_get_handle(bo);
357        #[cfg(feature = "dynamic")]
358        return self.lib.gbm_bo_get_handle(bo);
359    }
360
361    unsafe fn gbm_bo_get_fd(&self, bo: *mut ffi::gbm_bo) -> libc::c_int {
362        #[cfg(not(feature = "dynamic"))]
363        return ffi::gbm_bo_get_fd(bo);
364        #[cfg(feature = "dynamic")]
365        return self.lib.gbm_bo_get_fd(bo);
366    }
367
368    unsafe fn gbm_bo_get_modifier(&self, bo: *mut ffi::gbm_bo) -> u64 {
369        #[cfg(not(feature = "dynamic"))]
370        return ffi::gbm_bo_get_modifier(bo);
371        #[cfg(feature = "dynamic")]
372        return self.lib.gbm_bo_get_modifier(bo);
373    }
374
375    unsafe fn gbm_bo_get_plane_count(&self, bo: *mut ffi::gbm_bo) -> libc::c_int {
376        #[cfg(not(feature = "dynamic"))]
377        return ffi::gbm_bo_get_plane_count(bo);
378        #[cfg(feature = "dynamic")]
379        return self.lib.gbm_bo_get_plane_count(bo);
380    }
381
382    unsafe fn gbm_bo_get_handle_for_plane(
383        &self,
384        bo: *mut ffi::gbm_bo,
385        plane: libc::c_int,
386    ) -> ffi::gbm_bo_handle {
387        #[cfg(not(feature = "dynamic"))]
388        return ffi::gbm_bo_get_handle_for_plane(bo, plane);
389        #[cfg(feature = "dynamic")]
390        return self.lib.gbm_bo_get_handle_for_plane(bo, plane);
391    }
392
393    unsafe fn gbm_bo_get_fd_for_plane(
394        &self,
395        bo: *mut ffi::gbm_bo,
396        plane: libc::c_int,
397    ) -> libc::c_int {
398        #[cfg(not(feature = "dynamic"))]
399        return ffi::gbm_bo_get_fd_for_plane(bo, plane);
400        #[cfg(feature = "dynamic")]
401        return self.lib.gbm_bo_get_fd_for_plane(bo, plane);
402    }
403
404    unsafe fn gbm_bo_write(
405        &self,
406        bo: *mut ffi::gbm_bo,
407        buf: *const libc::c_void,
408        count: usize,
409    ) -> libc::c_int {
410        #[cfg(not(feature = "dynamic"))]
411        return ffi::gbm_bo_write(bo, buf, count);
412        #[cfg(feature = "dynamic")]
413        return self.lib.gbm_bo_write(bo, buf, count);
414    }
415
416    unsafe fn gbm_bo_set_user_data(
417        &self,
418        bo: *mut ffi::gbm_bo,
419        data: *mut libc::c_void,
420        destroy_user_data: ::std::option::Option<
421            unsafe extern "C" fn(arg1: *mut ffi::gbm_bo, arg2: *mut libc::c_void),
422        >,
423    ) {
424        #[cfg(not(feature = "dynamic"))]
425        return ffi::gbm_bo_set_user_data(bo, data, destroy_user_data);
426        #[cfg(feature = "dynamic")]
427        return self.lib.gbm_bo_set_user_data(bo, data, destroy_user_data);
428    }
429
430    unsafe fn gbm_bo_get_user_data(&self, bo: *mut ffi::gbm_bo) -> *mut libc::c_void {
431        #[cfg(not(feature = "dynamic"))]
432        return ffi::gbm_bo_get_user_data(bo);
433        #[cfg(feature = "dynamic")]
434        return self.lib.gbm_bo_get_user_data(bo);
435    }
436
437    unsafe fn gbm_bo_destroy(&self, bo: *mut ffi::gbm_bo) {
438        #[cfg(not(feature = "dynamic"))]
439        return ffi::gbm_bo_destroy(bo);
440        #[cfg(feature = "dynamic")]
441        return self.lib.gbm_bo_destroy(bo);
442    }
443
444    unsafe fn gbm_surface_create(
445        &self,
446        gbm: *mut ffi::gbm_device,
447        width: u32,
448        height: u32,
449        format: u32,
450        flags: u32,
451    ) -> *mut ffi::gbm_surface {
452        #[cfg(not(feature = "dynamic"))]
453        return ffi::gbm_surface_create(gbm, width, height, format, flags);
454        #[cfg(feature = "dynamic")]
455        return self
456            .lib
457            .gbm_surface_create(gbm, width, height, format, flags);
458    }
459
460    unsafe fn gbm_surface_create_with_modifiers(
461        &self,
462        gbm: *mut ffi::gbm_device,
463        width: u32,
464        height: u32,
465        format: u32,
466        modifiers: *const u64,
467        count: libc::c_uint,
468    ) -> *mut ffi::gbm_surface {
469        #[cfg(not(feature = "dynamic"))]
470        return ffi::gbm_surface_create_with_modifiers(
471            gbm, width, height, format, modifiers, count,
472        );
473        #[cfg(feature = "dynamic")]
474        return self
475            .lib
476            .gbm_surface_create_with_modifiers(gbm, width, height, format, modifiers, count);
477    }
478
479    unsafe fn gbm_surface_create_with_modifiers2(
480        &self,
481        gbm: *mut ffi::gbm_device,
482        width: u32,
483        height: u32,
484        format: u32,
485        modifiers: *const u64,
486        count: libc::c_uint,
487        flags: u32,
488    ) -> *mut ffi::gbm_surface {
489        #[cfg(not(feature = "dynamic"))]
490        return ffi::gbm_surface_create_with_modifiers2(
491            gbm, width, height, format, modifiers, count, flags,
492        );
493        #[cfg(feature = "dynamic")]
494        return self.lib.gbm_surface_create_with_modifiers2(
495            gbm, width, height, format, modifiers, count, flags,
496        );
497    }
498
499    unsafe fn gbm_surface_lock_front_buffer(
500        &self,
501        surface: *mut ffi::gbm_surface,
502    ) -> *mut ffi::gbm_bo {
503        #[cfg(not(feature = "dynamic"))]
504        return ffi::gbm_surface_lock_front_buffer(surface);
505        #[cfg(feature = "dynamic")]
506        return self.lib.gbm_surface_lock_front_buffer(surface);
507    }
508
509    unsafe fn gbm_surface_release_buffer(
510        &self,
511        surface: *mut ffi::gbm_surface,
512        bo: *mut ffi::gbm_bo,
513    ) {
514        #[cfg(not(feature = "dynamic"))]
515        return ffi::gbm_surface_release_buffer(surface, bo);
516        #[cfg(feature = "dynamic")]
517        return self.lib.gbm_surface_release_buffer(surface, bo);
518    }
519
520    unsafe fn gbm_surface_has_free_buffers(&self, surface: *mut ffi::gbm_surface) -> libc::c_int {
521        #[cfg(not(feature = "dynamic"))]
522        return ffi::gbm_surface_has_free_buffers(surface);
523        #[cfg(feature = "dynamic")]
524        return self.lib.gbm_surface_has_free_buffers(surface);
525    }
526
527    unsafe fn gbm_surface_destroy(&self, surface: *mut ffi::gbm_surface) {
528        #[cfg(not(feature = "dynamic"))]
529        return ffi::gbm_surface_destroy(surface);
530        #[cfg(feature = "dynamic")]
531        return self.lib.gbm_surface_destroy(surface);
532    }
533
534    unsafe fn gbm_format_get_name(
535        &self,
536        gbm_format: u32,
537        desc: *mut ffi::gbm_format_name_desc,
538    ) -> *mut libc::c_char {
539        #[cfg(not(feature = "dynamic"))]
540        return ffi::gbm_format_get_name(gbm_format, desc);
541        #[cfg(feature = "dynamic")]
542        return self.lib.gbm_format_get_name(gbm_format, desc);
543    }
544}
545
546/// Trait for types that allow obtaining the underlying raw pointer.
547pub trait AsRaw<T> {
548    /// Receive a raw pointer representing this type.
549    fn as_raw(&self) -> *const T;
550
551    #[doc(hidden)]
552    fn as_raw_mut(&self) -> *mut T {
553        self.as_raw() as *mut _
554    }
555}
556
557/// Private inner type for [`Ptr`]
558///
559/// This holds the raw pointer and the destructor callback for [`Ptr`].
560struct PtrDrop<T>(*mut T, Option<Box<dyn FnOnce(*mut T) + Send + 'static>>);
561
562impl<T> Drop for PtrDrop<T> {
563    fn drop(&mut self) {
564        (self.1.take().unwrap())(self.0);
565    }
566}
567
568/// A reference-counted smart pointer with destructor callback.
569///
570/// This type wraps a raw pointer into a thread-safe, reference-counted struct
571/// which will call a provided destructor when all references to the pointer
572/// are dropped.
573///
574/// Raw pointers are !Send and !Sync by default in Rust, but this wrapper
575/// explicitly implements Send and Sync. The gbm types used with Ptr in this
576/// crate are thread-safe and therefore this is correct within the context of
577/// the gbm crate.
578///
579/// The destructor callback takes the raw pointer as an argument. It will be
580/// called when all references to the Ptr have been dropped and should perform
581/// any necessary cleanup for the pointer type.
582#[derive(Clone)]
583pub(crate) struct Ptr<T>(Arc<PtrDrop<T>>);
584
585// SAFETY: The types used with Ptr in this crate are all Send and Sync (namely
586// gbm_device, gbm_surface and gbm_bo). Reference counting is implemented with
587// the thread-safe atomic `Arc`-wrapper. The type is private and therefore
588// cannot be used unsoundly by other crates.
589unsafe impl<T> Send for Ptr<T> {}
590unsafe impl<T> Sync for Ptr<T> {}
591
592impl<T> Ptr<T> {
593    fn new<F: FnOnce(*mut T) + Send + 'static>(ptr: *mut T, destructor: F) -> Ptr<T> {
594        Ptr(Arc::new(PtrDrop(ptr, Some(Box::new(destructor)))))
595    }
596}
597
598impl<T> std::ops::Deref for Ptr<T> {
599    type Target = *mut T;
600
601    fn deref(&self) -> &Self::Target {
602        &(self.0).0
603    }
604}
605
606impl<T> fmt::Pointer for Ptr<T> {
607    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
608        fmt::Pointer::fmt(&self.0 .0, f)
609    }
610}
611
612#[cfg(test)]
613mod test {
614    use std::os::unix::io::OwnedFd;
615
616    fn is_send<T: Send>() {}
617    fn is_sync<T: Sync>() {}
618
619    #[test]
620    fn device_is_send() {
621        is_send::<super::Device<std::fs::File>>();
622        is_send::<super::Device<OwnedFd>>();
623    }
624
625    #[test]
626    fn device_is_sync() {
627        is_sync::<super::Device<std::fs::File>>();
628        is_sync::<super::Device<OwnedFd>>();
629    }
630
631    #[test]
632    fn surface_is_send() {
633        is_send::<super::Surface<std::fs::File>>();
634        is_send::<super::Surface<OwnedFd>>();
635    }
636
637    #[test]
638    fn surface_is_sync() {
639        is_sync::<super::Surface<std::fs::File>>();
640        is_sync::<super::Surface<OwnedFd>>();
641    }
642
643    #[test]
644    fn unmapped_bo_is_send() {
645        is_send::<super::BufferObject<()>>();
646    }
647
648    #[test]
649    fn unmapped_bo_is_sync() {
650        is_sync::<super::BufferObject<()>>();
651    }
652}