use nix;
use nix::errno::Errno;
use nix::fcntl;
#[cfg(target_os = "linux")]
use nix::sys::memfd;
use nix::sys::mman;
use nix::sys::stat;
use nix::unistd;
use std::ffi::CStr;
use std::fs::File;
use std::io;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::RawFd;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
use memmap::MmapMut;
use wayland_client::protocol::{wl_buffer, wl_shm, wl_shm_pool};
use wayland_client::Proxy;
use wayland_client::protocol::wl_buffer::RequestsTrait;
use wayland_client::protocol::wl_shm::RequestsTrait as ShmRequests;
use wayland_client::protocol::wl_shm_pool::RequestsTrait as PoolRequests;
pub struct DoubleMemPool {
pool1: MemPool,
pool2: MemPool,
free: Arc<Mutex<bool>>,
}
impl DoubleMemPool {
pub fn new<Impl>(shm: &Proxy<wl_shm::WlShm>, implementation: Impl) -> io::Result<DoubleMemPool>
where
Impl: FnMut() + Send + 'static,
{
let free = Arc::new(Mutex::new(true));
let implementation = Arc::new(Mutex::new(implementation));
let my_free = free.clone();
let my_implementation = implementation.clone();
let pool1 = MemPool::new(shm, move || {
let mut my_free = my_free.lock().unwrap();
if !*my_free {
(&mut *my_implementation.lock().unwrap())();
*my_free = true
}
})?;
let my_free = free.clone();
let my_implementation = implementation.clone();
let pool2 = MemPool::new(shm, move || {
let mut my_free = my_free.lock().unwrap();
if !*my_free {
(&mut *my_implementation.lock().unwrap())();
*my_free = true
}
})?;
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.lock().unwrap() = false;
None
}
}
}
pub struct MemPool {
file: File,
len: usize,
pool: Proxy<wl_shm_pool::WlShmPool>,
buffer_count: Arc<Mutex<u32>>,
mmap: MmapMut,
implementation: Arc<Mutex<FnMut() + Send>>,
}
impl MemPool {
pub fn new<Impl>(shm: &Proxy<wl_shm::WlShm>, implementation: Impl) -> io::Result<MemPool>
where
Impl: FnMut() + Send + '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, |pool| pool.implement(|e, _| match e {}, ()))
.unwrap();
let mmap = unsafe { MmapMut::map_mut(&mem_file).unwrap() };
Ok(MemPool {
file: mem_file,
len: 128,
pool,
buffer_count: Arc::new(Mutex::new(0)),
mmap,
implementation: Arc::new(Mutex::new(implementation)),
})
}
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,
) -> Proxy<wl_buffer::WlBuffer> {
*self.buffer_count.lock().unwrap() += 1;
let my_buffer_count = self.buffer_count.clone();
let my_implementation = self.implementation.clone();
self.pool
.create_buffer(offset, width, height, stride, format, |buffer| {
buffer.implement(
move |event, buffer: Proxy<wl_buffer::WlBuffer>| match event {
wl_buffer::Event::Release => {
buffer.destroy();
let mut my_buffer_count = my_buffer_count.lock().unwrap();
*my_buffer_count -= 1;
if *my_buffer_count == 0 {
(&mut *my_implementation.lock().unwrap())();
}
}
},
(),
)
})
.unwrap()
}
pub fn mmap(&mut self) -> &mut MmapMut {
&mut self.mmap
}
pub fn is_used(&self) -> bool {
*self.buffer_count.lock().unwrap() != 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),
}
}
}