add input.Buffered and input.Keyboard and a binary to try combo
parent
c4e1c525d1
commit
bfdab392a0
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import "os"
|
||||||
|
|
||||||
type Input interface {
|
type Input interface {
|
||||||
Read() []Button
|
Read() []Button
|
||||||
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() Input {
|
func New() Input {
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,6 @@ import "testing"
|
||||||
|
|
||||||
func TestInput(t *testing.T) {
|
func TestInput(t *testing.T) {
|
||||||
var _ Input = &Random{}
|
var _ Input = &Random{}
|
||||||
|
var _ Input = Keyboard{}
|
||||||
|
var _ Input = &Buffered{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,9 @@ func NewRandom(generator func() byte) *Random {
|
||||||
return &Random{generator: generator}
|
return &Random{generator: generator}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Random) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Random) Read() []Button {
|
func (r *Random) Read() []Button {
|
||||||
if len(r.down) > 0 && rand.Int()%2 == 0 {
|
if len(r.down) > 0 && rand.Int()%2 == 0 {
|
||||||
was := r.down
|
was := r.down
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue