use crate::clientmem::ClientMem;
use crate::clientmem::ClientMemError;
use crate::format::formats;
use crate::gfx_api::GfxError;
use crate::ifs::wl_buffer::WlBuffer;
use crate::ifs::wl_buffer::WlBufferError;
use crate::ifs::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1;
use crate::leaks::Tracker;
use crate::object::Version;
use crate::utils::bhash::BHashMap;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::hash_map_ext::HashMapExt;
use crate::video::dmabuf::DmaBuf;
use crate::video::dmabuf::DmaBufPlane;
use crate::video::dmabuf::MAX_PLANES;
use crate::video::dmabuf::PlaneVec;
use crate::wire::WlBufferId;
use crate::wire::ZwpLinuxBufferParamsV1Id;
use crate::wire::zwp_linux_buffer_params_v1::*;
use jay_proc::Object;
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;
use thiserror::Error;
use uapi::c;
#[expect(unused)]
const Y_INVERT: u32 = 1;
#[expect(unused)]
const INTERLACED: u32 = 2;
#[expect(unused)]
const BOTTOM_FIRST: u32 = 4;
const MAX_PLANE: u32 = MAX_PLANES as u32 - 1;
#[derive(Object)]
pub struct ZwpLinuxBufferParamsV1 {
id: ZwpLinuxBufferParamsV1Id,
version: Version,
parent: Rc<ZwpLinuxDmabufV1>,
planes: RefCell<BHashMap<u32, Add>>,
used: Cell<bool>,
modifier: Cell<Option<u64>>,
sampling_device: Cell<Option<c::dev_t>>,
pub tracker: Tracker<Self>,
}
impl ZwpLinuxBufferParamsV1 {
pub fn new(id: ZwpLinuxBufferParamsV1Id, parent: &Rc<ZwpLinuxDmabufV1>) -> Self {
Self {
id,
version: parent.version,
parent: parent.clone(),
planes: RefCell::new(Default::default()),
used: Cell::new(false),
modifier: Cell::new(None),
sampling_device: Default::default(),
tracker: Default::default(),
}
}
fn send_created(&self, buffer_id: WlBufferId) {
self.parent.client.event(Created {
self_id: self.id,
buffer: buffer_id,
});
}
fn send_failed(&self) {
self.parent.client.event(Failed { self_id: self.id });
}
fn do_create(
&self,
buffer_id: Option<WlBufferId>,
width: i32,
height: i32,
format: u32,
_flags: u32,
) -> Result<WlBufferId, ZwpLinuxBufferParamsV1Error> {
let state = &self.parent.client.state;
let format = match formats().get(&format) {
Some(f) => *f,
None => return Err(ZwpLinuxBufferParamsV1Error::InvalidFormat(format)),
};
let Some(modifier) = self.modifier.get() else {
return Err(ZwpLinuxBufferParamsV1Error::NoPlanes);
};
let mut planes: Vec<_> = self.planes.borrow_mut().drain_values().collect();
planes.sort_by_key(|a| a.plane_idx);
let mut dplanes = PlaneVec::new();
for (i, p) in planes.into_iter().enumerate() {
if p.plane_idx as usize != i {
return Err(ZwpLinuxBufferParamsV1Error::MissingPlane(i));
}
dplanes.push(DmaBufPlane {
offset: p.offset,
stride: p.stride,
fd: p.fd,
});
}
let dmabuf = DmaBuf::new(&state.dma_buf_ids, width, height, format, modifier, dplanes);
let get_id = || match buffer_id {
None => self.parent.client.new_id(self),
Some(i) => i,
};
let buffer = if let Some(ctx) = state.render_ctx.get()
&& let Some(format) = ctx.formats().get(&dmabuf.format.drm)
&& format.supports_shm
&& let Some(size) = dmabuf.udmabuf_size()
{
let p = &dmabuf.planes[0];
let client_mem = ClientMem::new(
&p.fd,
size,
true,
Some(&self.parent.client),
Some(&state.cpu_worker),
true,
)
.map(Rc::new)
.map_err(ZwpLinuxBufferParamsV1Error::CreateClientMem)?;
WlBuffer::new_shm(
get_id(),
&self.parent.client,
p.offset as usize,
dmabuf.width,
dmabuf.height,
Some(dmabuf.clone()),
p.stride as _,
format.format,
&client_mem,
Some((&p.fd, size)),
)?
} else {
WlBuffer::new_dmabuf(
get_id(),
&self.parent.client,
format,
dmabuf,
self.sampling_device.get(),
)
};
track!(self.parent.client, buffer);
if buffer_id.is_some() {
self.parent.client.add_client_obj(&buffer);
} else {
self.parent.client.add_server_obj(&buffer);
}
Ok(buffer.id)
}
}
impl ZwpLinuxBufferParamsV1RequestHandler for ZwpLinuxBufferParamsV1 {
type Error = ZwpLinuxBufferParamsV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.parent.client.remove_obj(self);
Ok(())
}
fn add(&self, req: Add, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let modifier = req.modifier;
match self.modifier.get() {
Some(m) if m != modifier => {
return Err(ZwpLinuxBufferParamsV1Error::MixedModifiers(modifier, m));
}
_ => self.modifier.set(Some(modifier)),
}
let plane = req.plane_idx;
if plane > MAX_PLANE {
return Err(ZwpLinuxBufferParamsV1Error::MaxPlane);
}
if self.planes.borrow_mut().insert(plane, req).is_some() {
return Err(ZwpLinuxBufferParamsV1Error::AlreadySet(plane));
}
Ok(())
}
fn create(&self, req: Create, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.used.replace(true) {
return Err(ZwpLinuxBufferParamsV1Error::AlreadyUsed);
}
match self.do_create(None, req.width, req.height, req.format, req.flags) {
Ok(id) => {
self.send_created(id);
}
Err(e) => {
log::warn!("Could not create a dmabuf buffer: {}", ErrorFmt(e));
self.send_failed();
}
}
Ok(())
}
fn create_immed(
&self,
req: CreateImmed,
_slf: &Rc<Self>,
) -> Result<(), ZwpLinuxBufferParamsV1Error> {
if self.used.replace(true) {
return Err(ZwpLinuxBufferParamsV1Error::AlreadyUsed);
}
self.do_create(
Some(req.buffer_id),
req.width,
req.height,
req.format,
req.flags,
)?;
Ok(())
}
fn set_sampling_device(
&self,
req: SetSamplingDevice,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.sampling_device.set(Some(req.device));
Ok(())
}
}
#[derive(Debug, Error)]
pub enum ZwpLinuxBufferParamsV1Error {
#[error("The params object has already been used")]
AlreadyUsed,
#[error("A buffer can contain at most 4 planes")]
MaxPlane,
#[error("Tried to add a plane with modifier {0} that differs from a previous modifier {1}")]
MixedModifiers(u64, u64),
#[error("The plane {0} was already set")]
AlreadySet(u32),
#[error("The format {0} is not supported")]
InvalidFormat(u32),
#[error("No planes were added")]
NoPlanes,
#[error("Plane {0} was not set")]
MissingPlane(usize),
#[error("Could not import the buffer")]
ImportError(#[from] GfxError),
#[error("Could not create ClientMem")]
CreateClientMem(#[source] ClientMemError),
#[error(transparent)]
WlBufferError(Box<WlBufferError>),
}
efrom!(ZwpLinuxBufferParamsV1Error, WlBufferError);