An Apple TV session could not reach the statistics overlay at all.
Not a bug in the HUD — it renders on tvOS, and the docs already describe its tvOS-specific
floor-shaved math. There was simply no trigger:
StreamCommands.swift, which carries the ⌃⌥⇧S surface, is #if !os(tvOS)
InputCapture.onCycleStats fires only from the macOS keyDown monitor
TouchMouse's three-finger tap needs a touchscreen
So the only way to change the tier was Settings before connecting, or a profile's stats_verbosity. The "Cycle with" table in docs-site/content/docs/stats.md listed every
platform except Apple TV — that table is the quickest way to spot a client missing a surface.
What this adds
Two surfaces, because an Apple TV may have a controller in the room or only the remote.
Select + X on a controller — cycles one tier per completion (off → compact → normal →
detailed → off). Modelled on Android's GamepadRouter.MIC_CHORD (Select + Y), and deliberately
disjoint from the escape chord: X is none of L1+R1+Start+Select, so no way of reaching one chord
passes through the other. It reads the wire mask like the escape chord does, so a Select that the
hold-Select gesture has already turned into a guide cannot cycle the overlay on its way past. Both
buttons still forward to the host — the tier change is local.
It is active on every Apple platform, not only tvOS: a controller in both hands can reach
neither the keyboard combo nor the three-finger tap, which is the same problem on iPhone, iPad and
macOS. Y is left free so a mic chord can be ported onto it later without moving this one.
Hold Play/Pause on the Siri Remote (~0.5 s) — the remote's only spare button, and the only
route to the numbers on an Apple TV with no controller. The right-click is therefore deferred
until the press resolves: a tap still right-clicks, delivered on release with the release trailing
by tapPress. A right button held for half a second is a context menu on every desktop this
streams, so it could not simply go down on contact. The shape mirrors the existing hold-Select
gesture (suppress → tap on release, or the gesture past the threshold).
The trap this had to avoid
openSlot claims every element's preferredSystemGestureState while forwarding is on, but only
the chord elements' while it is off. A new chord therefore has to join that narrowed list —
otherwise, with Forward controllers off, X's press stays the system's on tvOS and the chord
silently never completes. That list is now GamepadCapture.chordElements (escape ∪ stats, with
Select shared once).
Tests
GamepadStatsChordTests pins the invariants whose failure mode is nothing happening, with nothing
logged: each chord's mask against its GameController alias list, that the two overlap only on
Select and that neither is a subset of the other, that the claim list covers both without
duplicates, and that the tier cycle reaches every tier and returns.
swift test — 262 tests, 0 failures; the new suite passes 5/5
⏳On-glass on a real Apple TV is still owed — both the chord and the deferred right-click.
Docs updated: stats.md gains its missing tvOS rows, input.md gains a "Statistics with a
controller" section and the Apple TV bullet now mentions the hold.
An Apple TV session could not reach the statistics overlay at all.
Not a bug in the HUD — it renders on tvOS, and the docs already describe its tvOS-specific
floor-shaved math. There was simply no **trigger**:
- `StreamCommands.swift`, which carries the ⌃⌥⇧S surface, is `#if !os(tvOS)`
- `InputCapture.onCycleStats` fires only from the **macOS** keyDown monitor
- `TouchMouse`'s three-finger tap needs a touchscreen
So the only way to change the tier was Settings before connecting, or a profile's
`stats_verbosity`. The "Cycle with" table in `docs-site/content/docs/stats.md` listed every
platform except Apple TV — that table is the quickest way to spot a client missing a surface.
## What this adds
Two surfaces, because an Apple TV may have a controller in the room or only the remote.
**Select + X on a controller** — cycles one tier per completion (off → compact → normal →
detailed → off). Modelled on Android's `GamepadRouter.MIC_CHORD` (Select + Y), and deliberately
disjoint from the escape chord: X is none of L1+R1+Start+Select, so no way of reaching one chord
passes through the other. It reads the wire mask like the escape chord does, so a Select that the
hold-Select gesture has already turned into a guide cannot cycle the overlay on its way past. Both
buttons still forward to the host — the tier change is local.
It is active on **every Apple platform**, not only tvOS: a controller in both hands can reach
neither the keyboard combo nor the three-finger tap, which is the same problem on iPhone, iPad and
macOS. Y is left free so a mic chord can be ported onto it later without moving this one.
**Hold Play/Pause on the Siri Remote** (~0.5 s) — the remote's only spare button, and the only
route to the numbers on an Apple TV with no controller. The right-click is therefore *deferred*
until the press resolves: a tap still right-clicks, delivered on release with the release trailing
by `tapPress`. A right button held for half a second is a context menu on every desktop this
streams, so it could not simply go down on contact. The shape mirrors the existing hold-Select
gesture (suppress → tap on release, or the gesture past the threshold).
## The trap this had to avoid
`openSlot` claims every element's `preferredSystemGestureState` while forwarding is on, but only
the **chord** elements' while it is off. A new chord therefore has to join that narrowed list —
otherwise, with **Forward controllers** off, X's press stays the system's on tvOS and the chord
silently never completes. That list is now `GamepadCapture.chordElements` (escape ∪ stats, with
Select shared once).
## Tests
`GamepadStatsChordTests` pins the invariants whose failure mode is *nothing happening, with nothing
logged*: each chord's mask against its GameController alias list, that the two overlap only on
Select and that neither is a subset of the other, that the claim list covers both without
duplicates, and that the tier cycle reaches every tier and returns.
## Verification
- tvOS typecheck (`swift build --triple arm64-apple-tvos17.0`) — **Build complete!**
- `swift test` — **262 tests, 0 failures**; the new suite passes 5/5
⏳ **On-glass on a real Apple TV is still owed** — both the chord and the deferred right-click.
Docs updated: `stats.md` gains its missing tvOS rows, `input.md` gains a "Statistics with a
controller" section and the Apple TV bullet now mentions the hold.
An Apple TV session had no way to the stats overlay at all. Every other client
cycles it in-stream — Ctrl+Alt+Shift+S on the desktops, a three-finger tap on
touch — and tvOS has neither a keyboard nor a screen to tap, so the only route
was Settings before connecting (or a profile). The docs' own "cycle with" table
simply had no row for it.
Two surfaces, because an Apple TV may have a controller in the room or only the
remote:
- Select + X on a controller, cycling one tier per completion. Built like
Android's mic chord (Select + Y) and deliberately disjoint from the escape
chord — X is none of its four buttons, so reaching for one can never trip the
other. Read off the wire mask like the escape chord, so a Select the
hold-Select gesture has turned into a guide can't cycle the overlay on its way
past. Available on every Apple platform: a controller in both hands is exactly
the case the keyboard combo and the three-finger tap can't serve.
- Hold Play/Pause on the Siri Remote. Its right-click is therefore deferred until
the press resolves — a tap still right-clicks, delivered on release with the
release trailing by TAP_PRESS — because a right button held for half a second
is a context menu on every desktop this streams.
A non-forwarding slot now claims the stats chord's elements too, alongside the
escape chord's: on tvOS an unclaimed button's press stays the system's and the
chord would silently never complete.
Tests pin both chords' masks against their GameController alias lists, that the
two overlap only on Select, and that the claim list covers both without
duplicates — the failure mode is nothing happening, with nothing logged.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
An Apple TV session could not reach the statistics overlay at all.
Not a bug in the HUD — it renders on tvOS, and the docs already describe its tvOS-specific
floor-shaved math. There was simply no trigger:
StreamCommands.swift, which carries the ⌃⌥⇧S surface, is#if !os(tvOS)InputCapture.onCycleStatsfires only from the macOS keyDown monitorTouchMouse's three-finger tap needs a touchscreenSo the only way to change the tier was Settings before connecting, or a profile's
stats_verbosity. The "Cycle with" table indocs-site/content/docs/stats.mdlisted everyplatform except Apple TV — that table is the quickest way to spot a client missing a surface.
What this adds
Two surfaces, because an Apple TV may have a controller in the room or only the remote.
Select + X on a controller — cycles one tier per completion (off → compact → normal →
detailed → off). Modelled on Android's
GamepadRouter.MIC_CHORD(Select + Y), and deliberatelydisjoint from the escape chord: X is none of L1+R1+Start+Select, so no way of reaching one chord
passes through the other. It reads the wire mask like the escape chord does, so a Select that the
hold-Select gesture has already turned into a guide cannot cycle the overlay on its way past. Both
buttons still forward to the host — the tier change is local.
It is active on every Apple platform, not only tvOS: a controller in both hands can reach
neither the keyboard combo nor the three-finger tap, which is the same problem on iPhone, iPad and
macOS. Y is left free so a mic chord can be ported onto it later without moving this one.
Hold Play/Pause on the Siri Remote (~0.5 s) — the remote's only spare button, and the only
route to the numbers on an Apple TV with no controller. The right-click is therefore deferred
until the press resolves: a tap still right-clicks, delivered on release with the release trailing
by
tapPress. A right button held for half a second is a context menu on every desktop thisstreams, so it could not simply go down on contact. The shape mirrors the existing hold-Select
gesture (suppress → tap on release, or the gesture past the threshold).
The trap this had to avoid
openSlotclaims every element'spreferredSystemGestureStatewhile forwarding is on, but onlythe chord elements' while it is off. A new chord therefore has to join that narrowed list —
otherwise, with Forward controllers off, X's press stays the system's on tvOS and the chord
silently never completes. That list is now
GamepadCapture.chordElements(escape ∪ stats, withSelect shared once).
Tests
GamepadStatsChordTestspins the invariants whose failure mode is nothing happening, with nothinglogged: each chord's mask against its GameController alias list, that the two overlap only on
Select and that neither is a subset of the other, that the claim list covers both without
duplicates, and that the tier cycle reaches every tier and returns.
Verification
swift build --triple arm64-apple-tvos17.0) — Build complete!swift test— 262 tests, 0 failures; the new suite passes 5/5⏳ On-glass on a real Apple TV is still owed — both the chord and the deferred right-click.
Docs updated:
stats.mdgains its missing tvOS rows,input.mdgains a "Statistics with acontroller" section and the Apple TV bullet now mentions the hold.