From e3436a553afb282ad1e6932f6a175b12f949c824 Mon Sep 17 00:00:00 2001 From: Justin Hawkins Date: Fri, 28 Jun 2024 17:51:57 +0930 Subject: [PATCH] Move things to different packages --- .../events_received.go | 19 +++++- events_send.go => events/events_send.go | 39 +++--------- streamdeck.go | 62 +++++++++---------- streamdesk_test.go | 11 ++-- tools/tools.go | 29 +++++++++ 5 files changed, 89 insertions(+), 71 deletions(-) rename events_received.go => events/events_received.go (97%) rename events_send.go => events/events_send.go (90%) create mode 100644 tools/tools.go diff --git a/events_received.go b/events/events_received.go similarity index 97% rename from events_received.go rename to events/events_received.go index ba6576e..55575a1 100644 --- a/events_received.go +++ b/events/events_received.go @@ -1,10 +1,27 @@ -package streamdeck +package events import ( "encoding/json" "reflect" ) +// ValidEventType returns a boolean indicating whether or not +// this is a valid event type +func ValidEventType(t reflect.Type) bool { + for i := range receivedEventTypeMap { + if receivedEventTypeMap[i] == t { + return true + } + } + return false +} + +// TypeForEvent returns the type for a particular event type string +func TypeForEvent(e string) (reflect.Type, bool) { + t, ok := receivedEventTypeMap[e] + return t, ok +} + var receivedEventTypeMap = map[string]reflect.Type{} func init() { diff --git a/events_send.go b/events/events_send.go similarity index 90% rename from events_send.go rename to events/events_send.go index e135ecc..dca2cf2 100644 --- a/events_send.go +++ b/events/events_send.go @@ -1,18 +1,14 @@ -package streamdeck +package events import ( - "bytes" - "encoding/base64" "encoding/json" - "image" - "image/png" ) type EventTarget int -const EventTargetBoth = 0 -const EventTargetHardware = 1 -const EventTargetSoftware = 2 +const EventTargetBoth = EventTarget(0) +const EventTargetHardware = EventTarget(1) +const EventTargetSoftware = EventTarget(2) type ESCommon struct { Event string `json:"event"` // name of this event type @@ -286,13 +282,13 @@ type ESSetStatePayload struct { State int `json:"state"` } -func NewESSetState(context string, state int) ESSetImage { - return ESSetImage{ +func NewESSetState(context string, state int) ESSetState { + return ESSetState{ ESCommon: ESCommon{ Event: "setState", Context: context, }, - Payload: ESSetImagePayload{ + Payload: ESSetStatePayload{ State: state, }, } @@ -362,24 +358,3 @@ func NewESSendToPlugin(context string, action string, payload json.RawMessage) E Payload: payload, } } - -/// --------- helpers ---------- - -// Turns an image.Image into a string suitable for delivering -// via a ESSetImage struct -func ImageToPayload(i image.Image) string { - - out := bytes.Buffer{} - b64 := base64.NewEncoder(base64.RawStdEncoding, &out) - err := png.Encode(b64, i) - if err != nil { - panic(err) - } - return "data:image/png;base64," + out.String() -} - -// Turns an image.Image into a string suitable for delivering -// via a ESSetImage struct -func SVGToPayload(context string, svg string) string { - return "data:image/svg+xml;charset=utf8," + svg -} diff --git a/streamdeck.go b/streamdeck.go index d507ef3..754e0d4 100644 --- a/streamdeck.go +++ b/streamdeck.go @@ -1,3 +1,5 @@ +// Package streamdeck interfaces with the Stream Deck plugin API, +// allowing you to create go-based plugins for the platform. package streamdeck import ( @@ -8,21 +10,16 @@ import ( "io" "reflect" + "github.com/tardisx/streamdeck-plugin/events" + "github.com/gorilla/websocket" ) +// these are automatically populated by parseFlags var flagPort int var flagEvent, flagInfo string var UUID string // the UUID this plugin is assigned -func init() { - flag.IntVar(&flagPort, "port", 0, "streamdeck sdk port") - flag.StringVar(&flagEvent, "registerEvent", "", "streamdeck sdk register event") - flag.StringVar(&flagInfo, "info", "", "streamdeck application info") - flag.StringVar(&UUID, "pluginUUID", "", "uuid") - flag.Parse() -} - type logger interface { Info(string, ...any) Error(string, ...any) @@ -35,11 +32,6 @@ func (nl nullLogger) Info(string, ...any) {} func (nl nullLogger) Error(string, ...any) {} func (nl nullLogger) Debug(string, ...any) {} -type EventHandler struct { - MsgType string - Handler func() -} - type Connection struct { ws *websocket.Conn logger logger @@ -48,9 +40,7 @@ type Connection struct { } // New creates a new struct for communication with the streamdeck -// plugin API. The command line flags required for the API to -// communicate with your plugin have already been parsed. -// The websocket will not connect until Connect is called. +// plugin API. The websocket will not connect until Connect is called. func New() Connection { return Connection{ handlers: make(map[reflect.Type]reflect.Value), @@ -67,6 +57,16 @@ func NewWithLogger(l logger) Connection { return c } +// parseFlags parses the command line flags to get the values provided +// by the Stream Deck plugin API. +func parseFlags() { + flag.IntVar(&flagPort, "port", 0, "streamdeck sdk port") + flag.StringVar(&flagEvent, "registerEvent", "", "streamdeck sdk register event") + flag.StringVar(&flagInfo, "info", "", "streamdeck application info") + flag.StringVar(&UUID, "pluginUUID", "", "uuid") + flag.Parse() +} + // Connect connects the plugin to the Stream Deck API via the websocket. // Once connected, events will be passed to handlers you have registered. // Handlers should thus be registered via RegisterHandler before calling @@ -75,14 +75,15 @@ func NewWithLogger(l logger) Connection { // then call WaitForPluginExit to block until the connection is closed. func (conn *Connection) Connect() error { + parseFlags() c, _, err := websocket.DefaultDialer.Dial(fmt.Sprintf("ws://localhost:%d", flagPort), nil) if err != nil { return err } conn.ws = c - msg := ESOpenMessage{ - ESCommonNoContext: ESCommonNoContext{ + msg := events.ESOpenMessage{ + ESCommonNoContext: events.ESCommonNoContext{ Event: flagEvent, }, UUID: UUID, @@ -103,8 +104,8 @@ func (conn *Connection) Connect() error { // WaitForPluginExit waits until the Stream Deck API closes // the websocket connection. -func (c *Connection) WaitForPluginExit() { - <-c.done +func (conn *Connection) WaitForPluginExit() { + <-conn.done } // RegisterHandler registers a function to be called for a particular event. The @@ -114,7 +115,7 @@ func (c *Connection) WaitForPluginExit() { // function per event type. This function will panic if the wrong kind of // function is passed in, or if you try to register more than one for a single // event type. -func (r *Connection) RegisterHandler(handler any) { +func (conn *Connection) RegisterHandler(handler any) { hType := reflect.TypeOf(handler) if hType.Kind() != reflect.Func { panic("handler must be a function") @@ -127,27 +128,20 @@ func (r *Connection) RegisterHandler(handler any) { argType := hType.In(0) // check its a valid one (one that matches an event type) - valid := false - for i := range receivedEventTypeMap { - if receivedEventTypeMap[i] == argType { - valid = true - break - } - } - if !valid { + if !events.ValidEventType(argType) { panic("you cannot register a handler with this argument type") } - _, alreadyExists := r.handlers[argType] + _, alreadyExists := conn.handlers[argType] if alreadyExists { panic("handler for " + argType.Name() + " already exists") } - r.handlers[argType] = reflect.ValueOf(handler) + conn.handlers[argType] = reflect.ValueOf(handler) } // Send sends a message to the API. It should be one of the -// ES* structs, such as ESOpenURL. +// events.ES* structs, such as events.ESOpenURL. func (conn *Connection) Send(e any) error { b, _ := json.Marshal(e) conn.logger.Debug(fmt.Sprintf("sending: %s", string(b))) @@ -185,14 +179,14 @@ func (conn *Connection) reader() { b := bytes.Buffer{} r = io.TeeReader(r, &b) - base := ERBase{} + base := events.ERBase{} err = json.NewDecoder(r).Decode(&base) if err != nil { conn.logger.Error("cannot decode: " + err.Error()) continue } - t, ok := receivedEventTypeMap[base.Event] + t, ok := events.TypeForEvent(base.Event) if !ok { conn.logger.Error(fmt.Sprintf("no type registered for event '%s'", base.Event)) continue diff --git a/streamdesk_test.go b/streamdesk_test.go index bf2f2cd..4606673 100644 --- a/streamdesk_test.go +++ b/streamdesk_test.go @@ -2,6 +2,8 @@ package streamdeck import ( "testing" + + "github.com/tardisx/streamdeck-plugin/events" ) type testLogger struct { @@ -16,10 +18,10 @@ func TestReflection(t *testing.T) { c := NewWithLogger(testLogger{t: t}) // incoming - in := ERDidReceiveSettingsPayload{} + in := events.ERDidReceiveSettingsPayload{} ranHandler := false - c.RegisterHandler(func(event ERDidReceiveSettingsPayload) { + c.RegisterHandler(func(event events.ERDidReceiveSettingsPayload) { ranHandler = true }) @@ -52,13 +54,14 @@ func TestUmmarshal(t *testing.T) { }`) c := NewWithLogger(testLogger{t: t}) - keyUp, err := c.unmarshalToConcrete(receivedEventTypeMap["keyUp"], b) + e, _ := events.TypeForEvent("keyUp") + keyUp, err := c.unmarshalToConcrete(e, b) if err != nil { t.Error(err) } - realKeyUp, ok := keyUp.(ERKeyUp) + realKeyUp, ok := keyUp.(events.ERKeyUp) if !ok { t.Errorf("wrong type (is %T)", keyUp) } diff --git a/tools/tools.go b/tools/tools.go new file mode 100644 index 0000000..09e4921 --- /dev/null +++ b/tools/tools.go @@ -0,0 +1,29 @@ +// Package tools provides some helper functions to assist with +// creating Stream Deck plugins +package tools + +import ( + "bytes" + "encoding/base64" + "image" + "image/png" +) + +// Turns an image.Image into a string suitable for delivering +// via an events.ESSetImage struct +func ImageToPayload(i image.Image) string { + + out := bytes.Buffer{} + b64 := base64.NewEncoder(base64.RawStdEncoding, &out) + err := png.Encode(b64, i) + if err != nil { + panic(err) + } + return "data:image/png;base64," + out.String() +} + +// SVGToPayload create the string necessary to send an SVG +// via a ESSetImage struct +func SVGToPayload(context string, svg string) string { + return "data:image/svg+xml;charset=utf8," + svg +}