use std::{
cell::RefCell,
ffi::CStr,
fs::File,
io,
os::unix::io::{FromRawFd, RawFd},
rc::Rc,
time::SystemTime,
time::UNIX_EPOCH,
};
#[cfg(target_os = "linux")]
use nix::sys::memfd;
use nix::{
errno::Errno,
fcntl,
sys::{mman, stat},
unistd,
};
use memmap2::MmapMut;
use wayland_client::{
protocol::{wl_buffer, wl_shm, wl_shm_pool},
Attached, Main,
};
pub struct DoubleMemPool {
pool1: MemPool,
pool2: MemPool,
free: Rc<RefCell<bool>>,
}
impl DoubleMemPool {
pub fn new<F>(shm: Attached<wl_shm::WlShm>, callback: F) -> io::Result<DoubleMemPool>
where
F: FnMut(wayland_client::DispatchData) + 'static,
{
let free = Rc::new(RefCell::new(true));
let callback = Rc::new(RefCell::new(callback));
let my_free = free.clone();
let my_callback = callback.clone();
let pool1 = MemPool::new(shm.clone(), move |ddata| {
let signal = {
let mut my_free = my_free.borrow_mut();
if !*my_free {
*my_free = true;
true
} else {
false
}
};
if signal {
(&mut *my_callback.borrow_mut())(ddata);
}
})?;
let my_free = free.clone();
let pool2 = MemPool::new(shm, move |ddata| {
let signal = {
let mut my_free = my_free.borrow_mut();
if !*my_free {
*my_free = true;
true
} else {
false
}
};
if signal {
(&mut *callback.borrow_mut())(ddata);
}
})?;
Ok(DoubleMemPool { pool1, pool2, free })
}
pub fn pool(&mut self) -> Option<&mut MemPool> {
if !self.pool1.is_used() {
Some(&mut self.pool1)
} else if !self.pool2.is_used() {
Some(&mut self.pool2)
} else {
*self.free.borrow_mut() = false;
None
}
}
}
pub struct MemPool {
file: File,
len: usize,
pool: Main<wl_shm_pool::WlShmPool>,
buffer_count: Rc<RefCell<u32>>,
mmap: MmapMut,
callback: Rc<RefCell<dyn FnMut(wayland_client::DispatchData)>>,
}
impl MemPool {
pub fn new<F>(shm: Attached<wl_shm::WlShm>, callback: F) -> io::Result<MemPool>
where
F: FnMut(wayland_client::DispatchData) + 'static,
{
let mem_fd = create_shm_fd()?;
let mem_file = unsafe { File::from_raw_fd(mem_fd) };
mem_file.set_len(128)?;
let pool = shm.create_pool(mem_fd, 128);
let mmap = unsafe { MmapMut::map_mut(&mem_file).unwrap() };
Ok(MemPool {
file: mem_file,
len: 128,
pool,
buffer_count: Rc::new(RefCell::new(0)),
mmap,
callback: Rc::new(RefCell::new(callback)),
})
}
pub fn resize(&mut self, newsize: usize) -> io::Result<()> {
if newsize > self.len {
self.file.set_len(newsize as u64)?;
self.pool.resize(newsize as i32);
self.len = newsize;
self.mmap = unsafe { MmapMut::map_mut(&self.file).unwrap() };
}
Ok(())
}
pub fn buffer(
&self,
offset: i32,
width: i32,
height: i32,
stride: i32,
format: wl_shm::Format,
) -> wl_buffer::WlBuffer {
*self.buffer_count.borrow_mut() += 1;
let my_buffer_count = self.buffer_count.clone();
let my_callback = self.callback.clone();
let buffer = self.pool.create_buffer(offset, width, height, stride, format);
buffer.quick_assign(move |buffer, event, dispatch_data| match event {
wl_buffer::Event::Release => {
buffer.destroy();
let new_count = {
let mut my_buffer_count = my_buffer_count.borrow_mut();
*my_buffer_count -= 1;
*my_buffer_count
};
if new_count == 0 {
(&mut *my_callback.borrow_mut())(dispatch_data);
}
}
_ => unreachable!(),
});
(*buffer).clone().detach()
}
pub fn mmap(&mut self) -> &mut MmapMut {
&mut self.mmap
}
pub fn is_used(&self) -> bool {
*self.buffer_count.borrow() != 0
}
}
impl Drop for MemPool {
fn drop(&mut self) {
self.pool.destroy();
}
}
impl io::Write for MemPool {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
io::Write::write(&mut self.file, buf)
}
fn flush(&mut self) -> io::Result<()> {
io::Write::flush(&mut self.file)
}
}
impl io::Seek for MemPool {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
io::Seek::seek(&mut self.file, pos)
}
}
fn create_shm_fd() -> io::Result<RawFd> {
#[cfg(target_os = "linux")]
loop {
match memfd::memfd_create(
CStr::from_bytes_with_nul(b"smithay-client-toolkit\0").unwrap(),
memfd::MemFdCreateFlag::MFD_CLOEXEC,
) {
Ok(fd) => return Ok(fd),
Err(nix::Error::Sys(Errno::EINTR)) => continue,
Err(nix::Error::Sys(Errno::ENOSYS)) => break,
Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)),
Err(err) => unreachable!(err),
}
}
let sys_time = SystemTime::now();
let mut mem_file_handle = format!(
"/smithay-client-toolkit-{}",
sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
);
loop {
match mman::shm_open(
mem_file_handle.as_str(),
fcntl::OFlag::O_CREAT
| fcntl::OFlag::O_EXCL
| fcntl::OFlag::O_RDWR
| fcntl::OFlag::O_CLOEXEC,
stat::Mode::S_IRUSR | stat::Mode::S_IWUSR,
) {
Ok(fd) => match mman::shm_unlink(mem_file_handle.as_str()) {
Ok(_) => return Ok(fd),
Err(nix::Error::Sys(errno)) => match unistd::close(fd) {
Ok(_) => return Err(io::Error::from(errno)),
Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)),
Err(err) => panic!(err),
},
Err(err) => panic!(err),
},
Err(nix::Error::Sys(Errno::EEXIST)) => {
mem_file_handle = format!(
"/smithay-client-toolkit-{}",
sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
);
continue;
}
Err(nix::Error::Sys(Errno::EINTR)) => continue,
Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)),
Err(err) => unreachable!(err),
}
}
}
impl<E> crate::environment::Environment<E>
where
E: crate::environment::GlobalHandler<wl_shm::WlShm>,
{
pub fn create_simple_pool<F>(&self, callback: F) -> io::Result<MemPool>
where
F: FnMut(wayland_client::DispatchData) + 'static,
{
MemPool::new(self.require_global::<wl_shm::WlShm>(), callback)
}
pub fn create_double_pool<F>(&self, callback: F) -> io::Result<DoubleMemPool>
where
F: FnMut(wayland_client::DispatchData) + 'static,
{
DoubleMemPool::new(self.require_global::<wl_shm::WlShm>(), callback)
}
}