Made the library Asyncronous (#2)

* Part 1 of supporting async events

made the dispatch process send events via json through a channel (the c kind), made another function that receives it and sends it trough another channel (the go kind)

todo: remove usage of function-local data trough channel
todo:find error that is causing the c channel to fill up?

* Part 2 of making the lib async

 - New event struct mimiking C structs
 - changed c channel library to eb_chan
 - changed API to something more palatable

* General cleanup

* updated go.mod

oopsie

* Probably final touches

i swear this time

* Changed constants, keychar type and test function

now Keychar is a rune, so it can be used as a regular character.

* WIP adding better support for conversion between Scancodes and Keychars

added table, will add rest of support soon(tm)

* finished conversion table

* Satisfied stringer interface for easier debug and logging, returned old function

what the title says

* Satisfied stringer interface for easier debug and logging, returned old function

what the title says

* forgot to import unsafe
This commit is contained in:
Cauê Felchar 2019-02-19 11:31:25 -03:00 committed by vz
parent e36d1aac6c
commit f0ddfd620c
12 changed files with 2058 additions and 140 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ _testmain.go
*.exe
*.test
*.prof
*.idea

View File

@ -1,25 +1,27 @@
# gohook
[![CircleCI Status](https://circleci.com/gh/robotn/gohook.svg?style=shield)](https://circleci.com/gh/robotn/gohook)
![Appveyor](https://ci.appveyor.com/api/projects/status/github/robotn/gohook?branch=master&svg=true)
[![Go Report Card](https://goreportcard.com/badge/github.com/robotn/gohook)](https://goreportcard.com/report/github.com/robotn/gohook)
[![GoDoc](https://godoc.org/github.com/robotn/gohook?status.svg)](https://godoc.org/github.com/robotn/gohook)
[![CircleCI Status](https://circleci.com/gh/cauefcr/gohook.svg?style=shield)](https://circleci.com/gh/cauefcr/gohook)
![Appveyor](https://ci.appveyor.com/api/projects/status/github/cauefcr/gohook?branch=master&svg=true)
[![Go Report Card](https://goreportcard.com/badge/github.com/cauefcr/gohook)](https://goreportcard.com/report/github.com/cauefcr/gohook)
[![GoDoc](https://godoc.org/github.com/cauefcr/gohook?status.svg)](https://godoc.org/github.com/cauefcr/gohook)
<!-- This is a work in progress. -->
Based on [libuiohook](https://github.com/kwhat/libuiohook)
```Go
package main
import (
"fmt"
"github.com/robotn/gohook"
//"github.com/robotn/gohook"
"github.com/cauefcr/gohook"
)
func main() {
// hook.AsyncHook()
veve := hook.AddEvent("v")
if veve == 0 {
fmt.Println("v...")
EvChan := hook.Start()
defer hook.End()
for ev := range EvChan {
fmt.Println(ev)
}
}
```

1430
chan/eb_chan.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,146 +7,116 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#ifndef goevent_h
#define goevent_h
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include "pub.h"
#include "../chan/eb_chan.h"
eb_chan events;
void go_send(char*);
void go_sleep(void);
bool sending = false;
void startev(){
events = eb_chan_create(1024);
eb_chan_retain(events);
sending = true;
add_event("q");
}
void pollEv(){
if(events == NULL) return;
for(;eb_chan_buf_len(events)!=0;){
char* tmp;
if(eb_chan_try_recv(events,(const void**) &tmp) == eb_chan_res_ok){
go_send(tmp);
free(tmp);
}else{
//
}
}
}
void endPoll(){
sending = false;
pollEv();//remove last things from channel
eb_chan_release(events);
}
void dispatch_proc(iohook_event * const event) {
char buffer[256] = { 0 };
size_t length = snprintf(buffer, sizeof(buffer),
"id=%i,when=%" PRIu64 ",mask=0x%X",
event->type, event->time, event->mask);
if(!sending) return;
//leaking memory? hope not
char* buffer = calloc(200,sizeof(char));
switch (event->type) {
case EVENT_HOOK_ENABLED:
case EVENT_HOOK_DISABLED:
sprintf(buffer,"{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu}",
event->type, event->time, event->mask,event->reserved);
break;//send it?
case EVENT_KEY_PRESSED:
// If the escape key is pressed, naturally terminate the program.
if (event->data.keyboard.keycode == VC_ESCAPE) {
// int status = hook_stop();
// switch (status) {
// // System level errors.
// case IOHOOK_ERROR_OUT_OF_MEMORY:
// loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
// break;
// case IOHOOK_ERROR_X_RECORD_GET_CONTEXT:
// // NOTE This is the only platform specific error that occurs on hook_stop().
// loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
// break;
// // Default error.
// case IOHOOK_FAILURE:
// default:
// loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
// break;
// }
}
case EVENT_KEY_RELEASED:
snprintf(buffer + length, sizeof(buffer) - length,
",keycode=%u,rawcode=0x%X",
event->data.keyboard.keycode, event->data.keyboard.rawcode);
int key_code = (uint16_t) event->data.keyboard.keycode;
if (event->data.keyboard.keycode == VC_ESCAPE
&& atoi(cevent) == 11) {
int stopEvent = stop_event();
// printf("stop_event%d\n", stopEvent);
cstatus = 0;
}
// printf("atoi(str)---%d\n", atoi(cevent));
if (key_code == atoi(cevent)) {
int stopEvent = stop_event();
// printf("%d\n", stopEvent);
cstatus = 0;
}
break;
case EVENT_KEY_TYPED:
snprintf(buffer + length, sizeof(buffer) - length,
",keychar=%lc,rawcode=%u",
(uint16_t) event->data.keyboard.keychar,
event->data.keyboard.rawcode);
#ifdef WE_REALLY_WANT_A_POINTER
char *buf = malloc (6);
#else
char buf[6];
#endif
sprintf(buf, "%lc", (uint16_t) event->data.keyboard.keychar);
#ifdef WE_REALLY_WANT_A_POINTER
free (buf);
#endif
if (strcmp(buf, cevent) == 0) {
int stopEvent = stop_event();
// printf("%d\n", stopEvent);
cstatus = 0;
}
// return (char*) event->data.keyboard.keychar;
break;
sprintf(buffer,
"{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu,\"keycode\":%hu,\"rawcode\":%hu,\"keychar\":%hu}",
event->type, event->time, event->mask,event->reserved,
event->data.keyboard.keycode,
event->data.keyboard.rawcode,
event->data.keyboard.keychar);
break;
case EVENT_MOUSE_PRESSED:
case EVENT_MOUSE_RELEASED:
case EVENT_MOUSE_CLICKED:
case EVENT_MOUSE_MOVED:
case EVENT_MOUSE_DRAGGED:
snprintf(buffer + length, sizeof(buffer) - length,
",x=%i,y=%i,button=%i,clicks=%i",
event->data.mouse.x, event->data.mouse.y,
event->data.mouse.button, event->data.mouse.clicks);
int abutton = event->data.mouse.button;
int aclicks = event->data.mouse.clicks;
int amouse = -1;
if (strcmp(cevent, "mleft") == 0) {
amouse = 1;
}
if (strcmp(cevent, "mright") == 0) {
amouse = 2;
}
if (strcmp(cevent, "wheelDown") == 0) {
amouse = 4;
}
if (strcmp(cevent, "wheelUp") == 0) {
amouse = 5;
}
if (strcmp(cevent, "wheelLeft") == 0) {
amouse = 6;
}
if (strcmp(cevent, "wheelRight") == 0) {
amouse = 7;
}
if (abutton == amouse && aclicks == 1) {
int stopEvent = stop_event();
cstatus = 0;
}
sprintf(buffer,
"{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu,\"x\":%hd,\"y\":%hd,\"button\":%u,\"clicks\":%u}",
event->type, event->time, event->mask,event->reserved,
event->data.mouse.x,
event->data.mouse.y,
event->data.mouse.button,
event->data.mouse.clicks);
break;
case EVENT_MOUSE_WHEEL:
snprintf(buffer + length, sizeof(buffer) - length,
",type=%i,amount=%i,rotation=%i",
event->data.wheel.type, event->data.wheel.amount,
event->data.wheel.rotation);
sprintf(buffer,
"{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu,\"clicks\":%hu,\"x\":%hd,\"y\":%hd,\"type\":%hu,\"ammount\":%hu,\"rotation\":%hd,\"direction\":%hu}",
event->type, event->time, event->mask, event->reserved,
event->data.wheel.clicks,
event->data.wheel.x,
event->data.wheel.y,
event->data.wheel.type,
event->data.wheel.amount,
event->data.wheel.rotation,
event->data.wheel.direction);
break;
default:
break;
fprintf(stderr,"\nError on file: %s, unusual event->type: %i\n",__FILE__,event->type);
return;
}
//to-do remove this for
for(int i = 0; i < 5; i++){
switch(eb_chan_try_send(events,buffer)){ //never block the hook callback
case eb_chan_res_ok:
i=5;
break;
default:
if (i == 4) {//let's not leak memory
free(buffer);
}
continue;
}
}
// fprintf(stdout, "----%s\n", buffer);
}
int add_event(char *key_event) {
// (uint16_t *)
cevent = key_event;
// Set the logger callback for library output.
hook_set_logger(&loggerProc);
@ -242,7 +212,7 @@ int stop_event(){
loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
break;
// Default error.
// Default error.
case IOHOOK_FAILURE:
default:
// loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
@ -250,4 +220,6 @@ int stop_event(){
}
return status;
}
}
#endif

View File

@ -23,7 +23,7 @@
#endif /* USE_X11 */
#if defined(IS_WINDOWS)
#define STRICT /* Require use of exact types. */
// #define STRICT /* Require use of exact types. */
#define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */
#include <windows.h>
#elif !defined(IS_MACOSX) && !defined(USE_X11)

33
extern.go Normal file
View File

@ -0,0 +1,33 @@
package hook
/*
// #include "event/hook_async.h"
*/
import "C"
import (
"encoding/json"
"log"
"time"
)
//export go_send
func go_send(s *C.char) {
str := []byte(C.GoString(s))
out := Event{}
err := json.Unmarshal(str, &out)
if err != nil {
log.Fatal(err)
}
if out.Keychar != CharUndefined {
raw2key[out.Rawcode] = string([]rune{out.Keychar})
}
//todo bury this deep into the C lib so that the time is correct
out.When = time.Now() //at least it's consistent
if err != nil {
log.Fatal(err)
}
//todo: maybe make non-bloking
ev <- out
}

2
go.mod
View File

@ -1 +1 @@
module github.com/robotn/gohook
module github.com/cauefcr/gohook

121
hook.go
View File

@ -20,15 +20,127 @@ package hook
//#cgo windows LDFLAGS: -lgdi32 -luser32
#include "event/goEvent.h"
// #include "event/hook_async.h"
*/
import "C"
import (
// "fmt"
"fmt"
"time"
"unsafe"
)
const (
HookEnabled = 1 //iota
HookDisabled = 2
KeyDown = 3
KeyHold = 4
KeyUp = 5
MouseUp = 6
MouseHold = 7
MouseDown = 8
MouseMove = 9
MouseDrag = 10
MouseWheel = 11
FakeEvent = 12
//Keychar could be v
CharUndefined = 0xFFFF
WheelUp = -1
WheelDown = 1
)
//Holds a system event
//If it's a Keyboard event the relevant fields are: Mask, Keycode, Rawcode, and Keychar,
//Keychar is probably what you want. If it's a Mouse event the relevant fields are:
//Button, Clicks, X, Y, Amount, Rotation and Direction
type Event struct {
Kind uint8 `json:"id"`
When time.Time
Mask uint16 `json:"mask"`
Reserved uint16 `json:"reserved"`
Keycode uint16 `json:"keycode"`
Rawcode uint16 `json:"rawcode"`
Keychar rune `json:"keychar"`
Button uint16 `json:"button"`
Clicks uint16 `json:"clicks"`
X int16 `json:"x"`
Y int16 `json:"y"`
Amount uint16 `json:"amount"`
Rotation int16 `json:"rotation"`
Direction uint8 `json:"direction"`
}
var (
ev = make(chan Event, 1024)
asyncon = false
)
func (e Event) String() string {
switch e.Kind {
case HookEnabled:
return fmt.Sprintf("%v - Event: {Kind: HookEnabled}", e.When)
case HookDisabled:
return fmt.Sprintf("%v - Event: {Kind: HookDisabled}", e.When)
case KeyUp:
return fmt.Sprintf("%v - Event: {Kind: KeyUp, Rawcode: %v, Keychar: %v}", e.When, e.Rawcode, e.Keychar)
case KeyHold:
return fmt.Sprintf("%v - Event: {Kind: KeyHold, Rawcode: %v, Keychar: %v}", e.When, e.Rawcode, e.Keychar)
case KeyDown:
return fmt.Sprintf("%v - Event: {Kind: KeyDown, Rawcode: %v, Keychar: %v}", e.When, e.Rawcode, e.Keychar)
case MouseUp:
return fmt.Sprintf("%v - Event: {Kind: MouseUp, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseHold:
return fmt.Sprintf("%v - Event: {Kind: MouseHold, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseDown:
return fmt.Sprintf("%v - Event: {Kind: MouseDown, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseMove:
return fmt.Sprintf("%v - Event: {Kind: MouseMove, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseDrag:
return fmt.Sprintf("%v - Event: {Kind: MouseDrag, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseWheel:
return fmt.Sprintf("%v - Event: {Kind: MouseWheel, Amount: %v, Rotation: %v, Direction: %v}", e.When, e.Amount, e.Rotation, e.Direction)
case FakeEvent:
return fmt.Sprintf("%v - Event: {Kind: FakeEvent}", e.When)
}
return "Unknown event, contact the mantainers"
}
func RawcodetoKeychar(r uint16) string {
return raw2key[r]
}
func KeychartoRawcode(kc string) uint16 {
return keytoraw[kc]
}
// Adds global event hook to OS
// returns event channel
func Start() chan Event {
asyncon = true
go C.startev()
go func() {
for {
C.pollEv()
time.Sleep(time.Millisecond * 50)
//todo: find smallest time that does not destroy the cpu utilization
if !asyncon {
return
}
}
}()
return ev
}
// End removes global event hook
func End() {
C.endPoll()
C.stop_event()
for len(ev) != 0 {
<-ev
}
asyncon = false
}
// AddEvent add event listener
func AddEvent(key string) int {
cs := C.CString(key)
@ -39,8 +151,3 @@ func AddEvent(key string) int {
return geve
}
// StopEvent stop event listener
func StopEvent() {
C.stop_event()
}

View File

@ -79,7 +79,7 @@ typedef struct _screen_data {
typedef struct _keyboard_event_data {
uint16_t keycode;
uint16_t rawcode;
uint16_t keychar;
uint32_t keychar;
// char *keychar;
} keyboard_event_data,
key_pressed_event_data,

View File

@ -293,8 +293,8 @@ unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) {
scancode = keycode_scancode_table[vk_code][0];
if (flags & LLKHF_EXTENDED) {
logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n",
__FUNCTION__, __LINE__, vk_code);
// logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n",
// __FUNCTION__, __LINE__, vk_code);
switch (vk_code) {
case VK_PRIOR:

351
tables.go Normal file
View File

@ -0,0 +1,351 @@
package hook
var (
raw2key = map[uint16]string{ //https://github.com/wesbos/keycodes
0: "error",
3: "break",
8: "backspace",
9: "tab",
12: "clear",
13: "enter",
16: "shift",
17: "ctrl",
18: "alt",
19: "pause/break",
20: "caps lock",
21: "hangul",
25: "hanja",
27: "escape",
28: "conversion",
29: "non-conversion",
32: "spacebar",
33: "page up",
34: "page down",
35: "end",
36: "home",
37: "left arrow",
38: "up arrow",
39: "right arrow",
40: "down arrow",
41: "select",
42: "print",
43: "execute",
44: "Print Screen",
45: "insert",
46: "delete",
47: "help",
48: "0",
49: "1",
50: "2",
51: "3",
52: "4",
53: "5",
54: "6",
55: "7",
56: "8",
57: "9",
58: ":",
59: ";",
60: "<",
61: "=",
63: "ß",
64: "@",
65: "a",
66: "b",
67: "c",
68: "d",
69: "e",
70: "f",
71: "g",
72: "h",
73: "i",
74: "j",
75: "k",
76: "l",
77: "m",
78: "n",
79: "o",
80: "p",
81: "q",
82: "r",
83: "s",
84: "t",
85: "u",
86: "v",
87: "w",
88: "x",
89: "y",
90: "z",
91: "l-super",
92: "r-super",
93: "apps",
95: "sleep",
96: "numpad 0",
97: "numpad 1",
98: "numpad 2",
99: "numpad 3",
100: "numpad 4",
101: "numpad 5",
102: "numpad 6",
103: "numpad 7",
104: "numpad 8",
105: "numpad 9",
106: "multiply",
107: "add",
108: "numpad period",
109: "subtract",
110: "decimal point",
111: "divide",
112: "f1",
113: "f2",
114: "f3",
115: "f4",
116: "f5",
117: "f6",
118: "f7",
119: "f8",
120: "f9",
121: "f10",
122: "f11",
123: "f12",
124: "f13",
125: "f14",
126: "f15",
127: "f16",
128: "f17",
129: "f18",
130: "f19",
131: "f20",
132: "f21",
133: "f22",
134: "f23",
135: "f24",
144: "num lock",
145: "scroll lock",
160: "^",
161: "!",
162: "؛",
163: "#",
164: "$",
165: "ù",
166: "page backward",
167: "page forward",
168: "refresh",
169: "closing paren (AZERTY)",
170: "*",
171: "~ + * key",
172: "home key",
173: "minus (firefox), mute/unmute",
174: "decrease volume level",
175: "increase volume level",
176: "next",
177: "previous",
178: "stop",
179: "play/pause",
180: "e-mail",
181: "mute/unmute (firefox)",
182: "decrease volume level (firefox)",
183: "increase volume level (firefox)",
186: "semi-colon / ñ",
187: "equal sign",
188: "comma",
189: "dash",
190: "period",
191: "forward slash / ç",
192: "grave accent / ñ / æ / ö",
193: "?, / or °",
194: "numpad period (chrome)",
219: "open bracket",
220: "back slash",
221: "close bracket / å",
222: "single quote / ø / ä",
223: "`",
224: "left or right ⌘ key (firefox)",
225: "altgr",
226: "< /git >, left back slash",
230: "GNOME Compose Key",
231: "ç",
233: "XF86Forward",
234: "XF86Back",
235: "non-conversion",
240: "alphanumeric",
242: "hiragana/katakana",
243: "half-width/full-width",
244: "kanji",
251: "unlock trackpad (Chrome/Edge)",
255: "toggle touchpad",
}
keytoraw = map[string]uint16{
"error": 0,
"break": 3,
"backspace": 8,
"tab": 9,
"clear": 12,
"enter": 13,
"shift": 16,
"ctrl": 17,
"alt": 18,
"pause/break": 19,
"caps lock": 20,
"hangul": 21,
"hanja": 25,
"escape": 27,
"conversion": 28,
"non-conversion": 29,
"spacebar": 32,
"page up": 33,
"page down": 34,
"end": 35,
"home": 36,
"left arrow": 37,
"up arrow": 38,
"right arrow": 39,
"down arrow": 40,
"select": 41,
"print": 42,
"execute": 43,
"Print Screen": 44,
"insert": 45,
"delete": 46,
"help": 47,
"0": 48,
"1": 49,
"2": 50,
"3": 51,
"4": 52,
"5": 53,
"6": 54,
"7": 55,
"8": 56,
"9": 57,
":": 58,
";": 59,
"<": 60,
"=": 61,
"ß": 63,
"@": 64,
"a": 65,
"b": 66,
"c": 67,
"d": 68,
"e": 69,
"f": 70,
"g": 71,
"h": 72,
"i": 73,
"j": 74,
"k": 75,
"l": 76,
"m": 77,
"n": 78,
"o": 79,
"p": 80,
"q": 81,
"r": 82,
"s": 83,
"t": 84,
"u": 85,
"v": 86,
"w": 87,
"x": 88,
"y": 89,
"z": 90,
"l-super": 91,
"r-super": 92,
"apps": 93,
"sleep": 95,
"numpad 0": 96,
"numpad 1": 97,
"numpad 2": 98,
"numpad 3": 99,
"numpad 4": 100,
"numpad 5": 101,
"numpad 6": 102,
"numpad 7": 103,
"numpad 8": 104,
"numpad 9": 105,
"multiply": 106,
"add": 107,
"numpad period": 108,
"subtract": 109,
"decimal point": 110,
"divide": 111,
"f1": 112,
"f2": 113,
"f3": 114,
"f4": 115,
"f5": 116,
"f6": 117,
"f7": 118,
"f8": 119,
"f9": 120,
"f10": 121,
"f11": 122,
"f12": 123,
"f13": 124,
"f14": 125,
"f15": 126,
"f16": 127,
"f17": 128,
"f18": 129,
"f19": 130,
"f20": 131,
"f21": 132,
"f22": 133,
"f23": 134,
"f24": 135,
"num lock": 144,
"scroll lock": 145,
"^": 160,
"!": 161,
"؛": 162,
"#": 163,
"$": 164,
"ù": 165,
"page backward": 166,
"page forward": 167,
"refresh": 168,
"closing paren (AZERTY)": 169,
"*": 170,
"~ + * key": 171,
"home key": 172,
"minus (firefox), mute/unmute": 173,
"decrease volume level": 174,
"increase volume level": 175,
"next": 176,
"previous": 177,
"stop": 178,
"play/pause": 179,
"e-mail": 180,
"mute/unmute (firefox)": 181,
"decrease volume level (firefox)": 182,
"increase volume level (firefox)": 183,
"semi-colon / ñ": 186,
"equal sign": 187,
"comma": 188,
"dash": 189,
"period": 190,
"forward slash / ç": 191,
"grave accent / ñ / æ / ö": 192,
"?, / or °": 193,
"numpad period (chrome)": 194,
"open bracket": 219,
"back slash": 220,
"close bracket / å": 221,
"single quote / ø / ä": 222,
"`": 223,
"left or right ⌘ key (firefox)": 224,
"altgr": 225,
"< /git >, left back slash": 226,
"GNOME Compose Key": 230,
"ç": 231,
"XF86Forward": 233,
"XF86Back": 234,
"alphanumeric": 240,
"hiragana/katakana": 242,
"half-width/full-width": 243,
"kanji": 244,
"unlock trackpad (Chrome/Edge)": 251,
"toggle touchpad": 255,
}
)

View File

@ -2,14 +2,36 @@ package main
import (
"fmt"
"time"
"github.com/robotn/gohook"
"github.com/cauefcr/gohook"
)
func main() {
// hook.AsyncHook()
veve := hook.AddEvent("v")
if veve == 0 {
fmt.Println("v...")
s := hook.Start()
defer hook.End()
tout := time.After(time.Hour*2)
done := false
for !done {
select {
case i := <-s:
if i.Kind >= hook.KeyDown && i.Kind <= hook.KeyUp {
if i.Keychar == 'q' {
tout = time.After(0)
}
fmt.Printf("%v key: %c:%v\n",i.Kind, i.Keychar,i.Rawcode)
} else if i.Kind >= hook.MouseDown && i.Kind < hook.MouseWheel {
//fmt.Printf("x: %v, y: %v, button: %v\n", i.X, i.Y, i.Button)
}else if i.Kind == hook.MouseWheel {
//fmt.Printf("x: %v, y: %v, button: %v, wheel: %v, rotation: %v\n", i.X, i.Y, i.Button,i.Amount,i.Rotation)
} else {
fmt.Printf("%+v\n",i)
}
case <-tout:
fmt.Print("Done.")
done = true
break
}
}
}