summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Radomski <dextero@google.com>2024-03-13 17:11:51 +0000
committerMarcin Radomski <dextero@google.com>2024-03-13 17:32:13 +0000
commit367c0f6625381ba9b0ae299bb94d3e6b729e797f (patch)
treeb0cc0dc0e8c27e95ffea0b288da2b9b82acb4dcf
parent3873302c6716e710b9ccae31d0de5d8af07b1554 (diff)
downloadgbm-upstream.tar.gz
Import 'gbm' crateupstream
Request Document: go/android-rust-importing-crates For CL Reviewers: go/android3p#cl-review For Build Team: go/ab-third-party-imports Bug: http://b/325953018 Test: m libgbm_rust (with aosp/2997556 + aosp/3000518 checked out) Change-Id: I64f7dc10caadfadd8e2bda0fb1c8e196de6c6f3f
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp30
-rw-r--r--CHANGELOG.md81
-rw-r--r--Cargo.toml76
-rw-r--r--Cargo.toml.orig49
-rw-r--r--LICENSE21
-rw-r--r--METADATA20
-rw-r--r--MODULE_LICENSE_MIT0
-rw-r--r--OWNERS7
-rw-r--r--README.md65
-rw-r--r--cargo_embargo.json10
-rw-r--r--src/buffer_object.rs711
-rw-r--r--src/device.rs421
-rw-r--r--src/lib.rs209
-rw-r--r--src/surface.rs125
15 files changed, 1831 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..c8aa4db
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "606c4260e0147256fb5c2901bbe837c0dc7d9f2d"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..6b22e07
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,30 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file as changes will be overridden on upgrade.
+
+// TODO: Add license.
+rust_library {
+ name: "libgbm_rust",
+ host_supported: true,
+ crate_name: "gbm",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.14.2",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ features: [
+ "drm",
+ "drm-support",
+ ],
+ rustlibs: [
+ "libbitflags",
+ "libdrm_fourcc",
+ "libdrm_rust",
+ "libgbm_sys",
+ "liblibc",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ product_available: true,
+ vendor_available: true,
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..402ff68
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,81 @@
+## 0.14.2
+
+- buffer objects: Add a method to receive the corresponding devices file descriptor
+
+## 0.14.1
+
+- Bugfix: Don't limit modifier lists to `GBM_MAX_PLANES`
+
+## 0.14.0
+
+- Update to drm-rs 0.11
+- Use `BorrowedFd` instead of `RawFd` in API
+- Don't require generated bindings for specific OS/architecture to build
+- Fix build without default features
+
+## 0.13.0
+
+- Update to drm-rs 0.10
+- Update wayland-server to 0.31
+
+## 0.12.0
+
+- Update to drm-rs 0.9
+
+## 0.11.0
+
+- Test for `-1` in fd and fd_for_plane
+
+## 0.10.0
+
+- Update `wayland-rs` to 0.30
+- Use io-safe types over `RawFd`
+- Update to drm-rs 0.8
+- YANKED: No errors for fd-methods, use 0.11.0
+
+## 0.9.0
+
+- Update to drm-rs 0.7
+
+## 0.8.0
+
+- Update to drm-rs 0.6
+
+## 0.7.0
+
+- Update to drm-rs 0.5
+
+## 0.6.0
+
+- Update to drm-rs 0.4
+- Update bindings, add new functionality
+- Make Device clonable
+- Use drm-fourcc for Formats
+- Implement Send where applicable
+- Switch to new std-Error trait
+
+## 0.5.0
+
+- Make `Surface::lock_front_buffer` unsafe as it may cause segfaults
+
+## 0.4.0
+
+- API overhaul to use ref-counting internally to:
+ - Enable out-of-order destruction without causing leaks, crashes or double-frees
+ - Remove lifetimes, which made this api a pain to work with and almost required hacks like the `rental` crate
+- Remove `FromRaw` as it is not possible to create most structs anymore without a reference to the underlying `Device`
+- Remove `Device` initializers other then `new_from_fd`. Lifetimes do not exist anymore and it is part of the contract to drop the `Device` before closing the file descriptor.
+- Add `Device` initializer `new` that wraps any open drm device.
+- Implement the [`drm-rs`](https://github.com/Smithay/drm-rs) `Device` traits for `Device` where applicable.
+
+## 0.3.0
+
+- Upgrade to bitflags 1.0 with associated consts
+
+## 0.2.0
+
+- drm-rs support
+
+## 0.1.0
+
+- Initial release
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..693d325
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,76 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "gbm"
+version = "0.14.2"
+authors = ["Victoria Brekenfeld <github@drakulix.de>"]
+exclude = [
+ ".gitignore",
+ ".travis.yml",
+ ".rustfmt.toml",
+ ".github",
+]
+description = "libgbm bindings for rust"
+documentation = "https://docs.rs/gbm"
+readme = "README.md"
+keywords = [
+ "wayland",
+ "gbm",
+ "drm",
+ "bindings",
+]
+categories = ["external-ffi-bindings"]
+license = "MIT"
+repository = "https://github.com/Smithay/gbm.rs"
+
+[dependencies.bitflags]
+version = "1.2"
+
+[dependencies.drm]
+version = "0.11.0"
+optional = true
+
+[dependencies.drm-fourcc]
+version = "2.2"
+
+[dependencies.gbm-sys]
+version = "0.3.0"
+
+[dependencies.libc]
+version = "0.2"
+
+[dependencies.wayland-backend]
+version = "0.3"
+features = ["server_system"]
+optional = true
+
+[dependencies.wayland-server]
+version = "0.31"
+optional = true
+
+[dev-dependencies.drm]
+version = "0.11.0"
+
+[features]
+default = [
+ "import-wayland",
+ "import-egl",
+ "drm-support",
+]
+drm-support = ["drm"]
+import-egl = []
+import-wayland = [
+ "wayland-server",
+ "wayland-backend",
+]
+use_bindgen = ["gbm-sys/use_bindgen"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..ecc5649
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,49 @@
+[package]
+name = "gbm"
+description = "libgbm bindings for rust"
+license = "MIT"
+documentation = "https://docs.rs/gbm"
+repository = "https://github.com/Smithay/gbm.rs"
+version = "0.14.2"
+keywords = ["wayland", "gbm", "drm", "bindings"]
+categories = ["external-ffi-bindings"]
+authors = ["Victoria Brekenfeld <github@drakulix.de>"]
+exclude = [".gitignore", ".travis.yml", ".rustfmt.toml", ".github"]
+edition = "2021"
+
+[dependencies]
+libc = "0.2"
+bitflags = "1.2"
+drm-fourcc = "2.2"
+
+[dependencies.gbm-sys]
+version = "0.3.0"
+path = "./gbm-sys"
+
+[dependencies.drm]
+version = "0.11.0"
+optional = true
+
+[dependencies.wayland-server]
+version = "0.31"
+optional = true
+
+[dependencies.wayland-backend]
+version = "0.3"
+features = ["server_system"]
+optional = true
+
+[dev-dependencies.drm]
+version = "0.11.0"
+
+[features]
+default = ["import-wayland", "import-egl", "drm-support"]
+import-wayland = ["wayland-server", "wayland-backend"]
+import-egl = []
+drm-support = ["drm"]
+use_bindgen = ["gbm-sys/use_bindgen"]
+
+[workspace]
+members = [
+ "gbm-sys"
+]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..286a2a3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Victor Brekenfeld
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..53181fb
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "gbm"
+description: "libgbm bindings for rust"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "gbm"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/gbm/gbm-0.14.2.crate"
+ primary_source: true
+ }
+ version: "0.14.2"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2024
+ month: 3
+ day: 13
+ }
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..697f117
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 688011
+include platform/prebuilts/rust:main:/OWNERS
+
+dextero@google.com
+vill@google.com
+nputikhin@google.com
+istvannador@google.com
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..116f775
--- /dev/null
+++ b/README.md
@@ -0,0 +1,65 @@
+## 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.
+
+## Usage
+
+Add to your Cargo.toml
+
+```toml
+gbm = "0.14.2"
+```
+
+## Example
+
+```rust
+extern crate drm;
+extern crate gbm;
+
+use drm::control::{self, crtc, framebuffer};
+use gbm::{BufferObjectFlags, Device, Format};
+
+// ... init your drm device ...
+let drm = init_drm_device();
+
+// init a GBM device
+let gbm = Device::new(drm).unwrap();
+
+// create a 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();
+
+// display it (and get a crtc, mode and connector before)
+gbm.set_crtc(crtc_handle, Some(fb), (0, 0), &[con], Some(mode))
+ .unwrap();
+```
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..f275bc5
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,10 @@
+{
+ "run_cargo": false,
+ "module_name_overrides": {
+ "libdrm": "libdrm_rust",
+ "libgbm": "libgbm_rust"
+ },
+ "features": [
+ "drm-support"
+ ]
+}
diff --git a/src/buffer_object.rs b/src/buffer_object.rs
new file mode 100644
index 0000000..514ee8e
--- /dev/null
+++ b/src/buffer_object.rs
@@ -0,0 +1,711 @@
+use crate::{AsRaw, Device, DeviceDestroyedError, Format, Modifier, Ptr, WeakPtr};
+
+#[cfg(feature = "drm-support")]
+use drm::buffer::{Buffer as DrmBuffer, Handle, PlanarBuffer as DrmPlanarBuffer};
+use std::os::unix::io::{AsFd, BorrowedFd, FromRawFd, OwnedFd};
+
+use std::error;
+use std::fmt;
+use std::io::{Error as IoError, Result as IoResult};
+use std::marker::PhantomData;
+use std::ops::{Deref, DerefMut};
+use std::ptr;
+use std::slice;
+
+/// A GBM buffer object
+pub struct BufferObject<T: 'static> {
+ pub(crate) ffi: Ptr<ffi::gbm_bo>,
+ pub(crate) _device: WeakPtr<ffi::gbm_device>,
+ pub(crate) _userdata: PhantomData<T>,
+}
+
+impl<T> fmt::Debug for BufferObject<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("BufferObject")
+ .field("ptr", &format_args!("{:p}", self.ffi))
+ .field("device", &format_args!("{:p}", &self._device))
+ .field("width", &self.width().unwrap_or(0))
+ .field("height", &self.height().unwrap_or(0))
+ .field("offsets", &self.offsets())
+ .field("stride", &self.stride().unwrap_or(0))
+ .field("format", &self.format().ok())
+ .field("modifier", &self.modifier().ok())
+ .finish()
+ }
+}
+
+bitflags! {
+ /// Flags to indicate the intended use for the buffer - these are passed into
+ /// [`Device::create_buffer_object()`].
+ ///
+ /// Use [`Device::is_format_supported()`] to check if the combination of format
+ /// and use flags are supported
+ pub struct BufferObjectFlags: u32 {
+ /// Buffer is going to be presented to the screen using an API such as KMS
+ const SCANOUT = ffi::gbm_bo_flags::GBM_BO_USE_SCANOUT as u32;
+ /// Buffer is going to be used as cursor
+ const CURSOR = ffi::gbm_bo_flags::GBM_BO_USE_CURSOR as u32;
+ /// Buffer is going to be used as cursor (deprecated)
+ #[deprecated = "Use CURSOR instead"]
+ const CURSOR_64X64 = ffi::gbm_bo_flags::GBM_BO_USE_CURSOR_64X64 as u32;
+ /// Buffer is to be used for rendering - for example it is going to be used
+ /// as the storage for a color buffer
+ const RENDERING = ffi::gbm_bo_flags::GBM_BO_USE_RENDERING as u32;
+ /// Buffer can be used for [`BufferObject::write()`]. This is guaranteed to work
+ /// with [`Self::CURSOR`], but may not work for other combinations.
+ const WRITE = ffi::gbm_bo_flags::GBM_BO_USE_WRITE as u32;
+ /// Buffer is linear, i.e. not tiled.
+ const LINEAR = ffi::gbm_bo_flags::GBM_BO_USE_LINEAR as u32;
+ /// Buffer is protected
+ const PROTECTED = ffi::gbm_bo_flags::GBM_BO_USE_PROTECTED as u32;
+ }
+}
+
+/// Abstraction representing the handle to a buffer allocated by the manager
+pub type BufferObjectHandle = ffi::gbm_bo_handle;
+
+enum BORef<'a, T: 'static> {
+ Ref(&'a BufferObject<T>),
+ Mut(&'a mut BufferObject<T>),
+}
+
+/// A mapped buffer object
+pub struct MappedBufferObject<'a, T: 'static> {
+ bo: BORef<'a, T>,
+ buffer: &'a mut [u8],
+ data: *mut ::libc::c_void,
+ stride: u32,
+ height: u32,
+ width: u32,
+ x: u32,
+ y: u32,
+}
+
+impl<'a, T> fmt::Debug for MappedBufferObject<'a, T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("MappedBufferObject")
+ .field(
+ "mode",
+ &match self.bo {
+ BORef::Ref(_) => format_args!("read"),
+ BORef::Mut(_) => format_args!("write"),
+ },
+ )
+ .field(
+ "buffer",
+ match &self.bo {
+ BORef::Ref(bo) => *bo,
+ BORef::Mut(bo) => *bo,
+ },
+ )
+ .finish()
+ }
+}
+
+impl<'a, T: 'static> MappedBufferObject<'a, T> {
+ /// Get the stride of the buffer object
+ ///
+ /// This is calculated by the backend when it does the allocation of the buffer.
+ pub fn stride(&self) -> u32 {
+ self.stride
+ }
+
+ /// The height of the mapped region for the buffer
+ pub fn height(&self) -> u32 {
+ self.height
+ }
+
+ /// The width of the mapped region for the buffer
+ pub fn width(&self) -> u32 {
+ self.width
+ }
+
+ /// The X (top left origin) starting position of the mapped region for the buffer
+ pub fn x(&self) -> u32 {
+ self.x
+ }
+
+ /// The Y (top left origin) starting position of the mapped region for the buffer
+ pub fn y(&self) -> u32 {
+ self.y
+ }
+
+ /// Access to the underlying image buffer
+ pub fn buffer(&self) -> &[u8] {
+ self.buffer
+ }
+
+ /// Mutable access to the underlying image buffer
+ pub fn buffer_mut(&mut self) -> &mut [u8] {
+ self.buffer
+ }
+}
+
+impl<'a, T: 'static> Deref for MappedBufferObject<'a, T> {
+ type Target = BufferObject<T>;
+ fn deref(&self) -> &BufferObject<T> {
+ match &self.bo {
+ BORef::Ref(bo) => bo,
+ BORef::Mut(bo) => bo,
+ }
+ }
+}
+
+impl<'a, T: 'static> DerefMut for MappedBufferObject<'a, T> {
+ fn deref_mut(&mut self) -> &mut BufferObject<T> {
+ match &mut self.bo {
+ BORef::Ref(_) => unreachable!(),
+ BORef::Mut(bo) => bo,
+ }
+ }
+}
+
+impl<'a, T: 'static> Drop for MappedBufferObject<'a, T> {
+ fn drop(&mut self) {
+ let ffi = match &self.bo {
+ BORef::Ref(bo) => &bo.ffi,
+ BORef::Mut(bo) => &bo.ffi,
+ };
+ unsafe { ffi::gbm_bo_unmap(**ffi, self.data) }
+ }
+}
+
+unsafe extern "C" fn destroy<T: 'static>(_: *mut ffi::gbm_bo, ptr: *mut ::libc::c_void) {
+ let ptr = ptr as *mut T;
+ if !ptr.is_null() {
+ let _ = Box::from_raw(ptr);
+ }
+}
+
+impl<T: 'static> BufferObject<T> {
+ /// Get the width of the buffer object
+ pub fn width(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_width(*self.ffi) })
+ }
+
+ /// Get the height of the buffer object
+ pub fn height(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_height(*self.ffi) })
+ }
+
+ /// Get the stride of the buffer object
+ pub fn stride(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_stride(*self.ffi) })
+ }
+
+ /// Get the stride of the buffer object
+ pub fn stride_for_plane(&self, plane: i32) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_stride_for_plane(*self.ffi, plane) })
+ }
+
+ /// Get the format of the buffer object
+ pub fn format(&self) -> Result<Format, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(
+ Format::try_from(unsafe { ffi::gbm_bo_get_format(*self.ffi) })
+ .expect("libgbm returned invalid buffer format"),
+ )
+ }
+
+ /// Get the bits per pixel of the buffer object
+ pub fn bpp(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_bpp(*self.ffi) })
+ }
+
+ /// Get the offset for a plane of the buffer object
+ pub fn offset(&self, plane: i32) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_offset(*self.ffi, plane) })
+ }
+
+ /// Get the plane count of the buffer object
+ pub fn plane_count(&self) -> Result<u32, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_plane_count(*self.ffi) as u32 })
+ }
+
+ /// Get the modifier of the buffer object
+ pub fn modifier(&self) -> Result<Modifier, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(Modifier::from(unsafe {
+ ffi::gbm_bo_get_modifier(*self.ffi)
+ }))
+ }
+
+ /// Get a DMA-BUF file descriptor for the buffer object
+ ///
+ /// This function creates a DMA-BUF (also known as PRIME) file descriptor
+ /// handle for the buffer object. Each call to [`Self::fd()`] returns a new
+ /// file descriptor and the caller is responsible for closing the file
+ /// descriptor.
+ pub fn fd(&self) -> Result<OwnedFd, FdError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ unsafe {
+ let fd = ffi::gbm_bo_get_fd(*self.ffi);
+
+ if fd == -1 {
+ return Err(InvalidFdError.into());
+ }
+
+ Ok(OwnedFd::from_raw_fd(fd))
+ }
+ }
+
+ /// Get the file descriptor of the gbm device of this buffer object
+ pub fn device_fd(&self) -> Result<BorrowedFd, DeviceDestroyedError> {
+ let device_ptr = self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*device_ptr)) })
+ }
+
+ /// Get the handle of the buffer object
+ ///
+ /// This is stored in the platform generic union [`BufferObjectHandle`] type. However
+ /// the format of this handle is platform specific.
+ pub fn handle(&self) -> Result<BufferObjectHandle, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_handle(*self.ffi) })
+ }
+
+ /// Get a DMA-BUF file descriptor for a plane of the buffer object
+ ///
+ /// This function creates a DMA-BUF (also known as PRIME) file descriptor
+ /// handle for a plane of the buffer object. Each call to [`Self::fd_for_plane()`]
+ /// returns a new file descriptor and the caller is responsible for closing
+ /// the file descriptor.
+ pub fn fd_for_plane(&self, plane: i32) -> Result<OwnedFd, FdError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ unsafe {
+ let fd = ffi::gbm_bo_get_fd_for_plane(*self.ffi, plane);
+
+ if fd == -1 {
+ return Err(InvalidFdError.into());
+ }
+
+ Ok(OwnedFd::from_raw_fd(fd))
+ }
+ }
+
+ /// Get the handle of a plane of the buffer object
+ ///
+ /// This is stored in the platform generic union [`BufferObjectHandle`] type. However
+ /// the format of this handle is platform specific.
+ pub fn handle_for_plane(&self, plane: i32) -> Result<BufferObjectHandle, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ Ok(unsafe { ffi::gbm_bo_get_handle_for_plane(*self.ffi, plane) })
+ }
+
+ /// Map a region of a GBM buffer object for cpu access
+ ///
+ /// This function maps a region of a GBM bo for cpu read access.
+ pub fn map<'a, D, F, S>(
+ &'a self,
+ device: &Device<D>,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ f: F,
+ ) -> Result<IoResult<S>, WrongDeviceError>
+ where
+ D: AsFd + 'static,
+ F: FnOnce(&MappedBufferObject<'a, T>) -> S,
+ {
+ let device_ref = self._device.upgrade().ok_or(WrongDeviceError)?;
+ if *device_ref != device.as_raw_mut() {
+ // not matching
+ return Err(WrongDeviceError);
+ }
+
+ unsafe {
+ let mut data: *mut ::libc::c_void = ptr::null_mut();
+ let mut stride = 0;
+ let ptr = ffi::gbm_bo_map(
+ *self.ffi,
+ x,
+ y,
+ width,
+ height,
+ ffi::gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ as u32,
+ &mut stride as *mut _,
+ &mut data as *mut _,
+ );
+
+ if ptr.is_null() {
+ Ok(Err(IoError::last_os_error()))
+ } else {
+ Ok(Ok(f(&MappedBufferObject {
+ bo: BORef::Ref(self),
+ buffer: slice::from_raw_parts_mut(ptr as *mut _, (height * stride) as usize),
+ data,
+ stride,
+ height,
+ width,
+ x,
+ y,
+ })))
+ }
+ }
+ }
+
+ /// Map a region of a GBM buffer object for cpu access
+ ///
+ /// This function maps a region of a GBM bo for cpu read/write access.
+ pub fn map_mut<'a, D, F, S>(
+ &'a mut self,
+ device: &Device<D>,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ f: F,
+ ) -> Result<IoResult<S>, WrongDeviceError>
+ where
+ D: AsFd + 'static,
+ F: FnOnce(&mut MappedBufferObject<'a, T>) -> S,
+ {
+ let device_ref = self._device.upgrade().ok_or(WrongDeviceError)?;
+ if *device_ref != device.as_raw_mut() {
+ // not matching
+ return Err(WrongDeviceError);
+ }
+
+ unsafe {
+ let mut data: *mut ::libc::c_void = ptr::null_mut();
+ let mut stride = 0;
+ let ptr = ffi::gbm_bo_map(
+ *self.ffi,
+ x,
+ y,
+ width,
+ height,
+ ffi::gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ_WRITE as u32,
+ &mut stride as *mut _,
+ &mut data as *mut _,
+ );
+
+ if ptr.is_null() {
+ Ok(Err(IoError::last_os_error()))
+ } else {
+ Ok(Ok(f(&mut MappedBufferObject {
+ bo: BORef::Mut(self),
+ buffer: slice::from_raw_parts_mut(ptr as *mut _, (height * stride) as usize),
+ data,
+ stride,
+ height,
+ width,
+ x,
+ y,
+ })))
+ }
+ }
+ }
+
+ /// Write data into the buffer object
+ ///
+ /// If the buffer object was created with the [`BufferObjectFlags::WRITE`] flag,
+ /// this function can be used to write data into the buffer object. The
+ /// data is copied directly into the object and it's the responsibility
+ /// of the caller to make sure the data represents valid pixel data,
+ /// according to the width, height, stride and format of the buffer object.
+ pub fn write(&mut self, buffer: &[u8]) -> Result<IoResult<()>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let result =
+ unsafe { ffi::gbm_bo_write(*self.ffi, buffer.as_ptr() as *const _, buffer.len() as _) };
+ if result != 0 {
+ Ok(Err(IoError::last_os_error()))
+ } else {
+ Ok(Ok(()))
+ }
+ }
+
+ /// Sets the userdata of the buffer object.
+ ///
+ /// If previously userdata was set, it is returned.
+ pub fn set_userdata(&mut self, userdata: T) -> Result<Option<T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let old = self.take_userdata();
+
+ let boxed = Box::new(userdata);
+ unsafe {
+ ffi::gbm_bo_set_user_data(
+ *self.ffi,
+ Box::into_raw(boxed) as *mut _,
+ Some(destroy::<T>),
+ );
+ }
+
+ old
+ }
+
+ /// Clears the set userdata of the buffer object.
+ pub fn clear_userdata(&mut self) -> Result<(), DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let _ = self.take_userdata();
+ Ok(())
+ }
+
+ /// Returns a reference to set userdata, if any.
+ pub fn userdata(&self) -> Result<Option<&T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+ if raw.is_null() {
+ Ok(None)
+ } else {
+ unsafe { Ok(Some(&*(raw as *mut T))) }
+ }
+ }
+
+ /// Returns a mutable reference to set userdata, if any.
+ pub fn userdata_mut(&mut self) -> Result<Option<&mut T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+ if raw.is_null() {
+ Ok(None)
+ } else {
+ unsafe { Ok(Some(&mut *(raw as *mut T))) }
+ }
+ }
+
+ /// Takes ownership of previously set userdata, if any.
+ ///
+ /// This removes the userdata from the buffer object.
+ pub fn take_userdata(&mut self) -> Result<Option<T>, DeviceDestroyedError> {
+ self._device.upgrade().ok_or(DeviceDestroyedError)?;
+ let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+ if raw.is_null() {
+ Ok(None)
+ } else {
+ unsafe {
+ let boxed = Box::from_raw(raw as *mut T);
+ ffi::gbm_bo_set_user_data(*self.ffi, ptr::null_mut(), None);
+ Ok(Some(*boxed))
+ }
+ }
+ }
+
+ pub(crate) unsafe fn new(
+ ffi: *mut ffi::gbm_bo,
+ device: WeakPtr<ffi::gbm_device>,
+ ) -> BufferObject<T> {
+ BufferObject {
+ ffi: Ptr::<ffi::gbm_bo>::new(ffi, |ptr| ffi::gbm_bo_destroy(ptr)),
+ _device: device,
+ _userdata: PhantomData,
+ }
+ }
+
+ fn offsets(&self) -> [u32; 4] {
+ let num = self
+ .plane_count()
+ .expect("GbmDevice does not exist anymore");
+ [
+ BufferObject::<T>::offset(self, 0).unwrap(),
+ if num > 1 {
+ BufferObject::<T>::offset(self, 1).unwrap()
+ } else {
+ 0
+ },
+ if num > 2 {
+ BufferObject::<T>::offset(self, 2).unwrap()
+ } else {
+ 0
+ },
+ if num > 3 {
+ BufferObject::<T>::offset(self, 3).unwrap()
+ } else {
+ 0
+ },
+ ]
+ }
+}
+
+impl<T: 'static> AsRaw<ffi::gbm_bo> for BufferObject<T> {
+ fn as_raw(&self) -> *const ffi::gbm_bo {
+ *self.ffi
+ }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: 'static> DrmBuffer for BufferObject<T> {
+ fn size(&self) -> (u32, u32) {
+ (
+ self.width().expect("GbmDevice does not exist anymore"),
+ self.height().expect("GbmDevice does not exist anymore"),
+ )
+ }
+
+ fn format(&self) -> Format {
+ BufferObject::<T>::format(self).expect("GbmDevice does not exist anymore")
+ }
+
+ fn pitch(&self) -> u32 {
+ self.stride().expect("GbmDevice does not exist anymore")
+ }
+
+ fn handle(&self) -> Handle {
+ use std::num::NonZeroU32;
+ unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ self.handle()
+ .expect("GbmDevice does not exist anymore")
+ .u32_,
+ ))
+ }
+ }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: 'static> DrmPlanarBuffer for BufferObject<T> {
+ fn size(&self) -> (u32, u32) {
+ (
+ self.width().expect("GbmDevice does not exist anymore"),
+ self.height().expect("GbmDevice does not exist anymore"),
+ )
+ }
+ fn format(&self) -> Format {
+ BufferObject::<T>::format(self).expect("GbmDevice does not exist anymore")
+ }
+ fn modifier(&self) -> Option<Modifier> {
+ Some(BufferObject::<T>::modifier(self).expect("GbmDevice does not exist anymore"))
+ }
+ fn pitches(&self) -> [u32; 4] {
+ let num = self
+ .plane_count()
+ .expect("GbmDevice does not exist anymore");
+ [
+ BufferObject::<T>::stride_for_plane(self, 0).unwrap(),
+ if num > 1 {
+ BufferObject::<T>::stride_for_plane(self, 1).unwrap()
+ } else {
+ 0
+ },
+ if num > 2 {
+ BufferObject::<T>::stride_for_plane(self, 2).unwrap()
+ } else {
+ 0
+ },
+ if num > 3 {
+ BufferObject::<T>::stride_for_plane(self, 3).unwrap()
+ } else {
+ 0
+ },
+ ]
+ }
+ fn handles(&self) -> [Option<Handle>; 4] {
+ use std::num::NonZeroU32;
+ let num = self
+ .plane_count()
+ .expect("GbmDevice does not exist anymore");
+ [
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 0).unwrap().u32_,
+ ))
+ }),
+ if num > 1 {
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 1).unwrap().u32_,
+ ))
+ })
+ } else {
+ None
+ },
+ if num > 2 {
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 2).unwrap().u32_,
+ ))
+ })
+ } else {
+ None
+ },
+ if num > 3 {
+ Some(unsafe {
+ Handle::from(NonZeroU32::new_unchecked(
+ BufferObject::<T>::handle_for_plane(self, 3).unwrap().u32_,
+ ))
+ })
+ } else {
+ None
+ },
+ ]
+ }
+ fn offsets(&self) -> [u32; 4] {
+ self.offsets()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// Thrown when the GBM device does not belong to the buffer object
+pub struct WrongDeviceError;
+
+impl fmt::Display for WrongDeviceError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "The gbm device specified is not the one this buffer object belongs to"
+ )
+ }
+}
+
+impl error::Error for WrongDeviceError {}
+
+/// Thrown when the fd is invalid
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct InvalidFdError;
+
+impl fmt::Display for InvalidFdError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "The returned fd is invalid")
+ }
+}
+
+impl error::Error for InvalidFdError {}
+
+/// Thrown when an error occurs during getting a bo fd
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FdError {
+ /// The underlying device has been destroyed
+ DeviceDestroyed(DeviceDestroyedError),
+ /// The operation returned an invalid fd
+ InvalidFd(InvalidFdError),
+}
+
+impl From<DeviceDestroyedError> for FdError {
+ fn from(err: DeviceDestroyedError) -> Self {
+ FdError::DeviceDestroyed(err)
+ }
+}
+
+impl From<InvalidFdError> for FdError {
+ fn from(err: InvalidFdError) -> Self {
+ FdError::InvalidFd(err)
+ }
+}
+
+impl fmt::Display for FdError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ FdError::DeviceDestroyed(err) => err.fmt(f),
+ FdError::InvalidFd(err) => err.fmt(f),
+ }
+ }
+}
+
+impl error::Error for FdError {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ FdError::DeviceDestroyed(err) => Some(err),
+ FdError::InvalidFd(err) => Some(err),
+ }
+ }
+}
diff --git a/src/device.rs b/src/device.rs
new file mode 100644
index 0000000..b5aa670
--- /dev/null
+++ b/src/device.rs
@@ -0,0 +1,421 @@
+use crate::{AsRaw, BufferObject, BufferObjectFlags, Format, Modifier, Ptr, Surface};
+
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
+
+use std::error;
+use std::ffi::CStr;
+use std::fmt;
+use std::io::{Error as IoError, Result as IoResult};
+use std::ops::{Deref, DerefMut};
+
+#[cfg(feature = "import-wayland")]
+use wayland_server::protocol::wl_buffer::WlBuffer;
+
+#[cfg(feature = "import-egl")]
+/// An EGLImage handle
+pub type EGLImage = *mut libc::c_void;
+
+#[cfg(feature = "drm-support")]
+use drm::control::Device as DrmControlDevice;
+#[cfg(feature = "drm-support")]
+use drm::Device as DrmDevice;
+
+/// An open GBM device
+pub struct Device<T: AsFd> {
+ // Declare `ffi` first so it is dropped before `fd`
+ ffi: Ptr<ffi::gbm_device>,
+ fd: T,
+}
+
+impl<T: AsFd> fmt::Debug for Device<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Device")
+ .field("ptr", &format_args!("{:p}", &self.ffi))
+ .finish()
+ }
+}
+
+impl<T: AsFd + Clone> Clone for Device<T> {
+ fn clone(&self) -> Device<T> {
+ Device {
+ fd: self.fd.clone(),
+ ffi: self.ffi.clone(),
+ }
+ }
+}
+
+impl<T: AsFd> AsFd for Device<T> {
+ fn as_fd(&self) -> BorrowedFd {
+ unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*self.ffi)) }
+ }
+}
+
+impl<T: AsFd> AsRaw<ffi::gbm_device> for Device<T> {
+ fn as_raw(&self) -> *const ffi::gbm_device {
+ *self.ffi
+ }
+}
+
+impl<T: AsFd> Deref for Device<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.fd
+ }
+}
+
+impl<T: AsFd> DerefMut for Device<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.fd
+ }
+}
+
+impl<T: AsFd> Device<T> {
+ /// Open a GBM device from a given open DRM device.
+ ///
+ /// The underlying file descriptor passed in is used by the backend to communicate with
+ /// platform for allocating the memory. For allocations using DRI this would be
+ /// the file descriptor returned when opening a device such as `/dev/dri/card0`.
+ pub fn new(fd: T) -> IoResult<Device<T>> {
+ let ptr = unsafe { ffi::gbm_create_device(fd.as_fd().as_raw_fd()) };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(Device {
+ fd,
+ ffi: Ptr::<ffi::gbm_device>::new(ptr, |ptr| unsafe {
+ ffi::gbm_device_destroy(ptr)
+ }),
+ })
+ }
+ }
+
+ /// Get the backend name
+ pub fn backend_name(&self) -> &str {
+ unsafe {
+ CStr::from_ptr(ffi::gbm_device_get_backend_name(*self.ffi))
+ .to_str()
+ .expect("GBM passed invalid utf8 string")
+ }
+ }
+
+ /// Test if a format is supported for a given set of usage flags
+ pub fn is_format_supported(&self, format: Format, usage: BufferObjectFlags) -> bool {
+ unsafe { ffi::gbm_device_is_format_supported(*self.ffi, format as u32, usage.bits()) != 0 }
+ }
+
+ /// Allocate a new surface object
+ pub fn create_surface<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ ) -> IoResult<Surface<U>> {
+ let ptr = unsafe {
+ ffi::gbm_surface_create(*self.ffi, width, height, format as u32, usage.bits())
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a new surface object with explicit modifiers
+ pub fn create_surface_with_modifiers<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ ) -> IoResult<Surface<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_surface_create_with_modifiers(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a new surface object with explicit modifiers and flags
+ pub fn create_surface_with_modifiers2<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ usage: BufferObjectFlags,
+ ) -> IoResult<Surface<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_surface_create_with_modifiers2(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a buffer object for the given dimensions
+ pub fn create_buffer_object<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let ptr =
+ unsafe { ffi::gbm_bo_create(*self.ffi, width, height, format as u32, usage.bits()) };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a buffer object for the given dimensions with explicit modifiers
+ pub fn create_buffer_object_with_modifiers<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ ) -> IoResult<BufferObject<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_bo_create_with_modifiers(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Allocate a buffer object for the given dimensions with explicit modifiers and flags
+ pub fn create_buffer_object_with_modifiers2<U: 'static>(
+ &self,
+ width: u32,
+ height: u32,
+ format: Format,
+ modifiers: impl Iterator<Item = Modifier>,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+ let ptr = unsafe {
+ ffi::gbm_bo_create_with_modifiers2(
+ *self.ffi,
+ width,
+ height,
+ format as u32,
+ mods.as_ptr(),
+ mods.len() as u32,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Create a GBM buffer object from a wayland buffer
+ ///
+ /// This function imports a foreign [`WlBuffer`] object and creates a new GBM
+ /// buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ #[cfg(feature = "import-wayland")]
+ pub fn import_buffer_object_from_wayland<U: 'static>(
+ &self,
+ buffer: &WlBuffer,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ use wayland_server::Resource;
+
+ let ptr = unsafe {
+ ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_WL_BUFFER as u32,
+ buffer.id().as_ptr() as *mut _,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Create a GBM buffer object from an egl buffer
+ ///
+ /// This function imports a foreign [`EGLImage`] object and creates a new GBM
+ /// buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ ///
+ /// # Safety
+ ///
+ /// The given [`EGLImage`] is a raw pointer. Passing null or an invalid [`EGLImage`] will
+ /// cause undefined behavior.
+ #[cfg(feature = "import-egl")]
+ pub unsafe fn import_buffer_object_from_egl<U: 'static>(
+ &self,
+ buffer: EGLImage,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let ptr = ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_EGL_IMAGE as u32,
+ buffer,
+ usage.bits(),
+ );
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(BufferObject::new(ptr, self.ffi.downgrade()))
+ }
+ }
+
+ /// Create a GBM buffer object from a dma buffer
+ ///
+ /// This function imports a foreign dma buffer from an open file descriptor
+ /// and creates a new GBM buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ pub fn import_buffer_object_from_dma_buf<U: 'static>(
+ &self,
+ buffer: BorrowedFd<'_>,
+ width: u32,
+ height: u32,
+ stride: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ ) -> IoResult<BufferObject<U>> {
+ let mut fd_data = ffi::gbm_import_fd_data {
+ fd: buffer.as_raw_fd(),
+ width,
+ height,
+ stride,
+ format: format as u32,
+ };
+
+ let ptr = unsafe {
+ ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_FD as u32,
+ &mut fd_data as *mut ffi::gbm_import_fd_data as *mut _,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+
+ /// Create a GBM buffer object from a dma buffer with explicit modifiers
+ ///
+ /// This function imports a foreign dma buffer from an open file descriptor
+ /// and creates a new GBM buffer object for it.
+ /// This enables using the foreign object with a display API such as KMS.
+ ///
+ /// The GBM bo shares the underlying pixels but its life-time is
+ /// independent of the foreign object.
+ #[allow(clippy::too_many_arguments)]
+ pub fn import_buffer_object_from_dma_buf_with_modifiers<U: 'static>(
+ &self,
+ len: u32,
+ buffers: [Option<BorrowedFd<'_>>; 4],
+ width: u32,
+ height: u32,
+ format: Format,
+ usage: BufferObjectFlags,
+ strides: [i32; 4],
+ offsets: [i32; 4],
+ modifier: Modifier,
+ ) -> IoResult<BufferObject<U>> {
+ let fds = buffers.map(|fd| fd.map_or(-1, |x| x.as_raw_fd()));
+ let mut fd_data = ffi::gbm_import_fd_modifier_data {
+ fds,
+ width,
+ height,
+ format: format as u32,
+ strides,
+ offsets,
+ modifier: modifier.into(),
+ num_fds: len,
+ };
+
+ let ptr = unsafe {
+ ffi::gbm_bo_import(
+ *self.ffi,
+ ffi::GBM_BO_IMPORT_FD_MODIFIER as u32,
+ &mut fd_data as *mut ffi::gbm_import_fd_modifier_data as *mut _,
+ usage.bits(),
+ )
+ };
+ if ptr.is_null() {
+ Err(IoError::last_os_error())
+ } else {
+ Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+ }
+ }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: DrmDevice + AsFd> DrmDevice for Device<T> {}
+
+#[cfg(feature = "drm-support")]
+impl<T: DrmControlDevice + AsFd> DrmControlDevice for Device<T> {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// Thrown when the underlying GBM device was already destroyed
+pub struct DeviceDestroyedError;
+
+impl fmt::Display for DeviceDestroyedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "The underlying GBM device was already destroyed")
+ }
+}
+
+impl error::Error for DeviceDestroyedError {
+ fn cause(&self) -> Option<&dyn error::Error> {
+ None
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..7d0c8cf
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,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<()>>();
+ }
+}
diff --git a/src/surface.rs b/src/surface.rs
new file mode 100644
index 0000000..f8040d2
--- /dev/null
+++ b/src/surface.rs
@@ -0,0 +1,125 @@
+use crate::{AsRaw, BufferObject, DeviceDestroyedError, Ptr, WeakPtr};
+use std::error;
+use std::fmt;
+use std::marker::PhantomData;
+
+/// A GBM rendering surface
+pub struct Surface<T: 'static> {
+ ffi: Ptr<ffi::gbm_surface>,
+ _device: WeakPtr<ffi::gbm_device>,
+ _bo_userdata: PhantomData<T>,
+}
+
+impl<T: 'static> fmt::Debug for Surface<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Surface")
+ .field("ptr", &format_args!("{:p}", &self.ffi))
+ .field("device", &format_args!("{:p}", &self._device))
+ .finish()
+ }
+}
+
+/// Errors that may happen when locking the front buffer
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FrontBufferError {
+ /// No free buffers are currently available
+ NoFreeBuffers,
+ /// An unknown error happened
+ Unknown,
+ /// Device was already released
+ Destroyed(DeviceDestroyedError),
+}
+
+impl fmt::Display for FrontBufferError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ FrontBufferError::NoFreeBuffers => write!(f, "No free buffers remaining"),
+ FrontBufferError::Unknown => write!(f, "Unknown error"),
+ FrontBufferError::Destroyed(ref err) => write!(f, "Buffer was destroyed: {}", err),
+ }
+ }
+}
+
+impl error::Error for FrontBufferError {
+ fn cause(&self) -> Option<&dyn error::Error> {
+ match *self {
+ FrontBufferError::Destroyed(ref err) => Some(err),
+ _ => None,
+ }
+ }
+}
+
+impl<T: 'static> Surface<T> {
+ /// Return whether or not a surface has free (non-locked) buffers
+ ///
+ /// Before starting a new frame, the surface must have a buffer
+ /// available for rendering. Initially, a GBM surface will have a free
+ /// buffer, but after one or more buffers
+ /// [have been locked](Self::lock_front_buffer()),
+ /// the application must check for a free buffer before rendering.
+ pub fn has_free_buffers(&self) -> bool {
+ let device = self._device.upgrade();
+ if device.is_some() {
+ unsafe { ffi::gbm_surface_has_free_buffers(*self.ffi) != 0 }
+ } else {
+ false
+ }
+ }
+
+ /// Lock the surface's current front buffer
+ ///
+ /// Locks rendering to the surface's current front buffer and returns
+ /// a handle to the underlying [`BufferObject`].
+ ///
+ /// If an error occurs a [`FrontBufferError`] is returned.
+ ///
+ /// # Safety
+ /// This function must be called exactly once after calling
+ /// `eglSwapBuffers`. Calling it before any `eglSwapBuffers` has happened
+ /// on the surface or two or more times after `eglSwapBuffers` is an
+ /// error and may cause undefined behavior.
+ pub unsafe fn lock_front_buffer(&self) -> Result<BufferObject<T>, FrontBufferError> {
+ let device = self._device.upgrade();
+ if device.is_some() {
+ if ffi::gbm_surface_has_free_buffers(*self.ffi) != 0 {
+ let buffer_ptr = ffi::gbm_surface_lock_front_buffer(*self.ffi);
+ if !buffer_ptr.is_null() {
+ let surface_ptr = self.ffi.downgrade();
+ let buffer = BufferObject {
+ ffi: Ptr::new(buffer_ptr, move |ptr| {
+ if let Some(surface) = surface_ptr.upgrade() {
+ ffi::gbm_surface_release_buffer(*surface, ptr);
+ }
+ }),
+ _device: self._device.clone(),
+ _userdata: std::marker::PhantomData,
+ };
+ Ok(buffer)
+ } else {
+ Err(FrontBufferError::Unknown)
+ }
+ } else {
+ Err(FrontBufferError::NoFreeBuffers)
+ }
+ } else {
+ Err(FrontBufferError::Destroyed(DeviceDestroyedError))
+ }
+ }
+
+ pub(crate) unsafe fn new(
+ ffi: *mut ffi::gbm_surface,
+ device: WeakPtr<ffi::gbm_device>,
+ ) -> Surface<T> {
+ Surface {
+ ffi: Ptr::new(ffi, |ptr| ffi::gbm_surface_destroy(ptr)),
+ _device: device,
+ _bo_userdata: PhantomData,
+ }
+ }
+}
+
+impl<T: 'static> AsRaw<ffi::gbm_surface> for Surface<T> {
+ fn as_raw(&self) -> *const ffi::gbm_surface {
+ *self.ffi
+ }
+}