1use crate::{error::GlobalError, globals::GlobalData, registry::GlobalProxy};
2use memmap2::{Mmap, MmapOptions};
3use std::{fmt, mem, os::unix::io::BorrowedFd, slice, sync::Mutex};
4use wayland_client::{
5 globals::GlobalList,
6 protocol::{wl_buffer, wl_surface},
7 Connection, Dispatch, Proxy, QueueHandle, WEnum,
8};
9use wayland_protocols::wp::linux_dmabuf::zv1::client::{
10 zwp_linux_buffer_params_v1,
11 zwp_linux_dmabuf_feedback_v1::{self, TrancheFlags},
12 zwp_linux_dmabuf_v1,
13};
14
15#[cfg(target_os = "freebsd")]
17type dev_t = u64;
18#[cfg(not(target_os = "freebsd"))]
19use libc::dev_t;
20
21#[derive(Debug)]
23pub struct DmabufFeedbackTranche {
24 pub device: dev_t,
27 pub flags: WEnum<TrancheFlags>,
29 pub formats: Vec<u16>,
31}
32
33impl Default for DmabufFeedbackTranche {
34 fn default() -> DmabufFeedbackTranche {
35 DmabufFeedbackTranche {
36 device: 0,
37 flags: WEnum::Value(TrancheFlags::empty()),
38 formats: Vec::new(),
39 }
40 }
41}
42
43#[repr(C)]
46pub struct DmabufFormat {
47 pub format: u32,
49 _padding: u32,
50 pub modifier: u64,
52}
53
54impl fmt::Debug for DmabufFormat {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.debug_struct("DmabufFormat")
57 .field("format", &self.format)
58 .field("modifier", &self.modifier)
59 .finish()
60 }
61}
62
63#[derive(Default)]
65pub struct DmabufFeedback {
66 format_table: Option<(Mmap, usize)>,
67 main_device: dev_t,
68 tranches: Vec<DmabufFeedbackTranche>,
69}
70
71impl fmt::Debug for DmabufFeedback {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.debug_struct("DmabufFeedback")
74 .field("format_table", &self.format_table())
75 .field("main_device", &self.main_device)
76 .field("tranches", &self.tranches)
77 .finish()
78 }
79}
80
81impl DmabufFeedback {
82 pub fn format_table(&self) -> &[DmabufFormat] {
84 self.format_table.as_ref().map_or(&[], |(mmap, len)| unsafe {
85 slice::from_raw_parts(mmap.as_ptr() as *const DmabufFormat, *len)
86 })
87 }
88
89 pub fn main_device(&self) -> dev_t {
91 self.main_device
92 }
93
94 pub fn tranches(&self) -> &[DmabufFeedbackTranche] {
96 &self.tranches
97 }
98}
99
100#[doc(hidden)]
101#[derive(Debug, Default)]
102pub struct DmabufFeedbackData {
103 pending: Mutex<DmabufFeedback>,
104 pending_tranche: Mutex<DmabufFeedbackTranche>,
105}
106
107#[doc(hidden)]
108#[derive(Debug)]
109pub struct DmaBufferData;
110
111#[derive(Debug)]
113pub struct DmabufState {
114 zwp_linux_dmabuf: GlobalProxy<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>,
115 modifiers: Vec<DmabufFormat>,
116}
117
118impl DmabufState {
119 pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
123 where
124 D: Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData> + 'static,
125 {
126 let zwp_linux_dmabuf = GlobalProxy::from(globals.bind(qh, 3..=5, GlobalData));
128 Self { zwp_linux_dmabuf, modifiers: Vec::new() }
129 }
130
131 pub fn modifiers(&self) -> &[DmabufFormat] {
135 &self.modifiers
136 }
137
138 pub fn version(&self) -> Option<u32> {
140 Some(self.zwp_linux_dmabuf.get().ok()?.version())
141 }
142
143 pub fn create_params<D>(&self, qh: &QueueHandle<D>) -> Result<DmabufParams, GlobalError>
148 where
149 D: Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData> + 'static,
150 {
151 let zwp_linux_dmabuf = self.zwp_linux_dmabuf.get()?;
152 let params = zwp_linux_dmabuf.create_params(qh, GlobalData);
153 Ok(DmabufParams { params })
154 }
155
156 pub fn get_default_feedback<D>(
160 &self,
161 qh: &QueueHandle<D>,
162 ) -> Result<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, GlobalError>
163 where
164 D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
165 + 'static,
166 {
167 let zwp_linux_dmabuf = self.zwp_linux_dmabuf.with_min_version(4)?;
168 Ok(zwp_linux_dmabuf.get_default_feedback(qh, DmabufFeedbackData::default()))
169 }
170
171 pub fn get_surface_feedback<D>(
175 &self,
176 surface: &wl_surface::WlSurface,
177 qh: &QueueHandle<D>,
178 ) -> Result<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, GlobalError>
179 where
180 D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
181 + 'static,
182 {
183 let zwp_linux_dmabuf = self.zwp_linux_dmabuf.with_min_version(4)?;
184 Ok(zwp_linux_dmabuf.get_surface_feedback(surface, qh, DmabufFeedbackData::default()))
185 }
186}
187
188pub trait DmabufHandler: Sized {
189 fn dmabuf_state(&mut self) -> &mut DmabufState;
190
191 fn dmabuf_feedback(
194 &mut self,
195 conn: &Connection,
196 qh: &QueueHandle<Self>,
197 proxy: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
198 feedback: DmabufFeedback,
199 );
200
201 fn created(
203 &mut self,
204 conn: &Connection,
205 qh: &QueueHandle<Self>,
206 params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
207 buffer: wl_buffer::WlBuffer,
208 );
209
210 fn failed(
212 &mut self,
213 conn: &Connection,
214 qh: &QueueHandle<Self>,
215 params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
216 );
217
218 fn released(&mut self, conn: &Connection, qh: &QueueHandle<Self>, buffer: &wl_buffer::WlBuffer);
220}
221
222#[derive(Debug)]
224pub struct DmabufParams {
225 params: zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
226}
227
228impl DmabufParams {
229 pub fn add(&self, fd: BorrowedFd<'_>, plane_idx: u32, offset: u32, stride: u32, modifier: u64) {
237 let modifier_hi = (modifier >> 32) as u32;
238 let modifier_lo = (modifier & 0xffffffff) as u32;
239 self.params.add(fd, plane_idx, offset, stride, modifier_hi, modifier_lo);
240 }
241
242 pub fn create(
247 self,
248 width: i32,
249 height: i32,
250 format: u32,
251 flags: zwp_linux_buffer_params_v1::Flags,
252 ) -> zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1 {
253 self.params.create(width, height, format, flags);
254 self.params
255 }
256
257 pub fn create_immed<D>(
262 self,
263 width: i32,
264 height: i32,
265 format: u32,
266 flags: zwp_linux_buffer_params_v1::Flags,
267 qh: &QueueHandle<D>,
268 ) -> (wl_buffer::WlBuffer, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1)
269 where
270 D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + 'static,
271 {
272 let buffer = self.params.create_immed(width, height, format, flags, qh, DmaBufferData);
273 (buffer, self.params)
274 }
275}
276
277impl<D> Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData, D> for DmabufState
278where
279 D: Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData> + DmabufHandler,
280{
281 fn event(
282 state: &mut D,
283 proxy: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
284 event: zwp_linux_dmabuf_v1::Event,
285 _: &GlobalData,
286 _: &Connection,
287 _: &QueueHandle<D>,
288 ) {
289 match event {
290 zwp_linux_dmabuf_v1::Event::Format { format: _ } => {
291 }
294 zwp_linux_dmabuf_v1::Event::Modifier { format, modifier_hi, modifier_lo } => {
295 if proxy.version() < 4 {
296 let modifier = (u64::from(modifier_hi) << 32) | u64::from(modifier_lo);
297 state.dmabuf_state().modifiers.push(DmabufFormat {
298 format,
299 _padding: 0,
300 modifier,
301 });
302 }
303 }
304 _ => unreachable!(),
305 }
306 }
307}
308
309impl<D> Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData, D>
310 for DmabufState
311where
312 D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
313 + DmabufHandler,
314{
315 fn event(
316 state: &mut D,
317 proxy: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
318 event: zwp_linux_dmabuf_feedback_v1::Event,
319 data: &DmabufFeedbackData,
320 conn: &Connection,
321 qh: &QueueHandle<D>,
322 ) {
323 match event {
324 zwp_linux_dmabuf_feedback_v1::Event::Done => {
325 let feedback = mem::take(&mut *data.pending.lock().unwrap());
326 state.dmabuf_feedback(conn, qh, proxy, feedback);
327 }
328 zwp_linux_dmabuf_feedback_v1::Event::FormatTable { fd, size } => {
329 let size = size as usize;
330 let mmap = unsafe {
331 MmapOptions::new().map_copy_read_only(&fd).expect("Failed to map format table")
332 };
333 assert!(mmap.len() >= size);
334 let entry_size = mem::size_of::<DmabufFormat>();
335 assert!((size % entry_size) == 0);
336 let len = size / entry_size;
337 data.pending.lock().unwrap().format_table = Some((mmap, len));
338 }
339 zwp_linux_dmabuf_feedback_v1::Event::MainDevice { device } => {
340 let device = dev_t::from_ne_bytes(device.try_into().unwrap());
341 data.pending.lock().unwrap().main_device = device;
342 }
343 zwp_linux_dmabuf_feedback_v1::Event::TrancheDone => {
344 let tranche = mem::take(&mut *data.pending_tranche.lock().unwrap());
345 data.pending.lock().unwrap().tranches.push(tranche);
346 }
347 zwp_linux_dmabuf_feedback_v1::Event::TrancheTargetDevice { device } => {
348 let device = dev_t::from_ne_bytes(device.try_into().unwrap());
349 data.pending_tranche.lock().unwrap().device = device;
350 }
351 zwp_linux_dmabuf_feedback_v1::Event::TrancheFormats { indices } => {
352 assert!((indices.len() % 2) == 0);
353 let indices =
354 indices.chunks(2).map(|i| u16::from_ne_bytes([i[0], i[1]])).collect::<Vec<_>>();
355 data.pending_tranche.lock().unwrap().formats = indices;
356 }
357 zwp_linux_dmabuf_feedback_v1::Event::TrancheFlags { flags } => {
358 data.pending_tranche.lock().unwrap().flags = flags;
359 }
360 _ => unreachable!(),
361 }
362 }
363}
364
365impl<D> Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData, D> for DmabufState
366where
367 D: Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData>
368 + Dispatch<wl_buffer::WlBuffer, DmaBufferData>
369 + DmabufHandler
370 + 'static,
371{
372 fn event(
373 state: &mut D,
374 proxy: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
375 event: zwp_linux_buffer_params_v1::Event,
376 _: &GlobalData,
377 conn: &Connection,
378 qh: &QueueHandle<D>,
379 ) {
380 match event {
381 zwp_linux_buffer_params_v1::Event::Created { buffer } => {
382 state.created(conn, qh, proxy, buffer);
383 }
384 zwp_linux_buffer_params_v1::Event::Failed => {
385 state.failed(conn, qh, proxy);
386 }
387 _ => unreachable!(),
388 }
389 }
390
391 wayland_client::event_created_child!(D, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, [
392 zwp_linux_buffer_params_v1::EVT_CREATED_OPCODE => (wl_buffer::WlBuffer, DmaBufferData)
393 ]);
394}
395
396impl<D> Dispatch<wl_buffer::WlBuffer, DmaBufferData, D> for DmabufState
397where
398 D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + DmabufHandler,
399{
400 fn event(
401 state: &mut D,
402 proxy: &wl_buffer::WlBuffer,
403 event: wl_buffer::Event,
404 _: &DmaBufferData,
405 conn: &Connection,
406 qh: &QueueHandle<D>,
407 ) {
408 match event {
409 wl_buffer::Event::Release => state.released(conn, qh, proxy),
410 _ => unreachable!(),
411 }
412 }
413}
414
415#[macro_export]
416macro_rules! delegate_dmabuf {
417 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
418 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
419 [
420 $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1: $crate::globals::GlobalData
421 ] => $crate::dmabuf::DmabufState
422 );
423 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
424 [
425 $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1: $crate::globals::GlobalData
426 ] => $crate::dmabuf::DmabufState
427 );
428 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
429 [
430 $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1: $crate::dmabuf::DmabufFeedbackData
431 ] => $crate::dmabuf::DmabufState
432 );
433 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
434 [
435 $crate::reexports::client::protocol::wl_buffer::WlBuffer: $crate::dmabuf::DmaBufferData
436 ] => $crate::dmabuf::DmabufState
437 );
438 };
439}