Wayland dmabuf handler
This commit is contained in:
parent
78874a8363
commit
e11c0278dd
|
@ -0,0 +1,377 @@
|
||||||
|
use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc};
|
||||||
|
|
||||||
|
pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags;
|
||||||
|
use wayland_protocols::unstable::linux_dmabuf::v1::server::{
|
||||||
|
zwp_linux_buffer_params_v1::{
|
||||||
|
Error as ParamError, RequestHandler as ParamRequestHandler, ZwpLinuxBufferParamsV1 as BufferParams,
|
||||||
|
},
|
||||||
|
zwp_linux_dmabuf_v1,
|
||||||
|
};
|
||||||
|
use wayland_server::{protocol::wl_buffer, Display, Global, NewResource};
|
||||||
|
|
||||||
|
/// Representation of a Dmabuf format, as advertized to the client
|
||||||
|
pub struct Format {
|
||||||
|
/// The format identifier
|
||||||
|
pub format: u32,
|
||||||
|
/// High part of the supported modifiers
|
||||||
|
pub modifier_hi: u32,
|
||||||
|
/// Low part of the supported modifiers
|
||||||
|
pub modifier_lo: u32,
|
||||||
|
/// Number of planes used by this format
|
||||||
|
pub plane_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A plane send by the client
|
||||||
|
pub struct Plane {
|
||||||
|
/// The file descriptor
|
||||||
|
pub fd: RawFd,
|
||||||
|
/// The plane index
|
||||||
|
pub plane_idx: u32,
|
||||||
|
/// Offset from the start of the Fd
|
||||||
|
pub offset: u32,
|
||||||
|
/// Stride for this plane
|
||||||
|
pub stride: u32,
|
||||||
|
/// High part of the modifiers for this plane
|
||||||
|
pub modifier_hi: u32,
|
||||||
|
/// Low part of the modifiers for this plane
|
||||||
|
pub modifier_lo: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The complete information provided by the client to create a dmabuf buffer
|
||||||
|
pub struct BufferInfo {
|
||||||
|
/// The submitted planes
|
||||||
|
pub planes: Vec<Plane>,
|
||||||
|
/// The width of this buffer
|
||||||
|
pub width: i32,
|
||||||
|
/// The height of this buffer
|
||||||
|
pub height: i32,
|
||||||
|
/// The format in use
|
||||||
|
pub format: u32,
|
||||||
|
/// The flags applied to it
|
||||||
|
///
|
||||||
|
/// This is a bitflag, to be compared with the `Flags` enum reexported by this module.
|
||||||
|
pub flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handler trait for dmabuf validation
|
||||||
|
///
|
||||||
|
/// You need to provide an implementation of this trait that will validate the parameters provided by the
|
||||||
|
/// client and import it as a dmabuf.
|
||||||
|
pub trait DmabufHandler {
|
||||||
|
/// The data of a successfully imported dmabuf.
|
||||||
|
///
|
||||||
|
/// This will be stored as the `user_data` of the `WlBuffer` associated with this dmabuf.
|
||||||
|
type BufferData: 'static;
|
||||||
|
/// Validate a dmabuf
|
||||||
|
///
|
||||||
|
/// From the information provided by the client, you need to validate and/or import the buffer.
|
||||||
|
///
|
||||||
|
/// You can then store any information your compositor will need to handle it later, when the client has
|
||||||
|
/// submitted the buffer by returning `Ok(BufferData)` where `BufferData` is the associated type of this,
|
||||||
|
/// trait, a type of your choosing.
|
||||||
|
///
|
||||||
|
/// If the buffer could not be imported, whatever the reason, return `Err(())`.
|
||||||
|
fn validate_dmabuf(&mut self, info: BufferInfo) -> Result<Self::BufferData, ()>;
|
||||||
|
/// Create a buffer from validated buffer data.
|
||||||
|
///
|
||||||
|
/// This method is pre-implemented for you by storing the provided `BufferData` as the `user_data` of the
|
||||||
|
/// provided `WlBuffer`. By default it assumes that your `BufferData` is not threadsafe.
|
||||||
|
///
|
||||||
|
/// You can override it if you need your `BufferData` to be threadsafe, or which to register a destructor
|
||||||
|
/// for the `WlBuffer` for example.
|
||||||
|
fn create_buffer(
|
||||||
|
&mut self,
|
||||||
|
data: Self::BufferData,
|
||||||
|
buffer: NewResource<wl_buffer::WlBuffer>,
|
||||||
|
) -> wl_buffer::WlBuffer {
|
||||||
|
buffer.implement_closure(|_, _| {}, None::<fn(_)>, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a dmabuf global.
|
||||||
|
///
|
||||||
|
/// You need to provide a vector of the supported formats, as well as an implementation fo the `DmabufHandler`
|
||||||
|
/// trait, which will receive the buffer creation requests from the clients.
|
||||||
|
pub fn init_dmabuf_global<H, L>(
|
||||||
|
display: &mut Display,
|
||||||
|
formats: Vec<Format>,
|
||||||
|
handler: H,
|
||||||
|
logger: L,
|
||||||
|
) -> Global<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
H: DmabufHandler + 'static,
|
||||||
|
{
|
||||||
|
let log = crate::slog_or_stdlog(logger);
|
||||||
|
|
||||||
|
let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0);
|
||||||
|
let formats = Rc::new(formats);
|
||||||
|
let handler = Rc::new(RefCell::new(handler));
|
||||||
|
|
||||||
|
display.create_global(3, move |new_dmabuf, version| {
|
||||||
|
let dma_formats = formats.clone();
|
||||||
|
let dma_handler = handler.clone();
|
||||||
|
let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = new_dmabuf.implement_closure(
|
||||||
|
move |req, _| match req {
|
||||||
|
zwp_linux_dmabuf_v1::Request::CreateParams { params_id } => {
|
||||||
|
params_id.implement(
|
||||||
|
ParamsHandler {
|
||||||
|
pending_planes: Vec::new(),
|
||||||
|
max_planes,
|
||||||
|
used: false,
|
||||||
|
formats: dma_formats.clone(),
|
||||||
|
handler: dma_handler.clone(),
|
||||||
|
},
|
||||||
|
None::<fn(_)>,
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
None::<fn(_)>,
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// send the supported formats
|
||||||
|
for f in &*formats {
|
||||||
|
dmabuf.format(f.format);
|
||||||
|
if version >= 3 {
|
||||||
|
dmabuf.modifier(f.format, f.modifier_hi, f.modifier_lo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParamsHandler<H: DmabufHandler> {
|
||||||
|
pending_planes: Vec<Plane>,
|
||||||
|
max_planes: u32,
|
||||||
|
used: bool,
|
||||||
|
formats: Rc<Vec<Format>>,
|
||||||
|
handler: Rc<RefCell<H>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
|
||||||
|
fn add(
|
||||||
|
&mut self,
|
||||||
|
params: BufferParams,
|
||||||
|
fd: RawFd,
|
||||||
|
plane_idx: u32,
|
||||||
|
offset: u32,
|
||||||
|
stride: u32,
|
||||||
|
modifier_hi: u32,
|
||||||
|
modifier_lo: u32,
|
||||||
|
) {
|
||||||
|
// protocol checks:
|
||||||
|
// Cannot reuse a params:
|
||||||
|
if self.used {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::AlreadyUsed as u32,
|
||||||
|
"This buffer_params has already been used to create a buffer.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// plane_idx is not too large
|
||||||
|
if plane_idx >= self.max_planes {
|
||||||
|
// plane_idx starts at 0
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::PlaneIdx as u32,
|
||||||
|
format!("Plane index {} is out of bounds.", plane_idx),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// plane_idx has already been set
|
||||||
|
if self.pending_planes.iter().any(|d| d.plane_idx == plane_idx) {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::PlaneSet as u32,
|
||||||
|
format!("Plane index {} is already set.", plane_idx),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// all checks passed, store the plane
|
||||||
|
self.pending_planes.push(Plane {
|
||||||
|
fd,
|
||||||
|
plane_idx,
|
||||||
|
offset,
|
||||||
|
stride,
|
||||||
|
modifier_hi,
|
||||||
|
modifier_lo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(&mut self, params: BufferParams, width: i32, height: i32, format: u32, flags: u32) {
|
||||||
|
// Cannot reuse a params:
|
||||||
|
if self.used {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::AlreadyUsed as u32,
|
||||||
|
"This buffer_params has already been used to create a buffer.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.used = true;
|
||||||
|
if !buffer_basic_checks(
|
||||||
|
&self.formats,
|
||||||
|
&self.pending_planes,
|
||||||
|
¶ms,
|
||||||
|
format,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let info = BufferInfo {
|
||||||
|
planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
flags,
|
||||||
|
};
|
||||||
|
let mut handler = self.handler.borrow_mut();
|
||||||
|
if let Ok(data) = handler.validate_dmabuf(info) {
|
||||||
|
if let Some(buffer) = params
|
||||||
|
.as_ref()
|
||||||
|
.client()
|
||||||
|
.and_then(|c| c.create_resource::<wl_buffer::WlBuffer>(1))
|
||||||
|
{
|
||||||
|
let buffer = handler.create_buffer(data, buffer);
|
||||||
|
params.created(&buffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_immed(
|
||||||
|
&mut self,
|
||||||
|
params: BufferParams,
|
||||||
|
buffer_id: NewResource<wl_buffer::WlBuffer>,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
format: u32,
|
||||||
|
flags: u32,
|
||||||
|
) {
|
||||||
|
// Cannot reuse a params:
|
||||||
|
if self.used {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::AlreadyUsed as u32,
|
||||||
|
"This buffer_params has already been used to create a buffer.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.used = true;
|
||||||
|
if !buffer_basic_checks(
|
||||||
|
&self.formats,
|
||||||
|
&self.pending_planes,
|
||||||
|
¶ms,
|
||||||
|
format,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let info = BufferInfo {
|
||||||
|
planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
flags,
|
||||||
|
};
|
||||||
|
let mut handler = self.handler.borrow_mut();
|
||||||
|
if let Ok(data) = handler.validate_dmabuf(info) {
|
||||||
|
handler.create_buffer(data, buffer_id);
|
||||||
|
} else {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::InvalidWlBuffer as u32,
|
||||||
|
"create_immed resulted in an invalid buffer.".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_basic_checks(
|
||||||
|
formats: &[Format],
|
||||||
|
pending_planes: &[Plane],
|
||||||
|
params: &BufferParams,
|
||||||
|
format: u32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
) -> bool {
|
||||||
|
// protocol_checks:
|
||||||
|
// This must be a known format
|
||||||
|
let format = match formats.iter().find(|f| f.format == format) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::InvalidFormat as u32,
|
||||||
|
format!("Format {:x} is not supported.", format),
|
||||||
|
);
|
||||||
|
return false;;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// The number of planes set must match what the format expects
|
||||||
|
let max_plane_set = pending_planes.iter().map(|d| d.plane_idx + 1).max().unwrap_or(0);
|
||||||
|
if max_plane_set != format.plane_count || pending_planes.len() < format.plane_count as usize {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::Incomplete as u32,
|
||||||
|
format!(
|
||||||
|
"Format {:x} requires {} planes but got {}.",
|
||||||
|
format.format, format.plane_count, max_plane_set
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Width and height must be positivie
|
||||||
|
if width < 1 || height < 1 {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::InvalidDimensions as u32,
|
||||||
|
format!("Dimensions ({},{}) are not valid.", width, height),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// check the size of each plane buffer
|
||||||
|
for plane in pending_planes {
|
||||||
|
// check size for overflow
|
||||||
|
let end = match plane
|
||||||
|
.stride
|
||||||
|
.checked_mul(height as u32)
|
||||||
|
.and_then(|o| o.checked_add(plane.offset))
|
||||||
|
{
|
||||||
|
None => {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::OutOfBounds as u32,
|
||||||
|
format!("Size overflow for plane {}.", plane.plane_idx),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Some(e) => e,
|
||||||
|
};
|
||||||
|
if let Ok(size) = ::nix::unistd::lseek(plane.fd, 0, ::nix::unistd::Whence::SeekEnd) {
|
||||||
|
if plane.offset as i64 > size {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::OutOfBounds as u32,
|
||||||
|
format!("Invalid offset {} for plane {}.", plane.offset, plane.plane_idx),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (plane.offset + plane.stride) as i64 > size {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::OutOfBounds as u32,
|
||||||
|
format!("Invalid stride {} for plane {}.", plane.stride, plane.plane_idx),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Planes > 0 can be subsampled, in which case 'size' will be smaller
|
||||||
|
// than expected.
|
||||||
|
if plane.plane_idx == 0 && end as i64 > size {
|
||||||
|
params.as_ref().post_error(
|
||||||
|
ParamError::OutOfBounds as u32,
|
||||||
|
format!(
|
||||||
|
"Invalid stride ({}) or height ({}) for plane {}.",
|
||||||
|
plane.stride, height, plane.plane_idx
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod data_device;
|
pub mod data_device;
|
||||||
|
pub mod dmabuf;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
pub mod seat;
|
pub mod seat;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
|
|
Loading…
Reference in New Issue