plasma: ESC P bitmap rows through the feedback endpoint (plasma row)

PlasmaCommands.GraphicsWrite/GraphicsRow port the display firmware graphics
command (ESC P s y x w h, MSB-left); PlasmaDisplay.RowAsync writes a locked
whole-row update; the line protocol gains `plasma row <y> <hex32>`. The
router plasma slot becomes a bounded FIFO queue: rows stream in order (a
frame must not tear), texts still coalesce to the newest, clear flushes, cap
128 with counted drops. Docs updated; 442 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-31 20:45:38 -05:00
co-authored by Claude Fable 5
parent 66c3cbdb57
commit 44b636ddd3
14 changed files with 435 additions and 41 deletions
+31 -12
View File
@@ -107,11 +107,13 @@ What the endpoint exposes (v1):
screen at once (e.g. callsign top line, score bottom line:
`plasma text 2 2 "VIPER 1-1"` + `plasma text 2 18 "SCORE 4200"`).
- **`plasma clear`** — blank the display.
- **`plasma row <y> <hex32>`** — one full 128-px bitmap row; see
[Bitmap graphics](#bitmap-graphics-esc-p).
Text is **Latin-1** (one byte per char) — don't send UTF-8.
Not exposed through the endpoint yet (small extensions when needed): explicit
font/attribute selection, box draw/fill, and the bitmap mode below.
font/attribute selection and box draw/fill.
### Bitmap graphics (`ESC P`)
@@ -134,20 +136,37 @@ game diffs and streams only changed rows — an animation that touches a few
rows per tick is smooth; full-frame repaints are ~1 fps. Text mode is far
cheaper for text; reserve bitmaps for logos, custom gauges, and icons.
Status: the hardware and the vPlasma emulator fully support `ESC P`, but
RIOJoy's `PlasmaCommands` port and the feedback endpoint don't expose it yet.
The natural extension is a line command (e.g.
`plasma row <y> <32-hex-digit row>` or a base64 block form) — if an
integration needs it, that plus a `PlasmaCommands.GraphicsWrite` builder is a
small, self-contained addition.
The endpoint exposes whole-row writes as a line command:
```
plasma row <y> <32 hex digits>
```
`y` is 031; the 32 hex digits are the row's 16 bytes left-to-right, MSB =
leftmost pixel. Rows stream strictly in arrival order (up to 128 queued;
overflow drops the incoming row and counts it), so push a frame as rows 031
and it lands intact. Example — a horizontal rule across row 16 and a lit
top-left corner block:
```
plasma row 16 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
plasma row 0 F0000000000000000000000000000000
```
For animation, keep a 1-bpp frame buffer client-side and send only the rows
that changed since the last tick — exactly what the native game does. The
partial-span form of `ESC P` (arbitrary `x/w/h`) exists in
`PlasmaCommands.GraphicsWrite` for host-side code but is not exposed as a
line command.
### Update semantics
Plasma writes are **single-flight, latest-pending-wins**: while one text is
being written, only the *newest* pending command survives. Flooding a score
update every frame is safe — the display shows the latest value — but
interleaving *two different fields* at high rate from one client can starve
one of them. Update fields on change, not on a timer.
Plasma writes are **single-flight over a bounded queue** whose rules follow
what each command means: flooded `text` updates coalesce to the newest value
(safe to spam a score — the display shows the latest), `clear` discards
everything queued before it, and bitmap `row`s stream strictly in order
(never coalesced — a frame is many rows). Still: update fields on change, not
on a timer.
Lifecycle: on profile activation the display clears and shows the profile's
`PlasmaGreeting` (if set); your first `plasma` command replaces it. On