113 lines
2.2 KiB
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
|
|
}
|