First shot at Windows XInput haptics.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 10 Mar 2013 13:05:47 -0400
changeset 6990 2514368c2aaf
parent 6989 be9c51af4f94
child 6991 ff49588e3ddb
First shot at Windows XInput haptics.
include/SDL_hints.h
src/core/windows/SDL_windows.c
src/core/windows/SDL_windows.h
src/haptic/windows/SDL_syshaptic.c
src/joystick/windows/SDL_dxjoystick.c
src/joystick/windows/SDL_dxjoystick_c.h
--- a/include/SDL_hints.h	Sun Mar 10 09:09:31 2013 -0700
+++ b/include/SDL_hints.h	Sun Mar 10 13:05:47 2013 -0400
@@ -203,7 +203,7 @@
  *    "0"       - Disable XInput timer (only uses direct input)
  *    "1"       - Enable XInput timer (the default)
  */
-#define SD_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"
+#define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"
 
 
 /**
--- a/src/core/windows/SDL_windows.c	Sun Mar 10 09:09:31 2013 -0700
+++ b/src/core/windows/SDL_windows.c	Sun Mar 10 13:05:47 2013 -0400
@@ -24,10 +24,73 @@
 
 #include "SDL_error.h"
 #include "SDL_windows.h"
+#include "SDL_assert.h"
 
 #include <objbase.h>  /* for CoInitialize/CoUninitialize */
 
 
+XInputGetState_t SDL_XInputGetState = NULL;
+XInputSetState_t SDL_XInputSetState = NULL;
+XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
+DWORD SDL_XInputVersion = 0;
+
+static HANDLE s_pXInputDLL = 0;
+static int s_XInputDLLRefCount = 0;
+
+int
+WIN_LoadXInputDLL(void)
+{
+    DWORD version = 0;
+
+    if (s_pXInputDLL) {
+        SDL_assert(s_XInputDLLRefCount > 0);
+        s_XInputDLLRefCount++;
+        return 0;  /* already loaded */
+    }
+
+    version = (1 << 16) | 4;
+    s_pXInputDLL = LoadLibrary( L"XInput1_4.dll" );  // 1.4 Ships with Windows 8.
+    if (!s_pXInputDLL) {
+        version = (1 << 16) | 3;
+        s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" );  // 1.3 Ships with Vista and Win7, can be installed as a restributable component.
+    }
+    if (!s_pXInputDLL) {
+        s_pXInputDLL = LoadLibrary( L"bin\\XInput1_3.dll" );
+    }
+    if (!s_pXInputDLL) {
+        return -1;
+    }
+
+    SDL_assert(s_XInputDLLRefCount == 0);
+    SDL_XInputVersion = version;
+    s_XInputDLLRefCount = 1;
+
+    /* 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think... */
+    SDL_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );
+    SDL_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" );
+    SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" );
+    if ( !SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities ) {
+        WIN_UnloadXInputDLL();
+        return -1;
+    }
+
+    return 0;
+}
+
+void
+WIN_UnloadXInputDLL(void)
+{
+    if ( s_pXInputDLL ) {
+        SDL_assert(s_XInputDLLRefCount > 0);
+        if (--s_XInputDLLRefCount == 0) {
+            FreeLibrary( s_pXInputDLL );
+            s_pXInputDLL = NULL;
+        }
+    } else {
+        SDL_assert(s_XInputDLLRefCount == 0);
+    }
+}
+
 /* Sets an error message based on GetLastError() */
 void
 WIN_SetError(const char *prefix)
--- a/src/core/windows/SDL_windows.h	Sun Mar 10 09:09:31 2013 -0700
+++ b/src/core/windows/SDL_windows.h	Sun Mar 10 13:05:47 2013 -0400
@@ -33,7 +33,7 @@
 #define _WIN32_WINNT  0x501   /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */
 
 #include <windows.h>
-
+#include <xinput.h>
 
 /* Routines to convert from UTF8 to native Windows text */
 #if UNICODE
@@ -51,6 +51,64 @@
 extern HRESULT WIN_CoInitialize(void);
 extern void WIN_CoUninitialize(void);
 
+/* typedef's for XInput structs we use */
+typedef struct
+{
+    WORD wButtons;
+    BYTE bLeftTrigger;
+    BYTE bRightTrigger;
+    SHORT sThumbLX;
+    SHORT sThumbLY;
+    SHORT sThumbRX;
+    SHORT sThumbRY;
+    DWORD dwPaddingReserved;
+} XINPUT_GAMEPAD_EX;
+
+typedef struct 
+{
+    DWORD dwPacketNumber;
+    XINPUT_GAMEPAD_EX Gamepad;
+} XINPUT_STATE_EX;
+
+
+/* Forward decl's for XInput API's we load dynamically and use if available */
+typedef DWORD (WINAPI *XInputGetState_t)
+	(
+	DWORD         dwUserIndex,  // [in] Index of the gamer associated with the device
+	XINPUT_STATE_EX* pState        // [out] Receives the current state
+	);
+
+typedef DWORD (WINAPI *XInputSetState_t)
+	(
+	DWORD             dwUserIndex,  // [in] Index of the gamer associated with the device
+	XINPUT_VIBRATION* pVibration    // [in, out] The vibration information to send to the controller
+	);
+
+typedef DWORD (WINAPI *XInputGetCapabilities_t)
+	(
+	DWORD                dwUserIndex,   // [in] Index of the gamer associated with the device
+	DWORD                dwFlags,       // [in] Input flags that identify the device type
+	XINPUT_CAPABILITIES* pCapabilities  // [out] Receives the capabilities
+	);
+
+extern int WIN_LoadXInputDLL(void);
+extern void WIN_UnloadXInputDLL(void);
+
+extern XInputGetState_t SDL_XInputGetState;
+extern XInputSetState_t SDL_XInputSetState;
+extern XInputGetCapabilities_t SDL_XInputGetCapabilities;
+extern DWORD SDL_XInputVersion;  // ((major << 16) & 0xFF00) | (minor & 0xFF)
+
+#define XINPUTGETSTATE			SDL_XInputGetState
+#define XINPUTSETSTATE			SDL_XInputSetState
+#define XINPUTGETCAPABILITIES	SDL_XInputGetCapabilities
+#define INVALID_XINPUT_USERID 255
+#define SDL_XINPUT_MAX_DEVICES 4
+
+#ifndef XINPUT_CAPS_FFB_SUPPORTED
+#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
+#endif
+
 #endif /* _INCLUDED_WINDOWS_H */
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/haptic/windows/SDL_syshaptic.c	Sun Mar 10 09:09:31 2013 -0700
+++ b/src/haptic/windows/SDL_syshaptic.c	Sun Mar 10 13:05:47 2013 -0400
@@ -22,16 +22,16 @@
 
 #ifdef SDL_HAPTIC_DINPUT
 
+#include "SDL_assert.h"
+#include "SDL_hints.h"
 #include "SDL_haptic.h"
 #include "../SDL_syshaptic.h"
 #include "SDL_joystick.h"
 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
 #include "../../joystick/windows/SDL_dxjoystick_c.h"      /* For joystick hwdata */
 
-
 #define MAX_HAPTICS  32
 
-
 /*
  * List of available haptic devices.
  */
@@ -41,6 +41,8 @@
     char *name;
     SDL_Haptic *haptic;
     DIDEVCAPS capabilities;
+    Uint8 bXInputHaptic; // Supports force feedback via XInput.
+    Uint8 userid; // XInput userid index for this joystick
 } SDL_hapticlist[MAX_HAPTICS];
 
 
@@ -52,6 +54,8 @@
     LPDIRECTINPUTDEVICE8 device;
     DWORD axes[3];              /* Axes to use. */
     int is_joystick;            /* Device is loaded as joystick. */
+    Uint8 bXInputHaptic; // Supports force feedback via XInput.
+    Uint8 userid; // XInput userid index for this joystick
 };
 
 
@@ -62,6 +66,7 @@
 {
     DIEFFECT effect;
     LPDIRECTINPUTEFFECT ref;
+    XINPUT_VIBRATION vibration;
 };
 
 
@@ -70,6 +75,7 @@
  */
 static SDL_bool coinitialized = SDL_FALSE;
 static LPDIRECTINPUT8 dinput = NULL;
+static SDL_bool loaded_xinput = SDL_FALSE;
 
 
 /*
@@ -87,6 +93,7 @@
                                           DIDEVICEINSTANCE instance);
 static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
                                          LPDIRECTINPUTDEVICE8 device8);
+static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid);
 static DWORD DIGetTriggerButton(Uint16 button);
 static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
                                 int naxes);
@@ -130,6 +137,7 @@
 int
 SDL_SYS_HapticInit(void)
 {
+    const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
     HRESULT ret;
     HINSTANCE instance;
 
@@ -187,6 +195,30 @@
         return -1;
     }
 
+    if (!env || SDL_atoi(env)) {
+        loaded_xinput = (WIN_LoadXInputDLL() == 0);
+    }
+
+    if (loaded_xinput) {
+        DWORD i;
+        const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
+
+        for (i = 0; (i < SDL_XINPUT_MAX_DEVICES) && (SDL_numhaptics < MAX_HAPTICS); i++) {
+            XINPUT_CAPABILITIES caps;
+            if (XINPUTGETCAPABILITIES(i, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) {
+                if ((!bIs14OrLater) || (caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
+                    /* !!! FIXME: I'm not bothering to query for a real name right now. */
+                    char buf[64];
+                    SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", i+1);
+                    SDL_hapticlist[SDL_numhaptics].name = SDL_strdup(buf);
+                    SDL_hapticlist[SDL_numhaptics].bXInputHaptic = 1;
+                    SDL_hapticlist[SDL_numhaptics].userid = (Uint8) i;
+                    SDL_numhaptics++;
+                }
+            }
+        }
+    }
+
     return SDL_numhaptics;
 }
 
@@ -363,6 +395,43 @@
     return -1;
 }
 
+static int
+SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid)
+{
+    XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
+    XINPUTSETSTATE(userid, &vibration);
+
+    /* !!! FIXME: we can probably do more than SINE if we figure out how to set up the left and right motors properly. */
+    haptic->supported = SDL_HAPTIC_SINE;
+
+    haptic->neffects = 1;
+    haptic->nplaying = 1;
+
+    /* Prepare effects memory. */
+    haptic->effects = (struct haptic_effect *)
+        SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
+    if (haptic->effects == NULL) {
+        SDL_OutOfMemory();
+        return -1;
+    }
+    /* Clear the memory */
+    SDL_memset(haptic->effects, 0,
+               sizeof(struct haptic_effect) * haptic->neffects);
+
+    haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
+    if (haptic->hwdata == NULL) {
+        SDL_free(haptic->effects);
+        haptic->effects = NULL;
+        SDL_OutOfMemory();
+        return -1;
+    }
+    SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
+
+    haptic->hwdata->bXInputHaptic = 1;
+    haptic->hwdata->userid = userid;
+
+    return 0;
+ }
 
 /*
  * Opens the haptic device from the file descriptor.
@@ -504,9 +573,11 @@
 int
 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
 {
-    return SDL_SYS_HapticOpenFromInstance(haptic,
-                                          SDL_hapticlist[haptic->index].
-                                          instance);
+    if (SDL_hapticlist[haptic->index].bXInputHaptic) {
+        return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
+    }
+
+    return SDL_SYS_HapticOpenFromInstance(haptic, SDL_hapticlist[haptic->index].instance);
 }
 
 
@@ -535,11 +606,9 @@
 int
 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
 {
-    if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
-        return SDL_TRUE;
-    }
-
-    return SDL_FALSE;
+    const struct joystick_hwdata *hwdata = joystick->hwdata;
+    return ( (hwdata->bXInputHaptic) ||
+             ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) );
 }
 
 
@@ -549,25 +618,30 @@
 int
 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
 {
-    HRESULT ret;
-    DIDEVICEINSTANCE hap_instance, joy_instance;
-    hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
-    joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
+    if ((joystick->hwdata->bXInputHaptic == haptic->hwdata->bXInputHaptic) && (haptic->hwdata->userid == joystick->hwdata->userid)) {
+        return 1;
+    } else {
+        HRESULT ret;
+        DIDEVICEINSTANCE hap_instance, joy_instance;
 
-    /* Get the device instances. */
-    ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
+        hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
+        joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
+
+        /* Get the device instances. */
+        ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
                                             &hap_instance);
-    if (FAILED(ret)) {
-        return 0;
+        if (FAILED(ret)) {
+            return 0;
+        }
+        ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
+                                                &joy_instance);
+        if (FAILED(ret)) {
+            return 0;
+        }
+
+        if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
+            return 1;
     }
-    ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
-                                            &joy_instance);
-    if (FAILED(ret)) {
-        return 0;
-    }
-
-    if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
-        return 1;
 
     return 0;
 }
@@ -585,16 +659,27 @@
     joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
 
     /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
-    for (i=0; i<SDL_numhaptics; i++) {
-        idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
-              &joy_instance);
-        if (FAILED(idret)) {
-            return -1;
+    if (joystick->hwdata->bXInputDevice) {
+        const Uint8 userid = joystick->hwdata->userid;
+        for (i=0; i<SDL_numhaptics; i++) {
+            if ((SDL_hapticlist[i].bXInputHaptic) && (SDL_hapticlist[i].userid == userid)) {
+                SDL_assert(joystick->hwdata->bXInputHaptic);
+                haptic->index = i;
+                break;
+            }
         }
-        if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
-                          &joy_instance.guidInstance)) {
-            haptic->index = i;
-            break;
+    } else {
+        for (i=0; i<SDL_numhaptics; i++) {
+            idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
+                  &joy_instance);
+            if (FAILED(idret)) {
+                return -1;
+            }
+            if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
+                              &joy_instance.guidInstance)) {
+                haptic->index = i;
+                break;
+            }
         }
     }
     if (i >= SDL_numhaptics) {
@@ -611,14 +696,17 @@
     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
 
     /* Now open the device. */
-    ret =
-        SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice);
-    if (ret < 0) {
-        return -1;
+    if (!joystick->hwdata->bXInputHaptic) {
+        ret = SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice);
+        if (ret < 0) {
+            return -1;
+        }
     }
 
     /* It's using the joystick device. */
     haptic->hwdata->is_joystick = 1;
+    haptic->hwdata->bXInputHaptic = joystick->hwdata->bXInputHaptic;
+    haptic->hwdata->userid = joystick->hwdata->userid;
 
     return 0;
 }
@@ -638,10 +726,12 @@
         haptic->neffects = 0;
 
         /* Clean up */
-        IDirectInputDevice8_Unacquire(haptic->hwdata->device);
-        /* Only release if isn't grabbed by a joystick. */
-        if (haptic->hwdata->is_joystick == 0) {
-            IDirectInputDevice8_Release(haptic->hwdata->device);
+        if (!haptic->hwdata->bXInputHaptic) {
+            IDirectInputDevice8_Unacquire(haptic->hwdata->device);
+            /* Only release if isn't grabbed by a joystick. */
+            if (haptic->hwdata->is_joystick == 0) {
+                IDirectInputDevice8_Release(haptic->hwdata->device);
+            }
         }
 
         /* Free */
@@ -659,6 +749,11 @@
 {
     int i;
 
+    if (loaded_xinput) {
+        WIN_UnloadXInputDLL();
+        loaded_xinput = SDL_FALSE;
+    }
+
     for (i = 0; i < SDL_arraysize(SDL_hapticlist); ++i) {
         if (SDL_hapticlist[i].name) {
             SDL_free(SDL_hapticlist[i].name);
@@ -1127,9 +1222,8 @@
                         SDL_HapticEffect * base)
 {
     HRESULT ret;
+    REFGUID type = SDL_SYS_HapticEffectType(base);
 
-    /* Get the type. */
-    REFGUID type = SDL_SYS_HapticEffectType(base);
     if (type == NULL) {
         goto err_hweffect;
     }
@@ -1142,6 +1236,13 @@
         goto err_hweffect;
     }
 
+    SDL_zerop(effect->hweffect);
+
+    if (haptic->hwdata->bXInputHaptic) {
+        SDL_assert(base->type == SDL_HAPTIC_SINE);  /* should catch this at higher level */
+        return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
+    }
+
     /* Get the effect. */
     if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
         goto err_effectdone;
@@ -1181,6 +1282,23 @@
     DWORD flags;
     DIEFFECT temp;
 
+    if (haptic->hwdata->bXInputHaptic) {
+        // !!! FIXME: this isn't close to right. We only support "sine" effects,
+        // !!! FIXME:  we ignore most of the parameters, and we probably get
+        // !!! FIXME:  the ones we don't ignore wrong, too.
+        // !!! FIXME: if I had a better understanding of how the two motors
+        // !!! FIXME:  could be used in unison, perhaps I could implement other
+        // !!! FIXME:  effect types?
+        /* From MSDN:
+            "Note that the right motor is the high-frequency motor, the left
+             motor is the low-frequency motor. They do not always need to be
+             set to the same amount, as they provide different effects." */
+        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
+        SDL_assert(data->type == SDL_HAPTIC_SINE);
+        vib->wLeftMotorSpeed = vib->wRightMotorSpeed = data->periodic.magnitude * 2;
+        return 0;
+    }
+
     /* Get the effect. */
     SDL_memset(&temp, 0, sizeof(DIEFFECT));
     if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
@@ -1226,6 +1344,11 @@
     HRESULT ret;
     DWORD iter;
 
+    if (haptic->hwdata->bXInputHaptic) {
+        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
+        return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS);
+    }
+
     /* Check if it's infinite. */
     if (iterations == SDL_HAPTIC_INFINITY) {
         iter = INFINITE;
@@ -1251,6 +1374,11 @@
 {
     HRESULT ret;
 
+    if (haptic->hwdata->bXInputHaptic) {
+        XINPUT_VIBRATION vibration = { 0, 0 };
+        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS);
+    }
+
     ret = IDirectInputEffect_Stop(effect->hweffect->ref);
     if (FAILED(ret)) {
         DI_SetError("Unable to stop effect", ret);
@@ -1269,12 +1397,16 @@
 {
     HRESULT ret;
 
-    ret = IDirectInputEffect_Unload(effect->hweffect->ref);
-    if (FAILED(ret)) {
-        DI_SetError("Removing effect from the device", ret);
+    if (haptic->hwdata->bXInputHaptic) {
+        SDL_SYS_HapticStopEffect(haptic, effect);
+    } else {
+        ret = IDirectInputEffect_Unload(effect->hweffect->ref);
+        if (FAILED(ret)) {
+            DI_SetError("Removing effect from the device", ret);
+        }
+        SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
+                                   effect->effect.type);
     }
-    SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
-                               effect->effect.type);
     SDL_free(effect->hweffect);
     effect->hweffect = NULL;
 }
@@ -1407,6 +1539,11 @@
 {
     HRESULT ret;
 
+    if (haptic->hwdata->bXInputHaptic) {
+        XINPUT_VIBRATION vibration = { 0, 0 };
+        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS);
+    }
+
     /* Try to stop the effects. */
     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
                                                        DISFFC_STOPALL);
--- a/src/joystick/windows/SDL_dxjoystick.c	Sun Mar 10 09:09:31 2013 -0700
+++ b/src/joystick/windows/SDL_dxjoystick.c	Sun Mar 10 13:05:47 2013 -0400
@@ -73,7 +73,6 @@
 static SDL_mutex *s_mutexJoyStickEnum = NULL;
 static SDL_Thread *s_threadJoystick = NULL;
 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
-static HANDLE s_pXInputDLL = 0;
 static SDL_bool s_bXInputEnabled = SDL_TRUE;
 
 extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion,
@@ -91,36 +90,6 @@
 	struct JoyStick_DeviceData_ *pNext;
 };
 
-
-/* Forward decl's for XInput API's we load dynamically and use if available */
-typedef DWORD (WINAPI *XInputGetState_t)
-	(
-	DWORD         dwUserIndex,  // [in] Index of the gamer associated with the device
-	XINPUT_STATE_EX* pState        // [out] Receives the current state
-	);
-
-typedef DWORD (WINAPI *XInputSetState_t)
-	(
-	DWORD             dwUserIndex,  // [in] Index of the gamer associated with the device
-	XINPUT_VIBRATION* pVibration    // [in, out] The vibration information to send to the controller
-	);
-
-typedef DWORD (WINAPI *XInputGetCapabilities_t)
-	(
-	DWORD                dwUserIndex,   // [in] Index of the gamer associated with the device
-	DWORD                dwFlags,       // [in] Input flags that identify the device type
-	XINPUT_CAPABILITIES* pCapabilities  // [out] Receives the capabilities
-	);
-
-XInputGetState_t PC_XInputGetState;
-XInputSetState_t PC_XInputSetState;
-XInputGetCapabilities_t PC_XInputGetCapabilities;
-
-#define XINPUTGETSTATE			PC_XInputGetState
-#define XINPUTSETSTATE			PC_XInputSetState
-#define XINPUTGETCAPABILITIES	PC_XInputGetCapabilities
-#define INVALID_XINPUT_USERID 255
-
 typedef struct JoyStick_DeviceData_ JoyStick_DeviceData;
 
 static JoyStick_DeviceData *SYS_Joystick;    /* array to hold joystick ID values */
@@ -634,7 +603,7 @@
 {
     HRESULT result;
     HINSTANCE instance;
-	const char *env = SDL_GetHint(SD_HINT_XINPUT_ENABLED);
+	const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
 	if (env && !SDL_atoi(env)) {
 		s_bXInputEnabled = SDL_FALSE;
 	}
@@ -672,32 +641,15 @@
         return (-1);
     }
 
-	s_mutexJoyStickEnum = SDL_CreateMutex();
-	s_condJoystickThread = SDL_CreateCond();
-	s_bDeviceAdded = SDL_TRUE; // force a scan of the system for joysticks this first time
-	SDL_SYS_JoystickDetect();
+    s_mutexJoyStickEnum = SDL_CreateMutex();
+    s_condJoystickThread = SDL_CreateCond();
+    s_bDeviceAdded = SDL_TRUE; // force a scan of the system for joysticks this first time
+    SDL_SYS_JoystickDetect();
 
-    if (s_bXInputEnabled) {
-		// try to load XInput support if available
-		s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" );
-		if ( !s_pXInputDLL )
-			s_pXInputDLL = LoadLibrary( L"bin\\XInput1_3.dll" );
-		if ( s_pXInputDLL )
-		{
-			// 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think...
-			PC_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );
-			PC_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" );
-			PC_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" );
-			if ( !PC_XInputGetState || !PC_XInputSetState || !PC_XInputGetCapabilities )
-			{
-				SDL_SYS_JoystickQuit();
-				SDL_SetError("GetProcAddress() failed when loading XInput.", GetLastError());
-				return (-1);
-			}
-		}
+    if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) {
+        s_bXInputEnabled = SDL_FALSE;  /* oh well. */
     }
 
-
 	if ( !s_threadJoystick )
 	{
 		s_bJoystickThreadQuit = SDL_FALSE;
@@ -978,6 +930,7 @@
 			result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
 			if ( result == ERROR_SUCCESS )
 			{
+                const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
 				SDL_bool bIsSupported = SDL_FALSE;
 				// Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad.
 				bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
@@ -990,6 +943,9 @@
 				{
 					// valid
 					joystick->hwdata->bXInputDevice = SDL_TRUE;
+                    if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
+					    joystick->hwdata->bXInputHaptic = SDL_TRUE;
+                    }
 					SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
 					joystickdevice->XInputUserId = userId;
 					joystick->hwdata->userid = userId;
@@ -1683,11 +1639,9 @@
 		s_pKnownJoystickGUIDs = NULL;
 	}
 
-	if ( s_pXInputDLL )
-	{
-		FreeLibrary( s_pXInputDLL );
-		s_pXInputDLL = NULL;
-	}
+    if (s_bXInputEnabled) {
+        WIN_UnloadXInputDLL();
+    }
 }
 
 
--- a/src/joystick/windows/SDL_dxjoystick_c.h	Sun Mar 10 09:09:31 2013 -0700
+++ b/src/joystick/windows/SDL_dxjoystick_c.h	Sun Mar 10 13:05:47 2013 -0400
@@ -62,25 +62,6 @@
     Uint8 num;
 } input_t;
 
-/* typedef's for XInput structs we use */
-typedef struct
-{
-	WORD                                wButtons;
-	BYTE                                bLeftTrigger;
-	BYTE                                bRightTrigger;
-	SHORT                               sThumbLX;
-	SHORT                               sThumbLY;
-	SHORT                               sThumbRX;
-	SHORT                               sThumbRY;
-	DWORD								dwPaddingReserved;
-} XINPUT_GAMEPAD_EX;
-
-typedef struct 
-{
-	DWORD                               dwPacketNumber;
-	XINPUT_GAMEPAD_EX                   Gamepad;
-} XINPUT_STATE_EX;
-
 /* The private structure used to keep track of a joystick */
 struct joystick_hwdata
 {
@@ -95,6 +76,7 @@
 	Uint8 removed;
 	Uint8 send_remove_event;
 	Uint8 bXInputDevice; // 1 if this device supports using the xinput API rather than DirectInput
+	Uint8 bXInputHaptic; // Supports force feedback via XInput.
 	Uint8 userid; // XInput userid index for this joystick
 	Uint8 currentXInputSlot; // the current position to write to in XInputState below, used so we can compare old and new values
 	XINPUT_STATE_EX	XInputState[2];