screencapturekit/stream/configuration/
stream_properties.rs1use super::internal::SCStreamConfiguration;
6#[cfg(feature = "macos_14_0")]
7use super::InteriorNulError;
8#[cfg(feature = "macos_14_0")]
9use crate::error::{SCError, SCResult};
10#[cfg(feature = "macos_14_0")]
11use crate::utils::ffi_string::{ffi_string_from_buffer, SMALL_BUFFER_SIZE};
12
13#[repr(i32)]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16pub enum SCCaptureDynamicRange {
17 #[default]
19 SDR = 0,
20 HDRLocalDisplay = 1,
22 HDRCanonicalDisplay = 2,
24}
25
26impl SCCaptureDynamicRange {
27 pub const fn from_raw(raw: i32) -> Option<Self> {
28 match raw {
29 0 => Some(Self::SDR),
30 1 => Some(Self::HDRLocalDisplay),
31 2 => Some(Self::HDRCanonicalDisplay),
32 _ => None,
33 }
34 }
35}
36
37impl SCStreamConfiguration {
38 #[cfg(feature = "macos_14_0")]
61 pub fn set_stream_name(&mut self, name: Option<&str>) -> SCResult<&mut Self> {
62 let c_name = name
63 .map(|stream_name| std::ffi::CString::new(stream_name).map_err(|_| InteriorNulError))
64 .transpose()?;
65 let applied = unsafe {
66 crate::ffi::sc_stream_configuration_set_stream_name(
67 self.as_ptr(),
68 c_name.as_ref().map_or(std::ptr::null(), |n| n.as_ptr()),
69 )
70 };
71 applied.then_some(self).ok_or_else(|| {
72 SCError::feature_not_available("SCStreamConfiguration.streamName", "14.0")
73 })
74 }
75
76 #[cfg(feature = "macos_14_0")]
78 #[allow(clippy::missing_errors_doc)]
79 pub fn with_stream_name(mut self, name: Option<&str>) -> SCResult<Self> {
80 self.set_stream_name(name)?;
81 Ok(self)
82 }
83
84 #[cfg(feature = "macos_14_0")]
88 pub fn stream_name(&self) -> Option<String> {
89 unsafe {
90 ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
91 crate::ffi::sc_stream_configuration_get_stream_name(self.as_ptr(), buf, len)
92 })
93 }
94 }
95
96 #[cfg(feature = "macos_15_0")]
122 #[allow(clippy::missing_errors_doc)]
123 pub fn set_capture_dynamic_range(
124 &mut self,
125 dynamic_range: SCCaptureDynamicRange,
126 ) -> SCResult<&mut Self> {
127 let applied = unsafe {
128 crate::ffi::sc_stream_configuration_set_capture_dynamic_range(
129 self.as_ptr(),
130 dynamic_range as i32,
131 )
132 };
133 applied.then_some(self).ok_or_else(|| {
134 SCError::feature_not_available("SCStreamConfiguration.captureDynamicRange", "15.0")
135 })
136 }
137
138 #[cfg(feature = "macos_15_0")]
140 #[allow(clippy::missing_errors_doc)]
141 pub fn with_capture_dynamic_range(
142 mut self,
143 dynamic_range: SCCaptureDynamicRange,
144 ) -> SCResult<Self> {
145 self.set_capture_dynamic_range(dynamic_range)?;
146 Ok(self)
147 }
148
149 #[cfg(feature = "macos_15_0")]
155 #[allow(clippy::missing_errors_doc)]
156 pub fn capture_dynamic_range(&self) -> SCResult<SCCaptureDynamicRange> {
157 let mut raw = 0_i32;
158 let available = unsafe {
159 crate::ffi::sc_stream_configuration_get_capture_dynamic_range(
160 self.as_ptr(),
161 &raw mut raw,
162 )
163 };
164 if !available {
165 return Err(SCError::feature_not_available(
166 "SCStreamConfiguration.captureDynamicRange",
167 "15.0",
168 ));
169 }
170 SCCaptureDynamicRange::from_raw(raw).ok_or_else(|| SCError::UnknownValue {
171 type_name: "SCCaptureDynamicRange",
172 raw: i64::from(raw),
173 })
174 }
175}
176
177#[cfg(all(test, feature = "macos_15_0"))]
178mod tests {
179 use super::{SCCaptureDynamicRange, SCStreamConfiguration};
180
181 #[test]
182 fn bridge_rejects_unknown_capture_dynamic_range_raw_values() {
183 let mut config = SCStreamConfiguration::new();
184 config
185 .set_capture_dynamic_range(SCCaptureDynamicRange::HDRCanonicalDisplay)
186 .expect("macOS 15.0 or later");
187 for raw in [3, -1, i32::MAX, i32::MIN] {
188 let applied = unsafe {
189 crate::ffi::sc_stream_configuration_set_capture_dynamic_range(config.as_ptr(), raw)
190 };
191 assert!(!applied, "the bridge accepted raw value {raw}");
192 assert_eq!(
193 config.capture_dynamic_range(),
194 Ok(SCCaptureDynamicRange::HDRCanonicalDisplay)
195 );
196 }
197 }
198}