Files
cabletest/input.go
T

126 lines
2.9 KiB
Go

package main
import (
"encoding/binary"
"fmt"
"os"
"sync"
"golang.org/x/sys/unix"
"g.fc.run/theater/cabletest/internal/evdev"
)
// Current finger position and whether it is down. Held as state rather than
// delivered as events, so a dropped release can never leave a hold armed.
type touchState struct {
mu sync.Mutex
x, y int
down bool
}
func (t *touchState) set(x, y int, down bool) {
t.mu.Lock()
t.x, t.y, t.down = x, y, down
t.mu.Unlock()
}
func (t *touchState) get() (x, y int, down bool) {
t.mu.Lock()
defer t.mu.Unlock()
return t.x, t.y, t.down
}
// Tracks the finger position, scaled to the given screen size.
func watchTouch(w, h int) (*touchState, error) {
ts, err := evdev.FindTouchscreen(os.O_RDONLY)
if err != nil {
return nil, err
}
state := &touchState{}
go func() {
defer holdPanic()
defer ts.F.Close()
buf := make([]byte, evdev.EventSize*32)
var rawX, rawY int32
var down bool
for {
n, err := ts.F.Read(buf)
// Nothing else feeds this state, so returning here would freeze the
// last touch in place and leave the reset button dead while the panel
// goes on looking alive.
if err != nil {
panic(fmt.Sprintf("reading touchscreen events: %v", err))
}
for o := 0; o+evdev.EventSize <= n; o += evdev.EventSize {
typ := binary.NativeEndian.Uint16(buf[o+16:])
code := binary.NativeEndian.Uint16(buf[o+18:])
val := int32(binary.NativeEndian.Uint32(buf[o+20:]))
switch typ {
case evdev.Abs:
switch code {
case evdev.AbsX:
rawX = val
case evdev.AbsY:
rawY = val
}
case evdev.Key:
if code == evdev.BtnTouch {
down = val != 0
}
case evdev.Syn:
if code != evdev.SynReport {
break
}
// Scaled onto [0, w-1], so full deflection is the last pixel
// rather than one past it.
state.set(
int(int64(rawX-ts.MinX)*int64(w-1)/int64(ts.MaxX-ts.MinX)),
int(int64(rawY-ts.MinY)*int64(h-1)/int64(ts.MaxY-ts.MinY)),
down)
}
}
}
}()
return state, nil
}
// Delivers each key as it is pressed. Puts the terminal in non-canonical mode
// so keypresses arrive without waiting for a newline, and returns a function
// that restores the original settings.
func watchKeys() (<-chan byte, func()) {
ch := make(chan byte, 8)
restore := func() {}
fd := int(os.Stdin.Fd())
if orig, err := unix.IoctlGetTermios(fd, unix.TCGETS); err == nil {
saved := *orig
raw := *orig
raw.Lflag &^= unix.ICANON | unix.ECHO
raw.Cc[unix.VMIN] = 1
raw.Cc[unix.VTIME] = 0
if unix.IoctlSetTermios(fd, unix.TCSETS, &raw) == nil {
restore = func() { unix.IoctlSetTermios(fd, unix.TCSETS, &saved) }
}
}
go func() {
buf := make([]byte, 64)
for {
n, err := os.Stdin.Read(buf)
if err != nil {
return
}
for _, b := range buf[:n] {
select {
case ch <- b:
default:
}
}
}
}()
return ch, restore
}