In this post, I will show you a cool trick to implement global event hook for keyboard and mouse in Go (golang).
First, create a new Go project and add this dependency into the project:
$ go get github.com/robotn/gohook
The library gohook is a wrapper around the libuihook, which provides a way for developers to access global keyboard and mouse events. I know what you’re thinking, isn’t it cool to build an application that can silently monitor and watch user actions in the background? 😈😈😈
If you’re on MacOS, you need to have Xcode installed.
If you’re on Windows or Linux, please follow the instructions to setup the required system libraries.
Once ready, let’s start the exciting code.
The hook application will always begin with a hook.Start()
and end with hook.End()
.
func Start() chan Event {}
func End() {}
As you can see, hook.Start()
return an hook.Event
-type channel, in which we can use to detect the keyboard and mouse events.
Try it out with the code below:
package main
import (
"fmt"
hook "github.com/robotn/gohook"
)
func main() {
chanHook := hook.Start()
defer hook.End()
for ev := range chanHook {
fmt.Printf("hook: %v\n", ev)
}
}
Build and run the application, then trigger some mouse and keyboard actions, you will the following output:
hook: 2022-06-26 14:57:22.421822 -0500 CDT m=+0.153599876 - Event: {Kind: HookEnabled}
hook: 2022-06-26 14:57:24.868832 -0500 CDT m=+2.600628918 - Event: {Kind: MouseMove, Button: 0, X: -919, Y: 404, Clicks: 0}
hook: 2022-06-26 14:57:24.919926 -0500 CDT m=+2.651723709 - Event: {Kind: MouseMove, Button: 0, X: -919, Y: 403, Clicks: 0}
hook: 2022-06-26 14:57:25.836433 -0500 CDT m=+3.568237293 - Event: {Kind: MouseHold, Button: 1, X: -919, Y: 403, Clicks: 1}
hook: 2022-06-26 14:57:25.938757 -0500 CDT m=+3.670562543 - Event: {Kind: MouseDown, Button: 1, X: -919, Y: 403, Clicks: 1}
hook: 2022-06-26 14:57:25.938773 -0500 CDT m=+3.670578043 - Event: {Kind: MouseUp, Button: 1, X: -919, Y: 403, Clicks: 1}
hook: 2022-06-26 14:57:27.061051 -0500 CDT m=+4.792865543 - Event: {Kind: KeyHold, Rawcode: 49, Keychar: 65535}
hook: 2022-06-26 14:57:27.061064 -0500 CDT m=+4.792878876 - Event: {Kind: KeyDown, Rawcode: 49, Keychar: 32}
hook: 2022-06-26 14:57:27.163188 -0500 CDT m=+4.895002918 - Event: {Kind: KeyUp, Rawcode: 49, Keychar: 65535}
I guess you can understand those event logs.
If you wonder, this is how hook.Event
struct is defined:
// Event 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 int32 `json:"rotation"`
Direction uint8 `json:"direction"`
}
So it says, depending on the event type mouse or keyboard, you will need to use different set of event properties, not all.
However, you might not want to listen to all global keyboard and mouse events, instead, a set of them, you can use hook.Register()
method for the job.
func Register(when uint8, cmds []string, cb func(Event))
Let say you want to listen for the Ctrl + Shift + X
key combination, you will write it like this:
func main() {
hook.Register(hook.KeyDown, []string{"ctrl", "shift", "x"}, func(e hook.Event) {
fmt.Println("[Event] Ctrl+Shift+X detected!")
hook.End()
})
s := hook.Start()
<-hook.Process(s)
}
For your interests, these events are available to register for hook:
KeyDown = 3
KeyHold = 4
KeyUp = 5
MouseUp = 6
MouseHold = 7
MouseDown = 8
MouseMove = 9
MouseDrag = 10
MouseWheel = 11
Alright, that’s how you implement global event hook for keyboard and mouse in Go (golang).
Have fun!