#[cfg(not(target_os = "macos"))]
compile_error!("grayscale only supports macOS");
use std::ffi::c_void;
use std::sync::Once;
const UA_PATH: &[u8] = b"/System/Library/PrivateFrameworks/UniversalAccess.framework/UniversalAccess\0";
type IsEnabledFn = unsafe extern "C" fn() -> bool;
type SetEnabledFn = unsafe extern "C" fn(bool);
static mut IS_ENABLED_FN: Option<IsEnabledFn> = None;
static mut SET_ENABLED_FN: Option<SetEnabledFn> = None;
static INIT: Once = Once::new();
unsafe extern "C" {
fn dlopen(path: *const u8, mode: i32) -> *mut c_void;
fn dlsym(handle: *mut c_void, symbol: *const u8) -> *mut c_void;
}
fn load() {
INIT.call_once(|| unsafe {
let handle = dlopen(UA_PATH.as_ptr(), 1 );
assert!(!handle.is_null(), "failed to load UniversalAccess.framework");
let is_sym = dlsym(handle, b"UAGrayscaleIsEnabled\0".as_ptr());
let set_sym = dlsym(handle, b"UAGrayscaleSetEnabled\0".as_ptr());
assert!(!is_sym.is_null() && !set_sym.is_null(), "failed to find UAGrayscale symbols");
IS_ENABLED_FN = Some(std::mem::transmute(is_sym));
SET_ENABLED_FN = Some(std::mem::transmute(set_sym));
});
}
pub fn is_enabled() -> bool {
load();
unsafe { (IS_ENABLED_FN.unwrap())() }
}
pub fn enable() {
load();
unsafe { (SET_ENABLED_FN.unwrap())(true) }
}
pub fn disable() {
load();
unsafe { (SET_ENABLED_FN.unwrap())(false) }
}
pub fn toggle() -> bool {
load();
let new_state = unsafe { !(IS_ENABLED_FN.unwrap())() };
unsafe { (SET_ENABLED_FN.unwrap())(new_state) }
new_state
}