Skip to content

Overlay #104: default to last-used controller + 'No controller selected' splash text#105

Merged
petegordon merged 8 commits into
mainfrom
feat/104-last-used-controller
Jul 7, 2026
Merged

Overlay #104: default to last-used controller + 'No controller selected' splash text#105
petegordon merged 8 commits into
mainfrom
feat/104-last-used-controller

Conversation

@petegordon

Copy link
Copy Markdown
Member

Implements the core of #104 (last-used controller default) and the first slice of its 3D-viewport communication. Draft — needs hardware verification (BT/wireless enumeration + grace timing).

In this PR

  • Splash text — the No-Controller state now shows the emoji plus "No controller selected" + a hint ("Press a controller, or open the list ⧉ to pick one."), so SELECTED vs not-SELECTED reads clearly instead of a bare emoji.
  • Persist last-useddesignateEntry records the SELECTED pad's identity (overlay:lastController, vid:pid) as the launch default.
  • Auto-select on launch (grace window) — for 6s after boot, tryAdoptLastUsed auto-SELECTs the remembered pad as soon as it's receiving, no button press required (relaxes the input-driven policy for the remembered pad only). A real press on any pad still wins — press-based autoAdoptFromPool runs first each loop and claims hidDevice before the last-used path.
  • Fallback — if the remembered pad never appears within the window, open the controllers window (AVAILABLE list) so the user can pick; the No-Controller splash stays.

Deferred (still tracked by #104, next iteration)

  • 3D viewport states — dimmed/inert placeholder model in IDLE, PREVIEW model-browser (pick a model with nothing SELECTED → inert slow-rotate, "Preview · not selected"), and the LIVE ● <name> badge.
  • Model dropdown snap/reset — default Auto-Detect is already in place; the explicit "on SELECT → snap to the real model AND reset dropdown to Auto-Detect, but manual override persists" behavior.
  • Per-unit serial matching in Electron (this PR matches on vid:pid, the browser-safe baseline).

Verify on hardware

  • Relaunch with the same pad connected → it auto-SELECTs (model + gyro) within ~6s, no press.
  • Relaunch without it → after ~6s, No-Controller splash and the controllers window opens.
  • Press a different pad during the window → that one is selected instead.

Refs #104.

The bare gamepad emoji under-communicated the not-SELECTED state. Add a title
('No controller selected') + hint ('Press a controller, or open the list ⧉ to
pick one.') beneath it, so SELECTED vs not-SELECTED reads clearly.
- Persist the last SELECTED pad's identity (vid:pid) in designateEntry
  (overlay:lastController).
- On launch, within a 6s grace window, auto-SELECT the remembered pad as soon
  as it's RECEIVING — no button press required (relaxes the input-driven policy
  ONLY for the remembered pad). A real press on any pad still wins: press-based
  autoAdoptFromPool runs first each loop and sets hidDevice before tryAdoptLastUsed.
- If the remembered pad never shows within the window, open the controllers
  window (AVAILABLE list) so the user can pick; No-Controller splash stays.

NEEDS HARDWARE VERIFY (grace/enumeration timing, BT pads). vid:pid identity is
the browser-safe baseline; per-unit Electron serial matching is a follow-up.
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🔎 PR #105 preview — torn down

This PR is closed; its web preview and desktop prerelease have been removed.

…embered pad

Fallback bug: the first launch after this feature ships has nothing persisted
(_lastUsed is null), and the early-out for that case skipped the fallback
entirely — so the list never opened when nothing was selected. Move the
no-remembered-pad check inside the grace branch (just wait it out) so the
'grace expired + nothing SELECTED -> open the AVAILABLE list' path fires
regardless of whether a pad was remembered.

(RESET DEFAULTS already clears overlay:lastController via its overlay* key
sweep + reload — no change needed there.)
With nothing remembered there's nothing to wait for, so open the AVAILABLE list
sooner (NO_PAD_GRACE_MS = 2s). The full 6s wait now applies only when we're
actually waiting for a remembered pad to enumerate (BT/wireless is slow). The 2s
beat still lets the user press a connected pad to select it first, and lets the
pool populate so the list isn't empty when it opens.
Adds a small '● <name>' badge shown while a controller is SELECTED and driving
the overlay, gated behind a new 'Show Controller Badge' setting that is OFF by
default (overlay:showBadge). updateLiveBadge() reflects designate/undesignate +
the toggle.
- Picking a model from the dropdown while nothing is SELECTED now shows a
  'Preview · not selected' label (the viewport doubles as a model browser).
- Such a preview pick is NO LONGER persisted (was becoming a sticky override);
  only a deliberate override (chosen while a controller is live) or an explicit
  Auto-Detect persists.
- When a real controller takes over, switchController resets the dropdown to
  Auto-Detect and snaps to that controller's own model (discards the preview).
- Choosing Auto-Detect with nothing live returns to the IDLE splash.
@petegordon

Copy link
Copy Markdown
Member Author

Tested by Pete ✅. Marking ready for review.

Final scope — all of #104 except the dimmed IDLE model (descoped; emoji+text is enough):

  • last-used auto-select on launch (grace window, a real press on another pad wins) + fallback to open the AVAILABLE list
  • "No controller selected" splash text
  • opt-in LIVE ● <name> badge (default OFF)
  • PREVIEW label + model browser (pick a model with nothing selected)
  • dropdown snap-to-Auto-Detect on select (preview picks no longer persist)

Optional follow-up: per-unit Electron serial matching (currently vid:pid).

…ust vid:pid)

When two same-model pads are connected (e.g. a BT GameSir and a USB DS4, both
054c:05c4), vid:pid alone could auto-select the wrong one. rememberLastUsed now
also stores the unit's transport (driver.connectionType) and its unit-specific
serial when resolvable (real MAC/serial, not a '(no serial)' placeholder).
tryAdoptLastUsed ranks same-vid:pid receiving pads: exact serial match first
(once the main-process inventory is populated via a list/pair gesture), else same
transport (works at boot — distinguishes BT vs USB), else any same-model pad.
Old records without conn/serial fall back to vid:pid — no regression.
@petegordon

Copy link
Copy Markdown
Member Author

Added the per-unit matching (was the last optional follow-up), commit 113073c:

rememberLastUsed now stores the unit's transport (driver.connectionType) + its serial when resolvable; tryAdoptLastUsed ranks same-vid:pid receiving pads serial → transport → any same-model. So a BT GameSir and a USB DS4 (both 054c:05c4) no longer get confused — transport disambiguates at boot (serials require the list/pair gesture to populate the inventory, so they're a refinement, not available at the instant of auto-select). Two identical same-transport units remain ambiguous (renderer can't map a handle→serial — the known Chromium limitation).

Needs a hardware pass with two same-vid:pid pads to confirm.

…re scan)

The #104 auto-opened controllers window had no user gesture, so the serial scan
(requestDevice, which needs user activation) never ran — its rows showed no
serials while manually clicking 'Show list…' did. Arm a one-shot scan on the
user's first interaction with the overlay (pointerdown/keydown); Electron
auto-picks with no chooser and upserts every present device's serial into the
live inventory the window reads. The manual 'Show list…' scan is unchanged.
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