From 7e78aefc199e782052d2594b261475da1ce81b93 Mon Sep 17 00:00:00 2001 From: Benjamin Dahlmanns Date: Sun, 22 Apr 2018 20:12:20 +0200 Subject: [PATCH] Split device logic up into separate source files. --- keyboard.go | 101 ++++++++++++ keyboard_test.go | 36 ++++ mouse.go | 181 ++++++++++++++++++++ mouse_test.go | 66 ++++++++ touchpad.go | 154 +++++++++++++++++ touchpad_test.go | 54 ++++++ uinput.go | 420 +---------------------------------------------- uinput_test.go | 181 +++----------------- 8 files changed, 623 insertions(+), 570 deletions(-) create mode 100644 keyboard.go create mode 100644 keyboard_test.go create mode 100644 mouse.go create mode 100644 mouse_test.go create mode 100644 touchpad.go create mode 100644 touchpad_test.go diff --git a/keyboard.go b/keyboard.go new file mode 100644 index 0000000..2371a7f --- /dev/null +++ b/keyboard.go @@ -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) +} diff --git a/keyboard_test.go b/keyboard_test.go new file mode 100644 index 0000000..4438fd7 --- /dev/null +++ b/keyboard_test.go @@ -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) + } +} diff --git a/mouse.go b/mouse.go new file mode 100644 index 0000000..b5c054a --- /dev/null +++ b/mouse.go @@ -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) +} diff --git a/mouse_test.go b/mouse_test.go new file mode 100644 index 0000000..1849dfa --- /dev/null +++ b/mouse_test.go @@ -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) + } +} diff --git a/touchpad.go b/touchpad.go new file mode 100644 index 0000000..e46fced --- /dev/null +++ b/touchpad.go @@ -0,0 +1,154 @@ +package uinput + +import ( + "fmt" + "io" + "os" +) + +// A TouchPad is an input device that uses absolute axis events, meaning that you can specify +// the exact position the cursor should move to. Therefore, it is necessary to define the size +// of the rectangle in which the cursor may move upon creation of the device. +type TouchPad interface { + // MoveTo will move the cursor to the specified position on the screen + MoveTo(x int32, y 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 vTouchPad struct { + name []byte + deviceFile *os.File +} + +// CreateTouchPad will create a new touch pad device. note that you will need to define the x and y axis boundaries +// (min and max) within which the cursor maybe moved around. +func CreateTouchPad(path string, name []byte, minX int32, maxX int32, minY int32, maxY int32) (TouchPad, error) { + validateDevicePath(path) + validateUinputName(name) + + fd, err := createTouchPad(path, name, minX, maxX, minY, maxY) + if err != nil { + return nil, err + } + + return vTouchPad{name: name, deviceFile: fd}, nil +} + +func (vTouch vTouchPad) MoveTo(x int32, y int32) error { + return sendAbsEvent(vTouch.deviceFile, x, y) +} + +func (vTouch vTouchPad) LeftClick() error { + err := sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStatePressed) + if err != nil { + return fmt.Errorf("Failed to issue the LeftClick event: %v", err) + } + + err = sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStateReleased) + if err != nil { + return fmt.Errorf("Failed to issue the KeyUp event: %v", err) + } + + err = syncEvents(vTouch.deviceFile) + if err != nil { + return fmt.Errorf("sync to device file failed: %v", err) + } + return nil +} + +func (vTouch vTouchPad) RightClick() error { + err := sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStatePressed) + if err != nil { + return fmt.Errorf("Failed to issue the RightClick event: %v", err) + } + + err = sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStateReleased) + if err != nil { + return fmt.Errorf("Failed to issue the KeyUp event: %v", err) + } + + err = syncEvents(vTouch.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 (vTouch vTouchPad) LeftPress() error { + err := sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStatePressed) + if err != nil { + return fmt.Errorf("Failed press the left mouse button: %v", err) + } + err = syncEvents(vTouch.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 (vTouch vTouchPad) LeftRelease() error { + err := sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStateReleased) + if err != nil { + return fmt.Errorf("Failed to release the left mouse button: %v", err) + } + err = syncEvents(vTouch.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 (vTouch vTouchPad) RightPress() error { + err := sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStatePressed) + if err != nil { + return fmt.Errorf("Failed to press the right mouse button: %v", err) + } + err = syncEvents(vTouch.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 (vTouch vTouchPad) RightRelease() error { + err := sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStateReleased) + if err != nil { + return fmt.Errorf("Failed to release the right mouse button: %v", err) + } + err = syncEvents(vTouch.deviceFile) + if err != nil { + return fmt.Errorf("sync to device file failed: %v", err) + } + return nil +} + +func (vTouch vTouchPad) Close() error { + return closeDevice(vTouch.deviceFile) +} diff --git a/touchpad_test.go b/touchpad_test.go new file mode 100644 index 0000000..68176ea --- /dev/null +++ b/touchpad_test.go @@ -0,0 +1,54 @@ +package uinput + +import "testing" + +func TestBasicTouchPadMoves(t *testing.T) { + absDev, err := CreateTouchPad("/dev/uinput", []byte("Test TouchPad"), 0, 1024, 0, 768) + if err != nil { + t.Fatalf("Failed to create the virtual touch pad. Last error was: %s\n", err) + } + + err = absDev.MoveTo(0, 0) + if err != nil { + t.Fatalf("Failed to move cursor to initial position. Last error was: %s\n", err) + } + + err = absDev.MoveTo(100, 200) + if err != nil { + t.Fatalf("Failed to move cursor to position x:100, y:200. Last error was: %s\n", err) + } + err = absDev.RightClick() + if err != nil { + t.Fatalf("Failed to perform right click. Last error was: %s\n", err) + } + + err = absDev.LeftClick() + if err != nil { + t.Fatalf("Failed to perform right click. Last error was: %s\n", err) + } + + err = absDev.LeftPress() + if err != nil { + t.Fatalf("Failed to perform left key press. Last error was: %s\n", err) + } + + err = absDev.LeftRelease() + if err != nil { + t.Fatalf("Failed to perform left key release. Last error was: %s\n", err) + } + + err = absDev.RightPress() + if err != nil { + t.Fatalf("Failed to perform right key press. Last error was: %s\n", err) + } + + err = absDev.RightRelease() + if err != nil { + t.Fatalf("Failed to perform right key release. Last error was: %s\n", err) + } + + err = absDev.Close() + if err != nil { + t.Fatalf("Failed to close device. Last error was: %s\n", err) + } +} diff --git a/uinput.go b/uinput.go index f2f3e7f..96b4397 100644 --- a/uinput.go +++ b/uinput.go @@ -78,429 +78,17 @@ 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 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 -} - -// 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 -} - -// A TouchPad is an input device that uses absolute axis events, meaning that you can specify -// the exact position the cursor should move to. Therefore, it is necessary to define the size -// of the rectangle in which the cursor may move upon creation of the device. -type TouchPad interface { - // MoveTo will move the cursor to the specified position on the screen - MoveTo(x int32, y 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 vTouchPad struct { - name []byte - deviceFile *os.File -} - -// CreateTouchPad will create a new touch pad device. note that you will need to define the x and y axis boundaries -// (min and max) within which the cursor maybe moved around. -func CreateTouchPad(path string, name []byte, minX int32, maxX int32, minY int32, maxY int32) (TouchPad, error) { - validateDevicePath(path) - validateUinputName(name) - - fd, err := createTouchPad(path, name, minX, maxX, minY, maxY) - if err != nil { - return nil, err - } - - return vTouchPad{name: name, deviceFile: fd}, nil -} - -func (vTouch vTouchPad) MoveTo(x int32, y int32) error { - return sendAbsEvent(vTouch.deviceFile, x, y) -} - -func (vTouch vTouchPad) LeftClick() error { - err := sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStatePressed) - if err != nil { - return fmt.Errorf("Failed to issue the LeftClick event: %v", err) - } - - err = sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStateReleased) - if err != nil { - return fmt.Errorf("Failed to issue the KeyUp event: %v", err) - } - - err = syncEvents(vTouch.deviceFile) - if err != nil { - return fmt.Errorf("sync to device file failed: %v", err) - } - return nil -} - -func (vTouch vTouchPad) RightClick() error { - err := sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStatePressed) - if err != nil { - return fmt.Errorf("Failed to issue the RightClick event: %v", err) - } - - err = sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStateReleased) - if err != nil { - return fmt.Errorf("Failed to issue the KeyUp event: %v", err) - } - - err = syncEvents(vTouch.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 (vTouch vTouchPad) LeftPress() error { - err := sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStatePressed) - if err != nil { - return fmt.Errorf("Failed press the left mouse button: %v", err) - } - err = syncEvents(vTouch.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 (vTouch vTouchPad) LeftRelease() error { - err := sendBtnEvent(vTouch.deviceFile, evBtnLeft, btnStateReleased) - if err != nil { - return fmt.Errorf("Failed to release the left mouse button: %v", err) - } - err = syncEvents(vTouch.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 (vTouch vTouchPad) RightPress() error { - err := sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStatePressed) - if err != nil { - return fmt.Errorf("Failed to press the right mouse button: %v", err) - } - err = syncEvents(vTouch.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 (vTouch vTouchPad) RightRelease() error { - err := sendBtnEvent(vTouch.deviceFile, evBtnRight, btnStateReleased) - if err != nil { - return fmt.Errorf("Failed to release the right mouse button: %v", err) - } - err = syncEvents(vTouch.deviceFile) - if err != nil { - return fmt.Errorf("sync to device file failed: %v", err) - } - return nil -} - -func (vTouch vTouchPad) Close() error { - return closeDevice(vTouch.deviceFile) -} - -// 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) -} - -// 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) -} - func validateDevicePath(path string) { if path == "" { panic("device path must not be empty") } + _, err := os.Stat(path) + if os.IsNotExist(err) { + panic(fmt.Sprintf("device path '%s' does not exist", path)) + } } func validateUinputName(name []byte) { diff --git a/uinput_test.go b/uinput_test.go index 527224f..ce345db 100644 --- a/uinput_test.go +++ b/uinput_test.go @@ -1,159 +1,32 @@ package uinput -import ( - "testing" -) - -// This test will confirm that all basic key events are working -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) - } -} - -// This test will confirm that a proper error code is returned if an invalid uinput path is -// passed to the library -func TestInvalidDevicePath(t *testing.T) { - vk, err := CreateKeyboard("/invalid/path", []byte("Invalid Device Path")) - if err == nil { - // this usually shouldn't happen, but if the device is created, we need to close it - vk.Close() - t.Fatalf("Expected error code,but received %s instead.\n", err) - } +import "testing" + +func TestValidateDevicePathEmptyPathPanics(t *testing.T) { + expected := "device path must not be empty" + defer func() { + if r := recover(); r != nil { + actual := r.(string) + if actual != expected { + t.Fatalf("Expected: %s\nActual: %s", expected, actual ) + } + } + }() + validateDevicePath("") + t.Fatalf("Empty path did not yield a panic") } -// 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) - } -} - -func TestBasicTouchPadMoves(t *testing.T) { - absDev, err := CreateTouchPad("/dev/uinput", []byte("Test TouchPad"), 0, 1024, 0, 768) - if err != nil { - t.Fatalf("Failed to create the virtual touch pad. Last error was: %s\n", err) - } - - err = absDev.MoveTo(0, 0) - if err != nil { - t.Fatalf("Failed to move cursor to initial position. Last error was: %s\n", err) - } - - err = absDev.MoveTo(100, 200) - if err != nil { - t.Fatalf("Failed to move cursor to position x:100, y:200. Last error was: %s\n", err) - } - err = absDev.RightClick() - if err != nil { - t.Fatalf("Failed to perform right click. Last error was: %s\n", err) - } - - err = absDev.LeftClick() - if err != nil { - t.Fatalf("Failed to perform right click. Last error was: %s\n", err) - } - - err = absDev.LeftPress() - if err != nil { - t.Fatalf("Failed to perform left key press. Last error was: %s\n", err) - } - - err = absDev.LeftRelease() - if err != nil { - t.Fatalf("Failed to perform left key release. Last error was: %s\n", err) - } - - err = absDev.RightPress() - if err != nil { - t.Fatalf("Failed to perform right key press. Last error was: %s\n", err) - } - - err = absDev.RightRelease() - if err != nil { - t.Fatalf("Failed to perform right key release. Last error was: %s\n", err) - } - - err = absDev.Close() - if err != nil { - t.Fatalf("Failed to close device. Last error was: %s\n", err) - } +func TestValidateDevicePathInvalidPathPanics(t *testing.T) { + path := "/some/bogus/path" + expected := "device path '" + path + "' does not exist" + defer func() { + if r := recover(); r != nil { + actual := r.(string) + if actual != expected { + t.Fatalf("Expected: %s\nActual: %s", expected, actual ) + } + } + }() + validateDevicePath(path) + t.Fatalf("Invalid path did not yield a panic") }