#include "../../SDL_internal.h"
#include "SDL.h"
#include "SDL_error.h"
#include "SDL_haptic.h"
#include "../SDL_syshaptic.h"
#if SDL_HAPTIC_XINPUT
#include "SDL_hints.h"
#include "SDL_timer.h"
#include "SDL_windowshaptic_c.h"
#include "SDL_xinputhaptic_c.h"
#include "../../core/windows/SDL_xinput.h"
#include "../../joystick/windows/SDL_windowsjoystick_c.h"
#include "../../thread/SDL_systhread.h"
#ifdef __cplusplus
extern "C" {
#endif
static SDL_bool loaded_xinput = SDL_FALSE;
int
SDL_XINPUT_HapticInit(void)
{
if (SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE)) {
loaded_xinput = (WIN_LoadXInputDLL() == 0) ? SDL_TRUE : SDL_FALSE;
}
if (loaded_xinput && !SDL_WasInit(SDL_INIT_JOYSTICK)) {
DWORD i;
for (i = 0; i < XUSER_MAX_COUNT; i++) {
SDL_XINPUT_HapticMaybeAddDevice(i);
}
}
return 0;
}
int
SDL_XINPUT_HapticMaybeAddDevice(const DWORD dwUserid)
{
const Uint8 userid = (Uint8)dwUserid;
SDL_hapticlist_item *item;
XINPUT_VIBRATION state;
if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
return -1;
}
for (item = SDL_hapticlist; item; item = item->next) {
if (item->bXInputHaptic && item->userid == userid) {
return -1;
}
}
SDL_zero(state);
if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) {
return -1;
}
item = (SDL_hapticlist_item *)SDL_malloc(sizeof(SDL_hapticlist_item));
if (item == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(item);
{
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;
}
item->bXInputHaptic = SDL_TRUE;
item->userid = userid;
return SDL_SYS_AddHapticDevice(item);
}
int
SDL_XINPUT_HapticMaybeRemoveDevice(const DWORD dwUserid)
{
const Uint8 userid = (Uint8)dwUserid;
SDL_hapticlist_item *item;
SDL_hapticlist_item *prev = NULL;
if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
return -1;
}
for (item = SDL_hapticlist; item != NULL; item = item->next) {
if (item->bXInputHaptic && item->userid == userid) {
return SDL_SYS_RemoveHapticDevice(prev, item);
}
prev = item;
}
return -1;
}
static int SDLCALL
SDL_RunXInputHaptic(void *arg)
{
struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
while (!SDL_AtomicGet(&hwdata->stopThread)) {
SDL_Delay(50);
SDL_LockMutex(hwdata->mutex);
if (hwdata->stopTicks) {
if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) {
XINPUT_VIBRATION vibration = { 0, 0 };
hwdata->stopTicks = 0;
XINPUTSETSTATE(hwdata->userid, &vibration);
}
}
SDL_UnlockMutex(hwdata->mutex);
}
return 0;
}
static int
SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid)
{
char threadName[32];
XINPUT_VIBRATION vibration = { 0, 0 };
XINPUTSETSTATE(userid, &vibration);
haptic->supported = SDL_HAPTIC_LEFTRIGHT;
haptic->neffects = 1;
haptic->nplaying = 1;
haptic->effects = (struct haptic_effect *)
SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
if (haptic->effects == NULL) {
return SDL_OutOfMemory();
}
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;
return SDL_OutOfMemory();
}
SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
haptic->hwdata->bXInputHaptic = 1;
haptic->hwdata->userid = userid;
haptic->hwdata->mutex = SDL_CreateMutex();
if (haptic->hwdata->mutex == NULL) {
SDL_free(haptic->effects);
SDL_free(haptic->hwdata);
haptic->effects = NULL;
return SDL_SetError("Couldn't create XInput haptic mutex");
}
SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid);
haptic->hwdata->thread = SDL_CreateThreadInternal(SDL_RunXInputHaptic, threadName, 64 * 1024, haptic->hwdata);
if (haptic->hwdata->thread == NULL) {
SDL_DestroyMutex(haptic->hwdata->mutex);
SDL_free(haptic->effects);
SDL_free(haptic->hwdata);
haptic->effects = NULL;
return SDL_SetError("Couldn't create XInput haptic thread");
}
return 0;
}
int
SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item)
{
return SDL_XINPUT_HapticOpenFromUserIndex(haptic, item->userid);
}
int
SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
{
return (haptic->hwdata->userid == joystick->hwdata->userid);
}
int
SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
{
SDL_hapticlist_item *item;
int index = 0;
for (item = SDL_hapticlist; item != NULL; item = item->next) {
if (item->bXInputHaptic && item->userid == joystick->hwdata->userid) {
haptic->index = index;
return SDL_XINPUT_HapticOpenFromUserIndex(haptic, joystick->hwdata->userid);
}
++index;
}
return SDL_SetError("Couldn't find joystick in haptic device list");
}
void
SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
{
SDL_AtomicSet(&haptic->hwdata->stopThread, 1);
SDL_WaitThread(haptic->hwdata->thread, NULL);
SDL_DestroyMutex(haptic->hwdata->mutex);
}
void
SDL_XINPUT_HapticQuit(void)
{
if (loaded_xinput) {
WIN_UnloadXInputDLL();
loaded_xinput = SDL_FALSE;
}
}
int
SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
{
SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);
return SDL_XINPUT_HapticUpdateEffect(haptic, effect, base);
}
int
SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
{
XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
vib->wLeftMotorSpeed = data->leftright.large_magnitude * 2;
vib->wRightMotorSpeed = data->leftright.small_magnitude * 2;
SDL_LockMutex(haptic->hwdata->mutex);
if (haptic->hwdata->stopTicks) {
XINPUTSETSTATE(haptic->hwdata->userid, vib);
}
SDL_UnlockMutex(haptic->hwdata->mutex);
return 0;
}
int
SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
{
XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);
SDL_LockMutex(haptic->hwdata->mutex);
if (effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) {
haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
} else if ((!effect->effect.leftright.length) || (!iterations)) {
} else {
haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) {
haptic->hwdata->stopTicks = 1;
}
}
SDL_UnlockMutex(haptic->hwdata->mutex);
return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
}
int
SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
{
XINPUT_VIBRATION vibration = { 0, 0 };
SDL_LockMutex(haptic->hwdata->mutex);
haptic->hwdata->stopTicks = 0;
SDL_UnlockMutex(haptic->hwdata->mutex);
return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
}
void
SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
{
SDL_XINPUT_HapticStopEffect(haptic, effect);
}
int
SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticPause(SDL_Haptic * haptic)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic)
{
XINPUT_VIBRATION vibration = { 0, 0 };
SDL_LockMutex(haptic->hwdata->mutex);
haptic->hwdata->stopTicks = 0;
SDL_UnlockMutex(haptic->hwdata->mutex);
return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
}
#ifdef __cplusplus
}
#endif
#else
#include "../../core/windows/SDL_windows.h"
typedef struct SDL_hapticlist_item SDL_hapticlist_item;
int
SDL_XINPUT_HapticInit(void)
{
return 0;
}
int
SDL_XINPUT_HapticMaybeAddDevice(const DWORD dwUserid)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticMaybeRemoveDevice(const DWORD dwUserid)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
{
return SDL_Unsupported();
}
void
SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
{
}
void
SDL_XINPUT_HapticQuit(void)
{
}
int
SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
{
return SDL_Unsupported();
}
void
SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
{
}
int
SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticPause(SDL_Haptic * haptic)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic)
{
return SDL_Unsupported();
}
int
SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic)
{
return SDL_Unsupported();
}
#endif