split src/devices/input into src/devices/input/{raw,wrap}

master
bel 2023-03-24 19:51:38 -06:00
parent ab673a81f0
commit 38b00e55b0
16 changed files with 161 additions and 159 deletions

View File

@ -2,63 +2,16 @@ package input
import ( import (
"context" "context"
"os" "mayhem-party/src/device/input/raw"
"strconv" "mayhem-party/src/device/input/wrap"
"syscall"
) )
type Input interface { type Input interface {
Read() []Button Read() []wrap.Button
Close() Close()
} }
func New(ctx context.Context) Input { func New(ctx context.Context) Input {
return newNew(ctx)() src := raw.New(ctx)
} return wrap.New(ctx, src)
func newNew(ctx context.Context) func() Input {
maker := newSourceFunc()
if os.Getenv("INPUT_BUFFERED") == "true" {
oldMaker := maker
maker = func() Input {
return NewBuffered(ctx, oldMaker())
}
}
if p := os.Getenv("INPUT_REMAP_FILE"); p != "" {
oldMaker := maker
maker = func() Input {
return NewRemapFromFile(oldMaker(), p)
}
}
if os.Getenv("INPUT_REFRESH_ON_SIGUSR1") != "" {
oldMaker := maker
c := NewRefreshCh(syscall.SIGUSR1)
maker = func() Input {
return NewRefresh(oldMaker, c)
}
}
return maker
}
func newSourceFunc() func() Input {
if os.Getenv("INPUT_KEYBOARD") == "true" {
singletonKeyboard := NewKeyboard()
return func() Input {
return singletonKeyboard
}
}
if port, _ := strconv.Atoi(os.Getenv("INPUT_UDP")); port != 0 {
singletonUDP := NewUDP(port)
return func() Input {
return singletonUDP
}
}
return func() Input {
generator := randomCharFromRange('a', 'g')
if p, ok := os.LookupEnv("INPUT_RANDOM_WEIGHT_FILE"); ok && len(p) > 0 {
generator = randomCharFromWeightFile(p)
}
return NewRandom(generator)
}
} }

View File

@ -30,11 +30,11 @@ func TestNewRemapped(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
os.Setenv("INPUT_REMAP_FILE", remap) os.Setenv("WRAP_REMAP_FILE", remap)
os.Setenv("INPUT_RANDOM_WEIGHT_FILE", rand) os.Setenv("RAW_RANDOM_WEIGHT_FILE", rand)
t.Cleanup(func() { t.Cleanup(func() {
os.Unsetenv("INPUT_REMAP_FILE") os.Unsetenv("WRAP_REMAP_FILE")
os.Unsetenv("INPUT_RANDOM_WEIGHT_FILE") os.Unsetenv("RAW_RANDOM_WEIGHT_FILE")
}) })
r := input.New(context.Background()) r := input.New(context.Background())
@ -50,9 +50,9 @@ func TestNewRemapped(t *testing.T) {
} }
func TestNewBuffered(t *testing.T) { func TestNewBuffered(t *testing.T) {
os.Setenv("INPUT_BUFFERED", "true") os.Setenv("WRAP_BUFFERED", "true")
t.Cleanup(func() { t.Cleanup(func() {
os.Unsetenv("INPUT_BUFFERED") os.Unsetenv("WRAP_BUFFERED")
}) })
r := input.New(context.Background()) r := input.New(context.Background())
@ -71,9 +71,9 @@ func TestNewRandomWeightFile(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
os.Setenv("INPUT_RANDOM_WEIGHT_FILE", p) os.Setenv("RAW_RANDOM_WEIGHT_FILE", p)
t.Cleanup(func() { t.Cleanup(func() {
os.Unsetenv("INPUT_RANDOM_WEIGHT_FILE") os.Unsetenv("RAW_RANDOM_WEIGHT_FILE")
}) })
r := input.New(context.Background()) r := input.New(context.Background())

View File

@ -1,33 +0,0 @@
package input
import (
"context"
"os"
"testing"
)
func TestInput(t *testing.T) {
var _ Input = &Random{}
var _ Input = Keyboard{}
var _ Input = &Buffered{}
var _ Input = Remap{}
var _ Input = &Refresh{}
var _ Input = UDP{}
}
func TestNewNew(t *testing.T) {
t.Run("refreshing", func(t *testing.T) {
ctx, can := context.WithCancel(context.Background())
defer can()
os.Setenv("INPUT_BUFFERED", "true")
os.Setenv("INPUT_REFRESH_ON_SIGUSR1", "true")
foo := New(ctx)
if refresh, ok := foo.(*Refresh); !ok {
t.Errorf("%T", foo)
} else if buffered, ok := refresh.input.(*Buffered); !ok {
t.Errorf("%T", refresh.input)
} else if _, ok := buffered.input.(*Random); !ok {
t.Errorf("%T", buffered.input)
}
})
}

View File

@ -1,4 +1,4 @@
package input package raw
import ( import (
"io" "io"
@ -44,25 +44,14 @@ func (kb Keyboard) Close() {
} }
} }
func (kb Keyboard) Read() []Button { func (kb Keyboard) Read() []byte {
b := make([]byte, 5) b := make([]byte, 5)
n, err := os.Stdin.Read(b) n, err := os.Stdin.Read(b)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
panic(err) panic(err)
} }
down := true
result := make([]Button, 0, n)
for i := 0; i < n; i++ {
if b[i] == '!' {
down = false
} else if b[i] != '\n' {
result = append(result, Button{Char: b[i], Down: down})
down = true
}
}
if os.Getenv("DEBUG") == "true" { if os.Getenv("DEBUG") == "true" {
log.Printf("input.Keyboard.Read() %s => %+v", b[:n], result) log.Printf("raw.Keyboard.Read() %s", b[:n])
} }
return result return b[:n]
} }

View File

@ -1,4 +1,4 @@
package input package raw
import ( import (
"bytes" "bytes"
@ -12,14 +12,9 @@ import (
"github.com/go-yaml/yaml" "github.com/go-yaml/yaml"
) )
type Button struct {
Char byte
Down bool
}
type Random struct { type Random struct {
generator func() byte generator func() byte
down []Button down []byte
} }
func NewRandom(generator func() byte) *Random { func NewRandom(generator func() byte) *Random {
@ -30,19 +25,8 @@ func NewRandom(generator func() byte) *Random {
func (r *Random) Close() { func (r *Random) Close() {
} }
func (r *Random) Read() []Button { func (r *Random) Read() []byte {
if len(r.down) > 0 && rand.Int()%2 == 0 { return []byte{r.generator()}
was := r.down
for i := range was {
was[i].Down = false
}
r.down = r.down[:0]
return was
} else {
c := Button{Char: r.generator(), Down: true}
r.down = append(r.down, c)
return []Button{c}
}
} }
func randomCharFromRange(start, stop byte) func() byte { func randomCharFromRange(start, stop byte) func() byte {

View File

@ -1,4 +1,4 @@
package input package raw
import ( import (
"strings" "strings"

View File

@ -0,0 +1,26 @@
package raw
import (
"context"
"os"
"strconv"
)
type Raw interface {
Read() []byte
Close()
}
func New(ctx context.Context) Raw {
if os.Getenv("RAW_KEYBOARD") == "true" {
return NewKeyboard()
}
if port, _ := strconv.Atoi(os.Getenv("RAW_UDP")); port != 0 {
return NewUDP(port)
}
generator := randomCharFromRange('a', 'g')
if p, ok := os.LookupEnv("RAW_RANDOM_WEIGHT_FILE"); ok && len(p) > 0 {
generator = randomCharFromWeightFile(p)
}
return NewRandom(generator)
}

View File

@ -0,0 +1,9 @@
package raw
import "testing"
func TestRaw(t *testing.T) {
var _ Raw = &Random{}
var _ Raw = UDP{}
var _ Raw = Keyboard{}
}

View File

@ -1,7 +1,9 @@
package input package raw
import ( import (
"log"
"net" "net"
"os"
"strconv" "strconv"
) )
@ -19,26 +21,17 @@ func NewUDP(port int) UDP {
} }
} }
func (udp UDP) Read() []Button { func (udp UDP) Read() []byte {
panic("NEEDS TO BE IN BG THREAD SO WE CAN SHUT DOWN WITHOUT BLOCKING ON READ OR AT LEAST BE RESPONSIVE") panic("NEEDS TO BE IN BG THREAD SO WE CAN SHUT DOWN WITHOUT BLOCKING ON READ OR AT LEAST BE RESPONSIVE")
buff := make([]byte, 1024) buff := make([]byte, 1024)
n, _, err := udp.conn.ReadFrom(buff) n, _, err := udp.conn.ReadFrom(buff)
if err != nil { if err != nil {
panic(err) panic(err)
} }
buttons := make([]Button, 0, n) if os.Getenv("DEBUG") == "true" {
down := true log.Printf("raw.UDP.Read() => %s", buff[:n])
for i := range buff[:n] {
if buff[i] == '!' {
down = false
} else {
if buff[i] != '\n' {
buttons = append(buttons, Button{Char: buff[i], Down: down})
}
down = true
}
} }
return buttons return buff[:n]
} }
func (udp UDP) Close() { func (udp UDP) Close() {

View File

@ -1,4 +1,4 @@
package input package wrap
import ( import (
"context" "context"
@ -12,15 +12,15 @@ type Buffered struct {
can context.CancelFunc can context.CancelFunc
lock sync.Mutex lock sync.Mutex
keys map[byte]int64 keys map[byte]int64
input Input input Wrap
listenInterval time.Duration listenInterval time.Duration
expirationInterval time.Duration expirationInterval time.Duration
} }
func NewBuffered(ctx context.Context, input Input) *Buffered { func NewBuffered(ctx context.Context, input Wrap) *Buffered {
ctx, can := context.WithCancel(ctx) ctx, can := context.WithCancel(ctx)
expirationInterval := time.Millisecond * 125 expirationInterval := time.Millisecond * 125
if d, err := time.ParseDuration(os.Getenv("INPUT_BUFFERED_STICKY_DURATION")); err == nil { if d, err := time.ParseDuration(os.Getenv("WRAP_BUFFERED_STICKY_DURATION")); err == nil {
expirationInterval = d expirationInterval = d
} }
result := &Buffered{ result := &Buffered{

View File

@ -0,0 +1,6 @@
package wrap
type Button struct {
Char byte
Down bool
}

View File

@ -1,4 +1,4 @@
package input package wrap
import ( import (
"context" "context"
@ -9,7 +9,7 @@ import (
type Refresh struct { type Refresh struct {
can context.CancelFunc can context.CancelFunc
input Input input Wrap
} }
func NewRefreshCh(sig os.Signal) <-chan os.Signal { func NewRefreshCh(sig os.Signal) <-chan os.Signal {
@ -18,11 +18,11 @@ func NewRefreshCh(sig os.Signal) <-chan os.Signal {
return c return c
} }
func NewRefresh(newInput func() Input, ch <-chan os.Signal) *Refresh { func NewRefresh(newWrap func() Wrap, ch <-chan os.Signal) *Refresh {
ctx, can := context.WithCancel(context.Background()) ctx, can := context.WithCancel(context.Background())
result := &Refresh{ result := &Refresh{
can: can, can: can,
input: newInput(), input: newWrap(),
} }
go func() { go func() {
defer log.Println("refreshing done") defer log.Println("refreshing done")
@ -32,7 +32,7 @@ func NewRefresh(newInput func() Input, ch <-chan os.Signal) *Refresh {
return return
case sig := <-ch: case sig := <-ch:
log.Println("refreshing for", sig) log.Println("refreshing for", sig)
result.input = newInput() result.input = newWrap()
} }
} }
}() }()

View File

@ -1,6 +1,7 @@
package input package wrap
import ( import (
"mayhem-party/src/device/input/raw"
"os" "os"
"syscall" "syscall"
"testing" "testing"
@ -9,9 +10,9 @@ import (
func TestRefresh(t *testing.T) { func TestRefresh(t *testing.T) {
b := byte('a') b := byte('a')
generator := func() Input { generator := func() Wrap {
b += byte(1) b += byte(1)
return NewRandom(func() byte { return b }) return explicit{src: raw.NewRandom(func() byte { return b })}
} }
ch := make(chan os.Signal, 1) ch := make(chan os.Signal, 1)
defer close(ch) defer close(ch)

View File

@ -1,4 +1,4 @@
package input package wrap
import ( import (
"os" "os"
@ -7,11 +7,11 @@ import (
) )
type Remap struct { type Remap struct {
input Input input Wrap
m map[byte]byte m map[byte]byte
} }
func NewRemapFromFile(input Input, p string) Remap { func NewRemapFromFile(input Wrap, p string) Remap {
b, err := os.ReadFile(p) b, err := os.ReadFile(p)
if err != nil { if err != nil {
panic(err) panic(err)
@ -30,7 +30,7 @@ func NewRemapFromFile(input Input, p string) Remap {
return NewRemap(input, remap) return NewRemap(input, remap)
} }
func NewRemap(input Input, m map[byte]byte) Remap { func NewRemap(input Wrap, m map[byte]byte) Remap {
return Remap{ return Remap{
input: input, input: input,
m: m, m: m,

View File

@ -0,0 +1,64 @@
package wrap
import (
"context"
"mayhem-party/src/device/input/raw"
"os"
"syscall"
)
type Wrap interface {
Read() []Button
Close()
}
func New(ctx context.Context, src raw.Raw) Wrap {
maker := func() Wrap {
return explicit{src: src}
}
if os.Getenv("WRAP_BUFFERED") == "true" {
oldMaker := maker
maker = func() Wrap {
return NewBuffered(ctx, oldMaker())
}
}
if p := os.Getenv("WRAP_REMAP_FILE"); p != "" {
oldMaker := maker
maker = func() Wrap {
return NewRemapFromFile(oldMaker(), p)
}
}
if os.Getenv("WRAP_REFRESH_ON_SIGUSR1") != "" {
oldMaker := maker
c := NewRefreshCh(syscall.SIGUSR1)
maker = func() Wrap {
return NewRefresh(oldMaker, c)
}
}
return maker()
}
type explicit struct {
src raw.Raw
}
func (e explicit) Close() {
e.src.Close()
}
func (e explicit) Read() []Button {
b := e.src.Read()
buttons := make([]Button, 0, len(b))
down := true
for i := range b {
if b[i] == '!' {
down = false
} else {
if b[i] != '\n' {
buttons = append(buttons, Button{Char: b[i], Down: down})
}
down = true
}
}
return buttons
}

View File

@ -0,0 +1,10 @@
package wrap
import "testing"
func TestWrap(t *testing.T) {
var _ Wrap = explicit{}
var _ Wrap = &Refresh{}
var _ Wrap = &Buffered{}
var _ Wrap = &Remap{}
}