18 Commits
Author SHA1 Message Date
Dylan DeverillandClaude Sonnet 5.5 8e87e7a930 Turn the controller off with Steam + Y (#106)
Steam reserves Steam + Y to power a wireless controller down. Do the same:
the firmware accepts feature command 0x9F with the payload "off!", confirmed
on a puck slot with PowerOffProbe, so SteamController::PowerOff() sends it
and the read loop fires it on the press of the chord.

On by default with no setting. The chord is consumed, so the game never sees
the Guide press or the Y, and the alerts for the silence and disconnect that
follow are suppressed because the user asked for them. A controller still
reporting three seconds later ignored the command (or woke straight back up)
and gets its input back. Skipped on a cable: the pad is powered by it and
would only ignore the command.

PowerOffProbe is the tool that found the command: it tries the candidate
payloads and reports whether the pad then goes quiet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 22:57:28 -07:00
Dylan DeverillandClaude Opus 5.5 1a1253a384 Build and sign releases through SignPath on GitHub Actions
SignPath Foundation signs only what a trusted build system built from the
repository, so releases move from local builds to a workflow. A tag push
builds, signs the three executables, compiles the installer around the
signed copies, signs the installer, and opens a draft release. A manual run
does the same under test-signing, without the release.

The README gains the code signing policy the Foundation requires, including
a privacy note for the Google Fonts request the remap window makes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-22 22:57:33 -07:00
Dylan Deverill 7bf69ca4de Update README.md 2026-09-06 23:44:49 -07:00
Dylan DeverillandClaude Opus 5 0dc1d2855d Bump to 1.20 and note the new trackpad modes
Version in resource.h and the installer script, which the header's own
comment asks to be kept in step. Verified on the built binary rather than
in the source: FileVersion and ProductVersion both read 1.20.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 21:26:09 -07:00
Dylan Deverill bd4206e891 Find far more games, and let a user name one we missed
The per-game picker drew on two sources: a Start Menu shortcut walk and the
installed MSIX package list. Issue #83 is a user whose app never appeared in
it, asking to just point at an .exe. Two structural gaps sat behind that.

Launcher-installed games were missing or wrong. Epic shortcuts a game with a
.url carrying a com.epicgames.launcher:// URI, and the walk keeps only .url
files pointing at Steam, so Epic games were dropped outright — Among Us was
invisible on the machine this was written on. Battle.net and Ubisoft write
.lnk files targeting the launcher executable with a per-game argument, and
IShellLink::GetPath returns only the executable, so a whole Blizzard library
collapsed into one wrongly-named "Battle.net Launcher.exe" entry.

LauncherGames reads what each launcher records about itself instead: Epic's
.item manifests, GOG's and Ubisoft's registry keys, Battle.net's uninstall
entries. Those name an install directory rather than an executable, which is
the better identity anyway and gets a fourth game-id shape, "dir:". A game
folder holds its launcher, its anti-cheat service and the game itself, and
which of them reaches the foreground varies by title and by moment — the same
reasoning SteamAppLocator already documents for Steam, and MatchProfile
resolves these by longest path prefix for the same reason.

Nothing without an installer entry could be listed at all, which is the long
tail the issue is really about. The picker now offers the applications with a
window open right now, and a file browser, both producing an ordinary exe-path
id that needed no new plumbing. The running list is the more useful of the two:
it asks the user to know nothing about where a game was installed.

Also moves the enumeration off the UI thread. It measured 1.4s warm and 5s
cold before this change added five sources to it, and OpenRemapWindow was
calling it synchronously — the window did not appear until it finished.

Fixes #83
2026-08-31 01:33:18 -07:00
Dylan Deverill fde18e600e Update README.md 2026-08-19 23:04:48 -07:00
Dylan Deverill 6db91983ed Update README.md 2026-08-19 22:58:36 -07:00
Dylan DeverillandClaude Opus 5 fde32bc650 Document what the installer needs that a clone does not have
Compiling resources\InnoInstallerScript.iss on a fresh checkout fails, and the
error names a file with no obvious way to obtain it. Two prerequisites were
undocumented.

The ViGEmBus installer the script packages is not in the repository. It is a 6
MB vendored binary and *.exe is gitignored — by the blanket "loose binaries
committed by mistake" rule rather than by anything written for it, which is why
nobody noticed. Left out rather than committed: the version is pinned in a
single #define, and a stale 6 MB binary in history is harder to walk back than a
line of prose.

The script also packages SteamlessDeviceCycle and ViGEmBusProbe, and the build
section above only ever told anyone to build SteamlessController. Both are now
in the build commands and in the target table, which listed neither.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:35:04 -07:00
Dylan DeverillandClaude Opus 5 ba215b2429 Fix custom back button mappings being ignored in mouse click mode
"Back Buttons as Mouse Click" hijacked L4 and R4 unconditionally: the
virtual controller skipped their bindings entirely, so a paddle mapped to
RT silently did nothing while the option was on. Only the upper paddles
were affected, so the symptom read as "two of my four paddles are dead"
rather than pointing at the toggle. The same gate also suppressed the
Left/Right Click bindings the remap window already offered, meaning the
feature disabled the very capability it existed to provide.

The toggle was a strict subset of the remap window, which already offers
left and right click per paddle, so retire it rather than teach it to
cooperate:

- Remove the backMouseEnabled gates, the tray item, and the duplicate
  L4/R4 handling in TrackpadMouse. Paddle mouse output now flows through
  the single edge-detection path in ReadLoop.
- Default L4 and R4 to left click so a fresh install still clicks out of
  the box.
- Add an explicit Off option to the picker. None was already a real
  "do nothing", but Reset now yields left click, so without this there
  would be no way to disable a paddle at all.
- Migrate BackButtons=1 into real bindings, filling in only paddles with
  no binding of their own so an explicit mapping is never overwritten,
  then delete the value so it runs once.

TrackpadMouse::Reset used to release a paddle held while game mode was
torn down. Removing its L4/R4 handling would have stranded the mouse
button down, so that cleanup moves to ReleasePaddleMouseButtons, which
also covers right click and runs on the enable, disable, and destructor
paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 18:55:24 -07:00
Dylan DeverillandClaude Fable 5 50a8ae246d Add Bluetooth LE support for the Steam Controller
The controller now works wired, through the dongle, or paired over
Bluetooth, with the full feature set: game mode, rumble, trackpad haptics,
battery, PlayStation-mode gyro, and taking the controller back from a
running Steam.

Windows exposes the entire vendor protocol over HID-over-GATT under PID
0x1303 — feature reports, output reports, and the 0x45 state report (54
bytes, ~157 Hz) all work through the existing HidDevice stack, so support
is a thin layer rather than a new transport backend.

- Add SC2026_BT_PID to enumeration and a per-slot Transport tag, detected
  from the interface path (HOGP GUID / bthenum markers win over PID
  tokens) and logged on CONNECT/DISCONNECT
- Swap the battery report 0x43 payload bytes on Bluetooth slots — the
  firmware sends [chargeState, percent], reversed from USB/dongle
- Bluetooth-specific disconnect alert copy, and a notification when
  acquisition gives up rather than a silently dead pad

Device cycling had to be reworked for reclaiming the controller from
Steam. DIF_PROPERTYCHANGE reports success while leaving the device and
Steam's handle untouched; CM_Query_And_Remove_SubTree is vetoed by open
handles; pnputil /restart-device works once, then leaves the devnode
reboot-pending and refuses thereafter. Disable+enable works repeatedly.

Two traps found against live hardware. SetupDiCallClassInstaller returned
ERROR_INVALID_DATA for a disable Windows had genuinely performed — the app
logged the device removal a tenth of a second before the call reported
failure — so return codes cannot gate the re-enable, or the controller is
stranded disabled and needs Device Manager to recover. CM_Get_DevNode_Status
is now the source of truth: whether the node went down decides if a real
cycle happened, the re-enable always runs and retries until the node
confirms it is enabled, and only a stranded device counts as failure. Some
devices also reject a global scope change, so DICS_FLAG_CONFIGSPECIFIC is
tried as well.

SteamProbe becomes a transport discovery tool: detailed enumeration with
transport detection, interface selection, non-fatal lizard-off with a
raw-dump fallback, an output-report test, report-rate measurement, and
--enum / --imu modes. The windowless cycle helper gains its own cycle.log,
since it cannot share the tray app's events.log (held open _SH_DENYWR) and
was previously impossible to diagnose.

Verified on hardware: game mode, rumble, haptics, battery, IMU streaming,
clean removal on power-off/sleep/range loss with automatic reconnect,
yielding to Steam on game launch, and reclaiming afterwards (~1.2s). Also
confirmed the firmware enforces one host transport at a time, so no
cross-transport deduplication is needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 17:41:43 -07:00
Dylan Deverill 5c3f4aacf0 Update README.md 2026-07-07 14:51:10 -07:00
Dylan Deverill 57f8844e7b Update README.md 2026-06-22 00:10:37 -07:00
Dylan Deverill 9599372101 Update README.md
Updated with new features outlined
2026-06-20 23:39:06 -07:00
Dylan Deverill 0447da8198 Merge pull request #3 from ddeverill/release/v1.0.0
Add Start with Windows, installer script, and release build support
2026-05-10 20:31:02 -07:00
Dylan Deverill 4b1d3fdec0 Update README.md 2026-05-10 20:09:53 -07:00
Dylan Deverill a5bf2cbd6b Update README.md 2026-05-10 20:08:11 -07:00
Dylan DeverillandClaude Sonnet 4.6 f4a2f04315 Add Start with Windows, installer script, and release build support
- Start with Windows toggle in tray menu — writes/removes the Run registry
  key so the app launches automatically at login
- Inno Setup installer script for packaging the release exe
- CMakePresets.json for named debug/release build configurations
- README updated with correct Visual Studio 2025 build commands and a
  note for VS 2022 users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 19:57:47 -07:00
Dylan Deverill 589ed11e4a Initial release 2026-05-10 19:28:03 -07:00