Skip to content

Commit 232d2ab

Browse files
author
Benjamin Dahlmanns
committed
Major refactoring:
- Adhere to go naming standards (renaming of all constants) - Improve error handling - Remove C dependencies completely - Add .gitignore
1 parent 6ab7fee commit 232d2ab

File tree

9 files changed

+470
-448
lines changed

9 files changed

+470
-448
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea/*

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
Uinput
22
======
33

4-
This go package provides wrapper functions for the LINUX uinput device. As it stands right now, only virtual keyboards are supported. Support for realative and absolute input devices will be added later on.
4+
This package provides pure go wrapper functions for the LINUX uinput device and therefore allows the creation of virtual input devices in userland. As it stands right now, only virtual keyboards are supported. Support for relative and absolute input devices will be added later on.
55

66
Please note that you will need to make sure to have the necessary rights to write to uinput. You can either chmod your uinput device, or add a rule in /etc/udev/rules.d to allow your user's group or a dedicated group to write to the device. An example file could be named "99-user.rules" and the line you would need to add for "user", belonging to the group "utest" would be <pre><code>KERNEL=="uinput", GROUP="utest", MODE:="0660"</code></pre> Also, make sure to restart in order for these settings to work. Which approach you'll take is up to you, although I would encourage the creation of a udev rule, as it is the clean approach.
77

88
Installation
99
-------------
1010
Simply check out the repository and use the commands <pre><code>go build && go install</code></pre> The package will then be installed to your local respository, along with the package documentation. The documentation contains more details on the usage of this package.
1111

12-
Don't worry about the C sources, as CGO will take care of compiling these for you as well. However, you will need to make sure to have the necessray header files for gcc installed on your system. They should be located underneath "/usr/include/linux".
13-
1412
License
1513
--------
1614
The package falls under the MIT license. Please see the "LICENSE" file for details.

TODO.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
TODO
22
====
33

4-
1. Create Tests for the uinput package
5-
2. Migrate code from C to GO
4+
1. ~~Create Tests for the uinput package~~
5+
2. ~~Migrate code from C to GO~~
66
3. Implement relative input
7-
4. Implement absolute input
7+
4. Implement absolute input
8+
5. Test on different platforms (besides x86_64)

uinput.go

Lines changed: 26 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
Package uinput provides access to the userland input device driver uinput on linux systems.
33
For now, only the creation of a virtual keyboard is supported. The keycodes, that are available
4-
and can be used to trigger key press events, are part of this package ("KEY_1" for number 1, for
4+
and can be used to trigger key press events, are part of this package ("Key1" for number 1, for
55
example).
66
77
In order to use the virtual keyboard, you will need to follow these three steps:
@@ -10,22 +10,19 @@ In order to use the virtual keyboard, you will need to follow these three steps:
1010
Example: vk, err := CreateKeyboard("/dev/uinput", "Virtual Keyboard")
1111
1212
2. Send Button events to the device
13-
Example: err = vk.SendKeyPress(uinput.KEY_D)
14-
err = vk.SendKeyRelease(uinput.KEY_D)
13+
Example: err = vk.SendKeyPress(uinput.KeyD)
14+
err = vk.SendKeyRelease(uinput.KeyD)
1515
1616
3. Close the device
1717
Example: err = vk.Close()
1818
*/
1919
package uinput
2020

21-
/*
22-
#include "uinputwrapper.h"
23-
*/
24-
import "C"
2521
import (
26-
"errors"
2722
"io"
28-
"unsafe"
23+
"os"
24+
"errors"
25+
"fmt"
2926
)
3027

3128
// A Keyboard is an key event output device. It is used to
@@ -43,65 +40,44 @@ type Keyboard interface {
4340
}
4441

4542
type vKeyboard struct {
46-
name string
47-
fd int
43+
name []byte
44+
deviceFile *os.File
4845
}
4946

5047
// CreateKeyboard will create a new keyboard using the given uinput
5148
// device path of the uinput device.
52-
func CreateKeyboard(path, name string) (Keyboard, error) {
49+
func CreateKeyboard(path string, name []byte) (Keyboard, error) {
50+
if path == "" {
51+
return nil, errors.New("device path must not be empty")
52+
}
53+
if len(name) > uinputMaxNameSize {
54+
return nil, fmt.Errorf("device name %s is too long (maximum of %d characters allowed)", name, uinputMaxNameSize)
55+
}
56+
5357
fd, err := createVKeyboardDevice(path, name)
5458
if err != nil {
5559
return nil, err
5660
}
5761

58-
return vKeyboard{name, fd}, nil
62+
return vKeyboard{name: name, deviceFile: fd}, nil
5963
}
6064

65+
// SendKeyPress will send the key code passed (see uinputdefs.go for available keycodes). Note that unless a key release
66+
// event is sent to the device, the key will remain pressed and therefore input will continuously be generated. Therefore,
67+
// do not forget to call "SendKeyRelease" afterwards.
6168
func (vk vKeyboard) SendKeyPress(key int) error {
62-
return sendBtnEvent(vk.fd, key, 1)
69+
return sendBtnEvent(vk.deviceFile, key, btnStatePressed)
6370
}
6471

72+
// SendKeyRelease will release the given key passed as a parameter (see uinputdefs.go for available keycodes). In most
73+
// cases it is recommended to call this function immediately after the "SendKeyPress" function in order to only issue a
74+
// singel key press.
6575
func (vk vKeyboard) SendKeyRelease(key int) error {
66-
return sendBtnEvent(vk.fd, key, 0)
76+
return sendBtnEvent(vk.deviceFile, key, btnStateReleased)
6777
}
6878

6979
// Close will close the device and free resources.
7080
// It's usually a good idea to use defer to call this function.
7181
func (vk vKeyboard) Close() error {
72-
return closeDevice(vk.fd)
73-
}
74-
75-
func createVKeyboardDevice(path, name string) (deviceID int, err error) {
76-
uinputDevice := C.CString(path)
77-
defer C.free(unsafe.Pointer(uinputDevice))
78-
79-
if name == "" {
80-
name = "uinput_default_vkeyboard"
81-
}
82-
virtDeviceName := C.CString(name)
83-
defer C.free(unsafe.Pointer(virtDeviceName))
84-
85-
var fd C.int
86-
fd = C.initVKeyboardDevice(uinputDevice, virtDeviceName)
87-
if fd < 0 {
88-
// TODO: Map ErrValues into more specific Errors
89-
return 0, errors.New("Could not initialize device")
90-
}
91-
92-
return int(fd), nil
93-
}
94-
95-
func sendBtnEvent(deviceID int, key int, btnState int) (err error) {
96-
if C.sendBtnEvent(C.int(deviceID), C.int(key), C.int(btnState)) < 0 {
97-
return errors.New("Sending keypress failed")
98-
}
99-
return nil
100-
}
101-
102-
func closeDevice(deviceID int) (err error) {
103-
if int(C.releaseDevice(C.int(deviceID))) < 0 {
104-
return errors.New("Closing device failed")
105-
}
106-
return nil
82+
return closeDevice(vk.deviceFile)
10783
}

uinput_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ import (
66

77
// This test will create a basic VKeyboard, send a key command and then close the keyboard device
88
func TestBasicKeyboard(t *testing.T) {
9-
vk, err := CreateKeyboard("/dev/uinput", "Test Basic Keyboard")
9+
vk, err := CreateKeyboard("/dev/uinput", []byte("Test Basic Keyboard"))
1010
if err != nil {
1111
t.Fatalf("Failed to create the virtual keyboard. Last error was: %s\n", err)
1212
}
1313

14-
err = vk.SendKeyPress(KEY_1)
14+
err = vk.SendKeyPress(Key1)
1515

1616
if err != nil {
1717
t.Fatalf("Failed to send key event. Last error was: %s\n", err)
1818
}
1919

20-
err = vk.SendKeyRelease(KEY_1)
20+
err = vk.SendKeyRelease(Key1)
2121

2222
if err != nil {
2323
t.Fatalf("Failed to send key event. Last error was: %s\n", err)
@@ -33,7 +33,7 @@ func TestBasicKeyboard(t *testing.T) {
3333
// This test will confirm that a proper error code is returned if an invalid uinput path is
3434
// passed to the library
3535
func TestInvalidDevicePath(t *testing.T) {
36-
vk, err := CreateKeyboard("/invalid/path", "Invalid Device Path")
36+
vk, err := CreateKeyboard("/invalid/path", []byte("Invalid Device Path"))
3737
if err == nil {
3838
// this usually shouldn't happen, but if the device is created, we need to close it
3939
vk.Close()
@@ -44,7 +44,7 @@ func TestInvalidDevicePath(t *testing.T) {
4444
// This test will confirm that a proper error code is returned if an invalid keycode is
4545
// passed to the library
4646
func TestInvalidKeycode(t *testing.T) {
47-
vk, err := CreateKeyboard("/dev/uinput", "Test Keyboard")
47+
vk, err := CreateKeyboard("/dev/uinput", []byte("Test Keyboard"))
4848
if err != nil {
4949
t.Fatalf("Failed to create the virtual keyboard. Last error was: %s\n", err)
5050
}

0 commit comments

Comments
 (0)