OpenRGB backend for non-ZSA keyboards #5

Merged
jwarykowski merged 2 commits from openrgb into main 2026-10-14 03:26:58 +00:00
jwarykowski commented 2026-10-11 10:39:18 +00:00 (Migrated from github.com)

Why

keybars only drove ZSA boards through Keymapp. OpenRGB's SDK server reaches most per-key RGB keyboards (Razer, Corsair, Logitech, SteelSeries, ASUS, QMK with OpenRGB support and more), and most of its keyboards come with a key matrix, so no map needs writing.

What

  • -backend openrgb (plus -openrgb ADDR, default 127.0.0.1:6742, and -device NAME) on every command that connects. It picks the first keyboard unless -device names one.
  • pkg/openrgb: an SDK client.
    • Asks for protocol version 1, the oldest with vendor names, so the description has no brightness or segment fields. Falls back to 0 when a server predates the handshake.
    • Skips unsolicited device-list-update packets while waiting for replies.
    • Enters the per-LED mode (SETCUSTOMMODE) on the first Set.
    • Buffers colours and sends one UPDATELEDS per frame. Many OpenRGB drivers update the whole device for each single-LED packet, so per-LED packets would be slower.
    • RestoreAll sends back the previous mode exactly as described (UPDATEMODE), and its colours if that mode was per-LED.
  • Maps: taken from the device's largest matrix zone. keybars maps -backend openrgb lists devices and prints the map to save and edit. keybars find -backend openrgb starts at 0 and prints each LED's name (LED 3 (Key: Q)). -model is refused, since built-in maps use Keymapp's numbering.
  • pkg/rgb: Colour and LEDs move out of keymapp (aliased there, so bars is untouched apart from the flush), plus an optional Flusher that the renderer calls once per frame that changed something.

Testing

  • testdata/controller_v1.bin is a keyboard description made by testdata/fixture.py (3×4 matrix with gaps, a logo zone, Direct, Static and Rainbow modes). It is checked by openrgb-python's own parser, and openrgb-python's UPDATEMODE encoding of the mode matches ours byte for byte.
  • A fake SDK server test covers the handshake, skipping device list updates, choosing a device, SETCUSTOMMODE then one UPDATELEDS on flush, restoring a hardware effect versus a per-LED mode, an old server with no version reply, and truncated descriptions. A mutation check confirmed that dropping the reply-skipping or the colour carry-over fails the tests.
  • End to end, the built binary ran against a Python fake server: maps printed the matrix, a paced -stdin run lit the expected keys with one UPDATELEDS per frame and restored the mode after -idle, and find and the error paths behaved.
  • Merged with main (public pkg/ packages, config file, start/stop, -source): go test -race ./... passes on Go 1.26, and CI passes.

Not verified

  • Not yet run on a real keyboard or real OpenRGB. GitLab, where OpenRGB lives, was blocked from the session, so the protocol comes from openrgb-python, not the server's source.
  • The one field I couldn't pin down is the zone's matrix length. Like openrgb-python, the code only checks it for zero and reads height × width.
  • Frame rate depends on the driver: one UPDATELEDS per frame is the best the SDK offers.
  • The device is addressed by its index from when keybars connected. If OpenRGB rescans its devices mid-run (a device plugged in or out), the index can point elsewhere until keybars restarts.
## Why keybars only drove ZSA boards through Keymapp. OpenRGB's SDK server reaches most per-key RGB keyboards (Razer, Corsair, Logitech, SteelSeries, ASUS, QMK with OpenRGB support and more), and most of its keyboards come with a key matrix, so no map needs writing. ## What - **`-backend openrgb`** (plus `-openrgb ADDR`, default `127.0.0.1:6742`, and `-device NAME`) on every command that connects. It picks the first keyboard unless `-device` names one. - **`pkg/openrgb`**: an SDK client. - Asks for protocol version 1, the oldest with vendor names, so the description has no brightness or segment fields. Falls back to 0 when a server predates the handshake. - Skips unsolicited device-list-update packets while waiting for replies. - Enters the per-LED mode (SETCUSTOMMODE) on the first `Set`. - Buffers colours and sends **one UPDATELEDS per frame**. Many OpenRGB drivers update the whole device for each single-LED packet, so per-LED packets would be slower. - `RestoreAll` sends back the previous mode exactly as described (UPDATEMODE), and its colours if that mode was per-LED. - **Maps**: taken from the device's largest matrix zone. `keybars maps -backend openrgb` lists devices and prints the map to save and edit. `keybars find -backend openrgb` starts at 0 and prints each LED's name (`LED 3 (Key: Q)`). `-model` is refused, since built-in maps use Keymapp's numbering. - **`pkg/rgb`**: `Colour` and `LEDs` move out of `keymapp` (aliased there, so `bars` is untouched apart from the flush), plus an optional `Flusher` that the renderer calls once per frame that changed something. ## Testing - `testdata/controller_v1.bin` is a keyboard description made by `testdata/fixture.py` (3×4 matrix with gaps, a logo zone, Direct, Static and Rainbow modes). It is **checked by openrgb-python's own parser**, and openrgb-python's UPDATEMODE encoding of the mode matches ours byte for byte. - A fake SDK server test covers the handshake, skipping device list updates, choosing a device, SETCUSTOMMODE then one UPDATELEDS on flush, restoring a hardware effect versus a per-LED mode, an old server with no version reply, and truncated descriptions. A mutation check confirmed that dropping the reply-skipping or the colour carry-over fails the tests. - End to end, the built binary ran against a Python fake server: `maps` printed the matrix, a paced `-stdin` run lit the expected keys with one UPDATELEDS per frame and restored the mode after `-idle`, and `find` and the error paths behaved. - Merged with main (public `pkg/` packages, config file, start/stop, `-source`): `go test -race ./...` passes on Go 1.26, and CI passes. ## Not verified - **Not yet run on a real keyboard or real OpenRGB.** GitLab, where OpenRGB lives, was blocked from the session, so the protocol comes from openrgb-python, not the server's source. - The one field I couldn't pin down is the zone's matrix length. Like openrgb-python, the code only checks it for zero and reads height × width. - Frame rate depends on the driver: one UPDATELEDS per frame is the best the SDK offers. - The device is addressed by its index from when keybars connected. If OpenRGB rescans its devices mid-run (a device plugged in or out), the index can point elsewhere until keybars restarts.
Sign in to join this conversation.
No description provided.