/* 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 }