mirror of
https://github.com/Relintai/sdl2_frt.git
synced 2025-03-16 15:06:22 +01:00
494 lines
13 KiB
C
494 lines
13 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
#include "../../SDL_internal.h"
|
|
|
|
#include "SDL_error.h"
|
|
#include "SDL_haptic.h"
|
|
#include "../SDL_syshaptic.h"
|
|
|
|
#if SDL_HAPTIC_XINPUT
|
|
|
|
#include "SDL_assert.h"
|
|
#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"
|
|
|
|
/*
|
|
* Internal stuff.
|
|
*/
|
|
static SDL_bool loaded_xinput = SDL_FALSE;
|
|
|
|
|
|
int
|
|
SDL_XINPUT_HapticInit(void)
|
|
{
|
|
const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
|
|
if (!env || SDL_atoi(env)) {
|
|
loaded_xinput = (WIN_LoadXInputDLL() == 0);
|
|
}
|
|
|
|
if (loaded_xinput) {
|
|
DWORD i;
|
|
for (i = 0; i < XUSER_MAX_COUNT; i++) {
|
|
SDL_XINPUT_MaybeAddDevice(i);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
|
|
{
|
|
const Uint8 userid = (Uint8)dwUserid;
|
|
SDL_hapticlist_item *item;
|
|
XINPUT_VIBRATION state;
|
|
|
|
if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
|
|
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 */
|
|
}
|
|
}
|
|
|
|
SDL_zero(state);
|
|
if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) {
|
|
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 = SDL_TRUE;
|
|
item->userid = userid;
|
|
|
|
return SDL_SYS_AddHapticDevice(item);
|
|
}
|
|
|
|
int
|
|
SDL_XINPUT_MaybeRemoveDevice(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) {
|
|
/* found it, remove it. */
|
|
return SDL_SYS_RemoveHapticDevice(prev, item);
|
|
}
|
|
prev = item;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* !!! FIXME: this is a hack, remove this later. */
|
|
/* Since XInput doesn't offer a way to vibrate for X time, we hook into
|
|
* SDL_PumpEvents() to check if it's time to stop vibrating with some
|
|
* frequency.
|
|
* In practice, this works for 99% of use cases. But in an ideal world,
|
|
* we do this in a separate thread so that:
|
|
* - we aren't bound to when the app chooses to pump the event queue.
|
|
* - we aren't adding more polling to the event queue
|
|
* - we can emulate all the haptic effects correctly (start on a delay,
|
|
* mix multiple effects, etc).
|
|
*
|
|
* Mostly, this is here to get rumbling to work, and all the other features
|
|
* are absent in the XInput path for now. :(
|
|
*/
|
|
static int SDLCALL
|
|
SDL_RunXInputHaptic(void *arg)
|
|
{
|
|
struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
|
|
|
|
while (!hwdata->stopThread) {
|
|
SDL_Delay(50);
|
|
SDL_LockMutex(hwdata->mutex);
|
|
/* If we're currently running and need to stop... */
|
|
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 }; /* stop any current vibration */
|
|
XINPUTSETSTATE(userid, &vibration);
|
|
|
|
haptic->supported = SDL_HAPTIC_LEFTRIGHT;
|
|
|
|
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) {
|
|
return SDL_OutOfMemory();
|
|
}
|
|
/* 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;
|
|
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);
|
|
|
|
#if defined(__WIN32__) && !defined(HAVE_LIBC) /* !!! FIXME: this is nasty. */
|
|
#undef SDL_CreateThread
|
|
#if SDL_DYNAMIC_API
|
|
haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
|
|
#else
|
|
haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
|
|
#endif
|
|
#else
|
|
haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata);
|
|
#endif
|
|
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;
|
|
|
|
/* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
|
|
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;
|
|
}
|
|
|
|
SDL_SetError("Couldn't find joystick in haptic device list");
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
|
|
{
|
|
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); /* should catch this at higher level */
|
|
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;
|
|
vib->wRightMotorSpeed = data->leftright.small_magnitude;
|
|
SDL_LockMutex(haptic->hwdata->mutex);
|
|
if (haptic->hwdata->stopTicks) { /* running right now? Update it. */
|
|
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); /* should catch this at higher level */
|
|
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)) {
|
|
/* do nothing. Effect runs for zero milliseconds. */
|
|
} 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; /* fix edge cases. */
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
#else /* !SDL_HAPTIC_XINPUT */
|
|
|
|
typedef struct SDL_hapticlist_item SDL_hapticlist_item;
|
|
|
|
int
|
|
SDL_XINPUT_HapticInit(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
|
|
{
|
|
return SDL_Unsupported();
|
|
}
|
|
|
|
int
|
|
SDL_XINPUT_MaybeRemoveDevice(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 /* SDL_HAPTIC_XINPUT */
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|