Skip to content

Commit 7fca009

Browse files
committed
fix: refactor input services
1 parent 7544814 commit 7fca009

File tree

11 files changed

+171
-160
lines changed

11 files changed

+171
-160
lines changed

packages/core/src/__tests__/test.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ describe('StreamDeck', () => {
415415
})
416416
})
417417

418-
test('down and up events', () => {
418+
test.only('down and up events', () => {
419419
const downSpy = jest.fn()
420420
const upSpy = jest.fn()
421421
streamDeck.on('down', downSpy)

packages/core/src/models/base.ts

+6-50
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ import type { ButtonsLcdDisplayService } from '../services/buttonsLcdDisplay/int
1212
import type { StreamDeckButtonControlDefinition, StreamDeckControlDefinition } from '../controlDefinition.js'
1313
import type { LcdStripDisplayService } from '../services/lcdStripDisplay/interface.js'
1414
import type { PropertiesService } from '../services/properties/interface.js'
15-
import type { EncoderInputService } from '../services/encoderInput.js'
16-
import type { LcdStripInputService } from '../services/lcdStripInput.js'
1715
import type { CallbackHook } from '../services/callback-hook.js'
16+
import type { StreamDeckInputService } from '../services/input/interface.js'
1817

1918
export type EncodeJPEGHelper = (buffer: Uint8Array, width: number, height: number) => Promise<Uint8Array>
2019

@@ -46,12 +45,11 @@ export type StreamDeckProperties = Readonly<{
4645

4746
export interface StreamDeckServicesDefinition {
4847
deviceProperties: StreamDeckProperties
49-
events: CallbackHook<StreamDeckEvents> | null
48+
events: CallbackHook<StreamDeckEvents>
5049
properties: PropertiesService
5150
buttonsLcd: ButtonsLcdDisplayService
51+
inputService: StreamDeckInputService
5252
lcdStripDisplay: LcdStripDisplayService | null
53-
lcdStripInput: LcdStripInputService | null
54-
encoderInput: EncoderInputService | null
5553
}
5654

5755
export class StreamDeckBase extends EventEmitter<StreamDeckEvents> implements StreamDeck {
@@ -88,10 +86,8 @@ export class StreamDeckBase extends EventEmitter<StreamDeckEvents> implements St
8886
readonly #propertiesService: PropertiesService
8987
readonly #buttonsLcdService: ButtonsLcdDisplayService
9088
readonly #lcdStripDisplayService: LcdStripDisplayService | null
91-
readonly #lcdStripInputService: LcdStripInputService | null
92-
readonly #encoderInputService: EncoderInputService | null
89+
readonly #inputService: StreamDeckInputService
9390
// private readonly options: Readonly<OpenStreamDeckOptions>
94-
readonly #keyState: boolean[]
9591

9692
constructor(device: HIDDevice, _options: OpenStreamDeckOptions, services: StreamDeckServicesDefinition) {
9793
super()
@@ -101,58 +97,18 @@ export class StreamDeckBase extends EventEmitter<StreamDeckEvents> implements St
10197
this.#propertiesService = services.properties
10298
this.#buttonsLcdService = services.buttonsLcd
10399
this.#lcdStripDisplayService = services.lcdStripDisplay
104-
this.#lcdStripInputService = services.lcdStripInput
105-
this.#encoderInputService = services.encoderInput
100+
this.#inputService = services.inputService
106101

107102
// propogate events
108103
services.events?.listen((key, ...args) => this.emit(key, ...args))
109104

110-
const maxButtonIndex = this.deviceProperties.CONTROLS.filter(
111-
(control): control is StreamDeckButtonControlDefinition => control.type === 'button',
112-
).map((control) => control.index)
113-
this.#keyState = new Array<boolean>(Math.max(-1, ...maxButtonIndex) + 1).fill(false)
114-
115-
this.device.on('input', (data: Uint8Array) => this.#handleInputBuffer(data))
105+
this.device.on('input', (data: Uint8Array) => this.#inputService.handleInput(data))
116106

117107
this.device.on('error', (err) => {
118108
this.emit('error', err)
119109
})
120110
}
121111

122-
#handleInputBuffer(data: Uint8Array): void {
123-
const inputType = data[0]
124-
switch (inputType) {
125-
case 0x00: // Button
126-
this.#handleButtonInputBuffer(data)
127-
break
128-
case 0x02: // LCD
129-
this.#lcdStripInputService?.handleInput(data)
130-
break
131-
case 0x03: // Encoder
132-
this.#encoderInputService?.handleInput(data)
133-
break
134-
}
135-
}
136-
137-
#handleButtonInputBuffer(data: Uint8Array): void {
138-
const dataOffset = this.deviceProperties.KEY_DATA_OFFSET || 0
139-
140-
for (const control of this.deviceProperties.CONTROLS) {
141-
if (control.type !== 'button') continue
142-
143-
const keyPressed = Boolean(data[dataOffset + control.hidIndex])
144-
const stateChanged = keyPressed !== this.#keyState[control.index]
145-
if (stateChanged) {
146-
this.#keyState[control.index] = keyPressed
147-
if (keyPressed) {
148-
this.emit('down', control)
149-
} else {
150-
this.emit('up', control)
151-
}
152-
}
153-
}
154-
}
155-
156112
protected checkValidKeyIndex(
157113
keyIndex: KeyIndex,
158114
feedbackType: StreamDeckButtonControlDefinition['feedbackType'] | null,

packages/core/src/models/generic-gen1.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import type { FillImageTargetOptions } from '../util.js'
66
import { Gen1PropertiesService } from '../services/properties/gen1.js'
77
import { DefaultButtonsLcdService } from '../services/buttonsLcdDisplay/default.js'
88
import { BitmapButtonLcdImagePacker } from '../services/imagePacker/bitmap.js'
9+
import { CallbackHook } from '../services/callback-hook.js'
10+
import type { StreamDeckEvents } from '../types.js'
11+
import { ButtonOnlyInputService } from '../services/input/gen1.js'
912

1013
function extendDevicePropertiesForGen1(rawProps: StreamDeckGen1Properties): StreamDeckProperties {
1114
return {
@@ -26,9 +29,11 @@ export function StreamDeckGen1Factory(
2629
): StreamDeckBase {
2730
const fullProperties = extendDevicePropertiesForGen1(properties)
2831

32+
const events = new CallbackHook<StreamDeckEvents>()
33+
2934
return new StreamDeckBase(device, options, {
3035
deviceProperties: fullProperties,
31-
events: null,
36+
events,
3237
properties: new Gen1PropertiesService(device),
3338
buttonsLcd: new DefaultButtonsLcdService(
3439
imageWriter,
@@ -42,7 +47,6 @@ export function StreamDeckGen1Factory(
4247
fullProperties,
4348
),
4449
lcdStripDisplay: null,
45-
lcdStripInput: null,
46-
encoderInput: null,
50+
inputService: new ButtonOnlyInputService(fullProperties, events),
4751
})
4852
}

packages/core/src/models/generic-gen2.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import type { HIDDevice } from '../hid-device.js'
22
import type { OpenStreamDeckOptions, StreamDeckProperties, StreamDeckServicesDefinition } from './base.js'
33
import { StreamdeckDefaultImageWriter } from '../services/imageWriter/imageWriter.js'
44
import { StreamdeckGen2ImageHeaderGenerator } from '../services/imageWriter/headerGenerator.js'
5-
import { EncoderInputService } from '../services/encoderInput.js'
65
import { DefaultButtonsLcdService } from '../services/buttonsLcdDisplay/default.js'
76
import { CallbackHook } from '../services/callback-hook.js'
87
import type { StreamDeckEvents } from '../types.js'
98
import { Gen2PropertiesService } from '../services/properties/gen2.js'
109
import { JpegButtonLcdImagePacker } from '../services/imagePacker/jpeg.js'
10+
import { Gen2InputService } from '../services/input/gen2.js'
1111

1212
function extendDevicePropertiesForGen2(rawProps: StreamDeckGen2Properties): StreamDeckProperties {
1313
return {
@@ -24,10 +24,7 @@ export function createBaseGen2Properties(
2424
options: Required<OpenStreamDeckOptions>,
2525
properties: StreamDeckGen2Properties,
2626
disableXYFlip?: boolean,
27-
): StreamDeckServicesDefinition & {
28-
// Always defined for Gen2
29-
events: CallbackHook<StreamDeckEvents>
30-
} {
27+
): StreamDeckServicesDefinition {
3128
const fullProperties = extendDevicePropertiesForGen2(properties)
3229

3330
const events = new CallbackHook<StreamDeckEvents>()
@@ -48,7 +45,6 @@ export function createBaseGen2Properties(
4845
fullProperties,
4946
),
5047
lcdStripDisplay: null,
51-
lcdStripInput: null,
52-
encoderInput: new EncoderInputService(events, properties.CONTROLS),
48+
inputService: new Gen2InputService(fullProperties, events),
5349
}
5450
}

packages/core/src/models/pedal.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import type { StreamDeckControlDefinition } from '../controlDefinition.js'
66
import { freezeDefinitions } from '../controlsGenerator.js'
77
import { PedalPropertiesService } from '../services/properties/pedal.js'
88
import { PedalLcdService } from '../services/buttonsLcdDisplay/pedal.js'
9+
import type { StreamDeckEvents } from '../types.js'
10+
import { CallbackHook } from '../services/callback-hook.js'
11+
import { ButtonOnlyInputService } from '../services/input/gen1.js'
912

1013
const pedalControls: StreamDeckControlDefinition[] = [
1114
{
@@ -49,13 +52,14 @@ const pedalProperties: StreamDeckProperties = {
4952
}
5053

5154
export function StreamDeckPedalFactory(device: HIDDevice, options: Required<OpenStreamDeckOptions>): StreamDeckBase {
55+
const events = new CallbackHook<StreamDeckEvents>()
56+
5257
return new StreamDeckBase(device, options, {
5358
deviceProperties: pedalProperties,
54-
events: null,
59+
events,
5560
properties: new PedalPropertiesService(device),
5661
buttonsLcd: new PedalLcdService(),
5762
lcdStripDisplay: null,
58-
lcdStripInput: null,
59-
encoderInput: null,
63+
inputService: new ButtonOnlyInputService(pedalProperties, events),
6064
})
6165
}

packages/core/src/models/plus.ts

-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { createBaseGen2Properties } from './generic-gen2.js'
66
import { DeviceModelId } from '../id.js'
77
import { freezeDefinitions, generateButtonsGrid } from '../controlsGenerator.js'
88
import type { StreamDeckControlDefinition, StreamDeckLcdStripControlDefinition } from '../controlDefinition.js'
9-
import { LcdStripInputService } from '../services/lcdStripInput.js'
109
import { StreamDeckPlusLcdService } from '../services/lcdStripDisplay/plus.js'
1110

1211
const plusControls: StreamDeckControlDefinition[] = generateButtonsGrid(4, 2)
@@ -74,7 +73,6 @@ const lcdStripControls = plusProperties.CONTROLS.filter(
7473
export function StreamDeckPlusFactory(device: HIDDevice, options: Required<OpenStreamDeckOptions>): StreamDeckBase {
7574
const services = createBaseGen2Properties(device, options, plusProperties, true)
7675
services.lcdStripDisplay = new StreamDeckPlusLcdService(options.encodeJPEG, device, lcdStripControls)
77-
services.lcdStripInput = new LcdStripInputService(lcdStripControls, services.events)
7876

7977
return new StreamDeckBase(device, options, services)
8078
}

packages/core/src/services/encoderInput.ts

-46
This file was deleted.
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { StreamDeckProperties } from '../../models/base.js'
2+
import type { StreamDeckInputService } from './interface.js'
3+
import type { StreamDeckEvents } from '../../types.js'
4+
import type { CallbackHook } from '../callback-hook.js'
5+
import type { StreamDeckButtonControlDefinition } from '../../controlDefinition.js'
6+
7+
export class ButtonOnlyInputService implements StreamDeckInputService {
8+
readonly #deviceProperties: Readonly<StreamDeckProperties>
9+
readonly #keyState: boolean[]
10+
readonly #eventSource: CallbackHook<StreamDeckEvents>
11+
12+
constructor(deviceProperties: Readonly<StreamDeckProperties>, eventSource: CallbackHook<StreamDeckEvents>) {
13+
this.#deviceProperties = deviceProperties
14+
this.#eventSource = eventSource
15+
16+
const maxButtonIndex = this.#deviceProperties.CONTROLS.filter(
17+
(control): control is StreamDeckButtonControlDefinition => control.type === 'button',
18+
).map((control) => control.index)
19+
this.#keyState = new Array<boolean>(Math.max(-1, ...maxButtonIndex) + 1).fill(false)
20+
}
21+
22+
handleInput(data: Uint8Array): void {
23+
const dataOffset = this.#deviceProperties.KEY_DATA_OFFSET || 0
24+
25+
for (const control of this.#deviceProperties.CONTROLS) {
26+
if (control.type !== 'button') continue
27+
28+
const keyPressed = Boolean(data[dataOffset + control.hidIndex])
29+
const stateChanged = keyPressed !== this.#keyState[control.index]
30+
if (stateChanged) {
31+
this.#keyState[control.index] = keyPressed
32+
if (keyPressed) {
33+
this.#eventSource.emit('down', control)
34+
} else {
35+
this.#eventSource.emit('up', control)
36+
}
37+
}
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)