kopia lustrzana https://github.com/abourget/shuttle-go
370 wiersze
9.1 KiB
Go
370 wiersze
9.1 KiB
Go
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
|
|
}
|