forked from bendahl/uinput
-
Notifications
You must be signed in to change notification settings - Fork 1
/
keyboard.go
132 lines (110 loc) · 3.82 KB
/
keyboard.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package uinput
import (
"fmt"
"io"
"os"
)
// A Keyboard is an key event output device. It is used to
// enable a program to simulate HID keyboard input events.
type Keyboard interface {
// KeyPress will cause the key to be pressed and immediately released.
KeyPress(key int) error
// KeyDown will send a keypress event to an existing keyboard device.
// The key can be any of the predefined keycodes from keycodes.go.
// Note that the key will be "held down" until "KeyUp" is called.
KeyDown(key int) error
// KeyUp will send a keyrelease event to an existing keyboard device.
// The key can be any of the predefined keycodes from keycodes.go.
KeyUp(key int) error
// FetchSysPath will return the syspath to the device file.
FetchSyspath() (string, error)
io.Closer
}
type vKeyboard struct {
name []byte
deviceFile *os.File
}
// CreateKeyboard will create a new keyboard using the given uinput
// device path of the uinput device.
func CreateKeyboard(path string, name []byte) (Keyboard, error) {
err := validateDevicePath(path)
if err != nil {
return nil, err
}
err = validateUinputName(name)
if err != nil {
return nil, err
}
fd, err := createVKeyboardDevice(path, name)
if err != nil {
return nil, err
}
return vKeyboard{name: name, deviceFile: fd}, nil
}
// KeyPress will issue a single key press (push down a key and then immediately release it).
func (vk vKeyboard) KeyPress(key int) error {
if !keyCodeInRange(key) {
return fmt.Errorf("failed to perform KeyPress. Code %d is not in range", key)
}
err := sendBtnEvent(vk.deviceFile, []int{key}, btnStatePressed)
if err != nil {
return fmt.Errorf("failed to issue the KeyDown event: %v", err)
}
return sendBtnEvent(vk.deviceFile, []int{key}, btnStateReleased)
}
// KeyDown will send the key code passed (see keycodes.go for available keycodes). Note that unless a key release
// event is sent to the device, the key will remain pressed and therefore input will continuously be generated. Therefore,
// do not forget to call "KeyUp" afterwards.
func (vk vKeyboard) KeyDown(key int) error {
if !keyCodeInRange(key) {
return fmt.Errorf("failed to perform KeyDown. Code %d is not in range", key)
}
return sendBtnEvent(vk.deviceFile, []int{key}, btnStatePressed)
}
// KeyUp will release the given key passed as a parameter (see keycodes.go for available keycodes). In most
// cases it is recommended to call this function immediately after the "KeyDown" function in order to only issue a
// single key press.
func (vk vKeyboard) KeyUp(key int) error {
if !keyCodeInRange(key) {
return fmt.Errorf("failed to perform KeyUp. Code %d is not in range", key)
}
return sendBtnEvent(vk.deviceFile, []int{key}, btnStateReleased)
}
// Close will close the device and free resources.
// It's usually a good idea to use defer to call this function.
func (vk vKeyboard) Close() error {
return closeDevice(vk.deviceFile)
}
func createVKeyboardDevice(path string, name []byte) (fd *os.File, err error) {
deviceFile, err := createDeviceFile(path)
if err != nil {
return nil, fmt.Errorf("failed to create virtual keyboard device: %v", err)
}
err = registerDevice(deviceFile, uintptr(evKey))
if err != nil {
deviceFile.Close()
return nil, fmt.Errorf("failed to register virtual keyboard device: %v", err)
}
// register key events
for i := 0; i <= keyMax; i++ {
err = ioctl(deviceFile, uiSetKeyBit, uintptr(i))
if err != nil {
deviceFile.Close()
return nil, fmt.Errorf("failed to register key number %d: %v", i, err)
}
}
return createUsbDevice(deviceFile,
uinputUserDev{
Name: toUinputName(name),
ID: inputID{
Bustype: busUsb,
Vendor: 0x4711,
Product: 0x0815,
Version: 1}})
}
func keyCodeInRange(key int) bool {
return key >= keyReserved && key <= keyMax
}
func (vk vKeyboard) FetchSyspath() (string, error) {
return fetchSyspath(vk.deviceFile)
}