#include "../SDL_internal.h"
#include "SDL_syshaptic.h"
#include "SDL_haptic_c.h"
#include "../joystick/SDL_joystick_c.h"
#if (defined(SDL_HAPTIC_DINPUT) && SDL_HAPTIC_DINPUT) || (defined(SDL_HAPTIC_XINPUT) && SDL_HAPTIC_XINPUT)
SDL_Haptic *SDL_haptics = NULL;
#else
static SDL_Haptic *SDL_haptics = NULL;
#endif
int
SDL_HapticInit(void)
{
int status;
status = SDL_SYS_HapticInit();
if (status >= 0) {
status = 0;
}
return status;
}
static int
ValidHaptic(SDL_Haptic * haptic)
{
int valid;
SDL_Haptic *hapticlist;
valid = 0;
if (haptic != NULL) {
hapticlist = SDL_haptics;
while ( hapticlist )
{
if (hapticlist == haptic) {
valid = 1;
break;
}
hapticlist = hapticlist->next;
}
}
if (valid == 0) {
SDL_SetError("Haptic: Invalid haptic device identifier");
}
return valid;
}
int
SDL_NumHaptics(void)
{
return SDL_SYS_NumHaptics();
}
const char *
SDL_HapticName(int device_index)
{
if ((device_index < 0) || (device_index >= SDL_NumHaptics())) {
SDL_SetError("Haptic: There are %d haptic devices available",
SDL_NumHaptics());
return NULL;
}
return SDL_SYS_HapticName(device_index);
}
SDL_Haptic *
SDL_HapticOpen(int device_index)
{
SDL_Haptic *haptic;
SDL_Haptic *hapticlist;
if ((device_index < 0) || (device_index >= SDL_NumHaptics())) {
SDL_SetError("Haptic: There are %d haptic devices available",
SDL_NumHaptics());
return NULL;
}
hapticlist = SDL_haptics;
while ( hapticlist )
{
if (device_index == hapticlist->index) {
haptic = hapticlist;
++haptic->ref_count;
return haptic;
}
hapticlist = hapticlist->next;
}
haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic));
if (haptic == NULL) {
SDL_OutOfMemory();
return NULL;
}
SDL_memset(haptic, 0, (sizeof *haptic));
haptic->rumble_id = -1;
haptic->index = device_index;
if (SDL_SYS_HapticOpen(haptic) < 0) {
SDL_free(haptic);
return NULL;
}
++haptic->ref_count;
haptic->next = SDL_haptics;
SDL_haptics = haptic;
if (haptic->supported & SDL_HAPTIC_GAIN)
SDL_HapticSetGain(haptic, 100);
if (haptic->supported & SDL_HAPTIC_AUTOCENTER)
SDL_HapticSetAutocenter(haptic, 0);
return haptic;
}
int
SDL_HapticOpened(int device_index)
{
int opened;
SDL_Haptic *hapticlist;
if ((device_index < 0) || (device_index >= SDL_NumHaptics())) {
SDL_SetError("Haptic: There are %d haptic devices available",
SDL_NumHaptics());
return 0;
}
opened = 0;
hapticlist = SDL_haptics;
while ( hapticlist )
{
if (hapticlist->index == (Uint8) device_index) {
opened = 1;
break;
}
hapticlist = hapticlist->next;
}
return opened;
}
int
SDL_HapticIndex(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
return haptic->index;
}
int
SDL_MouseIsHaptic(void)
{
if (SDL_SYS_HapticMouse() < 0)
return SDL_FALSE;
return SDL_TRUE;
}
SDL_Haptic *
SDL_HapticOpenFromMouse(void)
{
int device_index;
device_index = SDL_SYS_HapticMouse();
if (device_index < 0) {
SDL_SetError("Haptic: Mouse isn't a haptic device.");
return NULL;
}
return SDL_HapticOpen(device_index);
}
int
SDL_JoystickIsHaptic(SDL_Joystick * joystick)
{
int ret;
if (!SDL_PrivateJoystickValid(joystick)) {
return -1;
}
ret = SDL_SYS_JoystickIsHaptic(joystick);
if (ret > 0)
return SDL_TRUE;
else if (ret == 0)
return SDL_FALSE;
else
return -1;
}
SDL_Haptic *
SDL_HapticOpenFromJoystick(SDL_Joystick * joystick)
{
SDL_Haptic *haptic;
SDL_Haptic *hapticlist;
if (SDL_NumHaptics() <= 0) {
SDL_SetError("Haptic: There are %d haptic devices available",
SDL_NumHaptics());
return NULL;
}
if (!SDL_PrivateJoystickValid(joystick)) {
SDL_SetError("Haptic: Joystick isn't valid.");
return NULL;
}
if (SDL_SYS_JoystickIsHaptic(joystick) <= 0) {
SDL_SetError("Haptic: Joystick isn't a haptic device.");
return NULL;
}
hapticlist = SDL_haptics;
while ( hapticlist )
{
if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) {
haptic = hapticlist;
++haptic->ref_count;
return haptic;
}
hapticlist = hapticlist->next;
}
haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic));
if (haptic == NULL) {
SDL_OutOfMemory();
return NULL;
}
SDL_memset(haptic, 0, sizeof(SDL_Haptic));
haptic->rumble_id = -1;
if (SDL_SYS_HapticOpenFromJoystick(haptic, joystick) < 0) {
SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
SDL_free(haptic);
return NULL;
}
++haptic->ref_count;
haptic->next = SDL_haptics;
SDL_haptics = haptic;
return haptic;
}
void
SDL_HapticClose(SDL_Haptic * haptic)
{
int i;
SDL_Haptic *hapticlist;
SDL_Haptic *hapticlistprev;
if (!ValidHaptic(haptic)) {
return;
}
if (--haptic->ref_count > 0) {
return;
}
for (i = 0; i < haptic->neffects; i++) {
if (haptic->effects[i].hweffect != NULL) {
SDL_HapticDestroyEffect(haptic, i);
}
}
SDL_SYS_HapticClose(haptic);
hapticlist = SDL_haptics;
hapticlistprev = NULL;
while ( hapticlist )
{
if (haptic == hapticlist)
{
if ( hapticlistprev )
{
hapticlistprev->next = hapticlist->next;
}
else
{
SDL_haptics = haptic->next;
}
break;
}
hapticlistprev = hapticlist;
hapticlist = hapticlist->next;
}
SDL_free(haptic);
}
void
SDL_HapticQuit(void)
{
while (SDL_haptics) {
SDL_HapticClose(SDL_haptics);
}
SDL_SYS_HapticQuit();
}
int
SDL_HapticNumEffects(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
return haptic->neffects;
}
int
SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
return haptic->nplaying;
}
unsigned int
SDL_HapticQuery(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return 0;
}
return haptic->supported;
}
int
SDL_HapticNumAxes(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
return haptic->naxes;
}
int
SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect)
{
if (!ValidHaptic(haptic)) {
return -1;
}
if ((haptic->supported & effect->type) != 0)
return SDL_TRUE;
return SDL_FALSE;
}
int
SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect)
{
int i;
if (!ValidHaptic(haptic)) {
return -1;
}
if (SDL_HapticEffectSupported(haptic, effect) == SDL_FALSE) {
return SDL_SetError("Haptic: Effect not supported by haptic device.");
}
for (i = 0; i < haptic->neffects; i++) {
if (haptic->effects[i].hweffect == NULL) {
if (SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)
!= 0) {
return -1;
}
SDL_memcpy(&haptic->effects[i].effect, effect,
sizeof(SDL_HapticEffect));
return i;
}
}
return SDL_SetError("Haptic: Device has no free space left.");
}
static int
ValidEffect(SDL_Haptic * haptic, int effect)
{
if ((effect < 0) || (effect >= haptic->neffects)) {
SDL_SetError("Haptic: Invalid effect identifier.");
return 0;
}
return 1;
}
int
SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect,
SDL_HapticEffect * data)
{
if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
return -1;
}
if (data->type != haptic->effects[effect].effect.type) {
return SDL_SetError("Haptic: Updating effect type is illegal.");
}
if (SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data) <
0) {
return -1;
}
SDL_memcpy(&haptic->effects[effect].effect, data,
sizeof(SDL_HapticEffect));
return 0;
}
int
SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations)
{
if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
return -1;
}
if (SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)
< 0) {
return -1;
}
return 0;
}
int
SDL_HapticStopEffect(SDL_Haptic * haptic, int effect)
{
if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
return -1;
}
if (SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect]) < 0) {
return -1;
}
return 0;
}
void
SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect)
{
if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
return;
}
if (haptic->effects[effect].hweffect == NULL) {
return;
}
SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]);
}
int
SDL_HapticGetEffectStatus(SDL_Haptic * haptic, int effect)
{
if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
return -1;
}
if ((haptic->supported & SDL_HAPTIC_STATUS) == 0) {
return SDL_SetError("Haptic: Device does not support status queries.");
}
return SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]);
}
int
SDL_HapticSetGain(SDL_Haptic * haptic, int gain)
{
const char *env;
int real_gain, max_gain;
if (!ValidHaptic(haptic)) {
return -1;
}
if ((haptic->supported & SDL_HAPTIC_GAIN) == 0) {
return SDL_SetError("Haptic: Device does not support setting gain.");
}
if ((gain < 0) || (gain > 100)) {
return SDL_SetError("Haptic: Gain must be between 0 and 100.");
}
env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
if (env != NULL) {
max_gain = SDL_atoi(env);
if (max_gain < 0)
max_gain = 0;
else if (max_gain > 100)
max_gain = 100;
real_gain = (gain * max_gain) / 100;
} else {
real_gain = gain;
}
if (SDL_SYS_HapticSetGain(haptic, real_gain) < 0) {
return -1;
}
return 0;
}
int
SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
{
if (!ValidHaptic(haptic)) {
return -1;
}
if ((haptic->supported & SDL_HAPTIC_AUTOCENTER) == 0) {
return SDL_SetError("Haptic: Device does not support setting autocenter.");
}
if ((autocenter < 0) || (autocenter > 100)) {
return SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
}
if (SDL_SYS_HapticSetAutocenter(haptic, autocenter) < 0) {
return -1;
}
return 0;
}
int
SDL_HapticPause(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) {
return SDL_SetError("Haptic: Device does not support setting pausing.");
}
return SDL_SYS_HapticPause(haptic);
}
int
SDL_HapticUnpause(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) {
return 0;
}
return SDL_SYS_HapticUnpause(haptic);
}
int
SDL_HapticStopAll(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
return SDL_SYS_HapticStopAll(haptic);
}
int
SDL_HapticRumbleSupported(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
return ((haptic->supported & (SDL_HAPTIC_SINE|SDL_HAPTIC_LEFTRIGHT)) != 0);
}
int
SDL_HapticRumbleInit(SDL_Haptic * haptic)
{
SDL_HapticEffect *efx = &haptic->rumble_effect;
if (!ValidHaptic(haptic)) {
return -1;
}
if (haptic->rumble_id >= 0) {
return 0;
}
SDL_zerop(efx);
if (haptic->supported & SDL_HAPTIC_SINE) {
efx->type = SDL_HAPTIC_SINE;
efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN;
efx->periodic.period = 1000;
efx->periodic.magnitude = 0x4000;
efx->periodic.length = 5000;
efx->periodic.attack_length = 0;
efx->periodic.fade_length = 0;
} else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) {
efx->type = SDL_HAPTIC_LEFTRIGHT;
efx->leftright.length = 5000;
efx->leftright.large_magnitude = 0x4000;
efx->leftright.small_magnitude = 0x4000;
} else {
return SDL_SetError("Device doesn't support rumble");
}
haptic->rumble_id = SDL_HapticNewEffect(haptic, &haptic->rumble_effect);
if (haptic->rumble_id >= 0) {
return 0;
}
return -1;
}
int
SDL_HapticRumblePlay(SDL_Haptic * haptic, float strength, Uint32 length)
{
SDL_HapticEffect *efx;
Sint16 magnitude;
if (!ValidHaptic(haptic)) {
return -1;
}
if (haptic->rumble_id < 0) {
return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
}
if (strength > 1.0f) {
strength = 1.0f;
} else if (strength < 0.0f) {
strength = 0.0f;
}
magnitude = (Sint16)(32767.0f*strength);
efx = &haptic->rumble_effect;
if (efx->type == SDL_HAPTIC_SINE) {
efx->periodic.magnitude = magnitude;
efx->periodic.length = length;
} else if (efx->type == SDL_HAPTIC_LEFTRIGHT) {
efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude;
efx->leftright.length = length;
} else {
SDL_assert(0 && "This should have been caught elsewhere");
}
if (SDL_HapticUpdateEffect(haptic, haptic->rumble_id, &haptic->rumble_effect) < 0) {
return -1;
}
return SDL_HapticRunEffect(haptic, haptic->rumble_id, 1);
}
int
SDL_HapticRumbleStop(SDL_Haptic * haptic)
{
if (!ValidHaptic(haptic)) {
return -1;
}
if (haptic->rumble_id < 0) {
return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
}
return SDL_HapticStopEffect(haptic, haptic->rumble_id);
}