Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(linux): expose XSetErrorHandler #12

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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