kopia lustrzana https://github.com/abourget/shuttle-go
Removed dependency on uinput, we're using `xdotool` right now.
rodzic
4adc1c931d
commit
059cdb49ec
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash -xe
|
||||||
|
|
||||||
|
#CGO_ENABLED=0
|
||||||
|
GOOS=linux GOARCH=amd64 go build -v -o shuttle-go
|
10
main.go
10
main.go
|
@ -3,11 +3,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/bendahl/uinput"
|
|
||||||
"github.com/gvalkov/golang-evdev"
|
"github.com/gvalkov/golang-evdev"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,12 +47,6 @@ func main() {
|
||||||
|
|
||||||
go watcher.Run()
|
go watcher.Run()
|
||||||
|
|
||||||
// Virtual keyboard
|
|
||||||
vk, err := uinput.CreateKeyboard("/dev/uinput", []byte("Go Virtual Shuttle Pro V2"))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Can't open dev:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuttle device event receiver
|
// Shuttle device event receiver
|
||||||
dev, err := evdev.Open(devicePath)
|
dev, err := evdev.Open(devicePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,7 +55,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("ready")
|
fmt.Println("ready")
|
||||||
mapper := NewMapper(vk, dev)
|
mapper := NewMapper(dev)
|
||||||
mapper.watcher = watcher
|
mapper.watcher = watcher
|
||||||
for {
|
for {
|
||||||
if err := mapper.Process(); err != nil {
|
if err := mapper.Process(); err != nil {
|
||||||
|
|
13
mapper.go
13
mapper.go
|
@ -7,17 +7,15 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bendahl/uinput"
|
|
||||||
evdev "github.com/gvalkov/golang-evdev"
|
evdev "github.com/gvalkov/golang-evdev"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mapper receives events from the Shuttle devices, and maps (through
|
// Mapper receives events from the Shuttle devices, and maps (through
|
||||||
// configuration) to the Virtual Keyboard events.
|
// configuration) to the Virtual Keyboard events.
|
||||||
type Mapper struct {
|
type Mapper struct {
|
||||||
virtualKeyboard uinput.Keyboard
|
inputDevice *evdev.InputDevice
|
||||||
inputDevice *evdev.InputDevice
|
state buttonsState
|
||||||
state buttonsState
|
watcher *watcher
|
||||||
watcher *watcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type buttonsState struct {
|
type buttonsState struct {
|
||||||
|
@ -27,10 +25,9 @@ type buttonsState struct {
|
||||||
lastJog time.Time
|
lastJog time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMapper(virtualKeyboard uinput.Keyboard, inputDevice *evdev.InputDevice) *Mapper {
|
func NewMapper(inputDevice *evdev.InputDevice) *Mapper {
|
||||||
m := &Mapper{
|
m := &Mapper{
|
||||||
virtualKeyboard: virtualKeyboard,
|
inputDevice: inputDevice,
|
||||||
inputDevice: inputDevice,
|
|
||||||
}
|
}
|
||||||
m.state.buttonsHeld = make(map[int]bool)
|
m.state.buttonsHeld = make(map[int]bool)
|
||||||
m.state.jog = -1
|
m.state.jog = -1
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Benjamin Dahlmanns
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
|
@ -1,41 +0,0 @@
|
||||||
Uinput [![Build Status](https://travis-ci.org/bendahl/uinput.svg?branch=master)](https://travis-ci.org/bendahl/uinput) [![GoDoc](https://godoc.org/github.com/bendahl/uinput?status.png)](https://godoc.org/github.com/bendahl/uinput) [![Go Report Card](https://goreportcard.com/badge/github.com/bendahl/uinput)](https://goreportcard.com/report/github.com/bendahl/uinput)
|
|
||||||
====
|
|
||||||
|
|
||||||
This package provides pure go wrapper functions for the LINUX uinput device, which allows to create virtual input devices
|
|
||||||
in userspace. At the moment this package offers a virtual keyboard implementation as well as a virtual mouse device and
|
|
||||||
a touch pad device.
|
|
||||||
The keyboard can be used to either send single key presses or hold down a specified key and release it later
|
|
||||||
(useful for building game controllers). The mouse device issues relative positional change events to the x and y axis
|
|
||||||
of the mouse pointer and may also fire click events (left and right click). More functionality will be added in future
|
|
||||||
version.
|
|
||||||
The touch pad, on the other hand can be used to move the mouse cursor to the specified position on the screen and to
|
|
||||||
issue left and right clicks. Note that you'll need to specify the region size of your screen first though (happens during
|
|
||||||
device creation).
|
|
||||||
|
|
||||||
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.
|
|
||||||
You may use the following two commands to add the necessary rights for you current user to a file called 99-$USER.rules
|
|
||||||
(where $USER is your current user's name):
|
|
||||||
<pre><code>
|
|
||||||
echo KERNEL==\"uinput\", GROUP=\"$USER\", MODE:=\"0660\" | sudo tee /etc/udev/rules.d/99-$USER.rules
|
|
||||||
sudo udevadm trigger
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
Installation
|
|
||||||
-------------
|
|
||||||
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.
|
|
||||||
|
|
||||||
License
|
|
||||||
--------
|
|
||||||
The package falls under the MIT license. Please see the "LICENSE" file for details.
|
|
||||||
|
|
||||||
ToDos
|
|
||||||
------------------
|
|
||||||
All testing has been done on Ubunu 14.04 and 16.04 x86\_64.
|
|
||||||
Testing for other platforms will need to be done.
|
|
||||||
To get an idea of the things that are on the current todo list, check out the file "TODO.md".
|
|
||||||
As always, helpful comments and ideas are always welcome.
|
|
||||||
Feel free to do some testing on your own if you're up to it.
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
TODO
|
|
||||||
====
|
|
||||||
|
|
||||||
1. ~~Create Tests for the uinput package~~
|
|
||||||
2. ~~Migrate code from C to GO~~
|
|
||||||
3. ~~Implement relative input~~
|
|
||||||
4. ~~Implement absolute input~~
|
|
||||||
5. Test on different platforms (besides x86_64)
|
|
||||||
6. Implement functions to allow mouse button up and down events (for region selects)
|
|
||||||
7. Extend test cases
|
|
|
@ -1,375 +0,0 @@
|
||||||
/*
|
|
||||||
Package uinput is a pure go package that provides access to the userland input device driver uinput on linux systems.
|
|
||||||
Virtual keyboard devices as well as virtual mouse input devices may be created using this package.
|
|
||||||
The keycodes and other event definitions, that are available and can be used to trigger input events,
|
|
||||||
are part of this package ("Key1" for number 1, for example).
|
|
||||||
|
|
||||||
In order to use the virtual keyboard, you will need to follow these three steps:
|
|
||||||
|
|
||||||
1. Initialize the device
|
|
||||||
Example: vk, err := CreateKeyboard("/dev/uinput", "Virtual Keyboard")
|
|
||||||
|
|
||||||
2. Send Button events to the device
|
|
||||||
Example (print a single D):
|
|
||||||
err = vk.KeyPress(uinput.KeyD)
|
|
||||||
|
|
||||||
Example (keep moving right by holding down right arrow key):
|
|
||||||
err = vk.KeyDown(uinput.KeyRight)
|
|
||||||
|
|
||||||
Example (stop moving right by releasing the right arrow key):
|
|
||||||
err = vk.KeyUp(uinput.KeyRight)
|
|
||||||
|
|
||||||
3. Close the device
|
|
||||||
Example: err = vk.Close()
|
|
||||||
|
|
||||||
A virtual mouse input device is just as easy to create and use:
|
|
||||||
|
|
||||||
1. Initialize the device:
|
|
||||||
Example: vm, err := CreateMouse("/dev/uinput", "DangerMouse")
|
|
||||||
|
|
||||||
2. Move the cursor around and issue click events
|
|
||||||
Example (move mouse right):
|
|
||||||
err = vm.MoveRight(42)
|
|
||||||
|
|
||||||
Example (move mouse left):
|
|
||||||
err = vm.MoveLeft(42)
|
|
||||||
|
|
||||||
Example (move mouse up):
|
|
||||||
err = vm.MoveUp(42)
|
|
||||||
|
|
||||||
Example (move mouse down):
|
|
||||||
err = vm.MoveDown(42)
|
|
||||||
|
|
||||||
Example (trigger a left click):
|
|
||||||
err = vm.LeftClick()
|
|
||||||
|
|
||||||
Example (trigger a right click):
|
|
||||||
err = vm.RightClick()
|
|
||||||
|
|
||||||
3. Close the device
|
|
||||||
Example: err = vm.Close()
|
|
||||||
|
|
||||||
|
|
||||||
If you'd like to use absolute input events (move the cursor to specific positions on screen), use the touch pad.
|
|
||||||
Note that you'll need to specify the size of the screen area you want to use when you initialize the
|
|
||||||
device. Here are a few examples of how to use the virtual touch pad:
|
|
||||||
|
|
||||||
1. Initialize the device:
|
|
||||||
Example: vt, err := CreateTouchPad("/dev/uinput", "DontTouchThis", 0, 1024, 0, 768)
|
|
||||||
|
|
||||||
2. Move the cursor around and issue click events
|
|
||||||
Example (move cursor to the top left corner of the screen):
|
|
||||||
err = vt.MoveTo(0, 0)
|
|
||||||
|
|
||||||
Example (move cursor to the position x: 100, y: 250):
|
|
||||||
err = vt.MoveTo(100, 250)
|
|
||||||
|
|
||||||
Example (trigger a left click):
|
|
||||||
err = vt.LeftClick()
|
|
||||||
|
|
||||||
Example (trigger a right click):
|
|
||||||
err = vt.RightClick()
|
|
||||||
|
|
||||||
3. Close the device
|
|
||||||
Example: err = vt.Close()
|
|
||||||
|
|
||||||
*/
|
|
||||||
package uinput
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if path == "" {
|
|
||||||
return nil, errors.New("device path must not be empty")
|
|
||||||
}
|
|
||||||
if len(name) > uinputMaxNameSize {
|
|
||||||
return nil, fmt.Errorf("device name %s is too long (maximum of %d characters allowed)", name, uinputMaxNameSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if path == "" {
|
|
||||||
return nil, errors.New("device path must not be empty")
|
|
||||||
}
|
|
||||||
if len(name) > uinputMaxNameSize {
|
|
||||||
return nil, fmt.Errorf("device name %s is too long (maximum of %d characters allowed)", name, uinputMaxNameSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
if path == "" {
|
|
||||||
return nil, errors.New("device path must not be empty")
|
|
||||||
}
|
|
||||||
if len(name) > uinputMaxNameSize {
|
|
||||||
return nil, fmt.Errorf("device name %s is too long (maximum of %d characters allowed)", name, uinputMaxNameSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
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, btnStatePressed)
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,249 +0,0 @@
|
||||||
package uinput
|
|
||||||
|
|
||||||
// the constants were copied from input.h for convenience reasons
|
|
||||||
const (
|
|
||||||
keyReserved = 0
|
|
||||||
KeyEsc = 1
|
|
||||||
Key1 = 2
|
|
||||||
Key2 = 3
|
|
||||||
Key3 = 4
|
|
||||||
Key4 = 5
|
|
||||||
Key5 = 6
|
|
||||||
Key6 = 7
|
|
||||||
Key7 = 8
|
|
||||||
Key8 = 9
|
|
||||||
Key9 = 10
|
|
||||||
Key0 = 11
|
|
||||||
KeyMinus = 12
|
|
||||||
KeyEqual = 13
|
|
||||||
KeyBackspace = 14
|
|
||||||
KeyTab = 15
|
|
||||||
KeyQ = 16
|
|
||||||
KeyW = 17
|
|
||||||
KeyE = 18
|
|
||||||
KeyR = 19
|
|
||||||
KeyT = 20
|
|
||||||
KeyY = 21
|
|
||||||
KeyU = 22
|
|
||||||
KeyI = 23
|
|
||||||
KeyO = 24
|
|
||||||
KeyP = 25
|
|
||||||
KeyLeftbrace = 26
|
|
||||||
KeyRightbrace = 27
|
|
||||||
KeyEnter = 28
|
|
||||||
KeyLeftctrl = 29
|
|
||||||
KeyA = 30
|
|
||||||
KeyS = 31
|
|
||||||
KeyD = 32
|
|
||||||
KeyF = 33
|
|
||||||
KeyG = 34
|
|
||||||
KeyH = 35
|
|
||||||
KeyJ = 36
|
|
||||||
KeyK = 37
|
|
||||||
KeyL = 38
|
|
||||||
KeySemicolon = 39
|
|
||||||
KeyApostrophe = 40
|
|
||||||
KeyGrave = 41
|
|
||||||
KeyLeftshift = 42
|
|
||||||
KeyBackslash = 43
|
|
||||||
KeyZ = 44
|
|
||||||
KeyX = 45
|
|
||||||
KeyC = 46
|
|
||||||
KeyV = 47
|
|
||||||
KeyB = 48
|
|
||||||
KeyN = 49
|
|
||||||
KeyM = 50
|
|
||||||
KeyComma = 51
|
|
||||||
KeyDot = 52
|
|
||||||
KeySlash = 53
|
|
||||||
KeyRightshift = 54
|
|
||||||
KeyKpasterisk = 55
|
|
||||||
KeyLeftalt = 56
|
|
||||||
KeySpace = 57
|
|
||||||
KeyCapslock = 58
|
|
||||||
KeyF1 = 59
|
|
||||||
KeyF2 = 60
|
|
||||||
KeyF3 = 61
|
|
||||||
KeyF4 = 62
|
|
||||||
KeyF5 = 63
|
|
||||||
KeyF6 = 64
|
|
||||||
KeyF7 = 65
|
|
||||||
KeyF8 = 66
|
|
||||||
KeyF9 = 67
|
|
||||||
KeyF10 = 68
|
|
||||||
KeyNumlock = 69
|
|
||||||
KeyScrolllock = 70
|
|
||||||
KeyKp7 = 71
|
|
||||||
KeyKp8 = 72
|
|
||||||
KeyKp9 = 73
|
|
||||||
KeyKpminus = 74
|
|
||||||
KeyKp4 = 75
|
|
||||||
KeyKp5 = 76
|
|
||||||
KeyKp6 = 77
|
|
||||||
KeyKpplus = 78
|
|
||||||
KeyKp1 = 79
|
|
||||||
KeyKp2 = 80
|
|
||||||
KeyKp3 = 81
|
|
||||||
KeyKp0 = 82
|
|
||||||
KeyKpdot = 83
|
|
||||||
KeyZenkakuhankaku = 85
|
|
||||||
Key102Nd = 86
|
|
||||||
KeyF11 = 87
|
|
||||||
KeyF12 = 88
|
|
||||||
KeyRo = 89
|
|
||||||
KeyKatakana = 90
|
|
||||||
KeyHiragana = 91
|
|
||||||
KeyHenkan = 92
|
|
||||||
KeyKatakanahiragana = 93
|
|
||||||
KeyMuhenkan = 94
|
|
||||||
KeyKpjpcomma = 95
|
|
||||||
KeyKpenter = 96
|
|
||||||
KeyRightctrl = 97
|
|
||||||
KeyKpslash = 98
|
|
||||||
KeySysrq = 99
|
|
||||||
KeyRightalt = 100
|
|
||||||
KeyLinefeed = 101
|
|
||||||
KeyHome = 102
|
|
||||||
KeyUp = 103
|
|
||||||
KeyPageup = 104
|
|
||||||
KeyLeft = 105
|
|
||||||
KeyRight = 106
|
|
||||||
KeyEnd = 107
|
|
||||||
KeyDown = 108
|
|
||||||
KeyPagedown = 109
|
|
||||||
KeyInsert = 110
|
|
||||||
KeyDelete = 111
|
|
||||||
KeyMacro = 112
|
|
||||||
KeyMute = 113
|
|
||||||
KeyVolumedown = 114
|
|
||||||
KeyVolumeup = 115
|
|
||||||
KeyPower = 116 /*ScSystemPowerDown*/
|
|
||||||
KeyKpequal = 117
|
|
||||||
KeyKpplusminus = 118
|
|
||||||
KeyPause = 119
|
|
||||||
KeyScale = 120 /*AlCompizScale(Expose)*/
|
|
||||||
KeyKpcomma = 121
|
|
||||||
KeyHangeul = 122
|
|
||||||
KeyHanja = 123
|
|
||||||
KeyYen = 124
|
|
||||||
KeyLeftmeta = 125
|
|
||||||
KeyRightmeta = 126
|
|
||||||
KeyCompose = 127
|
|
||||||
KeyStop = 128 /*AcStop*/
|
|
||||||
KeyAgain = 129
|
|
||||||
KeyProps = 130 /*AcProperties*/
|
|
||||||
KeyUndo = 131 /*AcUndo*/
|
|
||||||
KeyFront = 132
|
|
||||||
KeyCopy = 133 /*AcCopy*/
|
|
||||||
KeyOpen = 134 /*AcOpen*/
|
|
||||||
KeyPaste = 135 /*AcPaste*/
|
|
||||||
KeyFind = 136 /*AcSearch*/
|
|
||||||
KeyCut = 137 /*AcCut*/
|
|
||||||
KeyHelp = 138 /*AlIntegratedHelpCenter*/
|
|
||||||
KeyMenu = 139 /*Menu(ShowMenu)*/
|
|
||||||
KeyCalc = 140 /*AlCalculator*/
|
|
||||||
KeySetup = 141
|
|
||||||
KeySleep = 142 /*ScSystemSleep*/
|
|
||||||
KeyWakeup = 143 /*SystemWakeUp*/
|
|
||||||
KeyFile = 144 /*AlLocalMachineBrowser*/
|
|
||||||
KeySendfile = 145
|
|
||||||
KeyDeletefile = 146
|
|
||||||
KeyXfer = 147
|
|
||||||
KeyProg1 = 148
|
|
||||||
KeyProg2 = 149
|
|
||||||
KeyWww = 150 /*AlInternetBrowser*/
|
|
||||||
KeyMsdos = 151
|
|
||||||
KeyCoffee = 152 /*AlTerminalLock/Screensaver*/
|
|
||||||
KeyDirection = 153
|
|
||||||
KeyCyclewindows = 154
|
|
||||||
KeyMail = 155
|
|
||||||
KeyBookmarks = 156 /*AcBookmarks*/
|
|
||||||
KeyComputer = 157
|
|
||||||
KeyBack = 158 /*AcBack*/
|
|
||||||
KeyForward = 159 /*AcForward*/
|
|
||||||
KeyClosecd = 160
|
|
||||||
KeyEjectcd = 161
|
|
||||||
KeyEjectclosecd = 162
|
|
||||||
KeyNextsong = 163
|
|
||||||
KeyPlaypause = 164
|
|
||||||
KeyPrevioussong = 165
|
|
||||||
KeyStopcd = 166
|
|
||||||
KeyRecord = 167
|
|
||||||
KeyRewind = 168
|
|
||||||
KeyPhone = 169 /*MediaSelectTelephone*/
|
|
||||||
KeyIso = 170
|
|
||||||
KeyConfig = 171 /*AlConsumerControlConfiguration*/
|
|
||||||
KeyHomepage = 172 /*AcHome*/
|
|
||||||
KeyRefresh = 173 /*AcRefresh*/
|
|
||||||
KeyExit = 174 /*AcExit*/
|
|
||||||
KeyMove = 175
|
|
||||||
KeyEdit = 176
|
|
||||||
KeyScrollup = 177
|
|
||||||
KeyScrolldown = 178
|
|
||||||
KeyKpleftparen = 179
|
|
||||||
KeyKprightparen = 180
|
|
||||||
KeyNew = 181 /*AcNew*/
|
|
||||||
KeyRedo = 182 /*AcRedo/Repeat*/
|
|
||||||
KeyF13 = 183
|
|
||||||
KeyF14 = 184
|
|
||||||
KeyF15 = 185
|
|
||||||
KeyF16 = 186
|
|
||||||
KeyF17 = 187
|
|
||||||
KeyF18 = 188
|
|
||||||
KeyF19 = 189
|
|
||||||
KeyF20 = 190
|
|
||||||
KeyF21 = 191
|
|
||||||
KeyF22 = 192
|
|
||||||
KeyF23 = 193
|
|
||||||
KeyF24 = 194
|
|
||||||
KeyPlaycd = 200
|
|
||||||
KeyPausecd = 201
|
|
||||||
KeyProg3 = 202
|
|
||||||
KeyProg4 = 203
|
|
||||||
KeyDashboard = 204 /*AlDashboard*/
|
|
||||||
KeySuspend = 205
|
|
||||||
KeyClose = 206 /*AcClose*/
|
|
||||||
KeyPlay = 207
|
|
||||||
KeyFastforward = 208
|
|
||||||
KeyBassboost = 209
|
|
||||||
KeyPrint = 210 /*AcPrint*/
|
|
||||||
KeyHp = 211
|
|
||||||
KeyCamera = 212
|
|
||||||
KeySound = 213
|
|
||||||
KeyQuestion = 214
|
|
||||||
KeyEmail = 215
|
|
||||||
KeyChat = 216
|
|
||||||
KeySearch = 217
|
|
||||||
KeyConnect = 218
|
|
||||||
KeyFinance = 219 /*AlCheckbook/Finance*/
|
|
||||||
KeySport = 220
|
|
||||||
KeyShop = 221
|
|
||||||
KeyAlterase = 222
|
|
||||||
KeyCancel = 223 /*AcCancel*/
|
|
||||||
KeyBrightnessdown = 224
|
|
||||||
KeyBrightnessup = 225
|
|
||||||
KeyMedia = 226
|
|
||||||
KeySwitchvideomode = 227 /*CycleBetweenAvailableVideo */
|
|
||||||
KeyKbdillumtoggle = 228
|
|
||||||
KeyKbdillumdown = 229
|
|
||||||
KeyKbdillumup = 230
|
|
||||||
KeySend = 231 /*AcSend*/
|
|
||||||
KeyReply = 232 /*AcReply*/
|
|
||||||
KeyForwardmail = 233 /*AcForwardMsg*/
|
|
||||||
KeySave = 234 /*AcSave*/
|
|
||||||
KeyDocuments = 235
|
|
||||||
KeyBattery = 236
|
|
||||||
KeyBluetooth = 237
|
|
||||||
KeyWlan = 238
|
|
||||||
KeyUwb = 239
|
|
||||||
KeyUnknown = 240
|
|
||||||
KeyVideoNext = 241 /*DriveNextVideoSource*/
|
|
||||||
KeyVideoPrev = 242 /*DrivePreviousVideoSource*/
|
|
||||||
KeyBrightnessCycle = 243 /*BrightnessUp,AfterMaxIsMin*/
|
|
||||||
KeyBrightnessZero = 244 /*BrightnessOff,UseAmbient*/
|
|
||||||
KeyDisplayOff = 245 /*DisplayDeviceToOffState*/
|
|
||||||
KeyWimax = 246
|
|
||||||
KeyRfkill = 247 /*KeyThatControlsAllRadios*/
|
|
||||||
KeyMicmute = 248 /*Mute/UnmuteTheMicrophone*/
|
|
||||||
keyMax = 248 // highest key currently defined
|
|
||||||
)
|
|
|
@ -1,369 +0,0 @@
|
||||||
package uinput
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// types needed from uinput.h
|
|
||||||
const (
|
|
||||||
uinputMaxNameSize = 80
|
|
||||||
uiDevCreate = 0x5501
|
|
||||||
uiDevDestroy = 0x5502
|
|
||||||
uiSetEvBit = 0x40045564
|
|
||||||
uiSetKeyBit = 0x40045565
|
|
||||||
uiSetRelBit = 0x40045566
|
|
||||||
uiSetAbsBit = 0x40045567
|
|
||||||
busUsb = 0x03
|
|
||||||
)
|
|
||||||
|
|
||||||
// input event codes as specified in input-event-codes.h
|
|
||||||
const (
|
|
||||||
evSyn = 0x00
|
|
||||||
evKey = 0x01
|
|
||||||
evRel = 0x02
|
|
||||||
evAbs = 0x03
|
|
||||||
relX = 0x0
|
|
||||||
relY = 0x1
|
|
||||||
absX = 0x0
|
|
||||||
absY = 0x1
|
|
||||||
synReport = 0
|
|
||||||
evBtnLeft = 0x110
|
|
||||||
evBtnRight = 0x111
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
btnStateReleased = 0
|
|
||||||
btnStatePressed = 1
|
|
||||||
absSize = 64
|
|
||||||
)
|
|
||||||
|
|
||||||
type inputID struct {
|
|
||||||
Bustype uint16
|
|
||||||
Vendor uint16
|
|
||||||
Product uint16
|
|
||||||
Version uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// translated to go from uinput.h
|
|
||||||
type uinputUserDev struct {
|
|
||||||
Name [uinputMaxNameSize]byte
|
|
||||||
ID inputID
|
|
||||||
EffectsMax uint32
|
|
||||||
Absmax [absSize]int32
|
|
||||||
Absmin [absSize]int32
|
|
||||||
Absfuzz [absSize]int32
|
|
||||||
Absflat [absSize]int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// translated to go from input.h
|
|
||||||
type inputEvent struct {
|
|
||||||
Time syscall.Timeval
|
|
||||||
Type uint16
|
|
||||||
Code uint16
|
|
||||||
Value int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeDevice(deviceFile *os.File) (err error) {
|
|
||||||
err = releaseDevice(deviceFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to close device: %v", err)
|
|
||||||
}
|
|
||||||
return deviceFile.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func releaseDevice(deviceFile *os.File) (err error) {
|
|
||||||
return ioctl(deviceFile, uiDevDestroy, uintptr(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
func createDeviceFile(path string) (fd *os.File, err error) {
|
|
||||||
deviceFile, err := os.OpenFile(path, syscall.O_WRONLY|syscall.O_NONBLOCK, 0660)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("could not open device file")
|
|
||||||
}
|
|
||||||
return deviceFile, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerDevice(deviceFile *os.File, evType uintptr) error {
|
|
||||||
err := ioctl(deviceFile, uiSetEvBit, evType)
|
|
||||||
if err != nil {
|
|
||||||
err = releaseDevice(deviceFile)
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return fmt.Errorf("failed to close device: %v", err)
|
|
||||||
}
|
|
||||||
deviceFile.Close()
|
|
||||||
return fmt.Errorf("invalid file handle returned from ioctl: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 toUinputName(name []byte) (uinputName [uinputMaxNameSize]byte) {
|
|
||||||
var fixedSizeName [uinputMaxNameSize]byte
|
|
||||||
copy(fixedSizeName[:], name)
|
|
||||||
return fixedSizeName
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTouchPad(path string, name []byte, minX int32, maxX int32, minY int32, maxY int32) (fd *os.File, err error) {
|
|
||||||
deviceFile, err := createDeviceFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not create absolute axis input device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = registerDevice(deviceFile, uintptr(evKey))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register key device: %v", err)
|
|
||||||
}
|
|
||||||
// register button events (in order to enable left and right click)
|
|
||||||
err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnLeft))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register left click event: %v", err)
|
|
||||||
}
|
|
||||||
err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnRight))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register right click event: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = registerDevice(deviceFile, uintptr(evAbs))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register absolute axis input device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// register x and y axis events
|
|
||||||
err = ioctl(deviceFile, uiSetAbsBit, uintptr(absX))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register absolute x axis events: %v", err)
|
|
||||||
}
|
|
||||||
err = ioctl(deviceFile, uiSetAbsBit, uintptr(absY))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register absolute y axis events: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var absMin [absSize]int32
|
|
||||||
absMin[absX] = minX
|
|
||||||
absMin[absY] = minY
|
|
||||||
|
|
||||||
var absMax [absSize]int32
|
|
||||||
absMax[absX] = maxX
|
|
||||||
absMax[absY] = maxY
|
|
||||||
|
|
||||||
return createUsbDevice(deviceFile,
|
|
||||||
uinputUserDev{
|
|
||||||
Name: toUinputName(name),
|
|
||||||
ID: inputID{
|
|
||||||
Bustype: busUsb,
|
|
||||||
Vendor: 0x4711,
|
|
||||||
Product: 0x0817,
|
|
||||||
Version: 1},
|
|
||||||
Absmin: absMin,
|
|
||||||
Absmax: absMax})
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMouse(path string, name []byte) (fd *os.File, err error) {
|
|
||||||
deviceFile, err := createDeviceFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not create relative axis input device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = registerDevice(deviceFile, uintptr(evKey))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register key device: %v", err)
|
|
||||||
}
|
|
||||||
// register button events (in order to enable left and right click)
|
|
||||||
err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnLeft))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register left click event: %v", err)
|
|
||||||
}
|
|
||||||
err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnRight))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register right click event: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = registerDevice(deviceFile, uintptr(evRel))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register relative axis input device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// register x and y axis events
|
|
||||||
err = ioctl(deviceFile, uiSetRelBit, uintptr(relX))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register relative x axis events: %v", err)
|
|
||||||
}
|
|
||||||
err = ioctl(deviceFile, uiSetRelBit, uintptr(relY))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to register relative y axis events: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return createUsbDevice(deviceFile,
|
|
||||||
uinputUserDev{
|
|
||||||
Name: toUinputName(name),
|
|
||||||
ID: inputID{
|
|
||||||
Bustype: busUsb,
|
|
||||||
Vendor: 0x4711,
|
|
||||||
Product: 0x0816,
|
|
||||||
Version: 1}})
|
|
||||||
}
|
|
||||||
|
|
||||||
func createUsbDevice(deviceFile *os.File, dev uinputUserDev) (fd *os.File, err error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
err = binary.Write(buf, binary.LittleEndian, dev)
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to write user device buffer: %v", err)
|
|
||||||
}
|
|
||||||
_, err = deviceFile.Write(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to write uidev struct to device file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ioctl(deviceFile, uiDevCreate, uintptr(0))
|
|
||||||
if err != nil {
|
|
||||||
deviceFile.Close()
|
|
||||||
return nil, fmt.Errorf("failed to create device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 200)
|
|
||||||
|
|
||||||
return deviceFile, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendBtnEvent(deviceFile *os.File, key int, btnState int) (err error) {
|
|
||||||
buf, err := inputEventToBuffer(inputEvent{
|
|
||||||
Time: syscall.Timeval{Sec: 0, Usec: 0},
|
|
||||||
Type: evKey,
|
|
||||||
Code: uint16(key),
|
|
||||||
Value: int32(btnState)})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("key event could not be set: %v", err)
|
|
||||||
}
|
|
||||||
_, err = deviceFile.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("writing btnEvent structure to the device file failed: %v", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendAbsEvent(deviceFile *os.File, xPos int32, yPos int32) error {
|
|
||||||
var ev [2]inputEvent
|
|
||||||
ev[0].Type = evAbs
|
|
||||||
ev[0].Code = absX
|
|
||||||
ev[0].Value = xPos
|
|
||||||
|
|
||||||
ev[1].Type = evAbs
|
|
||||||
ev[1].Code = absY
|
|
||||||
ev[1].Value = yPos
|
|
||||||
|
|
||||||
for _, iev := range ev {
|
|
||||||
buf, err := inputEventToBuffer(iev)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("writing abs event failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = deviceFile.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to write abs event to device file: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return syncEvents(deviceFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendRelEvent(deviceFile *os.File, eventCode uint16, pixel int32) error {
|
|
||||||
iev := inputEvent{
|
|
||||||
Time: syscall.Timeval{Sec: 0, Usec: 0},
|
|
||||||
Type: evRel,
|
|
||||||
Code: eventCode,
|
|
||||||
Value: pixel}
|
|
||||||
|
|
||||||
buf, err := inputEventToBuffer(iev)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("writing abs event failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = deviceFile.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to write rel event to device file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return syncEvents(deviceFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func syncEvents(deviceFile *os.File) (err error) {
|
|
||||||
buf, err := inputEventToBuffer(inputEvent{
|
|
||||||
Time: syscall.Timeval{Sec: 0, Usec: 0},
|
|
||||||
Type: evSyn,
|
|
||||||
Code: 0,
|
|
||||||
Value: int32(synReport)})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("writing sync event failed: %v", err)
|
|
||||||
}
|
|
||||||
_, err = deviceFile.Write(buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func inputEventToBuffer(iev inputEvent) (buffer []byte, err error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
err = binary.Write(buf, binary.LittleEndian, iev)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to write input event to buffer: %v", err)
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// original function taken from: https://github.com/tianon/debian-golang-pty/blob/master/ioctl.go
|
|
||||||
func ioctl(deviceFile *os.File, cmd, ptr uintptr) error {
|
|
||||||
_, _, errorCode := syscall.Syscall(syscall.SYS_IOCTL, deviceFile.Fd(), cmd, ptr)
|
|
||||||
if errorCode != 0 {
|
|
||||||
return errorCode
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -20,12 +20,6 @@
|
||||||
"revision": "27f122750802c950b2c869a5b63dafcf590ced95",
|
"revision": "27f122750802c950b2c869a5b63dafcf590ced95",
|
||||||
"revisionTime": "2016-05-22T18:18:43Z"
|
"revisionTime": "2016-05-22T18:18:43Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"checksumSHA1": "DuHM9UBDn6AHdGqYDMZZqzFMOkI=",
|
|
||||||
"path": "github.com/bendahl/uinput",
|
|
||||||
"revision": "050ec524df761f5a128a5bb9bb5b7609f1886f21",
|
|
||||||
"revisionTime": "2017-06-20T19:50:18Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"checksumSHA1": "OFu4xJEIjiI8Suu+j/gabfp+y6Q=",
|
"checksumSHA1": "OFu4xJEIjiI8Suu+j/gabfp+y6Q=",
|
||||||
"origin": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew",
|
"origin": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew",
|
||||||
|
|
Ładowanie…
Reference in New Issue