cscg22-gearboy

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

SDL_xinputjoystick.c (12265B)


      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_hints.h"
     25#include "../SDL_sysjoystick.h"
     26#include "SDL_windowsjoystick_c.h"
     27#include "SDL_xinputjoystick_c.h"
     28
     29
     30#if SDL_JOYSTICK_XINPUT
     31
     32/*
     33 * Internal stuff.
     34 */
     35static SDL_bool s_bXInputEnabled = SDL_TRUE;
     36
     37
     38static SDL_bool
     39SDL_XInputUseOldJoystickMapping()
     40{
     41    static int s_XInputUseOldJoystickMapping = -1;
     42    if (s_XInputUseOldJoystickMapping < 0) {
     43        const char *hint = SDL_GetHint(SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING);
     44        s_XInputUseOldJoystickMapping = (hint && *hint == '1') ? 1 : 0;
     45    }
     46    return (s_XInputUseOldJoystickMapping > 0);
     47}
     48
     49SDL_bool SDL_XINPUT_Enabled(void)
     50{
     51    return s_bXInputEnabled;
     52}
     53
     54int
     55SDL_XINPUT_JoystickInit(void)
     56{
     57    const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
     58    if (env && !SDL_atoi(env)) {
     59        s_bXInputEnabled = SDL_FALSE;
     60    }
     61
     62    if (s_bXInputEnabled && WIN_LoadXInputDLL() < 0) {
     63        s_bXInputEnabled = SDL_FALSE;  /* oh well. */
     64    }
     65    return 0;
     66}
     67
     68static char *
     69GetXInputName(const Uint8 userid, BYTE SubType)
     70{
     71    char name[32];
     72
     73    if (SDL_XInputUseOldJoystickMapping()) {
     74        SDL_snprintf(name, sizeof(name), "X360 Controller #%u", 1 + userid);
     75    } else {
     76        switch (SubType) {
     77        case XINPUT_DEVSUBTYPE_GAMEPAD:
     78            SDL_snprintf(name, sizeof(name), "XInput Controller #%u", 1 + userid);
     79            break;
     80        case XINPUT_DEVSUBTYPE_WHEEL:
     81            SDL_snprintf(name, sizeof(name), "XInput Wheel #%u", 1 + userid);
     82            break;
     83        case XINPUT_DEVSUBTYPE_ARCADE_STICK:
     84            SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%u", 1 + userid);
     85            break;
     86        case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
     87            SDL_snprintf(name, sizeof(name), "XInput FlightStick #%u", 1 + userid);
     88            break;
     89        case XINPUT_DEVSUBTYPE_DANCE_PAD:
     90            SDL_snprintf(name, sizeof(name), "XInput DancePad #%u", 1 + userid);
     91            break;
     92        case XINPUT_DEVSUBTYPE_GUITAR:
     93        case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
     94        case XINPUT_DEVSUBTYPE_GUITAR_BASS:
     95            SDL_snprintf(name, sizeof(name), "XInput Guitar #%u", 1 + userid);
     96            break;
     97        case XINPUT_DEVSUBTYPE_DRUM_KIT:
     98            SDL_snprintf(name, sizeof(name), "XInput DrumKit #%u", 1 + userid);
     99            break;
    100        case XINPUT_DEVSUBTYPE_ARCADE_PAD:
    101            SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%u", 1 + userid);
    102            break;
    103        default:
    104            SDL_snprintf(name, sizeof(name), "XInput Device #%u", 1 + userid);
    105            break;
    106        }
    107    }
    108    return SDL_strdup(name);
    109}
    110
    111static void
    112AddXInputDevice(const Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
    113{
    114    JoyStick_DeviceData *pPrevJoystick = NULL;
    115    JoyStick_DeviceData *pNewJoystick = *pContext;
    116
    117    if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
    118        return;
    119
    120    if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN)
    121        return;
    122
    123    while (pNewJoystick) {
    124        if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) {
    125            /* if we are replacing the front of the list then update it */
    126            if (pNewJoystick == *pContext) {
    127                *pContext = pNewJoystick->pNext;
    128            } else if (pPrevJoystick) {
    129                pPrevJoystick->pNext = pNewJoystick->pNext;
    130            }
    131
    132            pNewJoystick->pNext = SYS_Joystick;
    133            SYS_Joystick = pNewJoystick;
    134            return;   /* already in the list. */
    135        }
    136
    137        pPrevJoystick = pNewJoystick;
    138        pNewJoystick = pNewJoystick->pNext;
    139    }
    140
    141    pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
    142    if (!pNewJoystick) {
    143        return; /* better luck next time? */
    144    }
    145    SDL_zerop(pNewJoystick);
    146
    147    pNewJoystick->joystickname = GetXInputName(userid, SubType);
    148    if (!pNewJoystick->joystickname) {
    149        SDL_free(pNewJoystick);
    150        return; /* better luck next time? */
    151    }
    152
    153    pNewJoystick->bXInputDevice = SDL_TRUE;
    154    if (SDL_XInputUseOldJoystickMapping()) {
    155        SDL_zero(pNewJoystick->guid);
    156    } else {
    157        pNewJoystick->guid.data[0] = 'x';
    158        pNewJoystick->guid.data[1] = 'i';
    159        pNewJoystick->guid.data[2] = 'n';
    160        pNewJoystick->guid.data[3] = 'p';
    161        pNewJoystick->guid.data[4] = 'u';
    162        pNewJoystick->guid.data[5] = 't';
    163        pNewJoystick->guid.data[6] = SubType;
    164    }
    165    pNewJoystick->SubType = SubType;
    166    pNewJoystick->XInputUserId = userid;
    167    SDL_SYS_AddJoystickDevice(pNewJoystick);
    168}
    169
    170void
    171SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
    172{
    173    int iuserid;
    174
    175    if (!s_bXInputEnabled) {
    176        return;
    177    }
    178
    179    /* iterate in reverse, so these are in the final list in ascending numeric order. */
    180    for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) {
    181        const Uint8 userid = (Uint8)iuserid;
    182        XINPUT_CAPABILITIES capabilities;
    183        if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
    184            AddXInputDevice(userid, capabilities.SubType, pContext);
    185        }
    186    }
    187}
    188
    189int
    190SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
    191{
    192    const Uint8 userId = joystickdevice->XInputUserId;
    193    XINPUT_CAPABILITIES capabilities;
    194    XINPUT_VIBRATION state;
    195
    196    SDL_assert(s_bXInputEnabled);
    197    SDL_assert(XINPUTGETCAPABILITIES);
    198    SDL_assert(XINPUTSETSTATE);
    199    SDL_assert(userId < XUSER_MAX_COUNT);
    200
    201    joystick->hwdata->bXInputDevice = SDL_TRUE;
    202
    203    if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
    204        SDL_free(joystick->hwdata);
    205        joystick->hwdata = NULL;
    206        return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
    207    }
    208    SDL_zero(state);
    209    joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
    210    joystick->hwdata->userid = userId;
    211
    212    /* The XInput API has a hard coded button/axis mapping, so we just match it */
    213    if (SDL_XInputUseOldJoystickMapping()) {
    214        joystick->naxes = 6;
    215        joystick->nbuttons = 15;
    216    } else {
    217        joystick->naxes = 6;
    218        joystick->nbuttons = 11;
    219        joystick->nhats = 1;
    220    }
    221    return 0;
    222}
    223
    224static void
    225UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState)
    226{
    227    static WORD s_XInputButtons[] = {
    228        XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
    229        XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
    230        XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER,
    231        XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
    232        XINPUT_GAMEPAD_GUIDE
    233    };
    234    WORD wButtons = pXInputState->Gamepad.wButtons;
    235    Uint8 button;
    236
    237    SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
    238    SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
    239    SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX);
    240    SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
    241    SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
    242    SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
    243
    244    for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
    245        SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
    246    }
    247}
    248
    249static void
    250UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState)
    251{
    252    static WORD s_XInputButtons[] = {
    253        XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
    254        XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
    255        XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
    256        XINPUT_GAMEPAD_GUIDE
    257    };
    258    WORD wButtons = pXInputState->Gamepad.wButtons;
    259    Uint8 button;
    260    Uint8 hat = SDL_HAT_CENTERED;
    261
    262    SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
    263    SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
    264    SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
    265    SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX);
    266    SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
    267    SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
    268
    269    for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
    270        SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
    271    }
    272
    273    if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
    274        hat |= SDL_HAT_UP;
    275    }
    276    if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
    277        hat |= SDL_HAT_DOWN;
    278    }
    279    if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
    280        hat |= SDL_HAT_LEFT;
    281    }
    282    if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
    283        hat |= SDL_HAT_RIGHT;
    284    }
    285    SDL_PrivateJoystickHat(joystick, 0, hat);
    286}
    287
    288void
    289SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
    290{
    291    HRESULT result;
    292    XINPUT_STATE_EX XInputState;
    293
    294    if (!XINPUTGETSTATE)
    295        return;
    296
    297    result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
    298    if (result == ERROR_DEVICE_NOT_CONNECTED) {
    299        joystick->hwdata->send_remove_event = SDL_TRUE;
    300        joystick->hwdata->removed = SDL_TRUE;
    301        return;
    302    }
    303
    304    /* only fire events if the data changed from last time */
    305    if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
    306        if (SDL_XInputUseOldJoystickMapping()) {
    307            UpdateXInputJoystickState_OLD(joystick, &XInputState);
    308        } else {
    309            UpdateXInputJoystickState(joystick, &XInputState);
    310        }
    311        joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
    312    }
    313}
    314
    315void
    316SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
    317{
    318}
    319
    320void
    321SDL_XINPUT_JoystickQuit(void)
    322{
    323    if (s_bXInputEnabled) {
    324        WIN_UnloadXInputDLL();
    325    }
    326}
    327
    328SDL_bool
    329SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index)
    330{
    331    JoyStick_DeviceData *device = SYS_Joystick;
    332    int index;
    333
    334    for (index = device_index; index > 0; index--)
    335        device = device->pNext;
    336
    337    return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD);
    338}
    339
    340#else /* !SDL_JOYSTICK_XINPUT */
    341
    342
    343SDL_bool SDL_XINPUT_Enabled(void)
    344{
    345    return SDL_FALSE;
    346}
    347
    348int
    349SDL_XINPUT_JoystickInit(void)
    350{
    351    return 0;
    352}
    353
    354void
    355SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
    356{
    357}
    358
    359int
    360SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
    361{
    362    return SDL_Unsupported();
    363}
    364
    365void
    366SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
    367{
    368}
    369
    370void
    371SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
    372{
    373}
    374
    375void
    376SDL_XINPUT_JoystickQuit(void)
    377{
    378}
    379
    380#endif /* SDL_JOYSTICK_XINPUT */
    381
    382/* vi: set ts=4 sw=4 expandtab: */