Add Stream Deck+ support with encoders, touch strip, and LCD output
This commit is contained in:
@@ -10,13 +10,6 @@ import (
|
|||||||
"qrun/lib/streamdeck"
|
"qrun/lib/streamdeck"
|
||||||
)
|
)
|
||||||
|
|
||||||
var keyLabels = []string{
|
|
||||||
"1", "2", "3", "4", "5", "6", "7", "8",
|
|
||||||
"A", "B", "C", "D", "E", "F", "G", "H",
|
|
||||||
"I", "J", "K", "L", "M", "N", "O", "P",
|
|
||||||
"Q", "R", "S", "T", "U", "V", "W", "X",
|
|
||||||
}
|
|
||||||
|
|
||||||
var palette = []color.RGBA{
|
var palette = []color.RGBA{
|
||||||
{220, 50, 50, 255},
|
{220, 50, 50, 255},
|
||||||
{50, 180, 50, 255},
|
{50, 180, 50, 255},
|
||||||
@@ -28,15 +21,6 @@ var palette = []color.RGBA{
|
|||||||
{100, 100, 200, 255},
|
{100, 100, 200, 255},
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawKey(dev *streamdeck.Device, key int, active bool) {
|
|
||||||
col := key % streamdeck.KeyCols()
|
|
||||||
bg := palette[col]
|
|
||||||
if !active {
|
|
||||||
bg = color.RGBA{bg.R / 3, bg.G / 3, bg.B / 3, 255}
|
|
||||||
}
|
|
||||||
dev.SetKeyText(key, bg, color.White, keyLabels[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
dev, err := streamdeck.Open()
|
dev, err := streamdeck.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,19 +29,43 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer dev.Close()
|
defer dev.Close()
|
||||||
|
|
||||||
fmt.Printf("Connected to: %s (serial: %s)\n", dev.Product(), dev.SerialNumber())
|
model := dev.Model()
|
||||||
|
fmt.Printf("Connected to: %s (%s, serial: %s)\n", dev.Product(), model.Name, dev.SerialNumber())
|
||||||
|
fmt.Printf("Keys: %d (%dx%d), Encoders: %d\n", model.Keys, model.KeyCols, model.KeyRows, model.Encoders)
|
||||||
|
|
||||||
dev.SetBrightness(80)
|
dev.SetBrightness(80)
|
||||||
|
|
||||||
for i := 0; i < streamdeck.KeyCount(); i++ {
|
keyLabels := make([]string, model.Keys)
|
||||||
drawKey(dev, i, false)
|
for i := range keyLabels {
|
||||||
|
if i < 8 {
|
||||||
|
keyLabels[i] = string(rune('1' + i))
|
||||||
|
} else {
|
||||||
|
keyLabels[i] = string(rune('A' + i - 8))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
active := make([]bool, streamdeck.KeyCount())
|
drawKey := func(key int, active bool) {
|
||||||
|
col := key % model.KeyCols
|
||||||
|
bg := palette[col%len(palette)]
|
||||||
|
if !active {
|
||||||
|
bg = color.RGBA{bg.R / 3, bg.G / 3, bg.B / 3, 255}
|
||||||
|
}
|
||||||
|
dev.SetKeyText(key, bg, color.White, keyLabels[key])
|
||||||
|
}
|
||||||
|
|
||||||
keys := make(chan streamdeck.KeyEvent, 64)
|
for i := 0; i < model.Keys; i++ {
|
||||||
|
drawKey(i, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if model.LCDWidth > 0 {
|
||||||
|
dev.SetLCDColor(0, 0, model.LCDWidth, model.LCDHeight, color.RGBA{30, 30, 30, 255})
|
||||||
|
}
|
||||||
|
|
||||||
|
active := make([]bool, model.Keys)
|
||||||
|
|
||||||
|
input := make(chan streamdeck.InputEvent, 64)
|
||||||
go func() {
|
go func() {
|
||||||
if err := dev.ReadKeys(keys); err != nil {
|
if err := dev.ReadInput(input); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Read error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Read error: %v\n", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -67,13 +75,29 @@ func main() {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case ev := <-keys:
|
case ev := <-input:
|
||||||
if !ev.Pressed {
|
if ev.Key != nil && ev.Key.Pressed {
|
||||||
continue
|
k := ev.Key.Key
|
||||||
|
active[k] = !active[k]
|
||||||
|
drawKey(k, active[k])
|
||||||
|
fmt.Printf("Key %s toggled %v\n", keyLabels[k], active[k])
|
||||||
|
}
|
||||||
|
if ev.Encoder != nil {
|
||||||
|
e := ev.Encoder
|
||||||
|
if e.Delta != 0 {
|
||||||
|
fmt.Printf("Encoder %d rotated %+d\n", e.Encoder, e.Delta)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Encoder %d pressed=%v\n", e.Encoder, e.Pressed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ev.Touch != nil {
|
||||||
|
t := ev.Touch
|
||||||
|
if t.Type == streamdeck.TouchSwipe {
|
||||||
|
fmt.Printf("Touch swipe (%d,%d) -> (%d,%d)\n", t.X, t.Y, t.X2, t.Y2)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Touch %v at (%d,%d)\n", t.Type, t.X, t.Y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
active[ev.Key] = !active[ev.Key]
|
|
||||||
drawKey(dev, ev.Key, active[ev.Key])
|
|
||||||
fmt.Printf("Key %s toggled %v\n", keyLabels[ev.Key], active[ev.Key])
|
|
||||||
case <-sig:
|
case <-sig:
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package streamdeck
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
@@ -13,33 +14,83 @@ import (
|
|||||||
"rafaelmartins.com/p/usbhid"
|
"rafaelmartins.com/p/usbhid"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const elgatoVendorID = 0x0fd9
|
||||||
elgatoVendorID = 0x0fd9
|
|
||||||
keyCount = 32
|
|
||||||
keyRows = 4
|
|
||||||
keyCols = 8
|
|
||||||
keySize = 96
|
|
||||||
keyStart = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
var xlProductIDs = map[uint16]bool{
|
type Model struct {
|
||||||
0x006c: true,
|
Name string
|
||||||
0x008f: true,
|
Keys int
|
||||||
|
KeyRows int
|
||||||
|
KeyCols int
|
||||||
|
KeySize int
|
||||||
|
FlipKeys bool
|
||||||
|
Encoders int
|
||||||
|
LCDWidth int
|
||||||
|
LCDHeight int
|
||||||
|
}
|
||||||
|
|
||||||
|
var ModelXL = Model{
|
||||||
|
Name: "XL",
|
||||||
|
Keys: 32,
|
||||||
|
KeyRows: 4,
|
||||||
|
KeyCols: 8,
|
||||||
|
KeySize: 96,
|
||||||
|
FlipKeys: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ModelPlus = Model{
|
||||||
|
Name: "Plus",
|
||||||
|
Keys: 8,
|
||||||
|
KeyRows: 2,
|
||||||
|
KeyCols: 4,
|
||||||
|
KeySize: 120,
|
||||||
|
Encoders: 4,
|
||||||
|
LCDWidth: 800,
|
||||||
|
LCDHeight: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
var productModels = map[uint16]*Model{
|
||||||
|
0x006c: &ModelXL,
|
||||||
|
0x008f: &ModelXL,
|
||||||
|
0x0084: &ModelPlus,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
dev *usbhid.Device
|
dev *usbhid.Device
|
||||||
|
model *Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open() (*Device, error) {
|
func Open() (*Device, error) {
|
||||||
devices, err := usbhid.Enumerate(func(dev *usbhid.Device) bool {
|
devices, err := usbhid.Enumerate(func(dev *usbhid.Device) bool {
|
||||||
return dev.VendorId() == elgatoVendorID && xlProductIDs[dev.ProductId()]
|
return dev.VendorId() == elgatoVendorID && productModels[dev.ProductId()] != nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("streamdeck: enumerate: %w", err)
|
return nil, fmt.Errorf("streamdeck: enumerate: %w", err)
|
||||||
}
|
}
|
||||||
if len(devices) == 0 {
|
if len(devices) == 0 {
|
||||||
return nil, fmt.Errorf("streamdeck: no XL device found")
|
return nil, fmt.Errorf("streamdeck: no device found")
|
||||||
|
}
|
||||||
|
|
||||||
|
dev := devices[0]
|
||||||
|
model := productModels[dev.ProductId()]
|
||||||
|
if err := dev.Open(true); err != nil {
|
||||||
|
return nil, fmt.Errorf("streamdeck: open: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Device{dev: dev, model: model}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenModel(m *Model) (*Device, error) {
|
||||||
|
devices, err := usbhid.Enumerate(func(dev *usbhid.Device) bool {
|
||||||
|
if dev.VendorId() != elgatoVendorID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return productModels[dev.ProductId()] == m
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("streamdeck: enumerate: %w", err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
return nil, fmt.Errorf("streamdeck: no %s device found", m.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
dev := devices[0]
|
dev := devices[0]
|
||||||
@@ -47,20 +98,13 @@ func Open() (*Device, error) {
|
|||||||
return nil, fmt.Errorf("streamdeck: open: %w", err)
|
return nil, fmt.Errorf("streamdeck: open: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Device{dev: dev}, nil
|
return &Device{dev: dev, model: m}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) Close() error {
|
func (d *Device) Model() *Model { return d.model }
|
||||||
return d.dev.Close()
|
func (d *Device) Close() error { return d.dev.Close() }
|
||||||
}
|
func (d *Device) SerialNumber() string { return d.dev.SerialNumber() }
|
||||||
|
func (d *Device) Product() string { return d.dev.Product() }
|
||||||
func (d *Device) SerialNumber() string {
|
|
||||||
return d.dev.SerialNumber()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Product() string {
|
|
||||||
return d.dev.Product()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) FirmwareVersion() (string, error) {
|
func (d *Device) FirmwareVersion() (string, error) {
|
||||||
buf, err := d.dev.GetFeatureReport(5)
|
buf, err := d.dev.GetFeatureReport(5)
|
||||||
@@ -88,32 +132,38 @@ func (d *Device) Reset() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) SetKeyColor(key int, c color.Color) error {
|
func (d *Device) SetKeyColor(key int, c color.Color) error {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, keySize, keySize))
|
sz := d.model.KeySize
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, sz, sz))
|
||||||
xdraw.Draw(img, img.Bounds(), &image.Uniform{c}, image.Point{}, xdraw.Src)
|
xdraw.Draw(img, img.Bounds(), &image.Uniform{c}, image.Point{}, xdraw.Src)
|
||||||
return d.SetKeyImage(key, img)
|
return d.SetKeyImage(key, img)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) SetKeyImage(key int, img image.Image) error {
|
func (d *Device) SetKeyImage(key int, img image.Image) error {
|
||||||
if key < 0 || key >= keyCount {
|
if key < 0 || key >= d.model.Keys {
|
||||||
return fmt.Errorf("streamdeck: invalid key %d", key)
|
return fmt.Errorf("streamdeck: invalid key %d", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
scaled := image.NewRGBA(image.Rect(0, 0, keySize, keySize))
|
sz := d.model.KeySize
|
||||||
|
scaled := image.NewRGBA(image.Rect(0, 0, sz, sz))
|
||||||
xdraw.BiLinear.Scale(scaled, scaled.Bounds(), img, img.Bounds(), xdraw.Over, nil)
|
xdraw.BiLinear.Scale(scaled, scaled.Bounds(), img, img.Bounds(), xdraw.Over, nil)
|
||||||
|
|
||||||
flipped := image.NewRGBA(scaled.Bounds())
|
var src image.Image = scaled
|
||||||
for y := 0; y < keySize; y++ {
|
if d.model.FlipKeys {
|
||||||
for x := 0; x < keySize; x++ {
|
flipped := image.NewRGBA(scaled.Bounds())
|
||||||
flipped.Set(keySize-1-x, keySize-1-y, scaled.At(x, y))
|
for y := 0; y < sz; y++ {
|
||||||
|
for x := 0; x < sz; x++ {
|
||||||
|
flipped.Set(sz-1-x, sz-1-y, scaled.At(x, y))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
src = flipped
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := jpeg.Encode(&buf, flipped, &jpeg.Options{Quality: 100}); err != nil {
|
if err := jpeg.Encode(&buf, src, &jpeg.Options{Quality: 100}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.sendImage(byte(key), buf.Bytes())
|
return d.sendKeyImage(byte(key), buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) ClearKey(key int) error {
|
func (d *Device) ClearKey(key int) error {
|
||||||
@@ -121,7 +171,7 @@ func (d *Device) ClearKey(key int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) ClearAllKeys() error {
|
func (d *Device) ClearAllKeys() error {
|
||||||
for i := 0; i < keyCount; i++ {
|
for i := 0; i < d.model.Keys; i++ {
|
||||||
if err := d.ClearKey(i); err != nil {
|
if err := d.ClearKey(i); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -129,12 +179,12 @@ func (d *Device) ClearAllKeys() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) sendImage(key byte, imgData []byte) error {
|
func (d *Device) sendKeyImage(key byte, imgData []byte) error {
|
||||||
reportLen := d.dev.GetOutputReportLength()
|
reportLen := d.dev.GetOutputReportLength()
|
||||||
hdrLen := uint16(7)
|
hdrLen := uint16(8)
|
||||||
payloadLen := reportLen - hdrLen
|
payloadLen := reportLen - hdrLen
|
||||||
|
|
||||||
var page byte
|
var page uint16
|
||||||
for start := uint16(0); start < uint16(len(imgData)); page++ {
|
for start := uint16(0); start < uint16(len(imgData)); page++ {
|
||||||
end := start + payloadLen
|
end := start + payloadLen
|
||||||
last := byte(0)
|
last := byte(0)
|
||||||
@@ -145,13 +195,14 @@ func (d *Device) sendImage(key byte, imgData []byte) error {
|
|||||||
|
|
||||||
chunk := imgData[start:end]
|
chunk := imgData[start:end]
|
||||||
hdr := []byte{
|
hdr := []byte{
|
||||||
|
0x02,
|
||||||
0x07,
|
0x07,
|
||||||
key,
|
key,
|
||||||
last,
|
last,
|
||||||
byte(len(chunk)),
|
byte(len(chunk)),
|
||||||
byte(len(chunk) >> 8),
|
byte(len(chunk) >> 8),
|
||||||
page,
|
byte(page),
|
||||||
0,
|
byte(page >> 8),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := append(hdr, chunk...)
|
payload := append(hdr, chunk...)
|
||||||
@@ -166,39 +217,193 @@ func (d *Device) sendImage(key byte, imgData []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Device) SetLCDImage(x, y, w, h int, img image.Image) error {
|
||||||
|
if d.model.LCDWidth == 0 {
|
||||||
|
return fmt.Errorf("streamdeck: %s has no LCD", d.model.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled := image.NewRGBA(image.Rect(0, 0, w, h))
|
||||||
|
xdraw.BiLinear.Scale(scaled, scaled.Bounds(), img, img.Bounds(), xdraw.Over, nil)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := jpeg.Encode(&buf, scaled, &jpeg.Options{Quality: 100}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.sendLCDImage(uint16(x), uint16(y), uint16(w), uint16(h), buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) SetLCDColor(x, y, w, h int, c color.Color) error {
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, w, h))
|
||||||
|
xdraw.Draw(img, img.Bounds(), &image.Uniform{c}, image.Point{}, xdraw.Src)
|
||||||
|
return d.SetLCDImage(x, y, w, h, img)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) sendLCDImage(x, y, w, h uint16, imgData []byte) error {
|
||||||
|
reportLen := d.dev.GetOutputReportLength()
|
||||||
|
hdrLen := uint16(16)
|
||||||
|
payloadLen := reportLen - hdrLen
|
||||||
|
|
||||||
|
var page uint16
|
||||||
|
for start := uint16(0); start < uint16(len(imgData)); page++ {
|
||||||
|
end := start + payloadLen
|
||||||
|
last := byte(0)
|
||||||
|
if end >= uint16(len(imgData)) {
|
||||||
|
end = uint16(len(imgData))
|
||||||
|
last = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk := imgData[start:end]
|
||||||
|
hdr := make([]byte, 16)
|
||||||
|
hdr[0] = 0x02
|
||||||
|
hdr[1] = 0x0C
|
||||||
|
binary.LittleEndian.PutUint16(hdr[2:], x)
|
||||||
|
binary.LittleEndian.PutUint16(hdr[4:], y)
|
||||||
|
binary.LittleEndian.PutUint16(hdr[6:], w)
|
||||||
|
binary.LittleEndian.PutUint16(hdr[8:], h)
|
||||||
|
hdr[10] = last
|
||||||
|
binary.LittleEndian.PutUint16(hdr[11:], page)
|
||||||
|
binary.LittleEndian.PutUint16(hdr[13:], uint16(len(chunk)))
|
||||||
|
hdr[15] = 0
|
||||||
|
|
||||||
|
payload := append(hdr, chunk...)
|
||||||
|
padding := make([]byte, reportLen-uint16(len(payload)))
|
||||||
|
payload = append(payload, padding...)
|
||||||
|
|
||||||
|
if err := d.dev.SetOutputReport(2, payload); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
start = end
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type KeyEvent struct {
|
type KeyEvent struct {
|
||||||
Key int
|
Key int
|
||||||
Pressed bool
|
Pressed bool
|
||||||
Time time.Time
|
Time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) ReadKeys(ch chan<- KeyEvent) error {
|
type EncoderEvent struct {
|
||||||
states := make([]byte, keyCount)
|
Encoder int
|
||||||
|
Pressed bool
|
||||||
|
Delta int
|
||||||
|
Time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type TouchEvent struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
X2 int
|
||||||
|
Y2 int
|
||||||
|
Type TouchType
|
||||||
|
Time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type TouchType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TouchShort TouchType = 1
|
||||||
|
TouchLong TouchType = 2
|
||||||
|
TouchSwipe TouchType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type InputEvent struct {
|
||||||
|
Key *KeyEvent
|
||||||
|
Encoder *EncoderEvent
|
||||||
|
Touch *TouchEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) ReadInput(ch chan<- InputEvent) error {
|
||||||
|
keyStates := make([]byte, d.model.Keys)
|
||||||
|
encoderStates := make([]byte, d.model.Encoders)
|
||||||
for {
|
for {
|
||||||
_, buf, err := d.dev.GetInputReport()
|
_, buf, err := d.dev.GetInputReport()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(buf) < 4 {
|
||||||
if int(keyStart+keyCount) > len(buf) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
for i := 0; i < keyCount; i++ {
|
switch buf[0] {
|
||||||
st := buf[keyStart+i]
|
case 0x00:
|
||||||
if st != states[i] {
|
keyStart := 3
|
||||||
ch <- KeyEvent{
|
for i := 0; i < d.model.Keys; i++ {
|
||||||
Key: i,
|
if keyStart+i >= len(buf) {
|
||||||
Pressed: st > 0,
|
break
|
||||||
Time: t,
|
}
|
||||||
|
st := buf[keyStart+i]
|
||||||
|
if st != keyStates[i] {
|
||||||
|
ch <- InputEvent{Key: &KeyEvent{
|
||||||
|
Key: i,
|
||||||
|
Pressed: st > 0,
|
||||||
|
Time: t,
|
||||||
|
}}
|
||||||
|
keyStates[i] = st
|
||||||
}
|
}
|
||||||
states[i] = st
|
|
||||||
}
|
}
|
||||||
|
case 0x03:
|
||||||
|
if d.model.Encoders == 0 || len(buf) < 8 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subType := buf[3]
|
||||||
|
switch subType {
|
||||||
|
case 0x00:
|
||||||
|
for i := 0; i < d.model.Encoders; i++ {
|
||||||
|
st := buf[4+i]
|
||||||
|
if st != encoderStates[i] {
|
||||||
|
ch <- InputEvent{Encoder: &EncoderEvent{
|
||||||
|
Encoder: i,
|
||||||
|
Pressed: st > 0,
|
||||||
|
Time: t,
|
||||||
|
}}
|
||||||
|
encoderStates[i] = st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 0x01:
|
||||||
|
for i := 0; i < d.model.Encoders; i++ {
|
||||||
|
delta := int(int8(buf[4+i]))
|
||||||
|
if delta != 0 {
|
||||||
|
ch <- InputEvent{Encoder: &EncoderEvent{
|
||||||
|
Encoder: i,
|
||||||
|
Delta: delta,
|
||||||
|
Time: t,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 0x02:
|
||||||
|
if len(buf) < 14 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subType := TouchType(buf[3])
|
||||||
|
x := int(binary.LittleEndian.Uint16(buf[5:7]))
|
||||||
|
y := int(binary.LittleEndian.Uint16(buf[7:9]))
|
||||||
|
ev := TouchEvent{
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
Type: subType,
|
||||||
|
Time: t,
|
||||||
|
}
|
||||||
|
if subType == TouchSwipe {
|
||||||
|
ev.X2 = int(binary.LittleEndian.Uint16(buf[9:11]))
|
||||||
|
ev.Y2 = int(binary.LittleEndian.Uint16(buf[11:13]))
|
||||||
|
}
|
||||||
|
ch <- InputEvent{Touch: &ev}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func KeyCount() int { return keyCount }
|
func (d *Device) ReadKeys(ch chan<- KeyEvent) error {
|
||||||
func KeyRows() int { return keyRows }
|
input := make(chan InputEvent, 64)
|
||||||
func KeyCols() int { return keyCols }
|
go func() {
|
||||||
|
for ev := range input {
|
||||||
|
if ev.Key != nil {
|
||||||
|
ch <- *ev.Key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return d.ReadInput(input)
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,16 +77,12 @@ func DrawText(img *image.RGBA, face font.Face, fg color.Color, lines ...string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TextImage(bg color.Color, fg color.Color, lines ...string) image.Image {
|
func TextImageSized(size int, bg color.Color, fg color.Color, lines ...string) image.Image {
|
||||||
return TextImageWithFace(MonoMedium, bg, fg, lines...)
|
return TextImageWithFaceSized(MonoMedium, size, bg, fg, lines...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BoldTextImage(bg color.Color, fg color.Color, lines ...string) image.Image {
|
func TextImageWithFaceSized(face font.Face, size int, bg color.Color, fg color.Color, lines ...string) image.Image {
|
||||||
return TextImageWithFace(MonoBold, bg, fg, lines...)
|
img := image.NewRGBA(image.Rect(0, 0, size, size))
|
||||||
}
|
|
||||||
|
|
||||||
func TextImageWithFace(face font.Face, bg color.Color, fg color.Color, lines ...string) image.Image {
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, keySize, keySize))
|
|
||||||
draw.Draw(img, img.Bounds(), &image.Uniform{bg}, image.Point{}, draw.Src)
|
draw.Draw(img, img.Bounds(), &image.Uniform{bg}, image.Point{}, draw.Src)
|
||||||
DrawText(img, face, fg, lines...)
|
DrawText(img, face, fg, lines...)
|
||||||
return img
|
return img
|
||||||
@@ -94,10 +90,10 @@ func TextImageWithFace(face font.Face, bg color.Color, fg color.Color, lines ...
|
|||||||
|
|
||||||
func (d *Device) SetKeyText(key int, bg color.Color, fg color.Color, text string) error {
|
func (d *Device) SetKeyText(key int, bg color.Color, fg color.Color, text string) error {
|
||||||
lines := strings.Split(text, "\n")
|
lines := strings.Split(text, "\n")
|
||||||
return d.SetKeyImage(key, TextImage(bg, fg, lines...))
|
return d.SetKeyImage(key, TextImageSized(d.model.KeySize, bg, fg, lines...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) SetKeyBoldText(key int, bg color.Color, fg color.Color, text string) error {
|
func (d *Device) SetKeyBoldText(key int, bg color.Color, fg color.Color, text string) error {
|
||||||
lines := strings.Split(text, "\n")
|
lines := strings.Split(text, "\n")
|
||||||
return d.SetKeyImage(key, BoldTextImage(bg, fg, lines...))
|
return d.SetKeyImage(key, TextImageWithFaceSized(MonoBold, d.model.KeySize, bg, fg, lines...))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user