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