mirror of
https://github.com/robotn/gohook.git
synced 2025-04-28 02:04:59 +08:00
333 lines
7.1 KiB
Go
333 lines
7.1 KiB
Go
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <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.
|
|
|
|
package hook
|
|
|
|
/*
|
|
#cgo darwin CFLAGS: -x objective-c -Wno-deprecated-declarations
|
|
#cgo darwin LDFLAGS: -framework Cocoa
|
|
|
|
#cgo linux CFLAGS:-I/usr/src
|
|
#cgo linux LDFLAGS: -L/usr/src -lX11 -lXtst
|
|
#cgo linux LDFLAGS: -lX11-xcb -lxcb -lxcb-xkb -lxkbcommon -lxkbcommon-x11
|
|
//#cgo windows LDFLAGS: -lgdi32 -luser32
|
|
|
|
#include "event/goEvent.h"
|
|
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
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
|
|
pressed = make(map[uint16]bool, 256)
|
|
used = []int{}
|
|
keys = map[int][]uint16{}
|
|
cbs = map[int]func(Event){}
|
|
events = map[uint8][]int{}
|
|
grOn = 0
|
|
)
|
|
|
|
//from robotgo
|
|
type uMap map[string]uint16
|
|
|
|
// MouseMap robotgo hook mouse's code map
|
|
var MouseMap = uMap{
|
|
"left": 1,
|
|
"right": 2,
|
|
"center": 3,
|
|
"wheelDown": 4,
|
|
"wheelUp": 5,
|
|
"wheelLeft": 6,
|
|
"wheelRight": 7,
|
|
}
|
|
|
|
// Keycode robotgo hook key's code map
|
|
var Keycode = uMap{
|
|
"`": 41,
|
|
"1": 2,
|
|
"2": 3,
|
|
"3": 4,
|
|
"4": 5,
|
|
"5": 6,
|
|
"6": 7,
|
|
"7": 8,
|
|
"8": 9,
|
|
"9": 10,
|
|
"0": 11,
|
|
"-": 12,
|
|
"+": 13,
|
|
//
|
|
"q": 16,
|
|
"w": 17,
|
|
"e": 18,
|
|
"r": 19,
|
|
"t": 20,
|
|
"y": 21,
|
|
"u": 22,
|
|
"i": 23,
|
|
"o": 24,
|
|
"p": 25,
|
|
"[": 26,
|
|
"]": 27,
|
|
"\\": 43,
|
|
//
|
|
"a": 30,
|
|
"s": 31,
|
|
"d": 32,
|
|
"f": 33,
|
|
"g": 34,
|
|
"h": 35,
|
|
"j": 36,
|
|
"k": 37,
|
|
"l": 38,
|
|
";": 39,
|
|
"'": 40,
|
|
//
|
|
"z": 44,
|
|
"x": 45,
|
|
"c": 46,
|
|
"v": 47,
|
|
"b": 48,
|
|
"n": 49,
|
|
"m": 50,
|
|
",": 51,
|
|
".": 52,
|
|
"/": 53,
|
|
//
|
|
"f1": 59,
|
|
"f2": 60,
|
|
"f3": 61,
|
|
"f4": 62,
|
|
"f5": 63,
|
|
"f6": 64,
|
|
"f7": 65,
|
|
"f8": 66,
|
|
"f9": 67,
|
|
"f10": 68,
|
|
"f11": 69,
|
|
"f12": 70,
|
|
// more
|
|
"esc": 1,
|
|
"delete": 14,
|
|
"tab": 15,
|
|
"ctrl": 29,
|
|
"control": 29,
|
|
"alt": 56,
|
|
"space": 57,
|
|
"shift": 42,
|
|
"rshift": 54,
|
|
"enter": 28,
|
|
"cmd": 3675,
|
|
"command": 3675,
|
|
"rcmd": 3676,
|
|
"ralt": 3640,
|
|
"up": 57416,
|
|
"down": 57424,
|
|
"left": 57419,
|
|
"right": 57421,
|
|
}
|
|
|
|
func allPressed(pressed map[uint16]bool, keys ...uint16) bool {
|
|
|
|
for _, i := range keys {
|
|
// fmt.Print(i)
|
|
if pressed[i] == false {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func Register(when uint8, cmds []string, cb func(Event)) {
|
|
key := len(used)
|
|
used = append(used, key)
|
|
tmp := []uint16{}
|
|
for _, v := range cmds {
|
|
tmp = append(tmp, Keycode[v])
|
|
}
|
|
keys[key] = tmp
|
|
cbs[key] = cb
|
|
events[when] = append(events[when], key)
|
|
return
|
|
}
|
|
|
|
func Process(EvChan <-chan Event) (out chan bool) {
|
|
out = make(chan bool)
|
|
go func() {
|
|
for ev := range EvChan {
|
|
if ev.Kind == KeyDown || ev.Kind == KeyHold {
|
|
pressed[ev.Keycode] = true
|
|
} else if ev.Kind == KeyUp {
|
|
pressed[ev.Keycode] = false
|
|
}
|
|
for _, v := range events[ev.Kind] {
|
|
if !asyncon {
|
|
break
|
|
}
|
|
if allPressed(pressed, keys[v]...) {
|
|
cbs[v](ev)
|
|
}
|
|
}
|
|
}
|
|
// fmt.Println("exiting after end (process)")
|
|
out <- true
|
|
}()
|
|
return out
|
|
}
|
|
|
|
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 {
|
|
for grOn != 0 {
|
|
// fmt.Println("wating for goroutines to end")
|
|
time.After(50 * time.Millisecond)
|
|
}
|
|
ev = make(chan Event, 1024)
|
|
|
|
go func() {
|
|
grOn += 1
|
|
C.startev()
|
|
grOn -= 1
|
|
// fmt.Println("exiting after end (C.startev)")
|
|
}()
|
|
// fmt.Println("started ev")
|
|
asyncon = true
|
|
go func() {
|
|
grOn += 1
|
|
for {
|
|
if !asyncon {
|
|
// fmt.Println("exiting after end (start)")
|
|
grOn -= 1
|
|
return
|
|
} else {
|
|
// fmt.Println("polling events")
|
|
C.pollEv()
|
|
time.Sleep(time.Millisecond * 50)
|
|
//todo: find smallest time that does not destroy the cpu utilization
|
|
}
|
|
}
|
|
}()
|
|
return ev
|
|
}
|
|
|
|
// End removes global event hook
|
|
func End() {
|
|
asyncon = false
|
|
C.endPoll()
|
|
C.stop_event()
|
|
for len(ev) != 0 {
|
|
<-ev
|
|
}
|
|
close(ev)
|
|
pressed = make(map[uint16]bool, 256)
|
|
used = []int{}
|
|
keys = map[int][]uint16{}
|
|
cbs = map[int]func(Event){}
|
|
events = map[uint8][]int{}
|
|
// for grOn != 0 {
|
|
// time.After(time.Millisecond * 50)
|
|
// }
|
|
}
|
|
|
|
// AddEvent add event listener
|
|
// func AddEvent(key string) int {
|
|
// cs := C.CString(key)
|
|
// defer C.free(unsafe.Pointer(cs))
|
|
|
|
// eve := C.add_event(cs)
|
|
// geve := int(eve)
|
|
|
|
// return geve
|
|
// }
|