wayrs_utils/
dmabuf_feedback.rs1use libc::dev_t;
11use std::fmt;
12
13use wayrs_client::protocol::WlSurface;
14use wayrs_client::{Connection, EventCtx};
15use wayrs_protocols::linux_dmabuf_v1::*;
16
17#[derive(Debug)]
18pub struct DmabufFeedback {
19 wl: ZwpLinuxDmabufFeedbackV1,
20 main_device: Option<dev_t>,
21 format_table: Option<memmap2::Mmap>,
22 tranches: Vec<DmabufTranche>,
23 pending_tranche: DmabufTranche,
24 tranches_done: bool,
25}
26
27#[derive(Debug, Default)]
28pub struct DmabufTranche {
29 pub target_device: Option<dev_t>,
30 pub formats: Option<Vec<u16>>,
31 pub flags: zwp_linux_dmabuf_feedback_v1::TrancheFlags,
32}
33
34#[derive(Clone, Copy, Default)]
35#[repr(C)]
36pub struct FormatTableEntry {
37 pub fourcc: u32,
38 _padding: u32,
39 pub modifier: u64,
40}
41
42pub trait DmabufFeedbackHandler: Sized + 'static {
43 fn get_dmabuf_feedback(&mut self, wl: ZwpLinuxDmabufFeedbackV1) -> &mut DmabufFeedback;
47
48 fn feedback_done(&mut self, conn: &mut Connection<Self>, wl: ZwpLinuxDmabufFeedbackV1);
50}
51
52impl DmabufFeedback {
53 pub fn get_default<D: DmabufFeedbackHandler>(
54 conn: &mut Connection<D>,
55 linux_dmabuf: ZwpLinuxDmabufV1,
56 ) -> Self {
57 Self {
58 wl: linux_dmabuf.get_default_feedback_with_cb(conn, dmabuf_feedback_cb),
59 main_device: None,
60 format_table: None,
61 tranches: Vec::new(),
62 pending_tranche: DmabufTranche::default(),
63 tranches_done: false,
64 }
65 }
66
67 pub fn get_for_surface<D: DmabufFeedbackHandler>(
68 conn: &mut Connection<D>,
69 linux_dmabuf: ZwpLinuxDmabufV1,
70 surface: WlSurface,
71 ) -> Self {
72 Self {
73 wl: linux_dmabuf.get_surface_feedback_with_cb(conn, surface, dmabuf_feedback_cb),
74 main_device: None,
75 format_table: None,
76 tranches: Vec::new(),
77 pending_tranche: DmabufTranche::default(),
78 tranches_done: false,
79 }
80 }
81
82 #[must_use]
83 pub fn wl(&self) -> ZwpLinuxDmabufFeedbackV1 {
84 self.wl
85 }
86
87 #[must_use]
88 pub fn main_device(&self) -> Option<dev_t> {
89 self.main_device
90 }
91
92 #[must_use]
93 pub fn format_table(&self) -> &[FormatTableEntry] {
94 match &self.format_table {
95 Some(mmap) => unsafe {
96 std::slice::from_raw_parts(
97 mmap.as_ptr().cast(),
98 mmap.len() / std::mem::size_of::<FormatTableEntry>(),
99 )
100 },
101 None => &[],
102 }
103 }
104
105 #[must_use]
106 pub fn tranches(&self) -> &[DmabufTranche] {
107 &self.tranches
108 }
109
110 pub fn destroy<D>(self, conn: &mut Connection<D>) {
111 self.wl.destroy(conn);
112 }
113}
114
115fn dmabuf_feedback_cb<D: DmabufFeedbackHandler>(ctx: EventCtx<D, ZwpLinuxDmabufFeedbackV1>) {
116 let feedback = ctx.state.get_dmabuf_feedback(ctx.proxy);
117 assert_eq!(
118 feedback.wl, ctx.proxy,
119 "invalid DmabufFeedbackHandler::get_dmabuf_feedback() implementation"
120 );
121
122 use zwp_linux_dmabuf_feedback_v1::Event;
123 match ctx.event {
124 Event::Done => {
125 feedback.tranches_done = true;
126 ctx.state.feedback_done(ctx.conn, ctx.proxy);
127 }
128 Event::FormatTable(args) => {
129 let mmap = unsafe {
130 memmap2::MmapOptions::new()
131 .len(args.size as usize)
132 .map_copy_read_only(&args.fd)
133 .expect("mmap failed")
134 };
135 assert!(
136 mmap.as_ptr().cast::<FormatTableEntry>().is_aligned(),
137 "memory map is not alligned"
138 );
139 feedback.format_table = Some(mmap);
140 }
141 Event::MainDevice(main_dev) => {
142 feedback.main_device = Some(dev_t::from_ne_bytes(
143 main_dev.try_into().expect("invalid main_device size"),
144 ));
145 }
146 Event::TrancheDone => {
147 let tranche = std::mem::take(&mut feedback.pending_tranche);
148 feedback.tranches.push(tranche);
149 }
150 Event::TrancheTargetDevice(target_dev) => {
151 if feedback.tranches_done {
152 feedback.tranches.clear();
153 feedback.tranches_done = false;
154 }
155 feedback.pending_tranche.target_device = Some(dev_t::from_ne_bytes(
156 target_dev
157 .try_into()
158 .expect("invalid tranche_target_device size"),
159 ));
160 }
161 Event::TrancheFormats(indices) => {
162 if feedback.tranches_done {
163 feedback.tranches.clear();
164 feedback.tranches_done = false;
165 }
166 let mut formats = Vec::with_capacity(indices.len() / 2);
168 for index in indices.chunks_exact(2) {
169 let index = u16::from_ne_bytes(index.try_into().unwrap());
170 formats.push(index);
171 }
172 feedback.pending_tranche.formats = Some(formats);
173 }
174 Event::TrancheFlags(flags) => {
175 if feedback.tranches_done {
176 feedback.tranches.clear();
177 feedback.tranches_done = false;
178 }
179 feedback.pending_tranche.flags = flags;
180 }
181 _ => (),
182 }
183}
184
185impl fmt::Debug for FormatTableEntry {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 let [a, b, c, d] = self.fourcc.to_le_bytes();
188 write!(
189 f,
190 "{}{}{}{}:{}",
191 a.escape_ascii(),
192 b.escape_ascii(),
193 c.escape_ascii(),
194 d.escape_ascii(),
195 self.modifier
196 )
197 }
198}