The code is now reliant on SDL_PrivateJoystickAdded() and SDL_PrivateJoystickRemoved() being called correctly when devices are added or removed on Windows
Jimb Esser
Note: This is using DirectInput, I have to disable XInput as that causes all but the first 4 controllers to be completely ignored by SDL (I can find no way to reconcile XInput devices with DirectInput devices, otherwise I would make a patch that accepts the fifth and later controllers with DirectInput...). XInput does not seem to have the problem below, only DirectInput.
I plug in 3 identical wireless Xbox 360 controllers, call them J1, J2, J3. Direct Input shows them as having GUIDs G1, G2, G3. I unplug J1, then J2 and J3 show up as having GUIDs G1 and G2! Not so "unique"... I start my SDL app when just J2 and J3 are plugged in, and open J2 and J3. Then I plug in a new controller, SDL sees that now G3 exists, assigns that a new SDL joystick instance ID, which I request to be opened, but G3 at this point is J3, which I already had opened! So I end up with two instances of J3 opened, and none of J1. "Re-"opening G1 would get the actual handle to the newly attached controller, but there's no current way to know this. This is clearly a bug or poor design in DirectInput or my wireless receiver drivers, but is a showstopping bug for my 8-20 player games (as soon as any one controller runs out of battery or goes to sleep and gets turned back on, suddenly things are busted requiring a restart (or, at least, a reinitialization of all controllers - the game can't go on)).
The solution I found is to use HID paths instead of GUIDs to uniquely identify joysticks. GUIDs are still needed to open a controller, however I have added code to re-find the GUIDs for all joysticks whenever a new joystick is attached or removed. This does now require opening of all joysticks (instead of just enumerating them), though if your app, like mine, is opening all of them anyway so that any can press a button to join, that doesn't change much (although perhaps they joysticks should be kept open in this case, instead of closed and re-opened). If your app only ever opens one joystick, this will do more work at startup than it did previously.
white.magic
The logic which decides if a device enumerated via the direct input system in the function EnumJoysticksCallback in SDL_dinputjoystick.c is processed is discarding valid joystick devices due to the assumption that devices of the type DI8DEVTYPE_SUPPLEMENTAL are not valid devices.
This change was added with 2.0.4 with this commit http://hg.libsdl.org/SDL/rev/1b9d40126645 that is linked to this bug report https://bugzilla.libsdl.org/show_bug.cgi?id=2460 which indicates that in that case devices of the type DI8DEVTYPE_SUPPLEMENTAL were not desirable as they caused a singular device to emit multiple "device added" events.
Since then there appear to have been a few fixes to handle devices that fall into various other classes in the following two commits:
http://hg.libsdl.org/SDL/rev/10ffb4787d7a and http://hg.libsdl.org/SDL/rev/6a2bbac05728
Two devices I have reports of failing to be listed when the DI8DEVTYPE_SUPPLEMENTAL type is excluded are ECS Gametric Throttle and Thrustmaster MFD Cougar.
Sam Lantinga
I verified that the OUYA controller shows up as a single device with this change, so I've reverted the change to ignore supplemental devices, leaving framework in place to easily add devices that we want to ignore.
Fixed a case where partial trigger pull could be bound to another button
There is a fundamental problem not resolved by this commit:
Some controllers have axes (triggers, pedals, etc.) that don't start at zero, but we're guaranteed that if we get a value that it's correct. For these controllers, the current code works, where we take the first value we get and use that as the zero point and generate axis motion starting from that point on.
Other controllers have digital axes (D-pad) that assume a zero starting point, and the first value we get is the min or max axis value when the D-pad is moved. For these controllers, the current code thinks that the zero point is the axis value after the D-pad motion and this doesn't work.
My hypothesis is that the first class of devices is more common and that we should solve for that, and add an exception to SDL_JoystickAxesCenteredAtZero() as needed for the second class of devices.
These fixes are lumped into two categories:
1. add new file, SDL_dataqueue.c, to UWP/WinRT build-inputs (via MSVC project
files)
2. implement a temporary, hack-fix for a build error in SDL_xinputjoystick.c.
Win32's Raw Input APIs are, unfortunately, not available for use in UWP/WinRT
APIs. There does appear to be a replacement API, available in the
Windows.Devices.HumanInterfaceDevice namespace.
This fix should be sufficient to get SDL compiling again, without affecting
Win32 builds, however using the UWP/WinRT API (in UWP/WinRT builds) would
almost certainly be better (for UWP/WinRT builds).
TODO: research Windows.Devices.HumanInterfaceDevice, and use that if and as
appropriate.
Jan Klass
Not sure if this is limited to the joystick subsystem,
but I created a minimal program for reproducibility,
which is attached.
The issue occurs with my gamepad Razer Onza (an xbox-style gamepad) plugged in.
On initialization, the gamepad is being recognized.
After quitting the subsystem, the poll will receive the joystick added event,
which it instantly handles itself, calling SDL_SYS_JoystickDetect again,
which this time calls IDirectInput8_EnumDevices with dinput = NULL (after it was released on quit).
This seems to lead to an access violation within said function, which I have no source for.
Machiel van Hooren
In SDL_dxjoystick.c line 349 there is a constant c_dfDIJoystick2.
However, this constant is aparently also defined in dinput8.lib.
I encountered a linking error when statically linking to SDL:
SDL2_static.lib(SDL_dxjoystick.obj) : error LNK2005: _c_dfDIJoystick2 already defined in dinput8.lib
My application is also linking to dinput8.lib because we rolled our own joystick input and are not using the joystick functionality from SDL.
The Windows 10 Anniversary Update (1607) breaks the method uses that SDL uses to
detect XInput devices. That is, on Windows 10 Anniversary Update, it is no longer
possible to query RAWINPUT for HID devices, and check for "IG_" in the device name.
Presumably, this will be fixed in the future.
This patch works around the issue by adding the Xbox One controller series to the
well-known device list.
This skips the more expensive RAWINPUT check for those devices, and causes them to
be detected as XInput devices once again.
This allows us to set an explicit stack size (overriding the system default
and the global hint an app might have set), and remove all the macro salsa
for dealing with _beginthreadex and such, as internal threads always set those
to NULL anyhow.
I've taken some guesses on reasonable (and tiny!) stack sizes for our
internal threads, but some of these might turn out to be too small in
practice and need an increase. Most of them are simple functions, though.
This change has also been tested as buildable + runnable on Win32 + MSVC 2015,
2013, 2012, and 2010. It may fix similar build errors (in XInput code) that
are appearing in MingW builds (on buildbot).
The internal function SDL_EGL_LoadLibrary() did not delete and remove a mostly
uninitialized data structure if loading the library first failed. A later try to
use EGL then skipped initialization and assumed it was previously successful
because the data structure now already existed. This led to at least one crash
in the internal function SDL_EGL_ChooseConfig() because a NULL pointer was
dereferenced to make a call to eglBindAPI().