Because the construction of controllers and its handling has always been a terrible mess. Secondly, this mess results not only from the poor system API to support joysticks, but also from the laziness of OS and game developers. And this condition has persisted for decades.
In short, USB controllers have more buttons visible from the code level than there are physical ones, because the codes assigned to these buttons by the manufacturers are fixed to match pseudo-universal standards. Therefore, the clone of the NES controller has codes 8
and 9
for Select
and Start
, and the D-Pad is represented as the first two axes to match the pseudo-universal handling of the left analog stick.
So don’t worry if SDL sees more buttons and axes than there actually are, because that’s how the system (e.g. Windows) sees it — this is normal (unfortunately).
The universal mapper built into SDL is fundamentally wrong. You can’t treat all controllers the same way, because each one is different, has a different number of hats, axes, analog sticks and buttons, as well as a different physical arrangement of these manipulators and differently defined manipulator codes (mainly axis and button codes). This will sound controversial, but creating such a universal mapper for all types of controllers is nothing more but asking for trouble.
If, as a game developer, you want to ensure that the support for all types of controllers always is not only correct, but also ~unlimited, then the only sensible solution is a low-level API and your own implementation of the input mapper, operating at the application level. Don’t be lazy — write a code.
Each professional game should allow the use of any input device for control (mouse, keyboard, gamepad), give the ability to assign game functions to any manipulator of any type, and also provide the ability to calibrate them (e.g. setting dead zones, sensitivity, etc.). If the mapper is written well, there will be no problem even with assigning several devices to one player (e.g. mouse+keyboard, mouse+gamepad, gamepad+gamepad etc.), i.e. giving possibilities that are seemingly not useful, but which can allow players to play however they want and players with various disabilities to play comfortably.
This is what I prefer and what I did in my engine — the custom input mapper that is powerful and highly configurable. The engine can handle any joystick, assign devices as the player wants (one per player or several), and assign specific control functions to the manipulators that the player wants to use (the player, not the developer). All of this exists solely because of SDL’s low-level joystick API.
There are two downsides to this approach — you have to write quite a lot of code (around 2kLoC, depends of the programming language) and the player has to set the mapping themselves in order to use the device in the game. However, the advantages are so many that there is practically nothing to think about — freedom of mapping is worth its weight in gold, especially for players with various disabilities.