pod: fully explicit activation (--profile) + RIOJoy.Tray.Ready signal

No detection anywhere in the pod chain (Cyd: foreground detection is too
slow - the pad appears after the game already enumerated controllers).
--profile <name> activates immediately at startup, never runs the
auto-switch watcher, and on success signals the named manual-reset event
RIOJoy.Tray.Ready (process-lifetime, never stale) so a launcher waits on
the signal instead of counting winmm devices. Legible failures for the
launcher: exit 4 unknown profile, exit 5 activation failed with the reason
on stderr - both verified live against the built exe. Editor close
re-activates the explicit profile. build-pod start scripts now pass
--profile <Name> --exit-with <exe>; docs updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-31 23:01:44 -05:00
co-authored by Claude Fable 5
parent 5befd1d511
commit bdd30678e3
5 changed files with 164 additions and 28 deletions
+28 -8
View File
@@ -218,16 +218,36 @@ entry points the game's package wires up:
game:
```
start "" "...\riojoy\app\RioJoy.Tray.exe" --exit-with <game exe>
start "" "...\riojoy\app\RioJoy.Tray.exe" --profile "<Name>" --exit-with <game exe>
```
`--exit-with` makes RIOJoy self-managing: it activates when the game's window
comes foreground, and once the game process has run and then exited it tears
itself down completely (ports released, wallpaper restored, plasma blanked)
and quits. If the game never appears within 60 s it also quits, so a failed
launch can't strand it. Back-to-back launches hand over cleanly: a starting
`--exit-with` instance waits up to 15 s for the previous game's copy to
release the single-instance lock.
In pod mode **everything is explicit — there is no detection on either
side**. `--profile` activates the named profile immediately at startup, so
the virtual controller and the ports exist *before* the game launches and
enumerates input devices (foreground detection activates ~1 s after the
window appears — too late for startup enumeration, and the auto-switch
watcher never runs in this mode). On success RIOJoy signals the named event
**`RIOJoy.Tray.Ready`** — a launcher waits on that (with a timeout) instead
of guessing from device enumeration, and the failure modes stay legible:
| Launcher observes | Meaning |
|---|---|
| `RIOJoy.Tray.Ready` signaled | profile active; pad + ports exist — start the game |
| RIOJoy exited, code 4 | profile name not in the config (script typo) |
| RIOJoy exited, code 5 | activation failed — reason on stderr (port busy, bad endpoint) |
| no signal, still running | genuinely stuck — timeout and report |
The event is process-lifetime (it can never go stale); either side may create
it first — same name, manual-reset, both converge on one object. Batch-only
integrations without a launcher can simply order the script: `start`
RIOJoy, then the game — but a real launcher should wait on the event.
`--exit-with` handles the other end: once the game process has run and then
exited, RIOJoy tears itself down completely (ports released, wallpaper
restored, plasma blanked) and quits. If the game never appears within 60 s it
also quits, so a failed launch can't strand it. Back-to-back launches hand
over cleanly: a starting pod instance waits up to 15 s for the previous
game's copy to release the single-instance lock.
Properties that matter on a cabinet: each game pins the RIOJoy build it was
verified with (updating RIOJoy for a new game can't regress an old one);
+12
View File
@@ -503,6 +503,18 @@ total across the suite.
exe wins over `%APPDATA%\RIOJoy\config.json`; `TrayApplicationContext.
ConfigPath` resolves through it, so `--import-profile` targets the same
store. A pod bundle needs no import step: its config *is* the profile.
- **`--profile <name>`** — explicit immediate activation, **no detection**:
in pod mode the auto-switch watcher never runs. The rationale is
enumeration timing: games enumerate controllers at startup, and foreground
detection activates ~1 s after the window appears — too late, so the pod
launch script activates the profile *before* starting the game and the
ViGEm pad already exists when the game looks. On success RIOJoy signals the
named event `RIOJoy.Tray.Ready` (process-lifetime — never stale) so a pod
launcher waits on the signal instead of counting input devices. Exit code 4
for an unknown profile name (validated up front so a pod-script typo is
scriptable, not a silently idle tray) and 5 for failed activation (reason
on stderr) — the launcher can always tell "failed with reason" from
"hung". Closing the editor re-activates the explicit profile.
- **`--exit-with <exe|pid>`** (`CompanionTarget.Parse` — pid, or a name
normalized like auto-switch triggers): the tray polls the companion on its
existing 1 s timer and quits through the normal teardown (ports released,