Global Hotkey Interception
Relevant source files
The following files were used as context for generating this wiki page:
Global Hotkey Interception is a critical native capability of mausVoice that allows the application to respond to user input even when it is not the focused window. This system enables the "press a hotkey, speak, and release" workflow that defines the core user experience. The architecture follows a "Rust is the API, TypeScript is the Brain" pattern, where Rust handles low-level keyboard listening and TypeScript manages the state and business logic of these triggers. Sources: llms.txt:6-10, apps/desktop/README.md:44-48
The system is cross-platform, utilizing specific native APIs for macOS (Accessibility), Windows (Administrator/UAC), and Linux (uinput/udev/Compositor-level bindings). This allows the application to capture audio and inject text globally into any focused third-party application. Sources: llms.txt:55-58, apps/desktop/src/components/settings/SettingsPage.tsx:327-340
Architecture and Data Flow
The hotkey system operates through a feedback loop between the TypeScript frontend (Zustand state) and the Rust backend. The frontend synchronizes hotkey combinations to the native layer, which then listens for global events. When a matching combination is detected, Rust fires a bridge event back to TypeScript. Sources: apps/desktop/src/components/root/AppSideEffects.tsx:128-140
Hotkey Lifecycle
- Synchronization: TypeScript computes a "fingerprint" of all grabbable hotkeys based on state. Sources: apps/desktop/src/components/root/AppSideEffects.tsx:112-126
- Native Grab: Rust installs listeners or compositor-level shortcuts based on the platform strategy. Sources: apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:136-140
- Trigger: Upon physical key press, Rust emits
bridge_hotkey_trigger. Sources: apps/desktop/src/components/root/AppSideEffects.tsx:219-226 - Action: TypeScript store increments trigger counts, firing associated actions like starting recording or adding to the dictionary. Sources: apps/desktop/src/components/root/AppSideEffects.tsx:432-436
System Interaction Diagram
The following diagram illustrates the flow of a hotkey event from the hardware level to the application brain.
The diagram shows how native listeners communicate with the TypeScript store via bridge events. Sources: apps/desktop/src/components/root/AppSideEffects.tsx:128-150, apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:136-150
Platform Strategies
Hotkeys are implemented differently across OS environments to handle varying security models and display protocols. Sources: apps/desktop/src/components/settings/SettingsPage.tsx:327-355
Linux Compositor Integration
On Linux, mausVoice detects the active desktop environment (GNOME, KDE, Sway, Hyprland, or Cosmic) and deploys specific configurations. Sources: apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:136-146
- GNOME: Uses
gsettingsto write toorg.gnome.settings-daemon.plugins.media-keys. Sources: apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:250-255 - Sway/Hyprland: Writes to specific include files (e.g.,
mausvoice-hotkeys.conf) and triggers a reload. Sources: apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:360-370 - KDE: Generates
.desktopfiles in~/.local/share/applicationswithX-KDE-Shortcutsfields. Sources: apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:430-440
Security and Permissions
Global interception requires elevated permissions on all supported platforms.
| Platform | Requirement | Mechanism |
|---|---|---|
| macOS | Accessibility | System Settings prompt for input monitoring. |
| Windows | Administrator | UAC prompt or "Always run as administrator" setting. |
| Linux | uinput/udev | trigger-hotkey.sh deployment requiring admin privileges. |
Sources: apps/desktop/src/components/settings/SettingsPage.tsx:327-360
Implementation Details
Hotkey Synchronization
The synchronization process uses a hotkeyGrabFingerprint to prevent stale listeners. This fingerprint includes the hotkey strategy, active recording mode, and dictionary unlock status. Sources: apps/desktop/src/components/root/AppSideEffects.tsx:112-125
// Example fingerprint structure
const hotkeyGrabFingerprint = (state: AppState): string => {
const hotkeyKey = Object.values(state.hotkeyById)
.map((hotkey) => `${hotkey.actionName}:${hotkey.keys.join("+")}`)
.sort()
.join(",");
return [
state.hotkeyStrategy,
state.activeRecordingMode,
getIsDictationUnlocked(state) ? "1" : "0",
hotkeyKey,
].join("|");
};
Sources: apps/desktop/src/components/root/AppSideEffects.tsx:112-126
Native Key Translation
Rust functions handle the conversion of platform-neutral key strings into OS-specific formats. For example, classify_key translates generic modifiers like "Control" or "Meta" into specific strings like "ctrl" or "super" for Linux compositors. Sources: apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:165-175
Key Components
| Component | Responsibility |
|---|---|
AppSideEffects | Manages the lifecycle of listeners and synchronizes state to Rust. |
sync_compositor_hotkeys | Rust function that detects the Linux compositor and writes bindings. |
trigger-hotkey.sh | A shell script deployed on Linux to bridge compositor events to the app. |
KeyboardListenerHealth | State tracking for the native listener (e.g., healthy_grab, failed). |
Sources: apps/desktop/src/components/root/AppSideEffects.tsx:155-165, apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:24-30, apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:136-140
Summary
Global Hotkey Interception is the bridge between system-level keyboard events and the mausVoice transcription engine. By abstracting platform-specific complexities (such as D-Bus for GNOME or .desktop files for KDE) into a unified TypeScript state, the project ensures a consistent user experience while maintaining the high performance of a Rust-based native layer. Sources: llms.txt:40-45, apps/desktop/src-tauri/src/platform/linux/wl/compositor.rs:430-450