gohook/hook.go

276 lines
5.9 KiB
Go
Raw Normal View History

2018-09-18 02:09:59 +08:00
// 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.
2018-09-17 07:29:13 +08:00
package hook
/*
2019-11-14 00:37:29 +08:00
#cgo darwin CFLAGS: -x objective-c -Wno-deprecated-declarations
2018-09-18 02:17:14 +08:00
#cgo darwin LDFLAGS: -framework Cocoa
2018-09-18 01:30:41 +08:00
2018-09-17 07:29:13 +08:00
#cgo linux CFLAGS:-I/usr/src
2018-09-17 07:43:30 +08:00
#cgo linux LDFLAGS: -L/usr/src -lX11 -lXtst
#cgo linux LDFLAGS: -lX11-xcb -lxcb -lxcb-xkb -lxkbcommon -lxkbcommon-x11
2018-09-18 01:30:41 +08:00
//#cgo windows LDFLAGS: -lgdi32 -luser32
2018-09-17 07:29:13 +08:00
#include "event/goEvent.h"
*/
import "C"
2018-09-18 02:17:14 +08:00
import (
"fmt"
"sync"
"time"
2018-09-17 07:29:13 +08:00
"unsafe"
)
const (
2019-08-28 00:51:46 +08:00
// Version get the gohook version
2019-11-14 00:37:29 +08:00
Version = "v0.20.0.56, Sierra Nevada!"
2019-08-28 00:51:46 +08:00
// HookEnabled honk enable status
2019-02-19 22:47:03 +08:00
HookEnabled = 1 // iota
HookDisabled = 2
2019-02-19 22:47:03 +08:00
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
)
2019-02-19 22:47:03 +08:00
// Event Holds a system event
2019-10-30 00:45:28 +08:00
//
2019-02-19 22:47:03 +08:00
// If it's a Keyboard event the relevant fields are:
// Mask, Keycode, Rawcode, and Keychar,
// Keychar is probably what you want.
2019-10-30 00:45:28 +08:00
//
2019-02-19 22:47:03 +08:00
// If it's a Mouse event the relevant fields are:
// Button, Clicks, X, Y, Amount, Rotation and Direction
type Event struct {
2019-02-19 22:47:03 +08:00
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 int32 `json:"rotation"`
Direction uint8 `json:"direction"`
}
var (
ev = make(chan Event, 1024)
asyncon = false
lck sync.RWMutex
pressed = make(map[uint16]bool, 256)
used = []int{}
keys = map[int][]uint16{}
cbs = map[int]func(Event){}
events = map[uint8][]int{}
)
func allPressed(pressed map[uint16]bool, keys ...uint16) bool {
for _, i := range keys {
// fmt.Println(i)
if pressed[i] == false {
return false
}
}
return true
}
// Register register gohook event
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
}
// Process return go hook process
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
}
2019-02-19 22:47:03 +08:00
// String return hook kind string
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:
2019-02-21 20:57:48 +08:00
return fmt.Sprintf("%v - Event: {Kind: KeyUp, Rawcode: %v, Keychar: %v}",
e.When, e.Rawcode, e.Keychar)
case KeyHold:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: KeyHold, Rawcode: %v, Keychar: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Rawcode, e.Keychar)
case KeyDown:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: KeyDown, Rawcode: %v, Keychar: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Rawcode, e.Keychar)
case MouseUp:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: MouseUp, Button: %v, X: %v, Y: %v, Clicks: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseHold:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: MouseHold, Button: %v, X: %v, Y: %v, Clicks: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseDown:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: MouseDown, Button: %v, X: %v, Y: %v, Clicks: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseMove:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: MouseMove, Button: %v, X: %v, Y: %v, Clicks: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseDrag:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: MouseDrag, Button: %v, X: %v, Y: %v, Clicks: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Button, e.X, e.Y, e.Clicks)
case MouseWheel:
2019-02-21 21:10:31 +08:00
return fmt.Sprintf(
"%v - Event: {Kind: MouseWheel, Amount: %v, Rotation: %v, Direction: %v}",
2019-02-21 20:57:48 +08:00
e.When, e.Amount, e.Rotation, e.Direction)
case FakeEvent:
return fmt.Sprintf("%v - Event: {Kind: FakeEvent}", e.When)
}
2019-02-19 22:47:03 +08:00
return "Unknown event, contact the mantainers"
}
2019-02-19 22:47:03 +08:00
// RawcodetoKeychar rawcode to keychar
func RawcodetoKeychar(r uint16) string {
lck.RLock()
defer lck.RUnlock()
return raw2key[r]
}
// KeychartoRawcode key char to rawcode
func KeychartoRawcode(kc string) uint16 {
return keytoraw[kc]
}
2019-02-19 22:47:03 +08:00
// Start Adds global event hook to OS
// returns event channel
func Start() chan Event {
ev = make(chan Event, 1024)
2019-02-20 23:07:25 +08:00
go C.start_ev()
2019-02-19 22:47:03 +08:00
asyncon = true
go func() {
for {
if !asyncon {
return
}
C.pollEv()
time.Sleep(time.Millisecond * 50)
//todo: find smallest time that does not destroy the cpu utilization
}
}()
2019-02-19 22:47:03 +08:00
return ev
}
// End removes global event hook
func End() {
asyncon = false
C.endPoll()
C.stop_event()
2019-02-19 22:47:03 +08:00
for len(ev) != 0 {
<-ev
}
close(ev)
2019-02-19 22:47:03 +08:00
pressed = make(map[uint16]bool, 256)
used = []int{}
keys = map[int][]uint16{}
cbs = map[int]func(Event){}
events = map[uint8][]int{}
}
2018-09-17 07:29:13 +08:00
// AddEvent add event listener
func AddEvent(key string) int {
cs := C.CString(key)
2018-09-17 08:17:40 +08:00
defer C.free(unsafe.Pointer(cs))
2018-09-18 02:17:14 +08:00
2018-09-17 07:29:13 +08:00
eve := C.add_event(cs)
geve := int(eve)
return geve
}
2019-02-19 22:34:55 +08:00
// StopEvent stop event listener
func StopEvent() {
C.stop_event()
}