First shot at Windows XInput haptics.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 10 Mar 2013 13:05:47 -0400
changeset 69902514368c2aaf
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
     1.1 --- a/include/SDL_hints.h	Sun Mar 10 09:09:31 2013 -0700
     1.2 +++ b/include/SDL_hints.h	Sun Mar 10 13:05:47 2013 -0400
     1.3 @@ -203,7 +203,7 @@
     1.4   *    "0"       - Disable XInput timer (only uses direct input)
     1.5   *    "1"       - Enable XInput timer (the default)
     1.6   */
     1.7 -#define SD_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"
     1.8 +#define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"
     1.9  
    1.10  
    1.11  /**
     2.1 --- a/src/core/windows/SDL_windows.c	Sun Mar 10 09:09:31 2013 -0700
     2.2 +++ b/src/core/windows/SDL_windows.c	Sun Mar 10 13:05:47 2013 -0400
     2.3 @@ -24,10 +24,73 @@
     2.4  
     2.5  #include "SDL_error.h"
     2.6  #include "SDL_windows.h"
     2.7 +#include "SDL_assert.h"
     2.8  
     2.9  #include <objbase.h>  /* for CoInitialize/CoUninitialize */
    2.10  
    2.11  
    2.12 +XInputGetState_t SDL_XInputGetState = NULL;
    2.13 +XInputSetState_t SDL_XInputSetState = NULL;
    2.14 +XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
    2.15 +DWORD SDL_XInputVersion = 0;
    2.16 +
    2.17 +static HANDLE s_pXInputDLL = 0;
    2.18 +static int s_XInputDLLRefCount = 0;
    2.19 +
    2.20 +int
    2.21 +WIN_LoadXInputDLL(void)
    2.22 +{
    2.23 +    DWORD version = 0;
    2.24 +
    2.25 +    if (s_pXInputDLL) {
    2.26 +        SDL_assert(s_XInputDLLRefCount > 0);
    2.27 +        s_XInputDLLRefCount++;
    2.28 +        return 0;  /* already loaded */
    2.29 +    }
    2.30 +
    2.31 +    version = (1 << 16) | 4;
    2.32 +    s_pXInputDLL = LoadLibrary( L"XInput1_4.dll" );  // 1.4 Ships with Windows 8.
    2.33 +    if (!s_pXInputDLL) {
    2.34 +        version = (1 << 16) | 3;
    2.35 +        s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" );  // 1.3 Ships with Vista and Win7, can be installed as a restributable component.
    2.36 +    }
    2.37 +    if (!s_pXInputDLL) {
    2.38 +        s_pXInputDLL = LoadLibrary( L"bin\\XInput1_3.dll" );
    2.39 +    }
    2.40 +    if (!s_pXInputDLL) {
    2.41 +        return -1;
    2.42 +    }
    2.43 +
    2.44 +    SDL_assert(s_XInputDLLRefCount == 0);
    2.45 +    SDL_XInputVersion = version;
    2.46 +    s_XInputDLLRefCount = 1;
    2.47 +
    2.48 +    /* 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... */
    2.49 +    SDL_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );
    2.50 +    SDL_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" );
    2.51 +    SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" );
    2.52 +    if ( !SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities ) {
    2.53 +        WIN_UnloadXInputDLL();
    2.54 +        return -1;
    2.55 +    }
    2.56 +
    2.57 +    return 0;
    2.58 +}
    2.59 +
    2.60 +void
    2.61 +WIN_UnloadXInputDLL(void)
    2.62 +{
    2.63 +    if ( s_pXInputDLL ) {
    2.64 +        SDL_assert(s_XInputDLLRefCount > 0);
    2.65 +        if (--s_XInputDLLRefCount == 0) {
    2.66 +            FreeLibrary( s_pXInputDLL );
    2.67 +            s_pXInputDLL = NULL;
    2.68 +        }
    2.69 +    } else {
    2.70 +        SDL_assert(s_XInputDLLRefCount == 0);
    2.71 +    }
    2.72 +}
    2.73 +
    2.74  /* Sets an error message based on GetLastError() */
    2.75  void
    2.76  WIN_SetError(const char *prefix)
     3.1 --- a/src/core/windows/SDL_windows.h	Sun Mar 10 09:09:31 2013 -0700
     3.2 +++ b/src/core/windows/SDL_windows.h	Sun Mar 10 13:05:47 2013 -0400
     3.3 @@ -33,7 +33,7 @@
     3.4  #define _WIN32_WINNT  0x501   /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */
     3.5  
     3.6  #include <windows.h>
     3.7 -
     3.8 +#include <xinput.h>
     3.9  
    3.10  /* Routines to convert from UTF8 to native Windows text */
    3.11  #if UNICODE
    3.12 @@ -51,6 +51,64 @@
    3.13  extern HRESULT WIN_CoInitialize(void);
    3.14  extern void WIN_CoUninitialize(void);
    3.15  
    3.16 +/* typedef's for XInput structs we use */
    3.17 +typedef struct
    3.18 +{
    3.19 +    WORD wButtons;
    3.20 +    BYTE bLeftTrigger;
    3.21 +    BYTE bRightTrigger;
    3.22 +    SHORT sThumbLX;
    3.23 +    SHORT sThumbLY;
    3.24 +    SHORT sThumbRX;
    3.25 +    SHORT sThumbRY;
    3.26 +    DWORD dwPaddingReserved;
    3.27 +} XINPUT_GAMEPAD_EX;
    3.28 +
    3.29 +typedef struct 
    3.30 +{
    3.31 +    DWORD dwPacketNumber;
    3.32 +    XINPUT_GAMEPAD_EX Gamepad;
    3.33 +} XINPUT_STATE_EX;
    3.34 +
    3.35 +
    3.36 +/* Forward decl's for XInput API's we load dynamically and use if available */
    3.37 +typedef DWORD (WINAPI *XInputGetState_t)
    3.38 +	(
    3.39 +	DWORD         dwUserIndex,  // [in] Index of the gamer associated with the device
    3.40 +	XINPUT_STATE_EX* pState        // [out] Receives the current state
    3.41 +	);
    3.42 +
    3.43 +typedef DWORD (WINAPI *XInputSetState_t)
    3.44 +	(
    3.45 +	DWORD             dwUserIndex,  // [in] Index of the gamer associated with the device
    3.46 +	XINPUT_VIBRATION* pVibration    // [in, out] The vibration information to send to the controller
    3.47 +	);
    3.48 +
    3.49 +typedef DWORD (WINAPI *XInputGetCapabilities_t)
    3.50 +	(
    3.51 +	DWORD                dwUserIndex,   // [in] Index of the gamer associated with the device
    3.52 +	DWORD                dwFlags,       // [in] Input flags that identify the device type
    3.53 +	XINPUT_CAPABILITIES* pCapabilities  // [out] Receives the capabilities
    3.54 +	);
    3.55 +
    3.56 +extern int WIN_LoadXInputDLL(void);
    3.57 +extern void WIN_UnloadXInputDLL(void);
    3.58 +
    3.59 +extern XInputGetState_t SDL_XInputGetState;
    3.60 +extern XInputSetState_t SDL_XInputSetState;
    3.61 +extern XInputGetCapabilities_t SDL_XInputGetCapabilities;
    3.62 +extern DWORD SDL_XInputVersion;  // ((major << 16) & 0xFF00) | (minor & 0xFF)
    3.63 +
    3.64 +#define XINPUTGETSTATE			SDL_XInputGetState
    3.65 +#define XINPUTSETSTATE			SDL_XInputSetState
    3.66 +#define XINPUTGETCAPABILITIES	SDL_XInputGetCapabilities
    3.67 +#define INVALID_XINPUT_USERID 255
    3.68 +#define SDL_XINPUT_MAX_DEVICES 4
    3.69 +
    3.70 +#ifndef XINPUT_CAPS_FFB_SUPPORTED
    3.71 +#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
    3.72 +#endif
    3.73 +
    3.74  #endif /* _INCLUDED_WINDOWS_H */
    3.75  
    3.76  /* vi: set ts=4 sw=4 expandtab: */
     4.1 --- a/src/haptic/windows/SDL_syshaptic.c	Sun Mar 10 09:09:31 2013 -0700
     4.2 +++ b/src/haptic/windows/SDL_syshaptic.c	Sun Mar 10 13:05:47 2013 -0400
     4.3 @@ -22,16 +22,16 @@
     4.4  
     4.5  #ifdef SDL_HAPTIC_DINPUT
     4.6  
     4.7 +#include "SDL_assert.h"
     4.8 +#include "SDL_hints.h"
     4.9  #include "SDL_haptic.h"
    4.10  #include "../SDL_syshaptic.h"
    4.11  #include "SDL_joystick.h"
    4.12  #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
    4.13  #include "../../joystick/windows/SDL_dxjoystick_c.h"      /* For joystick hwdata */
    4.14  
    4.15 -
    4.16  #define MAX_HAPTICS  32
    4.17  
    4.18 -
    4.19  /*
    4.20   * List of available haptic devices.
    4.21   */
    4.22 @@ -41,6 +41,8 @@
    4.23      char *name;
    4.24      SDL_Haptic *haptic;
    4.25      DIDEVCAPS capabilities;
    4.26 +    Uint8 bXInputHaptic; // Supports force feedback via XInput.
    4.27 +    Uint8 userid; // XInput userid index for this joystick
    4.28  } SDL_hapticlist[MAX_HAPTICS];
    4.29  
    4.30  
    4.31 @@ -52,6 +54,8 @@
    4.32      LPDIRECTINPUTDEVICE8 device;
    4.33      DWORD axes[3];              /* Axes to use. */
    4.34      int is_joystick;            /* Device is loaded as joystick. */
    4.35 +    Uint8 bXInputHaptic; // Supports force feedback via XInput.
    4.36 +    Uint8 userid; // XInput userid index for this joystick
    4.37  };
    4.38  
    4.39  
    4.40 @@ -62,6 +66,7 @@
    4.41  {
    4.42      DIEFFECT effect;
    4.43      LPDIRECTINPUTEFFECT ref;
    4.44 +    XINPUT_VIBRATION vibration;
    4.45  };
    4.46  
    4.47  
    4.48 @@ -70,6 +75,7 @@
    4.49   */
    4.50  static SDL_bool coinitialized = SDL_FALSE;
    4.51  static LPDIRECTINPUT8 dinput = NULL;
    4.52 +static SDL_bool loaded_xinput = SDL_FALSE;
    4.53  
    4.54  
    4.55  /*
    4.56 @@ -87,6 +93,7 @@
    4.57                                            DIDEVICEINSTANCE instance);
    4.58  static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
    4.59                                           LPDIRECTINPUTDEVICE8 device8);
    4.60 +static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid);
    4.61  static DWORD DIGetTriggerButton(Uint16 button);
    4.62  static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
    4.63                                  int naxes);
    4.64 @@ -130,6 +137,7 @@
    4.65  int
    4.66  SDL_SYS_HapticInit(void)
    4.67  {
    4.68 +    const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
    4.69      HRESULT ret;
    4.70      HINSTANCE instance;
    4.71  
    4.72 @@ -187,6 +195,30 @@
    4.73          return -1;
    4.74      }
    4.75  
    4.76 +    if (!env || SDL_atoi(env)) {
    4.77 +        loaded_xinput = (WIN_LoadXInputDLL() == 0);
    4.78 +    }
    4.79 +
    4.80 +    if (loaded_xinput) {
    4.81 +        DWORD i;
    4.82 +        const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
    4.83 +
    4.84 +        for (i = 0; (i < SDL_XINPUT_MAX_DEVICES) && (SDL_numhaptics < MAX_HAPTICS); i++) {
    4.85 +            XINPUT_CAPABILITIES caps;
    4.86 +            if (XINPUTGETCAPABILITIES(i, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) {
    4.87 +                if ((!bIs14OrLater) || (caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
    4.88 +                    /* !!! FIXME: I'm not bothering to query for a real name right now. */
    4.89 +                    char buf[64];
    4.90 +                    SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", i+1);
    4.91 +                    SDL_hapticlist[SDL_numhaptics].name = SDL_strdup(buf);
    4.92 +                    SDL_hapticlist[SDL_numhaptics].bXInputHaptic = 1;
    4.93 +                    SDL_hapticlist[SDL_numhaptics].userid = (Uint8) i;
    4.94 +                    SDL_numhaptics++;
    4.95 +                }
    4.96 +            }
    4.97 +        }
    4.98 +    }
    4.99 +
   4.100      return SDL_numhaptics;
   4.101  }
   4.102  
   4.103 @@ -363,6 +395,43 @@
   4.104      return -1;
   4.105  }
   4.106  
   4.107 +static int
   4.108 +SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid)
   4.109 +{
   4.110 +    XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
   4.111 +    XINPUTSETSTATE(userid, &vibration);
   4.112 +
   4.113 +    /* !!! FIXME: we can probably do more than SINE if we figure out how to set up the left and right motors properly. */
   4.114 +    haptic->supported = SDL_HAPTIC_SINE;
   4.115 +
   4.116 +    haptic->neffects = 1;
   4.117 +    haptic->nplaying = 1;
   4.118 +
   4.119 +    /* Prepare effects memory. */
   4.120 +    haptic->effects = (struct haptic_effect *)
   4.121 +        SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   4.122 +    if (haptic->effects == NULL) {
   4.123 +        SDL_OutOfMemory();
   4.124 +        return -1;
   4.125 +    }
   4.126 +    /* Clear the memory */
   4.127 +    SDL_memset(haptic->effects, 0,
   4.128 +               sizeof(struct haptic_effect) * haptic->neffects);
   4.129 +
   4.130 +    haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
   4.131 +    if (haptic->hwdata == NULL) {
   4.132 +        SDL_free(haptic->effects);
   4.133 +        haptic->effects = NULL;
   4.134 +        SDL_OutOfMemory();
   4.135 +        return -1;
   4.136 +    }
   4.137 +    SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   4.138 +
   4.139 +    haptic->hwdata->bXInputHaptic = 1;
   4.140 +    haptic->hwdata->userid = userid;
   4.141 +
   4.142 +    return 0;
   4.143 + }
   4.144  
   4.145  /*
   4.146   * Opens the haptic device from the file descriptor.
   4.147 @@ -504,9 +573,11 @@
   4.148  int
   4.149  SDL_SYS_HapticOpen(SDL_Haptic * haptic)
   4.150  {
   4.151 -    return SDL_SYS_HapticOpenFromInstance(haptic,
   4.152 -                                          SDL_hapticlist[haptic->index].
   4.153 -                                          instance);
   4.154 +    if (SDL_hapticlist[haptic->index].bXInputHaptic) {
   4.155 +        return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
   4.156 +    }
   4.157 +
   4.158 +    return SDL_SYS_HapticOpenFromInstance(haptic, SDL_hapticlist[haptic->index].instance);
   4.159  }
   4.160  
   4.161  
   4.162 @@ -535,11 +606,9 @@
   4.163  int
   4.164  SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
   4.165  {
   4.166 -    if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
   4.167 -        return SDL_TRUE;
   4.168 -    }
   4.169 -
   4.170 -    return SDL_FALSE;
   4.171 +    const struct joystick_hwdata *hwdata = joystick->hwdata;
   4.172 +    return ( (hwdata->bXInputHaptic) ||
   4.173 +             ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) );
   4.174  }
   4.175  
   4.176  
   4.177 @@ -549,25 +618,30 @@
   4.178  int
   4.179  SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   4.180  {
   4.181 -    HRESULT ret;
   4.182 -    DIDEVICEINSTANCE hap_instance, joy_instance;
   4.183 -    hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   4.184 -    joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   4.185 +    if ((joystick->hwdata->bXInputHaptic == haptic->hwdata->bXInputHaptic) && (haptic->hwdata->userid == joystick->hwdata->userid)) {
   4.186 +        return 1;
   4.187 +    } else {
   4.188 +        HRESULT ret;
   4.189 +        DIDEVICEINSTANCE hap_instance, joy_instance;
   4.190  
   4.191 -    /* Get the device instances. */
   4.192 -    ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
   4.193 +        hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   4.194 +        joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   4.195 +
   4.196 +        /* Get the device instances. */
   4.197 +        ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
   4.198                                              &hap_instance);
   4.199 -    if (FAILED(ret)) {
   4.200 -        return 0;
   4.201 +        if (FAILED(ret)) {
   4.202 +            return 0;
   4.203 +        }
   4.204 +        ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   4.205 +                                                &joy_instance);
   4.206 +        if (FAILED(ret)) {
   4.207 +            return 0;
   4.208 +        }
   4.209 +
   4.210 +        if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
   4.211 +            return 1;
   4.212      }
   4.213 -    ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   4.214 -                                            &joy_instance);
   4.215 -    if (FAILED(ret)) {
   4.216 -        return 0;
   4.217 -    }
   4.218 -
   4.219 -    if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
   4.220 -        return 1;
   4.221  
   4.222      return 0;
   4.223  }
   4.224 @@ -585,16 +659,27 @@
   4.225      joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   4.226  
   4.227      /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
   4.228 -    for (i=0; i<SDL_numhaptics; i++) {
   4.229 -        idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   4.230 -              &joy_instance);
   4.231 -        if (FAILED(idret)) {
   4.232 -            return -1;
   4.233 +    if (joystick->hwdata->bXInputDevice) {
   4.234 +        const Uint8 userid = joystick->hwdata->userid;
   4.235 +        for (i=0; i<SDL_numhaptics; i++) {
   4.236 +            if ((SDL_hapticlist[i].bXInputHaptic) && (SDL_hapticlist[i].userid == userid)) {
   4.237 +                SDL_assert(joystick->hwdata->bXInputHaptic);
   4.238 +                haptic->index = i;
   4.239 +                break;
   4.240 +            }
   4.241          }
   4.242 -        if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
   4.243 -                          &joy_instance.guidInstance)) {
   4.244 -            haptic->index = i;
   4.245 -            break;
   4.246 +    } else {
   4.247 +        for (i=0; i<SDL_numhaptics; i++) {
   4.248 +            idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   4.249 +                  &joy_instance);
   4.250 +            if (FAILED(idret)) {
   4.251 +                return -1;
   4.252 +            }
   4.253 +            if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
   4.254 +                              &joy_instance.guidInstance)) {
   4.255 +                haptic->index = i;
   4.256 +                break;
   4.257 +            }
   4.258          }
   4.259      }
   4.260      if (i >= SDL_numhaptics) {
   4.261 @@ -611,14 +696,17 @@
   4.262      SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   4.263  
   4.264      /* Now open the device. */
   4.265 -    ret =
   4.266 -        SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice);
   4.267 -    if (ret < 0) {
   4.268 -        return -1;
   4.269 +    if (!joystick->hwdata->bXInputHaptic) {
   4.270 +        ret = SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice);
   4.271 +        if (ret < 0) {
   4.272 +            return -1;
   4.273 +        }
   4.274      }
   4.275  
   4.276      /* It's using the joystick device. */
   4.277      haptic->hwdata->is_joystick = 1;
   4.278 +    haptic->hwdata->bXInputHaptic = joystick->hwdata->bXInputHaptic;
   4.279 +    haptic->hwdata->userid = joystick->hwdata->userid;
   4.280  
   4.281      return 0;
   4.282  }
   4.283 @@ -638,10 +726,12 @@
   4.284          haptic->neffects = 0;
   4.285  
   4.286          /* Clean up */
   4.287 -        IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   4.288 -        /* Only release if isn't grabbed by a joystick. */
   4.289 -        if (haptic->hwdata->is_joystick == 0) {
   4.290 -            IDirectInputDevice8_Release(haptic->hwdata->device);
   4.291 +        if (!haptic->hwdata->bXInputHaptic) {
   4.292 +            IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   4.293 +            /* Only release if isn't grabbed by a joystick. */
   4.294 +            if (haptic->hwdata->is_joystick == 0) {
   4.295 +                IDirectInputDevice8_Release(haptic->hwdata->device);
   4.296 +            }
   4.297          }
   4.298  
   4.299          /* Free */
   4.300 @@ -659,6 +749,11 @@
   4.301  {
   4.302      int i;
   4.303  
   4.304 +    if (loaded_xinput) {
   4.305 +        WIN_UnloadXInputDLL();
   4.306 +        loaded_xinput = SDL_FALSE;
   4.307 +    }
   4.308 +
   4.309      for (i = 0; i < SDL_arraysize(SDL_hapticlist); ++i) {
   4.310          if (SDL_hapticlist[i].name) {
   4.311              SDL_free(SDL_hapticlist[i].name);
   4.312 @@ -1127,9 +1222,8 @@
   4.313                          SDL_HapticEffect * base)
   4.314  {
   4.315      HRESULT ret;
   4.316 +    REFGUID type = SDL_SYS_HapticEffectType(base);
   4.317  
   4.318 -    /* Get the type. */
   4.319 -    REFGUID type = SDL_SYS_HapticEffectType(base);
   4.320      if (type == NULL) {
   4.321          goto err_hweffect;
   4.322      }
   4.323 @@ -1142,6 +1236,13 @@
   4.324          goto err_hweffect;
   4.325      }
   4.326  
   4.327 +    SDL_zerop(effect->hweffect);
   4.328 +
   4.329 +    if (haptic->hwdata->bXInputHaptic) {
   4.330 +        SDL_assert(base->type == SDL_HAPTIC_SINE);  /* should catch this at higher level */
   4.331 +        return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
   4.332 +    }
   4.333 +
   4.334      /* Get the effect. */
   4.335      if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
   4.336          goto err_effectdone;
   4.337 @@ -1181,6 +1282,23 @@
   4.338      DWORD flags;
   4.339      DIEFFECT temp;
   4.340  
   4.341 +    if (haptic->hwdata->bXInputHaptic) {
   4.342 +        // !!! FIXME: this isn't close to right. We only support "sine" effects,
   4.343 +        // !!! FIXME:  we ignore most of the parameters, and we probably get
   4.344 +        // !!! FIXME:  the ones we don't ignore wrong, too.
   4.345 +        // !!! FIXME: if I had a better understanding of how the two motors
   4.346 +        // !!! FIXME:  could be used in unison, perhaps I could implement other
   4.347 +        // !!! FIXME:  effect types?
   4.348 +        /* From MSDN:
   4.349 +            "Note that the right motor is the high-frequency motor, the left
   4.350 +             motor is the low-frequency motor. They do not always need to be
   4.351 +             set to the same amount, as they provide different effects." */
   4.352 +        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
   4.353 +        SDL_assert(data->type == SDL_HAPTIC_SINE);
   4.354 +        vib->wLeftMotorSpeed = vib->wRightMotorSpeed = data->periodic.magnitude * 2;
   4.355 +        return 0;
   4.356 +    }
   4.357 +
   4.358      /* Get the effect. */
   4.359      SDL_memset(&temp, 0, sizeof(DIEFFECT));
   4.360      if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
   4.361 @@ -1226,6 +1344,11 @@
   4.362      HRESULT ret;
   4.363      DWORD iter;
   4.364  
   4.365 +    if (haptic->hwdata->bXInputHaptic) {
   4.366 +        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
   4.367 +        return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS);
   4.368 +    }
   4.369 +
   4.370      /* Check if it's infinite. */
   4.371      if (iterations == SDL_HAPTIC_INFINITY) {
   4.372          iter = INFINITE;
   4.373 @@ -1251,6 +1374,11 @@
   4.374  {
   4.375      HRESULT ret;
   4.376  
   4.377 +    if (haptic->hwdata->bXInputHaptic) {
   4.378 +        XINPUT_VIBRATION vibration = { 0, 0 };
   4.379 +        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS);
   4.380 +    }
   4.381 +
   4.382      ret = IDirectInputEffect_Stop(effect->hweffect->ref);
   4.383      if (FAILED(ret)) {
   4.384          DI_SetError("Unable to stop effect", ret);
   4.385 @@ -1269,12 +1397,16 @@
   4.386  {
   4.387      HRESULT ret;
   4.388  
   4.389 -    ret = IDirectInputEffect_Unload(effect->hweffect->ref);
   4.390 -    if (FAILED(ret)) {
   4.391 -        DI_SetError("Removing effect from the device", ret);
   4.392 +    if (haptic->hwdata->bXInputHaptic) {
   4.393 +        SDL_SYS_HapticStopEffect(haptic, effect);
   4.394 +    } else {
   4.395 +        ret = IDirectInputEffect_Unload(effect->hweffect->ref);
   4.396 +        if (FAILED(ret)) {
   4.397 +            DI_SetError("Removing effect from the device", ret);
   4.398 +        }
   4.399 +        SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
   4.400 +                                   effect->effect.type);
   4.401      }
   4.402 -    SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
   4.403 -                               effect->effect.type);
   4.404      SDL_free(effect->hweffect);
   4.405      effect->hweffect = NULL;
   4.406  }
   4.407 @@ -1407,6 +1539,11 @@
   4.408  {
   4.409      HRESULT ret;
   4.410  
   4.411 +    if (haptic->hwdata->bXInputHaptic) {
   4.412 +        XINPUT_VIBRATION vibration = { 0, 0 };
   4.413 +        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS);
   4.414 +    }
   4.415 +
   4.416      /* Try to stop the effects. */
   4.417      ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
   4.418                                                         DISFFC_STOPALL);
     5.1 --- a/src/joystick/windows/SDL_dxjoystick.c	Sun Mar 10 09:09:31 2013 -0700
     5.2 +++ b/src/joystick/windows/SDL_dxjoystick.c	Sun Mar 10 13:05:47 2013 -0400
     5.3 @@ -73,7 +73,6 @@
     5.4  static SDL_mutex *s_mutexJoyStickEnum = NULL;
     5.5  static SDL_Thread *s_threadJoystick = NULL;
     5.6  static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
     5.7 -static HANDLE s_pXInputDLL = 0;
     5.8  static SDL_bool s_bXInputEnabled = SDL_TRUE;
     5.9  
    5.10  extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion,
    5.11 @@ -91,36 +90,6 @@
    5.12  	struct JoyStick_DeviceData_ *pNext;
    5.13  };
    5.14  
    5.15 -
    5.16 -/* Forward decl's for XInput API's we load dynamically and use if available */
    5.17 -typedef DWORD (WINAPI *XInputGetState_t)
    5.18 -	(
    5.19 -	DWORD         dwUserIndex,  // [in] Index of the gamer associated with the device
    5.20 -	XINPUT_STATE_EX* pState        // [out] Receives the current state
    5.21 -	);
    5.22 -
    5.23 -typedef DWORD (WINAPI *XInputSetState_t)
    5.24 -	(
    5.25 -	DWORD             dwUserIndex,  // [in] Index of the gamer associated with the device
    5.26 -	XINPUT_VIBRATION* pVibration    // [in, out] The vibration information to send to the controller
    5.27 -	);
    5.28 -
    5.29 -typedef DWORD (WINAPI *XInputGetCapabilities_t)
    5.30 -	(
    5.31 -	DWORD                dwUserIndex,   // [in] Index of the gamer associated with the device
    5.32 -	DWORD                dwFlags,       // [in] Input flags that identify the device type
    5.33 -	XINPUT_CAPABILITIES* pCapabilities  // [out] Receives the capabilities
    5.34 -	);
    5.35 -
    5.36 -XInputGetState_t PC_XInputGetState;
    5.37 -XInputSetState_t PC_XInputSetState;
    5.38 -XInputGetCapabilities_t PC_XInputGetCapabilities;
    5.39 -
    5.40 -#define XINPUTGETSTATE			PC_XInputGetState
    5.41 -#define XINPUTSETSTATE			PC_XInputSetState
    5.42 -#define XINPUTGETCAPABILITIES	PC_XInputGetCapabilities
    5.43 -#define INVALID_XINPUT_USERID 255
    5.44 -
    5.45  typedef struct JoyStick_DeviceData_ JoyStick_DeviceData;
    5.46  
    5.47  static JoyStick_DeviceData *SYS_Joystick;    /* array to hold joystick ID values */
    5.48 @@ -634,7 +603,7 @@
    5.49  {
    5.50      HRESULT result;
    5.51      HINSTANCE instance;
    5.52 -	const char *env = SDL_GetHint(SD_HINT_XINPUT_ENABLED);
    5.53 +	const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
    5.54  	if (env && !SDL_atoi(env)) {
    5.55  		s_bXInputEnabled = SDL_FALSE;
    5.56  	}
    5.57 @@ -672,32 +641,15 @@
    5.58          return (-1);
    5.59      }
    5.60  
    5.61 -	s_mutexJoyStickEnum = SDL_CreateMutex();
    5.62 -	s_condJoystickThread = SDL_CreateCond();
    5.63 -	s_bDeviceAdded = SDL_TRUE; // force a scan of the system for joysticks this first time
    5.64 -	SDL_SYS_JoystickDetect();
    5.65 +    s_mutexJoyStickEnum = SDL_CreateMutex();
    5.66 +    s_condJoystickThread = SDL_CreateCond();
    5.67 +    s_bDeviceAdded = SDL_TRUE; // force a scan of the system for joysticks this first time
    5.68 +    SDL_SYS_JoystickDetect();
    5.69  
    5.70 -    if (s_bXInputEnabled) {
    5.71 -		// try to load XInput support if available
    5.72 -		s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" );
    5.73 -		if ( !s_pXInputDLL )
    5.74 -			s_pXInputDLL = LoadLibrary( L"bin\\XInput1_3.dll" );
    5.75 -		if ( s_pXInputDLL )
    5.76 -		{
    5.77 -			// 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...
    5.78 -			PC_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );
    5.79 -			PC_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" );
    5.80 -			PC_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" );
    5.81 -			if ( !PC_XInputGetState || !PC_XInputSetState || !PC_XInputGetCapabilities )
    5.82 -			{
    5.83 -				SDL_SYS_JoystickQuit();
    5.84 -				SDL_SetError("GetProcAddress() failed when loading XInput.", GetLastError());
    5.85 -				return (-1);
    5.86 -			}
    5.87 -		}
    5.88 +    if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) {
    5.89 +        s_bXInputEnabled = SDL_FALSE;  /* oh well. */
    5.90      }
    5.91  
    5.92 -
    5.93  	if ( !s_threadJoystick )
    5.94  	{
    5.95  		s_bJoystickThreadQuit = SDL_FALSE;
    5.96 @@ -978,6 +930,7 @@
    5.97  			result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
    5.98  			if ( result == ERROR_SUCCESS )
    5.99  			{
   5.100 +                const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
   5.101  				SDL_bool bIsSupported = SDL_FALSE;
   5.102  				// Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad.
   5.103  				bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
   5.104 @@ -990,6 +943,9 @@
   5.105  				{
   5.106  					// valid
   5.107  					joystick->hwdata->bXInputDevice = SDL_TRUE;
   5.108 +                    if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
   5.109 +					    joystick->hwdata->bXInputHaptic = SDL_TRUE;
   5.110 +                    }
   5.111  					SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
   5.112  					joystickdevice->XInputUserId = userId;
   5.113  					joystick->hwdata->userid = userId;
   5.114 @@ -1683,11 +1639,9 @@
   5.115  		s_pKnownJoystickGUIDs = NULL;
   5.116  	}
   5.117  
   5.118 -	if ( s_pXInputDLL )
   5.119 -	{
   5.120 -		FreeLibrary( s_pXInputDLL );
   5.121 -		s_pXInputDLL = NULL;
   5.122 -	}
   5.123 +    if (s_bXInputEnabled) {
   5.124 +        WIN_UnloadXInputDLL();
   5.125 +    }
   5.126  }
   5.127  
   5.128  
     6.1 --- a/src/joystick/windows/SDL_dxjoystick_c.h	Sun Mar 10 09:09:31 2013 -0700
     6.2 +++ b/src/joystick/windows/SDL_dxjoystick_c.h	Sun Mar 10 13:05:47 2013 -0400
     6.3 @@ -62,25 +62,6 @@
     6.4      Uint8 num;
     6.5  } input_t;
     6.6  
     6.7 -/* typedef's for XInput structs we use */
     6.8 -typedef struct
     6.9 -{
    6.10 -	WORD                                wButtons;
    6.11 -	BYTE                                bLeftTrigger;
    6.12 -	BYTE                                bRightTrigger;
    6.13 -	SHORT                               sThumbLX;
    6.14 -	SHORT                               sThumbLY;
    6.15 -	SHORT                               sThumbRX;
    6.16 -	SHORT                               sThumbRY;
    6.17 -	DWORD								dwPaddingReserved;
    6.18 -} XINPUT_GAMEPAD_EX;
    6.19 -
    6.20 -typedef struct 
    6.21 -{
    6.22 -	DWORD                               dwPacketNumber;
    6.23 -	XINPUT_GAMEPAD_EX                   Gamepad;
    6.24 -} XINPUT_STATE_EX;
    6.25 -
    6.26  /* The private structure used to keep track of a joystick */
    6.27  struct joystick_hwdata
    6.28  {
    6.29 @@ -95,6 +76,7 @@
    6.30  	Uint8 removed;
    6.31  	Uint8 send_remove_event;
    6.32  	Uint8 bXInputDevice; // 1 if this device supports using the xinput API rather than DirectInput
    6.33 +	Uint8 bXInputHaptic; // Supports force feedback via XInput.
    6.34  	Uint8 userid; // XInput userid index for this joystick
    6.35  	Uint8 currentXInputSlot; // the current position to write to in XInputState below, used so we can compare old and new values
    6.36  	XINPUT_STATE_EX	XInputState[2];