1use drm::buffer::{Buffer, DrmFourcc};
6use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
7use drm::control::{
8 connector, crtc, framebuffer, plane, ClipRect, Device as CtrlDevice, PageFlipFlags,
9};
10use drm::Device;
11
12use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
13
14use std::collections::HashSet;
15use std::marker::PhantomData;
16use std::num::NonZeroU32;
17use std::os::unix::io::{AsFd, BorrowedFd};
18use std::rc::Rc;
19
20use crate::error::{InitError, SoftBufferError, SwResultExt};
21
22#[derive(Debug)]
23pub(crate) struct KmsDisplayImpl<D: ?Sized> {
24 fd: BorrowedFd<'static>,
26
27 _display: D,
29}
30
31impl<D: ?Sized> AsFd for KmsDisplayImpl<D> {
32 fn as_fd(&self) -> BorrowedFd<'_> {
33 self.fd
34 }
35}
36
37impl<D: ?Sized> Device for KmsDisplayImpl<D> {}
38impl<D: ?Sized> CtrlDevice for KmsDisplayImpl<D> {}
39
40impl<D: HasDisplayHandle> KmsDisplayImpl<D> {
41 pub(crate) fn new(display: D) -> Result<Self, InitError<D>> {
42 let fd = match display.display_handle()?.as_raw() {
43 RawDisplayHandle::Drm(drm) => drm.fd,
44 _ => return Err(InitError::Unsupported(display)),
45 };
46 if fd == -1 {
47 return Err(SoftBufferError::IncompleteDisplayHandle.into());
48 }
49
50 let fd = unsafe { BorrowedFd::borrow_raw(fd) };
52
53 Ok(KmsDisplayImpl {
54 fd,
55 _display: display,
56 })
57 }
58}
59
60#[derive(Debug)]
62pub(crate) struct KmsImpl<D: ?Sized, W: ?Sized> {
63 display: Rc<KmsDisplayImpl<D>>,
65
66 connectors: Vec<connector::Handle>,
68
69 crtc: crtc::Info,
71
72 buffer: Option<Buffers>,
74
75 window_handle: W,
77}
78
79#[derive(Debug)]
80struct Buffers {
81 buffers: [SharedBuffer; 2],
83
84 first_is_front: bool,
86
87 zeroes: Box<[u32]>,
89}
90
91pub(crate) struct BufferImpl<'a, D: ?Sized, W: ?Sized> {
93 mapping: DumbMapping<'a>,
95
96 front_fb: framebuffer::Handle,
98
99 crtc_handle: crtc::Handle,
101
102 first_is_front: &'a mut bool,
104
105 zeroes: &'a [u32],
107
108 size: (NonZeroU32, NonZeroU32),
110
111 display: &'a KmsDisplayImpl<D>,
113
114 front_age: &'a mut u8,
116
117 back_age: &'a mut u8,
119
120 _window: PhantomData<&'a mut W>,
122}
123
124#[derive(Debug)]
126struct SharedBuffer {
127 fb: framebuffer::Handle,
129
130 db: DumbBuffer,
132
133 age: u8,
135}
136
137impl<D: ?Sized, W: HasWindowHandle> KmsImpl<D, W> {
138 pub(crate) fn new(window: W, display: Rc<KmsDisplayImpl<D>>) -> Result<Self, InitError<W>> {
140 let plane_handle = match window.window_handle()?.as_raw() {
142 RawWindowHandle::Drm(drm) => match NonZeroU32::new(drm.plane) {
143 Some(handle) => plane::Handle::from(handle),
144 None => return Err(SoftBufferError::IncompleteWindowHandle.into()),
145 },
146 _ => return Err(InitError::Unsupported(window)),
147 };
148
149 let plane_info = display
150 .get_plane(plane_handle)
151 .swbuf_err("failed to get plane info")?;
152 let handles = display
153 .resource_handles()
154 .swbuf_err("failed to get resource handles")?;
155
156 let crtc = {
158 let handle = match plane_info.crtc() {
159 Some(crtc) => crtc,
160 None => {
161 log::warn!("no CRTC attached to plane, falling back to primary CRTC");
162 handles
163 .filter_crtcs(plane_info.possible_crtcs())
164 .first()
165 .copied()
166 .swbuf_err("failed to find a primary CRTC")?
167 }
168 };
169
170 display
172 .get_crtc(handle)
173 .swbuf_err("failed to get CRTC info")?
174 };
175
176 let encoders = handles
178 .encoders
179 .iter()
180 .flat_map(|handle| display.get_encoder(*handle))
181 .filter(|encoder| encoder.crtc() == Some(crtc.handle()))
182 .map(|encoder| encoder.handle())
183 .collect::<HashSet<_>>();
184
185 let connectors = handles
187 .connectors
188 .iter()
189 .flat_map(|handle| display.get_connector(*handle, false))
190 .filter(|connector| {
191 connector
192 .current_encoder()
193 .map_or(false, |encoder| encoders.contains(&encoder))
194 })
195 .map(|info| info.handle())
196 .collect::<Vec<_>>();
197
198 Ok(Self {
199 crtc,
200 connectors,
201 display,
202 buffer: None,
203 window_handle: window,
204 })
205 }
206
207 #[inline]
209 pub fn window(&self) -> &W {
210 &self.window_handle
211 }
212
213 pub(crate) fn resize(
215 &mut self,
216 width: NonZeroU32,
217 height: NonZeroU32,
218 ) -> Result<(), SoftBufferError> {
219 if let Some(buffer) = &self.buffer {
221 let (buffer_width, buffer_height) = buffer.size();
222 if buffer_width == width && buffer_height == height {
223 return Ok(());
224 }
225 }
226
227 let front_buffer = SharedBuffer::new(&self.display, width, height)?;
229 let back_buffer = SharedBuffer::new(&self.display, width, height)?;
230
231 self.buffer = Some(Buffers {
232 first_is_front: true,
233 buffers: [front_buffer, back_buffer],
234 zeroes: vec![0; width.get() as usize * height.get() as usize].into_boxed_slice(),
235 });
236
237 Ok(())
238 }
239
240 pub(crate) fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
242 Err(SoftBufferError::Unimplemented)
244 }
245
246 pub(crate) fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
248 let set = self
250 .buffer
251 .as_mut()
252 .expect("Must set size of surface before calling `buffer_mut()`");
253
254 let size = set.size();
255
256 let [first_buffer, second_buffer] = &mut set.buffers;
257 let (front_buffer, back_buffer) = if set.first_is_front {
258 (first_buffer, second_buffer)
259 } else {
260 (second_buffer, first_buffer)
261 };
262
263 let front_fb = front_buffer.fb;
264 let front_age = &mut front_buffer.age;
265 let back_age = &mut back_buffer.age;
266
267 let mapping = self
268 .display
269 .map_dumb_buffer(&mut front_buffer.db)
270 .swbuf_err("failed to map dumb buffer")?;
271
272 Ok(BufferImpl {
273 mapping,
274 size,
275 first_is_front: &mut set.first_is_front,
276 front_fb,
277 crtc_handle: self.crtc.handle(),
278 display: &self.display,
279 zeroes: &set.zeroes,
280 front_age,
281 back_age,
282 _window: PhantomData,
283 })
284 }
285}
286
287impl<D: ?Sized, W: ?Sized> Drop for KmsImpl<D, W> {
288 fn drop(&mut self) {
289 self.display
291 .set_crtc(
292 self.crtc.handle(),
293 self.crtc.framebuffer(),
294 self.crtc.position(),
295 &self.connectors,
296 self.crtc.mode(),
297 )
298 .ok();
299 }
300}
301
302impl<D: ?Sized, W: ?Sized> BufferImpl<'_, D, W> {
303 #[inline]
304 pub fn pixels(&self) -> &[u32] {
305 self.zeroes
308 }
309
310 #[inline]
311 pub fn pixels_mut(&mut self) -> &mut [u32] {
312 bytemuck::cast_slice_mut(self.mapping.as_mut())
313 }
314
315 #[inline]
316 pub fn age(&self) -> u8 {
317 *self.front_age
318 }
319
320 #[inline]
321 pub fn present_with_damage(self, damage: &[crate::Rect]) -> Result<(), SoftBufferError> {
322 let rectangles = damage
323 .iter()
324 .map(|&rect| {
325 let err = || SoftBufferError::DamageOutOfRange { rect };
326 Ok::<_, SoftBufferError>(ClipRect::new(
327 rect.x.try_into().map_err(|_| err())?,
328 rect.y.try_into().map_err(|_| err())?,
329 rect.x
330 .checked_add(rect.width.get())
331 .and_then(|x| x.try_into().ok())
332 .ok_or_else(err)?,
333 rect.y
334 .checked_add(rect.height.get())
335 .and_then(|y| y.try_into().ok())
336 .ok_or_else(err)?,
337 ))
338 })
339 .collect::<Result<Vec<_>, _>>()?;
340
341 match self.display.dirty_framebuffer(self.front_fb, &rectangles) {
348 Ok(()) => {}
349 Err(e) if e.raw_os_error() == Some(rustix::io::Errno::NOSYS.raw_os_error()) => {}
350 Err(e) => {
351 return Err(SoftBufferError::PlatformError(
352 Some("failed to dirty framebuffer".into()),
353 Some(e.into()),
354 ));
355 }
356 }
357
358 self.display
361 .page_flip(self.crtc_handle, self.front_fb, PageFlipFlags::EVENT, None)
362 .swbuf_err("failed to page flip")?;
363
364 *self.first_is_front = !*self.first_is_front;
366
367 *self.front_age = 1;
369 if *self.back_age != 0 {
370 *self.back_age += 1;
371 }
372
373 Ok(())
374 }
375
376 #[inline]
377 pub fn present(self) -> Result<(), SoftBufferError> {
378 let (width, height) = self.size;
379 self.present_with_damage(&[crate::Rect {
380 x: 0,
381 y: 0,
382 width,
383 height,
384 }])
385 }
386}
387
388impl SharedBuffer {
389 pub(crate) fn new<D: ?Sized>(
391 display: &KmsDisplayImpl<D>,
392 width: NonZeroU32,
393 height: NonZeroU32,
394 ) -> Result<Self, SoftBufferError> {
395 let db = display
396 .create_dumb_buffer((width.get(), height.get()), DrmFourcc::Xrgb8888, 32)
397 .swbuf_err("failed to create dumb buffer")?;
398 let fb = display
399 .add_framebuffer(&db, 24, 32)
400 .swbuf_err("failed to add framebuffer")?;
401
402 Ok(SharedBuffer { fb, db, age: 0 })
403 }
404
405 pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) {
407 let (width, height) = self.db.size();
408
409 NonZeroU32::new(width)
410 .and_then(|width| NonZeroU32::new(height).map(|height| (width, height)))
411 .expect("buffer size is zero")
412 }
413}
414
415impl Buffers {
416 pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) {
418 self.buffers[0].size()
419 }
420}