use drm::buffer::{Buffer, DrmFourcc};
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use drm::control::{
connector, crtc, framebuffer, plane, ClipRect, Device as CtrlDevice, PageFlipFlags,
};
use drm::Device;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
use std::collections::HashSet;
use std::marker::PhantomData;
use std::num::NonZeroU32;
use std::os::unix::io::{AsFd, BorrowedFd};
use std::rc::Rc;
use crate::error::{InitError, SoftBufferError, SwResultExt};
#[derive(Debug)]
pub(crate) struct KmsDisplayImpl<D: ?Sized> {
fd: BorrowedFd<'static>,
_display: D,
}
impl<D: ?Sized> AsFd for KmsDisplayImpl<D> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd
}
}
impl<D: ?Sized> Device for KmsDisplayImpl<D> {}
impl<D: ?Sized> CtrlDevice for KmsDisplayImpl<D> {}
impl<D: HasDisplayHandle> KmsDisplayImpl<D> {
pub(crate) fn new(display: D) -> Result<Self, InitError<D>> {
let fd = match display.display_handle()?.as_raw() {
RawDisplayHandle::Drm(drm) => drm.fd,
_ => return Err(InitError::Unsupported(display)),
};
if fd == -1 {
return Err(SoftBufferError::IncompleteDisplayHandle.into());
}
let fd = unsafe { BorrowedFd::borrow_raw(fd) };
Ok(KmsDisplayImpl {
fd,
_display: display,
})
}
}
#[derive(Debug)]
pub(crate) struct KmsImpl<D: ?Sized, W: ?Sized> {
display: Rc<KmsDisplayImpl<D>>,
connectors: Vec<connector::Handle>,
crtc: crtc::Info,
buffer: Option<Buffers>,
window_handle: W,
}
#[derive(Debug)]
struct Buffers {
buffers: [SharedBuffer; 2],
first_is_front: bool,
zeroes: Box<[u32]>,
}
pub(crate) struct BufferImpl<'a, D: ?Sized, W: ?Sized> {
mapping: DumbMapping<'a>,
front_fb: framebuffer::Handle,
crtc_handle: crtc::Handle,
first_is_front: &'a mut bool,
zeroes: &'a [u32],
size: (NonZeroU32, NonZeroU32),
display: &'a KmsDisplayImpl<D>,
front_age: &'a mut u8,
back_age: &'a mut u8,
_window: PhantomData<&'a mut W>,
}
#[derive(Debug)]
struct SharedBuffer {
fb: framebuffer::Handle,
db: DumbBuffer,
age: u8,
}
impl<D: ?Sized, W: HasWindowHandle> KmsImpl<D, W> {
pub(crate) fn new(window: W, display: Rc<KmsDisplayImpl<D>>) -> Result<Self, InitError<W>> {
let plane_handle = match window.window_handle()?.as_raw() {
RawWindowHandle::Drm(drm) => match NonZeroU32::new(drm.plane) {
Some(handle) => plane::Handle::from(handle),
None => return Err(SoftBufferError::IncompleteWindowHandle.into()),
},
_ => return Err(InitError::Unsupported(window)),
};
let plane_info = display
.get_plane(plane_handle)
.swbuf_err("failed to get plane info")?;
let handles = display
.resource_handles()
.swbuf_err("failed to get resource handles")?;
let crtc = {
let handle = match plane_info.crtc() {
Some(crtc) => crtc,
None => {
log::warn!("no CRTC attached to plane, falling back to primary CRTC");
handles
.filter_crtcs(plane_info.possible_crtcs())
.first()
.copied()
.swbuf_err("failed to find a primary CRTC")?
}
};
display
.get_crtc(handle)
.swbuf_err("failed to get CRTC info")?
};
let encoders = handles
.encoders
.iter()
.flat_map(|handle| display.get_encoder(*handle))
.filter(|encoder| encoder.crtc() == Some(crtc.handle()))
.map(|encoder| encoder.handle())
.collect::<HashSet<_>>();
let connectors = handles
.connectors
.iter()
.flat_map(|handle| display.get_connector(*handle, false))
.filter(|connector| {
connector
.current_encoder()
.map_or(false, |encoder| encoders.contains(&encoder))
})
.map(|info| info.handle())
.collect::<Vec<_>>();
Ok(Self {
crtc,
connectors,
display,
buffer: None,
window_handle: window,
})
}
#[inline]
pub fn window(&self) -> &W {
&self.window_handle
}
pub(crate) fn resize(
&mut self,
width: NonZeroU32,
height: NonZeroU32,
) -> Result<(), SoftBufferError> {
if let Some(buffer) = &self.buffer {
let (buffer_width, buffer_height) = buffer.size();
if buffer_width == width && buffer_height == height {
return Ok(());
}
}
let front_buffer = SharedBuffer::new(&self.display, width, height)?;
let back_buffer = SharedBuffer::new(&self.display, width, height)?;
self.buffer = Some(Buffers {
first_is_front: true,
buffers: [front_buffer, back_buffer],
zeroes: vec![0; width.get() as usize * height.get() as usize].into_boxed_slice(),
});
Ok(())
}
pub(crate) fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
pub(crate) fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
let set = self
.buffer
.as_mut()
.expect("Must set size of surface before calling `buffer_mut()`");
let size = set.size();
let [first_buffer, second_buffer] = &mut set.buffers;
let (front_buffer, back_buffer) = if set.first_is_front {
(first_buffer, second_buffer)
} else {
(second_buffer, first_buffer)
};
let front_fb = front_buffer.fb;
let front_age = &mut front_buffer.age;
let back_age = &mut back_buffer.age;
let mapping = self
.display
.map_dumb_buffer(&mut front_buffer.db)
.swbuf_err("failed to map dumb buffer")?;
Ok(BufferImpl {
mapping,
size,
first_is_front: &mut set.first_is_front,
front_fb,
crtc_handle: self.crtc.handle(),
display: &self.display,
zeroes: &set.zeroes,
front_age,
back_age,
_window: PhantomData,
})
}
}
impl<D: ?Sized, W: ?Sized> Drop for KmsImpl<D, W> {
fn drop(&mut self) {
self.display
.set_crtc(
self.crtc.handle(),
self.crtc.framebuffer(),
self.crtc.position(),
&self.connectors,
self.crtc.mode(),
)
.ok();
}
}
impl<D: ?Sized, W: ?Sized> BufferImpl<'_, D, W> {
#[inline]
pub fn pixels(&self) -> &[u32] {
self.zeroes
}
#[inline]
pub fn pixels_mut(&mut self) -> &mut [u32] {
bytemuck::cast_slice_mut(self.mapping.as_mut())
}
#[inline]
pub fn age(&self) -> u8 {
*self.front_age
}
#[inline]
pub fn present_with_damage(self, damage: &[crate::Rect]) -> Result<(), SoftBufferError> {
let rectangles = damage
.iter()
.map(|&rect| {
let err = || SoftBufferError::DamageOutOfRange { rect };
Ok::<_, SoftBufferError>(ClipRect::new(
rect.x.try_into().map_err(|_| err())?,
rect.y.try_into().map_err(|_| err())?,
rect.x
.checked_add(rect.width.get())
.and_then(|x| x.try_into().ok())
.ok_or_else(err)?,
rect.y
.checked_add(rect.height.get())
.and_then(|y| y.try_into().ok())
.ok_or_else(err)?,
))
})
.collect::<Result<Vec<_>, _>>()?;
match self.display.dirty_framebuffer(self.front_fb, &rectangles) {
Ok(()) => {}
Err(e) if e.raw_os_error() == Some(rustix::io::Errno::NOSYS.raw_os_error()) => {}
Err(e) => {
return Err(SoftBufferError::PlatformError(
Some("failed to dirty framebuffer".into()),
Some(e.into()),
));
}
}
self.display
.page_flip(self.crtc_handle, self.front_fb, PageFlipFlags::EVENT, None)
.swbuf_err("failed to page flip")?;
*self.first_is_front = !*self.first_is_front;
*self.front_age = 1;
if *self.back_age != 0 {
*self.back_age += 1;
}
Ok(())
}
#[inline]
pub fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = self.size;
self.present_with_damage(&[crate::Rect {
x: 0,
y: 0,
width,
height,
}])
}
}
impl SharedBuffer {
pub(crate) fn new<D: ?Sized>(
display: &KmsDisplayImpl<D>,
width: NonZeroU32,
height: NonZeroU32,
) -> Result<Self, SoftBufferError> {
let db = display
.create_dumb_buffer((width.get(), height.get()), DrmFourcc::Xrgb8888, 32)
.swbuf_err("failed to create dumb buffer")?;
let fb = display
.add_framebuffer(&db, 24, 32)
.swbuf_err("failed to add framebuffer")?;
Ok(SharedBuffer { fb, db, age: 0 })
}
pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) {
let (width, height) = self.db.size();
NonZeroU32::new(width)
.and_then(|width| NonZeroU32::new(height).map(|height| (width, height)))
.expect("buffer size is zero")
}
}
impl Buffers {
pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) {
self.buffers[0].size()
}
}