diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index d3c68f191..1c99a6c2d 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -318,12 +318,15 @@
+
+
+
@@ -432,6 +435,7 @@
+
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 9f536e981..79ed8305f 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -323,6 +323,9 @@
+
+
+
@@ -478,6 +481,7 @@
+
diff --git a/configure b/configure
index beda25d0d..d81855a0a 100755
--- a/configure
+++ b/configure
@@ -24336,6 +24336,8 @@ fi
$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
+$as_echo "#define SDL_JOYSTICK_RAWINPUT 1" >>confdefs.h
+
EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
SOURCES="$SOURCES $srcdir/src/hidapi/SDL_hidapi.c"
diff --git a/configure.ac b/configure.ac
index 2a3654da3..4596adc6b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3316,6 +3316,7 @@ AS_HELP_STRING([--enable-hidapi], [use HIDAPI for low level joystick drivers [[d
if test x$hidapi_support = xyes; then
AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
+ AC_DEFINE(SDL_JOYSTICK_RAWINPUT, 1, [ ])
EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
SOURCES="$SOURCES $srcdir/src/hidapi/SDL_hidapi.c"
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index b47da68c7..42488cc9a 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -289,6 +289,7 @@
#undef SDL_JOYSTICK_USBHID
#undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
#undef SDL_JOYSTICK_HIDAPI
+#undef SDL_JOYSTICK_RAWINPUT
#undef SDL_JOYSTICK_EMSCRIPTEN
#undef SDL_JOYSTICK_VIRTUAL
#undef SDL_HAPTIC_DUMMY
diff --git a/include/SDL_config_windows.h b/include/SDL_config_windows.h
index f269bfc04..16fa06f72 100644
--- a/include/SDL_config_windows.h
+++ b/include/SDL_config_windows.h
@@ -197,6 +197,7 @@ typedef unsigned int uintptr_t;
#define SDL_JOYSTICK_DINPUT 1
#define SDL_JOYSTICK_XINPUT 1
#define SDL_JOYSTICK_HIDAPI 1
+#define SDL_JOYSTICK_RAWINPUT 1
#define SDL_HAPTIC_DINPUT 1
#define SDL_HAPTIC_XINPUT 1
diff --git a/include/SDL_hints.h b/include/SDL_hints.h
index a3a537383..c07c7f9e3 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -634,10 +634,23 @@ extern "C" {
* "0" - HIDAPI driver is not used
* "1" - HIDAPI driver is used
*
- * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ * The default is "0" on Windows, otherwise the value of SDL_HINT_JOYSTICK_HIDAPI
*/
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX "SDL_JOYSTICK_HIDAPI_XBOX"
+ /**
+ * \brief A variable controlling whether the HIDAPI driver for XBox controllers on Windows should pull correlated
+ * data from XInput.
+ *
+ * This variable can be set to the following values:
+ * "0" - HIDAPI Xbox driver will only use HIDAPI data
+ * "1" - HIDAPI Xbox driver will also pull data from XInput, providing better trigger axes, guide button
+ * presses, and rumble support
+ *
+ * The default is "1". This hint applies to any joysticks opened after setting the hint.
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT "SDL_JOYSTICK_HIDAPI_CORRELATE_XINPUT"
+
/**
* \brief A variable controlling whether the HIDAPI driver for Nintendo GameCube controllers should be used.
*
@@ -660,6 +673,15 @@ extern "C" {
*/
#define SDL_HINT_ENABLE_STEAM_CONTROLLERS "SDL_ENABLE_STEAM_CONTROLLERS"
+ /**
+ * \brief A variable controlling whether the RAWINPUT joystick drivers should be used for better handling XInput-capable devices.
+ *
+ * This variable can be set to the following values:
+ * "0" - RAWINPUT drivers are not used
+ * "1" - RAWINPUT drivers are used (the default)
+ *
+ */
+#define SDL_HINT_JOYSTICK_RAWINPUT "SDL_JOYSTICK_RAWINPUT"
/**
* \brief If set to "0" then never set the top most bit on a SDL Window, even if the video mode expects it.
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 3775ad11c..9d92aa5cf 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -439,6 +439,12 @@ static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickG
/* This is a HIDAPI device */
return s_pHIDAPIMapping;
}
+#if SDL_JOYSTICK_RAWINPUT
+ if (SDL_IsJoystickRAWINPUT(*guid)) {
+ /* This is a RAWINPUT device - same data as HIDAPI */
+ return s_pHIDAPIMapping;
+ }
+#endif
#if SDL_JOYSTICK_XINPUT
if (SDL_IsJoystickXInput(*guid)) {
/* This is an XInput device */
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 709d199d1..aeedd296d 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -51,6 +51,13 @@
#endif
static SDL_JoystickDriver *SDL_joystick_drivers[] = {
+#ifdef SDL_JOYSTICK_RAWINPUT /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */
+ /* Also before HIDAPI, as HIDAPI wants to check if this driver is handling things */
+ &SDL_RAWINPUT_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */
+ &SDL_HIDAPI_JoystickDriver,
+#endif
#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
&SDL_WINDOWS_JoystickDriver,
#endif
@@ -75,9 +82,6 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
#ifdef SDL_JOYSTICK_USBHID /* !!! FIXME: "USBHID" is a generic name, and doubly-confusing with HIDAPI next to it. This is the *BSD interface, rename this. */
&SDL_BSD_JoystickDriver,
#endif
-#ifdef SDL_JOYSTICK_HIDAPI
- &SDL_HIDAPI_JoystickDriver,
-#endif
#ifdef SDL_JOYSTICK_VIRTUAL
&SDL_VIRTUAL_JoystickDriver,
#endif
@@ -1729,6 +1733,12 @@ SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid)
return (guid.data[14] == 'h') ? SDL_TRUE : SDL_FALSE;
}
+SDL_bool
+SDL_IsJoystickRAWINPUT(SDL_JoystickGUID guid)
+{
+ return (guid.data[14] == 'r') ? SDL_TRUE : SDL_FALSE;
+}
+
SDL_bool
SDL_IsJoystickVirtual(SDL_JoystickGUID guid)
{
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index e1329738d..6a56cc4eb 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -73,6 +73,9 @@ extern SDL_bool SDL_IsJoystickXInput(SDL_JoystickGUID guid);
/* Function to return whether a joystick guid comes from the HIDAPI driver */
extern SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid);
+/* Function to return whether a joystick guid comes from the RAWINPUT driver */
+extern SDL_bool SDL_IsJoystickRAWINPUT(SDL_JoystickGUID guid);
+
/* Function to return whether a joystick guid comes from the Virtual driver */
extern SDL_bool SDL_IsJoystickVirtual(SDL_JoystickGUID guid);
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index a6b2858d9..5aa7577df 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -150,6 +150,7 @@ extern SDL_JoystickDriver SDL_DUMMY_JoystickDriver;
extern SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver;
extern SDL_JoystickDriver SDL_HAIKU_JoystickDriver;
extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
+extern SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver;
extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c
index 7ef22b673..35b25bd93 100644
--- a/src/joystick/hidapi/SDL_hidapi_gamecube.c
+++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c
@@ -176,11 +176,11 @@ HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
if (ctx->joysticks[i] == -1) {
ResetAxisRange(ctx, i);
- HIDAPI_JoystickConnected(device, &ctx->joysticks[i]);
+ HIDAPI_JoystickConnected(device, &ctx->joysticks[i], SDL_FALSE);
}
} else {
if (ctx->joysticks[i] != -1) {
- HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]);
+ HIDAPI_JoystickDisconnected(device, ctx->joysticks[i], SDL_FALSE);
ctx->joysticks[i] = -1;
}
continue;
@@ -252,7 +252,7 @@ HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device)
if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
if (ctx->joysticks[i] == -1) {
ResetAxisRange(ctx, i);
- HIDAPI_JoystickConnected(device, &ctx->joysticks[i]);
+ HIDAPI_JoystickConnected(device, &ctx->joysticks[i], SDL_FALSE);
}
joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]);
@@ -262,7 +262,7 @@ HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device)
}
} else {
if (ctx->joysticks[i] != -1) {
- HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]);
+ HIDAPI_JoystickDisconnected(device, ctx->joysticks[i], SDL_FALSE);
ctx->joysticks[i] = -1;
}
continue;
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 8288d1fb1..f56fdc326 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -213,7 +213,7 @@ SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index)
static SDL_bool
HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
{
- return HIDAPI_JoystickConnected(device, NULL);
+ return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
}
static int
@@ -496,7 +496,7 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
if (size < 0) {
/* Read error, device is disconnected */
- HIDAPI_JoystickDisconnected(device, joystick->instance_id);
+ HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
}
return (size >= 0);
}
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index 26f72271e..a6747357e 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -671,7 +671,7 @@ static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
static SDL_bool
HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
{
- return HIDAPI_JoystickConnected(device, NULL);
+ return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
}
static int
@@ -1121,7 +1121,7 @@ HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
if (size < 0) {
/* Read error, device is disconnected */
- HIDAPI_JoystickDisconnected(device, joystick->instance_id);
+ HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
}
return (size >= 0);
}
diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c
index 1be44a0f8..d22c9544d 100644
--- a/src/joystick/hidapi/SDL_hidapi_xbox360.c
+++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c
@@ -47,34 +47,190 @@
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
#include "../../core/windows/SDL_windows.h"
+typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState;
+#define GamepadButtons_GUIDE 0x40000000
#define COBJMACROS
#include "windows.gaming.input.h"
#endif
+#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
+#define SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+#endif
typedef struct {
Uint8 last_state[USB_PACKET_LENGTH];
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+ Uint32 match_state; /* Low 16 bits for button states, high 16 for 4 4bit axes */
+ Uint32 last_state_packet;
+#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
SDL_bool xinput_enabled;
+ SDL_bool xinput_correlated;
+ Uint8 xinput_correlation_id;
+ Uint8 xinput_correlation_count;
+ Uint8 xinput_uncorrelate_count;
Uint8 xinput_slot;
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
- SDL_bool coinitialized;
- __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
- __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
- struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
+ SDL_bool wgi_correlated;
+ Uint8 wgi_correlation_id;
+ Uint8 wgi_correlation_count;
+ Uint8 wgi_uncorrelate_count;
+ WindowsGamingInputGamepadState *wgi_slot;
#endif
} SDL_DriverXbox360_Context;
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+static struct {
+ Uint32 last_state_packet;
+ SDL_Joystick *joystick;
+ SDL_Joystick *last_joystick;
+} guide_button_candidate;
+
+typedef struct WindowsMatchState {
+ SHORT match_axes[4];
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+ WORD xinput_buttons;
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+ Uint32 wgi_buttons;
+#endif
+ SDL_bool any_data;
+} WindowsMatchState;
+
+static void HIDAPI_DriverXbox360_FillMatchState(WindowsMatchState *state, Uint32 match_state)
+{
+ int ii;
+ state->any_data = SDL_FALSE;
+ /* SHORT state->match_axes[4] = {
+ (match_state & 0x000F0000) >> 4,
+ (match_state & 0x00F00000) >> 8,
+ (match_state & 0x0F000000) >> 12,
+ (match_state & 0xF0000000) >> 16,
+ }; */
+ for (ii = 0; ii < 4; ii++) {
+ state->match_axes[ii] = (match_state & (0x000F0000 << (ii * 4))) >> (4 + ii * 4);
+ if ((Uint32)(state->match_axes[ii] + 0x1000) > 0x2000) { /* match_state bit is not 0xF, 0x1, or 0x2 */
+ state->any_data = SDL_TRUE;
+ }
+ }
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
-static Uint8 xinput_slots;
+ /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */
+#define XInputAxesMatch(gamepad) (\
+ (Uint32)(gamepad.sThumbLX - state->match_axes[0] + 0x1000) <= 0x2fff && \
+ (Uint32)(~gamepad.sThumbLY - state->match_axes[1] + 0x1000) <= 0x2fff && \
+ (Uint32)(gamepad.sThumbRX - state->match_axes[2] + 0x1000) <= 0x2fff && \
+ (Uint32)(~gamepad.sThumbRY - state->match_axes[3] + 0x1000) <= 0x2fff)
+ /* Explicit
+#define XInputAxesMatch(gamepad) (\
+ SDL_abs((Sint8)((gamepad.sThumbLX & 0xF000) >> 8) - ((match_state & 0x000F0000) >> 12)) <= 0x10 && \
+ SDL_abs((Sint8)((~gamepad.sThumbLY & 0xF000) >> 8) - ((match_state & 0x00F00000) >> 16)) <= 0x10 && \
+ SDL_abs((Sint8)((gamepad.sThumbRX & 0xF000) >> 8) - ((match_state & 0x0F000000) >> 20)) <= 0x10 && \
+ SDL_abs((Sint8)((~gamepad.sThumbRY & 0xF000) >> 8) - ((match_state & 0xF0000000) >> 24)) <= 0x10) */
+
+
+ state->xinput_buttons =
+ /* Bitwise map .RLDUWVQTS.KYXBA -> YXBA..WVQTKSRLDU */
+ match_state << 12 | (match_state & 0x0780) >> 1 | (match_state & 0x0010) << 1 | (match_state & 0x0040) >> 2 | (match_state & 0x7800) >> 11;
+ /* Explicit
+ ((match_state & (1<xinput_buttons)
+ state->any_data = SDL_TRUE;
+#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+ /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */
+#define WindowsGamingInputAxesMatch(gamepad) (\
+ (Uint16)(((Sint16)(gamepad.LeftThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[0] + 0x1000) <= 0x2fff && \
+ (Uint16)((~(Sint16)(gamepad.LeftThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[1] + 0x1000) <= 0x2fff && \
+ (Uint16)(((Sint16)(gamepad.RightThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[2] + 0x1000) <= 0x2fff && \
+ (Uint16)((~(Sint16)(gamepad.RightThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[3] + 0x1000) <= 0x2fff)
+
+
+ state->wgi_buttons =
+ /* Bitwise map .RLD UWVQ TS.K YXBA -> ..QT WVRL DUYX BAKS */
+ /* RStick/LStick (QT) RShould/LShould (WV) DPad R/L/D/U YXBA bac(K) (S)tart */
+ (match_state & 0x0180) << 5 | (match_state & 0x0600) << 1 | (match_state & 0x7800) >> 5 | (match_state & 0x000F) << 2 | (match_state & 0x0010) >> 3 | (match_state & 0x0040) >> 6;
+ /* Explicit
+ ((match_state & (1<wgi_buttons)
+ state->any_data = SDL_TRUE;
+#endif
+
+}
+
+
+#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+static struct {
+ XINPUT_STATE_EX state;
+ SDL_bool connected; /* Currently has an active XInput device */
+ SDL_bool used; /* Is currently mapped to an SDL device */
+ Uint8 correlation_id;
+} xinput_state[XUSER_MAX_COUNT];
+static SDL_bool xinput_device_change = SDL_TRUE;
+static SDL_bool xinput_state_dirty = SDL_TRUE;
+
+static void
+HIDAPI_DriverXbox360_UpdateXInput()
+{
+ DWORD user_index;
+ if (xinput_device_change) {
+ for (user_index = 0; user_index < XUSER_MAX_COUNT; user_index++) {
+ XINPUT_CAPABILITIES capabilities;
+ xinput_state[user_index].connected = XINPUTGETCAPABILITIES(user_index, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS;
+ }
+ xinput_device_change = SDL_FALSE;
+ xinput_state_dirty = SDL_TRUE;
+ }
+ if (xinput_state_dirty) {
+ xinput_state_dirty = SDL_FALSE;
+ for (user_index = 0; user_index < SDL_arraysize(xinput_state); ++user_index) {
+ if (xinput_state[user_index].connected) {
+ if (!XINPUTGETSTATE(user_index, &xinput_state[user_index].state) == ERROR_SUCCESS) {
+ xinput_state[user_index].connected = SDL_FALSE;
+ }
+ }
+ }
+ }
+}
static void
HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
{
if (xinput_slot != XUSER_INDEX_ANY) {
- xinput_slots |= (0x01 << xinput_slot);
+ xinput_state[xinput_slot].used = SDL_TRUE;
}
}
@@ -82,58 +238,209 @@ static void
HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
{
if (xinput_slot != XUSER_INDEX_ANY) {
- xinput_slots &= ~(0x01 << xinput_slot);
+ xinput_state[xinput_slot].used = SDL_FALSE;
}
}
-
static SDL_bool
HIDAPI_DriverXbox360_MissingXInputSlot()
{
- return xinput_slots != 0x0F;
+ int ii;
+ for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
+ if (xinput_state[ii].connected && !xinput_state[ii].used) {
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
}
-static Uint8
-HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
+static SDL_bool
+HIDAPI_DriverXbox360_XInputSlotMatches(const WindowsMatchState *state, Uint8 slot_idx)
{
- DWORD user_index;
- int match_count;
- Uint8 match_slot;
-
- if (!XINPUTGETSTATE) {
- return XUSER_INDEX_ANY;
+ if (xinput_state[slot_idx].connected) {
+ WORD xinput_buttons = xinput_state[slot_idx].state.Gamepad.wButtons;
+ if ((xinput_buttons & ~XINPUT_GAMEPAD_GUIDE) == state->xinput_buttons && XInputAxesMatch(xinput_state[slot_idx].state.Gamepad)) {
+ return SDL_TRUE;
+ }
}
+ return SDL_FALSE;
+}
+
+
+static SDL_bool
+HIDAPI_DriverXbox360_GuessXInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, Uint8 *slot_idx)
+{
+ int user_index;
+ int match_count;
match_count = 0;
for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
- XINPUT_STATE_EX xinput_state;
-
- if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
- if (xinput_state.Gamepad.wButtons == wButtons) {
- ++match_count;
- match_slot = (Uint8)user_index;
- }
+ if (!xinput_state[user_index].used && HIDAPI_DriverXbox360_XInputSlotMatches(state, user_index)) {
+ ++match_count;
+ *slot_idx = (Uint8)user_index;
+ /* Incrementing correlation_id for any match, as negative evidence for others being correlated */
+ *correlation_id = ++xinput_state[user_index].correlation_id;
}
}
- if (match_count == 1) {
- return match_slot;
+ /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
+ Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
+ data. */
+ if (match_count == 1 && state->any_data) {
+ return SDL_TRUE;
}
- return XUSER_INDEX_ANY;
+ return SDL_FALSE;
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+typedef struct WindowsGamingInputGamepadState {
+ __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
+ struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
+ SDL_DriverXbox360_Context *correlated_context;
+ SDL_bool used; /* Is currently mapped to an SDL device */
+ SDL_bool connected; /* Just used during update to track disconnected */
+ Uint8 correlation_id;
+ struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
+} WindowsGamingInputGamepadState;
+
+static struct {
+ WindowsGamingInputGamepadState **per_gamepad;
+ int per_gamepad_count;
+ SDL_bool initialized;
+ SDL_bool dirty;
+ SDL_bool need_device_list_update;
+ int ref_count;
+ __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
+} wgi_state;
+
+static void
+HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, SDL_DriverXbox360_Context *ctx)
+{
+ wgi_slot->used = SDL_TRUE;
+ wgi_slot->correlated_context = ctx;
+}
+
+static void
+HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState *wgi_slot)
+{
+ wgi_slot->used = SDL_FALSE;
+ wgi_slot->correlated_context = NULL;
+}
+
+static SDL_bool
+HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot()
+{
+ int ii;
+ for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
+ if (!wgi_state.per_gamepad[ii]->used) {
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
+}
+
+static void
+HIDAPI_DriverXbox360_UpdateWindowsGamingInput()
+{
+ int ii;
+ if (!wgi_state.gamepad_statics)
+ return;
+
+ if (!wgi_state.dirty)
+ return;
+ wgi_state.dirty = SDL_FALSE;
+
+ if (wgi_state.need_device_list_update) {
+ wgi_state.need_device_list_update = SDL_FALSE;
+ for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
+ wgi_state.per_gamepad[ii]->connected = SDL_FALSE;
+ }
+ HRESULT hr;
+ __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
+
+ hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(wgi_state.gamepad_statics, &gamepads);
+ if (SUCCEEDED(hr)) {
+ unsigned int num_gamepads;
+
+ hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
+ if (SUCCEEDED(hr)) {
+ unsigned int i;
+ for (i = 0; i < num_gamepads; ++i) {
+ __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
+
+ hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
+ if (SUCCEEDED(hr)) {
+ SDL_bool found = SDL_FALSE;
+ int jj;
+ for (jj = 0; jj < wgi_state.per_gamepad_count ; jj++) {
+ if (wgi_state.per_gamepad[jj]->gamepad == gamepad) {
+ found = SDL_TRUE;
+ wgi_state.per_gamepad[jj]->connected = SDL_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ /* New device, add it */
+ wgi_state.per_gamepad_count++;
+ wgi_state.per_gamepad = SDL_realloc(wgi_state.per_gamepad, sizeof(wgi_state.per_gamepad[0]) * wgi_state.per_gamepad_count);
+ if (!wgi_state.per_gamepad) {
+ SDL_OutOfMemory();
+ return;
+ }
+ WindowsGamingInputGamepadState *gamepad_state = SDL_calloc(1, sizeof(*gamepad_state));
+ if (!gamepad_state) {
+ SDL_OutOfMemory();
+ return;
+ }
+ wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1] = gamepad_state;
+ gamepad_state->gamepad = gamepad;
+ gamepad_state->connected = SDL_TRUE;
+ } else {
+ /* Already tracked */
+ __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
+ }
+ }
+ }
+ for (ii = wgi_state.per_gamepad_count - 1; ii >= 0; ii--) {
+ WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
+ if (!gamepad_state->connected) {
+ /* Device missing, must be disconnected */
+ if (gamepad_state->correlated_context) {
+ gamepad_state->correlated_context->wgi_correlated = SDL_FALSE;
+ gamepad_state->correlated_context->wgi_slot = NULL;
+ }
+ __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad_state->gamepad);
+ SDL_free(gamepad_state);
+ wgi_state.per_gamepad[ii] = wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1];
+ --wgi_state.per_gamepad_count;
+ }
+ }
+ }
+ __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
+ }
+ } /* need_device_list_update */
+
+ for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
+ HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(wgi_state.per_gamepad[ii]->gamepad, &wgi_state.per_gamepad[ii]->state);
+ if (!SUCCEEDED(hr)) {
+ wgi_state.per_gamepad[ii]->connected = SDL_FALSE; /* Not used by anything, currently */
+ }
+ }
+}
static void
HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
{
- /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
- if (FAILED(WIN_CoInitialize())) {
- return;
- }
- ctx->coinitialized = SDL_TRUE;
+ wgi_state.need_device_list_update = SDL_TRUE;
+ wgi_state.ref_count++;
+ if (!wgi_state.initialized) {
+ /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
+ if (FAILED(WIN_CoInitialize())) {
+ return;
+ }
+ wgi_state.initialized = SDL_TRUE;
+ wgi_state.dirty = SDL_TRUE;
- {
static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
HRESULT hr;
HMODULE hModule = LoadLibraryA("combase.dll");
@@ -151,7 +458,7 @@ HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
if (SUCCEEDED(hr)) {
- RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &ctx->gamepad_statics);
+ RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &wgi_state.gamepad_statics);
WindowsDeleteStringFunc(hNamespaceString);
}
}
@@ -160,90 +467,112 @@ HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
}
}
-static Uint8
-HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad)
+static SDL_bool
+HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(const WindowsMatchState *state, WindowsGamingInputGamepadState *slot)
{
- HRESULT hr;
- struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
- Uint8 buttons = 0;
-
- hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(gamepad, &state);
- if (SUCCEEDED(hr)) {
- if (state.Buttons & GamepadButtons_A) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
- }
- if (state.Buttons & GamepadButtons_B) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
- }
- if (state.Buttons & GamepadButtons_X) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
- }
- if (state.Buttons & GamepadButtons_Y) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
- }
+ Uint32 wgi_buttons = slot->state.Buttons;
+ if ((wgi_buttons & 0x3FFF) == state->wgi_buttons && WindowsGamingInputAxesMatch(slot->state)) {
+ return SDL_TRUE;
}
- return buttons;
+ return SDL_FALSE;
}
-static void
-HIDAPI_DriverXbox360_GuessGamepad(SDL_DriverXbox360_Context *ctx, Uint8 buttons)
+static SDL_bool
+HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, WindowsGamingInputGamepadState **slot)
{
- HRESULT hr;
- __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
+ int match_count;
- hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(ctx->gamepad_statics, &gamepads);
- if (SUCCEEDED(hr)) {
- unsigned int i, num_gamepads;
-
- hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
- if (SUCCEEDED(hr)) {
- int match_count;
- unsigned int match_slot;
-
- match_count = 0;
- for (i = 0; i < num_gamepads; ++i) {
- __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
-
- hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
- if (SUCCEEDED(hr)) {
- Uint8 gamepad_buttons = HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(gamepad);
- if (buttons == gamepad_buttons) {
- ++match_count;
- match_slot = i;
- }
- __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
- }
- }
- if (match_count == 1) {
- hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, match_slot, &ctx->gamepad);
- if (SUCCEEDED(hr)) {
- }
- }
+ match_count = 0;
+ for (int user_index = 0; user_index < wgi_state.per_gamepad_count; ++user_index) {
+ WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[user_index];
+ if (HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(state, gamepad_state)) {
+ ++match_count;
+ *slot = gamepad_state;
+ /* Incrementing correlation_id for any match, as negative evidence for others being correlated */
+ *correlation_id = ++gamepad_state->correlation_id;
}
- __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
}
+ /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
+ Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
+ data. */
+ if (match_count == 1 && state->any_data) {
+ return SDL_TRUE;
+ }
+ return SDL_FALSE;
}
static void
HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
{
- if (ctx->gamepad_statics) {
- __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(ctx->gamepad_statics);
- ctx->gamepad_statics = NULL;
- }
- if (ctx->gamepad) {
- __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(ctx->gamepad);
- ctx->gamepad = NULL;
- }
-
- if (ctx->coinitialized) {
+ wgi_state.need_device_list_update = SDL_TRUE;
+ --wgi_state.ref_count;
+ if (!wgi_state.ref_count && wgi_state.initialized) {
+ for (int ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
+ __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(wgi_state.per_gamepad[ii]->gamepad);
+ }
+ if (wgi_state.per_gamepad) {
+ SDL_free(wgi_state.per_gamepad);
+ wgi_state.per_gamepad = NULL;
+ }
+ wgi_state.per_gamepad_count = 0;
+ if (wgi_state.gamepad_statics) {
+ __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics);
+ wgi_state.gamepad_statics = NULL;
+ }
WIN_CoUninitialize();
- ctx->coinitialized = SDL_FALSE;
+ wgi_state.initialized = SDL_FALSE;
}
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
+static void
+HIDAPI_DriverXbox360_PostUpdate(void)
+{
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+ SDL_bool unmapped_guide_pressed = SDL_FALSE;
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+ if (!wgi_state.dirty) {
+ int ii;
+ for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
+ WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
+ if (!gamepad_state->used && (gamepad_state->state.Buttons & GamepadButtons_GUIDE)) {
+ unmapped_guide_pressed = SDL_TRUE;
+ break;
+ }
+ }
+ }
+ wgi_state.dirty = SDL_TRUE;
+#endif
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+ if (!xinput_state_dirty) {
+ int ii;
+ for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
+ if (xinput_state[ii].connected && !xinput_state[ii].used && (xinput_state[ii].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE)) {
+ unmapped_guide_pressed = SDL_TRUE;
+ break;
+ }
+ }
+ }
+ xinput_state_dirty = SDL_TRUE;
+#endif
+
+ if (unmapped_guide_pressed) {
+ if (guide_button_candidate.joystick && !guide_button_candidate.last_joystick) {
+ SDL_PrivateJoystickButton(guide_button_candidate.joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_PRESSED);
+ guide_button_candidate.last_joystick = guide_button_candidate.joystick;
+ }
+ } else if (guide_button_candidate.last_joystick) {
+ SDL_PrivateJoystickButton(guide_button_candidate.last_joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
+ guide_button_candidate.last_joystick = NULL;
+ }
+ guide_button_candidate.joystick = NULL;
+#endif
+}
+
#if defined(__MACOSX__)
static SDL_bool
IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
@@ -320,7 +649,7 @@ static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
static SDL_bool
HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
{
- return HIDAPI_JoystickConnected(device, NULL);
+ return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
}
static int
@@ -349,17 +678,20 @@ HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
return SDL_FALSE;
}
- device->dev = hid_open_path(device->path, 0);
- if (!device->dev) {
- SDL_free(ctx);
- SDL_SetError("Couldn't open %s", device->path);
- return SDL_FALSE;
+ if (device->path) { /* else opened for RAWINPUT driver */
+ device->dev = hid_open_path(device->path, 0);
+ if (!device->dev) {
+ SDL_SetError("Couldn't open %s", device->path);
+ SDL_free(ctx);
+ return SDL_FALSE;
+ }
}
device->context = ctx;
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
- ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
- if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
+ xinput_device_change = SDL_TRUE;
+ ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT, SDL_TRUE);
+ if (ctx->xinput_enabled && (WIN_LoadXInputDLL() < 0 || !XINPUTGETSTATE)) {
ctx->xinput_enabled = SDL_FALSE;
}
ctx->xinput_slot = XUSER_INDEX_ANY;
@@ -370,7 +702,7 @@ HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
/* Set the controller LED */
player_index = SDL_JoystickGetPlayerIndex(joystick);
- if (player_index >= 0) {
+ if (player_index >= 0 && device->dev) {
SetSlotLED(device->dev, (player_index % 4));
}
@@ -393,12 +725,12 @@ HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
SDL_bool rumbled = SDL_FALSE;
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
- if (!rumbled && ctx->gamepad) {
+ if (!rumbled && ctx->wgi_correlated) {
+ WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
HRESULT hr;
-
- ctx->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
- ctx->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
- hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(ctx->gamepad, ctx->vibration);
+ gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
+ gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
+ hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration);
if (SUCCEEDED(hr)) {
rumbled = SDL_TRUE;
}
@@ -406,7 +738,7 @@ HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
- if (!rumbled && ctx->xinput_slot != XUSER_INDEX_ANY) {
+ if (!rumbled && ctx->xinput_correlated) {
XINPUT_VIBRATION XVibration;
if (!XINPUTSETSTATE) {
@@ -473,6 +805,13 @@ HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
static void
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
{
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+ Uint32 match_state = ctx->match_state;
+ /* Update match_state with button bit, then fall through */
+# define SDL_PrivateJoystickButton(joystick, button, state) if (state) match_state |= 1 << (button); else match_state &=~(1<<(button)); SDL_PrivateJoystickButton(joystick, button, state)
+ /* Grab high 4 bits of value, then fall through */
+# define SDL_PrivateJoystickAxis(joystick, axis, value) if (axis < 4) match_state = (match_state & ~(0xF << (4 * axis + 16))) | ((value) & 0xF000) << (4 * axis + 4); SDL_PrivateJoystickAxis(joystick, axis, value)
+#endif
Sint16 axis;
SDL_bool has_trigger_data = SDL_FALSE;
@@ -543,96 +882,56 @@ HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev,
axis = (int)*(Uint16*)(&data[6]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
-#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
- if (ctx->gamepad_statics && !ctx->gamepad) {
- Uint8 buttons = 0;
-
- if (data[10] & 0x01) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
- }
- if (data[10] & 0x02) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
- }
- if (data[10] & 0x04) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
- }
- if (data[10] & 0x08) {
- buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
- }
- if (buttons != 0) {
- HIDAPI_DriverXbox360_GuessGamepad(ctx, buttons);
- }
- }
-
- if (ctx->gamepad) {
- HRESULT hr;
- struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
-
- hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(ctx->gamepad, &state);
- if (SUCCEEDED(hr)) {
- SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state.Buttons & 0x40000000) ? SDL_PRESSED : SDL_RELEASED);
- SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state.LeftTrigger * SDL_MAX_UINT16)) - 32768);
- SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state.RightTrigger * SDL_MAX_UINT16)) - 32768);
- has_trigger_data = SDL_TRUE;
- }
- }
-#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+#undef SDL_PrivateJoystickAxis
+#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
- if (ctx->xinput_enabled) {
- if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
- WORD wButtons = 0;
-
- if (data[10] & 0x01) {
- wButtons |= XINPUT_GAMEPAD_A;
- }
- if (data[10] & 0x02) {
- wButtons |= XINPUT_GAMEPAD_B;
- }
- if (data[10] & 0x04) {
- wButtons |= XINPUT_GAMEPAD_X;
- }
- if (data[10] & 0x08) {
- wButtons |= XINPUT_GAMEPAD_Y;
- }
- if (wButtons != 0) {
- Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
- if (xinput_slot != XUSER_INDEX_ANY) {
- HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
- ctx->xinput_slot = xinput_slot;
- }
- }
- }
-
- if (!has_trigger_data && ctx->xinput_slot != XUSER_INDEX_ANY) {
- XINPUT_STATE_EX xinput_state;
-
- if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
- SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
- SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
- SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
- has_trigger_data = SDL_TRUE;
- }
- }
+ /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */
+ if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) {
+ has_trigger_data = SDL_TRUE;
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+ if (!has_trigger_data && ctx->wgi_correlated) {
+ has_trigger_data = SDL_TRUE;
+ }
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
+
if (!has_trigger_data) {
axis = (data[9] * 257) - 32768;
if (data[9] < 0x80) {
axis = -axis * 2 - 32769;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
} else if (data[9] > 0x80) {
axis = axis * 2 - 32767;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
} else {
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
}
}
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+ ctx->match_state = match_state;
+ ctx->last_state_packet = SDL_GetTicks();
+#undef SDL_PrivateJoystickButton
+#endif
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
+
+#ifdef SDL_JOYSTICK_RAWINPUT
+static void
+HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 *data, int size)
+{
+ SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
+ HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, data, size);
+}
+#endif
+
#else
static void
@@ -689,13 +988,243 @@ HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev,
}
#endif /* __WIN32__ */
+static void
+HIDAPI_DriverXbox360_UpdateOtherAPIs(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
+{
+ SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
+ SDL_bool has_trigger_data = SDL_FALSE;
+ SDL_bool correlated = SDL_FALSE;
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+ WindowsMatchState match_state_xinput;
+#endif
+
+ /* Poll for trigger data once (not per-state-packet) */
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+ /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */
+ if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) {
+ HIDAPI_DriverXbox360_UpdateXInput();
+ if (xinput_state[ctx->xinput_slot].connected) {
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state[ctx->xinput_slot].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bLeftTrigger * 257) - 32768);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bRightTrigger * 257) - 32768);
+ has_trigger_data = SDL_TRUE;
+ }
+ }
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+ if (!has_trigger_data && ctx->wgi_correlated) {
+ HIDAPI_DriverXbox360_UpdateWindowsGamingInput(); /* May detect disconnect / cause uncorrelation */
+ if (ctx->wgi_correlated) { /* Still connected */
+ struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading *state = &ctx->wgi_slot->state;
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state->Buttons & GamepadButtons_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state->LeftTrigger * SDL_MAX_UINT16)) - 32768);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state->RightTrigger * SDL_MAX_UINT16)) - 32768);
+ has_trigger_data = SDL_TRUE;
+ }
+ }
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+ HIDAPI_DriverXbox360_FillMatchState(&match_state_xinput, ctx->match_state);
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+ /* Parallel logic to WINDOWS_XINPUT below */
+ HIDAPI_DriverXbox360_UpdateWindowsGamingInput();
+ if (ctx->wgi_correlated) {
+ /* We have been previously correlated, ensure we are still matching, see comments in XINPUT section */
+ if (HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(&match_state_xinput, ctx->wgi_slot)) {
+ ctx->wgi_uncorrelate_count = 0;
+ } else {
+ ++ctx->wgi_uncorrelate_count;
+ /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event
+ pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but
+ let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision
+ triggers for a frame. */
+ if (ctx->wgi_uncorrelate_count >= 3) {
+#ifdef DEBUG_JOYSTICK
+ SDL_Log("UN-Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, ctx->wgi_slot);
+#endif
+ HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(ctx->wgi_slot);
+ ctx->wgi_correlated = SDL_FALSE;
+ ctx->wgi_correlation_count = 0;
+ /* Force immediate update of triggers */
+ HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
+ /* Force release of Guide button, it can't possibly be down on this device now. */
+ /* It gets left down if we were actually correlated incorrectly and it was released on the WindowsGamingInput
+ device but we didn't get a state packet. */
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
+ }
+ }
+ }
+ if (!ctx->wgi_correlated) {
+ SDL_bool new_correlation_count = 0;
+ if (HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot()) {
+ Uint8 correlation_id;
+ WindowsGamingInputGamepadState *slot_idx;
+ if (HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) {
+ /* we match exactly one WindowsGamingInput device */
+ /* Probably can do without wgi_correlation_count, just check and clear wgi_slot to NULL, unless we need
+ even more frames to be sure. */
+ if (ctx->wgi_correlation_count && ctx->wgi_slot == slot_idx) {
+ /* was correlated previously, and still the same device */
+ if (ctx->wgi_correlation_id + 1 == correlation_id) {
+ /* no one else was correlated in the meantime */
+ new_correlation_count = ctx->wgi_correlation_count + 1;
+ if (new_correlation_count == 2) {
+ /* correlation stayed steady and uncontested across multiple frames, guaranteed match */
+ ctx->wgi_correlated = SDL_TRUE;
+#ifdef DEBUG_JOYSTICK
+ SDL_Log("Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, slot_idx);
+#endif
+ correlated = SDL_TRUE;
+ HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(ctx->wgi_slot, ctx);
+ /* If the generalized Guide button was using us, it doesn't need to anymore */
+ if (guide_button_candidate.joystick == joystick)
+ guide_button_candidate.joystick = NULL;
+ if (guide_button_candidate.last_joystick == joystick)
+ guide_button_candidate.last_joystick = NULL;
+ /* Force immediate update of guide button / triggers */
+ HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
+ }
+ } else {
+ /* someone else also possibly correlated to this device, start over */
+ new_correlation_count = 1;
+ }
+ } else {
+ /* new possible correlation */
+ new_correlation_count = 1;
+ ctx->wgi_slot = slot_idx;
+ }
+ ctx->wgi_correlation_id = correlation_id;
+ } else {
+ /* Match multiple WindowsGamingInput devices, or none (possibly due to no buttons pressed) */
+ }
+ }
+ ctx->wgi_correlation_count = new_correlation_count;
+ } else {
+ correlated = SDL_TRUE;
+ }
+#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+ /* Parallel logic to WINDOWS_GAMING_INPUT above */
+ if (ctx->xinput_enabled) {
+ HIDAPI_DriverXbox360_UpdateXInput();
+ if (ctx->xinput_correlated) {
+ /* We have been previously correlated, ensure we are still matching */
+ /* This is required to deal with two (mostly) un-preventable mis-correlation situations:
+ A) Since the HID data stream does not provide an initial state (but polling XInput does), if we open
+ 5 controllers (#1-4 XInput mapped, #5 is not), and controller 1 had the A button down (and we don't
+ know), and the user presses A on controller #5, we'll see exactly 1 controller with A down (#5) and
+ exactly 1 XInput device with A down (#1), and incorrectly correlate. This code will then un-correlate
+ when A is released from either controller #1 or #5.
+ B) Since the app may not open all controllers, we could have a similar situation where only controller #5
+ is opened, and the user holds A on controllers #1 and #5 simultaneously - again we see only 1 controller
+ with A down and 1 XInput device with A down, and incorrectly correlate. This should be very unusual
+ (only when apps do not open all controllers, yet are listening to Guide button presses, yet
+ for some reason want to ignore guide button presses on the un-opened controllers, yet users are
+ pressing buttons on the unopened controllers), and will resolve itself when either button is released
+ and we un-correlate. We could prevent this by processing the state packets for *all* controllers,
+ even un-opened ones, as that would allow more precise correlation.
+ */
+ if (HIDAPI_DriverXbox360_XInputSlotMatches(&match_state_xinput, ctx->xinput_slot)) {
+ ctx->xinput_uncorrelate_count = 0;
+ } else {
+ ++ctx->xinput_uncorrelate_count;
+ /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event
+ pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but
+ let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision
+ triggers for a frame. */
+ if (ctx->xinput_uncorrelate_count >= 3) {
+#ifdef DEBUG_JOYSTICK
+ SDL_Log("UN-Correlated joystick %d to XInput device #%d\n", joystick->instance_id, ctx->xinput_slot);
+#endif
+ HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
+ ctx->xinput_correlated = SDL_FALSE;
+ ctx->xinput_correlation_count = 0;
+ /* Force immediate update of triggers */
+ HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
+ /* Force release of Guide button, it can't possibly be down on this device now. */
+ /* It gets left down if we were actually correlated incorrectly and it was released on the XInput
+ device but we didn't get a state packet. */
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
+ }
+ }
+ }
+ if (!ctx->xinput_correlated) {
+ SDL_bool new_correlation_count = 0;
+ if (HIDAPI_DriverXbox360_MissingXInputSlot()) {
+ Uint8 correlation_id;
+ Uint8 slot_idx;
+ if (HIDAPI_DriverXbox360_GuessXInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) {
+ /* we match exactly one XInput device */
+ /* Probably can do without xinput_correlation_count, just check and clear xinput_slot to ANY, unless
+ we need even more frames to be sure */
+ if (ctx->xinput_correlation_count && ctx->xinput_slot == slot_idx) {
+ /* was correlated previously, and still the same device */
+ if (ctx->xinput_correlation_id + 1 == correlation_id) {
+ /* no one else was correlated in the meantime */
+ new_correlation_count = ctx->xinput_correlation_count + 1;
+ if (new_correlation_count == 2) {
+ /* correlation stayed steady and uncontested across multiple frames, guaranteed match */
+ ctx->xinput_correlated = SDL_TRUE;
+#ifdef DEBUG_JOYSTICK
+ SDL_Log("Correlated joystick %d to XInput device #%d\n", joystick->instance_id, slot_idx);
+#endif
+ correlated = SDL_TRUE;
+ HIDAPI_DriverXbox360_MarkXInputSlotUsed(ctx->xinput_slot);
+ /* If the generalized Guide button was using us, it doesn't need to anymore */
+ if (guide_button_candidate.joystick == joystick)
+ guide_button_candidate.joystick = NULL;
+ if (guide_button_candidate.last_joystick == joystick)
+ guide_button_candidate.last_joystick = NULL;
+ /* Force immediate update of guide button / triggers */
+ HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
+ }
+ } else {
+ /* someone else also possibly correlated to this device, start over */
+ new_correlation_count = 1;
+ }
+ } else {
+ /* new possible correlation */
+ new_correlation_count = 1;
+ ctx->xinput_slot = slot_idx;
+ }
+ ctx->xinput_correlation_id = correlation_id;
+ } else {
+ /* Match multiple XInput devices, or none (possibly due to no buttons pressed) */
+ }
+ }
+ ctx->xinput_correlation_count = new_correlation_count;
+ } else {
+ correlated = SDL_TRUE;
+ }
+ }
+#endif
+
+ if (!correlated) {
+ if (!guide_button_candidate.joystick ||
+ (ctx->last_state_packet && (
+ !guide_button_candidate.last_state_packet ||
+ SDL_TICKS_PASSED(ctx->last_state_packet, guide_button_candidate.last_state_packet)
+ ))
+ ) {
+ guide_button_candidate.joystick = joystick;
+ guide_button_candidate.last_state_packet = ctx->last_state_packet;
+ }
+ }
+#endif
+
+}
+
static SDL_bool
HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH];
- int size;
+ int size = 0;
if (device->num_joysticks > 0) {
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
@@ -704,14 +1233,17 @@ HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
return SDL_FALSE;
}
- while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
+ while (device->dev && (size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size);
}
if (size < 0) {
/* Read error, device is disconnected */
- HIDAPI_JoystickDisconnected(device, joystick->instance_id);
+ HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
+ } else {
+ HIDAPI_DriverXbox360_UpdateOtherAPIs(device, joystick);
}
+
return (size >= 0);
}
@@ -721,10 +1253,20 @@ HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joys
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
+ if (guide_button_candidate.joystick == joystick)
+ guide_button_candidate.joystick = NULL;
+ if (guide_button_candidate.last_joystick == joystick)
+ guide_button_candidate.last_joystick = NULL;
+#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+ xinput_device_change = SDL_TRUE;
if (ctx->xinput_enabled) {
- HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
+ if (ctx->xinput_correlated) {
+ HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
+ }
WIN_UnloadXInputDLL();
}
#endif
@@ -732,8 +1274,10 @@ HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joys
HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx);
#endif
- hid_close(device->dev);
- device->dev = NULL;
+ if (device->dev) {
+ hid_close(device->dev);
+ device->dev = NULL;
+ }
SDL_free(device->context);
device->context = NULL;
@@ -757,7 +1301,11 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
HIDAPI_DriverXbox360_OpenJoystick,
HIDAPI_DriverXbox360_RumbleJoystick,
HIDAPI_DriverXbox360_CloseJoystick,
- HIDAPI_DriverXbox360_FreeDevice
+ HIDAPI_DriverXbox360_FreeDevice,
+ HIDAPI_DriverXbox360_PostUpdate,
+#ifdef SDL_JOYSTICK_RAWINPUT
+ HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT,
+#endif
};
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c
index 13dca5e56..9834870a5 100644
--- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c
+++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c
@@ -231,10 +231,10 @@ HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
if (connected) {
SDL_JoystickID joystickID;
- HIDAPI_JoystickConnected(device, &joystickID);
+ HIDAPI_JoystickConnected(device, &joystickID, SDL_FALSE);
} else if (device->num_joysticks > 0) {
- HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
+ HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE);
}
}
} else if (size == 29 && data[0] == 0x00 && data[1] == 0x0f && data[2] == 0x00 && data[3] == 0xf0) {
@@ -262,7 +262,7 @@ HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
if (joystick) {
if (size < 0) {
/* Read error, device is disconnected */
- HIDAPI_JoystickDisconnected(device, joystick->instance_id);
+ HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
}
}
return (size >= 0);
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index 991b81f84..48568d460 100644
--- a/src/joystick/hidapi/SDL_hidapi_xboxone.c
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -279,7 +279,7 @@ HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
static SDL_bool
HIDAPI_DriverXboxOne_InitDevice(SDL_HIDAPI_Device *device)
{
- return HIDAPI_JoystickConnected(device, NULL);
+ return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
}
static int
@@ -694,7 +694,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
!ControllerSendsWaitingForInit(device->vendor_id, device->product_id)) {
if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->start_time + CONTROLLER_INIT_DELAY_MS)) {
if (!SendControllerInit(device, ctx)) {
- HIDAPI_JoystickDisconnected(device, joystick->instance_id);
+ HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
return SDL_FALSE;
}
ctx->initialized = SDL_TRUE;
@@ -737,7 +737,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
SDL_Log("Delay after init: %ums\n", SDL_GetTicks() - ctx->start_time);
#endif
if (!SendControllerInit(device, ctx)) {
- HIDAPI_JoystickDisconnected(device, joystick->instance_id);
+ HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
return SDL_FALSE;
}
ctx->initialized = SDL_TRUE;
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 9eeaf0872..ad7446161 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -37,6 +37,7 @@
#if defined(__WIN32__)
#include "../../core/windows/SDL_windows.h"
+#include "../windows/SDL_rawinputjoystick_c.h"
#endif
#if defined(__MACOSX__)
@@ -419,6 +420,13 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
return NULL;
}
+#ifdef SDL_JOYSTICK_RAWINPUT
+ if (RAWINPUT_IsDevicePresent(device->vendor_id, device->product_id, device->version)) {
+ /* The RAWINPUT driver is taking care of this device */
+ return NULL;
+ }
+#endif
+
if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
return NULL;
}
@@ -502,7 +510,7 @@ HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
/* Disconnect any joysticks */
while (device->num_joysticks) {
- HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
+ HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE);
}
device->driver->FreeDevice(device);
@@ -575,6 +583,11 @@ HIDAPI_JoystickInit(void)
return -1;
}
+#ifdef __WINDOWS__
+ /* On Windows, turns out HIDAPI for Xbox controllers doesn't allow background input, so off by default */
+ SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0", SDL_HINT_DEFAULT);
+#endif
+
for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
@@ -591,7 +604,7 @@ HIDAPI_JoystickInit(void)
}
SDL_bool
-HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
+HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, SDL_bool is_external)
{
SDL_JoystickID joystickID;
SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
@@ -602,7 +615,9 @@ HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
joystickID = SDL_GetNextJoystickInstanceID();
device->joysticks = joysticks;
device->joysticks[device->num_joysticks++] = joystickID;
- ++SDL_HIDAPI_numjoysticks;
+ if (!is_external) {
+ ++SDL_HIDAPI_numjoysticks;
+ }
SDL_PrivateJoystickAdded(joystickID);
@@ -613,20 +628,22 @@ HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
}
void
-HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
+HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID, SDL_bool is_external)
{
int i;
for (i = 0; i < device->num_joysticks; ++i) {
if (device->joysticks[i] == joystickID) {
SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID);
- if (joystick) {
+ if (joystick && !is_external) {
HIDAPI_JoystickClose(joystick);
}
SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1);
--device->num_joysticks;
- --SDL_HIDAPI_numjoysticks;
+ if (!is_external) {
+ --SDL_HIDAPI_numjoysticks;
+ }
if (device->num_joysticks == 0) {
SDL_free(device->joysticks);
device->joysticks = NULL;
@@ -882,6 +899,7 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons
static void
HIDAPI_JoystickDetect(void)
{
+ int i;
if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
HIDAPI_UpdateDiscovery();
if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
@@ -891,6 +909,12 @@ HIDAPI_JoystickDetect(void)
}
SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
}
+ for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+ SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+ if (driver->enabled && driver->PostUpdate) {
+ driver->PostUpdate();
+ }
+ }
}
void
diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h
index 562e8fa65..363ed3273 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick_c.h
+++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h
@@ -40,8 +40,6 @@
#ifdef __WINDOWS__
/* On Windows, Xbox One controllers are handled by the Xbox 360 driver */
#undef SDL_JOYSTICK_HIDAPI_XBOXONE
-/* It turns out HIDAPI for Xbox controllers doesn't allow background input */
-#undef SDL_JOYSTICK_HIDAPI_XBOX360
#endif
#ifdef __MACOSX__
@@ -103,6 +101,10 @@ typedef struct _SDL_HIDAPI_DeviceDriver
int (*RumbleJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
void (*FreeDevice)(SDL_HIDAPI_Device *device);
+ void (*PostUpdate)(void);
+#ifdef SDL_JOYSTICK_RAWINPUT
+ void (*HandleStatePacketFromRAWINPUT)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 *data, int size);
+#endif
} SDL_HIDAPI_DeviceDriver;
@@ -120,8 +122,8 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube;
extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
extern void HIDAPI_UpdateDevices(void);
-extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID);
-extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID);
+extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, SDL_bool is_external);
+extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID, SDL_bool is_external);
#endif /* SDL_JOYSTICK_HIDAPI_H */
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index 578a63604..13e549e08 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -26,6 +26,7 @@
#include "SDL_windowsjoystick_c.h"
#include "SDL_dinputjoystick_c.h"
+#include "SDL_rawinputjoystick_c.h"
#include "SDL_xinputjoystick_c.h"
#include "../hidapi/SDL_hidapijoystick_c.h"
@@ -672,6 +673,14 @@ EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
}
#endif
+#ifdef SDL_JOYSTICK_RAWINPUT
+ if (RAWINPUT_IsDevicePresent(vendor, product, 0)) {
+ /* The RAWINPUT driver is taking care of this device */
+ SDL_free(pNewJoystick);
+ return DIENUM_CONTINUE;
+ }
+#endif
+
WINDOWS_AddJoystickDevice(pNewJoystick);
return DIENUM_CONTINUE; /* get next device, please */
diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c
index 85ebb5c5b..a70fa173f 100644
--- a/src/joystick/windows/SDL_xinputjoystick.c
+++ b/src/joystick/windows/SDL_xinputjoystick.c
@@ -30,6 +30,7 @@
#include "SDL_timer.h"
#include "SDL_windowsjoystick_c.h"
#include "SDL_xinputjoystick_c.h"
+#include "SDL_rawinputjoystick_c.h"
#include "../hidapi/SDL_hidapijoystick_c.h"
/*
@@ -292,6 +293,14 @@ AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
}
#endif
+#ifdef SDL_JOYSTICK_RAWINPUT
+ if (RAWINPUT_IsDevicePresent(vendor, product, version)) {
+ /* The RAWINPUT driver is taking care of this device */
+ SDL_free(pNewJoystick);
+ return;
+ }
+#endif
+
WINDOWS_AddJoystickDevice(pNewJoystick);
}
@@ -318,6 +327,18 @@ SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
const Uint8 userid = (Uint8)iuserid;
XINPUT_CAPABILITIES capabilities;
if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
+ /* Adding a new device, must handle all removes first, or GuessXInputDevice goes terribly wrong (returns
+ a product/vendor ID that is not even attached to the system) when we get a remove and add on the same tick
+ (e.g. when disconnecting a device and the OS reassigns which userid an already-attached controller is)
+ */
+ int iuserid2;
+ for (iuserid2 = iuserid - 1; iuserid2 >= 0; iuserid2--) {
+ const Uint8 userid2 = (Uint8)iuserid2;
+ XINPUT_CAPABILITIES capabilities2;
+ if (XINPUTGETCAPABILITIES(userid2, XINPUT_FLAG_GAMEPAD, &capabilities2) != ERROR_SUCCESS) {
+ DelXInputDevice(userid2);
+ }
+ }
AddXInputDevice(userid, capabilities.SubType, pContext);
} else {
DelXInputDevice(userid);
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index 237d38082..c1162937d 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -30,6 +30,7 @@
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
+#include "../../joystick/windows/SDL_rawinputjoystick_c.h"
#include "SDL_windowsvideo.h"
#include "SDL_windowswindow.h"
#include "SDL_hints.h"
@@ -808,9 +809,18 @@ WIN_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
}
}
+static LRESULT CALLBACK SDL_HelperWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+#if SDL_JOYSTICK_RAWINPUT
+ if (RAWINPUT_WindowProc(hWnd, msg, wParam, lParam) == 0) {
+ return 0;
+ }
+#endif
+ return DefWindowProc(hWnd, msg, wParam, lParam);
+}
/*
- * Creates a HelperWindow used for DirectInput events.
+ * Creates a HelperWindow used for DirectInput and RawInput events.
*/
int
SDL_HelperWindowCreate(void)
@@ -825,7 +835,7 @@ SDL_HelperWindowCreate(void)
/* Create the class. */
SDL_zero(wce);
- wce.lpfnWndProc = DefWindowProc;
+ wce.lpfnWndProc = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE) ? SDL_HelperWindowProc : DefWindowProc;
wce.lpszClassName = (LPCWSTR) SDL_HelperWindowClassName;
wce.hInstance = hInstance;