Files

92 lines
2.1 KiB
Go

// Package evdev is the kernel input-event ABI.
package evdev
import (
"fmt"
"os"
"path/filepath"
"unsafe"
"golang.org/x/sys/unix"
)
const (
Syn = 0x00
Key = 0x01
Abs = 0x03
SynReport = 0x00
BtnTouch = 0x14a
AbsX = 0x00
AbsY = 0x01
propDirect = 0x01
EventSize = 24
)
func ioc(nr, size uintptr) uintptr {
const iocRead = 2
return iocRead<<30 | size<<16 | 'E'<<8 | nr
}
type Touchscreen struct {
F *os.File
MinX, MaxX, MinY, MaxY int32
}
// Picks the direct-input device that reports absolute X and Y, which is the
// touchscreen; a mouse is relative and a keyboard has no absolute axes.
func FindTouchscreen(flag int) (*Touchscreen, error) {
paths, err := filepath.Glob("/dev/input/event*")
if err != nil {
return nil, err
}
for _, p := range paths {
f, err := os.OpenFile(p, flag, 0)
if err != nil {
continue
}
var props, absBits uint64
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(),
ioc(0x09, 8), uintptr(unsafe.Pointer(&props))); errno != 0 {
f.Close()
continue
}
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(),
ioc(0x20+Abs, 8), uintptr(unsafe.Pointer(&absBits))); errno != 0 {
f.Close()
continue
}
if props&(1<<propDirect) == 0 ||
absBits&(1<<AbsX) == 0 || absBits&(1<<AbsY) == 0 {
f.Close()
continue
}
t := &Touchscreen{F: f}
if t.MinX, t.MaxX, err = axisRange(f, AbsX); err != nil {
f.Close()
return nil, fmt.Errorf("%s x range: %w", p, err)
}
if t.MinY, t.MaxY, err = axisRange(f, AbsY); err != nil {
f.Close()
return nil, fmt.Errorf("%s y range: %w", p, err)
}
if t.MaxX <= t.MinX || t.MaxY <= t.MinY {
f.Close()
return nil, fmt.Errorf("%s: degenerate axes x %d..%d y %d..%d",
p, t.MinX, t.MaxX, t.MinY, t.MaxY)
}
return t, nil
}
return nil, fmt.Errorf("no touchscreen found among /dev/input/event*")
}
func axisRange(f *os.File, axis uintptr) (min, max int32, err error) {
var info [6]int32
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(),
ioc(0x40+axis, 24), uintptr(unsafe.Pointer(&info[0]))); errno != 0 {
return 0, 0, errno
}
return info[1], info[2], nil
}