Skip to content

Commit

Permalink
Add support for background image and cleanup widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
zMoooooritz committed May 12, 2021
1 parent 0a0b293 commit 3cb4426
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 61 deletions.
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (
"github.com/BurntSushi/toml"
)

type Background struct {
Image string `toml:"image"`
}

type DBusConfig struct {
Object string `toml:"object,omitempty"`
Path string `toml:"path,omitempty"`
Expand All @@ -33,9 +37,11 @@ type KeyConfig struct {
Action *ActionConfig `toml:"action,omitempty"`
ActionHold *ActionConfig `toml:"action_hold,omitempty"`
}

type Keys []KeyConfig

type DeckConfig struct {
Background Background `toml:"background,omitempty`
Keys Keys `toml:"keys"`
}

Expand Down
67 changes: 62 additions & 5 deletions deck.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package main
import (
"fmt"
"log"
"os"
"os/exec"
"strconv"
"strings"
"image"
"image/draw"

"github.com/muesli/streamdeck"
"github.com/atotto/clipboard"
"github.com/godbus/dbus"
)
Expand All @@ -17,15 +21,68 @@ type Deck struct {
}

// LoadDeck loads a deck configuration.
func LoadDeck(deck string) (*Deck, error) {
func LoadDeck(deck string, dev *streamdeck.Device) (*Deck, error) {
d := Deck{}
dc, err := LoadConfig(deck)
if err != nil {
return nil, err
}

for _, k := range dc.Keys {
w := NewWidget(k.Index, k.Widget.ID, k.Action, k.ActionHold, k.Widget.Config)
var background image.Image
if dc.Background.Image != "" {
f, err := os.Open(dc.Background.Image)
if err != nil {
return nil, err
}
defer f.Close()

background, _, err = image.Decode(f)
if err != nil {
return nil, err
}
// padding := int(dev.Padding)
padding := 16
width := int(dev.Columns) * int(dev.Pixels) + (int(dev.Columns)-1) * padding
height := int(dev.Rows) * int(dev.Pixels) + (int(dev.Rows)-1) * padding
if background.Bounds().Dx() != width ||
background.Bounds().Dy() != height {
return nil, fmt.Errorf("supplied background image has wrong dimensions, expected %dx%d pixels", width, height)
}
}

padding := 16
// padding := int(dev.Padding)
pixels := int(dev.Pixels)
rows := int(dev.Rows)
cols := int(dev.Columns)
for i:=0; i<cols*rows; i++ {
buttonImg := image.NewRGBA(image.Rect(0, 0, pixels, pixels))

if background != nil {
startx := (i % cols) * (pixels + padding)
starty := (i / cols) * (pixels + padding)
draw.Draw(buttonImg, buttonImg.Bounds(), background, image.Point{startx, starty}, draw.Src)
}

var widget KeyConfig
isWidget := false
for _, k := range dc.Keys {
if int(k.Index) == i {
widget = k
isWidget = true
break
}
}
var w Widget
k := widget
if isWidget {
// A widget is defined in the .deck file
w = NewWidget(k.Index, k.Widget.ID, k.Action, k.ActionHold, buttonImg, k.Widget.Config)
dc.Keys = dc.Keys[1:]
} else {
// No widget is defined for this button in the .deck file
w = NewWidget(uint8(i), "empty", nil, nil, buttonImg, nil)
}
d.Widgets = append(d.Widgets, w)
}

Expand Down Expand Up @@ -102,7 +159,7 @@ func (d *Deck) triggerAction(index uint8, hold bool) {
if a != nil {
fmt.Println("Executing overloaded action")
if a.Deck != "" {
d, err := LoadDeck(a.Deck)
d, err := LoadDeck(a.Deck, &dev)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -136,6 +193,6 @@ func (d *Deck) triggerAction(index uint8, hold bool) {
// updateWidgets updates/repaints all the widgets.
func (d *Deck) updateWidgets() {
for _, w := range d.Widgets {
w.Update(&dev)
w.SetImage(&dev, w.Update())
}
}
10 changes: 6 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ func main() {
flag.Parse()

var err error
deck, err = LoadDeck(*deckFile)
if err != nil {
log.Fatal(err)
}

dbusConn, err = dbus.SessionBus()
if err != nil {
Expand Down Expand Up @@ -105,10 +101,16 @@ func main() {
fmt.Printf("Found device with serial %s (firmware %s)\n",
dev.Serial, ver)

deck, err = LoadDeck(*deckFile, &dev)
if err != nil {
log.Fatal(err)
}

err = dev.Reset()
if err != nil {
log.Fatal(err)
}
deck.updateWidgets()

if *brightness > 100 {
*brightness = 100
Expand Down
29 changes: 23 additions & 6 deletions widget.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ var (

type Widget interface {
Key() uint8
Update(dev *streamdeck.Device)
Update() *image.RGBA
SetImage(dev *streamdeck.Device, bg *image.RGBA) error
Action() *ActionConfig
ActionHold() *ActionConfig
TriggerAction()
Expand All @@ -37,12 +38,25 @@ type BaseWidget struct {
key uint8
action *ActionConfig
actionHold *ActionConfig
bg image.Image
}

func (w *BaseWidget) Key() uint8 {
return w.key
}

func (w *BaseWidget) SetImage(dev *streamdeck.Device, img *image.RGBA) error {
pixels := int(dev.Pixels)

buttonImg := image.NewRGBA(image.Rect(0, 0, pixels, pixels))
if w.bg != nil {
draw.Draw(buttonImg, buttonImg.Bounds(), w.bg, image.ZP, draw.Over)
}
draw.Draw(buttonImg, buttonImg.Bounds(), img, image.ZP, draw.Over)

return dev.SetImage(w.key, buttonImg)
}

func (w *BaseWidget) Action() *ActionConfig {
return w.action
}
Expand All @@ -54,8 +68,8 @@ func (w *BaseWidget) ActionHold() *ActionConfig {
func (w *BaseWidget) TriggerAction() {
}

func NewWidget(index uint8, id string, action *ActionConfig, actionHold *ActionConfig, config map[string]string) Widget {
bw := BaseWidget{index, action, actionHold}
func NewWidget(index uint8, id string, action *ActionConfig, actionHold *ActionConfig, bg image.Image, config map[string]string) Widget {
bw := BaseWidget{index, action, actionHold, bg}

switch id {
case "button":
Expand Down Expand Up @@ -87,6 +101,9 @@ func NewWidget(index uint8, id string, action *ActionConfig, actionHold *ActionC
mode: config["mode"],
fillColor: config["fillColor"],
}
case "empty":
return &EmptyWidget{bw}

default:
// unknown widget ID
fmt.Println("Unknown widget with ID:", id)
Expand All @@ -95,7 +112,7 @@ func NewWidget(index uint8, id string, action *ActionConfig, actionHold *ActionC
return nil
}

func drawImage(img *image.RGBA, path string, size uint, x uint, y uint) error {
func drawImage(img *image.RGBA, path string, sizex uint, sizey uint, x uint, y uint) error {
f, err := os.Open(path)
if err != nil {
return err
Expand All @@ -107,8 +124,8 @@ func drawImage(img *image.RGBA, path string, size uint, x uint, y uint) error {
return err
}

icon = resize.Resize(size, size, icon, resize.Bilinear)
draw.Draw(img, image.Rect(int(x), int(y), int(x+size), int(y+size)), icon, image.Point{0, 0}, draw.Src)
icon = resize.Resize(sizex, sizey, icon, resize.Bilinear)
draw.Draw(img, image.Rect(int(x), int(y), int(x+sizex), int(y+sizey)), icon, image.Point{0, 0}, draw.Src)

return nil
}
Expand Down
25 changes: 9 additions & 16 deletions widget_button.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package main

import (
"image"
"log"
"sync"

"github.com/golang/freetype"
"github.com/muesli/streamdeck"
)

type ButtonWidget struct {
Expand All @@ -17,19 +15,14 @@ type ButtonWidget struct {
init sync.Once
}

func (w *ButtonWidget) Update(dev *streamdeck.Device) {
w.init.Do(func() {
img := image.NewRGBA(image.Rect(0, 0, 72, 72))
if w.label != "" {
_ = drawImage(img, w.icon, 48, 12, 4)
drawString(img, ttfFont, w.label, 8, freetype.Pt(-1, img.Bounds().Dx()-6))
} else {
_ = drawImage(img, w.icon, 64, 4, 4)
}
func (w *ButtonWidget) Update() *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, 72, 72))
if w.label != "" {
_ = drawImage(img, w.icon, 48, 48, 12, 4)
drawString(img, ttfFont, w.label, 8, freetype.Pt(-1, img.Bounds().Dx()-6))
} else {
_ = drawImage(img, w.icon, 72, 72, 0, 0)
}

err := dev.SetImage(w.key, img)
if err != nil {
log.Fatal(err)
}
})
return img
}
9 changes: 2 additions & 7 deletions widget_clock.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ package main

import (
"image"
"log"
"time"

"github.com/golang/freetype"
"github.com/muesli/streamdeck"
)

type ClockWidget struct {
BaseWidget
}

func (w *ClockWidget) Update(dev *streamdeck.Device) {
func (w *ClockWidget) Update() *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, 72, 72))

t := time.Now()
Expand All @@ -24,8 +22,5 @@ func (w *ClockWidget) Update(dev *streamdeck.Device) {
drawString(img, ttfFont, min, 13, freetype.Pt(-1, 43))
drawString(img, ttfThinFont, sec, 13, freetype.Pt(-1, 66))

err := dev.SetImage(w.key, img)
if err != nil {
log.Fatal(err)
}
return img
}
9 changes: 2 additions & 7 deletions widget_date.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ package main

import (
"image"
"log"
"strconv"
"time"

"github.com/golang/freetype"
"github.com/muesli/streamdeck"
)

type DateWidget struct {
BaseWidget
}

func (w *DateWidget) Update(dev *streamdeck.Device) {
func (w *DateWidget) Update() *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, 72, 72))

t := time.Now()
Expand All @@ -25,8 +23,5 @@ func (w *DateWidget) Update(dev *streamdeck.Device) {
drawString(img, ttfFont, month, 13, freetype.Pt(-1, 43))
drawString(img, ttfThinFont, strconv.Itoa(year), 13, freetype.Pt(-1, 66))

err := dev.SetImage(w.key, img)
if err != nil {
log.Fatal(err)
}
return img
}
14 changes: 14 additions & 0 deletions widget_empty.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

import (
"image"
)

type EmptyWidget struct {
BaseWidget
}

func (w *EmptyWidget) Update() *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, 72, 72))
return img
}
11 changes: 3 additions & 8 deletions widget_recent_window.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package main
import (
"image"
"image/draw"
"log"

"github.com/muesli/streamdeck"
"github.com/nfnt/resize"
)

Expand All @@ -16,23 +14,20 @@ type RecentWindowWidget struct {
lastClass string
}

func (w *RecentWindowWidget) Update(dev *streamdeck.Device) {
func (w *RecentWindowWidget) Update() *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, 72, 72))

if int(w.window) < len(recentWindows) {
if w.lastClass == recentWindows[w.window].Class {
return
return img
}
w.lastClass = recentWindows[w.window].Class

icon := resize.Resize(64, 64, recentWindows[w.window].Icon, resize.Bilinear)
draw.Draw(img, image.Rect(4, 4, 68, 68), icon, image.Point{0, 0}, draw.Src)
}

err := dev.SetImage(w.key, img)
if err != nil {
log.Fatal(err)
}
return img
}

func (w *RecentWindowWidget) TriggerAction() {
Expand Down

0 comments on commit 3cb4426

Please sign in to comment.