Files
SteamlessController/README.md
T
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

4.1 KiB

SteamlessController

A lightweight Windows system tray app that lets you use a Steam Controller as a standard gamepad — without Steam running.

image

When Steamless Mode is active, the app disables the controller's built-in keyboard/mouse emulation (lizard mode) and exposes it as a virtual Xbox 360 controller via ViGEmBus, making it compatible with any game that supports XInput or the Xbox controller.

Features

  • NEW in 1.9 - Bluetooth! Use the controller wired, through the dongle, or paired over Bluetooth LE
  • NEW in 1.8 - Automatically enable/disable based on if Steam is running, or a Steam game is running!
  • NEW in 1.8 - Advanced debugging and local logging to help diagnose issues
  • Rumble Support v2.0
  • Support for multiple Steam Controllers! Long live split screen!
  • New shiny UI for remapping L4/L5 R4/L5 back buttons!
  • Support for Xbox OR PlayStation controllers - with PS Touchpad and Gyro plumbing.
  • System tray icon shows connection and mode status
  • Steamless Mode — disables lizard mode and exposes controller as Xbox 360 gamepad
  • Trackpad Mouse — use the right (or left) trackpad as a mouse cursor
  • Back Buttons for Clicking — map R4/R5 (or L4/L5) to left/right mouse click
  • Use Left Trackpad Instead — mirror all trackpad/back-button functionality to the left side for left-handed users
  • Start with Windows — launch automatically at login
  • Settings persist across restarts
image image

Requirements

To run

  • Windows 10 or later (64-bit)
  • ViGEmBus driver installed
  • Steam Controller — wired USB (PID 0x1302), wireless dongle (PID 0x1304), or Bluetooth LE (PID 0x1303, pair via Windows Bluetooth settings)
  • Steam closed (Steam claims the controller when running)

To build

  • Visual Studio 2022 with the Desktop development with C++ workload
  • CMake 3.20 or later (included with Visual Studio, or install separately)
  • Windows SDK 10.0.22000 or later (installed via Visual Studio Installer)

Building

git clone https://github.com/your-username/SteamlessController.git
cd SteamlessController
cmake -B build
cmake --build build --target SteamlessController

The executable will be at build\Debug\SteamlessController.exe.

For a release build:

cmake -B build/release -G "Visual Studio 18 2025"
cmake --build build/release --config Release --target SteamlessController

If you have Visual Studio 2022, replace "Visual Studio 18 2025" with "Visual Studio 17 2022".

CMake Targets

Target Description
SteamlessController Main system tray application
SteamProbe Console diagnostic tool — dumps raw HID report bytes as you interact with the controller. Useful for protocol research.
RawControllerProbe Checks whether Windows.Gaming.Input.RawGameController can enumerate the Steam Controller (requires WinRT).

How it works

The Steam Controller exposes a vendor HID collection (usage page 0xFF00) that carries all game input in a 54-byte report (ID 0x42) at ~60 Hz. By default the firmware runs in lizard mode, emulating a keyboard and mouse so the controller works without drivers.

SteamlessController sends HID feature reports to disable lizard mode, then reads the raw input reports and translates them into a virtual Xbox 360 controller via ViGEmBus. A background heartbeat re-sends the disable command every 800 ms to keep lizard mode off.

The full input report layout is documented in src/steam/SteamController.h.

Third-party

  • ViGEmClient — MIT License, built from source as a static library