1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! # Safe `libgbm` bindings for [rust](https://www.rust-lang.org)
//!
//! The Generic Buffer Manager
//!
//! This module provides an abstraction that the caller can use to request a
//! buffer from the underlying memory management system for the platform.
//!
//! This allows the creation of portable code whilst still allowing access to
//! the underlying memory manager.
//!
//! This library is best used in combination with [`drm-rs`](https://github.com/Smithay/drm-rs),
//! provided through the `drm-support` feature.
//!
//! ## Example
//!
//! ```rust,no_run
//! # extern crate drm;
//! # extern crate gbm;
//! # use drm::control::connector::Info as ConnectorInfo;
//! # use drm::control::Mode;
//! use drm::control::{self, crtc, framebuffer};
//! use gbm::{BufferObjectFlags, Device, Format};
//!
//! # use std::fs::{File, OpenOptions};
//! # use std::os::unix::io::{AsFd, BorrowedFd};
//! #
//! # use drm::control::Device as ControlDevice;
//! # use drm::Device as BasicDevice;
//! # struct Card(File);
//! #
//! # impl AsFd for Card {
//! #     fn as_fd(&self) -> BorrowedFd {
//! #         self.0.as_fd()
//! #     }
//! # }
//! #
//! # impl BasicDevice for Card {}
//! # impl ControlDevice for Card {}
//! #
//! # fn init_drm_device() -> Card {
//! #     let mut options = OpenOptions::new();
//! #     options.read(true);
//! #     options.write(true);
//! #     let file = options.open("/dev/dri/card0").unwrap();
//! #     Card(file)
//! # }
//! # fn main() {
//! // ... init your drm device ...
//! let drm = init_drm_device();
//!
//! // init a GBM device
//! let gbm = Device::new(drm).unwrap();
//!
//! // create a 4x4 buffer
//! let mut bo = gbm
//!     .create_buffer_object::<()>(
//!         1280,
//!         720,
//!         Format::Argb8888,
//!         BufferObjectFlags::SCANOUT | BufferObjectFlags::WRITE,
//!     )
//!     .unwrap();
//! // write something to it (usually use import or egl rendering instead)
//! let buffer = {
//!     let mut buffer = Vec::new();
//!     for i in 0..1280 {
//!         for _ in 0..720 {
//!             buffer.push(if i % 2 == 0 { 0 } else { 255 });
//!         }
//!     }
//!     buffer
//! };
//! bo.write(&buffer).unwrap();
//!
//! // create a framebuffer from our buffer
//! let fb = gbm.add_framebuffer(&bo, 32, 32).unwrap();
//!
//! # let res_handles = gbm.resource_handles().unwrap();
//! # let con = *res_handles.connectors().iter().next().unwrap();
//! # let crtc_handle = *res_handles.crtcs().iter().next().unwrap();
//! # let connector_info: ConnectorInfo = gbm.get_connector(con, false).unwrap();
//! # let mode: Mode = connector_info.modes()[0];
//! #
//! // display it (and get a crtc, mode and connector before)
//! gbm.set_crtc(crtc_handle, Some(fb), (0, 0), &[con], Some(mode))
//!     .unwrap();
//! # }
//! ```
#![warn(missing_debug_implementations)]
#![deny(missing_docs)]

extern crate gbm_sys as ffi;
extern crate libc;

#[cfg(feature = "import-wayland")]
extern crate wayland_server;

#[cfg(feature = "drm-support")]
extern crate drm;

extern crate drm_fourcc;

#[macro_use]
extern crate bitflags;

mod buffer_object;
mod device;
mod surface;

pub use self::buffer_object::*;
pub use self::device::*;
pub use self::surface::*;
pub use drm_fourcc::{DrmFourcc as Format, DrmModifier as Modifier};

use std::fmt;
use std::sync::{Arc, Weak};

/// Trait for types that allow to obtain the underlying raw libinput pointer.
pub trait AsRaw<T> {
    /// Receive a raw pointer representing this type.
    fn as_raw(&self) -> *const T;

    #[doc(hidden)]
    fn as_raw_mut(&self) -> *mut T {
        self.as_raw() as *mut _
    }
}

struct PtrDrop<T>(*mut T, Option<Box<dyn FnOnce(*mut T) + Send + 'static>>);

impl<T> Drop for PtrDrop<T> {
    fn drop(&mut self) {
        (self.1.take().unwrap())(self.0);
    }
}

#[derive(Clone)]
pub(crate) struct Ptr<T>(Arc<PtrDrop<T>>);
// SAFETY: The types used with Ptr in this crate are all Send (namely gbm_device, gbm_surface and gbm_bo).
// The type is private and can thus not be used unsoundly by other crates.
unsafe impl<T> Send for Ptr<T> {}

impl<T> Ptr<T> {
    fn new<F: FnOnce(*mut T) + Send + 'static>(ptr: *mut T, destructor: F) -> Ptr<T> {
        Ptr(Arc::new(PtrDrop(ptr, Some(Box::new(destructor)))))
    }

    fn downgrade(&self) -> WeakPtr<T> {
        WeakPtr(Arc::downgrade(&self.0))
    }
}

impl<T> std::ops::Deref for Ptr<T> {
    type Target = *mut T;

    fn deref(&self) -> &Self::Target {
        &(self.0).0
    }
}

impl<T> fmt::Pointer for Ptr<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        fmt::Pointer::fmt(&self.0 .0, f)
    }
}

#[derive(Clone)]
pub(crate) struct WeakPtr<T>(Weak<PtrDrop<T>>);

impl<T> WeakPtr<T> {
    fn upgrade(&self) -> Option<Ptr<T>> {
        self.0.upgrade().map(Ptr)
    }
}

impl<T> fmt::Pointer for WeakPtr<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        match self.upgrade() {
            Some(x) => fmt::Pointer::fmt(&x, f),
            None => fmt::Pointer::fmt(&std::ptr::null::<T>(), f),
        }
    }
}

unsafe impl<T> Send for WeakPtr<T> where Ptr<T>: Send {}

#[cfg(test)]
mod test {
    use std::os::unix::io::OwnedFd;

    fn is_send<T: Send>() {}

    #[test]
    fn device_is_send() {
        is_send::<super::Device<std::fs::File>>();
        is_send::<super::Device<OwnedFd>>();
    }

    #[test]
    fn surface_is_send() {
        is_send::<super::Surface<std::fs::File>>();
        is_send::<super::Surface<OwnedFd>>();
    }

    #[test]
    fn unmapped_bo_is_send() {
        is_send::<super::BufferObject<()>>();
    }
}