Skip to content

Commit

Permalink
feat(linux): expose XSetErrorHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
ventsislav-georgiev committed Jan 5, 2022
1 parent b950a6e commit 16e8db4
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 21 deletions.
91 changes: 91 additions & 0 deletions examples/multiple/main_linux.go
Original file line number Diff line number Diff line change
@@ -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
}
37 changes: 19 additions & 18 deletions hotkey_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -74,4 +75,4 @@ int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) {
return 0;
}
}
}
}
35 changes: 32 additions & 3 deletions hotkey_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ package hotkey
#include <stdint.h>
int displayTest();
int setErrorHandler();
int restoreErrorHandler();
int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key);
*/
import "C"
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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()
Expand All @@ -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 {
Expand Down

0 comments on commit 16e8db4

Please sign in to comment.