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// Workaround until `libc` updates to FreeBSD 12 ABI
16#[cfg(target_os = "freebsd")]
17type dev_t = u64;
18#[cfg(not(target_os = "freebsd"))]
19use libc::dev_t;
20
21/// A preference tranche of dmabuf formats
22#[derive(Debug)]
23pub struct DmabufFeedbackTranche {
24 /// `dev_t` value for preferred target device. May be scan-out or
25 /// renderer device.
26 pub device: dev_t,
27 /// Flags for tranche
28 pub flags: WEnum<TrancheFlags>,
29 /// Indices of formats in the format table
30 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/// A single dmabuf format/modifier pair
44// Must have correct representation to be able to mmap format table
45#[repr(C)]
46pub struct DmabufFormat {
47 /// Fourcc format
48 pub format: u32,
49 _padding: u32,
50 /// Modifier, or `DRM_FORMAT_MOD_INVALID` for implict modifier
51 pub modifier: u64,
52}
53
54impl fmt::Debug for DmabufFormat {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f&mut DebugStruct<'_, '_>.debug_struct("DmabufFormat")
57 .field("format", &self.format)
58 .field(name:"modifier", &self.modifier)
59 .finish()
60 }
61}
62
63/// Description of supported and preferred dmabuf formats
64#[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&mut DebugStruct<'_, '_>.debug_struct("DmabufFeedback")
74 .field("format_table", &self.format_table())
75 .field("main_device", &self.main_device)
76 .field(name:"tranches", &self.tranches)
77 .finish()
78 }
79}
80
81impl DmabufFeedback {
82 /// Format/modifier pairs
83 pub fn format_table(&self) -> &[DmabufFormat] {
84 self.format_table.as_ref().map_or(&[], |(mmap: &Mmap, len: &usize)| unsafe {
85 slice::from_raw_parts(data:mmap.as_ptr() as *const DmabufFormat, *len)
86 })
87 }
88
89 /// `dev_t` value for main device. Buffers must be importable from main device.
90 pub fn main_device(&self) -> dev_t {
91 self.main_device
92 }
93
94 /// Tranches in descending order of preference
95 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/// A handler for [`zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1`]
112#[derive(Debug)]
113pub struct DmabufState {
114 zwp_linux_dmabuf: GlobalProxy<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>,
115 modifiers: Vec<DmabufFormat>,
116}
117
118impl DmabufState {
119 /// Bind `zwp_linux_dmabuf_v1` global version 3 or 4, if it exists.
120 ///
121 /// This does not fail if the global does not exist.
122 pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
123 where
124 D: Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData> + 'static,
125 {
126 // Mesa (at least the latest version) also requires version 3 or 4
127 let zwp_linux_dmabuf = GlobalProxy::from(globals.bind(qh, 3..=4, GlobalData));
128 Self { zwp_linux_dmabuf, modifiers: Vec::new() }
129 }
130
131 /// Only populated in version `<4`
132 ///
133 /// On version `4`, use [`DmabufState::get_surface_feedback`].
134 pub fn modifiers(&self) -> &[DmabufFormat] {
135 &self.modifiers
136 }
137
138 /// Supported protocol version, if any
139 pub fn version(&self) -> Option<u32> {
140 Some(self.zwp_linux_dmabuf.get().ok()?.version())
141 }
142
143 /// Create a params object for constructing a buffer
144 ///
145 /// Errors if `zwp_linux_dmabuf_v1` does not exist or has unsupported
146 /// version. An application can then fallback to using `shm` buffers.
147 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 /// Get default dmabuf feedback. Requires version `4`.
157 ///
158 /// On version `3`, use [`DmabufState::modifiers`].
159 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 /// Get default dmabuf feedback for given surface. Requires version `4`.
172 ///
173 /// On version `3`, use [`DmabufState::modifiers`].
174 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 /// Server has sent dmabuf feedback information. This may be received multiple
192 /// times by a `ZwpLinuxDmabufFeedbackV1` object.
193 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 /// `wl_buffer` associated with `params` has been created successfully.
202 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 /// Failed to create `wl_buffer` for `params`.
211 fn failed(
212 &mut self,
213 conn: &Connection,
214 qh: &QueueHandle<Self>,
215 params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
216 );
217
218 /// Compositor has released a `wl_buffer` created through [`DmabufParams`].
219 fn released(&mut self, conn: &Connection, qh: &QueueHandle<Self>, buffer: &wl_buffer::WlBuffer);
220}
221
222/// Builder for a dmabuf backed buffer
223#[derive(Debug)]
224pub struct DmabufParams {
225 params: zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
226}
227
228impl DmabufParams {
229 /// Add a plane
230 ///
231 /// In version `4`, it is a protocol error if `format`/`modifier` pair wasn't
232 /// advertised as supported.
233 pub fn add(&self, fd: BorrowedFd<'_>, plane_idx: u32, offset: u32, stride: u32, modifier: u64) {
234 let modifier_hi = (modifier >> 32) as u32;
235 let modifier_lo = (modifier & 0xffffffff) as u32;
236 self.params.add(fd, plane_idx, offset, stride, modifier_hi, modifier_lo);
237 }
238
239 /// Create buffer.
240 ///
241 /// [`DmabufHandler::created`] or [`DmabufHandler::failed`] will be invoked when the
242 /// operation succeeds or fails.
243 pub fn create(
244 self,
245 width: i32,
246 height: i32,
247 format: u32,
248 flags: zwp_linux_buffer_params_v1::Flags,
249 ) -> zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1 {
250 self.params.create(width, height, format, flags);
251 self.params
252 }
253
254 /// Create buffer immediately.
255 ///
256 /// On failure buffer is invalid, and server may raise protocol error or
257 /// send [`DmabufHandler::failed`].
258 pub fn create_immed<D>(
259 self,
260 width: i32,
261 height: i32,
262 format: u32,
263 flags: zwp_linux_buffer_params_v1::Flags,
264 qh: &QueueHandle<D>,
265 ) -> (wl_buffer::WlBuffer, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1)
266 where
267 D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + 'static,
268 {
269 let buffer = self.params.create_immed(width, height, format, flags, qh, DmaBufferData);
270 (buffer, self.params)
271 }
272}
273
274impl<D> Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData, D> for DmabufState
275where
276 D: Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData> + DmabufHandler,
277{
278 fn event(
279 state: &mut D,
280 proxy: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
281 event: zwp_linux_dmabuf_v1::Event,
282 _: &GlobalData,
283 _: &Connection,
284 _: &QueueHandle<D>,
285 ) {
286 match event {
287 zwp_linux_dmabuf_v1::Event::Format { format: _ } => {
288 // Formats are duplicated by modifier events since version 3.
289 // Ignore this event, like Mesa does.
290 }
291 zwp_linux_dmabuf_v1::Event::Modifier { format, modifier_hi, modifier_lo } => {
292 if proxy.version() < 4 {
293 let modifier = (u64::from(modifier_hi) << 32) | u64::from(modifier_lo);
294 state.dmabuf_state().modifiers.push(DmabufFormat {
295 format,
296 _padding: 0,
297 modifier,
298 });
299 }
300 }
301 _ => unreachable!(),
302 }
303 }
304}
305
306impl<D> Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData, D>
307 for DmabufState
308where
309 D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
310 + DmabufHandler,
311{
312 fn event(
313 state: &mut D,
314 proxy: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
315 event: zwp_linux_dmabuf_feedback_v1::Event,
316 data: &DmabufFeedbackData,
317 conn: &Connection,
318 qh: &QueueHandle<D>,
319 ) {
320 match event {
321 zwp_linux_dmabuf_feedback_v1::Event::Done => {
322 let feedback = mem::take(&mut *data.pending.lock().unwrap());
323 state.dmabuf_feedback(conn, qh, proxy, feedback);
324 }
325 zwp_linux_dmabuf_feedback_v1::Event::FormatTable { fd, size } => {
326 let size = size as usize;
327 let mmap = unsafe {
328 MmapOptions::new().map_copy_read_only(&fd).expect("Failed to map format table")
329 };
330 assert!(mmap.len() >= size);
331 let entry_size = mem::size_of::<DmabufFormat>();
332 assert!((size % entry_size) == 0);
333 let len = size / entry_size;
334 data.pending.lock().unwrap().format_table = Some((mmap, len));
335 }
336 zwp_linux_dmabuf_feedback_v1::Event::MainDevice { device } => {
337 let device = dev_t::from_ne_bytes(device.try_into().unwrap());
338 data.pending.lock().unwrap().main_device = device;
339 }
340 zwp_linux_dmabuf_feedback_v1::Event::TrancheDone => {
341 let tranche = mem::take(&mut *data.pending_tranche.lock().unwrap());
342 data.pending.lock().unwrap().tranches.push(tranche);
343 }
344 zwp_linux_dmabuf_feedback_v1::Event::TrancheTargetDevice { device } => {
345 let device = dev_t::from_ne_bytes(device.try_into().unwrap());
346 data.pending_tranche.lock().unwrap().device = device;
347 }
348 zwp_linux_dmabuf_feedback_v1::Event::TrancheFormats { indices } => {
349 assert!((indices.len() % 2) == 0);
350 let indices =
351 indices.chunks(2).map(|i| u16::from_ne_bytes([i[0], i[1]])).collect::<Vec<_>>();
352 data.pending_tranche.lock().unwrap().formats = indices;
353 }
354 zwp_linux_dmabuf_feedback_v1::Event::TrancheFlags { flags } => {
355 data.pending_tranche.lock().unwrap().flags = flags;
356 }
357 _ => unreachable!(),
358 }
359 }
360}
361
362impl<D> Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData, D> for DmabufState
363where
364 D: Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData>
365 + Dispatch<wl_buffer::WlBuffer, DmaBufferData>
366 + DmabufHandler
367 + 'static,
368{
369 fn event(
370 state: &mut D,
371 proxy: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
372 event: zwp_linux_buffer_params_v1::Event,
373 _: &GlobalData,
374 conn: &Connection,
375 qh: &QueueHandle<D>,
376 ) {
377 match event {
378 zwp_linux_buffer_params_v1::Event::Created { buffer } => {
379 state.created(conn, qh, params:proxy, buffer);
380 }
381 zwp_linux_buffer_params_v1::Event::Failed => {
382 state.failed(conn, qh, params:proxy);
383 }
384 _ => unreachable!(),
385 }
386 }
387
388 wayland_client::event_created_child!(D, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, [
389 zwp_linux_buffer_params_v1::EVT_CREATED_OPCODE => (wl_buffer::WlBuffer, DmaBufferData)
390 ]);
391}
392
393impl<D> Dispatch<wl_buffer::WlBuffer, DmaBufferData, D> for DmabufState
394where
395 D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + DmabufHandler,
396{
397 fn event(
398 state: &mut D,
399 proxy: &wl_buffer::WlBuffer,
400 event: wl_buffer::Event,
401 _: &DmaBufferData,
402 conn: &Connection,
403 qh: &QueueHandle<D>,
404 ) {
405 match event {
406 wl_buffer::Event::Release => state.released(conn, qh, buffer:proxy),
407 _ => unreachable!(),
408 }
409 }
410}
411
412#[macro_export]
413macro_rules! delegate_dmabuf {
414 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
415 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
416 [
417 $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1: $crate::globals::GlobalData
418 ] => $crate::dmabuf::DmabufState
419 );
420 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
421 [
422 $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1: $crate::globals::GlobalData
423 ] => $crate::dmabuf::DmabufState
424 );
425 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
426 [
427 $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1: $crate::dmabuf::DmabufFeedbackData
428 ] => $crate::dmabuf::DmabufState
429 );
430 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
431 [
432 $crate::reexports::client::protocol::wl_buffer::WlBuffer: $crate::dmabuf::DmaBufferData
433 ] => $crate::dmabuf::DmabufState
434 );
435 };
436}
437