add input.Buffered and input.Keyboard and a binary to try combo

master
Bel LaPointe 2023-03-02 15:11:57 -07:00
parent c4e1c525d1
commit bfdab392a0
6 changed files with 168 additions and 0 deletions

View File

@ -0,0 +1,24 @@
package main
import (
"log"
"mayhem-party/src/device/input"
)
func main() {
k := input.NewBuffered(input.NewKeyboard())
defer func() {
recover()
k.Close()
}()
log.Printf("try the keyboard")
n := 5
for n > 0 {
result := k.Read()
n -= len(result)
for j := range result {
log.Printf("[%d][%d] %c|%v", n, j, result[j].Char, result[j].Down)
}
}
}

View File

@ -0,0 +1,80 @@
package input
import (
"context"
"sync"
"time"
)
type Buffered struct {
ctx context.Context
can context.CancelFunc
lock sync.Mutex
keys map[byte]int64
input Input
listenInterval time.Duration
expirationInterval time.Duration
}
func NewBuffered(input Input) *Buffered {
ctx, can := context.WithCancel(context.Background())
result := &Buffered{
input: input,
ctx: ctx,
can: can,
lock: sync.Mutex{},
keys: map[byte]int64{},
listenInterval: time.Millisecond * 20,
expirationInterval: time.Millisecond * 100,
}
go result.listen()
return result
}
func (b *Buffered) listen() {
for b.ctx.Err() == nil {
buttons := b.input.Read()
b.lock.Lock()
for i := range buttons {
if buttons[i].Down {
b.keys[buttons[i].Char] = time.Now().UnixNano()
}
}
b.lock.Unlock()
select {
case <-b.ctx.Done():
case <-time.After(b.listenInterval):
}
}
}
func (b *Buffered) Close() {
b.input.Close()
b.can()
}
func (b *Buffered) Read() []Button {
b.lock.Lock()
defer b.lock.Unlock()
result := make([]Button, 0, len(b.keys))
for k, v := range b.keys {
isFresh := v > 0
isStale := v < 0 && time.Since(time.Unix(0, -1*v)) > b.expirationInterval
if isFresh || isStale {
result = append(result, Button{Char: k, Down: isFresh})
}
if isFresh {
b.keys[k] = -1 * v
}
}
for i := range result {
if !result[i].Down {
delete(b.keys, result[i].Char)
}
}
return result
}

View File

@ -4,6 +4,7 @@ import "os"
type Input interface {
Read() []Button
Close()
}
func New() Input {

View File

@ -4,4 +4,6 @@ import "testing"
func TestInput(t *testing.T) {
var _ Input = &Random{}
var _ Input = Keyboard{}
var _ Input = &Buffered{}
}

View File

@ -0,0 +1,58 @@
package input
import (
"io"
"os"
"os/exec"
"runtime"
)
type Keyboard struct {
}
func NewKeyboard() Keyboard {
switch runtime.GOOS {
case "linux":
if err := exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run(); err != nil {
panic(err)
} else if err := exec.Command("stty", "-F", "/dev/tty", "-echo").Run(); err != nil {
panic(err)
}
case "darwin":
if err := exec.Command("stty", "-f", "/dev/tty", "cbreak", "min", "1").Run(); err != nil {
panic(err)
} else if err := exec.Command("stty", "-f", "/dev/tty", "-echo").Run(); err != nil {
panic(err)
}
default:
panic(runtime.GOOS)
}
return Keyboard{}
}
func (kb Keyboard) Close() {
switch runtime.GOOS {
case "linux":
if err := exec.Command("stty", "-F", "/dev/tty", "echo").Run(); err != nil {
panic(err)
}
case "darwin":
if err := exec.Command("stty", "-f", "/dev/tty", "echo").Run(); err != nil {
panic(err)
}
}
}
func (kb Keyboard) Read() []Button {
b := make([]byte, 5)
n, err := os.Stdin.Read(b)
if err != nil && err != io.EOF {
panic(err)
}
result := make([]Button, n)
for i := range result {
result[i] = Button{Char: b[i], Down: true}
}
return result
}

View File

@ -26,6 +26,9 @@ func NewRandom(generator func() byte) *Random {
return &Random{generator: generator}
}
func (r *Random) Close() {
}
func (r *Random) Read() []Button {
if len(r.down) > 0 && rand.Int()%2 == 0 {
was := r.down