How FluidVoice Implements the Global Hotkey for Background Voice Capture
FluidVoice captures voice in the background by installing a system-wide CGEvent tap that listens for low-level keyboard events, matches them against user-defined shortcuts in GlobalHotkeyManager.swift, and triggers the ASR service through a callback supplied by ContentView.
FluidVoice, developed by the altic-dev repository, enables seamless background voice capture through a sophisticated global hotkey system. This implementation allows users to trigger speech recognition from any application without requiring the FluidVoice window to be active. Understanding how this global hotkey for background voice capture works reveals the intersection of macOS accessibility APIs, Swift concurrency, and event-driven architecture.
Installing the System-Wide Event Tap
The core mechanism resides in Sources/Fluid/Services/GlobalHotkeyManager.swift. The manager creates a CGEvent tap using CGEvent.tapCreate to intercept system-wide keyboard and mouse events before they reach active applications.
The implementation inserts the tap at the session level with .headInsertEventTap priority:
self.eventTap = CGEvent.tapCreate(
tap: .cgSessionEventTap,
place: .headInsertEventTap,
options: .defaultTap,
eventsOfInterest: CGEventMask(eventMask),
callback: { proxy, type, event, refcon -> Unmanaged<CGEvent>? in
// Reference to manager instance passed via userInfo
return manager.handleKeyEvent(proxy: proxy, type: type, event: event)
},
userInfo: Unmanaged.passUnretained(self).toOpaque()
)
The tap listens for key-down, key-up, flags-changed, and mouse-button events (lines 57-66). The system requires accessibility permissions to create this tap; if permissions are missing, the method returns false at line 52 and the tap is not established.
Configuring Hotkey Shortcuts and Activation Modes
Hotkey configurations persist in Sources/Fluid/Persistence/SettingsStore.swift. The GlobalHotkeyManager reads these settings during initialization to determine which key combinations trigger voice capture.
The manager supports three distinct HotkeyActivationMode values:
- Hold: Recording continues only while the hotkey remains pressed
- Automatic: Distinguishes between short taps and long holds using a 0.4-second threshold
- Toggle: Press once to start, press again to stop
Users can configure modifier-only shortcuts (such as Right Option/Alt) or full key combinations. The manager stores these as [HotkeyShortcut] arrays, checking them against incoming events using the matches(keyCode:modifiers:) method defined in Sources/Fluid/Models/HotkeyShortcut.swift.
Matching Keystrokes and Triggering ASR
When the event tap fires, the handleKeyEvent method processes every keystroke through a priority cascade. First, it checks for cancel shortcuts, then paste-last-transcription commands, followed by mode-specific shortcuts (prompt, command, rewrite).
For primary dictation, the code evaluates shortcuts at lines 1024-1029:
if let shortcut = self.primaryShortcuts.first(where: {
$0.matches(keyCode: keyCode, modifiers: eventModifiers)
}) {
guard self.beginPrimaryShortcutPress(.keyboard(shortcut.keyCode)) else { return nil }
self.handlePrimaryDictationTriggerDown()
return nil
}
The beginPrimaryShortcutPress method prevents double-presses by tracking the current press state. Upon successful validation, handlePrimaryDictationTriggerDown initiates the recording sequence.
Bridging to the UI: The Recording Callback
The GlobalHotkeyManager does not directly control audio capture. Instead, it delegates to the UI layer through a closure-based callback mechanism defined during initialization in Sources/Fluid/ContentView.swift (lines 3160-3162).
ContentView supplies the startRecordingCallback when creating the manager:
.hotkeyManager = GlobalHotkeyManager(
asrService: asrService,
primaryShortcuts: storedShortcuts,
// ... other shortcuts ...
startRecordingCallback: {
DebugLogger.shared.debug("ContentView: startRecordingCallback invoked by hotkey", source: "ContentView")
self.startRecording()
},
// ... other callbacks ...
)
When the hotkey matches, startRecordingIfNeeded() executes this closure asynchronously:
private func startRecordingIfNeeded() {
if let callback = self.startRecordingCallback {
Task { await callback() }
}
}
This design decouples the low-level event tap from the ASRService audio capture logic, ensuring the event callback returns quickly while recording starts on a separate task.
Handling Lifecycle Events and Tap Recovery
macOS may disable event taps due to timeout protection or user input security policies. The implementation handles this through handleTapDisableEvent (lines 1010-1029), which immediately attempts to re-enable the tap. If re-enabling fails, the manager recreates the entire tap via setupGlobalHotkeyWithRetry.
A periodic health-check timer validates the tap's status, ensuring the global hotkey remains responsive even after system sleep or security updates. This robustness guarantees that the background voice capture remains available across extended usage sessions.
Summary
- FluidVoice uses a CGEvent tap created in
GlobalHotkeyManager.swiftto intercept system-wide keyboard events at the session level. - Accessibility permissions are mandatory; the tap fails silently if the user hasn't granted these rights.
- Three activation modes (hold, automatic, toggle) provide flexible interaction patterns for starting voice capture.
- Closure-based callbacks bridge the event tap to
ContentView, which initializes the actualASRServicerecording. - Automatic recovery mechanisms handle tap disable events through retry logic and health monitoring.
Frequently Asked Questions
What permissions are required for the global hotkey to work?
FluidVoice requires accessibility permissions to install the CGEvent tap. Without these permissions, CGEvent.tapCreate returns nil and the manager sets isEnabled to false at line 52. Users must grant these permissions in System Settings under Privacy & Security > Accessibility.
How does FluidVoice prevent the hotkey from triggering multiple times?
The manager implements press-state tracking through beginPrimaryShortcutPress, which returns false if a shortcut is already active. This guard prevents duplicate recordings when the user holds the key slightly longer than intended or when the system generates rapid key-repeat events.
Can the hotkey activate while FluidVoice is not the active application?
Yes. Because the implementation uses a system-wide event tap (.cgSessionEventTap), it receives keyboard events regardless of which application currently holds focus. This allows users to trigger voice capture while typing in any other application, with the transcription being inserted at the current cursor position.
What happens if macOS disables the event tap?
The system periodically disables event taps as a security measure. FluidVoice detects this through handleTapDisableEvent and attempts to re-enable the tap, or recreates it via setupGlobalHotkeyWithRetry if necessary. A background timer performs periodic health checks to ensure continuous availability.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →