Wired up haptic hotplugging for Windows DirectInput/XInput code.

This commit is contained in:
Ryan C. Gordon 2014-02-06 07:37:20 -05:00
parent 9cf8c49c39
commit e42d46b463
3 changed files with 334 additions and 95 deletions

View file

@ -33,12 +33,12 @@
#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
#include "../../joystick/windows/SDL_dxjoystick_c.h" /* For joystick hwdata */
#define MAX_HAPTICS 32
#include "SDL_syshaptic_c.h"
/*
* List of available haptic devices.
*/
static struct
typedef struct SDL_hapticlist_item
{
DIDEVICEINSTANCE instance;
char *name;
@ -46,7 +46,8 @@ static struct
DIDEVCAPS capabilities;
Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
Uint8 userid; /* XInput userid index for this joystick */
} SDL_hapticlist[MAX_HAPTICS];
struct SDL_hapticlist_item *next;
} SDL_hapticlist_item;
/*
@ -83,7 +84,9 @@ struct haptic_hweffect
static SDL_bool coinitialized = SDL_FALSE;
static LPDIRECTINPUT8 dinput = NULL;
static SDL_bool loaded_xinput = SDL_FALSE;
static SDL_hapticlist_item *SDL_hapticlist = NULL;
static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
static int numhaptics = 0;
/*
* External stuff.
@ -101,7 +104,7 @@ static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic,
static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
LPDIRECTINPUTDEVICE8 device8,
SDL_bool is_joystick);
static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid);
static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, const Uint8 userid);
static DWORD DIGetTriggerButton(Uint16 button);
static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
int naxes);
@ -155,11 +158,6 @@ SDL_SYS_HapticInit(void)
return SDL_SetError("Haptic: SubSystem already open.");
}
/* Clear all the memory. */
SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
SDL_numhaptics = 0;
ret = WIN_CoInitialize();
if (FAILED(ret)) {
return DI_SetError("Coinitialize", ret);
@ -205,81 +203,260 @@ SDL_SYS_HapticInit(void)
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++;
}
}
for (i = 0; i < SDL_XINPUT_MAX_DEVICES; i++) {
XInputHaptic_MaybeAddDevice(i);
}
}
return SDL_numhaptics;
return numhaptics;
}
int
DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance)
{
HRESULT ret;
LPDIRECTINPUTDEVICE8 device;
const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK;
DIDEVCAPS capabilities;
SDL_hapticlist_item *item = NULL;
/* Make sure we don't already have it */
for (item = SDL_hapticlist; item; item = item->next) {
if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) {
return -1; /* Already added */
}
}
/* Open the device */
ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &device, NULL);
if (FAILED(ret)) {
/* DI_SetError("Creating DirectInput device",ret); */
return -1;
}
/* Get capabilities. */
SDL_zero(capabilities);
capabilities.dwSize = sizeof (DIDEVCAPS);
ret = IDirectInputDevice8_GetCapabilities(device, &capabilities);
IDirectInputDevice8_Release(device);
if (FAILED(ret)) {
/* DI_SetError("Getting device capabilities",ret); */
return -1;
}
if ((capabilities.dwFlags & needflags) != needflags) {
return -1; /* not a device we can use. */
}
item = (SDL_hapticlist_item *)SDL_malloc( sizeof(SDL_hapticlist_item));
if (item == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(item);
item->name = WIN_StringToUTF8(pdidInstance->tszProductName);
if (!item->name) {
SDL_free(item);
return -1;
}
/* Copy the instance over, useful for creating devices. */
SDL_memcpy(&item->instance, pdidInstance, sizeof (DIDEVICEINSTANCE));
SDL_memcpy(&item->capabilities, &capabilities, sizeof (capabilities));
if (SDL_hapticlist_tail == NULL) {
SDL_hapticlist = SDL_hapticlist_tail = item;
} else {
SDL_hapticlist_tail->next = item;
SDL_hapticlist_tail = item;
}
/* Device has been added. */
++numhaptics;
return numhaptics;
}
int
DirectInputHaptic_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance)
{
SDL_hapticlist_item *item;
SDL_hapticlist_item *prev = NULL;
for (item = SDL_hapticlist; item != NULL; item = item->next) {
if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) {
/* found it, remove it. */
const int retval = item->haptic ? item->haptic->index : -1;
if (prev != NULL) {
prev->next = item->next;
} else {
SDL_assert(SDL_hapticlist == item);
SDL_hapticlist = item->next;
}
if (item == SDL_hapticlist_tail) {
SDL_hapticlist_tail = prev;
}
--numhaptics;
/* !!! TODO: Send a haptic remove event? */
SDL_free(item);
return retval;
}
prev = item;
}
return -1;
}
int
XInputHaptic_MaybeAddDevice(const DWORD dwUserid)
{
const Uint8 userid = (Uint8) dwUserid;
XINPUT_CAPABILITIES caps;
const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
SDL_hapticlist_item *item;
if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) {
return -1;
}
/* Make sure we don't already have it */
for (item = SDL_hapticlist; item; item = item->next) {
if ((item->bXInputHaptic) && (item->userid == userid)) {
return -1; /* Already added */
}
}
if (XINPUTGETCAPABILITIES(dwUserid, XINPUT_FLAG_GAMEPAD, &caps) != ERROR_SUCCESS) {
return -1; /* maybe controller isn't plugged in. */
}
/* XInput < 1.4 is probably only for original XBox360 controllers,
which don't offer the flag, and always have force feedback */
if ( (bIs14OrLater) && ((caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) == 0) ) {
return -1; /* no force feedback on this device. */
}
item = (SDL_hapticlist_item *)SDL_malloc( sizeof(SDL_hapticlist_item));
if (item == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(item);
/* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */
{
char buf[64];
SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", (unsigned int) (userid+1));
item->name = SDL_strdup(buf);
}
if (!item->name) {
SDL_free(item);
return -1;
}
/* Copy the instance over, useful for creating devices. */
item->bXInputHaptic = 1;
item->userid = userid;
if (SDL_hapticlist_tail == NULL) {
SDL_hapticlist = SDL_hapticlist_tail = item;
} else {
SDL_hapticlist_tail->next = item;
SDL_hapticlist_tail = item;
}
/* Device has been added. */
++numhaptics;
return numhaptics;
}
int
XInputHaptic_MaybeRemoveDevice(const DWORD dwUserid)
{
const Uint8 userid = (Uint8) dwUserid;
SDL_hapticlist_item *item;
SDL_hapticlist_item *prev = NULL;
if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) {
return -1;
}
for (item = SDL_hapticlist; item != NULL; item = item->next) {
if ((item->bXInputHaptic) && (item->userid == userid)) {
/* found it, remove it. */
const int retval = item->haptic ? item->haptic->index : -1;
if (prev != NULL) {
prev->next = item->next;
} else {
SDL_assert(SDL_hapticlist == item);
SDL_hapticlist = item->next;
}
if (item == SDL_hapticlist_tail) {
SDL_hapticlist_tail = prev;
}
--numhaptics;
/* !!! TODO: Send a haptic remove event? */
SDL_free(item);
return retval;
}
prev = item;
}
return -1;
}
/*
* Callback to find the haptic devices.
*/
static BOOL CALLBACK
EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
{
HRESULT ret;
LPDIRECTINPUTDEVICE8 device;
/* Copy the instance over, useful for creating devices. */
SDL_memcpy(&SDL_hapticlist[SDL_numhaptics].instance, pdidInstance,
sizeof(DIDEVICEINSTANCE));
/* Open the device */
ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance,
&device, NULL);
if (FAILED(ret)) {
/* DI_SetError("Creating DirectInput device",ret); */
return DIENUM_CONTINUE;
}
/* Get capabilities. */
SDL_hapticlist[SDL_numhaptics].capabilities.dwSize = sizeof(DIDEVCAPS);
ret = IDirectInputDevice8_GetCapabilities(device,
&SDL_hapticlist[SDL_numhaptics].
capabilities);
if (FAILED(ret)) {
/* DI_SetError("Getting device capabilities",ret); */
IDirectInputDevice8_Release(device);
return DIENUM_CONTINUE;
}
/* Copy the name */
SDL_hapticlist[SDL_numhaptics].name = WIN_StringToUTF8(SDL_hapticlist[SDL_numhaptics].instance.tszProductName);
/* Close up device and count it. */
IDirectInputDevice8_Release(device);
SDL_numhaptics++;
/* Watch out for hard limit. */
if (SDL_numhaptics >= MAX_HAPTICS)
return DIENUM_STOP;
return DIENUM_CONTINUE;
(void) pContext;
DirectInputHaptic_MaybeAddDevice(pdidInstance);
return DIENUM_CONTINUE; /* continue enumerating */
}
int
SDL_SYS_NumHaptics()
{
return numhaptics;
}
static SDL_hapticlist_item *
HapticByDevIndex(int device_index)
{
SDL_hapticlist_item *item = SDL_hapticlist;
if ((device_index < 0) || (device_index >= numhaptics)) {
return NULL;
}
while (device_index > 0) {
SDL_assert(item != NULL);
device_index--;
item = item->next;
}
return item;
}
/*
* Return the name of a haptic device, does not need to be opened.
*/
const char *
SDL_SYS_HapticName(int index)
{
return SDL_hapticlist[index].name;
SDL_hapticlist_item *item = HapticByDevIndex(index);
return item->name;
}
@ -384,7 +561,7 @@ SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
}
static int
SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid)
SDL_SYS_HapticOpenFromXInput(SDL_Haptic *haptic, const Uint8 userid)
{
char threadName[32];
XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */
@ -595,11 +772,8 @@ SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
int
SDL_SYS_HapticOpen(SDL_Haptic * haptic)
{
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);
SDL_hapticlist_item *item = HapticByDevIndex(haptic->index);
return (item->bXInputHaptic) ? SDL_SYS_HapticOpenFromXInput(haptic, item->userid) : SDL_SYS_HapticOpenFromInstance(haptic, item->instance);
}
@ -609,13 +783,16 @@ SDL_SYS_HapticOpen(SDL_Haptic * haptic)
int
SDL_SYS_HapticMouse(void)
{
int i;
SDL_hapticlist_item *item;
int index = numhaptics-1;
/* Grab the first mouse haptic device we find. */
for (i = 0; i < SDL_numhaptics; i++) {
if (SDL_hapticlist[i].capabilities.dwDevType == DI8DEVCLASS_POINTER ) {
return i;
for (item = SDL_hapticlist; item != NULL; item = item->next) {
SDL_assert(index >= 0);
if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER ) {
return index;
}
index--;
}
return -1;
@ -677,34 +854,39 @@ SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
int
SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
{
int i;
HRESULT idret;
DIDEVICEINSTANCE joy_instance;
joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
SDL_hapticlist_item *item;
int index = numhaptics-1;
/* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
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)) {
for (item = SDL_hapticlist; item != NULL; item = item->next) {
if ((item->bXInputHaptic) && (item->userid == userid)) {
SDL_assert(joystick->hwdata->bXInputHaptic);
haptic->index = i;
return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
haptic->index = index;
return SDL_SYS_HapticOpenFromXInput(haptic, userid);
}
index--;
}
} 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;
HRESULT idret;
DIDEVICEINSTANCE joy_instance;
joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance);
if (FAILED(idret)) {
return -1;
}
for (item = SDL_hapticlist; item != NULL; item = item->next) {
if (DI_GUIDIsSame(&item->instance.guidInstance, &joy_instance.guidInstance)) {
haptic->index = index;
return SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice, SDL_TRUE);
}
index--;
}
}
/* No match to our haptic list */
return -1;
}
@ -749,16 +931,20 @@ SDL_SYS_HapticClose(SDL_Haptic * haptic)
void
SDL_SYS_HapticQuit(void)
{
int i;
SDL_hapticlist_item *item;
SDL_hapticlist_item *next = NULL;
if (loaded_xinput) {
WIN_UnloadXInputDLL();
loaded_xinput = SDL_FALSE;
}
for (i = 0; i < SDL_arraysize(SDL_hapticlist); ++i) {
SDL_free(SDL_hapticlist[i].name);
SDL_hapticlist[i].name = NULL;
for (item = SDL_hapticlist; item; item = next) {
/* Opened and not closed haptics are leaked, this is on purpose.
* Close your haptic devices after usage. */
next = item->next;
SDL_free(item->name);
SDL_free(item);
}
if (dinput != NULL) {