Skip to content

Overlay #106: gesture-free serials via node-hid enumeration in main#107

Draft
petegordon wants to merge 3 commits into
mainfrom
feat/106-node-hid-main
Draft

Overlay #106: gesture-free serials via node-hid enumeration in main#107
petegordon wants to merge 3 commits into
mainfrom
feat/106-node-hid-main

Conversation

@petegordon

Copy link
Copy Markdown
Member

Implements win #1 of #106: enumerate HID in the Electron main process via node-hid, so serials populate at launch with no user gesture — fixing the #104 auto-opened list showing blank serials (the renderer's WebHID requestDevice scan needs transient activation, which the auto-open lacks).

What it does

  • Guarded require('node-hid') in main. If it fails to load for this Electron ABI it stays null and we degrade to the existing Electron HID-event inventory — no regression.
  • enumerateHidViaNodeHid() reads HID.devices() (metadata only — no open, so no contention with the renderer's WebHID handles), filters to controller VIDs, and rebuilds the existing hidControllers inventory. Runs at launch + polls every 2s for hot-plug; broadcasts only on change.
  • hidKey now prefers vid:pid:serial so a multi-interface device collapses to one row — the Steam Puck exposes ~11 HID interfaces sharing one serial.
  • Skips productId === 0 / nameless vendor-shared junk (e.g. Microsoft 045e keyboards/mice).
  • forge.config.js: adds @electron-forge/plugin-auto-unpack-natives (so the .node binary loads from the packaged asar); npm run rebuild helper + @electron/rebuild dev dep for local dev.

Feasibility — verified under Node on this Windows machine

node-hid installed from a prebuilt in 2s (no compiler) and enumerated the real pads gesture-free with matching serials:

deduped physical controllers: 3
   28de:1304 | Steam Controller Puck | FXB9960202571
   54c:5c4   | Wireless Controller   | (none)
   57e:2009  | Wireless Gamepad      | 98e255badd18

(the Puck's 11 interfaces collapsed to 1; serials match the earlier gesture-scan values).

Verify (Electron runtime + package) — draft until confirmed

cd apps/overlay
npm install
npm run rebuild      # build node-hid for Electron 33 (may be unnecessary if the N-API prebuilt loads directly)
npm start
  • Open the controllers list without clicking anything first → serials should already be present (Steam/Switch), no gesture.
  • Main log should show node-hid loaded — gesture-free serial enumeration active and node-hid enumeration: N controller(s).
  • Then npm run make and confirm the packaged app still loads node-hid (auto-unpack-natives).

Scope

Delivers gesture-free serials (win #1). Per-unit same-transport auto-select (win #2) still needs the renderer WebHID-handle → node-hid-path bridge (or moving report I/O to main) — left as the follow-up noted in #106.

Refs #106, #104, #105.

The renderer's WebHID serial scan needs a user gesture (Blink), so the #104
auto-opened list showed no serials. node-hid enumerates in MAIN — per-device
path + serialNumber, read-only (no open, no contention with WebHID handles), and
NO gesture. Populates the existing hidControllers inventory at launch + polls
for hot-plug; broadcasts only on change.

- Guarded require: if the native module isn't built for this Electron ABI it
  stays null and we fall back to the Electron HID-event inventory (no regression).
- hidKey now prefers vid:pid:serial so a multi-interface device collapses to one
  entry (the Steam Puck exposes ~11 interfaces sharing one serial).
- Skips productId-0 / nameless vendor-shared junk (e.g. Microsoft 045e HID).
- forge: @electron-forge/plugin-auto-unpack-natives so the .node binary is
  loadable from the packaged asar; `npm run rebuild` helper for local dev.

FEASIBILITY VERIFIED under Node on this Windows machine: node-hid installed from
a prebuilt (2s, no compiler) and enumerated the real pads gesture-free with
matching serials — Steam FXB9960202571, Switch 98e255badd18, DS4 (none) — and the
dedup collapsed the Puck's 11 interfaces to 1. NEEDS verify under Electron 33
runtime + packaged build. This delivers win #1 (gesture-free serials); per-unit
same-transport auto-select (win #2) still needs the handle->path bridge — noted.
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🔎 PR #107 preview — test it in isolation

🌐 Web overlay: https://lab.usersfirst.games/pr-preview/pr-107/overlay/
(landing page: https://lab.usersfirst.games/pr-preview/pr-107/)

💻 Desktop installers (prerelease): https://github.com/UsersFirst/tandemonium-controller-lab/releases/tag/pr-107

  • Windows…-Setup.exe (installer) or .zip (portable)
  • macOSWebHID-Controller-Overlay.dmg or .zip (unsigned: right-click → Open)

Updated for 8560fda. Web preview and prerelease are torn down when this PR closes.

… pads (USB DS4)

The USB DualShock 4 leaves iSerialNumber empty (WebHID + node-hid both saw
serial=(none)), but it exposes its Bluetooth MAC via HID feature report 0x12 —
a report Chromium blocklists in WebHID (why the renderer can't read it) but
node-hid in main can. On enumeration, when a pad has no serialNumber, probe the
MAC (per-controller report id; DS4 0x12, bytes 1..6 little-endian), format it as
12 hex chars to match node-hid's serial format, and use it as the unit's id.
Probe once per device path and cache (no re-open on the 2s poll).

HARDWARE-VERIFIED on this Windows machine (probe succeeded while the overlay had
the pad open over WebHID — shared feature-report reads work): the DS4 now shows
serial 90fba6ba591c; Steam FXB9960202571 / Switch 98e255badd18 unchanged; dedup
intact. Gives each physical unit an id in the inventory, incl. the serial-less
DS4. NOTE: full two-identical-same-transport AUTO-SELECT still needs the renderer
WebHID-handle -> unit bridge (grant-time correlation) — remaining #106 follow-up.
@petegordon

Copy link
Copy Markdown
Member Author

Win #2 (feature-report MAC) added — commit 8560fda. Pads with no iSerialNumber now get their MAC from a HID feature report (DS4 report 0x12) via node-hid in main. Hardware-verified on Windows (probe succeeded even with the overlay holding the pad open over WebHID — shared feature-report reads work):

28de:1304 | Steam Controller Puck | FXB9960202571
54c:5c4   | Wireless Controller   | 90fba6ba591c   <- was (none)
57e:2009  | Wireless Gamepad      | 98e255badd18

Every physical unit now has an id in the inventory, including the serial-less USB DS4. Remaining for full two-identical-same-transport auto-select: the renderer WebHID-handle -> unit bridge (grant-time correlation) — the last #106 follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant