Skip to main content

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}