# How the Radial Menu Works as an Input Overlay in Vorssaint-utils

> Explore how the radial menu in vorssaint-utils acts as an input overlay, intercepting mouse and keyboard events to offer a transparent, glass-styled wheel for seamless interaction.

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

---

**The radial menu in Vorssaint-utils functions as a transparent input overlay that intercepts mouse, keyboard, and system events through a dedicated NSPanel, presenting a glass-styled wheel at the cursor or screen center while swallowing all input until the user selects an action or dismisses the interface.**

Vorssaint-utils implements a self-contained radial menu input overlay that appears instantly above all applications without requiring focus changes. The system captures low-level input events through Carbon hotkeys and Core Graphics event taps, ensuring that underlying applications never receive the triggering clicks or keystrokes while the menu is active. This architecture allows users to trigger actions via extra mouse buttons, keyboard shortcuts, or pointer movement without disrupting their current workflow.

## Core Architecture of the Input Overlay

The overlay consists of three primary Swift components that manage the entire session lifecycle from activation to dismissal.

### RadialMenuService (Session Controller)

Located in [`Sources/Vorssaint/Services/RadialMenu/RadialMenuService.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Services/RadialMenu/RadialMenuService.swift), this singleton `ObservableObject` owns the overlay state machine. It registers global hotkeys via `QuickToolHotkey`, creates a transparent `NSPanel` through `ensurePanel()`, and manages the event tap lifecycle through `installMonitors()` and `removeMonitors()`. The service publishes state changes—such as `highlightedIndex`, `visible`, and the menu `stack`—that drive the SwiftUI interface.

### RadialMenuView (Visual Rendering)

Defined in [`Sources/Vorssaint/UI/RadialMenu/RadialMenuView.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/RadialMenu/RadialMenuView.swift), this view renders the glass-styled wheel, the sweeping `RadialWedgeShape` highlight, and the central hub. It reacts to `RadialMenuService` published properties and forwards user gestures back to the service via `activatePointer()` and `stepBack()` methods.

### RadialMenuSettings (Configuration Interface)

Found in [`Sources/Vorssaint/UI/Settings/RadialMenuSettings.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/Settings/RadialMenuSettings.swift), this component manages user preferences stored in `UserDefaults` under keys like `DefaultsKey.radialMenuProfiles`. Changes persist immediately and trigger `syncWithPreferences()` in the service to update hotkey registrations and mouse-button triggers.

## Initializing the Input Overlay Session

The radial menu input overlay begins its lifecycle when the user invokes it through one of two primary mechanisms.

### Global Hotkey Activation

When the application launches, `RadialMenuService.shared.syncWithPreferences()` reads the stored shortcut configuration and registers a `QuickToolHotkey`. Upon triggering, the hotkey callback invokes `beginSession()`, which constructs the transparent panel and positions the wheel either at the mouse pointer (if `DefaultsKey.radialMenuAtPointer` is true) or at the screen center. The wheel initially renders at a minimal size before animating open.

### Extra Mouse Button Capture

For users who prefer mouse-driven activation, the service creates a low-level event tap using `CGEvent.tapCreate` that monitors `otherMouseDown` and `otherMouseUp` events. When the configured extra button presses, `handleMouseTap` immediately calls `beginSession()` and **swallows the click** by returning `nil` from the callback, ensuring the underlying application never receives the button press.

## Input Interception and Event Handling

Once active, the overlay must capture all input types without passing them through to background applications. The system installs temporary, session-scoped monitors that exist only while the wheel is visible.

### Swallowing Extra Mouse Buttons

The mouse event tap installed in `RadialMenuService` runs at the `CGEvent.tapCreate` level with `kCGEventTapOptionCGSession` scope. When `handleMouseTap` detects the configured button, it returns `nil` to the event stream, effectively consuming the input. The tap also supports `isReportingMouseButtons` mode for the Settings UI, allowing users to configure which button triggers the menu.

### Tracking Pointer Movement

Local and global monitors for `mouseMoved` and `leftMouseDragged` events feed into `pointerMoved()`, which calls `refreshHighlight()`. This method uses `RadialMenuGeometry.highlightedIndex` to calculate which pie slice sits beneath the cursor, updating the `highlightedIndex` published property that drives the wedge animation in `RadialMenuView.syncWedge`.

### Keyboard Navigation Capture

A local monitor added via `addLocalMonitorForEvents(matching: .keyDown)` captures all keystrokes while the panel is key. The `handleKeyDown` method implements vim-style navigation: arrow keys invoke `rotateHighlight()`, digits 1-9 call `select(index)` directly, `Return` activates the current selection, and `Esc` triggers `stepBack()` to navigate submenus.

### Modifier Flag Monitoring

For users operating in hold mode (keeping modifiers pressed to maintain the menu), a `flagsChanged` monitor watches for the release of shortcut modifiers. When the trigger keys lift, `handleFlagsChanged()` invokes `endHoldPhase()`, automatically executing the highlighted action without requiring an explicit click.

## Selection Logic and Action Execution

When the user commits to an action, the overlay must determine what was selected and execute it cleanly.

### Geometry-Based Selection

The `activatePointer()` method distinguishes between three zones: clicks outside the wheel boundary dismiss the session via `endSession()`, clicks in the central dead-zone trigger `stepBack()` for submenu navigation, and clicks over a highlighted slice invoke `select(index)`.

### Dispatching Actions

The `run(_:)` method in `RadialMenuService` dispatches based on `RadialMenuItem.kind`:

- **Apps, Files, URLs**: Opened via `NSWorkspace.shared.open()`.
- **Keyboard Shortcuts**: Posted as synthetic `CGEvent` sequences through `postWhenModifiersReleased()`, ensuring held modifiers from the trigger don't interfere with the shortcut.
- **Media Keys**: Dispatched as system-defined events via `postMediaKey()`.
- **Tools and Toggles**: Invoke internal Vorssaint services (screenshot, window layout) after a short delay to allow the overlay to fade.

All actions execute only after `PanelDismissal` completes the fade-out animation, guaranteeing the overlay has fully disappeared before sending events to the system.

## Permissions and System Integration

The input overlay requires specific macOS permissions to function correctly. The `ensureAccessibilityPermission()` method checks for and requests accessibility access via `Permissions.shared.requestAccessibility()`. Without this permission, the mouse event tap cannot intercept extra buttons and the service emits a system beep to alert the user.

The overlay respects system accessibility preferences: it disables animations when the user enables *Reduce Motion* and adjusts transparency settings based on the *Reduce Transparency* flag.

## Implementation Examples

Below are practical code snippets demonstrating common integration patterns for the radial menu input overlay.

Trigger via Global Shortcut:

```swift
// Register the stored hotkey at application launch
RadialMenuService.shared.syncWithPreferences()

// When pressed, the internal hotkeyPressed(for:) method
// automatically invokes beginSession()

```

Enable and Capture Extra Mouse Buttons:

```swift
// Enable mouse button trigger in preferences
UserDefaults.standard.set(true, forKey: DefaultsKey.radialMenuEnabled)

// Temporarily enable button reporting for configuration UI
RadialMenuService.shared.setReportingMouseButtons(true)

// The service now creates the event tap that swallows clicks
// via handleMouseTap -> beginSession

```

Present a Preview from Settings:

```swift
// Decode the stored profile and present non-interactive preview
if let profile = RadialMenuSupport.decodeProfiles(
    UserDefaults.standard.data(forKey: DefaultsKey.radialMenuProfiles),
    defaults: .standard
).first {
    RadialMenuService.shared.presentPreview(for: profile)
}

```

## Summary

- **RadialMenuService** ([`RadialMenuService.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/RadialMenuService.swift)) orchestrates the input overlay lifecycle, managing global hotkeys, event taps, and session state through `beginSession()` and `endSession()`.
- The overlay captures input through temporary monitors that **swallow events** (returning `nil` for mouse taps, intercepting keyDown) to prevent underlying applications from receiving trigger inputs.
- **RadialMenuView** renders the glass-styled wheel and responds to geometry calculations from `RadialMenuGeometry.highlightedIndex` to provide real-time visual feedback.
- Actions dispatch through `run(_:)` only after the panel dismisses, using `postWhenModifiersReleased()` for shortcuts to avoid modifier interference.
- The system requires Accessibility permissions to create the low-level event tap needed for extra mouse button capture.

## Frequently Asked Questions

### How does Vorssaint-utils prevent the underlying app from receiving the triggering mouse click?

The service creates a low-level Core Graphics event tap in [`RadialMenuService.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/RadialMenuService.swift) that monitors `otherMouseDown` events. When the configured extra button presses, the `handleMouseTap` callback returns `nil` to the event stream, effectively consuming the click before it reaches the underlying application.

### Can the radial menu appear at the current mouse position instead of screen center?

Yes. When `beginSession()` creates the overlay, it checks `DefaultsKey.radialMenuAtPointer`. If true, it positions the `NSPanel` at the current cursor coordinates; otherwise, it centers the wheel on the active screen.

### What happens to keyboard shortcuts that use modifiers if I'm holding keys to keep the menu open?

The `handleFlagsChanged()` method monitors modifier key states. When you release the shortcut modifiers that initially triggered the menu in hold mode, it automatically invokes `endHoldPhase()`, which releases the modifiers before posting the final shortcut through `postWhenModifiersReleased()`, preventing stuck keys or conflicting inputs.

### How does the overlay handle file and application icons without blocking the main thread?

[`RadialMenuIconStore.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/RadialMenuIconStore.swift) maintains a cache of file and custom icons loaded asynchronously. When `RadialMenuView` renders slices, it reads from this cache rather than accessing the file system directly, ensuring that pointer movement and highlight updates remain fluid even when displaying complex folder contents.