diff --git a/crates/chanora_audio/src/ptt_backends/macos.rs b/crates/chanora_audio/src/ptt_backends/macos.rs index 8733e2e..eb31e2c 100644 --- a/crates/chanora_audio/src/ptt_backends/macos.rs +++ b/crates/chanora_audio/src/ptt_backends/macos.rs @@ -58,15 +58,31 @@ impl PermissionState { } fn query_permission() -> PermissionState { - // Live `IOHIDCheckAccess` query lands in the macOS platform - // verification commit (it needs the IOKit framework link). - // Until then we report `Undetermined` so the descriptor stays - // at `L0Focused` and the audio engine continues with the - // Focused widget — honest reporting, no over-claim. The - // periodic re-query loop below still exercises the watch - // sender path so a future replacement of this function - // automatically engages the runtime upgrade. - PermissionState::Undetermined + // Live IOHIDCheckAccess query against the IOKit HID subsystem. + // The Input Monitoring permission ("Listen Event" access type) + // is what gates CGEventTap from receiving global key/mouse + // events. Returns the live permission state every call; the + // periodic re-query loop in `MacOSEventTapBackend::start` + // republishes the descriptor whenever this value transitions. + // + // SAFETY: IOHIDCheckAccess is a thread-safe synchronous IOKit + // entry point with no side effects beyond reading a system + // preference. The `#[link]` attribute on the extern block + // resolves at build time against the IOKit framework. The u32 + // return value maps directly onto the documented + // IOHIDAccessType enumerator { Granted=0, Denied=1, Unknown=2 }. + #[link(name = "IOKit", kind = "framework")] + extern "C" { + fn IOHIDCheckAccess(request_type: u32) -> u32; + } + // kIOHIDRequestTypeListenEvent == 1 per IOHIDLib.h. + const REQUEST_TYPE_LISTEN_EVENT: u32 = 1; + let raw = unsafe { IOHIDCheckAccess(REQUEST_TYPE_LISTEN_EVENT) }; + match raw { + 0 => PermissionState::Granted, + 1 => PermissionState::Denied, + _ => PermissionState::Undetermined, + } } /// Try to construct the macOS event-tap backend. Returns `None`