Skip to main content

screencapturekit/shareable_content/
running_application.rs

1use core::fmt;
2use std::ffi::c_void;
3
4use crate::utils::ffi_string::ffi_string_owned_or_empty;
5
6/// Wrapper around `SCRunningApplication` from `ScreenCaptureKit`
7///
8/// Represents a running application that can be captured.
9///
10/// # Examples
11///
12/// ```no_run
13/// use screencapturekit::shareable_content::SCShareableContent;
14///
15/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
16/// let content = SCShareableContent::get()?;
17/// for app in content.applications() {
18///     println!("App: {} (PID: {})",
19///         app.application_name(),
20///         app.process_id()
21///     );
22/// }
23/// # Ok(())
24/// # }
25/// ```
26#[repr(transparent)]
27pub struct SCRunningApplication(*const c_void);
28
29impl PartialEq for SCRunningApplication {
30    fn eq(&self, other: &Self) -> bool {
31        self.0 == other.0
32    }
33}
34
35impl Eq for SCRunningApplication {}
36
37impl std::hash::Hash for SCRunningApplication {
38    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39        self.0.hash(state);
40    }
41}
42
43impl SCRunningApplication {
44    /// Create from raw pointer (used internally by shareable content)
45    pub(crate) unsafe fn from_ptr(ptr: *const c_void) -> Self {
46        Self(ptr)
47    }
48
49    /// Create from an FFI-owned (retained) pointer, returning `None` if null.
50    ///
51    /// # Safety
52    /// `ptr` must be null or a valid retained `SCRunningApplication` pointer
53    /// transferred from the Swift FFI bridge (ownership moves into the wrapper).
54    pub(crate) unsafe fn from_retained_ptr(ptr: *const c_void) -> Option<Self> {
55        if ptr.is_null() {
56            None
57        } else {
58            Some(unsafe { Self::from_ptr(ptr) })
59        }
60    }
61
62    /// Get the raw pointer (used internally)
63    pub(crate) fn as_ptr(&self) -> *const c_void {
64        self.0
65    }
66
67    /// Get process ID
68    pub fn process_id(&self) -> i32 {
69        unsafe { crate::ffi::sc_running_application_get_process_id(self.0) }
70    }
71
72    /// Get application name
73    pub fn application_name(&self) -> String {
74        unsafe {
75            ffi_string_owned_or_empty(|| {
76                crate::ffi::sc_running_application_get_application_name_owned(self.0)
77            })
78        }
79    }
80
81    /// Get bundle identifier
82    pub fn bundle_identifier(&self) -> String {
83        unsafe {
84            ffi_string_owned_or_empty(|| {
85                crate::ffi::sc_running_application_get_bundle_identifier_owned(self.0)
86            })
87        }
88    }
89}
90
91crate::utils::retained::sc_retained!(
92    SCRunningApplication,
93    retain = crate::ffi::sc_running_application_retain,
94    release = crate::ffi::sc_running_application_release,
95);
96
97impl fmt::Debug for SCRunningApplication {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        f.debug_struct("SCRunningApplication")
100            .field("bundle_identifier", &self.bundle_identifier())
101            .field("application_name", &self.application_name())
102            .field("process_id", &self.process_id())
103            .finish()
104    }
105}
106
107impl fmt::Display for SCRunningApplication {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        write!(
110            f,
111            "{} ({}) [PID: {}]",
112            self.application_name(),
113            self.bundle_identifier(),
114            self.process_id()
115        )
116    }
117}
118
119// SAFETY: `SCRunningApplication` wraps an immutable Objective-C ScreenCaptureKit
120// object. ObjC reference counting is atomic and these accessor-only objects are
121// safe to send between and share across threads.
122unsafe impl Send for SCRunningApplication {}
123unsafe impl Sync for SCRunningApplication {}