mirror of
https://github.com/Luzifer/streamdeck.git
synced 2024-12-29 22:21:24 +00:00
Implement dynamic exec displays
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
eee8f31f82
commit
fcbbfc0c30
3 changed files with 66 additions and 10 deletions
|
@ -11,21 +11,25 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang/freetype"
|
"github.com/golang/freetype"
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/image/font"
|
"golang.org/x/image/font"
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerDisplayElement("exec", displayElementCommand{})
|
registerDisplayElement("exec", &displayElementExec{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type displayElementCommand struct{}
|
type displayElementExec struct {
|
||||||
|
running bool
|
||||||
|
}
|
||||||
|
|
||||||
func (d displayElementCommand) Display(idx int, attributes map[string]interface{}) error {
|
func (d displayElementExec) Display(idx int, attributes map[string]interface{}) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
img draw.Image = image.NewRGBA(image.Rect(0, 0, sd.IconSize(), sd.IconSize()))
|
img draw.Image = image.NewRGBA(image.Rect(0, 0, sd.IconSize(), sd.IconSize()))
|
||||||
|
@ -107,7 +111,35 @@ func (d displayElementCommand) Display(idx int, attributes map[string]interface{
|
||||||
return errors.Wrap(sd.FillImage(idx, img), "Unable to set image")
|
return errors.Wrap(sd.FillImage(idx, img), "Unable to set image")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (displayElementCommand) drawText(c *freetype.Context, text string, textColor color.Color, fontsize float64, border int) error {
|
func (d *displayElementExec) StartLoopDisplay(idx int, attributes map[string]interface{}) error {
|
||||||
|
d.running = true
|
||||||
|
|
||||||
|
var interval = 5 * time.Second
|
||||||
|
if v, ok := attributes["interval"].(int); ok {
|
||||||
|
interval = time.Duration(v) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for tick := time.NewTicker(interval); ; <-tick.C {
|
||||||
|
if !d.running {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Display(idx, attributes); err != nil {
|
||||||
|
log.WithError(err).Error("Unable to refresh element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayElementExec) StopLoopDisplay() error {
|
||||||
|
d.running = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (displayElementExec) drawText(c *freetype.Context, text string, textColor color.Color, fontsize float64, border int) error {
|
||||||
c.SetSrc(image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0x0})) // Transparent for text size guessing
|
c.SetSrc(image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0x0})) // Transparent for text size guessing
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -140,7 +172,7 @@ func (displayElementCommand) drawText(c *freetype.Context, text string, textColo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (displayElementCommand) getImageFromDisk(filename string) (image.Image, error) {
|
func (displayElementExec) getImageFromDisk(filename string) (image.Image, error) {
|
||||||
f, err := os.Open(filename)
|
f, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Unable to open image")
|
return nil, errors.Wrap(err, "Unable to open image")
|
||||||
|
@ -155,7 +187,7 @@ func (displayElementCommand) getImageFromDisk(filename string) (image.Image, err
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (displayElementCommand) loadFont() (*truetype.Font, error) {
|
func (displayElementExec) loadFont() (*truetype.Font, error) {
|
||||||
fontRaw, err := ioutil.ReadFile(userConfig.RenderFont)
|
fontRaw, err := ioutil.ReadFile(userConfig.RenderFont)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Unable to read font file")
|
return nil, errors.Wrap(err, "Unable to read font file")
|
||||||
|
|
|
@ -23,8 +23,9 @@ var (
|
||||||
|
|
||||||
currentBrightness int
|
currentBrightness int
|
||||||
|
|
||||||
userConfig config
|
userConfig config
|
||||||
activePage page
|
activePage page
|
||||||
|
activeLoops []refreshingDisplayElement
|
||||||
|
|
||||||
sd *streamdeck.Client
|
sd *streamdeck.Client
|
||||||
|
|
||||||
|
@ -130,6 +131,14 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func togglePage(page string) error {
|
func togglePage(page string) error {
|
||||||
|
// Reset potentially running looped elements
|
||||||
|
for _, l := range activeLoops {
|
||||||
|
if err := l.StopLoopDisplay(); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to stop element refresh")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeLoops = nil
|
||||||
|
|
||||||
activePage = userConfig.Pages[page]
|
activePage = userConfig.Pages[page]
|
||||||
sd.ClearAllKeys()
|
sd.ClearAllKeys()
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,11 @@ type displayElement interface {
|
||||||
Display(idx int, attributes map[string]interface{}) error
|
Display(idx int, attributes map[string]interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type refreshingDisplayElement interface {
|
||||||
|
StartLoopDisplay(idx int, attributes map[string]interface{}) error
|
||||||
|
StopLoopDisplay() error
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
registeredActions = map[string]reflect.Type{}
|
registeredActions = map[string]reflect.Type{}
|
||||||
registeredActionsLock sync.Mutex
|
registeredActionsLock sync.Mutex
|
||||||
|
@ -53,7 +58,17 @@ func callDisplayElement(idx int, kd keyDefinition) error {
|
||||||
return errors.Errorf("Unknown display type %q", kd.Display.Type)
|
return errors.Errorf("Unknown display type %q", kd.Display.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
inst := reflect.New(t).Interface().(displayElement)
|
var inst interface{}
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
inst = reflect.New(t.Elem()).Interface()
|
||||||
|
} else {
|
||||||
|
inst = reflect.New(t).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
return inst.Display(idx, kd.Display.Attributes)
|
if t.Implements(reflect.TypeOf((*refreshingDisplayElement)(nil)).Elem()) {
|
||||||
|
activeLoops = append(activeLoops, inst.(refreshingDisplayElement))
|
||||||
|
return inst.(refreshingDisplayElement).StartLoopDisplay(idx, kd.Display.Attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return inst.(displayElement).Display(idx, kd.Display.Attributes)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue