gbm-0.18.0/.cargo_vcs_info.json0000644000000001360000000000100116670ustar { "git": { "sha1": "2b4047f8e5ab4d050d4ed88f5ee2515069478fac" }, "path_in_vcs": "" }gbm-0.18.0/CHANGELOG.md000064400000000000000000000051371046102023000122760ustar 00000000000000## 0.18.0 - Removed unnecessary generic from `BufferObject::map`/`BufferObject::map_mut` ## 0.17.0 - `Surface`/`BufferObject` now take strong references to the underlying `gbm_device` - Fixes a segfault when dropping the `Device` before it's derived objects - Removes `DeviceDestroyedError`, `InvalidDeviceError` and `FdError` - Removes the `device`-argument from `BufferObject::map` and `BufferObject::map_mut` ## 0.16.1 - `Device`/`Surface`/`BufferObject` are now `Sync` - `Device::format_modifier_plane_count` was added as a wrapper for `gbm_device_get_format_modifier_plane_count` ## 0.16.0 - Update drm-rs to 0.14 - Fix `use_bindgen` feature ## 0.15.0 - Update drm-rs to 0.12 - Update bitflags to v2 - Fix for `Surface::lock_front_buffer` failing on some drivers ## 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 gbm-0.18.0/Cargo.toml0000644000000035040000000000100076670ustar # 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.18.0" authors = ["Victoria Brekenfeld "] build = false exclude = [ ".gitignore", ".travis.yml", ".rustfmt.toml", ".github", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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" [lib] name = "gbm" path = "src/lib.rs" [dependencies.bitflags] version = "2" [dependencies.drm] version = "0.14.0" optional = true [dependencies.drm-fourcc] version = "2.2" [dependencies.gbm-sys] version = "0.4.0" [dependencies.libc] version = "0.2" [dependencies.serde] version = "1.0.103" features = ["derive"] optional = true [dependencies.wayland-backend] version = "0.3" features = ["server_system"] optional = true [dependencies.wayland-server] version = "0.31" optional = true [dev-dependencies.drm] version = "0.14.0" [features] default = [ "import-wayland", "import-egl", "drm-support", ] drm-support = ["drm"] import-egl = [] import-wayland = [ "wayland-server", "wayland-backend", ] serde = [ "dep:serde", "bitflags/serde", ] use_bindgen = ["gbm-sys/use_bindgen"] gbm-0.18.0/Cargo.toml.orig000064400000000000000000000025431046102023000133520ustar 00000000000000[package] name = "gbm" description = "libgbm bindings for rust" license = "MIT" documentation = "https://docs.rs/gbm" repository = "https://github.com/Smithay/gbm.rs" version = "0.18.0" keywords = ["wayland", "gbm", "drm", "bindings"] categories = ["external-ffi-bindings"] authors = ["Victoria Brekenfeld "] exclude = [".gitignore", ".travis.yml", ".rustfmt.toml", ".github"] edition = "2021" [dependencies] libc = "0.2" bitflags = "2" drm-fourcc = "2.2" [dependencies.gbm-sys] version = "0.4.0" path = "./gbm-sys" [dependencies.drm] version = "0.14.0" optional = true [dependencies.wayland-server] version = "0.31" optional = true [dependencies.wayland-backend] version = "0.3" features = ["server_system"] optional = true [dependencies.serde] # Minimal serde version that can compile serde-annotated bitflags v2. # Workaround that allows -Z minimal-versions build to succeed despite bitflags # crate not declaring minimum versions of its dependencies precisely enough. version = "1.0.103" features = ["derive"] optional = true [dev-dependencies.drm] version = "0.14.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"] serde = ["dep:serde", "bitflags/serde"] [workspace] members = [ "gbm-sys" ] gbm-0.18.0/LICENSE000064400000000000000000000020621046102023000114640ustar 00000000000000MIT 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. gbm-0.18.0/README.md000064400000000000000000000027371046102023000117470ustar 00000000000000## 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.18.0" ``` ## Example ```rust 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(); ``` gbm-0.18.0/src/buffer_object.rs000064400000000000000000000437621046102023000144270ustar 00000000000000#![allow(clippy::unnecessary_cast)] use crate::{AsRaw, Format, Modifier, Ptr}; #[cfg(feature = "drm-support")] use drm::buffer::{Buffer as DrmBuffer, Handle, PlanarBuffer as DrmPlanarBuffer}; use std::os::unix::io::{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 { // Declare `ffi` first so it is dropped before `_device` pub(crate) ffi: Ptr, pub(crate) _device: Ptr, pub(crate) _userdata: PhantomData, } impl fmt::Debug for BufferObject { 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()) .field("height", &self.height()) .field("offsets", &self.offsets()) .field("stride", &self.stride()) .field("format", &self.format()) .field("modifier", &self.modifier()) .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 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))] #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] 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), Mut(&'a mut BufferObject), } /// 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; fn deref(&self) -> &BufferObject { 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 { 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(_: *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 BufferObject { /// Get the width of the buffer object pub fn width(&self) -> u32 { unsafe { ffi::gbm_bo_get_width(*self.ffi) } } /// Get the height of the buffer object pub fn height(&self) -> u32 { unsafe { ffi::gbm_bo_get_height(*self.ffi) } } /// Get the stride of the buffer object pub fn stride(&self) -> u32 { unsafe { ffi::gbm_bo_get_stride(*self.ffi) } } /// Get the stride of the buffer object pub fn stride_for_plane(&self, plane: i32) -> u32 { unsafe { ffi::gbm_bo_get_stride_for_plane(*self.ffi, plane) } } /// Get the format of the buffer object pub fn format(&self) -> Format { 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) -> u32 { unsafe { ffi::gbm_bo_get_bpp(*self.ffi) } } /// Get the offset for a plane of the buffer object pub fn offset(&self, plane: i32) -> u32 { unsafe { ffi::gbm_bo_get_offset(*self.ffi, plane) } } /// Get the plane count of the buffer object pub fn plane_count(&self) -> u32 { unsafe { ffi::gbm_bo_get_plane_count(*self.ffi) as u32 } } /// Get the modifier of the buffer object pub fn modifier(&self) -> Modifier { 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 { unsafe { let fd = ffi::gbm_bo_get_fd(*self.ffi); if fd == -1 { return Err(InvalidFdError); } Ok(OwnedFd::from_raw_fd(fd)) } } /// Get the file descriptor of the gbm device of this buffer object pub fn device_fd(&self) -> BorrowedFd { unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*self._device)) } } /// 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) -> BufferObjectHandle { 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 { unsafe { let fd = ffi::gbm_bo_get_fd_for_plane(*self.ffi, plane); if fd == -1 { return Err(InvalidFdError); } 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) -> BufferObjectHandle { 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, F, S>(&'a self, x: u32, y: u32, width: u32, height: u32, f: F) -> IoResult where F: FnOnce(&MappedBufferObject<'a, T>) -> S, { 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() { Err(IoError::last_os_error()) } else { 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, F, S>( &'a mut self, x: u32, y: u32, width: u32, height: u32, f: F, ) -> IoResult where F: FnOnce(&mut MappedBufferObject<'a, T>) -> S, { 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() { Err(IoError::last_os_error()) } else { 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]) -> IoResult<()> { let result = unsafe { ffi::gbm_bo_write(*self.ffi, buffer.as_ptr() as *const _, buffer.len() as _) }; if result != 0 { Err(IoError::last_os_error()) } else { Ok(()) } } /// Sets the userdata of the buffer object. /// /// If previously userdata was set, it is returned. pub fn set_userdata(&mut self, userdata: T) -> Option { 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::), ); } old } /// Clears the set userdata of the buffer object. pub fn clear_userdata(&mut self) { let _ = self.take_userdata(); } /// Returns a reference to set userdata, if any. pub fn userdata(&self) -> Option<&T> { let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) }; if raw.is_null() { None } else { unsafe { Some(&*(raw as *mut T)) } } } /// Returns a mutable reference to set userdata, if any. pub fn userdata_mut(&mut self) -> Option<&mut T> { let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) }; if raw.is_null() { None } else { unsafe { 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) -> Option { let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) }; if raw.is_null() { None } else { unsafe { let boxed = Box::from_raw(raw as *mut T); ffi::gbm_bo_set_user_data(*self.ffi, ptr::null_mut(), None); Some(*boxed) } } } pub(crate) unsafe fn new( ffi: *mut ffi::gbm_bo, device: Ptr, ) -> BufferObject { BufferObject { ffi: Ptr::::new(ffi, |ptr| ffi::gbm_bo_destroy(ptr)), _device: device, _userdata: PhantomData, } } fn offsets(&self) -> [u32; 4] { let num = self.plane_count(); [ BufferObject::::offset(self, 0), if num > 1 { BufferObject::::offset(self, 1) } else { 0 }, if num > 2 { BufferObject::::offset(self, 2) } else { 0 }, if num > 3 { BufferObject::::offset(self, 3) } else { 0 }, ] } } impl AsRaw for BufferObject { fn as_raw(&self) -> *const ffi::gbm_bo { *self.ffi } } #[cfg(feature = "drm-support")] impl DrmBuffer for BufferObject { fn size(&self) -> (u32, u32) { (self.width(), self.height()) } fn format(&self) -> Format { BufferObject::::format(self) } fn pitch(&self) -> u32 { self.stride() } fn handle(&self) -> Handle { use std::num::NonZeroU32; unsafe { Handle::from(NonZeroU32::new_unchecked(self.handle().u32_)) } } } #[cfg(feature = "drm-support")] impl DrmPlanarBuffer for BufferObject { fn size(&self) -> (u32, u32) { (self.width(), self.height()) } fn format(&self) -> Format { BufferObject::::format(self) } fn modifier(&self) -> Option { Some(BufferObject::::modifier(self)) } fn pitches(&self) -> [u32; 4] { let num = self.plane_count(); [ BufferObject::::stride_for_plane(self, 0), if num > 1 { BufferObject::::stride_for_plane(self, 1) } else { 0 }, if num > 2 { BufferObject::::stride_for_plane(self, 2) } else { 0 }, if num > 3 { BufferObject::::stride_for_plane(self, 3) } else { 0 }, ] } fn handles(&self) -> [Option; 4] { use std::num::NonZeroU32; let num = self.plane_count(); [ Some(unsafe { Handle::from(NonZeroU32::new_unchecked( BufferObject::::handle_for_plane(self, 0).u32_, )) }), if num > 1 { Some(unsafe { Handle::from(NonZeroU32::new_unchecked( BufferObject::::handle_for_plane(self, 1).u32_, )) }) } else { None }, if num > 2 { Some(unsafe { Handle::from(NonZeroU32::new_unchecked( BufferObject::::handle_for_plane(self, 2).u32_, )) }) } else { None }, if num > 3 { Some(unsafe { Handle::from(NonZeroU32::new_unchecked( BufferObject::::handle_for_plane(self, 3).u32_, )) }) } else { None }, ] } fn offsets(&self) -> [u32; 4] { self.offsets() } } /// 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 {} gbm-0.18.0/src/device.rs000064400000000000000000000315411046102023000130570ustar 00000000000000use crate::{AsRaw, BufferObject, BufferObjectFlags, Format, Modifier, Ptr, Surface}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; 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 { // Declare `ffi` first so it is dropped before `fd` ffi: Ptr, fd: T, } impl fmt::Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Device") .field("ptr", &format_args!("{:p}", &self.ffi)) .finish() } } impl Clone for Device { fn clone(&self) -> Device { Device { fd: self.fd.clone(), ffi: self.ffi.clone(), } } } impl AsFd for Device { fn as_fd(&self) -> BorrowedFd { unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*self.ffi)) } } } impl AsRaw for Device { fn as_raw(&self) -> *const ffi::gbm_device { *self.ffi } } impl Deref for Device { type Target = T; fn deref(&self) -> &T { &self.fd } } impl DerefMut for Device { fn deref_mut(&mut self) -> &mut T { &mut self.fd } } impl Device { /// 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> { 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::::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 } } /// Get the required number of planes for a given format and modifier /// /// Some combination (e.g. when using a `Modifier::Invalid`) might not /// have a defined/fixed number of planes. In these cases the function /// might return `Option::None`. pub fn format_modifier_plane_count(&self, format: Format, modifier: Modifier) -> Option { unsafe { ffi::gbm_device_get_format_modifier_plane_count( *self.ffi, format as u32, modifier.into(), ) .try_into() .ok() } } /// Allocate a new surface object pub fn create_surface( &self, width: u32, height: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult> { 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.clone()) }) } } /// Allocate a new surface object with explicit modifiers pub fn create_surface_with_modifiers( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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.clone()) }) } } /// Allocate a new surface object with explicit modifiers and flags pub fn create_surface_with_modifiers2( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, usage: BufferObjectFlags, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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.clone()) }) } } /// Allocate a buffer object for the given dimensions pub fn create_buffer_object( &self, width: u32, height: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult> { 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.clone()) }) } } /// Allocate a buffer object for the given dimensions with explicit modifiers pub fn create_buffer_object_with_modifiers( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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.clone()) }) } } /// Allocate a buffer object for the given dimensions with explicit modifiers and flags pub fn create_buffer_object_with_modifiers2( &self, width: u32, height: u32, format: Format, modifiers: impl Iterator, usage: BufferObjectFlags, ) -> IoResult> { let mods = modifiers.map(|m| m.into()).collect::>(); 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.clone()) }) } } /// 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( &self, buffer: &WlBuffer, usage: BufferObjectFlags, ) -> IoResult> { use wayland_server::Resource; let ptr = unsafe { ffi::gbm_bo_import( *self.ffi, ffi::GBM_BO_IMPORT_WL_BUFFER, 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.clone()) }) } } /// 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( &self, buffer: EGLImage, usage: BufferObjectFlags, ) -> IoResult> { let ptr = ffi::gbm_bo_import( *self.ffi, ffi::GBM_BO_IMPORT_EGL_IMAGE, buffer, usage.bits(), ); if ptr.is_null() { Err(IoError::last_os_error()) } else { Ok(BufferObject::new(ptr, self.ffi.clone())) } } /// 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( &self, buffer: BorrowedFd<'_>, width: u32, height: u32, stride: u32, format: Format, usage: BufferObjectFlags, ) -> IoResult> { 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, &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.clone()) }) } } /// 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( &self, len: u32, buffers: [Option>; 4], width: u32, height: u32, format: Format, usage: BufferObjectFlags, strides: [i32; 4], offsets: [i32; 4], modifier: Modifier, ) -> IoResult> { 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, &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.clone()) }) } } } #[cfg(feature = "drm-support")] impl DrmDevice for Device {} #[cfg(feature = "drm-support")] impl DrmControlDevice for Device {} gbm-0.18.0/src/lib.rs000064400000000000000000000126421046102023000123670ustar 00000000000000//! # 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; /// Trait for types that allow to obtain the underlying raw libinput pointer. pub trait AsRaw { /// 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(*mut T, Option>); impl Drop for PtrDrop { fn drop(&mut self) { (self.1.take().unwrap())(self.0); } } #[derive(Clone)] pub(crate) struct Ptr(Arc>); // SAFETY: The types used with Ptr in this crate are all Send and Sync (namely gbm_device, gbm_surface and gbm_bo). // Reference counting is implemented with the thread-safe atomic `Arc`-wrapper. // The type is private and can thus not be used unsoundly by other crates. unsafe impl Send for Ptr {} unsafe impl Sync for Ptr {} impl Ptr { fn new(ptr: *mut T, destructor: F) -> Ptr { Ptr(Arc::new(PtrDrop(ptr, Some(Box::new(destructor))))) } } impl std::ops::Deref for Ptr { type Target = *mut T; fn deref(&self) -> &Self::Target { &(self.0).0 } } impl fmt::Pointer for Ptr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fmt::Pointer::fmt(&self.0 .0, f) } } #[cfg(test)] mod test { use std::os::unix::io::OwnedFd; fn is_send() {} fn is_sync() {} #[test] fn device_is_send() { is_send::>(); is_send::>(); } #[test] fn device_is_sync() { is_sync::>(); is_sync::>(); } #[test] fn surface_is_send() { is_send::>(); is_send::>(); } #[test] fn surface_is_sync() { is_sync::>(); is_sync::>(); } #[test] fn unmapped_bo_is_send() { is_send::>(); } #[test] fn unmapped_bo_is_sync() { is_sync::>(); } } gbm-0.18.0/src/surface.rs000064400000000000000000000060361046102023000132510ustar 00000000000000use crate::{AsRaw, BufferObject, Ptr}; use std::error; use std::fmt; use std::marker::PhantomData; /// A GBM rendering surface pub struct Surface { // Declare `ffi` first so it is dropped before `_device` ffi: Ptr, _device: Ptr, _bo_userdata: PhantomData, } impl fmt::Debug for Surface { 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 struct FrontBufferError; impl fmt::Display for FrontBufferError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Unknown error") } } impl error::Error for FrontBufferError {} impl Surface { /// 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 { unsafe { ffi::gbm_surface_has_free_buffers(*self.ffi) != 0 } } /// 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, FrontBufferError> { let buffer_ptr = ffi::gbm_surface_lock_front_buffer(*self.ffi); if !buffer_ptr.is_null() { let surface_ptr = self.ffi.clone(); let buffer = BufferObject { ffi: Ptr::new(buffer_ptr, move |ptr| { ffi::gbm_surface_release_buffer(*surface_ptr, ptr); }), _device: self._device.clone(), _userdata: std::marker::PhantomData, }; Ok(buffer) } else { Err(FrontBufferError) } } pub(crate) unsafe fn new( ffi: *mut ffi::gbm_surface, device: Ptr, ) -> Surface { Surface { ffi: Ptr::new(ffi, |ptr| ffi::gbm_surface_destroy(ptr)), _device: device, _bo_userdata: PhantomData, } } } impl AsRaw for Surface { fn as_raw(&self) -> *const ffi::gbm_surface { *self.ffi } }