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
277#[derive(Debug)]
278pub struct ScreencopyState {
279    capturer: Capturer,
280}
281
282impl ScreencopyState {
283    pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
284    where
285        D: 'static,
286        D: Dispatch<ext_image_copy_capture_manager_v1::ExtImageCopyCaptureManagerV1, GlobalData>,
287        D: Dispatch<ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1, GlobalData>,
288        D: Dispatch<ext_foreign_toplevel_image_capture_source_manager_v1::ExtForeignToplevelImageCaptureSourceManagerV1, GlobalData>,
289        D: Dispatch<zcosmic_workspace_image_capture_source_manager_v1::ZcosmicWorkspaceImageCaptureSourceManagerV1, GlobalData>,
290    {
291        let image_copy_capture_manager = globals.bind(qh, 1..=1, GlobalData).ok();
292        let output_source_manager = globals.bind(qh, 1..=1, GlobalData).ok();
293        let foreign_toplevel_source_manager = globals.bind(qh, 1..=1, GlobalData).ok();
294        let workspace_source_manager = globals.bind(qh, 1..=1, GlobalData).ok();
295
296        let capturer = Capturer(Arc::new(CapturerInner {
297            image_copy_capture_manager,
298            output_source_manager,
299            foreign_toplevel_source_manager,
300            workspace_source_manager,
301        }));
302
303        Self { capturer }
304    }
305
306    pub fn capturer(&self) -> &Capturer {
307        &self.capturer
308    }
309}
310
311pub trait ScreencopyHandler: Sized {
312    fn screencopy_state(&mut self) -> &mut ScreencopyState;
313
314    fn init_done(
315        &mut self,
316        conn: &Connection,
317        qh: &QueueHandle<Self>,
318        session: &CaptureSession,
319        formats: &Formats,
320    );
321
322    fn stopped(&mut self, conn: &Connection, qh: &QueueHandle<Self>, session: &CaptureSession);
323
324    fn ready(
325        &mut self,
326        conn: &Connection,
327        qh: &QueueHandle<Self>,
328        screencopy_frame: &CaptureFrame,
329        frame: Frame,
330    );
331
332    fn failed(
333        &mut self,
334        conn: &Connection,
335        qh: &QueueHandle<Self>,
336        screencopy_frame: &CaptureFrame,
337        reason: WEnum<FailureReason>,
338    );
339
340    fn cursor_enter(
341        &mut self,
342        _conn: &Connection,
343        _qh: &QueueHandle<Self>,
344        _cursor_session: &CaptureCursorSession,
345    ) {
346    }
347
348    fn cursor_leave(
349        &mut self,
350        _conn: &Connection,
351        _qh: &QueueHandle<Self>,
352        _cursor_session: &CaptureCursorSession,
353    ) {
354    }
355
356    fn cursor_position(
357        &mut self,
358        _conn: &Connection,
359        _qh: &QueueHandle<Self>,
360        _cursor_session: &CaptureCursorSession,
361        _x: i32,
362        _y: i32,
363    ) {
364    }
365
366    fn cursor_hotspot(
367        &mut self,
368        _conn: &Connection,
369        _qh: &QueueHandle<Self>,
370        _cursor_session: &CaptureCursorSession,
371        _x: i32,
372        _y: i32,
373    ) {
374    }
375}
376
377pub trait ScreencopySessionDataExt {
378    fn screencopy_session_data(&self) -> &ScreencopySessionData;
379}
380
381#[derive(Default)]
382pub struct ScreencopySessionData {
383    formats: Mutex<Formats>,
384    session: OnceLock<Weak<CaptureSessionInner>>,
385}
386
387impl ScreencopySessionDataExt for ScreencopySessionData {
388    fn screencopy_session_data(&self) -> &ScreencopySessionData {
389        self
390    }
391}
392
393#[derive(Default)]
394pub struct ScreencopyFrameData {
395    frame: Mutex<Frame>,
396    session: OnceLock<Weak<CaptureSessionInner>>,
397}
398
399pub trait ScreencopyFrameDataExt {
400    fn screencopy_frame_data(&self) -> &ScreencopyFrameData;
401}
402
403impl ScreencopyFrameDataExt for ScreencopyFrameData {
404    fn screencopy_frame_data(&self) -> &ScreencopyFrameData {
405        self
406    }
407}
408
409#[derive(Default)]
410pub struct ScreencopyCursorSessionData {
411    session: OnceLock<Weak<CaptureCursorSessionInner>>,
412}
413
414pub trait ScreencopyCursorSessionDataExt {
415    fn screencopy_cursor_session_data(&self) -> &ScreencopyCursorSessionData;
416}
417
418impl ScreencopyCursorSessionDataExt for ScreencopyCursorSessionData {
419    fn screencopy_cursor_session_data(&self) -> &ScreencopyCursorSessionData {
420        self
421    }
422}