/* 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 syscall "golang.org/x/sys/unix" import "fmt" import "path/filepath" import "time" import "runtime" // Device models an input device type Device struct { *os.File } const Directory = "/dev/input" func (d * Device) KeepAlive() { runtime.KeepAlive(d.File) } func Open(name string) (*Device, error) { f, err := os.OpenFile(filepath.Join(Directory, name), syscall.O_ASYNC|syscall.O_NONBLOCK|os.O_RDWR, 0666) if err != nil { return nil, err } if err = syscall.SetNonblock(int(f.Fd()), true); err != nil { return nil, err } return &Device{File: f}, nil } 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 { fmt.Printf("ioctl: %d %d %d\n", uintptr(d.Fd()), uintptr(code), uintptr(pointer)) _, _, errno := syscall.Syscall( syscall.SYS_IOCTL, uintptr(d.Fd()), uintptr(code), uintptr(pointer)) if (errno != 0) { return errno } d.KeepAlive() 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. 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 } fmt.Printf("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) } // Read performs a read syscall on the given device func (d * Device) Read(pointer unsafe.Pointer, size uintptr) (uintptr, error) { fmt.Printf("read: %d %d %d\n", uintptr(d.Fd()), uintptr(pointer), uintptr(size)) read, _, errno := syscall.Syscall( syscall.SYS_READ, uintptr(d.Fd()), uintptr(pointer), uintptr(size)) d.KeepAlive() if (errno != 0) { return read, errno } return read, nil } // Write performs a write syscall on the given device func (d * Device) Write(pointer unsafe.Pointer, size uintptr) (uintptr, error) { fmt.Printf("write: %d %d %d\n", uintptr(d.Fd()), uintptr(pointer), uintptr(size)) wrote, _, errno := syscall.Syscall( syscall.SYS_WRITE, uintptr(d.Fd()), uintptr(pointer), uintptr(size)) d.KeepAlive() if (errno != 0) { return wrote, errno } return wrote, nil } 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 } 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{} fmt.Printf("Supported Absolute axes:\n"); for yalv := uint(0); yalv < linux.ABS_MAX; yalv++ { if TestBit(abs_b[0:len(abs_b)], uint(yalv)) { fmt.Printf(" 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 } fmt.Printf("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) RelativeAxes() ([]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 } fmt.Printf("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 } /* printf("Supported events:\n"); for (i = 0; i < EV_MAX; i++) if (TestBit(i, bit[0])) { } printf("Testing ... (interrupt to exit)\n"); while (1) { rd = read(fd, ev, sizeof(struct input_event) * 64); if (rd < (int) sizeof(struct input_event)) { printf("yyy\n"); perror("\nevtest: error reading"); return 1; } for (i = 0; i < rd / sizeof(struct input_event); i++) if (ev[i].type == EV_SYN) { printf("Event: time %ld.%06ld, -------------- %s ------------\n", ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].code ? "Config Sync" : "Report Sync" ); } else if (ev[i].type == EV_MSC && (ev[i].code == MSC_RAW || ev[i].code == MSC_SCAN)) { printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %02x\n", ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].type, events[ev[i].type] ? events[ev[i].type] : "?", ev[i].code, names[ev[i].type] ? (names[ev[i].type][ev[i].code] ? names[ev[i].type][ev[i].code] : "?") : "?", ev[i].value); } else { printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %d\n", ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].type, events[ev[i].type] ? events[ev[i].type] : "?", ev[i].code, names[ev[i].type] ? (names[ev[i].type][ev[i].code] ? names[ev[i].type][ev[i].code] : "?") : "?", ev[i].value); } } } */