use std::ffi::c_void;
use core_foundation::base::TCFType;
use core_foundation::boolean::CFBoolean;
use core_foundation::dictionary::CFDictionary;
use core_foundation::string::CFString;
use crate::errors::Result;
use crate::models::{PermissionRequest, PermissionSnapshot};
pub fn permissions_impl() -> Result<PermissionSnapshot> {
let screen_recording = cg_preflight_screen_capture_access();
let accessibility = ax_is_process_trusted(false);
Ok(PermissionSnapshot {
screen_recording,
accessibility,
needs_restart: false,
})
}
pub fn request_permissions_impl(needs: PermissionRequest) -> Result<PermissionSnapshot> {
let before = permissions_impl()?;
if needs.screen_recording && !before.screen_recording {
cg_request_screen_capture_access();
}
if needs.accessibility && !before.accessibility {
ax_is_process_trusted(true);
}
let after = PermissionSnapshot {
screen_recording: cg_preflight_screen_capture_access(),
accessibility: ax_is_process_trusted(false),
needs_restart: needs.screen_recording && !before.screen_recording,
};
Ok(after)
}
fn cg_preflight_screen_capture_access() -> bool {
unsafe { ffi::CGPreflightScreenCaptureAccess() }
}
fn cg_request_screen_capture_access() -> bool {
unsafe { ffi::CGRequestScreenCaptureAccess() }
}
fn ax_is_process_trusted(prompt: bool) -> bool {
let key = CFString::new("AXTrustedCheckOptionPrompt");
let value = CFBoolean::from(prompt);
let dict = CFDictionary::from_CFType_pairs(&[(key.as_CFType(), value.as_CFType())]);
unsafe { ffi::AXIsProcessTrustedWithOptions(dict.as_concrete_TypeRef() as *const c_void) }
}
mod ffi {
use std::ffi::c_void;
#[link(name = "CoreGraphics", kind = "framework")]
extern "C" {
pub fn CGPreflightScreenCaptureAccess() -> bool;
pub fn CGRequestScreenCaptureAccess() -> bool;
}
#[link(name = "ApplicationServices", kind = "framework")]
extern "C" {
pub fn AXIsProcessTrustedWithOptions(options: *const c_void) -> bool;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn preflight_returns_a_boolean() {
let _ = cg_preflight_screen_capture_access();
let _ = ax_is_process_trusted(false);
}
#[test]
fn permissions_impl_returns_well_shaped_snapshot() {
let snap = permissions_impl().expect("permissions_impl never errors");
assert!(!snap.needs_restart);
}
}