1use crate::{dispatch2::Dispatch2, error::GlobalError, globals::GlobalData, registry::GlobalProxy};
2use memmap2::{Mmap, MmapOptions};
3use rustix::fs::Dev as dev_t;
4use std::{fmt, mem, os::unix::io::BorrowedFd, slice, sync::Mutex};
5use wayland_client::{
6 globals::GlobalList,
7 protocol::{wl_buffer, wl_surface},
8 Connection, Dispatch, Proxy, QueueHandle, WEnum,
9};
10use wayland_protocols::wp::linux_dmabuf::zv1::client::{
11 zwp_linux_buffer_params_v1,
12 zwp_linux_dmabuf_feedback_v1::{self, TrancheFlags},
13 zwp_linux_dmabuf_v1,
14};
15
16#[derive(Clone, Debug)]
18pub struct DmabufFeedbackTranche {
19 pub device: dev_t,
22 pub flags: WEnum<TrancheFlags>,
24 pub formats: Vec<u16>,
26}
27
28impl Default for DmabufFeedbackTranche {
29 fn default() -> DmabufFeedbackTranche {
30 DmabufFeedbackTranche {
31 device: 0,
32 flags: WEnum::Value(TrancheFlags::empty()),
33 formats: Vec::new(),
34 }
35 }
36}
37
38#[repr(C)]
41#[derive(Copy, Clone)]
42pub struct DmabufFormat {
43 pub format: u32,
45 _padding: u32,
46 pub modifier: u64,
48}
49
50impl fmt::Debug for DmabufFormat {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 f.debug_struct("DmabufFormat")
53 .field("format", &self.format)
54 .field("modifier", &self.modifier)
55 .finish()
56 }
57}
58
59#[derive(Default)]
61pub struct DmabufFeedback {
62 format_table: Option<(Mmap, usize)>,
63 main_device: dev_t,
64 tranches: Vec<DmabufFeedbackTranche>,
65}
66
67impl fmt::Debug for DmabufFeedback {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.debug_struct("DmabufFeedback")
70 .field("format_table", &self.format_table())
71 .field("main_device", &self.main_device)
72 .field("tranches", &self.tranches)
73 .finish()
74 }
75}
76
77impl DmabufFeedback {
78 pub fn format_table(&self) -> &[DmabufFormat] {
80 self.format_table.as_ref().map_or(&[], |(mmap, len)| unsafe {
81 slice::from_raw_parts(mmap.as_ptr() as *const DmabufFormat, *len)
82 })
83 }
84
85 pub fn main_device(&self) -> dev_t {
87 self.main_device
88 }
89
90 pub fn tranches(&self) -> &[DmabufFeedbackTranche] {
92 &self.tranches
93 }
94}
95
96#[doc(hidden)]
97#[derive(Debug, Default)]
98pub struct DmabufFeedbackData {
99 pending: Mutex<DmabufFeedback>,
100 pending_tranche: Mutex<DmabufFeedbackTranche>,
101}
102
103#[doc(hidden)]
104#[derive(Debug)]
105pub struct DmaBufferData;
106
107#[derive(Debug)]
109pub struct DmabufState {
110 zwp_linux_dmabuf: GlobalProxy<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>,
111 modifiers: Vec<DmabufFormat>,
112}
113
114impl DmabufState {
115 pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
119 where
120 D: Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData> + 'static,
121 {
122 let zwp_linux_dmabuf = GlobalProxy::from(globals.bind(qh, 3..=5, GlobalData));
124 Self { zwp_linux_dmabuf, modifiers: Vec::new() }
125 }
126
127 pub fn modifiers(&self) -> &[DmabufFormat] {
131 &self.modifiers
132 }
133
134 pub fn version(&self) -> Option<u32> {
136 Some(self.zwp_linux_dmabuf.get().ok()?.version())
137 }
138
139 pub fn create_params<D>(&self, qh: &QueueHandle<D>) -> Result<DmabufParams, GlobalError>
144 where
145 D: Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData> + 'static,
146 {
147 let zwp_linux_dmabuf = self.zwp_linux_dmabuf.get()?;
148 let params = zwp_linux_dmabuf.create_params(qh, GlobalData);
149 Ok(DmabufParams { params })
150 }
151
152 pub fn get_default_feedback<D>(
156 &self,
157 qh: &QueueHandle<D>,
158 ) -> Result<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, GlobalError>
159 where
160 D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
161 + 'static,
162 {
163 let zwp_linux_dmabuf = self.zwp_linux_dmabuf.with_min_version(4)?;
164 Ok(zwp_linux_dmabuf.get_default_feedback(qh, DmabufFeedbackData::default()))
165 }
166
167 pub fn get_surface_feedback<D>(
171 &self,
172 surface: &wl_surface::WlSurface,
173 qh: &QueueHandle<D>,
174 ) -> Result<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, GlobalError>
175 where
176 D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
177 + 'static,
178 {
179 let zwp_linux_dmabuf = self.zwp_linux_dmabuf.with_min_version(4)?;
180 Ok(zwp_linux_dmabuf.get_surface_feedback(surface, qh, DmabufFeedbackData::default()))
181 }
182}
183
184pub trait DmabufHandler: Sized {
185 fn dmabuf_state(&mut self) -> &mut DmabufState;
186
187 fn dmabuf_feedback(
190 &mut self,
191 conn: &Connection,
192 qh: &QueueHandle<Self>,
193 proxy: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
194 feedback: DmabufFeedback,
195 );
196
197 fn created(
199 &mut self,
200 conn: &Connection,
201 qh: &QueueHandle<Self>,
202 params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
203 buffer: wl_buffer::WlBuffer,
204 );
205
206 fn failed(
208 &mut self,
209 conn: &Connection,
210 qh: &QueueHandle<Self>,
211 params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
212 );
213
214 fn released(&mut self, conn: &Connection, qh: &QueueHandle<Self>, buffer: &wl_buffer::WlBuffer);
216}
217
218#[derive(Debug)]
220pub struct DmabufParams {
221 params: zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
222}
223
224impl DmabufParams {
225 pub fn add(&self, fd: BorrowedFd<'_>, plane_idx: u32, offset: u32, stride: u32, modifier: u64) {
233 let modifier_hi = (modifier >> 32) as u32;
234 let modifier_lo = (modifier & 0xffffffff) as u32;
235 self.params.add(fd, plane_idx, offset, stride, modifier_hi, modifier_lo);
236 }
237
238 pub fn create(
243 self,
244 width: i32,
245 height: i32,
246 format: u32,
247 flags: zwp_linux_buffer_params_v1::Flags,
248 ) -> zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1 {
249 self.params.create(width, height, format, flags);
250 self.params
251 }
252
253 pub fn create_immed<D>(
258 self,
259 width: i32,
260 height: i32,
261 format: u32,
262 flags: zwp_linux_buffer_params_v1::Flags,
263 qh: &QueueHandle<D>,
264 ) -> (wl_buffer::WlBuffer, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1)
265 where
266 D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + 'static,
267 {
268 let buffer = self.params.create_immed(width, height, format, flags, qh, DmaBufferData);
269 (buffer, self.params)
270 }
271}
272
273impl<D> Dispatch2<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, D> for GlobalData
274where
275 D: DmabufHandler,
276{
277 fn event(
278 &self,
279 state: &mut D,
280 proxy: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
281 event: zwp_linux_dmabuf_v1::Event,
282 _: &Connection,
283 _: &QueueHandle<D>,
284 ) {
285 match event {
286 zwp_linux_dmabuf_v1::Event::Format { format: _ } => {
287 }
290 zwp_linux_dmabuf_v1::Event::Modifier { format, modifier_hi, modifier_lo } => {
291 if proxy.version() < 4 {
292 let modifier = (u64::from(modifier_hi) << 32) | u64::from(modifier_lo);
293 state.dmabuf_state().modifiers.push(DmabufFormat {
294 format,
295 _padding: 0,
296 modifier,
297 });
298 }
299 }
300 _ => unreachable!(),
301 }
302 }
303}
304
305impl<D> Dispatch2<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, D> for DmabufFeedbackData
306where
307 D: DmabufHandler,
308{
309 fn event(
310 &self,
311 state: &mut D,
312 proxy: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
313 event: zwp_linux_dmabuf_feedback_v1::Event,
314 conn: &Connection,
315 qh: &QueueHandle<D>,
316 ) {
317 match event {
318 zwp_linux_dmabuf_feedback_v1::Event::Done => {
319 let feedback = mem::take(&mut *self.pending.lock().unwrap());
320 state.dmabuf_feedback(conn, qh, proxy, feedback);
321 }
322 zwp_linux_dmabuf_feedback_v1::Event::FormatTable { fd, size } => {
323 let size = size as usize;
324 let mmap = unsafe {
325 MmapOptions::new().map_copy_read_only(&fd).expect("Failed to map format table")
326 };
327 assert!(mmap.len() >= size);
328 let entry_size = mem::size_of::<DmabufFormat>();
329 assert!((size % entry_size) == 0);
330 let len = size / entry_size;
331 self.pending.lock().unwrap().format_table = Some((mmap, len));
332 }
333 zwp_linux_dmabuf_feedback_v1::Event::MainDevice { device } => {
334 let device = dev_t::from_ne_bytes(device.try_into().unwrap());
335 self.pending.lock().unwrap().main_device = device;
336 }
337 zwp_linux_dmabuf_feedback_v1::Event::TrancheDone => {
338 let tranche = mem::take(&mut *self.pending_tranche.lock().unwrap());
339 self.pending.lock().unwrap().tranches.push(tranche);
340 }
341 zwp_linux_dmabuf_feedback_v1::Event::TrancheTargetDevice { device } => {
342 let device = dev_t::from_ne_bytes(device.try_into().unwrap());
343 self.pending_tranche.lock().unwrap().device = device;
344 }
345 zwp_linux_dmabuf_feedback_v1::Event::TrancheFormats { indices } => {
346 assert!((indices.len() % 2) == 0);
347 let indices =
348 indices.chunks(2).map(|i| u16::from_ne_bytes([i[0], i[1]])).collect::<Vec<_>>();
349 self.pending_tranche.lock().unwrap().formats = indices;
350 }
351 zwp_linux_dmabuf_feedback_v1::Event::TrancheFlags { flags } => {
352 self.pending_tranche.lock().unwrap().flags = flags;
353 }
354 _ => unreachable!(),
355 }
356 }
357}
358
359impl<D> Dispatch2<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, D> for GlobalData
360where
361 D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + DmabufHandler + 'static,
362{
363 fn event(
364 &self,
365 state: &mut D,
366 proxy: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
367 event: zwp_linux_buffer_params_v1::Event,
368 conn: &Connection,
369 qh: &QueueHandle<D>,
370 ) {
371 match event {
372 zwp_linux_buffer_params_v1::Event::Created { buffer } => {
373 state.created(conn, qh, proxy, buffer);
374 }
375 zwp_linux_buffer_params_v1::Event::Failed => {
376 state.failed(conn, qh, proxy);
377 }
378 _ => unreachable!(),
379 }
380 }
381
382 wayland_client::event_created_child!(D, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, [
383 zwp_linux_buffer_params_v1::EVT_CREATED_OPCODE => (wl_buffer::WlBuffer, DmaBufferData)
384 ]);
385}
386
387impl<D> Dispatch2<wl_buffer::WlBuffer, D> for DmaBufferData
388where
389 D: DmabufHandler,
390{
391 fn event(
392 &self,
393 state: &mut D,
394 proxy: &wl_buffer::WlBuffer,
395 event: wl_buffer::Event,
396 conn: &Connection,
397 qh: &QueueHandle<D>,
398 ) {
399 match event {
400 wl_buffer::Event::Release => state.released(conn, qh, proxy),
401 _ => unreachable!(),
402 }
403 }
404}