Skip to content

Overlay: default to the last-used controller — auto-activate + SELECT on launch, else open the AVAILABLE list #104

Description

@petegordon

Goal

On overlay launch, pick up where the user left off: remember the last-used (last SELECTED) controller and — as much as possible — automatically bring it to ACTIVE and make it SELECTED, no button press required. If it isn't available (even after a reasonable wait), show the No-Controller emoji and auto-open the controllers window (the AVAILABLE list) so the user can pick.

Current behavior

The single overlay is input-driven: nothing is SELECTED at boot, an idle controller never auto-selects, and the No-Controller emoji shows until the user interacts with a controller (which engages ACTIVE → SELECTED). See apps/overlay/src/js/app.jsautoAdoptFromPool gates on _everPressed; designateEntry / undesignateEntry; selectedEntry; the No-Controller emoji.

Safe, but every launch starts from scratch — the user must re-engage their controller each time.

Desired behavior

  1. Persist last-used. When a controller becomes SELECTED, remember its identity using the best unique id per platform: Electron/node-hid → serial/MAC (unique per unit); browser → VID:PID (Chromium blocklists per-unit ids). (Ties into the controller-identity / inventory work — #241.)
  2. Auto-activate + SELECT on launch. On boot, if the remembered controller is in the pool, designate it (ACTIVE → SELECTED) automatically — running the normal designate flow (gravity/yaw + recalibration) — without requiring a button press.
  3. Wait an appropriate amount. BT/wireless pads (DualSense-BT, Switch Pro, GameSir Super Nova auto-re-pairing off its charger) don't appear instantly. Wait a tunable grace window (a few seconds) for the remembered controller to enumerate/stream before giving up. During the wait, show a "looking for …" affordance (or the No-Controller emoji with a hint).
  4. Fallback if not available. After the wait, if the remembered controller hasn't appeared:
    • Show the No-Controller emoji, and
    • Auto-open the controllers window (controllers-window.html, the pop-out AVAILABLE list) so the user can choose.

Considerations / edge cases

  • "As much as possible": some pads only start streaming after a button press (pooled but silent). For those we may only be able to *pre-*SELECT and light up on the first report, or fall back to the list — capture what's auto-activatable vs needs-a-nudge.
  • Don't override active intent: if the user engages a different controller during the wait (a real button press), that wins over the remembered default (input-driven still applies to non-remembered pads).
  • Ambiguous identity in browser: VID:PID isn't unique per unit, so "last used" may match any same-model pad — picking the first available of that model is acceptable in-browser; Electron matches the exact unit by serial.
  • Recalibration: auto-designate must run the same calibration/gravity/yaw path as a manual SELECT (designateEntry) so model + orientation are correct.
  • Scope: single overlay (one SELECTED). Multi-overlay / lobby (many SELECTED / ASSIGNED) remembering the last set is a possible follow-up.

Acceptance criteria

  • Relaunch with the same controller connected → it's auto-SELECTED (model shown, gyro live) without pressing anything, within the grace window.
  • Relaunch without it → after the wait, No-Controller emoji and the controllers window opens to the AVAILABLE list.
  • Interacting with a different controller during the wait selects that one instead.

Related

  • apps/overlay/src/js/app.js (designateEntry/undesignateEntry, autoAdoptFromPool, selectedEntry, No-Controller emoji), apps/overlay/src/controllers-window.html / controllers-window.js, Electron controller-select IPC.
  • Three-state model: AVAILABLE → ACTIVE → SELECTED.
  • Controller identity / inventory: #241.

3D model visualization — communicate SELECTED vs not-SELECTED (added 2026-07-06)

A bare "No Controller" emoji under-communicates the SELECTED-vs-not distinction. The 3D viewport should always show a model plus a status label so the current state is legible, and it can double as a model browser when nothing is driving the overlay.

Viewport states

  • Live — a controller is SELECTED: its model (Auto-Detect) rendering live gyro; small corner badge ● <Controller name>.
  • Idle — nothing SELECTED, Auto-Detect (the default): keep the No-Controller emoji icon and add text with it — e.g. "No controller selected" + hint "Press a controller, or open the list ⧉ to pick one." — over a dimmed/inert placeholder model. The emoji stays (glanceable state); the text explains.
  • Preview / browse — nothing SELECTED, user picked a model: render that model inert (slow auto-rotate), labeled "Preview · not selected." The Model dropdown acts as a browser so the user can see the different 3D visualizations when nothing is live.

Model default + override

  • The Model dropdown defaults to Auto-Detect and stays there.
  • When a controller becomes SELECTED → snap to that controller's real model AND reset the dropdown to Auto-Detect (a live controller always shows its own model; any prior browse-preview is discarded — NOT sticky).
  • The user can still manually override the 3D model in Settings to whatever they want, at any time; an explicit override persists until they set it back to Auto-Detect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions