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