From 16164dfc7540a5d9e9580fc44476c0d32f30ebe9 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 10 Mar 2013 13:05:47 -0400 Subject: [PATCH] First shot at Windows XInput haptics. --- include/SDL_hints.h | 2 +- src/core/windows/SDL_windows.c | 63 +++++++ src/core/windows/SDL_windows.h | 60 +++++- src/haptic/windows/SDL_syshaptic.c | 237 +++++++++++++++++++----- src/joystick/windows/SDL_dxjoystick.c | 74 ++------ src/joystick/windows/SDL_dxjoystick_c.h | 20 +- 6 files changed, 325 insertions(+), 131 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 9c1080c53..315a33133 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -203,7 +203,7 @@ extern "C" { * "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" /** diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c index b59e2cca2..49f2f04fd 100644 --- a/src/core/windows/SDL_windows.c +++ b/src/core/windows/SDL_windows.c @@ -24,10 +24,73 @@ #include "SDL_error.h" #include "SDL_windows.h" +#include "SDL_assert.h" #include /* 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) diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h index 9c57696d2..a68182777 100644 --- a/src/core/windows/SDL_windows.h +++ b/src/core/windows/SDL_windows.h @@ -33,7 +33,7 @@ #define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */ #include - +#include /* Routines to convert from UTF8 to native Windows text */ #if UNICODE @@ -51,6 +51,64 @@ extern void WIN_SetError(const char *prefix); 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: */ diff --git a/src/haptic/windows/SDL_syshaptic.c b/src/haptic/windows/SDL_syshaptic.c index 9993a5b25..a4a2644a1 100644 --- a/src/haptic/windows/SDL_syshaptic.c +++ b/src/haptic/windows/SDL_syshaptic.c @@ -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 @@ static struct 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 @@ struct haptic_hwdata 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 @@ struct haptic_hweffect { DIEFFECT effect; LPDIRECTINPUTEFFECT ref; + XINPUT_VIBRATION vibration; }; @@ -70,6 +75,7 @@ struct haptic_hweffect */ static SDL_bool coinitialized = SDL_FALSE; static LPDIRECTINPUT8 dinput = NULL; +static SDL_bool loaded_xinput = SDL_FALSE; /* @@ -87,6 +93,7 @@ static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, 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 @@ DI_GUIDIsSame(const GUID * a, const GUID * b) int SDL_SYS_HapticInit(void) { + const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED); HRESULT ret; HINSTANCE instance; @@ -187,6 +195,30 @@ SDL_SYS_HapticInit(void) 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 @@ SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance) 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 @@ SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic, 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 @@ SDL_SYS_HapticMouse(void) 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 @@ SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) 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; - } - ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, - &joy_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; + if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance)) + return 1; + } return 0; } @@ -585,16 +659,27 @@ SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) 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; ihwdata->InputDevice, - &joy_instance); - if (FAILED(idret)) { - return -1; + if (joystick->hwdata->bXInputDevice) { + const Uint8 userid = joystick->hwdata->userid; + for (i=0; ihwdata->bXInputHaptic); + haptic->index = i; + break; + } } - if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance, - &joy_instance.guidInstance)) { - haptic->index = i; - break; + } else { + for (i=0; ihwdata->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_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) 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 @@ SDL_SYS_HapticClose(SDL_Haptic * haptic) 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 @@ SDL_SYS_HapticQuit(void) { 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_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) { HRESULT ret; - - /* Get the type. */ REFGUID type = SDL_SYS_HapticEffectType(base); + if (type == NULL) { goto err_hweffect; } @@ -1142,6 +1236,13 @@ SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, 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 @@ SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, 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 @@ SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, 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 @@ SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) { 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 @@ SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) { 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 @@ SDL_SYS_HapticStopAll(SDL_Haptic * haptic) { 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); diff --git a/src/joystick/windows/SDL_dxjoystick.c b/src/joystick/windows/SDL_dxjoystick.c index 6493388f3..69f7da3aa 100644 --- a/src/joystick/windows/SDL_dxjoystick.c +++ b/src/joystick/windows/SDL_dxjoystick.c @@ -73,7 +73,6 @@ static SDL_cond *s_condJoystickThread = NULL; 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_ 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 @@ SDL_SYS_JoystickInit(void) { 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 @@ SDL_SYS_JoystickInit(void) 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 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) 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 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) { // 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 @@ SDL_SYS_JoystickQuit(void) s_pKnownJoystickGUIDs = NULL; } - if ( s_pXInputDLL ) - { - FreeLibrary( s_pXInputDLL ); - s_pXInputDLL = NULL; - } + if (s_bXInputEnabled) { + WIN_UnloadXInputDLL(); + } } diff --git a/src/joystick/windows/SDL_dxjoystick_c.h b/src/joystick/windows/SDL_dxjoystick_c.h index 631bab745..6d4957815 100644 --- a/src/joystick/windows/SDL_dxjoystick_c.h +++ b/src/joystick/windows/SDL_dxjoystick_c.h @@ -62,25 +62,6 @@ typedef struct input_t 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 @@ struct joystick_hwdata 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];