Global hotkeys
Register OS-wide keyboard shortcuts that fire even when your app does not have focus — for media players, screenshots, accessibility tools.
A global hotkey is a key combination the OS routes to your app regardless of which window has focus. Screen capture tools, music players, screen rulers — they all need it. global-hotkey exposes it as a single Kotlin API across macOS, Windows, and Linux.
TL;DR
- Register key + modifier combinations or dedicated media keys.
- One callback per binding, returns a handle for unregistering.
- Native bridges per OS — no AWT, no key-event polling.
- On macOS, the user must grant Input Monitoring / Accessibility permission for some bindings.
Install
dependencies {
implementation("dev.nucleusframework:nucleus.global-hotkey:<version>")
}Quickstart
import dev.nucleusframework.globalhotkey.GlobalHotKeyManager
import dev.nucleusframework.globalhotkey.HotKeyModifier
import java.awt.event.KeyEvent
GlobalHotKeyManager.initialize()
val handle = GlobalHotKeyManager.register(
modifiers = setOf(HotKeyModifier.CTRL, HotKeyModifier.SHIFT),
keyCode = KeyEvent.VK_F12,
) { _ ->
println("Hotkey pressed")
}
// later
GlobalHotKeyManager.unregister(handle)
GlobalHotKeyManager.shutdown()How it works
GlobalHotKeyManager opens an OS event tap on initialize() (CGEventTap on macOS, RegisterHotKey on Windows, an X11/Wayland keygrabber on Linux). Each register(...) call returns a Long handle. Listeners run on a Nucleus-owned dispatcher thread — marshal back to your UI thread as needed.
Use DisposableEffect to scope a hotkey to a Composable's lifetime:
DisposableEffect(Unit) {
GlobalHotKeyManager.initialize()
val handle = GlobalHotKeyManager.register(
modifiers = setOf(HotKeyModifier.META),
keyCode = KeyEvent.VK_K,
) { showQuickSwitcher() }
onDispose {
GlobalHotKeyManager.unregister(handle)
}
}Reference
Modifiers
HotKeyModifier.CTRL, ALT, SHIFT, META. On macOS, META is Command; on Linux/Windows, the Win/Super key.
Media keys
GlobalHotKeyManager.register(MediaKey.Play) { player.togglePlayPause() }
GlobalHotKeyManager.register(MediaKey.Next) { player.skipNext() }MediaKey covers Play, Pause, Next, Prev, VolumeUp, VolumeDown, Mute.
Capability check
if (!GlobalHotKeyManager.isAvailable) {
// OS support unavailable (rare; usually a Linux compositor that bans grabs).
}Conflict detection
register(...) returns 0L if the system rejects the binding (already grabbed by another app or the desktop environment). Always check the return value.
Notes
macOS permissions
The first time you register a hotkey on macOS, the user is prompted to grant Input Monitoring (and sometimes Accessibility) for your app under System Settings → Privacy & Security. Until they accept, callbacks never fire. Surface a friendly prompt and link to x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent.
- Linux Wayland compositors generally disallow global key grabs from arbitrary apps. The library falls back to the
org.freedesktop.portal.GlobalShortcutsportal where available; on others, the binding may simply not fire. - Call
shutdown()on app exit to release the event tap cleanly.