smithay_client_toolkit/shm/
raw.rs1use rustix::{
7 io::Errno,
8 shm::{Mode, OFlags},
9};
10use std::{
11 fs::File,
12 io,
13 ops::Deref,
14 os::unix::prelude::{AsFd, BorrowedFd, OwnedFd},
15 sync::Arc,
16 time::{SystemTime, UNIX_EPOCH},
17};
18
19use memmap2::MmapMut;
20use wayland_client::{
21 backend::ObjectData,
22 protocol::{wl_buffer, wl_shm, wl_shm_pool},
23 Dispatch, Proxy, QueueHandle, WEnum,
24};
25
26use crate::globals::ProvidesBoundGlobal;
27
28use super::CreatePoolError;
29
30#[derive(Debug)]
36pub struct RawPool {
37 pool: DestroyOnDropPool,
38 len: usize,
39 mem_file: File,
40 mmap: MmapMut,
41}
42
43impl RawPool {
44 pub fn new(
45 len: usize,
46 shm: &impl ProvidesBoundGlobal<wl_shm::WlShm, 1>,
47 ) -> Result<RawPool, CreatePoolError> {
48 let shm = shm.bound_global()?;
49 let shm_fd = RawPool::create_shm_fd()?;
50 let mem_file = File::from(shm_fd);
51 mem_file.set_len(len as u64)?;
52
53 let pool = shm
54 .send_constructor(
55 wl_shm::Request::CreatePool { fd: mem_file.as_fd(), size: len as i32 },
56 Arc::new(ShmPoolData),
57 )
58 .unwrap_or_else(|_| Proxy::inert(shm.backend().clone()));
59 let mmap = unsafe { MmapMut::map_mut(&mem_file)? };
60
61 Ok(RawPool { pool: DestroyOnDropPool(pool), len, mem_file, mmap })
62 }
63
64 pub fn resize(&mut self, size: usize) -> io::Result<()> {
69 if size > self.len {
70 self.len = size;
71 self.mem_file.set_len(size as u64)?;
72 self.pool.resize(size as i32);
73 self.mmap = unsafe { MmapMut::map_mut(&self.mem_file) }?;
74 }
75
76 Ok(())
77 }
78
79 pub fn mmap(&mut self) -> &mut MmapMut {
81 &mut self.mmap
82 }
83
84 #[allow(clippy::len_without_is_empty)]
86 pub fn len(&self) -> usize {
87 self.len
88 }
89
90 #[allow(clippy::too_many_arguments)]
104 pub fn create_buffer<D, U>(
105 &mut self,
106 offset: i32,
107 width: i32,
108 height: i32,
109 stride: i32,
110 format: wl_shm::Format,
111 udata: U,
112 qh: &QueueHandle<D>,
113 ) -> wl_buffer::WlBuffer
114 where
115 D: Dispatch<wl_buffer::WlBuffer, U> + 'static,
116 U: Send + Sync + 'static,
117 {
118 self.pool.create_buffer(offset, width, height, stride, format, qh, udata)
119 }
120
121 #[allow(clippy::too_many_arguments)]
126 pub fn create_buffer_raw(
127 &mut self,
128 offset: i32,
129 width: i32,
130 height: i32,
131 stride: i32,
132 format: wl_shm::Format,
133 data: Arc<dyn ObjectData + 'static>,
134 ) -> wl_buffer::WlBuffer {
135 self.pool
136 .send_constructor(
137 wl_shm_pool::Request::CreateBuffer {
138 offset,
139 width,
140 height,
141 stride,
142 format: WEnum::Value(format),
143 },
144 data,
145 )
146 .unwrap_or_else(|_| Proxy::inert(self.pool.backend().clone()))
147 }
148
149 pub fn pool(&self) -> &wl_shm_pool::WlShmPool {
151 &self.pool
152 }
153}
154
155impl AsFd for RawPool {
156 fn as_fd(&self) -> BorrowedFd {
157 self.mem_file.as_fd()
158 }
159}
160
161impl From<RawPool> for OwnedFd {
162 fn from(pool: RawPool) -> Self {
163 pool.mem_file.into()
164 }
165}
166
167impl io::Write for RawPool {
168 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
169 io::Write::write(&mut self.mem_file, buf)
170 }
171
172 fn flush(&mut self) -> io::Result<()> {
173 io::Write::flush(&mut self.mem_file)
174 }
175}
176
177impl io::Seek for RawPool {
178 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
179 io::Seek::seek(&mut self.mem_file, pos)
180 }
181}
182
183impl RawPool {
184 fn create_shm_fd() -> io::Result<OwnedFd> {
185 #[cfg(target_os = "linux")]
186 {
187 match RawPool::create_memfd() {
188 Ok(fd) => return Ok(fd),
189
190 Err(Errno::NOSYS) => (),
192
193 Err(err) => return Err(Into::<io::Error>::into(err)),
194 };
195 }
196
197 let time = SystemTime::now();
198 let mut mem_file_handle = format!(
199 "/smithay-client-toolkit-{}",
200 time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
201 );
202
203 loop {
204 let flags = OFlags::CREATE | OFlags::EXCL | OFlags::RDWR;
205
206 let mode = Mode::RUSR | Mode::WUSR;
207
208 match rustix::shm::open(mem_file_handle.as_str(), flags, mode) {
209 Ok(fd) => match rustix::shm::unlink(mem_file_handle.as_str()) {
210 Ok(_) => return Ok(fd),
211
212 Err(errno) => {
213 return Err(errno.into());
214 }
215 },
216
217 Err(Errno::EXIST) => {
218 let time = SystemTime::now();
220
221 mem_file_handle = format!(
222 "/smithay-client-toolkit-{}",
223 time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
224 );
225
226 continue;
227 }
228
229 Err(Errno::INTR) => continue,
230
231 Err(err) => return Err(err.into()),
232 }
233 }
234 }
235
236 #[cfg(target_os = "linux")]
237 fn create_memfd() -> rustix::io::Result<OwnedFd> {
238 use std::ffi::CStr;
239
240 use rustix::fs::{MemfdFlags, SealFlags};
241
242 loop {
243 let name = CStr::from_bytes_with_nul(b"smithay-client-toolkit\0").unwrap();
244 let flags = MemfdFlags::ALLOW_SEALING | MemfdFlags::CLOEXEC;
245
246 match rustix::fs::memfd_create(name, flags) {
247 Ok(fd) => {
248 let _ = rustix::fs::fcntl_add_seals(&fd, SealFlags::SHRINK | SealFlags::SEAL);
250 return Ok(fd);
251 }
252
253 Err(Errno::INTR) => continue,
254
255 Err(err) => return Err(err),
256 }
257 }
258 }
259}
260
261#[derive(Debug)]
262struct DestroyOnDropPool(wl_shm_pool::WlShmPool);
263
264impl Deref for DestroyOnDropPool {
265 type Target = wl_shm_pool::WlShmPool;
266
267 fn deref(&self) -> &Self::Target {
268 &self.0
269 }
270}
271
272impl Drop for DestroyOnDropPool {
273 fn drop(&mut self) {
274 self.0.destroy();
275 }
276}
277
278#[derive(Debug)]
279struct ShmPoolData;
280
281impl ObjectData for ShmPoolData {
282 fn event(
283 self: Arc<Self>,
284 _: &wayland_client::backend::Backend,
285 _: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>,
286 ) -> Option<Arc<(dyn ObjectData + 'static)>> {
287 unreachable!("wl_shm_pool has no events")
288 }
289
290 fn destroyed(&self, _: wayland_client::backend::ObjectId) {}
291}