Skip to main content

cosmic_client_toolkit/screencopy/
mod.rs

1use cosmic_protocols::image_capture_source::v1::client::zcosmic_workspace_image_capture_source_manager_v1;
2use std::{
3    sync::{Arc, Mutex, OnceLock, Weak},
4    time::Duration,
5};
6use wayland_client::{
7    Connection, Dispatch, Proxy, QueueHandle, WEnum,
8    globals::GlobalList,
9    protocol::{wl_buffer, wl_output::Transform, wl_pointer, wl_shm},
10};
11use wayland_protocols::ext::{
12    image_capture_source::v1::client::{
13        ext_foreign_toplevel_image_capture_source_manager_v1, ext_image_capture_source_v1,
14        ext_output_image_capture_source_manager_v1,
15    },
16    image_copy_capture::v1::client::{
17        ext_image_copy_capture_cursor_session_v1, ext_image_copy_capture_frame_v1,
18        ext_image_copy_capture_manager_v1, ext_image_copy_capture_session_v1,
19    },
20};
21
22pub use ext_image_copy_capture_frame_v1::FailureReason;
23pub use ext_image_copy_capture_manager_v1::Options as CaptureOptions;
24
25use crate::GlobalData;
26
27mod capture_source;
28pub use capture_source::{CaptureSource, CaptureSourceError, CaptureSourceKind};
29mod dispatch;
30
31#[derive(Clone, Debug)]
32pub struct Rect {
33    pub x: i32,
34    pub y: i32,
35    pub width: i32,
36    pub height: i32,
37}
38
39#[derive(Clone, Debug)]
40pub struct Frame {
41    pub transform: WEnum<Transform>,
42    pub damage: Vec<Rect>,
43    // XXX monotonic? Is this used elsewhere in wayland?
44    pub present_time: Option<Duration>,
45}
46
47impl Default for Frame {
48    fn default() -> Self {
49        Self {
50            transform: WEnum::Value(Transform::Normal),
51            damage: Vec::new(),
52            present_time: None,
53        }
54    }
55}
56
57#[derive(Clone, Debug, Default, PartialEq, Eq)]
58pub struct Formats {
59    pub buffer_size: (u32, u32),
60    pub shm_formats: Vec<wl_shm::Format>,
61    pub dmabuf_device: Option<libc::dev_t>,
62    pub dmabuf_formats: Vec<(u32, Vec<u64>)>,
63}
64
65#[derive(Debug)]
66struct CapturerInner {
67    image_copy_capture_manager: Option<ext_image_copy_capture_manager_v1::ExtImageCopyCaptureManagerV1>,
68    output_source_manager: Option<ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1>,
69    foreign_toplevel_source_manager: Option<ext_foreign_toplevel_image_capture_source_manager_v1::ExtForeignToplevelImageCaptureSourceManagerV1>,
70    workspace_source_manager: Option<zcosmic_workspace_image_capture_source_manager_v1::ZcosmicWorkspaceImageCaptureSourceManagerV1>,
71}
72
73impl Drop for CapturerInner {
74    fn drop(&mut self) {
75        if let Some(manager) = &self.image_copy_capture_manager {
76            manager.destroy();
77        }
78        if let Some(manager) = &self.output_source_manager {
79            manager.destroy();
80        }
81        if let Some(manager) = &self.foreign_toplevel_source_manager {
82            manager.destroy();
83        }
84        if let Some(manager) = &self.workspace_source_manager {
85            manager.destroy();
86        }
87    }
88}
89
90#[derive(Clone, Debug)]
91pub struct Capturer(Arc<CapturerInner>);
92
93impl Capturer {
94    // TODO check supported capture types
95
96    pub fn create_session<D, U>(
97        &self,
98        source: &CaptureSource,
99        options: CaptureOptions,
100        qh: &QueueHandle<D>,
101        udata: U,
102    ) -> Result<CaptureSession, CaptureSourceError>
103    where
104        D: 'static,
105        D: Dispatch<ext_image_capture_source_v1::ExtImageCaptureSourceV1, GlobalData>,
106        D: Dispatch<ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1, U>,
107        U: ScreencopySessionDataExt + Send + Sync + 'static,
108    {
109        let source = source.create_source(self, qh)?;
110        Ok(CaptureSession(Arc::new_cyclic(|weak_session| {
111            udata
112                .screencopy_session_data()
113                .session
114                .set(weak_session.clone())
115                .unwrap();
116            CaptureSessionInner {
117                session: self
118                    .0
119                    .image_copy_capture_manager
120                    .as_ref()
121                    .expect("ext capture source with no image capture copy manager")
122                    .create_session(&source.0, options, qh, udata),
123            }
124        })))
125    }
126
127    pub fn create_cursor_session<D, U>(
128        &self,
129        source: &CaptureSource,
130        pointer: &wl_pointer::WlPointer,
131        qh: &QueueHandle<D>,
132        udata: U,
133    ) -> Result<CaptureCursorSession, CaptureSourceError>
134    where
135        D: 'static,
136        D: Dispatch<ext_image_capture_source_v1::ExtImageCaptureSourceV1, GlobalData>,
137        D: Dispatch<
138                ext_image_copy_capture_cursor_session_v1::ExtImageCopyCaptureCursorSessionV1,
139                U,
140            >,
141        U: ScreencopyCursorSessionDataExt + Send + Sync + 'static,
142    {
143        let source = source.create_source(self, qh)?;
144        Ok(CaptureCursorSession(Arc::new_cyclic(|weak_session| {
145            udata
146                .screencopy_cursor_session_data()
147                .session
148                .set(weak_session.clone())
149                .unwrap();
150            CaptureCursorSessionInner {
151                session: self
152                    .0
153                    .image_copy_capture_manager
154                    .as_ref()
155                    .expect("ext capture source with no image capture copy manager")
156                    .create_pointer_cursor_session(&source.0, pointer, qh, udata),
157            }
158        })))
159    }
160}
161
162#[derive(Debug, PartialEq, Eq, Hash)]
163struct CaptureSessionInner {
164    session: ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
165}
166
167impl Drop for CaptureSessionInner {
168    fn drop(&mut self) {
169        self.session.destroy();
170    }
171}
172
173#[derive(Clone, Debug, PartialEq, Eq, Hash)]
174pub struct CaptureSession(Arc<CaptureSessionInner>);
175
176impl CaptureSession {
177    pub fn capture<D, U>(
178        &self,
179        buffer: &wl_buffer::WlBuffer,
180        buffer_damage: &[Rect],
181        qh: &QueueHandle<D>,
182        udata: U,
183    ) -> CaptureFrame
184    where
185        D: 'static,
186        D: Dispatch<ext_image_copy_capture_frame_v1::ExtImageCopyCaptureFrameV1, U>,
187        U: ScreencopyFrameDataExt + Send + Sync + 'static,
188    {
189        udata
190            .screencopy_frame_data()
191            .session
192            .set(Arc::downgrade(&self.0))
193            .unwrap();
194        let frame = self.0.session.create_frame(qh, udata);
195        frame.attach_buffer(buffer);
196        for Rect {
197            x,
198            y,
199            width,
200            height,
201        } in buffer_damage
202        {
203            frame.damage_buffer(*x, *y, *width, *height);
204        }
205        frame.capture();
206        CaptureFrame { frame }
207    }
208
209    pub fn data<U: Send + Sync + 'static>(&self) -> Option<&U> {
210        self.0.session.data()
211    }
212}
213
214#[derive(Clone, Debug, PartialEq, Eq, Hash)]
215pub struct CaptureFrame {
216    frame: ext_image_copy_capture_frame_v1::ExtImageCopyCaptureFrameV1,
217}
218
219impl CaptureFrame {
220    pub fn session<U: ScreencopyFrameDataExt + Send + Sync + 'static>(
221        &self,
222    ) -> Option<CaptureSession> {
223        Some(CaptureSession(
224            self.data::<U>()?
225                .screencopy_frame_data()
226                .session
227                .get()
228                .unwrap()
229                .upgrade()?,
230        ))
231    }
232
233    pub fn data<U: Send + Sync + 'static>(&self) -> Option<&U> {
234        self.frame.data()
235    }
236}
237
238#[derive(Debug, PartialEq, Eq, Hash)]
239struct CaptureCursorSessionInner {
240    session: ext_image_copy_capture_cursor_session_v1::ExtImageCopyCaptureCursorSessionV1,
241}
242
243impl Drop for CaptureCursorSessionInner {
244    fn drop(&mut self) {
245        self.session.destroy();
246    }
247}
248
249#[derive(Clone, Debug, PartialEq, Eq, Hash)]
250pub struct CaptureCursorSession(Arc<CaptureCursorSessionInner>);
251
252impl CaptureCursorSession {
253    pub fn capture_session<D, U>(
254        &self,
255        qh: &QueueHandle<D>,
256        udata: U,
257    ) -> Result<CaptureSession, CaptureSourceError>
258    where
259        D: 'static,
260        D: Dispatch<ext_image_capture_source_v1::ExtImageCaptureSourceV1, GlobalData>,
261        D: Dispatch<ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1, U>,
262        U: ScreencopySessionDataExt + Send + Sync + 'static,
263    {
264        Ok(CaptureSession(Arc::new_cyclic(|weak_session| {
265            udata
266                .screencopy_session_data()
267                .session
268                .set(weak_session.clone())
269                .unwrap();
270            CaptureSessionInner {
271                session: self.0.session.get_capture_session(qh, udata),
272            }
273        })))
274    }
275
276    pub fn data<U: Send + Sync + 'static>(&self) -> Option<&U> {
277        self.0.session.data()
278    }
279}
280
281#[derive(Debug)]
282pub struct ScreencopyState {
283    capturer: Capturer,
284}
285
286impl ScreencopyState {
287    pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
288    where
289        D: 'static,
290        D: Dispatch<ext_image_copy_capture_manager_v1::ExtImageCopyCaptureManagerV1, GlobalData>,
291        D: Dispatch<ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1, GlobalData>,
292        D: Dispatch<ext_foreign_toplevel_image_capture_source_manager_v1::ExtForeignToplevelImageCaptureSourceManagerV1, GlobalData>,
293        D: Dispatch<zcosmic_workspace_image_capture_source_manager_v1::ZcosmicWorkspaceImageCaptureSourceManagerV1, GlobalData>,
294    {
295        let image_copy_capture_manager = globals.bind(qh, 1..=1, GlobalData).ok();
296        let output_source_manager = globals.bind(qh, 1..=1, GlobalData).ok();
297        let foreign_toplevel_source_manager = globals.bind(qh, 1..=1, GlobalData).ok();
298        let workspace_source_manager = globals.bind(qh, 1..=1, GlobalData).ok();
299
300        let capturer = Capturer(Arc::new(CapturerInner {
301            image_copy_capture_manager,
302            output_source_manager,
303            foreign_toplevel_source_manager,
304            workspace_source_manager,
305        }));
306
307        Self { capturer }
308    }
309
310    pub fn capturer(&self) -> &Capturer {
311        &self.capturer
312    }
313}
314
315pub trait ScreencopyHandler: Sized {
316    fn screencopy_state(&mut self) -> &mut ScreencopyState;
317
318    fn init_done(
319        &mut self,
320        conn: &Connection,
321        qh: &QueueHandle<Self>,
322        session: &CaptureSession,
323        formats: &Formats,
324    );
325
326    fn stopped(&mut self, conn: &Connection, qh: &QueueHandle<Self>, session: &CaptureSession);
327
328    fn ready(
329        &mut self,
330        conn: &Connection,
331        qh: &QueueHandle<Self>,
332        screencopy_frame: &CaptureFrame,
333        frame: Frame,
334    );
335
336    fn failed(
337        &mut self,
338        conn: &Connection,
339        qh: &QueueHandle<Self>,
340        screencopy_frame: &CaptureFrame,
341        reason: WEnum<FailureReason>,
342    );
343
344    fn cursor_enter(
345        &mut self,
346        _conn: &Connection,
347        _qh: &QueueHandle<Self>,
348        _cursor_session: &CaptureCursorSession,
349    ) {
350    }
351
352    fn cursor_leave(
353        &mut self,
354        _conn: &Connection,
355        _qh: &QueueHandle<Self>,
356        _cursor_session: &CaptureCursorSession,
357    ) {
358    }
359
360    fn cursor_position(
361        &mut self,
362        _conn: &Connection,
363        _qh: &QueueHandle<Self>,
364        _cursor_session: &CaptureCursorSession,
365        _x: i32,
366        _y: i32,
367    ) {
368    }
369
370    fn cursor_hotspot(
371        &mut self,
372        _conn: &Connection,
373        _qh: &QueueHandle<Self>,
374        _cursor_session: &CaptureCursorSession,
375        _x: i32,
376        _y: i32,
377    ) {
378    }
379}
380
381pub trait ScreencopySessionDataExt {
382    fn screencopy_session_data(&self) -> &ScreencopySessionData;
383}
384
385#[derive(Default)]
386pub struct ScreencopySessionData {
387    formats: Mutex<Formats>,
388    session: OnceLock<Weak<CaptureSessionInner>>,
389}
390
391impl ScreencopySessionDataExt for ScreencopySessionData {
392    fn screencopy_session_data(&self) -> &ScreencopySessionData {
393        self
394    }
395}
396
397#[derive(Default)]
398pub struct ScreencopyFrameData {
399    frame: Mutex<Frame>,
400    session: OnceLock<Weak<CaptureSessionInner>>,
401}
402
403pub trait ScreencopyFrameDataExt {
404    fn screencopy_frame_data(&self) -> &ScreencopyFrameData;
405}
406
407impl ScreencopyFrameDataExt for ScreencopyFrameData {
408    fn screencopy_frame_data(&self) -> &ScreencopyFrameData {
409        self
410    }
411}
412
413#[derive(Default)]
414pub struct ScreencopyCursorSessionData {
415    session: OnceLock<Weak<CaptureCursorSessionInner>>,
416}
417
418pub trait ScreencopyCursorSessionDataExt {
419    fn screencopy_cursor_session_data(&self) -> &ScreencopyCursorSessionData;
420}
421
422impl ScreencopyCursorSessionDataExt for ScreencopyCursorSessionData {
423    fn screencopy_cursor_session_data(&self) -> &ScreencopyCursorSessionData {
424        self
425    }
426}