From 725f57c4bae2c8cc10d4b951d277661cbcf5c4d9 Mon Sep 17 00:00:00 2001 From: ventsislav-georgiev Date: Wed, 5 Jan 2022 22:32:37 +0200 Subject: [PATCH] fix(linux): crash on existing hotkey grab --- examples/multiple/main.go | 17 +++++++++++++++- hotkey.go | 14 +++++++++++++ hotkey_linux.c | 41 +++++++++++++++++++++------------------ hotkey_linux.go | 8 +++++--- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/examples/multiple/main.go b/examples/multiple/main.go index 7e5738b..fd63ed5 100644 --- a/examples/multiple/main.go +++ b/examples/multiple/main.go @@ -15,7 +15,7 @@ import ( func main() { mainthread.Init(fn) } func fn() { wg := sync.WaitGroup{} - wg.Add(2) + wg.Add(3) go func() { defer wg.Done() @@ -32,6 +32,21 @@ func fn() { log.Println(err) } }() + go func() { + defer wg.Done() + + err := listenHotkey(hotkey.KeyA, hotkey.ModCtrl, hotkey.ModShift) + if err != nil { + log.Println(err) + } + }() + + go func() { + <-hotkey.Err() + log.Println("some key binding has failed") + wg.Done() + }() + wg.Wait() } diff --git a/hotkey.go b/hotkey.go index d441cd7..3c71116 100644 --- a/hotkey.go +++ b/hotkey.go @@ -44,6 +44,20 @@ import ( "runtime" ) +var ( + errIn chan<- Event + errOut <-chan Event +) + +// Err returns a channel that receives a signal when an unexpected error has occured. +// On Linux systems using X11 this indicates a failed XGrabKey. +// Not used for Windows and Darwin +func Err() <-chan Event { return errOut } + +func init() { + errIn, errOut = newEventChan() +} + // Event represents a hotkey event type Event struct{} diff --git a/hotkey_linux.c b/hotkey_linux.c index 0cb8b1c..f73c97b 100644 --- a/hotkey_linux.c +++ b/hotkey_linux.c @@ -11,9 +11,28 @@ #include #include +extern void err(); extern void hotkeyDown(uintptr_t hkhandle); extern void hotkeyUp(uintptr_t hkhandle); +static int xErrHandler(Display* d, XErrorEvent* pErr) +{ + if( pErr->request_code == 33 ){ // X_GrabKey + if( pErr->error_code == BadAccess ){ + err(); + return 0; + } + } else { + printf("X Error Handler called, values: %d/%lu/%d/%d/%d\n", + pErr->type, + pErr->serial, + pErr->error_code, + pErr->request_code, + pErr->minor_code); + } + return 0; +} + int displayTest() { Display* d = NULL; for (int i = 0; i < 42; i++) { @@ -24,27 +43,10 @@ int displayTest() { if (d == NULL) { return -1; } + XSetErrorHandler(xErrHandler); return 0; } -// FIXME: handle bad access properly. -// int handleErrors( Display* dpy, XErrorEvent* pErr ) -// { -// printf("X Error Handler called, values: %d/%lu/%d/%d/%d\n", -// pErr->type, -// pErr->serial, -// pErr->error_code, -// pErr->request_code, -// pErr->minor_code ); -// if( pErr->request_code == 33 ){ // 33 (X_GrabKey) -// if( pErr->error_code == BadAccess ){ -// printf("ERROR: key combination already grabbed by another client.\n"); -// return 0; -// } -// } -// return 0; -// } - // waitHotkey blocks until the hotkey is triggered. // this function crashes the program if the hotkey already grabbed by others. int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) { @@ -59,6 +61,7 @@ int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) { } int keycode = XKeysymToKeycode(d, key); XGrabKey(d, keycode, mod, DefaultRootWindow(d), False, GrabModeAsync, GrabModeAsync); + XSelectInput(d, DefaultRootWindow(d), KeyPressMask); XEvent ev; while(1) { @@ -74,4 +77,4 @@ int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) { return 0; } } -} \ No newline at end of file +} diff --git a/hotkey_linux.go b/hotkey_linux.go index a988213..06389bc 100644 --- a/hotkey_linux.go +++ b/hotkey_linux.go @@ -44,6 +44,11 @@ func init() { } } +//export err +func err() { + errIn <- Event{} +} + type platformHotkey struct { mu sync.Mutex registered bool @@ -52,7 +57,6 @@ type platformHotkey struct { canceled chan struct{} } -// Nothing needs to do for register func (hk *Hotkey) register() error { hk.mu.Lock() if hk.registered { @@ -68,7 +72,6 @@ func (hk *Hotkey) register() error { return nil } -// Nothing needs to do for unregister func (hk *Hotkey) unregister() error { hk.mu.Lock() defer hk.mu.Unlock() @@ -86,7 +89,6 @@ func (hk *Hotkey) unregister() error { 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 {