Skip to content

Controller Learner

jazzphone edited this page Aug 8, 2026 · 1 revision

Controller Learner

Teaches SteamShell to read a controller it does not already understand.

When you need it

RawInput has one built-in layout — the ROG Ally's controller, a 16-byte report. Any other pad is rejected by the decoder, which is safe on the desktop (XInput takes over) and leaves you with no working controller inside Xbox FSE, where XInput reads zeros.

The log says so plainly:

RawInput: ignoring 34-byte reports from device 0x...
The built-in layout only understands 16-byte reports.
Use Settings -> Controller & Cursor -> Learn Controller to teach this one.

Running it

Settings → Controller & Cursor → Learn Controller.

The wizard asks for one control at a time and watches which bytes move:

  1. Let go of everything while it measures what your pad sends at rest.
  2. Press each button once when named. Skip anything the controller does not have.
  3. Move sticks and triggers fully as prompted, then release.

It saves to SteamShell-Controllers.ini, keyed on a stable identity for the device — the USB vendor and product IDs, or a hash of the HID descriptor if Windows will not say. Handles change across sleep and re-plugging, so those are never used as the key.

Getting a good result

  • Really let go during the rest countdown. Anything held then is recorded as "always moving" and ignored for the rest of the session. If a control was held, the log says so and tells you to start over.
  • Move sticks all the way and hold briefly at the extreme, then release.
  • Skip what you do not have. A missing control is better than a guessed one.

Things it handles

  • Gyro and motion axes. A pad streaming six motion axes at 1 kHz will not have those mistaken for a trigger.
  • Shared trigger axes. Some pads put LT and RT on one axis moving opposite ways.
  • D-pads as either a hat byte or four independent bits — decided after all four directions are known, because a single direction cannot tell you which it is.
  • Analogue rest points that are not centred, which is normal for triggers.

If it goes wrong

  • Ctrl+Alt+Shift+D, or Settings → Delete Learned Profile, restores the built-in layout. Use this if a bad profile makes the pointer run away.
  • A saved profile is checked at rest on load. If an axis is pegged with nothing touched, it tells you and offers to delete it.
  • The D-pad is left unmapped rather than guessed when the directions are neither a regular hat nor four clean bits — a wrong guess maps several directions to one bit, which is worse than nothing.

Clone this wiki locally