//go:build darwin package darwin /* #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Cocoa -framework Carbon #include #import #import extern void keydownCallback(uintptr_t handle); extern void keyupCallback(uintptr_t handle); int registerHotKey(int mod, int key, uintptr_t handle, EventHotKeyRef* ref); int unregisterHotKey(EventHotKeyRef ref); */ import "C" import ( "errors" "fmt" "runtime/cgo" "sync" ) // Standard errors var ( ErrAlreadyRegistered = errors.New("hotkey: already registered") ErrNotRegistered = errors.New("hotkey: not registered") ErrFailedToRegister = errors.New("hotkey: failed to register") ErrFailedToUnregister = errors.New("hotkey: failed to unregister") ) // PlatformHotkey is a combination of modifiers and key to trigger an event type PlatformHotkey struct { mu sync.Mutex registered bool hkref C.EventHotKeyRef handle cgo.Handle } func (ph *PlatformHotkey) Register(mods []Modifier, key Key, keydownIn, keyupIn chan<- interface{}) error { ph.mu.Lock() defer ph.mu.Unlock() if ph.registered { return ErrAlreadyRegistered } // Store callbacks info for C side callbacks := &callbackData{ keydownIn: keydownIn, keyupIn: keyupIn, } ph.handle = cgo.NewHandle(callbacks) var mod Modifier for _, m := range mods { mod += m } ret := C.registerHotKey(C.int(mod), C.int(key), C.uintptr_t(ph.handle), &ph.hkref) if ret == C.int(-1) { ph.handle.Delete() return fmt.Errorf("%w: Carbon API returned error", ErrFailedToRegister) } ph.registered = true return nil } func (ph *PlatformHotkey) Unregister() error { ph.mu.Lock() defer ph.mu.Unlock() if !ph.registered { return ErrNotRegistered } ret := C.unregisterHotKey(ph.hkref) if ret == C.int(-1) { return fmt.Errorf("%w: Carbon API returned error", ErrFailedToUnregister) } // Clean up CGO handle ph.handle.Delete() ph.registered = false return nil } type callbackData struct { keydownIn chan<- interface{} keyupIn chan<- interface{} } //export keydownCallback func keydownCallback(h uintptr) { cb := cgo.Handle(h).Value().(*callbackData) cb.keydownIn <- struct{}{} } //export keyupCallback func keyupCallback(h uintptr) { cb := cgo.Handle(h).Value().(*callbackData) cb.keyupIn <- struct{}{} } // Modifier represents a modifier. // See: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h type Modifier uint32 // All kinds of Modifiers const ( ModCtrl Modifier = 0x1000 ModShift Modifier = 0x200 ModOption Modifier = 0x800 ModCmd Modifier = 0x100 ) // Key represents a key. // See: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h type Key uint8 // All kinds of keys const ( KeySpace Key = 49 Key1 Key = 18 Key2 Key = 19 Key3 Key = 20 Key4 Key = 21 Key5 Key = 23 Key6 Key = 22 Key7 Key = 26 Key8 Key = 28 Key9 Key = 25 Key0 Key = 29 KeyA Key = 0 KeyB Key = 11 KeyC Key = 8 KeyD Key = 2 KeyE Key = 14 KeyF Key = 3 KeyG Key = 5 KeyH Key = 4 KeyI Key = 34 KeyJ Key = 38 KeyK Key = 40 KeyL Key = 37 KeyM Key = 46 KeyN Key = 45 KeyO Key = 31 KeyP Key = 35 KeyQ Key = 12 KeyR Key = 15 KeyS Key = 1 KeyT Key = 17 KeyU Key = 32 KeyV Key = 9 KeyW Key = 13 KeyX Key = 7 KeyY Key = 16 KeyZ Key = 6 KeyReturn Key = 0x24 KeyEscape Key = 0x35 KeyDelete Key = 0x33 KeyTab Key = 0x30 KeyLeft Key = 0x7B KeyRight Key = 0x7C KeyUp Key = 0x7E KeyDown Key = 0x7D KeyF1 Key = 0x7A KeyF2 Key = 0x78 KeyF3 Key = 0x63 KeyF4 Key = 0x76 KeyF5 Key = 0x60 KeyF6 Key = 0x61 KeyF7 Key = 0x62 KeyF8 Key = 0x64 KeyF9 Key = 0x65 KeyF10 Key = 0x6D KeyF11 Key = 0x67 KeyF12 Key = 0x6F KeyF13 Key = 0x69 KeyF14 Key = 0x6B KeyF15 Key = 0x71 KeyF16 Key = 0x6A KeyF17 Key = 0x40 KeyF18 Key = 0x4F KeyF19 Key = 0x50 KeyF20 Key = 0x5A )