mayhem-party/src/device/input/wrap/buffered.go

113 lines
2.2 KiB
Go

package wrap
import (
"context"
"mayhem-party/src/device/input/button"
"mayhem-party/src/device/input/raw"
"os"
"sync"
"time"
)
var (
FlagBufferedStickyDuration = os.Getenv("WRAP_BUFFERED_STICKY_DURATION")
)
type Buffered struct {
ctx context.Context
can context.CancelFunc
lock sync.Mutex
keys map[byte]int64
input Wrap
listenInterval time.Duration
expirationInterval time.Duration
}
func NewBuffered(ctx context.Context, input Wrap) *Buffered {
ctx, can := context.WithCancel(ctx)
expirationInterval := time.Millisecond * 125
if d, err := time.ParseDuration(FlagBufferedStickyDuration); err == nil {
expirationInterval = d
}
result := &Buffered{
input: input,
ctx: ctx,
can: can,
lock: sync.Mutex{},
keys: map[byte]int64{},
listenInterval: time.Millisecond * 10,
expirationInterval: expirationInterval,
}
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()
} else {
b.keys[buttons[i].Char] = 0
}
}
b.lock.Unlock()
select {
case <-b.ctx.Done():
case <-time.After(b.listenInterval):
}
}
}
func (b *Buffered) CloseWrap() raw.Raw {
b.can()
return b.input.CloseWrap()
}
func (b *Buffered) Close() {
b.input.Close()
b.can()
}
func (b *Buffered) Read() []button.Button {
for b.ctx.Err() == nil {
result := b.read()
if len(result) > 0 {
return result
}
select {
case <-b.ctx.Done():
case <-time.After(b.listenInterval):
}
}
return []button.Button{}
}
func (b *Buffered) read() []button.Button {
b.lock.Lock()
defer b.lock.Unlock()
result := make([]button.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.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
}