use std::{
ffi::CString,
os::fd::OwnedFd,
time::{SystemTime, UNIX_EPOCH},
};
use gbm::BufferObject;
use image::{ColorType, DynamicImage, ImageBuffer, Pixel};
use memmap2::MmapMut;
use rustix::{
fs::{self, SealFlags},
io, shm,
};
use wayland_client::protocol::{
wl_buffer::WlBuffer, wl_output, wl_shm::Format, wl_shm_pool::WlShmPool,
};
use crate::{
Error, Result,
region::{LogicalRegion, Size},
};
pub struct FrameGuard {
pub buffer: WlBuffer,
pub shm_pool: WlShmPool,
}
impl Drop for FrameGuard {
fn drop(&mut self) {
self.buffer.destroy();
self.shm_pool.destroy();
}
}
pub struct DMAFrameGuard {
pub buffer: WlBuffer,
}
impl Drop for DMAFrameGuard {
fn drop(&mut self) {
self.buffer.destroy();
}
}
pub struct EGLImageGuard<'a, T: khronos_egl::api::EGL1_5> {
pub image: khronos_egl::Image,
pub(crate) egl_instance: &'a khronos_egl::Instance<T>,
pub(crate) egl_display: khronos_egl::Display,
}
impl<T: khronos_egl::api::EGL1_5> Drop for EGLImageGuard<'_, T> {
fn drop(&mut self) {
self.egl_instance
.destroy_image(self.egl_display, self.image)
.unwrap_or_else(|e| {
tracing::error!("EGLimage destruction had error: {e}");
});
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct FrameFormat {
pub format: Format,
pub size: Size,
pub stride: u32,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct DMAFrameFormat {
pub format: u32,
pub size: Size,
}
impl FrameFormat {
pub fn byte_size(&self) -> u64 {
self.stride as u64 * self.size.height as u64
}
}
#[tracing::instrument(skip(frame_data))]
fn create_image_buffer<P>(
frame_format: &FrameFormat,
frame_data: &FrameData,
) -> Result<ImageBuffer<P, Vec<P::Subpixel>>>
where
P: Pixel<Subpixel = u8>,
{
tracing::debug!("Creating image buffer");
match frame_data {
FrameData::Mmap(frame_mmap) => ImageBuffer::from_vec(
frame_format.size.width,
frame_format.size.height,
frame_mmap.to_vec(),
)
.ok_or(Error::BufferTooSmall),
FrameData::GBMBo(_) => todo!(),
}
}
#[derive(Debug)]
pub enum FrameData {
Mmap(MmapMut),
GBMBo(BufferObject<()>),
}
#[derive(Debug)]
pub struct FrameCopy {
pub frame_format: FrameFormat,
pub frame_color_type: ColorType,
pub frame_data: FrameData,
pub transform: wl_output::Transform,
pub logical_region: LogicalRegion,
pub physical_size: Size,
}
impl TryFrom<&FrameCopy> for DynamicImage {
type Error = Error;
fn try_from(value: &FrameCopy) -> Result<Self> {
Ok(match value.frame_color_type {
ColorType::Rgb8 => {
Self::ImageRgb8(create_image_buffer(&value.frame_format, &value.frame_data)?)
}
ColorType::Rgba8 => {
Self::ImageRgba8(create_image_buffer(&value.frame_format, &value.frame_data)?)
}
_ => return Err(Error::InvalidColor),
})
}
}
fn get_mem_file_handle() -> String {
format!(
"/libwayshot-{}",
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|time| time.subsec_nanos().to_string())
.unwrap_or("unknown".into())
)
}
pub fn create_shm_fd() -> std::io::Result<OwnedFd> {
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
loop {
match fs::memfd_create(
CString::new("libwayshot")?.as_c_str(),
fs::MemfdFlags::CLOEXEC | fs::MemfdFlags::ALLOW_SEALING,
) {
Ok(fd) => {
let _ = fs::fcntl_add_seals(&fd, fs::SealFlags::SHRINK | SealFlags::SEAL);
return Ok(fd);
}
Err(io::Errno::INTR) => continue,
Err(io::Errno::NOSYS) => break,
Err(errno) => return Err(std::io::Error::from(errno)),
}
}
let mut mem_file_handle = get_mem_file_handle();
loop {
let open_result = shm::open(
mem_file_handle.as_str(),
shm::OFlags::CREATE | shm::OFlags::EXCL | shm::OFlags::RDWR,
fs::Mode::RUSR | fs::Mode::WUSR,
);
match open_result {
Ok(fd) => match shm::unlink(mem_file_handle.as_str()) {
Ok(_) => return Ok(fd),
Err(errno) => return Err(std::io::Error::from(errno)),
},
Err(io::Errno::EXIST) => {
mem_file_handle = get_mem_file_handle();
continue;
}
Err(io::Errno::INTR) => continue,
Err(errno) => return Err(std::io::Error::from(errno)),
}
}
}