From 16e8db4bb80e964da5144fcc054ca0234272f1b5 Mon Sep 17 00:00:00 2001 From: ventsislav-georgiev Date: Thu, 6 Jan 2022 01:21:47 +0200 Subject: [PATCH] feat(linux): expose XSetErrorHandler --- examples/multiple/main_linux.go | 91 +++++++++++++++++++++++++++++++++ hotkey_linux.c | 37 +++++++------- hotkey_linux.go | 35 +++++++++++-- 3 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 examples/multiple/main_linux.go diff --git a/examples/multiple/main_linux.go b/examples/multiple/main_linux.go new file mode 100644 index 0000000..1e298d5 --- /dev/null +++ b/examples/multiple/main_linux.go @@ -0,0 +1,91 @@ +// Copyright 2022 The golang.design Initiative Authors. +// All rights reserved. Use of this source code is governed +// by a MIT license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "log" + "sync" + + "golang.design/x/hotkey" + "golang.design/x/hotkey/mainthread" +) + +func main() { mainthread.Init(fn) } +func fn() { + wg := sync.WaitGroup{} + wg.Add(2) + + hotkey.XSetErrorHandler(func(e hotkey.XErrorEvent) { + fmt.Printf("X error: %v\n", e) + }) + + go func() { + defer wg.Done() + + err := listenHotkey(hotkey.KeyS, hotkey.ModCtrl, hotkey.ModShift) + if err != nil { + log.Println(err) + } + }() + go func() { + defer wg.Done() + + err := listenHotkey(hotkey.KeyA, hotkey.ModCtrl, hotkey.ModShift) + if err != nil { + log.Println(err) + } + }() + go func() { + defer wg.Done() + + err := listenHotkey(hotkey.KeyA, hotkey.ModCtrl, hotkey.ModShift) + if err != nil { + log.Println(err) + } + }() + wg.Wait() + + hotkey.XRestoreErrorHandler() + + // Will now crash with BadAccess + wg.Add(2) + go func() { + defer wg.Done() + + err := listenHotkey(hotkey.KeyA, hotkey.ModCtrl, hotkey.ModShift) + if err != nil { + log.Println(err) + } + }() + go func() { + defer wg.Done() + + err := listenHotkey(hotkey.KeyA, hotkey.ModCtrl, hotkey.ModShift) + if err != nil { + log.Println(err) + } + }() + wg.Wait() +} + +func listenHotkey(key hotkey.Key, mods ...hotkey.Modifier) (err error) { + ms := []hotkey.Modifier{} + ms = append(ms, mods...) + hk := hotkey.New(ms, key) + + err = hk.Register() + if err != nil { + return + } + + // Blocks until the hokey is triggered. + <-hk.Keydown() + log.Printf("hotkey: %v is down\n", hk) + <-hk.Keyup() + log.Printf("hotkey: %v is up\n", hk) + hk.Unregister() + return +} diff --git a/hotkey_linux.c b/hotkey_linux.c index 0cb8b1c..1a23174 100644 --- a/hotkey_linux.c +++ b/hotkey_linux.c @@ -13,6 +13,7 @@ extern void hotkeyDown(uintptr_t hkhandle); extern void hotkeyUp(uintptr_t hkhandle); +extern void onError(int typ, unsigned long serial, unsigned char error_code, unsigned char request_code, unsigned char minor_code); int displayTest() { Display* d = NULL; @@ -27,23 +28,23 @@ 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 handleErrors(Display* dpy, XErrorEvent* pErr) +{ + onError(pErr->type, pErr->serial, pErr->error_code, pErr->request_code, pErr->minor_code ); + return 0; +} + +static int (*defaultErrHandler)(Display*, XErrorEvent*); + +int setErrorHandler() { + if (!defaultErrHandler) { + defaultErrHandler = XSetErrorHandler(handleErrors); + } +} + +int restoreErrorHandler() { + XSetErrorHandler(defaultErrHandler); +} // waitHotkey blocks until the hotkey is triggered. // this function crashes the program if the hotkey already grabbed by others. @@ -74,4 +75,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..b3872fd 100644 --- a/hotkey_linux.go +++ b/hotkey_linux.go @@ -14,6 +14,8 @@ package hotkey #include int displayTest(); +int setErrorHandler(); +int restoreErrorHandler(); int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key); */ import "C" @@ -44,6 +46,36 @@ func init() { } } +type XErrorEvent struct { + typ int + serial uint64 + errorCode uint8 + requestCode uint8 + minorCode uint8 +} + +var ( + errHandler func(e XErrorEvent) +) + +func XSetErrorHandler(handler func(e XErrorEvent)) { + errHandler = handler + C.setErrorHandler() +} + +func XRestoreErrorHandler() { + C.restoreErrorHandler() + errHandler = nil +} + +//export onError +func onError(typ int, serial uint64, errorCode uint8, requestCode uint8, minorCode uint8) { + if errHandler == nil { + return + } + errHandler(XErrorEvent{typ, serial, errorCode, requestCode, minorCode}) +} + type platformHotkey struct { mu sync.Mutex registered bool @@ -52,7 +84,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 +99,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 +116,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 {