sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_xinputhaptic.c (13305B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../../SDL_internal.h"
     22 
     23 #include "SDL_error.h"
     24 #include "SDL_haptic.h"
     25 #include "../SDL_syshaptic.h"
     26 
     27 #if SDL_HAPTIC_XINPUT
     28 
     29 #include "SDL_hints.h"
     30 #include "SDL_timer.h"
     31 #include "SDL_windowshaptic_c.h"
     32 #include "SDL_xinputhaptic_c.h"
     33 #include "../../core/windows/SDL_xinput.h"
     34 #include "../../joystick/windows/SDL_windowsjoystick_c.h"
     35 #include "../../thread/SDL_systhread.h"
     36 
     37 /*
     38  * Internal stuff.
     39  */
     40 static SDL_bool loaded_xinput = SDL_FALSE;
     41 
     42 
     43 int
     44 SDL_XINPUT_HapticInit(void)
     45 {
     46     if (SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE)) {
     47         loaded_xinput = (WIN_LoadXInputDLL() == 0);
     48     }
     49 
     50     if (loaded_xinput) {
     51         DWORD i;
     52         for (i = 0; i < XUSER_MAX_COUNT; i++) {
     53             SDL_XINPUT_MaybeAddDevice(i);
     54         }
     55     }
     56     return 0;
     57 }
     58 
     59 int
     60 SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
     61 {
     62     const Uint8 userid = (Uint8)dwUserid;
     63     SDL_hapticlist_item *item;
     64     XINPUT_VIBRATION state;
     65 
     66     if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
     67         return -1;
     68     }
     69 
     70     /* Make sure we don't already have it */
     71     for (item = SDL_hapticlist; item; item = item->next) {
     72         if (item->bXInputHaptic && item->userid == userid) {
     73             return -1;  /* Already added */
     74         }
     75     }
     76 
     77     SDL_zero(state);
     78     if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) {
     79         return -1;  /* no force feedback on this device. */
     80     }
     81 
     82     item = (SDL_hapticlist_item *)SDL_malloc(sizeof(SDL_hapticlist_item));
     83     if (item == NULL) {
     84         return SDL_OutOfMemory();
     85     }
     86 
     87     SDL_zerop(item);
     88 
     89     /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */
     90     {
     91         char buf[64];
     92         SDL_snprintf(buf, sizeof(buf), "XInput Controller #%u", (unsigned int)(userid + 1));
     93         item->name = SDL_strdup(buf);
     94     }
     95 
     96     if (!item->name) {
     97         SDL_free(item);
     98         return -1;
     99     }
    100 
    101     /* Copy the instance over, useful for creating devices. */
    102     item->bXInputHaptic = SDL_TRUE;
    103     item->userid = userid;
    104 
    105     return SDL_SYS_AddHapticDevice(item);
    106 }
    107 
    108 int
    109 SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
    110 {
    111     const Uint8 userid = (Uint8)dwUserid;
    112     SDL_hapticlist_item *item;
    113     SDL_hapticlist_item *prev = NULL;
    114 
    115     if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
    116         return -1;
    117     }
    118 
    119     for (item = SDL_hapticlist; item != NULL; item = item->next) {
    120         if (item->bXInputHaptic && item->userid == userid) {
    121             /* found it, remove it. */
    122             return SDL_SYS_RemoveHapticDevice(prev, item);
    123         }
    124         prev = item;
    125     }
    126     return -1;
    127 }
    128 
    129 /* !!! FIXME: this is a hack, remove this later. */
    130 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
    131  *  SDL_PumpEvents() to check if it's time to stop vibrating with some
    132  *  frequency.
    133  * In practice, this works for 99% of use cases. But in an ideal world,
    134  *  we do this in a separate thread so that:
    135  *    - we aren't bound to when the app chooses to pump the event queue.
    136  *    - we aren't adding more polling to the event queue
    137  *    - we can emulate all the haptic effects correctly (start on a delay,
    138  *      mix multiple effects, etc).
    139  *
    140  * Mostly, this is here to get rumbling to work, and all the other features
    141  *  are absent in the XInput path for now.  :(
    142  */
    143 static int SDLCALL
    144 SDL_RunXInputHaptic(void *arg)
    145 {
    146     struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
    147 
    148     while (!SDL_AtomicGet(&hwdata->stopThread)) {
    149         SDL_Delay(50);
    150         SDL_LockMutex(hwdata->mutex);
    151         /* If we're currently running and need to stop... */
    152         if (hwdata->stopTicks) {
    153             if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) {
    154                 XINPUT_VIBRATION vibration = { 0, 0 };
    155                 hwdata->stopTicks = 0;
    156                 XINPUTSETSTATE(hwdata->userid, &vibration);
    157             }
    158         }
    159         SDL_UnlockMutex(hwdata->mutex);
    160     }
    161 
    162     return 0;
    163 }
    164 
    165 static int
    166 SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid)
    167 {
    168     char threadName[32];
    169     XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
    170     XINPUTSETSTATE(userid, &vibration);
    171 
    172     haptic->supported = SDL_HAPTIC_LEFTRIGHT;
    173 
    174     haptic->neffects = 1;
    175     haptic->nplaying = 1;
    176 
    177     /* Prepare effects memory. */
    178     haptic->effects = (struct haptic_effect *)
    179         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
    180     if (haptic->effects == NULL) {
    181         return SDL_OutOfMemory();
    182     }
    183     /* Clear the memory */
    184     SDL_memset(haptic->effects, 0,
    185         sizeof(struct haptic_effect) * haptic->neffects);
    186 
    187     haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
    188     if (haptic->hwdata == NULL) {
    189         SDL_free(haptic->effects);
    190         haptic->effects = NULL;
    191         return SDL_OutOfMemory();
    192     }
    193     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
    194 
    195     haptic->hwdata->bXInputHaptic = 1;
    196     haptic->hwdata->userid = userid;
    197 
    198     haptic->hwdata->mutex = SDL_CreateMutex();
    199     if (haptic->hwdata->mutex == NULL) {
    200         SDL_free(haptic->effects);
    201         SDL_free(haptic->hwdata);
    202         haptic->effects = NULL;
    203         return SDL_SetError("Couldn't create XInput haptic mutex");
    204     }
    205 
    206     SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid);
    207     haptic->hwdata->thread = SDL_CreateThreadInternal(SDL_RunXInputHaptic, threadName, 64 * 1024, haptic->hwdata);
    208 
    209     if (haptic->hwdata->thread == NULL) {
    210         SDL_DestroyMutex(haptic->hwdata->mutex);
    211         SDL_free(haptic->effects);
    212         SDL_free(haptic->hwdata);
    213         haptic->effects = NULL;
    214         return SDL_SetError("Couldn't create XInput haptic thread");
    215     }
    216 
    217     return 0;
    218 }
    219 
    220 int
    221 SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item)
    222 {
    223     return SDL_XINPUT_HapticOpenFromUserIndex(haptic, item->userid);
    224 }
    225 
    226 int
    227 SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
    228 {
    229     return (haptic->hwdata->userid == joystick->hwdata->userid);
    230 }
    231 
    232 int
    233 SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
    234 {
    235     SDL_hapticlist_item *item;
    236     int index = 0;
    237 
    238     /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
    239     for (item = SDL_hapticlist; item != NULL; item = item->next) {
    240         if (item->bXInputHaptic && item->userid == joystick->hwdata->userid) {
    241             haptic->index = index;
    242             return SDL_XINPUT_HapticOpenFromUserIndex(haptic, joystick->hwdata->userid);
    243         }
    244         ++index;
    245     }
    246 
    247     SDL_SetError("Couldn't find joystick in haptic device list");
    248     return -1;
    249 }
    250 
    251 void
    252 SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
    253 {
    254     SDL_AtomicSet(&haptic->hwdata->stopThread, 1);
    255     SDL_WaitThread(haptic->hwdata->thread, NULL);
    256     SDL_DestroyMutex(haptic->hwdata->mutex);
    257 }
    258 
    259 void
    260 SDL_XINPUT_HapticQuit(void)
    261 {
    262     if (loaded_xinput) {
    263         WIN_UnloadXInputDLL();
    264         loaded_xinput = SDL_FALSE;
    265     }
    266 }
    267 
    268 int
    269 SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
    270 {
    271     SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
    272     return SDL_XINPUT_HapticUpdateEffect(haptic, effect, base);
    273 }
    274 
    275 int
    276 SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
    277 {
    278     XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
    279     SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
    280     /* SDL_HapticEffect has max magnitude of 32767, XInput expects 65535 max, so multiply */
    281     vib->wLeftMotorSpeed = data->leftright.large_magnitude * 2;
    282     vib->wRightMotorSpeed = data->leftright.small_magnitude * 2;
    283     SDL_LockMutex(haptic->hwdata->mutex);
    284     if (haptic->hwdata->stopTicks) {  /* running right now? Update it. */
    285         XINPUTSETSTATE(haptic->hwdata->userid, vib);
    286     }
    287     SDL_UnlockMutex(haptic->hwdata->mutex);
    288     return 0;
    289 }
    290 
    291 int
    292 SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
    293 {
    294     XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
    295     SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
    296     SDL_LockMutex(haptic->hwdata->mutex);
    297     if (effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) {
    298         haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
    299     } else if ((!effect->effect.leftright.length) || (!iterations)) {
    300         /* do nothing. Effect runs for zero milliseconds. */
    301     } else {
    302         haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
    303         if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) {
    304             haptic->hwdata->stopTicks = 1;  /* fix edge cases. */
    305         }
    306     }
    307     SDL_UnlockMutex(haptic->hwdata->mutex);
    308     return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
    309 }
    310 
    311 int
    312 SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
    313 {
    314     XINPUT_VIBRATION vibration = { 0, 0 };
    315     SDL_LockMutex(haptic->hwdata->mutex);
    316     haptic->hwdata->stopTicks = 0;
    317     SDL_UnlockMutex(haptic->hwdata->mutex);
    318     return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
    319 }
    320 
    321 void
    322 SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
    323 {
    324     SDL_XINPUT_HapticStopEffect(haptic, effect);
    325 }
    326 
    327 int
    328 SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
    329 {
    330     return SDL_Unsupported();
    331 }
    332 
    333 int
    334 SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
    335 {
    336     return SDL_Unsupported();
    337 }
    338 
    339 int
    340 SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
    341 {
    342     return SDL_Unsupported();
    343 }
    344 
    345 int
    346 SDL_XINPUT_HapticPause(SDL_Haptic * haptic)
    347 {
    348     return SDL_Unsupported();
    349 }
    350 
    351 int
    352 SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic)
    353 {
    354     return SDL_Unsupported();
    355 }
    356 
    357 int
    358 SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic)
    359 {
    360     XINPUT_VIBRATION vibration = { 0, 0 };
    361     SDL_LockMutex(haptic->hwdata->mutex);
    362     haptic->hwdata->stopTicks = 0;
    363     SDL_UnlockMutex(haptic->hwdata->mutex);
    364     return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
    365 }
    366 
    367 #else /* !SDL_HAPTIC_XINPUT */
    368 
    369 #include "../../core/windows/SDL_windows.h"
    370 
    371 typedef struct SDL_hapticlist_item SDL_hapticlist_item;
    372 
    373 int
    374 SDL_XINPUT_HapticInit(void)
    375 {
    376     return 0;
    377 }
    378 
    379 int
    380 SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
    381 {
    382     return SDL_Unsupported();
    383 }
    384 
    385 int
    386 SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
    387 {
    388     return SDL_Unsupported();
    389 }
    390 
    391 int
    392 SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item)
    393 {
    394     return SDL_Unsupported();
    395 }
    396 
    397 int
    398 SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
    399 {
    400     return SDL_Unsupported();
    401 }
    402 
    403 int
    404 SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
    405 {
    406     return SDL_Unsupported();
    407 }
    408 
    409 void
    410 SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
    411 {
    412 }
    413 
    414 void
    415 SDL_XINPUT_HapticQuit(void)
    416 {
    417 }
    418 
    419 int
    420 SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
    421 {
    422     return SDL_Unsupported();
    423 }
    424 
    425 int
    426 SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
    427 {
    428     return SDL_Unsupported();
    429 }
    430 
    431 int
    432 SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
    433 {
    434     return SDL_Unsupported();
    435 }
    436 
    437 int
    438 SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
    439 {
    440     return SDL_Unsupported();
    441 }
    442 
    443 void
    444 SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
    445 {
    446 }
    447 
    448 int
    449 SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
    450 {
    451     return SDL_Unsupported();
    452 }
    453 
    454 int
    455 SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
    456 {
    457     return SDL_Unsupported();
    458 }
    459 
    460 int
    461 SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
    462 {
    463     return SDL_Unsupported();
    464 }
    465 
    466 int
    467 SDL_XINPUT_HapticPause(SDL_Haptic * haptic)
    468 {
    469     return SDL_Unsupported();
    470 }
    471 
    472 int
    473 SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic)
    474 {
    475     return SDL_Unsupported();
    476 }
    477 
    478 int
    479 SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic)
    480 {
    481     return SDL_Unsupported();
    482 }
    483 
    484 #endif /* SDL_HAPTIC_XINPUT */
    485 
    486 /* vi: set ts=4 sw=4 expandtab: */