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 }