diff --git a/cmd/streamdeck/display_color.go b/cmd/streamdeck/display_color.go index cce9742..72fe985 100644 --- a/cmd/streamdeck/display_color.go +++ b/cmd/streamdeck/display_color.go @@ -1,6 +1,7 @@ package main import ( + "context" "image/color" "github.com/pkg/errors" @@ -12,7 +13,7 @@ func init() { type displayElementColor struct{} -func (d displayElementColor) Display(idx int, attributes map[string]interface{}) error { +func (d displayElementColor) Display(ctx context.Context, idx int, attributes map[string]interface{}) error { if name, ok := attributes["color"].(string); ok { return d.displayPredefinedColor(idx, name) } @@ -22,6 +23,11 @@ func (d displayElementColor) Display(idx int, attributes map[string]interface{}) return errors.New("RGBA color definition needs 4 hex values") } + if err := ctx.Err(); err != nil { + // Page context was cancelled, do not draw + return err + } + return sd.FillColor(idx, color.RGBA{uint8(rgba[0].(int)), uint8(rgba[1].(int)), uint8(rgba[2].(int)), uint8(rgba[3].(int))}) } diff --git a/cmd/streamdeck/display_exec.go b/cmd/streamdeck/display_exec.go index 9c56dac..6020366 100644 --- a/cmd/streamdeck/display_exec.go +++ b/cmd/streamdeck/display_exec.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "encoding/json" "image" "image/color" @@ -30,7 +31,7 @@ type displayElementExec struct { running bool } -func (d displayElementExec) Display(idx int, attributes map[string]interface{}) error { +func (d displayElementExec) Display(ctx context.Context, idx int, attributes map[string]interface{}) error { var ( err error img draw.Image = image.NewRGBA(image.Rect(0, 0, sd.IconSize(), sd.IconSize())) @@ -141,6 +142,11 @@ func (d displayElementExec) Display(idx int, attributes map[string]interface{}) return nil } + if err := ctx.Err(); err != nil { + // Page context was cancelled, do not draw + return err + } + return errors.Wrap(sd.FillImage(idx, img), "Unable to set image") } @@ -152,7 +158,7 @@ func (d displayElementExec) NeedsLoop(attributes map[string]interface{}) bool { return false } -func (d *displayElementExec) StartLoopDisplay(idx int, attributes map[string]interface{}) error { +func (d *displayElementExec) StartLoopDisplay(ctx context.Context, idx int, attributes map[string]interface{}) error { d.running = true var interval = 5 * time.Second @@ -166,7 +172,7 @@ func (d *displayElementExec) StartLoopDisplay(idx int, attributes map[string]int return } - if err := d.Display(idx, attributes); err != nil { + if err := d.Display(ctx, idx, attributes); err != nil { log.WithError(err).Error("Unable to refresh element") } } diff --git a/cmd/streamdeck/display_image.go b/cmd/streamdeck/display_image.go index 95c73ab..44011c8 100644 --- a/cmd/streamdeck/display_image.go +++ b/cmd/streamdeck/display_image.go @@ -1,6 +1,7 @@ package main import ( + "context" "image" _ "image/jpeg" _ "image/png" @@ -15,7 +16,7 @@ func init() { type displayElementImage struct{} -func (d displayElementImage) Display(idx int, attributes map[string]interface{}) error { +func (d displayElementImage) Display(ctx context.Context, idx int, attributes map[string]interface{}) error { filename, ok := attributes["path"].(string) if !ok { return errors.New("No path attribute specified") @@ -34,5 +35,10 @@ func (d displayElementImage) Display(idx int, attributes map[string]interface{}) img = autoSizeImage(img, sd.IconSize()) + if err := ctx.Err(); err != nil { + // Page context was cancelled, do not draw + return err + } + return errors.Wrap(sd.FillImage(idx, img), "Unable to set image") } diff --git a/cmd/streamdeck/main.go b/cmd/streamdeck/main.go index b1f9297..5cbc886 100644 --- a/cmd/streamdeck/main.go +++ b/cmd/streamdeck/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "os" "os/signal" @@ -28,10 +29,12 @@ var ( currentBrightness int - userConfig config - activePage page - activePageName string - activeLoops []refreshingDisplayElement + userConfig config + activePage page + activePageCtx context.Context + activePageCtxCancel context.CancelFunc + activePageName string + activeLoops []refreshingDisplayElement sd *streamdeck.Client @@ -208,6 +211,11 @@ func main() { } func togglePage(page string) error { + if activePageCtxCancel != nil { + // Ensure old display events are no longer executed + activePageCtxCancel() + } + // Reset potentially running looped elements for _, l := range activeLoops { if err := l.StopLoopDisplay(); err != nil { @@ -218,11 +226,12 @@ func togglePage(page string) error { activePage = userConfig.Pages[page] activePageName = page + activePageCtx, activePageCtxCancel = context.WithCancel(context.Background()) sd.ClearAllKeys() for idx, kd := range activePage.Keys { if kd.Display.Type != "" { - if err := callDisplayElement(idx, kd); err != nil { + if err := callDisplayElement(activePageCtx, idx, kd); err != nil { return errors.Wrapf(err, "Unable to execute display element on key %d", idx) } } diff --git a/cmd/streamdeck/registry.go b/cmd/streamdeck/registry.go index c2b0c30..ecd9585 100644 --- a/cmd/streamdeck/registry.go +++ b/cmd/streamdeck/registry.go @@ -1,6 +1,7 @@ package main import ( + "context" "reflect" "sync" @@ -13,12 +14,12 @@ type action interface { } type displayElement interface { - Display(idx int, attributes map[string]interface{}) error + Display(ctx context.Context, idx int, attributes map[string]interface{}) error } type refreshingDisplayElement interface { NeedsLoop(attributes map[string]interface{}) bool - StartLoopDisplay(idx int, attributes map[string]interface{}) error + StartLoopDisplay(ctx context.Context, idx int, attributes map[string]interface{}) error StopLoopDisplay() error } @@ -54,7 +55,7 @@ func callAction(a dynamicElement) error { return inst.Execute(a.Attributes) } -func callDisplayElement(idx int, kd keyDefinition) error { +func callDisplayElement(ctx context.Context, idx int, kd keyDefinition) error { t, ok := registeredDisplayElements[kd.Display.Type] if !ok { return errors.Errorf("Unknown display type %q", kd.Display.Type) @@ -74,8 +75,8 @@ func callDisplayElement(idx int, kd keyDefinition) error { "display_type": kd.Display.Type, }).Debug("Starting loop") activeLoops = append(activeLoops, inst.(refreshingDisplayElement)) - return inst.(refreshingDisplayElement).StartLoopDisplay(idx, kd.Display.Attributes) + return inst.(refreshingDisplayElement).StartLoopDisplay(ctx, idx, kd.Display.Attributes) } - return inst.(displayElement).Display(idx, kd.Display.Attributes) + return inst.(displayElement).Display(ctx, idx, kd.Display.Attributes) }