cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_xinputhaptic.c (13434B)


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