diff --git a/compiler/src/specgen.ts b/compiler/src/specgen.ts index d317199cb5..d24a7c4c30 100644 --- a/compiler/src/specgen.ts +++ b/compiler/src/specgen.ts @@ -505,7 +505,14 @@ ${varname}.${sig} const regs = info.packets .filter(pkt => isRegister(pkt.kind)) .filter(pkt => !pkt.derived && !pkt.internal && !pkt.lowLevel) - if (regs?.length) r.push("## Registers", "") + if (regs?.length) { + r.push( + "## Registers", + "", + `{@import optional ../clients-custom/${info.shortId}-registers.mdp}`, + "" + ) + } regs.forEach(pkt => { const cmt = addComment(pkt) const nobuild = status === "stable" && !pkt.client ? "" : "skip" diff --git a/packages/core/src/events.ts b/packages/core/src/events.ts index 5f1aebfb90..b48db5f235 100644 --- a/packages/core/src/events.ts +++ b/packages/core/src/events.ts @@ -72,10 +72,8 @@ class ClientRegister implements ds.ClientRegister { } emit(newValue: T) { - if (this.value !== newValue) { - this.value = newValue - this.emitter?.emit(this.value) - } + this.value = newValue + this.emitter?.emit(this.value) } } diff --git a/packages/core/src/gamepad.ts b/packages/core/src/gamepad.ts new file mode 100644 index 0000000000..408c9edf65 --- /dev/null +++ b/packages/core/src/gamepad.ts @@ -0,0 +1,36 @@ +import * as ds from "@devicescript/core" + +declare module "@devicescript/core" { + interface Gamepad { + /** + * The thumbstick position register if any + */ + axes(): ds.ClientRegister<{ x: number; y: number }> + + /** + * Button (or combo) register + */ + button(value: ds.GamepadButtons): ds.ClientRegister + } +} + +ds.Gamepad.prototype.axes = function axes() { + let r = (this as any).__axes as ds.ClientRegister<{ x: number; y: number }> + if (!r) { + ;(this as any).__axes = r = ds.clientRegister<{ x: number; y: number }>( + { x: 0, y: 0 } + ) + this.reading.subscribe(rv => r.emit({ x: rv[1], y: rv[2] })) + } + return r +} + +ds.Gamepad.prototype.button = function button(value: ds.GamepadButtons) { + const key = `__${value}` + let r = (this as any)[key] as ds.ClientRegister + if (!r) { + ;(this as any)[key] = r = ds.clientRegister(false) + this.reading.subscribe(rv => r.emit((rv[0] & value) === value)) + } + return r +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 84e3d9bffc..5374d219a2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,4 +14,5 @@ import "./rotaryencoder" import "./button" import "./magneticfieldlevel" import "./buzzer" -import "./ledstrip" \ No newline at end of file +import "./ledstrip" +import "./gamepad" \ No newline at end of file diff --git a/packages/sampleprj/src/maingamepad.ts b/packages/sampleprj/src/maingamepad.ts new file mode 100644 index 0000000000..f64a30db7b --- /dev/null +++ b/packages/sampleprj/src/maingamepad.ts @@ -0,0 +1,11 @@ +import { Gamepad, GamepadButtons } from "@devicescript/core" + +const gamepad = new Gamepad() + +const axes = gamepad.axes() +axes.subscribe(({ x, y }) => console.log({ x, y })) + +const A = gamepad.button(GamepadButtons.A) +A.subscribe(a => console.log({ a })) +const B = gamepad.button(GamepadButtons.B) +B.subscribe(b => console.log({ b })) diff --git a/website/docs/api/clients-custom/gamepad-registers.mdp b/website/docs/api/clients-custom/gamepad-registers.mdp new file mode 100644 index 0000000000..e6aee4b77a --- /dev/null +++ b/website/docs/api/clients-custom/gamepad-registers.mdp @@ -0,0 +1,32 @@ +### axes {#ro:axes} + +An array representing the controls with axes present on the device (e.g. analog thumb sticks), +as `[x, y]`. Each entry in the array is a floating point value in the range ` -1.0 – 1.0``, representing the axis position from the lowest value ( `-1.0`) to the highest value (`1.0`). + +- type: `ClientRegister<{ x: number; y: number }>` (packing format `i1.15 i1.15`) + +```ts +import { Gamepad } from "@devicescript/core" + +const gamepad = new Gamepad() +// ... +gamepad.axes.subscribe(async ({ x, y }) => { + console.log({ x, y }) +}) +``` + +### button {#ro:button} + +A client register for the requested button or combination of buttons. The value is `true` if the button is pressed, `false` otherwise. + +- type: `ClientRegister` + +```ts +import { Gamepad, GamepadButtons } from "@devicescript/core" + +const gamepad = new Gamepad() +// ... +gamepad.button(GamepadButtons.Down).subscribe(async pressed => { + console.log({ pressed }) +}) +```