A virtual pad that could not be created was reported as GameModeOutcome::Blocked — the same answer the manager gives when another process holds the controller. The acquire path answers Blocked by restarting the controller's devnode to invalidate somebody's handle, so a ViGEm problem drove device cycles at hardware with nothing wrong with it. That is what left a reporter's puck collections stuck at problem code 22 in #65. VirtualPadUnavailable is now its own outcome, and the acquire path returns on it ahead of all the contention machinery: no retry counter, no cycling, no holder scan. Measured against a disabled bus with Steam closed, so that contention could not account for the result: zero device cycles across the whole episode, where the previous build spent three. Blocked still outranks it in the aggregate, because contention is the one of the two that a cycle can actually resolve. The failing slot releases its exclusive handle. It claimed the device and switched lizard mode off before discovering it had nowhere to send input, then held that claim for nothing — locking Steam out, and making the app itself the handle that vetoes its own device cycle, which is the veto #65's logs recorded. The driver notice fires once. The acquire burst re-entered EnableGameMode eleven times in half a second and every pass re-armed the balloon, which is the toast storm reported in #68 — Windows was still replaying queued toasts minutes after the app had been closed. The burst is called off as soon as a missing pad is the answer, the latch is cleared only by a pad that actually came up, and this path retries every 30 seconds rather than every four. Retrying no longer disturbs the controller. Learning that the bus is still absent by attempting an enable costs an exclusive claim and a lizard mode toggle every time, so once the absence is established it is re-established by enumerating, which touches no device at all. The tray menu re-enumerates before it is drawn. Releasing clears the slot list and only a device event refilled it, so after a manual disable the toggle grayed itself out as "(no controller detected)" and stayed that way until the app was restarted — #68's third item, and why a restart was the only recovery. The bus report reaches the log as UTF-8. EventLog's %ls goes through fprintf, which converts through the C locale and abandons the line at the first character it cannot encode: the report logged as "no virtual gamepad bus is present" and stopped dead at the em dash that followed. Converted at this call site only — the remaining %ls arguments carry device paths and process names and have the same flaw wherever those are not ASCII. Recovery no longer needs a restart. With the retry running, re-enabling the bus brought game mode up 30 seconds later with no relaunch and no clicking. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SteamlessController
A lightweight Windows system tray app that lets you use a Steam Controller as a standard gamepad — without Steam running.
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 — the upper paddles (L4/R4) left-click by default; rebind any paddle to a gamepad button, right click, or Off
- 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
Requirements
To run
- Windows 10 or later (64-bit)
- ViGEmBus driver installed
- Steam Controller — wired USB (PID
0x1302), wireless dongle (PID0x1304), or Bluetooth LE (PID0x1303, 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