mirror of
https://github.com/Luzifer/streamdeck.git
synced 2024-12-20 09:41:19 +00:00
Add initial version of display-command
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
68ff332d24
commit
de53ed8419
1 changed files with 165 additions and 0 deletions
165
cmd/streamdeck/display_command.go
Normal file
165
cmd/streamdeck/display_command.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerDisplayElement("exec", displayElementCommand{})
|
||||
}
|
||||
|
||||
type displayElementCommand struct{}
|
||||
|
||||
func (d displayElementCommand) Display(idx int, attributes map[string]interface{}) error {
|
||||
var (
|
||||
err error
|
||||
img draw.Image = image.NewRGBA(image.Rect(0, 0, sd.IconSize(), sd.IconSize()))
|
||||
)
|
||||
|
||||
// Initialize black image
|
||||
draw.Draw(img, img.Bounds(), image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0xff}), image.ZP, draw.Src)
|
||||
|
||||
cmd, ok := attributes["command"].([]interface{})
|
||||
if !ok {
|
||||
return errors.New("No command supplied")
|
||||
}
|
||||
|
||||
var args []string
|
||||
for _, c := range cmd {
|
||||
if v, ok := c.(string); ok {
|
||||
args = append(args, v)
|
||||
continue
|
||||
}
|
||||
return errors.New("Command conatins non-string argument")
|
||||
}
|
||||
|
||||
if filename, ok := attributes["image"].(string); ok {
|
||||
bgi, err := d.getImageFromDisk(filename)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to get image from disk")
|
||||
}
|
||||
|
||||
draw.Draw(img, img.Bounds(), bgi, image.ZP, draw.Src)
|
||||
}
|
||||
|
||||
var textColor color.Color = color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||
if rgba, ok := attributes["color"].([]interface{}); ok {
|
||||
if len(rgba) != 4 {
|
||||
return errors.New("RGBA color definition needs 4 hex values")
|
||||
}
|
||||
|
||||
textColor = color.RGBA{uint8(rgba[0].(int)), uint8(rgba[1].(int)), uint8(rgba[2].(int)), uint8(rgba[3].(int))}
|
||||
}
|
||||
|
||||
var fontsize float64 = 120
|
||||
if v, ok := attributes["font_size"].(float64); ok {
|
||||
fontsize = v
|
||||
}
|
||||
|
||||
var border = 10
|
||||
if v, ok := attributes["border"].(int); ok {
|
||||
border = v
|
||||
}
|
||||
|
||||
var buf = new(bytes.Buffer)
|
||||
|
||||
command := exec.Command(args[0], args[1:]...)
|
||||
command.Env = os.Environ()
|
||||
command.Stdout = buf
|
||||
|
||||
if err := command.Run(); err != nil {
|
||||
return errors.Wrap(err, "Command has exit != 0")
|
||||
}
|
||||
|
||||
f, err := d.loadFont()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to load font")
|
||||
}
|
||||
|
||||
c := freetype.NewContext()
|
||||
c.SetClip(img.Bounds())
|
||||
c.SetDPI(72)
|
||||
c.SetDst(img)
|
||||
c.SetFont(f)
|
||||
c.SetHinting(font.HintingNone)
|
||||
|
||||
if strings.TrimSpace(buf.String()) != "" {
|
||||
if err = d.drawText(c, strings.TrimSpace(buf.String()), textColor, fontsize, border); err != nil {
|
||||
return errors.Wrap(err, "Unable to render text")
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
c.SetSrc(image.NewUniform(color.RGBA{0x0, 0x0, 0x0, 0x0})) // Transparent for text size guessing
|
||||
|
||||
var (
|
||||
err error
|
||||
ext fixed.Point26_6
|
||||
)
|
||||
for {
|
||||
c.SetFontSize(fontsize)
|
||||
|
||||
ext, err = c.DrawString(text, freetype.Pt(0, 0))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to measure text")
|
||||
}
|
||||
|
||||
if int(float64(ext.X)/64) > sd.IconSize()-2*border || int(c.PointToFixed(fontsize/2.0)/64) > sd.IconSize()-2*border {
|
||||
fontsize -= 2
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
c.SetSrc(image.NewUniform(textColor)) // TODO: Dynamic
|
||||
|
||||
xcenter := (float64(sd.IconSize()-2*border) / 2.0) - (float64(int(float64(ext.X)/64)) / 2.0) + float64(border)
|
||||
ycenter := (float64(sd.IconSize()-2*border) / 2.0) + (float64(c.PointToFixed(fontsize/2.0)/64) / 2.0) + float64(border)
|
||||
|
||||
_, err = c.DrawString(text, freetype.Pt(int(xcenter), int(ycenter)))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (displayElementCommand) getImageFromDisk(filename string) (image.Image, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unable to open image")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Umable to decode image")
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func (displayElementCommand) loadFont() (*truetype.Font, error) {
|
||||
fontRaw, err := ioutil.ReadFile(userConfig.RenderFont)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unable to read font file")
|
||||
}
|
||||
|
||||
return truetype.Parse(fontRaw)
|
||||
}
|
Loading…
Reference in a new issue