1use std::ffi::c_void;
26use std::ptr::NonNull;
27
28use mtl_foundation::{Referencing, Url};
29use mtl_sys::{msg_send_0, msg_send_1, msg_send_2, sel};
30
31use crate::CommandQueue;
32use crate::Device;
33use crate::enums::{CaptureDestination, CaptureError};
34
35#[repr(transparent)]
43pub struct CaptureDescriptor(NonNull<c_void>);
44
45impl CaptureDescriptor {
46 pub fn alloc() -> Option<Self> {
50 unsafe {
51 let cls = mtl_sys::Class::get("MTLCaptureDescriptor")?;
52 let ptr: *mut c_void = msg_send_0(cls.as_ptr(), sel!(alloc));
53 Self::from_raw(ptr)
54 }
55 }
56
57 pub fn init(&self) -> Option<Self> {
61 unsafe {
62 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(init));
63 Self::from_raw(ptr)
64 }
65 }
66
67 pub fn new() -> Option<Self> {
69 Self::alloc()?.init()
70 }
71
72 #[inline]
78 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
79 NonNull::new(ptr).map(Self)
80 }
81
82 #[inline]
84 pub fn as_raw(&self) -> *mut c_void {
85 self.0.as_ptr()
86 }
87
88 pub fn capture_object(&self) -> Option<*mut c_void> {
92 unsafe {
93 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(captureObject));
94 if ptr.is_null() { None } else { Some(ptr) }
95 }
96 }
97
98 pub fn set_capture_object(&self, object: *const c_void) {
102 unsafe {
103 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setCaptureObject:), object);
104 }
105 }
106
107 pub fn set_capture_device(&self, device: &Device) {
109 self.set_capture_object(device.as_ptr());
110 }
111
112 pub fn set_capture_command_queue(&self, queue: &CommandQueue) {
114 self.set_capture_object(queue.as_ptr());
115 }
116
117 pub fn set_capture_scope(&self, scope: &CaptureScope) {
119 self.set_capture_object(scope.as_ptr());
120 }
121
122 #[inline]
126 pub fn destination(&self) -> CaptureDestination {
127 unsafe { msg_send_0(self.as_ptr(), sel!(destination)) }
128 }
129
130 #[inline]
134 pub fn set_destination(&self, destination: CaptureDestination) {
135 unsafe {
136 msg_send_1::<(), CaptureDestination>(self.as_ptr(), sel!(setDestination:), destination);
137 }
138 }
139
140 pub fn output_url(&self) -> Option<Url> {
144 unsafe {
145 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(outputURL));
146 if ptr.is_null() {
147 return None;
148 }
149 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
150 Url::from_ptr(ptr)
151 }
152 }
153
154 pub fn set_output_url(&self, url: &Url) {
158 unsafe {
159 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setOutputURL:), url.as_ptr());
160 }
161 }
162}
163
164impl Default for CaptureDescriptor {
165 fn default() -> Self {
166 Self::new().expect("failed to create CaptureDescriptor")
167 }
168}
169
170impl Clone for CaptureDescriptor {
171 fn clone(&self) -> Self {
172 unsafe {
173 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(copy));
174 Self::from_raw(ptr).expect("failed to copy CaptureDescriptor")
175 }
176 }
177}
178
179impl Drop for CaptureDescriptor {
180 fn drop(&mut self) {
181 unsafe {
182 msg_send_0::<()>(self.as_ptr(), sel!(release));
183 }
184 }
185}
186
187impl Referencing for CaptureDescriptor {
188 #[inline]
189 fn as_ptr(&self) -> *const c_void {
190 self.0.as_ptr()
191 }
192}
193
194unsafe impl Send for CaptureDescriptor {}
195unsafe impl Sync for CaptureDescriptor {}
196
197impl std::fmt::Debug for CaptureDescriptor {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 f.debug_struct("CaptureDescriptor")
200 .field("destination", &self.destination())
201 .finish()
202 }
203}
204
205#[repr(transparent)]
213pub struct CaptureScope(NonNull<c_void>);
214
215impl CaptureScope {
216 #[inline]
222 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
223 NonNull::new(ptr).map(Self)
224 }
225
226 #[inline]
228 pub fn as_raw(&self) -> *mut c_void {
229 self.0.as_ptr()
230 }
231
232 pub fn device(&self) -> Device {
236 unsafe {
237 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
238 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
239 Device::from_raw(ptr).expect("capture scope has no device")
240 }
241 }
242
243 pub fn command_queue(&self) -> Option<CommandQueue> {
247 unsafe {
248 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(commandQueue));
249 if ptr.is_null() {
250 return None;
251 }
252 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
253 CommandQueue::from_raw(ptr)
254 }
255 }
256
257 pub fn label(&self) -> Option<String> {
261 unsafe {
262 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
263 if ptr.is_null() {
264 return None;
265 }
266 let utf8_ptr: *const std::ffi::c_char =
267 mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
268 if utf8_ptr.is_null() {
269 return None;
270 }
271 let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
272 Some(c_str.to_string_lossy().into_owned())
273 }
274 }
275
276 pub fn set_label(&self, label: &str) {
280 if let Some(ns_label) = mtl_foundation::String::from_str(label) {
281 unsafe {
282 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
283 }
284 }
285 }
286
287 #[inline]
291 pub fn begin_scope(&self) {
292 unsafe {
293 msg_send_0::<()>(self.as_ptr(), sel!(beginScope));
294 }
295 }
296
297 #[inline]
301 pub fn end_scope(&self) {
302 unsafe {
303 msg_send_0::<()>(self.as_ptr(), sel!(endScope));
304 }
305 }
306}
307
308impl Clone for CaptureScope {
309 fn clone(&self) -> Self {
310 unsafe {
311 msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
312 }
313 Self(self.0)
314 }
315}
316
317impl Drop for CaptureScope {
318 fn drop(&mut self) {
319 unsafe {
320 msg_send_0::<()>(self.as_ptr(), sel!(release));
321 }
322 }
323}
324
325impl Referencing for CaptureScope {
326 #[inline]
327 fn as_ptr(&self) -> *const c_void {
328 self.0.as_ptr()
329 }
330}
331
332unsafe impl Send for CaptureScope {}
333unsafe impl Sync for CaptureScope {}
334
335impl std::fmt::Debug for CaptureScope {
336 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
337 f.debug_struct("CaptureScope")
338 .field("label", &self.label())
339 .finish()
340 }
341}
342
343#[repr(transparent)]
353pub struct CaptureManager(NonNull<c_void>);
354
355impl CaptureManager {
356 pub fn shared() -> Option<Self> {
362 unsafe {
363 let cls = mtl_sys::Class::get("MTLCaptureManager")?;
364 let ptr: *mut c_void = msg_send_0(cls.as_ptr(), sel!(sharedCaptureManager));
365 if ptr.is_null() {
366 return None;
367 }
368 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
370 Self::from_raw(ptr)
371 }
372 }
373
374 #[inline]
380 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
381 NonNull::new(ptr).map(Self)
382 }
383
384 #[inline]
386 pub fn as_raw(&self) -> *mut c_void {
387 self.0.as_ptr()
388 }
389
390 #[inline]
394 pub fn is_capturing(&self) -> bool {
395 unsafe { msg_send_0(self.as_ptr(), sel!(isCapturing)) }
396 }
397
398 pub fn default_capture_scope(&self) -> Option<CaptureScope> {
402 unsafe {
403 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(defaultCaptureScope));
404 if ptr.is_null() {
405 return None;
406 }
407 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
408 CaptureScope::from_raw(ptr)
409 }
410 }
411
412 pub fn set_default_capture_scope(&self, scope: &CaptureScope) {
416 unsafe {
417 msg_send_1::<(), *const c_void>(
418 self.as_ptr(),
419 sel!(setDefaultCaptureScope:),
420 scope.as_ptr(),
421 );
422 }
423 }
424
425 pub fn new_capture_scope_with_device(&self, device: &Device) -> Option<CaptureScope> {
429 unsafe {
430 let ptr: *mut c_void = msg_send_1(
431 self.as_ptr(),
432 sel!(newCaptureScopeWithDevice:),
433 device.as_ptr(),
434 );
435 CaptureScope::from_raw(ptr)
436 }
437 }
438
439 pub fn new_capture_scope_with_command_queue(
443 &self,
444 queue: &CommandQueue,
445 ) -> Option<CaptureScope> {
446 unsafe {
447 let ptr: *mut c_void = msg_send_1(
448 self.as_ptr(),
449 sel!(newCaptureScopeWithCommandQueue:),
450 queue.as_ptr(),
451 );
452 CaptureScope::from_raw(ptr)
453 }
454 }
455
456 #[inline]
460 pub fn supports_destination(&self, destination: CaptureDestination) -> bool {
461 unsafe { msg_send_1(self.as_ptr(), sel!(supportsDestination:), destination) }
462 }
463
464 pub fn start_capture(&self, descriptor: &CaptureDescriptor) -> Result<(), CaptureError> {
470 unsafe {
471 let mut error: *mut c_void = std::ptr::null_mut();
472 let success: bool = msg_send_2(
473 self.as_ptr(),
474 sel!(startCaptureWithDescriptor: error:),
475 descriptor.as_ptr(),
476 &mut error as *mut *mut c_void,
477 );
478 if success {
479 Ok(())
480 } else {
481 if !error.is_null() {
483 let code: mtl_foundation::Integer =
484 mtl_sys::msg_send_0(error as *const c_void, sel!(code));
485 Err(CaptureError(code))
486 } else {
487 Err(CaptureError::NOT_SUPPORTED)
488 }
489 }
490 }
491 }
492
493 #[inline]
497 pub fn start_capture_with_device(&self, device: &Device) {
498 unsafe {
499 msg_send_1::<(), *const c_void>(
500 self.as_ptr(),
501 sel!(startCaptureWithDevice:),
502 device.as_ptr(),
503 );
504 }
505 }
506
507 #[inline]
511 pub fn start_capture_with_command_queue(&self, queue: &CommandQueue) {
512 unsafe {
513 msg_send_1::<(), *const c_void>(
514 self.as_ptr(),
515 sel!(startCaptureWithCommandQueue:),
516 queue.as_ptr(),
517 );
518 }
519 }
520
521 #[inline]
525 pub fn start_capture_with_scope(&self, scope: &CaptureScope) {
526 unsafe {
527 msg_send_1::<(), *const c_void>(
528 self.as_ptr(),
529 sel!(startCaptureWithScope:),
530 scope.as_ptr(),
531 );
532 }
533 }
534
535 #[inline]
539 pub fn stop_capture(&self) {
540 unsafe {
541 msg_send_0::<()>(self.as_ptr(), sel!(stopCapture));
542 }
543 }
544}
545
546impl Clone for CaptureManager {
547 fn clone(&self) -> Self {
548 unsafe {
549 msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
550 }
551 Self(self.0)
552 }
553}
554
555impl Drop for CaptureManager {
556 fn drop(&mut self) {
557 unsafe {
558 msg_send_0::<()>(self.as_ptr(), sel!(release));
559 }
560 }
561}
562
563impl Referencing for CaptureManager {
564 #[inline]
565 fn as_ptr(&self) -> *const c_void {
566 self.0.as_ptr()
567 }
568}
569
570unsafe impl Send for CaptureManager {}
571unsafe impl Sync for CaptureManager {}
572
573impl std::fmt::Debug for CaptureManager {
574 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
575 f.debug_struct("CaptureManager")
576 .field("is_capturing", &self.is_capturing())
577 .finish()
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584
585 #[test]
586 fn test_capture_descriptor_size() {
587 assert_eq!(
588 std::mem::size_of::<CaptureDescriptor>(),
589 std::mem::size_of::<*mut c_void>()
590 );
591 }
592
593 #[test]
594 fn test_capture_scope_size() {
595 assert_eq!(
596 std::mem::size_of::<CaptureScope>(),
597 std::mem::size_of::<*mut c_void>()
598 );
599 }
600
601 #[test]
602 fn test_capture_manager_size() {
603 assert_eq!(
604 std::mem::size_of::<CaptureManager>(),
605 std::mem::size_of::<*mut c_void>()
606 );
607 }
608
609 #[test]
610 fn test_shared_capture_manager() {
611 if let Some(manager) = CaptureManager::shared() {
613 assert!(!manager.is_capturing());
615 }
616 }
617
618 }