From 802b84a7a670693b5e926fcb75f14a02c7130896 Mon Sep 17 00:00:00 2001 From: Wilhelmina Drengwitz Date: Tue, 30 Aug 2016 12:21:13 -0400 Subject: [PATCH 1/3] Reset vKeyboard file descriptor after a successful Close() operation --- uinput.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/uinput.go b/uinput.go index d9f33ec..649bcdb 100644 --- a/uinput.go +++ b/uinput.go @@ -76,7 +76,14 @@ func (vk *VKeyboard) Close() (err error) { if vk.id < 0 { return errors.New("Keyboard not initialized. Closing device failed.") } - return closeDevice(vk.id) + + err = closeDevice(vk.id) + if err != nil { + return err + } + + vk.id = -1 + return nil } func createVKeyboardDevice(path, name string) (deviceId int, err error) { From 2b9c3e73b7eee7f96bcd2c77542f2a3faea95a9b Mon Sep 17 00:00:00 2001 From: Wilhelmina Drengwitz Date: Tue, 30 Aug 2016 12:34:55 -0400 Subject: [PATCH 2/3] Hide keyboard behind an interface to prevent invalid file descriptor states --- uinput.go | 72 ++++++++++++++++++++------------------------------ uinput_test.go | 15 +++-------- 2 files changed, 32 insertions(+), 55 deletions(-) diff --git a/uinput.go b/uinput.go index 649bcdb..8038c07 100644 --- a/uinput.go +++ b/uinput.go @@ -7,12 +7,11 @@ example). In order to use the virtual keyboard, you will need to follow these three steps: 1. Initialize the device - Example: vk := VKeyboard{} - err := vk.Create("/dev/uinput") - + Example: vk, err := CreateKeyboard("/dev/uinput", "Virtual Keyboard") 2. Send Button events to the device Example: err = vk.SendKeyPress(uinput.KEY_D) + err = vk.SendKeyRelease(uinput.KEY_D) 3. Close the device Example: err = vk.Close() @@ -25,65 +24,50 @@ package uinput import "C" import ( "errors" + "io" "unsafe" ) -// VKeyboard represents a virtual keyboard device. There are several -// methods available to work with this virtual device. Devices can be -// created, receive events, and closed. -type VKeyboard struct { - // The Name of the uinput device. Will be trimmed to a Max Length of 80 bytes. - // If left blank the device will have a default name. - Name string +// A Keyboard is an key event output device. It is used to +// enable a program to simulate HID keyboard input events. +type Keyboard interface { + // SendKeyPress will send a keypress event to an existing keyboard device. + // The key can be any of the predefined keycodes from uinputdefs. + SendKeyPress(key int) error - id int -} + // SendKeyRelease will send a keyrelease event to an existing keyboard device. + // The key can be any of the predefined keycodes from uinputdefs. + SendKeyRelease(key int) error -// Create creates a new virtual keyboard device. -// Make sure to pass the correct path to the current system's -// uinput device (usually either "/dev/uinput" or "/dev/input/uinput". -func (vk *VKeyboard) Create(path string) (err error) { - vk.id = -1 - var ret error + io.Closer +} - vk.id, ret = createVKeyboardDevice(path, vk.Name) - return ret +type vKeyboard struct { + name string + id int } -// SendKeyPress will send a keypress event to an existing keyboard device. -// The key can be any of the predefined keycodes from uinputdefs. -func (vk *VKeyboard) SendKeyPress(key int) (err error) { - if vk.id < 0 { - return errors.New("Keyboard not initialized. Sending keypress event failed.") +func CreateKeyboard(path, name string) (Keyboard, error) { + fd, err := createVKeyboardDevice(path, name) + if err != nil { + return nil, err } - return sendBtnEvent(vk.id, key, 1) + return vKeyboard{name, fd}, nil } -// SendKeyRelease will send a keyrelease event to an existing keyboard device. -// The key can be any of the predefined keycodes from uinputdefs. -func (vk *VKeyboard) SendKeyRelease(key int) (err error) { - if vk.id < 0 { - return errors.New("Keyboard not initialized. Sending keyrelease event failed.") - } +func (vk vKeyboard) SendKeyPress(key int) error { + return sendBtnEvent(vk.id, key, 1) +} +func (vk vKeyboard) SendKeyRelease(key int) error { return sendBtnEvent(vk.id, key, 0) } // 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() (err error) { - if vk.id < 0 { - return errors.New("Keyboard not initialized. Closing device failed.") - } - - err = closeDevice(vk.id) - if err != nil { - return err - } - - vk.id = -1 - return nil +func (vk vKeyboard) Close() error { + return closeDevice(vk.id) } func createVKeyboardDevice(path, name string) (deviceId int, err error) { diff --git a/uinput_test.go b/uinput_test.go index 8ba4165..3cc6229 100644 --- a/uinput_test.go +++ b/uinput_test.go @@ -5,10 +5,8 @@ import ( ) // This test will create a basic VKeyboard, send a key command and then close the keyboard device -func TestBasicVKeyboard(t *testing.T) { - vk := VKeyboard{} - err := vk.Create("/dev/uinput") - +func TestBasicKeyboard(t *testing.T) { + vk, err := CreateKeyboard("/dev/uinput", "Test Basic Keyboard") if err != nil { t.Fatalf("Failed to create the virtual keyboard. Last error was: %s\n", err) } @@ -35,9 +33,7 @@ func TestBasicVKeyboard(t *testing.T) { // 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 := VKeyboard{} - err := vk.Create("/invalid/path") - + vk, err := CreateKeyboard("/invalid/path", "Invalid Device Path") if err == nil { // this usually shouldn't happen, but if the device is created, we need to close it vk.Close() @@ -48,15 +44,12 @@ func TestInvalidDevicePath(t *testing.T) { // This test will confirm that a proper error code is returned if an invalid keycode is // passed to the library func TestInvalidKeycode(t *testing.T) { - vk := VKeyboard{} - err := vk.Create("/dev/uinput") - + vk, err := CreateKeyboard("/dev/uinput", "Test Keyboard") if err != nil { t.Fatalf("Failed to create the virtual keyboard. Last error was: %s\n", err) } err = vk.SendKeyPress(4711) - if err == nil { t.Fatalf("Sending an invalid keycode did not trigger an error. Got: %d.\n") } From c70a20642ebb54dcf4b8af930a554e88f4c3406c Mon Sep 17 00:00:00 2001 From: Wilhelmina Drengwitz Date: Tue, 30 Aug 2016 12:40:51 -0400 Subject: [PATCH 3/3] Rename 'id' => 'fd' to improve self documentation --- uinput.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uinput.go b/uinput.go index 8038c07..6546241 100644 --- a/uinput.go +++ b/uinput.go @@ -44,7 +44,7 @@ type Keyboard interface { type vKeyboard struct { name string - id int + fd int } func CreateKeyboard(path, name string) (Keyboard, error) { @@ -57,17 +57,17 @@ func CreateKeyboard(path, name string) (Keyboard, error) { } func (vk vKeyboard) SendKeyPress(key int) error { - return sendBtnEvent(vk.id, key, 1) + return sendBtnEvent(vk.fd, key, 1) } func (vk vKeyboard) SendKeyRelease(key int) error { - return sendBtnEvent(vk.id, key, 0) + return sendBtnEvent(vk.fd, key, 0) } // 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.id) + return closeDevice(vk.fd) } func createVKeyboardDevice(path, name string) (deviceId int, err error) {