screencapturekit/stream/configuration/dimensions.rs
1//! Dimension and scaling configuration for stream capture
2//!
3//! This module provides methods to configure the output dimensions, scaling behavior,
4//! and source/destination rectangles for captured streams.
5
6use crate::cg::CGRect;
7#[cfg(feature = "macos_14_0")]
8use crate::error::{SCError, SCResult};
9
10use super::internal::SCStreamConfiguration;
11
12impl SCStreamConfiguration {
13 /// Set the output width in pixels
14 ///
15 /// The width determines the width of captured frames.
16 ///
17 /// # Examples
18 ///
19 /// ```
20 /// use screencapturekit::prelude::*;
21 ///
22 /// let mut config = SCStreamConfiguration::default();
23 /// config.set_width(1920);
24 /// assert_eq!(config.width(), 1920);
25 /// ```
26 pub fn set_width(&mut self, width: u32) -> &mut Self {
27 // FFI expects isize; u32 may wrap on 32-bit platforms (acceptable)
28 #[allow(clippy::cast_possible_wrap)]
29 unsafe {
30 crate::ffi::sc_stream_configuration_set_width(self.as_ptr(), width as isize);
31 }
32 self
33 }
34
35 /// Set the output width in pixels (builder pattern)
36 ///
37 /// # Examples
38 ///
39 /// ```
40 /// use screencapturekit::prelude::*;
41 ///
42 /// let config = SCStreamConfiguration::new()
43 /// .with_width(1920)
44 /// .with_height(1080);
45 /// ```
46 #[must_use]
47 pub fn with_width(mut self, width: u32) -> Self {
48 self.set_width(width);
49 self
50 }
51
52 /// Get the configured output width in pixels
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// use screencapturekit::prelude::*;
58 ///
59 /// let mut config = SCStreamConfiguration::default();
60 /// config.set_width(1920);
61 /// assert_eq!(config.width(), 1920);
62 /// ```
63 pub fn width(&self) -> u32 {
64 // FFI returns isize but width is always positive and fits in u32
65 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
66 unsafe {
67 crate::ffi::sc_stream_configuration_get_width(self.as_ptr()) as u32
68 }
69 }
70
71 /// Set the output height in pixels
72 ///
73 /// The height determines the height of captured frames.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use screencapturekit::prelude::*;
79 ///
80 /// let mut config = SCStreamConfiguration::default();
81 /// config.set_height(1080);
82 /// assert_eq!(config.height(), 1080);
83 /// ```
84 pub fn set_height(&mut self, height: u32) -> &mut Self {
85 // FFI expects isize; u32 may wrap on 32-bit platforms (acceptable)
86 #[allow(clippy::cast_possible_wrap)]
87 unsafe {
88 crate::ffi::sc_stream_configuration_set_height(self.as_ptr(), height as isize);
89 }
90 self
91 }
92
93 /// Set the output height in pixels (builder pattern)
94 ///
95 /// # Examples
96 ///
97 /// ```
98 /// use screencapturekit::prelude::*;
99 ///
100 /// let config = SCStreamConfiguration::new()
101 /// .with_width(1920)
102 /// .with_height(1080);
103 /// ```
104 #[must_use]
105 pub fn with_height(mut self, height: u32) -> Self {
106 self.set_height(height);
107 self
108 }
109
110 /// Get the configured output height in pixels
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use screencapturekit::prelude::*;
116 ///
117 /// let mut config = SCStreamConfiguration::default();
118 /// config.set_height(1080);
119 /// assert_eq!(config.height(), 1080);
120 /// ```
121 pub fn height(&self) -> u32 {
122 // FFI returns isize but height is always positive and fits in u32
123 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
124 unsafe {
125 crate::ffi::sc_stream_configuration_get_height(self.as_ptr()) as u32
126 }
127 }
128
129 /// Enable or disable scaling to fit the output dimensions
130 ///
131 /// When enabled, the source content will be scaled to fit within the
132 /// configured width and height, potentially changing aspect ratio.
133 ///
134 /// # Examples
135 ///
136 /// ```
137 /// use screencapturekit::prelude::*;
138 ///
139 /// let mut config = SCStreamConfiguration::default();
140 /// config.set_scales_to_fit(true);
141 /// assert!(config.scales_to_fit());
142 /// ```
143 pub fn set_scales_to_fit(&mut self, scales_to_fit: bool) -> &mut Self {
144 unsafe {
145 crate::ffi::sc_stream_configuration_set_scales_to_fit(self.as_ptr(), scales_to_fit);
146 }
147 self
148 }
149
150 /// Enable or disable scaling to fit (builder pattern)
151 #[must_use]
152 pub fn with_scales_to_fit(mut self, scales_to_fit: bool) -> Self {
153 self.set_scales_to_fit(scales_to_fit);
154 self
155 }
156
157 /// Check if scaling to fit is enabled
158 pub fn scales_to_fit(&self) -> bool {
159 unsafe { crate::ffi::sc_stream_configuration_get_scales_to_fit(self.as_ptr()) }
160 }
161
162 /// Set the source rectangle to capture
163 ///
164 /// Defines which portion of the source content to capture. Coordinates are
165 /// relative to the source content's coordinate system.
166 ///
167 /// # Examples
168 ///
169 /// ```
170 /// use screencapturekit::prelude::*;
171 /// use screencapturekit::cg::CGRect;
172 ///
173 /// // Capture only top-left quarter of screen
174 /// let rect = CGRect::new(0.0, 0.0, 960.0, 540.0);
175 /// let mut config = SCStreamConfiguration::default();
176 /// config.set_source_rect(rect);
177 /// ```
178 pub fn set_source_rect(&mut self, source_rect: CGRect) -> &mut Self {
179 unsafe {
180 crate::ffi::sc_stream_configuration_set_source_rect(
181 self.as_ptr(),
182 source_rect.origin.x,
183 source_rect.origin.y,
184 source_rect.size.width,
185 source_rect.size.height,
186 );
187 }
188 self
189 }
190
191 /// Set the source rectangle (builder pattern)
192 #[must_use]
193 pub fn with_source_rect(mut self, source_rect: CGRect) -> Self {
194 self.set_source_rect(source_rect);
195 self
196 }
197
198 /// Get the configured source rectangle
199 pub fn source_rect(&self) -> CGRect {
200 unsafe {
201 let mut x = 0.0;
202 let mut y = 0.0;
203 let mut width = 0.0;
204 let mut height = 0.0;
205 crate::ffi::sc_stream_configuration_get_source_rect(
206 self.as_ptr(),
207 &raw mut x,
208 &raw mut y,
209 &raw mut width,
210 &raw mut height,
211 );
212 CGRect::new(x, y, width, height)
213 }
214 }
215
216 /// Set the destination rectangle for captured content
217 ///
218 /// Defines where the captured content will be placed in the output frame.
219 /// Useful for picture-in-picture or multi-source compositions.
220 ///
221 /// # Examples
222 ///
223 /// ```
224 /// use screencapturekit::prelude::*;
225 /// use screencapturekit::cg::CGRect;
226 ///
227 /// // Place captured content in top-left corner
228 /// let rect = CGRect::new(0.0, 0.0, 640.0, 480.0);
229 /// let mut config = SCStreamConfiguration::default();
230 /// config.set_destination_rect(rect);
231 /// ```
232 pub fn set_destination_rect(&mut self, destination_rect: CGRect) -> &mut Self {
233 unsafe {
234 crate::ffi::sc_stream_configuration_set_destination_rect(
235 self.as_ptr(),
236 destination_rect.origin.x,
237 destination_rect.origin.y,
238 destination_rect.size.width,
239 destination_rect.size.height,
240 );
241 }
242 self
243 }
244
245 /// Set the destination rectangle (builder pattern)
246 #[must_use]
247 pub fn with_destination_rect(mut self, destination_rect: CGRect) -> Self {
248 self.set_destination_rect(destination_rect);
249 self
250 }
251
252 /// Get the configured destination rectangle
253 pub fn destination_rect(&self) -> CGRect {
254 unsafe {
255 let mut x = 0.0;
256 let mut y = 0.0;
257 let mut width = 0.0;
258 let mut height = 0.0;
259 crate::ffi::sc_stream_configuration_get_destination_rect(
260 self.as_ptr(),
261 &raw mut x,
262 &raw mut y,
263 &raw mut width,
264 &raw mut height,
265 );
266 CGRect::new(x, y, width, height)
267 }
268 }
269
270 /// Preserve aspect ratio when scaling
271 ///
272 /// When enabled, the content will be scaled while maintaining its original
273 /// aspect ratio, potentially adding letterboxing or pillarboxing.
274 ///
275 /// Note: This property requires macOS 14.0+. On older versions, the setter
276 /// returns `SCError::FeatureNotAvailable` and the getter returns `false`.
277 ///
278 /// # Examples
279 ///
280 /// ```
281 /// use screencapturekit::prelude::*;
282 ///
283 /// let mut config = SCStreamConfiguration::default();
284 /// config
285 /// .set_preserves_aspect_ratio(true)
286 /// .expect("macOS 14.0 or later");
287 /// // Returns true on macOS 14.0+, false on older versions
288 /// let _ = config.preserves_aspect_ratio();
289 /// ```
290 #[cfg(feature = "macos_14_0")]
291 #[allow(clippy::missing_errors_doc)]
292 pub fn set_preserves_aspect_ratio(
293 &mut self,
294 preserves_aspect_ratio: bool,
295 ) -> SCResult<&mut Self> {
296 let applied = unsafe {
297 crate::ffi::sc_stream_configuration_set_preserves_aspect_ratio(
298 self.as_ptr(),
299 preserves_aspect_ratio,
300 )
301 };
302 applied.then_some(self).ok_or_else(|| {
303 SCError::feature_not_available("SCStreamConfiguration.preservesAspectRatio", "14.0")
304 })
305 }
306
307 /// Preserve aspect ratio when scaling (builder pattern)
308 #[cfg(feature = "macos_14_0")]
309 #[allow(clippy::missing_errors_doc)]
310 pub fn with_preserves_aspect_ratio(mut self, preserves_aspect_ratio: bool) -> SCResult<Self> {
311 self.set_preserves_aspect_ratio(preserves_aspect_ratio)?;
312 Ok(self)
313 }
314
315 /// Check if aspect ratio preservation is enabled
316 #[cfg(feature = "macos_14_0")]
317 pub fn preserves_aspect_ratio(&self) -> bool {
318 unsafe { crate::ffi::sc_stream_configuration_get_preserves_aspect_ratio(self.as_ptr()) }
319 }
320}