Skip to content

Commit

Permalink
Split device logic up into separate source files.
Browse files Browse the repository at this point in the history
  • Loading branch information
bendahl committed Apr 22, 2018
1 parent 3eeb385 commit 7e78aef
Show file tree
Hide file tree
Showing 8 changed files with 623 additions and 570 deletions.
101 changes: 101 additions & 0 deletions keyboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package uinput

import (
"io"
"os"
"fmt"
)

// 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 uinputdefs.
// 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 uinputdefs.
KeyUp(key int) 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) {
validateDevicePath(path)
validateUinputName(name)

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 {
err := sendBtnEvent(vk.deviceFile, key, btnStatePressed)
if err != nil {
return fmt.Errorf("Failed to issue the KeyDown event: %v", err)
}

err = sendBtnEvent(vk.deviceFile, key, btnStateReleased)
if err != nil {
return fmt.Errorf("Failed to issue the KeyUp event: %v", err)
}

err = syncEvents(vk.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// KeyDown will send the key code passed (see uinputdefs.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 {
err := sendBtnEvent(vk.deviceFile, key, btnStatePressed)
if err != nil {
return fmt.Errorf("Failed to issue the KeyDown event: %v", err)
}

err = syncEvents(vk.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// KeyUp will release the given key passed as a parameter (see uinputdefs.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 {
err := sendBtnEvent(vk.deviceFile, key, btnStateReleased)
if err != nil {
return fmt.Errorf("Failed to issue the KeyUp event: %v", err)
}

err = syncEvents(vk.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// 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)
}
36 changes: 36 additions & 0 deletions keyboard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package uinput

import "testing"

// This test will confirm that basic key events are working.
// Note that only Key1 is used here, as the purpose of this test is to ensure that the event handling for
// keyboard devices is working. All other keys, defined in uinputdefs should work as well if this test passes.
// Another thing to keep in mind is that there are certain key codes that might not be great candidates for
// unit testing, as they may create unwanted side effects, like logging out the current user, etc...
func TestBasicKeyboard(t *testing.T) {
vk, err := CreateKeyboard("/dev/uinput", []byte("Test Basic Keyboard"))
if err != nil {
t.Fatalf("Failed to create the virtual keyboard. Last error was: %s\n", err)
}

err = vk.KeyPress(Key1)
if err != nil {
t.Fatalf("Failed to send key press. Last error was: %s\n", err)
}

err = vk.KeyDown(Key1)
if err != nil {
t.Fatalf("Failed to send key down event. Last error was: %s\n", err)
}

err = vk.KeyUp(Key1)
if err != nil {
t.Fatalf("Failed to send key up event. Last error was: %s\n", err)
}

err = vk.Close()

if err != nil {
t.Fatalf("Failed to close device. Last error was: %s\n", err)
}
}
181 changes: 181 additions & 0 deletions mouse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package uinput

import (
"io"
"os"
"fmt"
)

// A Mouse is a device that will trigger an absolute change event.
// For details see: https://www.kernel.org/doc/Documentation/input/event-codes.txt
type Mouse interface {
// MoveLeft will move the mouse cursor left by the given number of pixel.
MoveLeft(pixel int32) error

// MoveRight will move the mouse cursor right by the given number of pixel.
MoveRight(pixel int32) error

// MoveUp will move the mouse cursor up by the given number of pixel.
MoveUp(pixel int32) error

// MoveDown will move the mouse cursor down by the given number of pixel.
MoveDown(pixel int32) error

// LeftClick will issue a single left click.
LeftClick() error

// RightClick will issue a right click.
RightClick() error

// LeftPress will simulate a press of the left mouse button. Note that the button will not be released until
// LeftRelease is invoked.
LeftPress() error

// LeftRelease will simulate the release of the left mouse button.
LeftRelease() error

// RightPress will simulate the press of the right mouse button. Note that the button will not be released until
// RightRelease is invoked.
RightPress() error

// RightRelease will simulate the release of the right mouse button.
RightRelease() error

io.Closer
}

type vMouse struct {
name []byte
deviceFile *os.File
}

// CreateMouse will create a new mouse input device. A mouse is a device that allows relative input.
// Relative input means that all changes to the x and y coordinates of the mouse pointer will be
func CreateMouse(path string, name []byte) (Mouse, error) {
validateDevicePath(path)
validateUinputName(name)

fd, err := createMouse(path, name)
if err != nil {
return nil, err
}

return vMouse{name: name, deviceFile: fd}, nil
}

// MoveLeft will move the cursor left by the number of pixel specified.
func (vRel vMouse) MoveLeft(pixel int32) error {
return sendRelEvent(vRel.deviceFile, relX, -pixel)
}

// MoveRight will move the cursor right by the number of pixel specified.
func (vRel vMouse) MoveRight(pixel int32) error {
return sendRelEvent(vRel.deviceFile, relX, pixel)
}

// MoveUp will move the cursor up by the number of pixel specified.
func (vRel vMouse) MoveUp(pixel int32) error {
return sendRelEvent(vRel.deviceFile, relY, -pixel)
}

// MoveDown will move the cursor down by the number of pixel specified.
func (vRel vMouse) MoveDown(pixel int32) error {
return sendRelEvent(vRel.deviceFile, relY, pixel)
}

// LeftClick will issue a LeftClick.
func (vRel vMouse) LeftClick() error {
err := sendBtnEvent(vRel.deviceFile, evBtnLeft, btnStatePressed)
if err != nil {
return fmt.Errorf("Failed to issue the LeftClick event: %v", err)
}

err = sendBtnEvent(vRel.deviceFile, evBtnLeft, btnStateReleased)
if err != nil {
return fmt.Errorf("Failed to issue the KeyUp event: %v", err)
}

err = syncEvents(vRel.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// RightClick will issue a RightClick
func (vRel vMouse) RightClick() error {
err := sendBtnEvent(vRel.deviceFile, evBtnRight, btnStatePressed)
if err != nil {
return fmt.Errorf("Failed to issue the RightClick event: %v", err)
}

err = sendBtnEvent(vRel.deviceFile, evBtnRight, btnStateReleased)
if err != nil {
return fmt.Errorf("Failed to issue the KeyUp event: %v", err)
}

err = syncEvents(vRel.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// LeftPress will simulate a press of the left mouse button. Note that the button will not be released until
// LeftRelease is invoked.
func (vRel vMouse) LeftPress() error {
err := sendBtnEvent(vRel.deviceFile, evBtnLeft, btnStatePressed)
if err != nil {
return fmt.Errorf("Failed press the left mouse button: %v", err)
}
err = syncEvents(vRel.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// LeftRelease will simulate the release of the left mouse button.
func (vRel vMouse) LeftRelease() error {
err := sendBtnEvent(vRel.deviceFile, evBtnLeft, btnStateReleased)
if err != nil {
return fmt.Errorf("Failed to release the left mouse button: %v", err)
}
err = syncEvents(vRel.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// RightPress will simulate the press of the right mouse button. Note that the button will not be released until
// RightRelease is invoked.
func (vRel vMouse) RightPress() error {
err := sendBtnEvent(vRel.deviceFile, evBtnRight, btnStatePressed)
if err != nil {
return fmt.Errorf("Failed to press the right mouse button: %v", err)
}
err = syncEvents(vRel.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// RightRelease will simulate the release of the right mouse button.
func (vRel vMouse) RightRelease() error {
err := sendBtnEvent(vRel.deviceFile, evBtnRight, btnStateReleased)
if err != nil {
return fmt.Errorf("Failed to release the right mouse button: %v", err)
}
err = syncEvents(vRel.deviceFile)
if err != nil {
return fmt.Errorf("sync to device file failed: %v", err)
}
return nil
}

// Close closes the device and releases the device.
func (vRel vMouse) Close() error {
return closeDevice(vRel.deviceFile)
}
66 changes: 66 additions & 0 deletions mouse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package uinput

import "testing"

// This test confirms that all basic mouse events are working as expected.
func TestBasicMouseMoves(t *testing.T) {
relDev, err := CreateMouse("/dev/uinput", []byte("Test Basic Mouse"))
if err != nil {
t.Fatalf("Failed to create the virtual mouse. Last error was: %s\n", err)
}

err = relDev.MoveLeft(100)
if err != nil {
t.Fatalf("Failed to move mouse left. Last error was: %s\n", err)
}

err = relDev.MoveRight(150)
if err != nil {
t.Fatalf("Failed to move mouse right. Last error was: %s\n", err)
}

err = relDev.MoveUp(50)
if err != nil {
t.Fatalf("Failed to move mouse up. Last error was: %s\n", err)
}

err = relDev.MoveDown(100)
if err != nil {
t.Fatalf("Failed to move mouse down. Last error was: %s\n", err)
}

err = relDev.RightClick()
if err != nil {
t.Fatalf("Failed to perform right click. Last error was: %s\n", err)
}

err = relDev.LeftClick()
if err != nil {
t.Fatalf("Failed to perform right click. Last error was: %s\n", err)
}

err = relDev.LeftPress()
if err != nil {
t.Fatalf("Failed to perform left key press. Last error was: %s\n", err)
}

err = relDev.LeftRelease()
if err != nil {
t.Fatalf("Failed to perform left key release. Last error was: %s\n", err)
}

err = relDev.RightPress()
if err != nil {
t.Fatalf("Failed to perform right key press. Last error was: %s\n", err)
}

err = relDev.RightRelease()
if err != nil {
t.Fatalf("Failed to perform right key release. Last error was: %s\n", err)
}

err = relDev.Close()
if err != nil {
t.Fatalf("Failed to close device. Last error was: %s\n", err)
}
}
Loading

0 comments on commit 7e78aef

Please sign in to comment.