1 | use crate::{error::GlobalError, globals::GlobalData, registry::GlobalProxy}; |
2 | use memmap2::{Mmap, MmapOptions}; |
3 | use std::{fmt, mem, os::unix::io::BorrowedFd, slice, sync::Mutex}; |
4 | use wayland_client::{ |
5 | globals::GlobalList, |
6 | protocol::{wl_buffer, wl_surface}, |
7 | Connection, Dispatch, Proxy, QueueHandle, WEnum, |
8 | }; |
9 | use 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" )] |
17 | type dev_t = u64; |
18 | #[cfg (not(target_os = "freebsd" ))] |
19 | use libc::dev_t; |
20 | |
21 | /// A preference tranche of dmabuf formats |
22 | #[derive (Debug)] |
23 | pub 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 | |
33 | impl 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)] |
46 | pub 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 | |
54 | impl 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)] |
65 | pub struct DmabufFeedback { |
66 | format_table: Option<(Mmap, usize)>, |
67 | main_device: dev_t, |
68 | tranches: Vec<DmabufFeedbackTranche>, |
69 | } |
70 | |
71 | impl 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 | |
81 | impl 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)] |
102 | pub struct DmabufFeedbackData { |
103 | pending: Mutex<DmabufFeedback>, |
104 | pending_tranche: Mutex<DmabufFeedbackTranche>, |
105 | } |
106 | |
107 | #[doc (hidden)] |
108 | #[derive (Debug)] |
109 | pub struct DmaBufferData; |
110 | |
111 | /// A handler for [`zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1`] |
112 | #[derive (Debug)] |
113 | pub struct DmabufState { |
114 | zwp_linux_dmabuf: GlobalProxy<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>, |
115 | modifiers: Vec<DmabufFormat>, |
116 | } |
117 | |
118 | impl 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 | |
188 | pub 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)] |
224 | pub struct DmabufParams { |
225 | params: zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, |
226 | } |
227 | |
228 | impl 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 | |
274 | impl<D> Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData, D> for DmabufState |
275 | where |
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 | |
306 | impl<D> Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData, D> |
307 | for DmabufState |
308 | where |
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 | |
362 | impl<D> Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData, D> for DmabufState |
363 | where |
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 | |
393 | impl<D> Dispatch<wl_buffer::WlBuffer, DmaBufferData, D> for DmabufState |
394 | where |
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 ] |
413 | macro_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 | |