Skip to content

Commit

Permalink
all: support keyup event (linux, x11)
Browse files Browse the repository at this point in the history
Updates #9
  • Loading branch information
changkun committed Jan 4, 2022
1 parent 743f5b9 commit b4e6fbb
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 11 deletions.
6 changes: 5 additions & 1 deletion hotkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@ func New(mods []Modifier, key Key) *Hotkey {
// collaborating with the golang.design/x/hotkey/mainthread package.
func (hk *Hotkey) Register() error { return hk.register() }

// Keydown returns a channel that receives a signal when hotkey is triggered.
// Keydown returns a channel that receives a signal when the hotkey is triggered.
func (hk *Hotkey) Keydown() <-chan Event { return hk.keydownOut }

// Keyup returns a channel that receives a signal when the hotkey is released.
//
// Platform specific details:
// - On Linux (X11), When AutoRepeat is enabled in the X server, the Keyup
// is triggered automatically and continuously as Keydown continues.
func (hk *Hotkey) Keyup() <-chan Event { return hk.keyupOut }

// Unregister unregisters the hotkey.
Expand Down
13 changes: 9 additions & 4 deletions hotkey_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@

//go:build linux

#include <stdint.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

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

int displayTest() {
Display* d = NULL;
for (int i = 0; i < 42; i++) {
Expand Down Expand Up @@ -43,10 +47,7 @@ int displayTest() {

// waitHotkey blocks until the hotkey is triggered.
// this function crashes the program if the hotkey already grabbed by others.
int waitHotkey(unsigned int mod, int key) {
// FIXME: handle registered hotkey properly.
// XSetErrorHandler(handleErrors);

int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key) {
Display* d = NULL;
for (int i = 0; i < 42; i++) {
d = XOpenDisplay(0);
Expand All @@ -64,6 +65,10 @@ int waitHotkey(unsigned int mod, int key) {
XNextEvent(d, &ev);
switch(ev.type) {
case KeyPress:
hotkeyDown(hkhandle);
continue;
case KeyRelease:
hotkeyUp(hkhandle);
XUngrabKey(d, keycode, mod, DefaultRootWindow(d));
XCloseDisplay(d);
return 0;
Expand Down
29 changes: 23 additions & 6 deletions hotkey_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ package hotkey
/*
#cgo LDFLAGS: -lX11
#include <stdint.h>
int displayTest();
int waitHotkey(unsigned int mod, int key);
int waitHotkey(uintptr_t hkhandle, unsigned int mod, int key);
*/
import "C"
import (
"context"
"errors"
"runtime"
"runtime/cgo"
"sync"
)

Expand Down Expand Up @@ -80,27 +84,40 @@ func (hk *Hotkey) unregister() error {
// handle registers an application global hotkey to the system,
// and returns a channel that will signal if the hotkey is triggered.
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 {
mod = mod | m
}
h := cgo.NewHandle(hk)
defer h.Delete()

for {
select {
case <-hk.ctx.Done():
close(hk.canceled)
return
default:
ret := C.waitHotkey(C.uint(mod), C.int(hk.key))
if ret != 0 {
continue
}
hk.keydownIn <- Event{}
_ = C.waitHotkey(C.uintptr_t(h), C.uint(mod), C.int(hk.key))
}
}
}

//export hotkeyDown
func hotkeyDown(h uintptr) {
hk := cgo.Handle(h).Value().(*Hotkey)
hk.keydownIn <- Event{}
}

//export hotkeyUp
func hotkeyUp(h uintptr) {
hk := cgo.Handle(h).Value().(*Hotkey)
hk.keyupIn <- Event{}
}

// Modifier represents a modifier.
type Modifier uint32

Expand Down

0 comments on commit b4e6fbb

Please sign in to comment.