shuttle-go/vendor/github.com/gvalkov/golang-evdev/device.go

308 wiersze
7.1 KiB
Go

// +build linux
package evdev
import (
"bytes"
"encoding/binary"
"fmt"
"os"
"path/filepath"
"strings"
"unsafe"
)
// A Linux input device from which events can be read.
type InputDevice struct {
Fn string // path to input device (devnode)
Name string // device name
Phys string // physical topology of device
File *os.File // an open file handle to the input device
Bustype uint16 // bus type identifier
Vendor uint16 // vendor identifier
Product uint16 // product identifier
Version uint16 // version identifier
EvdevVersion int // evdev protocol version
Capabilities map[CapabilityType][]CapabilityCode // supported event types and codes.
CapabilitiesFlat map[int][]int
}
// Open an evdev input device.
func Open(devnode string) (*InputDevice, error) {
f, err := os.Open(devnode)
if err != nil {
return nil, err
}
dev := InputDevice{}
dev.Fn = devnode
dev.File = f
dev.set_device_info()
dev.set_device_capabilities()
return &dev, nil
}
// Read and return a slice of input events from device.
func (dev *InputDevice) Read() ([]InputEvent, error) {
events := make([]InputEvent, 16)
buffer := make([]byte, eventsize*16)
_, err := dev.File.Read(buffer)
if err != nil {
return events, err
}
b := bytes.NewBuffer(buffer)
err = binary.Read(b, binary.LittleEndian, &events)
if err != nil {
return events, err
}
// remove trailing structures
for i := range events {
if events[i].Time.Sec == 0 {
events = append(events[:i])
break
}
}
return events, err
}
// Read and return a single input event.
func (dev *InputDevice) ReadOne() (*InputEvent, error) {
event := InputEvent{}
buffer := make([]byte, eventsize)
_, err := dev.File.Read(buffer)
if err != nil {
return &event, err
}
b := bytes.NewBuffer(buffer)
err = binary.Read(b, binary.LittleEndian, &event)
if err != nil {
return &event, err
}
return &event, err
}
// Get a useful description for an input device. Example:
// InputDevice /dev/input/event3 (fd 3)
// name Logitech USB Laser Mouse
// phys usb-0000:00:12.0-2/input0
// bus 0x3, vendor 0x46d, product 0xc069, version 0x110
// events EV_KEY 1, EV_SYN 0, EV_REL 2, EV_MSC 4
func (dev *InputDevice) String() string {
evtypes := make([]string, 0)
for ev := range dev.Capabilities {
evtypes = append(evtypes, fmt.Sprintf("%s %d", ev.Name, ev.Type))
}
evtypes_s := strings.Join(evtypes, ", ")
return fmt.Sprintf(
"InputDevice %s (fd %d)\n"+
" name %s\n"+
" phys %s\n"+
" bus 0x%04x, vendor 0x%04x, product 0x%04x, version 0x%04x\n"+
" events %s",
dev.Fn, dev.File.Fd(), dev.Name, dev.Phys, dev.Bustype,
dev.Vendor, dev.Product, dev.Version, evtypes_s)
}
// Gets the event types and event codes that the input device supports.
func (dev *InputDevice) set_device_capabilities() error {
// Capabilities is a map of supported event types to lists of
// events e.g: {1: [272, 273, 274, 275], 2: [0, 1, 6, 8]}
// capabilities := make(map[int][]int)
capabilities := make(map[CapabilityType][]CapabilityCode)
evbits := new([(EV_MAX + 1) / 8]byte)
codebits := new([(KEY_MAX + 1) / 8]byte)
// absbits := new([6]byte)
err := ioctl(dev.File.Fd(), uintptr(EVIOCGBIT(0, EV_MAX)), unsafe.Pointer(evbits))
if err != 0 {
return err
}
// Build a map of the device's capabilities
for evtype := 0; evtype < EV_MAX; evtype++ {
if evbits[evtype/8]&(1<<uint(evtype%8)) != 0 {
eventcodes := make([]CapabilityCode, 0)
ioctl(dev.File.Fd(), uintptr(EVIOCGBIT(evtype, KEY_MAX)), unsafe.Pointer(codebits))
for evcode := 0; evcode < KEY_MAX; evcode++ {
if codebits[evcode/8]&(1<<uint(evcode%8)) != 0 {
c := CapabilityCode{evcode, ByEventType[evtype][evcode]}
eventcodes = append(eventcodes, c)
}
}
// capabilities[EV_KEY] = [KEY_A, KEY_B, KEY_C, ...]
key := CapabilityType{evtype, EV[evtype]}
capabilities[key] = eventcodes
}
}
dev.Capabilities = capabilities
return nil
}
// An all-in-one function for describing an input device.
func (dev *InputDevice) set_device_info() error {
info := device_info{}
name := new([MAX_NAME_SIZE]byte)
phys := new([MAX_NAME_SIZE]byte)
err := ioctl(dev.File.Fd(), uintptr(EVIOCGID), unsafe.Pointer(&info))
if err != 0 {
return err
}
ioctl(dev.File.Fd(), uintptr(EVIOCGNAME), unsafe.Pointer(name))
if err != 0 {
return err
}
// it's ok if the topology info is not available
ioctl(dev.File.Fd(), uintptr(EVIOCGPHYS), unsafe.Pointer(phys))
dev.Name = bytes_to_string(name)
dev.Phys = bytes_to_string(phys)
dev.Vendor = info.vendor
dev.Bustype = info.bustype
dev.Product = info.product
dev.Version = info.version
ev_version := new(int)
ioctl(dev.File.Fd(), uintptr(EVIOCGVERSION), unsafe.Pointer(ev_version))
dev.EvdevVersion = *ev_version
return nil
}
// Get repeat rate as a two element array.
// [0] repeat rate in characters per second
// [1] amount of time that a key must be depressed before it will start
// to repeat (in milliseconds)
func (dev *InputDevice) GetRepeatRate() *[2]uint {
repeat_delay := new([2]uint)
ioctl(dev.File.Fd(), uintptr(EVIOCGREP), unsafe.Pointer(repeat_delay))
return repeat_delay
}
// Set repeat rate and delay.
func (dev *InputDevice) SetRepeatRate(repeat, delay uint) {
repeat_delay := new([2]uint)
repeat_delay[0], repeat_delay[1] = repeat, delay
ioctl(dev.File.Fd(), uintptr(EVIOCSREP), unsafe.Pointer(repeat_delay))
}
type CapabilityType struct {
Type int
Name string
}
type CapabilityCode struct {
Code int
Name string
}
type AbsInfo struct {
value int32
minimum int32
maximum int32
fuzz int32
flat int32
resolution int32
}
// Corresponds to the input_id struct.
type device_info struct {
bustype, vendor, product, version uint16
}
// Return the keys of a map as a slice (dict.keys())
func keys(cap *map[int][]int) []int {
slice := make([]int, 0)
for key := range *cap {
slice = append(slice, key)
}
return slice
}
// Determine if a path exist and is a character input device.
func IsInputDevice(path string) bool {
fi, err := os.Stat(path)
if os.IsNotExist(err) {
return false
}
m := fi.Mode()
if m&os.ModeCharDevice == 0 {
return false
}
return true
}
// Return a list of accessible input device names matched by
// deviceglob (default '/dev/input/event*').
func ListInputDevicePaths(device_glob string) ([]string, error) {
paths, err := filepath.Glob(device_glob)
if err != nil {
return nil, err
}
devices := make([]string, 0)
for _, path := range paths {
if IsInputDevice(path) {
devices = append(devices, path)
}
}
return devices, nil
}
// Return a list of accessible input devices matched by deviceglob
// (default '/dev/input/event/*').
func ListInputDevices(device_glob_arg ...string) ([]*InputDevice, error) {
device_glob := "/dev/input/event*"
if len(device_glob_arg) > 0 {
device_glob = device_glob_arg[0]
}
fns, _ := ListInputDevicePaths(device_glob)
devices := make([]*InputDevice, 0)
for i := range fns {
dev, err := Open(fns[i])
if err == nil {
devices = append(devices, dev)
}
}
return devices, nil
}
func bytes_to_string(b *[MAX_NAME_SIZE]byte) string {
idx := bytes.IndexByte(b[:], 0)
return string(b[:idx])
}