screencapturekit/shareable_content/display.rs
1use crate::cg::CGRect;
2use core::fmt;
3use std::ffi::c_void;
4
5/// Opaque wrapper around `SCDisplay` from `ScreenCaptureKit`
6///
7/// Represents a physical or virtual display that can be captured.
8///
9/// # Examples
10///
11/// ```no_run
12/// use screencapturekit::shareable_content::SCShareableContent;
13///
14/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
15/// let content = SCShareableContent::get()?;
16/// for display in content.displays() {
17/// println!("Display {}: {}x{}",
18/// display.display_id(),
19/// display.width(),
20/// display.height()
21/// );
22/// }
23/// # Ok(())
24/// # }
25/// ```
26#[repr(transparent)]
27pub struct SCDisplay(*const c_void);
28
29impl PartialEq for SCDisplay {
30 fn eq(&self, other: &Self) -> bool {
31 self.0 == other.0
32 }
33}
34
35impl Eq for SCDisplay {}
36
37impl std::hash::Hash for SCDisplay {
38 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39 self.0.hash(state);
40 }
41}
42
43impl SCDisplay {
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 `SCDisplay` pointer transferred
53 /// from the Swift FFI bridge (ownership moves into the returned 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 display ID
68 ///
69 /// # Examples
70 ///
71 /// ```no_run
72 /// # use screencapturekit::shareable_content::SCShareableContent;
73 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
74 /// let content = SCShareableContent::get()?;
75 /// if let Some(display) = content.displays().first() {
76 /// println!("Display ID: {}", display.display_id());
77 /// }
78 /// # Ok(())
79 /// # }
80 /// ```
81 pub fn display_id(&self) -> u32 {
82 unsafe { crate::ffi::sc_display_get_display_id(self.0) }
83 }
84
85 /// Get display frame (position and size)
86 pub fn frame(&self) -> CGRect {
87 let mut x = 0.0;
88 let mut y = 0.0;
89 let mut width = 0.0;
90 let mut height = 0.0;
91 unsafe {
92 crate::ffi::sc_display_get_frame_packed(
93 self.0,
94 &mut x,
95 &mut y,
96 &mut width,
97 &mut height,
98 );
99 }
100 CGRect::new(x, y, width, height)
101 }
102
103 /// Get display height in pixels
104 ///
105 /// # Examples
106 ///
107 /// ```no_run
108 /// # use screencapturekit::shareable_content::SCShareableContent;
109 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
110 /// let content = SCShareableContent::get()?;
111 /// if let Some(display) = content.displays().first() {
112 /// println!("Display resolution: {}x{}", display.width(), display.height());
113 /// }
114 /// # Ok(())
115 /// # }
116 /// ```
117 pub fn height(&self) -> u32 {
118 // FFI returns isize but display dimensions are always positive and fit in u32
119 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
120 unsafe {
121 crate::ffi::sc_display_get_height(self.0) as u32
122 }
123 }
124
125 /// Get display width in pixels
126 pub fn width(&self) -> u32 {
127 // FFI returns isize but display dimensions are always positive and fit in u32
128 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
129 unsafe {
130 crate::ffi::sc_display_get_width(self.0) as u32
131 }
132 }
133}
134
135crate::utils::retained::sc_retained!(
136 SCDisplay,
137 retain = crate::ffi::sc_display_retain,
138 release = crate::ffi::sc_display_release,
139);
140
141// SAFETY: `SCDisplay` wraps an immutable Objective-C ScreenCaptureKit object.
142// ObjC reference counting is atomic and these accessor-only objects are safe to
143// send between and share across threads.
144unsafe impl Send for SCDisplay {}
145unsafe impl Sync for SCDisplay {}
146
147impl fmt::Debug for SCDisplay {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 f.debug_struct("SCDisplay")
150 .field("display_id", &self.display_id())
151 .field("width", &self.width())
152 .field("height", &self.height())
153 .finish()
154 }
155}
156
157impl fmt::Display for SCDisplay {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 write!(
160 f,
161 "Display {} ({}x{})",
162 self.display_id(),
163 self.width(),
164 self.height()
165 )
166 }
167}