use crate::{CaptureError, DeviceKind, PermissionState, Support, Unavailable};
use objc2_av_foundation::{
AVAuthorizationStatus, AVCaptureDevice, AVMediaTypeAudio, AVMediaTypeVideo,
};
#[must_use]
#[allow(
clippy::missing_const_for_fn,
reason = "const-eligible on macOS (screen_support is a literal) but not on iOS, where \
screen_support makes a real RPScreenRecorder FFI call — can't be const on one \
platform and not the other from a single fn body"
)]
pub fn support(kind: DeviceKind) -> Support {
match kind {
DeviceKind::Camera | DeviceKind::Microphone => Support::Supported,
DeviceKind::Screen => screen_support(),
_ => Support::Unavailable(Unavailable::NotImplemented),
}
}
#[cfg(target_os = "macos")]
const fn screen_support() -> Support {
Support::Supported
}
#[cfg(target_os = "ios")]
fn screen_support() -> Support {
let available = unsafe { objc2_replay_kit::RPScreenRecorder::sharedRecorder().isAvailable() };
if available {
Support::Supported
} else {
Support::Unavailable(Unavailable::NoDeviceFound)
}
}
pub fn request_permission(kind: DeviceKind) -> Result<PermissionState, CaptureError> {
if matches!(support(kind), Support::Unavailable(_)) {
return Ok(PermissionState::NotSupported);
}
match kind {
DeviceKind::Camera => {
let media_type = unsafe { AVMediaTypeVideo }.ok_or(CaptureError::Backend)?;
Ok(map_authorization(unsafe {
AVCaptureDevice::authorizationStatusForMediaType(media_type)
}))
}
DeviceKind::Microphone => {
let media_type = unsafe { AVMediaTypeAudio }.ok_or(CaptureError::Backend)?;
Ok(map_authorization(unsafe {
AVCaptureDevice::authorizationStatusForMediaType(media_type)
}))
}
DeviceKind::Screen => Ok(screen_permission()),
_ => Ok(PermissionState::NotSupported),
}
}
const fn map_authorization(status: AVAuthorizationStatus) -> PermissionState {
match status {
AVAuthorizationStatus::Authorized => PermissionState::Granted,
AVAuthorizationStatus::Denied | AVAuthorizationStatus::Restricted => {
PermissionState::Denied
}
_ => PermissionState::Unknown,
}
}
#[cfg(target_os = "macos")]
fn screen_permission() -> PermissionState {
let granted = objc2_core_graphics::CGPreflightScreenCaptureAccess();
if granted {
PermissionState::Granted
} else {
PermissionState::Unknown
}
}
#[cfg(target_os = "ios")]
const fn screen_permission() -> PermissionState {
PermissionState::Unknown
}
#[cfg(test)]
#[path = "capabilities_tests.rs"]
mod tests;