Skip to content

Commit

Permalink
fix(linux): crash on existing hotkey grab
Browse files Browse the repository at this point in the history
  • Loading branch information
ventsislav-georgiev committed Jan 5, 2022
1 parent b950a6e commit 124fe8d
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 22 deletions.
17 changes: 16 additions & 1 deletion examples/multiple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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()
}

Expand Down
14 changes: 14 additions & 0 deletions hotkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}

Expand Down
45 changes: 27 additions & 18 deletions hotkey_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>

extern void err();
extern void hotkeyDown(uintptr_t hkhandle);
extern void hotkeyUp(uintptr_t hkhandle);

Expand All @@ -27,27 +28,33 @@ int displayTest() {
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;
// }
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 setupErrHandler() {
XSetErrorHandler(xErrHandler);
}

// 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) {
int (*defaultErrHandler)(Display*, XErrorEvent*);

Display* d = NULL;
for (int i = 0; i < 42; i++) {
d = XOpenDisplay(0);
Expand All @@ -57,8 +64,10 @@ int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) {
if (d == NULL) {
return -1;
}

int keycode = XKeysymToKeycode(d, key);
XGrabKey(d, keycode, mod, DefaultRootWindow(d), False, GrabModeAsync, GrabModeAsync);

XSelectInput(d, DefaultRootWindow(d), KeyPressMask);
XEvent ev;
while(1) {
Expand All @@ -74,4 +83,4 @@ int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) {
return 0;
}
}
}
}
10 changes: 7 additions & 3 deletions hotkey_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package hotkey
#include <stdint.h>
int displayTest();
int setupErrHandler();
int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key);
*/
import "C"
Expand Down Expand Up @@ -42,6 +43,12 @@ func init() {
if C.displayTest() != 0 {
panic(errmsg)
}
C.setupErrHandler()
}

//export err
func err() {
errIn <- Event{}
}

type platformHotkey struct {
Expand All @@ -52,7 +59,6 @@ type platformHotkey struct {
canceled chan struct{}
}

// Nothing needs to do for register
func (hk *Hotkey) register() error {
hk.mu.Lock()
if hk.registered {
Expand All @@ -68,7 +74,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()
Expand All @@ -86,7 +91,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 {
Expand Down

0 comments on commit 124fe8d

Please sign in to comment.