hotkey

repository·main·Indexed 42 days ago

https://github.com/golang-design/hotkey

A cross-platform Go package for registering system-level global hotkeys on macOS, Linux (X11), and Windows. It allows applications to respond to key combinations via Keydown() and Keyup() channels even without window focus. Includes the golang.design/x/hotkey/mainthread package for macOS main thread requirements and supports custom keycodes for Linux X11.

Tokens
1.8K
Snippets
3
Records
9
Agent score
4%

What's inside hotkey

  1. Platform-specific considerations for Linux (X11)

    main

    When running on Linux with X11, be aware of the following behaviors:

    • AutoRepeat: If X server AutoRepeat is enabled, a Keyup event will be triggered automatically and continuously while the key is held down (Keydown).
    • Modifier Mapping: Some keys may map to multiple Mod keys. To register a combination correctly, you may need to use the underlying keycode combination (e.g., Ctrl+Alt+S might require Ctrl+Mod2+Mod4+S).
    • Custom Keycodes: If a specific key is not provided in the package constants, you can use a raw keycode by casting it: hotkey.Key(0x15).
  2. Handle macOS main thread and accessibility requirements

    main

    When using hotkey on macOS, you must account for two specific requirements:

    1. Main Thread Execution: Hotkey events must be handled on the OS main thread. For self-contained applications, use the golang.design/x/hotkey/mainthread package to initialize your application loop. If you are using a GUI framework like Fyne, Ebiten, or Gio, this is handled automatically and you do not need to call mainthread.Init().

    2. Accessibility Permissions: macOS delivers hotkeys via CGEventTap, which requires the application to have Accessibility (Input Monitoring) permissions. If permissions are missing, Register() will return an error. You can grant this in: System Settings → Privacy & Security → Accessibility.

  3. Register and listen to global hotkeys

    main

    Use the hotkey package to register system-level global hotkey shortcuts. A hotkey consists of a combination of Modifiers and a single Key. Once registered, you can listen for Keydown and Keyup events via channels.

    Platform Specifics

    • macOS: Hotkey events must be handled on the "main thread". For self-contained applications, use the golang.design/x/hotkey/mainthread package to start the OS main event loop. If you are using GUI frameworks like Fyne, Ebiten, or Gio, this is unnecessary.
    • macOS Permissions: Hotkeys use CGEventTap, which requires Accessibility (Input Monitoring) permissions. Grant this in System Settings → Privacy & Security → Accessibility.
    • Linux (X11):
      • If AutoRepeat is enabled in the X server, Keyup may be triggered automatically and continuously while Keydown is active.
      • Some keys map to multiple Mod keys. You may need to use the specific underlying keycode combination (e.g., Ctrl+Mod2+Mod4+S instead of Ctrl+Alt+S).
    • Custom Keycodes: If a specific key is not provided in the package, you can use hotkey.Key(keycode) where keycode is a numeric value (e.g., hotkey.Key(0x15)).
    package main
    
    import (
    	"log"
    
    	"golang.design/x/hotkey"
    	"golang.design/x/hotkey/mainthread"
    )
    
    func main() {
    	// mainthread.Init is required on macOS for self-contained apps
    	mainthread.Init(fn)
    }
    
    func fn() {
    	// Create a hotkey: Ctrl + Shift + S
    	hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS)
    
    	// Register the hotkey with the system
    	err := hk.Register()
    	if err != nil {
    		log.Fatalf("hotkey: failed to register hotkey: %v", err)
    	}
    
    	log.Printf("hotkey: %v is registered\n", hk)
    
    	// Wait for key down event
    	<-hk.Keydown()
    	log.Printf("hotkey: %v is down\n", hk)
    
    	// Wait for key up event
    	<-hk.Keyup()
    	log.Printf("hotkey: %v is up\n", hk)
    
    	// Unregister the hotkey
    	hk.Unregister()
    	log.Printf("hotkey: %v is unregistered\n", hk)
    }
  4. Register and use global hotkeys

    main

    The hotkey package allows you to register system-level global hotkey shortcuts. A hotkey is defined as a combination of hotkey.Modifier values and a single hotkey.Key.

    To use it:

    1. Create a new hotkey using hotkey.New([]hotkey.Modifier{...}, hotkey.Key).
    2. Call hk.Register() to activate the shortcut.
    3. Listen for events using the hk.Keydown() and hk.Keyup() channels.
    4. Call hk.Unregister() to release the hotkey when finished.
    package main
    
    import (
    	"log"
    
    	"golang.design/x/hotkey"
    	"golang.design/x/hotkey/mainthread"
    )
    
    func main() { mainthread.Init(fn) } // Not necessary when use in Fyne, Ebiten or Gio.
    func fn() {
    	hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS)
    	err := hk.Register()
    	if err != nil {
    		log.Fatalf("hotkey: failed to register hotkey: %v", err)
    		return
    	}
    
    	log.Printf("hotkey: %v is registered\n", hk)
    	<-hk.Keydown()
    	log.Printf("hotkey: %v is down\n", hk)
    	<-hk.Keyup()
    	log.Printf("hotkey: %v is up\n", hk)
    	hk.Unregister()
    	log.Printf("hotkey: %v is unregistered\n", hk)
    }
  5. Register a Hotkey with Register()

    main

    The Register method registers the hotkey combination with the operating system. If the hotkey is already registered, this function invalidates the old registration and overwrites its callback.

    Signature: func (hk *Hotkey) Register() error

    Errors:

    • errAlreadyRegistered: The hotkey is already registered.
    • errRegisterFailed: The combination might already be taken by another application.
  6. Create a new Hotkey with New()

    main

    The New function initializes a Hotkey instance. It requires a slice of Modifiers and a single Key.

    Signature: func New(mods []Modifier, key Key) *Hotkey

    hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModAlt}, hotkey.KeyS)
  7. Listen for Hotkey events via Keydown() and Keyup()

    main

    The Hotkey type provides two channels to listen for user interaction:

    1. Keydown(): Returns a channel that receives an Event when the hotkey is pressed.
    2. Keyup(): Returns a channel that receives an Event when the hotkey is released.

    Signatures: func (hk *Hotkey) Keydown() <-chan Event func (hk *Hotkey) Keyup() <-chan Event

  8. Hotkey error constants

    main

    The following error variables are returned by Register and Unregister. These are consistent across all supported platforms.

    • errAlreadyRegistered: hotkey: already registered
    • errNotRegistered: hotkey: not registered
    • errRegisterFailed: hotkey: failed to register, the combination might already be taken by another application