diff --git a/hotkey.go b/hotkey.go index 479ba6a..330f84d 100644 --- a/hotkey.go +++ b/hotkey.go @@ -73,10 +73,14 @@ func New(mods []Modifier, key Key) *Hotkey { // collaborating with the golang.design/x/hotkey/mainthread package. func (hk *Hotkey) Register() error { return hk.register() } -// Keydown returns a channel that receives a signal when hotkey is triggered. +// Keydown returns a channel that receives a signal when the hotkey is triggered. func (hk *Hotkey) Keydown() <-chan Event { return hk.keydownOut } // Keyup returns a channel that receives a signal when the hotkey is released. +// +// Platform specific details: +// - On Linux (X11), When AutoRepeat is enabled in the X server, the Keyup +// is triggered automatically and continuously as Keydown continues. func (hk *Hotkey) Keyup() <-chan Event { return hk.keyupOut } // Unregister unregisters the hotkey. diff --git a/hotkey_linux.c b/hotkey_linux.c index 69c9398..0cb8b1c 100644 --- a/hotkey_linux.c +++ b/hotkey_linux.c @@ -6,10 +6,14 @@ //go:build linux +#include #include #include #include +extern void hotkeyDown(uintptr_t hkhandle); +extern void hotkeyUp(uintptr_t hkhandle); + int displayTest() { Display* d = NULL; for (int i = 0; i < 42; i++) { @@ -43,10 +47,7 @@ int displayTest() { // waitHotkey blocks until the hotkey is triggered. // this function crashes the program if the hotkey already grabbed by others. -int waitHotkey(unsigned int mod, int key) { - // FIXME: handle registered hotkey properly. - // XSetErrorHandler(handleErrors); - +int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) { Display* d = NULL; for (int i = 0; i < 42; i++) { d = XOpenDisplay(0); @@ -64,6 +65,10 @@ int waitHotkey(unsigned int mod, int key) { XNextEvent(d, &ev); switch(ev.type) { case KeyPress: + hotkeyDown(hkhandle); + continue; + case KeyRelease: + hotkeyUp(hkhandle); XUngrabKey(d, keycode, mod, DefaultRootWindow(d)); XCloseDisplay(d); return 0; diff --git a/hotkey_linux.go b/hotkey_linux.go index f653732..a988213 100644 --- a/hotkey_linux.go +++ b/hotkey_linux.go @@ -11,13 +11,17 @@ package hotkey /* #cgo LDFLAGS: -lX11 +#include + int displayTest(); -int waitHotkey(unsigned int mod, int key); +int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key); */ import "C" import ( "context" "errors" + "runtime" + "runtime/cgo" "sync" ) @@ -80,27 +84,40 @@ func (hk *Hotkey) unregister() error { // handle registers an application global hotkey to the system, // and returns a channel that will signal if the hotkey is triggered. func (hk *Hotkey) handle() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() // KNOWN ISSUE: if a hotkey is grabbed by others, C side will crash the program var mod Modifier for _, m := range hk.mods { mod = mod | m } + h := cgo.NewHandle(hk) + defer h.Delete() + for { select { case <-hk.ctx.Done(): close(hk.canceled) return default: - ret := C.waitHotkey(C.uint(mod), C.int(hk.key)) - if ret != 0 { - continue - } - hk.keydownIn <- Event{} + _ = C.waitHotkey(C.uintptr_t(h), C.uint(mod), C.int(hk.key)) } } } +//export hotkeyDown +func hotkeyDown(h uintptr) { + hk := cgo.Handle(h).Value().(*Hotkey) + hk.keydownIn <- Event{} +} + +//export hotkeyUp +func hotkeyUp(h uintptr) { + hk := cgo.Handle(h).Value().(*Hotkey) + hk.keyupIn <- Event{} +} + // Modifier represents a modifier. type Modifier uint32