Wired up haptic hotplugging for Windows DirectInput/XInput code.
This commit is contained in:
parent
9cf8c49c39
commit
e42d46b463
3 changed files with 334 additions and 95 deletions
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue