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 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 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}