Skip to content

Commit bfc0dee

Browse files
authored
Refactor handlers to simplified interface (#9)
* WIP * WIP * Add combo handlers, and split background image method * Fix tests * Update docs * Add tests
1 parent 611046f commit bfc0dee

9 files changed

Lines changed: 615 additions & 280 deletions

File tree

Module.go

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,20 @@ package api
33
type Module struct {
44
Name string `json:"name,omitempty"`
55

6-
NewIcon func() IconHandler `json:"-"`
7-
NewKey func() KeyHandler `json:"-"`
8-
NewLcd func() LcdHandler `json:"-"`
9-
NewKnobOrTouch func() KnobOrTouchHandler `json:"-"`
10-
NewKeyGridBackground func() KeyGridBackgroundHandler `json:"-"`
11-
NewTouchPanelBackgroundHandler func() TouchPanelBackgroundHandler `json:"-"`
6+
NewForeground func() ForegroundHandler `json:"-"`
7+
NewInput func() InputHandler `json:"-"`
8+
NewBackground func() BackgroundHandler `json:"-"`
129

13-
IconFields []Field `json:"icon_fields,omitempty"`
14-
KeyFields []Field `json:"key_fields,omitempty"`
15-
LcdFields []Field `json:"lcd_fields,omitempty"`
16-
KnobOrTouchFields []Field `json:"knob_or_touch_fields,omitempty"`
17-
LinkedFields []Field `json:"linked_fields,omitempty"`
18-
KeyGridBackgroundFields []Field `json:"key_grid_background_fields"`
19-
TouchPanelBackgroundFields []Field `json:"touch_panel_background_fields"`
10+
ForegroundFields []Field `json:"icon_fields,omitempty"`
11+
InputFields []Field `json:"key_fields,omitempty"`
12+
BackgroundFields []Field `json:"lcd_fields,omitempty"`
13+
LinkedFields []Field `json:"linked_fields,omitempty"`
2014

21-
IsIcon bool `json:"is_icon,omitempty"`
22-
IsKey bool `json:"is_key,omitempty"`
23-
IsLcd bool `json:"is_lcd,omitempty"`
24-
IsKnob bool `json:"is_knob,omitempty"`
25-
IsLinkedHandlers bool `json:"is_linked_handlers,omitempty"`
26-
IsKeyGridBackground bool `json:"is_key_grid_background"`
27-
IsTouchPanelBackground bool `json:"is_touch_panel_background"`
28-
Linked bool `json:"linked,omitempty"`
15+
IsForeground bool `json:"is_foreground,omitempty"`
16+
IsInput bool `json:"is_input,omitempty"`
17+
IsBackground bool `json:"is_background,omitempty"`
18+
IsLinkedHandlers bool `json:"is_linked_handlers,omitempty"`
19+
Linked bool `json:"linked,omitempty"`
2920
}
3021

3122
type FieldType string

README.md

Lines changed: 23 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -66,83 +66,51 @@ imgWithText, _ := api.DrawText(resizedImg, "Hello", 0, "CENTER")
6666
### Implementing handlers
6767

6868
```go
69-
// Implement a key handler
70-
type MyKeyHandler struct{}
71-
72-
func (h *MyKeyHandler) Key(key api.KeyConfigV3, info api.StreamDeckInfoV1) {
73-
// Handle key press
69+
// Implement a handler
70+
type MyHandler struct{
71+
running bool
7472
}
7573

76-
// Implement an icon handler
77-
type MyIconHandler struct {
78-
running bool
74+
func (h *MyHandler) Input(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, event InputEvent) {
75+
switch event.EventType {
76+
case api.KNOB_CW:
77+
// Handle clockwise rotation
78+
case api.KNOB_CCW:
79+
// Handle counter-clockwise rotation
80+
case api.KNOB_PRESS:
81+
// Handle knob press
82+
case api.KEY_PRESS:
83+
// Handle key press
84+
}
7985
}
8086

81-
func (h *MyIconHandler) Start(key api.KeyConfigV3, info api.StreamDeckInfoV1, callback func(image image.Image)) {
82-
h.running = true
83-
// Generate and update icon
84-
// Call callback with new images when needed
87+
func (h *MyHandler) Start(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, callback func(image image.Image)) {
88+
// Generate image and send it back via callback
8589
}
8690

87-
func (h *MyIconHandler) IsRunning() bool {
91+
func (h *MyHandler) IsRunning() bool {
8892
return h.running
8993
}
9094

91-
func (h *MyIconHandler) SetRunning(running bool) {
95+
func (h *MyHandler) SetRunning(running bool) {
9296
h.running = running
9397
}
9498

95-
func (h *MyIconHandler) Stop() {
99+
func (h *MyHandler) Stop() {
96100
h.running = false
97101
// Clean up resources and stop calling callback
98102
}
99-
100-
// Implement an LCD handler (for Stream Deck Plus)
101-
type MyLcdHandler struct {
102-
running bool
103-
}
104-
105-
func (h *MyLcdHandler) Start(knob api.KnobConfigV3, info api.StreamDeckInfoV1, callback func(image image.Image)) {
106-
h.running = true
107-
// Generate and update LCD image
108-
}
109-
110-
func (h *MyLcdHandler) IsRunning() bool {
111-
return h.running
112-
}
113-
114-
func (h *MyLcdHandler) SetRunning(running bool) {
115-
h.running = running
116-
}
117-
118-
func (h *MyLcdHandler) Stop() {
119-
h.running = false
120-
}
121-
122-
// Implement a knob or touch handler (for Stream Deck Plus)
123-
type MyKnobHandler struct{}
124-
125-
func (h *MyKnobHandler) Input(knob api.KnobConfigV3, info api.StreamDeckInfoV1, event api.InputEvent) {
126-
switch event.EventType {
127-
case api.KNOB_CW:
128-
// Handle clockwise rotation
129-
case api.KNOB_CCW:
130-
// Handle counter-clockwise rotation
131-
case api.KNOB_PRESS:
132-
// Handle knob press
133-
}
134-
}
135103
```
136104

137105
## API Documentation
138106

139107
The API provides several interfaces for handling Stream Deck interactions:
140108

141109
- `Handler`: Base interface for all handlers
142-
- `IconHandler`: For handling dynamic icons/images
143-
- `KeyHandler`: For handling key press events
144-
- `LcdHandler`: For handling LCD displays (Stream Deck Plus)
145-
- `KnobOrTouchHandler`: For handling knob rotations and touch events (Stream Deck Plus)
110+
- `ForegroundHandler`: For handling dynamic icons/images
111+
- `InputHandler`: For handling input events
112+
- `BackgroundHandler`: For handling dynamic backgrounds for individual displays, or whole deck
113+
- `CombinedHandler`: For handlers that can do foregrounds and handle input, if this handler is applied to the foreground and input of a specific key/lcd segment, the same instance of the struct will be used, so resources can be shared
146114

147115
Key components:
148116

config.go

Lines changed: 116 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,71 @@ package api
22

33
import (
44
"image"
5+
"math"
56
"time"
67
)
78

89
type LcdBackgrounder interface {
910
GetTouchPanelBackground() string
1011
GetTouchPanelBackgroundBuff() []image.Image
1112
SetTouchPanelBackgroundBuff(img []image.Image)
12-
GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler
13-
SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler)
13+
GetTouchPanelBackgroundHandler() BackgroundHandler
14+
SetTouchPanelBackgroundHandler(handler BackgroundHandler)
1415
GetTouchPanelBackgroundHandlerFields() map[string]any
1516
}
1617

1718
type KeyGridBackgrounder interface {
1819
GetKeyGridBackground() string
1920
GetKeyGridBackgroundBuff() []image.Image
2021
SetKeyGridBackgroundBuff(img []image.Image)
21-
GetKeyGridBackgroundHandler() KeyGridBackgroundHandler
22-
SetKeyGridBackgroundHandler(handler KeyGridBackgroundHandler)
22+
GetKeyGridBackgroundHandler() BackgroundHandler
23+
SetKeyGridBackgroundHandler(handler BackgroundHandler)
2324
GetKeyGridBackgroundHandlerFields() map[string]any
2425
}
2526

27+
type InputActions interface {
28+
GetSwitchPage() int
29+
GetKeyBind() string
30+
GetCommand() string
31+
GetBrightness() int
32+
GetUrl() string
33+
GetObsCommand() string
34+
GetObsCommandParams() map[string]string
35+
}
36+
37+
type ForegroundActions interface {
38+
GetIcon() string
39+
GetText() string
40+
GetTextSize() int
41+
GetTextAlignment() VerticalAlignment
42+
GetFontFace() string
43+
GetTextColour() string
44+
}
45+
2646
type ConfigV3 struct {
2747
Modules []string `json:"modules,omitempty"`
2848
Decks []DeckV3 `json:"decks"`
2949
ObsConnectionInfo ObsConnectionInfoV2 `json:"obs_connection_info,omitempty"`
3050
}
3151

3252
type DeckV3 struct {
33-
Serial string `json:"serial"`
34-
Pages []PageV3 `json:"pages"`
35-
TouchPanelBackground string `json:"touch_panel_background"`
36-
TouchPanelBackgroundBuff []image.Image `json:"-"`
37-
TouchPanelBackgroundHandler TouchPanelBackgroundHandler `json:"-"`
38-
TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"`
39-
KeyGridBackground string `json:"key_grid_background"`
40-
KeyGridBackgroundBuff []image.Image `json:"-"`
41-
KeyGridBackgroundHandler KeyGridBackgroundHandler `json:"-"`
42-
KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"`
43-
}
44-
45-
func (d *DeckV3) SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) {
53+
Serial string `json:"serial"`
54+
Pages []PageV3 `json:"pages"`
55+
TouchPanelBackground string `json:"touch_panel_background"`
56+
TouchPanelBackgroundBuff []image.Image `json:"-"`
57+
TouchPanelBackgroundHandler BackgroundHandler `json:"-"`
58+
TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"`
59+
KeyGridBackground string `json:"key_grid_background"`
60+
KeyGridBackgroundBuff []image.Image `json:"-"`
61+
KeyGridBackgroundHandler BackgroundHandler `json:"-"`
62+
KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"`
63+
}
64+
65+
func (d *DeckV3) SetTouchPanelBackgroundHandler(handler BackgroundHandler) {
4666
d.TouchPanelBackgroundHandler = handler
4767
}
4868

49-
func (d *DeckV3) GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler {
69+
func (d *DeckV3) GetTouchPanelBackgroundHandler() BackgroundHandler {
5070
return d.TouchPanelBackgroundHandler
5171
}
5272

@@ -78,11 +98,11 @@ func (d *DeckV3) SetKeyGridBackgroundBuff(img []image.Image) {
7898
d.KeyGridBackgroundBuff = img
7999
}
80100

81-
func (d *DeckV3) SetKeyGridBackgroundHandler(handler KeyGridBackgroundHandler) {
101+
func (d *DeckV3) SetKeyGridBackgroundHandler(handler BackgroundHandler) {
82102
d.KeyGridBackgroundHandler = handler
83103
}
84104

85-
func (d *DeckV3) GetKeyGridBackgroundHandler() KeyGridBackgroundHandler {
105+
func (d *DeckV3) GetKeyGridBackgroundHandler() BackgroundHandler {
86106
return d.KeyGridBackgroundHandler
87107
}
88108

@@ -91,23 +111,23 @@ func (d *DeckV3) GetKeyGridBackgroundHandlerFields() map[string]any {
91111
}
92112

93113
type PageV3 struct {
94-
Keys []KeyV3 `json:"keys"`
95-
Knobs []KnobV3 `json:"knobs"`
96-
TouchPanelBackground string `json:"touch_panel_background"`
97-
TouchPanelBackgroundBuff []image.Image `json:"-"`
98-
TouchPanelBackgroundHandler TouchPanelBackgroundHandler `json:"-"`
99-
TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"`
100-
KeyGridBackground string `json:"key_grid_background"`
101-
KeyGridBackgroundBuff []image.Image `json:"-"`
102-
KeyGridBackgroundHandler KeyGridBackgroundHandler `json:"-"`
103-
KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"`
104-
}
105-
106-
func (p *PageV3) SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) {
114+
Keys []KeyV3 `json:"keys"`
115+
Knobs []KnobV3 `json:"knobs"`
116+
TouchPanelBackground string `json:"touch_panel_background"`
117+
TouchPanelBackgroundBuff []image.Image `json:"-"`
118+
TouchPanelBackgroundHandler BackgroundHandler `json:"-"`
119+
TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"`
120+
KeyGridBackground string `json:"key_grid_background"`
121+
KeyGridBackgroundBuff []image.Image `json:"-"`
122+
KeyGridBackgroundHandler BackgroundHandler `json:"-"`
123+
KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"`
124+
}
125+
126+
func (p *PageV3) SetTouchPanelBackgroundHandler(handler BackgroundHandler) {
107127
p.TouchPanelBackgroundHandler = handler
108128
}
109129

110-
func (p *PageV3) GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler {
130+
func (p *PageV3) GetTouchPanelBackgroundHandler() BackgroundHandler {
111131
return p.TouchPanelBackgroundHandler
112132
}
113133

@@ -139,11 +159,11 @@ func (p *PageV3) SetKeyGridBackgroundBuff(img []image.Image) {
139159
p.KeyGridBackgroundBuff = img
140160
}
141161

142-
func (p *PageV3) SetKeyGridBackgroundHandler(handler KeyGridBackgroundHandler) {
162+
func (p *PageV3) SetKeyGridBackgroundHandler(handler BackgroundHandler) {
143163
p.KeyGridBackgroundHandler = handler
144164
}
145165

146-
func (p *PageV3) GetKeyGridBackgroundHandler() KeyGridBackgroundHandler {
166+
func (p *PageV3) GetKeyGridBackgroundHandler() BackgroundHandler {
147167
return p.KeyGridBackgroundHandler
148168
}
149169

@@ -152,21 +172,66 @@ func (p *PageV3) GetKeyGridBackgroundHandlerFields() map[string]any {
152172
}
153173

154174
type StreamDeckInfoV1 struct {
155-
Cols int `json:"cols,omitempty"`
156-
Rows int `json:"rows,omitempty"`
157-
IconSize int `json:"icon_size,omitempty"`
158-
Page int `json:"page"`
159-
Serial string `json:"serial,omitempty"`
160-
Name string `json:"name,omitempty"`
161-
Connected bool `json:"connected"`
162-
LastConnected time.Time `json:"last_connected,omitempty"`
163-
LastDisconnected time.Time `json:"last_disconnected,omitempty"`
164-
LcdWidth int `json:"lcd_width,omitempty"`
165-
LcdHeight int `json:"lcd_height,omitempty"`
166-
LcdCols int `json:"lcd_cols,omitempty"`
167-
KnobCols int `json:"knob_cols,omitempty"`
168-
PaddingX int `json:"padding_x"`
169-
PaddingY int `json:"padding_y"`
175+
Cols int `json:"cols,omitempty"`
176+
Rows int `json:"rows,omitempty"`
177+
IconSize int `json:"icon_size,omitempty"`
178+
Page int `json:"page"`
179+
Serial string `json:"serial,omitempty"`
180+
Name string `json:"name,omitempty"`
181+
Connected bool `json:"connected"`
182+
LastConnected time.Time `json:"last_connected,omitempty"`
183+
LastDisconnected time.Time `json:"last_disconnected,omitempty"`
184+
LcdWidth int `json:"lcd_width,omitempty"`
185+
LcdHeight int `json:"lcd_height,omitempty"`
186+
LcdCols int `json:"lcd_cols,omitempty"`
187+
KnobCols int `json:"knob_cols,omitempty"`
188+
PaddingX int `json:"padding_x"`
189+
PaddingY int `json:"padding_y"`
190+
KeyGridBackgroundWidth int `json:"key_grid_background_width"`
191+
KeyGridBackgroundHeight int `json:"key_grid_background_height"`
192+
LcdBackgroundWidth int `json:"lcd_background_width"`
193+
LcdBackgroundHeight int `json:"lcd_background_height"`
194+
}
195+
196+
func (info StreamDeckInfoV1) GetDimensions(handlerType HandlerType) (int, int) {
197+
if handlerType == LCD {
198+
return info.LcdWidth, info.LcdHeight
199+
}
200+
return info.IconSize, info.IconSize
201+
}
202+
203+
func (info StreamDeckInfoV1) GetGridDimensions(handlerType HandlerType) (int, int) {
204+
if handlerType == LCD {
205+
return info.LcdBackgroundWidth, info.LcdBackgroundHeight
206+
}
207+
return info.KeyGridBackgroundWidth, info.KeyGridBackgroundHeight
208+
}
209+
210+
func (info StreamDeckInfoV1) SplitBackgroundImage(background image.Image, handlerType HandlerType) []image.Image {
211+
var frameArr []image.Image
212+
213+
if handlerType == KEY {
214+
for keyIndex := range info.Cols * info.Rows {
215+
keyX := keyIndex % info.Cols
216+
keyY := int(math.Floor(float64(keyIndex) / float64(info.Cols)))
217+
218+
x0, y0 := keyX*(info.IconSize+info.PaddingX), keyY*(info.IconSize+info.PaddingY)
219+
x1, y1 := keyX*(info.IconSize+info.PaddingX)+info.IconSize, keyY*(info.IconSize+info.PaddingY)+info.IconSize
220+
221+
frameArr = append(frameArr, SubImage(background, x0, y0, x1, y1))
222+
}
223+
} else {
224+
for lcdIndex := range info.LcdCols {
225+
x0, y0 := info.LcdWidth*lcdIndex, 0
226+
x1, y1 := info.LcdWidth*(lcdIndex+1), info.LcdHeight
227+
228+
subImage := SubImage(background, x0, y0, x1, y1)
229+
230+
frameArr = append(frameArr, subImage)
231+
}
232+
}
233+
234+
return frameArr
170235
}
171236

172237
type ObsConnectionInfoV2 struct {

0 commit comments

Comments
 (0)