# How the Accessibility Permission System Works with Event Taps in vorssaint‑utils

> Understand how vorssaint-utils leverages macOS Accessibility permissions to install event taps for keyboard and mouse control. Learn about privacy requirements and system integration.

- Repository: [vorssaint/vorssaint-utils](https://github.com/vorssaint/vorssaint-utils)
- Tags: internals
- Published: 2026-09-11

---

**vorssaint‑utils requires two macOS privacy permissions—Accessibility and Screen Recording—to install low‑level event taps that intercept keyboard, mouse, and display events before the system processes them.**

The **accessibility permission system** in vorssaint‑utils orchestrates detection, polling, and enforcement of macOS TCC (Transparency, Consent, and Control) grants. All global event taps that monitor user input rely on this centralized architecture, which lives in [`Sources/Vorssaint/Core/Permissions.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Core/Permissions.swift) and coordinates with individual service implementations like `SuperKeyService` and `WindowLayoutService`.

## Core Permission Requirements

vorssaint‑utils distinguishes between two permission classes that gate different categories of event taps:

- **Accessibility** (`AXIsProcessTrusted`) – Required for any tap that listens to raw HID input events (keyboard, mouse, trackpad) before system processing. All global input monitors depend on this grant.
- **Screen Recording** (`CGPreflightScreenCaptureAccess`) – Required for taps that read screen contents, including window titles and thumbnails used by the window switcher and layout features.

Both permissions are managed by the `Permissions` singleton, which exposes `@Published` properties that SwiftUI views and background services observe to react instantly when grants are revoked or restored.

## Detecting Current Permission Status

The system determines authorization state through cheap, side‑effect‑free CoreGraphics and Accessibility API calls that can run on a background queue. In [`Permissions.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Permissions.swift), the `refreshActivePermissions()` method polls both flags:

```swift
private func refreshActivePermissions() {
    let ax = AXIsProcessTrusted()               // Accessibility
    let sr = CGPreflightScreenCaptureAccess()   // Screen Recording
    DispatchQueue.main.async {
        if self.accessibility != ax { self.accessibility = ax }
        if self.screenRecording != sr { self.screenRecording = sr }
        // Re‑schedule the poll if the cadence changed
        self.scheduleActivePermissionPolling()
    }
}

```

`AXIsProcessTrusted()` returns a **Boolean** indicating whether the app appears in System Settings → Privacy & Security → Accessibility. `CGPreflightScreenCaptureAccess()` performs the equivalent check for Screen Recording. Because these calls are non‑blocking, vorssaint‑utils can poll them frequently without impacting UI responsiveness.

## Conditional Polling Strategy

Rather than running a perpetual background timer, the **accessibility permission system** calculates a dynamic polling interval via `desiredPollInterval` (lines 82‑92 in [`Permissions.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Permissions.swift)). The timer only activates when:

1. A feature requiring the permission is currently active, or
2. A UI surface displaying the permission state is visible.

If no active feature needs the grant, `scheduleActivePermissionPolling()` stops the timer entirely to eliminate unnecessary CPU usage. This lazy evaluation prevents vorssaint‑utils from draining battery while idle.

## Requesting Permissions from the User

When a user enables a feature that requires Accessibility, the UI invokes `requestAccessibility()`, which triggers the system consent prompt:

```swift
func requestAccessibility() {
    let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary
    AXIsProcessTrustedWithOptions(options)                 // Shows the system prompt
    refreshActivePermissions()
    if !accessibility {
        PermissionGuideOverlay.shared.show(for: .accessibility)
    }
}

```

The system displays the native permission dialog **once per TCC reset**. The code immediately calls `refreshActivePermissions()` because the user might grant access without dismissing the dialog, allowing the app to enable the feature instantly. Screen Recording follows an identical pattern using `CGRequestScreenCaptureAccess()`.

## Reacting to Permission Changes

[`Permissions.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Permissions.swift) registers two observers to detect external changes without polling:

- **Application activation** – When the user returns from System Settings after manually toggling a permission, `scheduleActivePermissionPolling()` fires immediately to update state.
- **UserDefaults changes** – Feature toggles that enable or disable permission UI surfaces trigger a cadence recalculation.

These observers ensure the app never waits for the next timer tick to discover that a user revoked Accessibility access in System Settings.

## Guarding Event Tap Creation

Every service that installs a global or local event tap checks the shared permission state before calling `CGEventTapCreate`. For example, the **Super‑Key** service in [`Sources/Vorssaint/Services/SuperKey/SuperKeyService.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Services/SuperKey/SuperKeyService.swift) uses a guard clause:

```swift
guard Permissions.shared.accessibility else {
    // Permission missing – show UI or abort tap installation
    return
}
eventTap = CGEventTapCreate(...)
// … configure and enable the tap

```

If the user revokes Accessibility while the tap is active, the next poll sets `Permissions.shared.accessibility` to `false`. The service’s Combine subscriber observes this change, tears down the `CFMachPort`, and optionally re‑displays the permission guide overlay.

## Resetting Permissions for Development

The `Permissions.startOver(_:)` method supports advanced troubleshooting by invoking the `tccutil` command‑line tool to reset TCC entries for Accessibility or Screen Recording. This executes on a background queue via `DispatchQueue.global()` to keep the UI responsive while forcing macOS to show the consent prompt again on the next request.

## Summary

- **Two permissions gate event taps**: Accessibility for input interception, Screen Recording for display content reading.
- **Centralized detection**: `Permissions.refreshActivePermissions()` queries `AXIsProcessTrusted()` and `CGPreflightScreenCaptureAccess()` without side effects.
- **Lazy polling**: The timer only runs when needed, calculated by `desiredPollInterval` to conserve resources.
- **System integration**: `AXIsProcessTrustedWithOptions()` displays the native consent dialog; observers on app activation catch manual changes instantly.
- **Defensive coding**: Services like `SuperKeyService` guard `CGEventTapCreate` with `Permissions.shared.accessibility` checks and tear down taps immediately when grants disappear.

## Frequently Asked Questions

### What happens if I deny the Accessibility permission when vorssaint‑utils asks for it?

The app stores the denial but continues to function for features that do not require event taps. The specific service (such as Super‑Key) will remain inactive, and the UI will display `PermissionGuideOverlay` explaining how to manually grant access in System Settings. The next time you attempt to enable the feature, `requestAccessibility()` will trigger the system prompt again unless you have previously checked "Don't Ask Again" in macOS TCC settings.

### Why does vorssaint‑utils need Screen Recording permission for window management features?

Window layout and switcher features use `CGWindowListCopyWindowInfo` or similar APIs to enumerate window titles and positions. Since macOS Catalina, any code that reads window titles or captures screen contents—including generating thumbnails—requires Screen Recording permission. The **accessibility permission system** checks `CGPreflightScreenCaptureAccess()` before installing taps that inspect `kCGWindowName` or `kCGWindowBounds` in [`Sources/Vorssaint/Services/WindowLayout/WindowLayoutService.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Services/WindowLayout/WindowLayoutService.swift).

### How often does vorssaint‑utils poll for permission changes?

The polling cadence is dynamic. If a feature requiring Accessibility is active, `scheduleActivePermissionPolling()` typically sets a short interval (often 1–2 seconds) to detect revocations quickly. When no relevant features are active, the timer stops entirely. This logic prevents background CPU usage while ensuring the UI updates within seconds of a user changing permissions in System Settings.

### Canvorssaint‑utils work partially without Accessibility permission?

Yes. Features that do not rely on low‑level event taps—such as timers, UI themes, or file utilities—function normally. Only services that call `CGEventTapCreate`, like the scroll inverter or keyboard remapper, require the grant. Each service independently checks `Permissions.shared.accessibility` before attempting to install its tap, ensuring the app never crashes from unauthorized API calls.