123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- /* package input is a thing Go wrapper around the Linux kernel input event
- * system. */
- package input
- import "gitlab.com/beoran/galago/os/linux"
- import "strings"
- import "os"
- import "unsafe"
- import "golang.org/x/sys/unix"
- import "fmt"
- import "path/filepath"
- import "time"
- import "sync"
- import "runtime"
- import "syscall"
- // Device models an input device
- type Device struct {
- FileName string
- fd int
- // Cached device information
- Info struct {
- Events []SupportedEvent
- Keys []SupportedKey
- Axes []AbsoluteAxis
- Rolls []RelativeAxis
- Name string
- ID string
- }
- }
- const Directory = "/dev/input"
- var DoLogDebug = false
- func LogDebug(format string, args...interface{}) {
- if !DoLogDebug {
- return
- }
- LogDebug(format, args...)
- }
- func (d * Device) KeepAlive() {
- runtime.KeepAlive(d)
- }
- func (d * Device) String() string {
- return fmt.Sprintf("%s: %s (%s):%v,%v,%v,%v",
- d.FileName, d.Info.Name, d.Info.ID,
- d.Info.Events, d.Info.Axes, d.Info.Keys,
- d.Info.Rolls)
- }
- func (d *Device) SetNonblock(nonblocking bool) error {
- err := unix.SetNonblock(int(d.fd), nonblocking)
- return err
- }
- func Open(name string) (*Device, error) {
- path := filepath.Join(Directory, name)
- fd, err := unix.Open(path, unix.O_ASYNC|unix.O_NONBLOCK|unix.O_RDWR, 0666)
- if err != nil {
- return nil, err
- }
-
- device := &Device{FileName: name, fd: fd}
-
- if err = device.SetNonblock(true) ; err != nil {
- return nil, err
- }
-
- runtime.SetFinalizer(device, (*Device).Close)
-
- return device, nil
- }
- func (d *Device) GatherInfo() error {
- var err error
- if d.Info.Events, err = d.SupportedEvents(); err != nil {
- return err
- }
- if d.Info.Keys, err = d.SupportedKeys(); err != nil {
- return err
- }
- if d.Info.Axes, err = d.SupportedAxes(); err != nil {
- return err
- }
- if d.Info.Rolls, err = d.SupportedRelatives(); err != nil {
- return err
- }
- if id, err := d.Id(); err != nil {
- return err
- } else {
- d.Info.ID = fmt.Sprintf("%v", id)
- }
- if d.Info.Name, err = d.Name(); err != nil {
- return err
- }
- return nil
- }
- func OpenAndGatherInfo(name string) (*Device, error) {
- dev, err := Open(name)
- if err != nil {
- return dev, err
- }
- err = dev.GatherInfo()
- return dev, err
- }
- func List() ([]string, error) {
- dir, err := os.Open(Directory)
- if err != nil {
- return nil, err
- }
-
- names, err := dir.Readdirnames(-1)
- if err != nil {
- return names, err
- }
- results := names[:0]
- for _, name := range names {
- if strings.HasPrefix(name, "event") {
- results = append(results, name)
- }
- }
- return results, nil
- }
- // Icotl performs an ioctl on the given device
- func (d * Device) Ioctl(code uint32, pointer unsafe.Pointer) error {
- var errno syscall.Errno = 0
- LogDebug("ioctl: %d %d %d\n", uintptr(d.fd), uintptr(code), uintptr(pointer))
- _, _, errno = unix.Syscall(
- unix.SYS_IOCTL,
- uintptr(d.fd),
- uintptr(code),
- uintptr(pointer))
- if errno != 0 {
- return errno
- }
- return nil
- }
- // Read performs a read unix on the given device. The read is noblocking
- // and will retrn 0 if EAGEIN was raised.
- func (d * Device) Read(pointer unsafe.Pointer, size uintptr) (uintptr, error) {
- if d.IsClosed() {
- return 0, syscall.EINVAL
- }
- var errno syscall.Errno = 0
- var read uintptr = 0
- LogDebug("read: %d %d %d\n", uintptr(d.fd), uintptr(pointer), uintptr(size))
- read, _, errno = unix.Syscall(
- unix.SYS_READ,
- uintptr(d.fd),
- uintptr(pointer),
- uintptr(size))
- if errno == 0 {
- return read, nil
- }
- if errno == syscall.EAGAIN {
- return 0, nil
- }
- return read, errno
- }
- // Read performs a read unix on the given device. The read is blocking.
- func (d * Device) ReadBlock(pointer unsafe.Pointer, size uintptr) (uintptr, error) {
- read, err := d.Read(pointer, size)
- for read < 1 {
- if err != nil {
- return 0, err
- }
- read, err = d.Read(pointer, size)
- }
- return read, err
- }
- // Write performs a write unix on the given device. The write is blocking.
- func (d * Device) Write(pointer unsafe.Pointer, size uintptr) (uintptr, error) {
- if d.IsClosed() {
- return 0, syscall.EINVAL
- }
-
- var errno syscall.Errno = syscall.EAGAIN
- var wrote uintptr = 0
- for errno == syscall.EAGAIN {
- LogDebug("write: %d %d %d\n", uintptr(d.fd), uintptr(pointer), uintptr(size))
- wrote, _, errno = unix.Syscall(
- unix.SYS_WRITE,
- uintptr(d.fd),
- uintptr(pointer),
- uintptr(size))
- }
- if errno == 0 {
- return wrote, nil
- }
- return wrote, errno
- }
- func (d * Device) IsClosed() bool {
- return d.fd < 0
- }
- // Close closes the device
- func (d * Device) Close() error {
- if d.IsClosed() {
- return nil
- }
- err := unix.Close(d.fd)
- if err != nil {
- return err
- }
- return nil
- }
- func (d * Device) DriverVersion() (int32, error) {
- res := int32(0)
- data := unsafe.Pointer(&res)
- err := d.Ioctl(linux.EVIOCGVERSION, data)
- return res, err
- }
- const NAME_MAX = 256
- func (d * Device) Name() (string, error) {
- buffer := [NAME_MAX]byte{}
- err := d.Ioctl(linux.EVIOCGNAME(uintptr(len(buffer))), unsafe.Pointer(&buffer))
- return string(buffer[0:len(buffer)]), err
- }
- func (d * Device) Id() (linux.INPUT_id, error) {
- var result linux.INPUT_id
- err := d.Ioctl(linux.EVIOCGID, unsafe.Pointer(&result))
- return result, err
- }
- // The Linux developers thought it was a great idea a to use an array of
- // unsigned longs for the bit flags of input devices. Of course, the length
- // of an unsigned long is platform dependent which then entails all sorts of
- // gymnastics to extract the bits from the array.
- // To avoid this, we use a byte array instead, which might lessen performance,
- // but is much easier to use.
- // This is a unsafe.Sizeof trick, which I hope will keep working.
- const SIZEOF_LONG = uint(unsafe.Sizeof(*((*linux.UnsignedLong)(nil))))
- const BITS_PER_LONG = SIZEOF_LONG * 8
- func BitsToLong(bits uint) uint {
- return ((bits) + (8 * SIZEOF_LONG) - 1) / (8 * SIZEOF_LONG)
- }
- const TOPOLOGY_MAX = 256
- func (d * Device) Topology() (string, error) {
- buffer := [TOPOLOGY_MAX]byte{}
- err := d.Ioctl(linux.EVIOCGPHYS(uintptr(len(buffer))), unsafe.Pointer(&buffer))
- return string(buffer[0:len(buffer)]), err
- }
- func TestBit(array []uint8, bit uint) bool {
- elem := uint(array[ bit / 8 ])
- flag := uint(1) << uint(bit % 8)
- return (elem & flag ) != 0
- }
- type SupportedEvent uint
- func (se SupportedEvent) Name() string {
- return linux.EvToString(uint(se))
- }
- func (se SupportedEvent) String() string {
- return se.Name()
- }
- func (d * Device) SupportedEvents() ([]SupportedEvent, error) {
- var bits [linux.EV_MAX / 8 + 1]uint8
- size := unsafe.Sizeof(bits)
- err := d.Ioctl(linux.EVIOCGBIT(0, uintptr(size)), unsafe.Pointer(&bits));
- if err != nil {
- return nil, err
- }
- LogDebug("size %d, bits: %v\n", size, bits)
- result := []SupportedEvent{}
- for i := uint(0); i < uint(linux.EV_MAX); i++ {
- if (TestBit(bits[0:len(bits)],i)) {
- result = append(result, SupportedEvent(uint(i)))
- }
- }
-
- return result, nil
- }
- // More Go-like names for the low leve kernel api structs
- type InputAbsinfo linux.INPUT_absinfo
- type InputEvent linux.INPUT_event
- type AbsoluteAxis struct {
- Index uint
- InputAbsinfo
- }
- func (ax AbsoluteAxis) Name() string {
- return linux.AbsToString(ax.Index)
- }
- func (ax AbsoluteAxis) String() string {
- return fmt.Sprintf("%s %d (min:%d max:%d flat:%d fuzz:%d)",
- ax.Name(),
- ax.Value,
- ax.Minimum,
- ax.Maximum,
- ax.Flat,
- ax.Fuzz)
- }
- func (ev InputEvent) Time() time.Time {
- return time.Unix(ev.Timeval.Tv_sec, ev.Timeval.Tv_usec)
- }
- func (ev InputEvent) String() string {
- return fmt.Sprintf("Event: Time: %s Type: %s, Code: %d, Value:%d",
- ev.Time().Format("2006-01-02 15:04:05.000000"),
- linux.EvToString(uint(ev.Type)),
- ev.Code,
- ev.Value)
- }
- const READ_EVENTS_MAX = 64
- func (d * Device) ReadEvents() ([]InputEvent, error) {
- var events [READ_EVENTS_MAX]InputEvent
- size := unsafe.Sizeof(events)
- read, err := d.Read(unsafe.Pointer(&events), size)
- if err != nil {
- return nil, err
- }
- len := read / unsafe.Sizeof(events[0])
- return events[0:len], nil
- }
- func (d * Device) ReadEventsBlock() ([]InputEvent, error) {
- var events [READ_EVENTS_MAX]InputEvent
- size := unsafe.Sizeof(events)
- read, err := d.ReadBlock(unsafe.Pointer(&events), size)
- if err != nil {
- return nil, err
- }
- len := read / unsafe.Sizeof(events[0])
- return events[0:len], nil
- }
- const WRITE_EVENTS_MAX = 64
- func (d * Device) WriteEvents(events []InputEvent) (uint, error) {
- size := unsafe.Sizeof(events[0]) * uintptr(len(events))
- wrote, err := d.Write(unsafe.Pointer(&events[0]), size)
- len := uint(wrote / size)
- return len, err
- }
- func (d * Device) SupportedAxes() ([]AbsoluteAxis, error) {
- var abs_feat InputAbsinfo;
- var abs_b [linux.ABS_MAX/8 + 1]uint8;
- size := unsafe.Sizeof(abs_b)
- err := d.Ioctl(linux.EVIOCGBIT(linux.EV_ABS, size), unsafe.Pointer(&abs_b))
- if err != nil {
- return nil, err
- }
- result := []AbsoluteAxis{}
- LogDebug("Supported Absolute axes:\n");
- for yalv := uint(0); yalv < linux.ABS_MAX; yalv++ {
- if TestBit(abs_b[0:len(abs_b)], uint(yalv)) {
- LogDebug(" Absolute axis 0x%02x %s", yalv, linux.AbsToString(yalv))
- err = d.Ioctl(linux.EVIOCGABS(uint32(yalv)), unsafe.Pointer(&abs_feat))
- if err != nil {
- return result, err
- }
- axis := AbsoluteAxis{Index: yalv, InputAbsinfo: abs_feat}
- result = append(result, axis)
- }
- }
- return result, nil
- }
- type SupportedKey uint
- func (se SupportedKey) Name() string {
- return linux.KeyString(uint(se))
- }
- func (se SupportedKey) String() string {
- return se.Name()
- }
- func (d * Device) SupportedKeys() ([]SupportedKey, error) {
- var bits [linux.KEY_MAX / 8 + 1]uint8
- size := unsafe.Sizeof(bits)
- err := d.Ioctl(linux.EVIOCGBIT(linux.EV_KEY, uintptr(size)), unsafe.Pointer(&bits));
- if err != nil {
- return nil, err
- }
- LogDebug("size %d, bits: %v\n", size, bits)
- result := []SupportedKey{}
- for i := uint(0); i < uint(linux.KEY_MAX); i++ {
- if (TestBit(bits[0:len(bits)],i)) {
- result = append(result, SupportedKey(uint(i)))
- }
- }
- return result, nil
- }
- type RelativeAxis uint
- func (se RelativeAxis) Name() string {
- return linux.RelString(uint(se))
- }
- func (se RelativeAxis) String() string {
- return se.Name()
- }
- func (d * Device) SupportedRelatives() ([]RelativeAxis, error) {
- var bits [linux.REL_MAX / 8 + 1]uint8
- size := unsafe.Sizeof(bits)
- err := d.Ioctl(linux.EVIOCGBIT(linux.EV_REL, uintptr(size)), unsafe.Pointer(&bits));
- if err != nil {
- return nil, err
- }
- LogDebug("size %d, bits: %v\n", size, bits)
- result := []RelativeAxis{}
- for i := uint(0); i < uint(linux.REL_MAX); i++ {
- if (TestBit(bits[0:len(bits)],i)) {
- result = append(result, RelativeAxis(uint(i)))
- }
- }
- return result, nil
- }
- // MANAGER_DEVICE_SCAN_DELAY is the delay between device scans for the device driver.
- const MANAGER_DEVICE_SCAN_DELAY = 3 * time.Second
- type Driver struct {
- sync.Mutex
- Connect chan(*Device)
- Disconnect chan(*Device)
- Errors chan(error)
- Events chan(InputEvent)
- Done bool
- Devices []*Device
- DevicesByName map[string] *Device
- DevicesByFd map[int] *Device
- Verbose bool
- fds []unix.PollFd
- }
- func NewDriver() * Driver {
- eveChan := make(chan InputEvent)
- conChan := make(chan *Device)
- disChan := make(chan *Device)
- errChan := make(chan error)
- driver := &Driver{Connect: conChan, Disconnect: disChan, Errors: errChan,
- Events: eveChan,
- Done: false}
- driver.Devices = []*Device{}
- driver.DevicesByName = make(map[string] *Device)
- driver.DevicesByFd = make(map[int] *Device)
- driver.fds = []unix.PollFd{}
- return driver
- }
- func (driver *Driver) connectDevice(device * Device) {
- driver.Lock()
- defer driver.Unlock()
- driver.DevicesByName[device.FileName] = device
- driver.DevicesByFd[int(device.fd)] = device
- driver.Devices = append(driver.Devices, device)
- pollfd := unix.PollFd{Fd: int32(device.fd), Events : unix.POLLIN}
- driver.fds = append(driver.fds, pollfd)
- driver.Connect <- device
- }
- func (driver *Driver) disconnectDevice(device * Device) {
- driver.Lock()
- defer driver.Unlock()
- delete(driver.DevicesByName, device.FileName)
- delete(driver.DevicesByFd, int(device.fd))
- found := false
- var i int
- var pollfd unix.PollFd
- for i, pollfd = range driver.fds {
- if pollfd.Fd == int32(device.fd) {
- found = true
- break
- }
- }
- if found {
- driver.fds = append(driver.fds[:i], driver.fds[(i+1):]...)
- driver.Devices = append(driver.Devices[:i], driver.Devices[(i+1):]...)
- driver.Disconnect <- device
- }
- }
- func (driver * Driver) ManageNewDevicesOnce() {
- fmt.Println("ManageNewDevicesOnce")
- names, err := List()
- if err != nil {
- driver.Errors <- err
- } else {
- for _, name := range names {
- if _, found := driver.DevicesByName[name] ; !found {
- device, err := OpenAndGatherInfo(name)
- if err == nil {
- driver.connectDevice(device)
- } else if driver.Verbose {
- driver.Errors <- err
- }
- }
- }
- }
- }
- func (driver *Driver) ReadEventsOnce(device * Device) {
- events, err := device.ReadEvents()
- if err != nil {
- driver.Errors <- err
- driver.disconnectDevice(device)
- } else {
- for _, event := range events {
- driver.Events <- event
- }
- }
- }
- func (driver *Driver) ManageInputOnce() {
- res, err := unix.Poll(driver.fds, 0)
- if err != nil {
- driver.Errors <- err
- }
- if res > 0 {
- for _, fd := range driver.fds {
- if (fd.Revents & unix.POLLIN) != 0 {
- device, ok := driver.DevicesByFd[int(fd.Fd)]
- if ok && device != nil {
- driver.ReadEventsOnce(device)
- }
- }
- if (fd.Revents & unix.POLLHUP) != 0 {
- device, ok := driver.DevicesByFd[int(fd.Fd)]
- if ok && device != nil {
- driver.disconnectDevice(device)
- }
- }
- }
- }
- }
- func (driver *Driver) ManageDevices() {
- for !driver.Done {
- driver.ManageNewDevicesOnce()
- time.Sleep(MANAGER_DEVICE_SCAN_DELAY)
- }
- }
- func (driver *Driver) ManageInput() {
- for !driver.Done {
- driver.ManageInputOnce()
- }
- }
- func (driver *Driver) Start() {
- go driver.ManageDevices()
- go driver.ManageInput()
- }
- func (driver *Driver) Stop() {
- driver.Lock() ; defer driver.Unlock()
- driver.Done = true
- }
|