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

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

View File

@@ -0,0 +1,57 @@
package raw
import (
"io"
"log"
"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() []byte {
b := make([]byte, 5)
n, err := os.Stdin.Read(b)
if err != nil && err != io.EOF {
panic(err)
}
if os.Getenv("DEBUG") == "true" {
log.Printf("raw.Keyboard.Read() %s", b[:n])
}
return b[:n]
}

View File

@@ -0,0 +1,92 @@
package raw
import (
"bytes"
"fmt"
"io"
"math/rand"
"os"
"sort"
"time"
"github.com/go-yaml/yaml"
)
type Random struct {
generator func() byte
down []byte
}
func NewRandom(generator func() byte) *Random {
rand.Seed(time.Now().UnixNano())
return &Random{generator: generator}
}
func (r *Random) Close() {
}
func (r *Random) Read() []byte {
return []byte{r.generator()}
}
func randomCharFromRange(start, stop byte) func() byte {
return func() byte {
return start + byte(rand.Int()%int(1+stop-start))
}
}
func randomCharFromWeightFile(p string) func() byte {
b, err := os.ReadFile(p)
if err != nil {
panic(err)
}
return randomCharFromWeightReader(bytes.NewReader(b))
}
func randomCharFromWeightReader(r io.Reader) func() byte {
var m map[string]int
if err := yaml.NewDecoder(r).Decode(&m); err != nil {
panic(err)
}
byted := map[byte]int{}
for k, v := range m {
if len(k) != 1 {
panic(fmt.Sprintf("keys must be 1 character but got %q", k))
}
byted[k[0]] = v
}
return randomCharFromWeights(byted)
}
func randomCharFromWeights(m map[byte]int) func() byte {
type pair struct {
b byte
i int
}
result := make([]pair, 0, len(m))
sum := 0
for k, v := range m {
result = append(result, pair{b: k, i: v})
if v < 0 {
panic("each weight must each be natural")
}
sum += v
}
sort.Slice(result, func(i, j int) bool {
return result[i].i < result[j].i
})
if sum <= 0 {
panic("weights must total nonzero")
}
return func() byte {
r := rand.Int()
n := r % (sum + 1)
for _, v := range result {
n -= v.i
if n <= 0 {
return v.b
}
}
panic("how")
}
}

View File

@@ -0,0 +1,45 @@
package raw
import (
"strings"
"testing"
)
func TestRandomCharFromWeights(t *testing.T) {
weights := map[byte]int{
'a': 1,
'b': 20,
}
foo := randomCharFromWeights(weights)
for {
got := foo()
if got == 'a' {
break
}
}
}
func TestRandomCharFromWeightsReader(t *testing.T) {
t.Run("json", func(t *testing.T) {
b := `{"a": 1}`
r := strings.NewReader(b)
foo := randomCharFromWeightReader(r)
for i := 0; i < 10; i++ {
if got := foo(); got != 'a' {
t.Errorf("%c", got)
}
}
})
t.Run("yaml", func(t *testing.T) {
b := `
a: 2000
`
r := strings.NewReader(b)
foo := randomCharFromWeightReader(r)
for i := 0; i < 10; i++ {
if got := foo(); got != 'a' {
t.Errorf("%c", got)
}
}
})
}

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

@@ -0,0 +1,39 @@
package raw
import (
"log"
"net"
"os"
"strconv"
)
type UDP struct {
conn net.PacketConn
}
func NewUDP(port int) UDP {
conn, err := net.ListenPacket("udp", ":"+strconv.Itoa(port))
if err != nil {
panic(err)
}
return UDP{
conn: conn,
}
}
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")
buff := make([]byte, 1024)
n, _, err := udp.conn.ReadFrom(buff)
if err != nil {
panic(err)
}
if os.Getenv("DEBUG") == "true" {
log.Printf("raw.UDP.Read() => %s", buff[:n])
}
return buff[:n]
}
func (udp UDP) Close() {
udp.conn.Close()
}