cscg22-gearboy

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

SDL_windowsjoystick.c (16970B)


      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#if SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT
     24
     25/* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
     26 * A. Formiga's WINMM driver.
     27 *
     28 * Hats and sliders are completely untested; the app I'm writing this for mostly
     29 * doesn't use them and I don't own any joysticks with them.
     30 *
     31 * We don't bother to use event notification here.  It doesn't seem to work
     32 * with polled devices, and it's fine to call IDirectInputDevice8_GetDeviceData and
     33 * let it return 0 events. */
     34
     35#include "SDL_error.h"
     36#include "SDL_assert.h"
     37#include "SDL_events.h"
     38#include "SDL_thread.h"
     39#include "SDL_timer.h"
     40#include "SDL_mutex.h"
     41#include "SDL_events.h"
     42#include "SDL_hints.h"
     43#include "SDL_joystick.h"
     44#include "../SDL_sysjoystick.h"
     45#if !SDL_EVENTS_DISABLED
     46#include "../../events/SDL_events_c.h"
     47#endif
     48#include "../../core/windows/SDL_windows.h"
     49#if !defined(__WINRT__)
     50#include <dbt.h>
     51#endif
     52
     53#define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
     54#include "SDL_windowsjoystick_c.h"
     55#include "SDL_dinputjoystick_c.h"
     56#include "SDL_xinputjoystick_c.h"
     57
     58#include "../../haptic/windows/SDL_dinputhaptic_c.h"    /* For haptic hot plugging */
     59#include "../../haptic/windows/SDL_xinputhaptic_c.h"    /* For haptic hot plugging */
     60
     61
     62#ifndef DEVICE_NOTIFY_WINDOW_HANDLE
     63#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
     64#endif
     65
     66/* local variables */
     67static SDL_bool s_bDeviceAdded = SDL_FALSE;
     68static SDL_bool s_bDeviceRemoved = SDL_FALSE;
     69static SDL_JoystickID s_nInstanceID = -1;
     70static SDL_cond *s_condJoystickThread = NULL;
     71static SDL_mutex *s_mutexJoyStickEnum = NULL;
     72static SDL_Thread *s_threadJoystick = NULL;
     73static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
     74
     75JoyStick_DeviceData *SYS_Joystick;    /* array to hold joystick ID values */
     76
     77static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
     78
     79#ifdef __WINRT__
     80
     81typedef struct
     82{
     83    int unused;
     84} SDL_DeviceNotificationData;
     85
     86static void
     87SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
     88{
     89}
     90
     91static int
     92SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
     93{
     94    return 0;
     95}
     96
     97static void
     98SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
     99{
    100}
    101
    102#else /* !__WINRT__ */
    103
    104typedef struct
    105{
    106    HRESULT coinitialized;
    107    WNDCLASSEX wincl;
    108    HWND messageWindow;
    109    HDEVNOTIFY hNotify;
    110} SDL_DeviceNotificationData;
    111
    112
    113/* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */
    114static LRESULT CALLBACK
    115SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    116{
    117    switch (message) {
    118    case WM_DEVICECHANGE:
    119        switch (wParam) {
    120        case DBT_DEVICEARRIVAL:
    121            if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
    122                s_bWindowsDeviceChanged = SDL_TRUE;
    123            }
    124            break;
    125        case DBT_DEVICEREMOVECOMPLETE:
    126            if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
    127                s_bWindowsDeviceChanged = SDL_TRUE;
    128            }
    129            break;
    130        }
    131        return 0;
    132    }
    133
    134    return DefWindowProc (hwnd, message, wParam, lParam);
    135}
    136
    137static void
    138SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
    139{
    140    if (data->hNotify)
    141        UnregisterDeviceNotification(data->hNotify);
    142
    143    if (data->messageWindow)
    144        DestroyWindow(data->messageWindow);
    145
    146    UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
    147
    148    if (data->coinitialized == S_OK) {
    149        WIN_CoUninitialize();
    150    }
    151}
    152
    153static int
    154SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
    155{
    156    DEV_BROADCAST_DEVICEINTERFACE dbh;
    157    GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
    158
    159    SDL_zerop(data);
    160
    161    data->coinitialized = WIN_CoInitialize();
    162
    163    data->wincl.hInstance = GetModuleHandle(NULL);
    164    data->wincl.lpszClassName = L"Message";
    165    data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc;      /* This function is called by windows */
    166    data->wincl.cbSize = sizeof (WNDCLASSEX);
    167
    168    if (!RegisterClassEx(&data->wincl)) {
    169        WIN_SetError("Failed to create register class for joystick autodetect");
    170        SDL_CleanupDeviceNotification(data);
    171        return -1;
    172    }
    173
    174    data->messageWindow = (HWND)CreateWindowEx(0,  L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    175    if (!data->messageWindow) {
    176        WIN_SetError("Failed to create message window for joystick autodetect");
    177        SDL_CleanupDeviceNotification(data);
    178        return -1;
    179    }
    180
    181    SDL_zero(dbh);
    182    dbh.dbcc_size = sizeof(dbh);
    183    dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    184    dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
    185
    186    data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
    187    if (!data->hNotify) {
    188        WIN_SetError("Failed to create notify device for joystick autodetect");
    189        SDL_CleanupDeviceNotification(data);
    190        return -1;
    191    }
    192    return 0;
    193}
    194
    195static void
    196SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
    197{
    198    MSG msg;
    199
    200    if (!data->messageWindow) {
    201        return;
    202    }
    203
    204    while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) {
    205        if (GetMessage(&msg, data->messageWindow, 0, 0) != 0)  {
    206            TranslateMessage(&msg);
    207            DispatchMessage(&msg);
    208        }
    209    }
    210}
    211
    212#endif /* __WINRT__ */
    213
    214/* Function/thread to scan the system for joysticks. */
    215static int
    216SDL_JoystickThread(void *_data)
    217{
    218    SDL_DeviceNotificationData notification_data;
    219
    220#if SDL_JOYSTICK_XINPUT
    221    SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT];
    222    SDL_zero(bOpenedXInputDevices);
    223#endif
    224
    225    if (SDL_CreateDeviceNotification(&notification_data) < 0) {
    226        return -1;
    227    }
    228
    229    SDL_LockMutex(s_mutexJoyStickEnum);
    230    while (s_bJoystickThreadQuit == SDL_FALSE) {
    231        SDL_bool bXInputChanged = SDL_FALSE;
    232
    233        SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300);
    234
    235        SDL_CheckDeviceNotification(&notification_data);
    236
    237#if SDL_JOYSTICK_XINPUT
    238        if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
    239            /* scan for any change in XInput devices */
    240            Uint8 userId;
    241            for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
    242                XINPUT_CAPABILITIES capabilities;
    243                const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
    244                const SDL_bool available = (result == ERROR_SUCCESS);
    245                if (bOpenedXInputDevices[userId] != available) {
    246                    bXInputChanged = SDL_TRUE;
    247                    bOpenedXInputDevices[userId] = available;
    248                }
    249            }
    250        }
    251#endif /* SDL_JOYSTICK_XINPUT */
    252
    253        if (s_bWindowsDeviceChanged || bXInputChanged) {
    254            SDL_UnlockMutex(s_mutexJoyStickEnum);  /* let main thread go while we SDL_Delay(). */
    255            SDL_Delay(300); /* wait for direct input to find out about this device */
    256            SDL_LockMutex(s_mutexJoyStickEnum);
    257
    258            s_bDeviceRemoved = SDL_TRUE;
    259            s_bDeviceAdded = SDL_TRUE;
    260            s_bWindowsDeviceChanged = SDL_FALSE;
    261        }
    262    }
    263    SDL_UnlockMutex(s_mutexJoyStickEnum);
    264
    265    SDL_CleanupDeviceNotification(&notification_data);
    266
    267    return 1;
    268}
    269
    270void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
    271{
    272    device->send_add_event = SDL_TRUE;
    273    device->nInstanceID = ++s_nInstanceID;
    274    device->pNext = SYS_Joystick;
    275    SYS_Joystick = device;
    276
    277    s_bDeviceAdded = SDL_TRUE;
    278}
    279
    280/* Function to scan the system for joysticks.
    281 * This function should set SDL_numjoysticks to the number of available
    282 * joysticks.  Joystick 0 should be the system default joystick.
    283 * It should return 0, or -1 on an unrecoverable fatal error.
    284 */
    285int
    286SDL_SYS_JoystickInit(void)
    287{
    288    if (SDL_DINPUT_JoystickInit() < 0) {
    289        SDL_SYS_JoystickQuit();
    290        return -1;
    291    }
    292
    293    if (SDL_XINPUT_JoystickInit() < 0) {
    294        SDL_SYS_JoystickQuit();
    295        return -1;
    296    }
    297
    298    s_mutexJoyStickEnum = SDL_CreateMutex();
    299    s_condJoystickThread = SDL_CreateCond();
    300    s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
    301
    302    SDL_SYS_JoystickDetect();
    303
    304    if (!s_threadJoystick) {
    305        s_bJoystickThreadQuit = SDL_FALSE;
    306        /* spin up the thread to detect hotplug of devices */
    307#if defined(__WIN32__) && !defined(HAVE_LIBC)
    308#undef SDL_CreateThread
    309#if SDL_DYNAMIC_API
    310        s_threadJoystick= SDL_CreateThread_REAL(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL);
    311#else
    312        s_threadJoystick= SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL);
    313#endif
    314#else
    315        s_threadJoystick = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL);
    316#endif
    317    }
    318    return SDL_SYS_NumJoysticks();
    319}
    320
    321/* return the number of joysticks that are connected right now */
    322int
    323SDL_SYS_NumJoysticks()
    324{
    325    int nJoysticks = 0;
    326    JoyStick_DeviceData *device = SYS_Joystick;
    327    while (device) {
    328        nJoysticks++;
    329        device = device->pNext;
    330    }
    331
    332    return nJoysticks;
    333}
    334
    335/* detect any new joysticks being inserted into the system */
    336void
    337SDL_SYS_JoystickDetect()
    338{
    339    JoyStick_DeviceData *pCurList = NULL;
    340#if !SDL_EVENTS_DISABLED
    341    SDL_Event event;
    342#endif
    343
    344    /* only enum the devices if the joystick thread told us something changed */
    345    if (!s_bDeviceAdded && !s_bDeviceRemoved) {
    346        return;  /* thread hasn't signaled, nothing to do right now. */
    347    }
    348
    349    SDL_LockMutex(s_mutexJoyStickEnum);
    350
    351    s_bDeviceAdded = SDL_FALSE;
    352    s_bDeviceRemoved = SDL_FALSE;
    353
    354    pCurList = SYS_Joystick;
    355    SYS_Joystick = NULL;
    356
    357    /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
    358    SDL_DINPUT_JoystickDetect(&pCurList);
    359
    360    /* Look for XInput devices. Do this last, so they're first in the final list. */
    361    SDL_XINPUT_JoystickDetect(&pCurList);
    362
    363    SDL_UnlockMutex(s_mutexJoyStickEnum);
    364
    365    while (pCurList) {
    366        JoyStick_DeviceData *pListNext = NULL;
    367
    368        if (pCurList->bXInputDevice) {
    369            SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId);
    370        } else {
    371            SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice);
    372        }
    373
    374#if !SDL_EVENTS_DISABLED
    375        SDL_zero(event);
    376        event.type = SDL_JOYDEVICEREMOVED;
    377
    378        if (SDL_GetEventState(event.type) == SDL_ENABLE) {
    379            event.jdevice.which = pCurList->nInstanceID;
    380            if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
    381                SDL_PushEvent(&event);
    382            }
    383        }
    384#endif /* !SDL_EVENTS_DISABLED */
    385
    386        pListNext = pCurList->pNext;
    387        SDL_free(pCurList->joystickname);
    388        SDL_free(pCurList);
    389        pCurList = pListNext;
    390    }
    391
    392    if (s_bDeviceAdded) {
    393        JoyStick_DeviceData *pNewJoystick;
    394        int device_index = 0;
    395        s_bDeviceAdded = SDL_FALSE;
    396        pNewJoystick = SYS_Joystick;
    397        while (pNewJoystick) {
    398            if (pNewJoystick->send_add_event) {
    399                if (pNewJoystick->bXInputDevice) {
    400                    SDL_XINPUT_MaybeAddDevice(pNewJoystick->XInputUserId);
    401                } else {
    402                    SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
    403                }
    404
    405#if !SDL_EVENTS_DISABLED
    406                SDL_zero(event);
    407                event.type = SDL_JOYDEVICEADDED;
    408
    409                if (SDL_GetEventState(event.type) == SDL_ENABLE) {
    410                    event.jdevice.which = device_index;
    411                    if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
    412                        SDL_PushEvent(&event);
    413                    }
    414                }
    415#endif /* !SDL_EVENTS_DISABLED */
    416                pNewJoystick->send_add_event = SDL_FALSE;
    417            }
    418            device_index++;
    419            pNewJoystick = pNewJoystick->pNext;
    420        }
    421    }
    422}
    423
    424/* Function to get the device-dependent name of a joystick */
    425const char *
    426SDL_SYS_JoystickNameForDeviceIndex(int device_index)
    427{
    428    JoyStick_DeviceData *device = SYS_Joystick;
    429
    430    for (; device_index > 0; device_index--)
    431        device = device->pNext;
    432
    433    return device->joystickname;
    434}
    435
    436/* Function to perform the mapping between current device instance and this joysticks instance id */
    437SDL_JoystickID
    438SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
    439{
    440    JoyStick_DeviceData *device = SYS_Joystick;
    441    int index;
    442
    443    for (index = device_index; index > 0; index--)
    444        device = device->pNext;
    445
    446    return device->nInstanceID;
    447}
    448
    449/* Function to open a joystick for use.
    450   The joystick to open is specified by the index field of the joystick.
    451   This should fill the nbuttons and naxes fields of the joystick structure.
    452   It returns 0, or -1 if there is an error.
    453 */
    454int
    455SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
    456{
    457    JoyStick_DeviceData *joystickdevice = SYS_Joystick;
    458
    459    for (; device_index > 0; device_index--)
    460        joystickdevice = joystickdevice->pNext;
    461
    462    /* allocate memory for system specific hardware data */
    463    joystick->instance_id = joystickdevice->nInstanceID;
    464    joystick->closed = SDL_FALSE;
    465    joystick->hwdata =
    466        (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
    467    if (joystick->hwdata == NULL) {
    468        return SDL_OutOfMemory();
    469    }
    470    SDL_zerop(joystick->hwdata);
    471    joystick->hwdata->guid = joystickdevice->guid;
    472
    473    if (joystickdevice->bXInputDevice) {
    474        return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
    475    } else {
    476        return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
    477    }
    478}
    479
    480/* return true if this joystick is plugged in right now */
    481SDL_bool 
    482SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
    483{
    484    return !joystick->closed && !joystick->hwdata->removed;
    485}
    486
    487void
    488SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
    489{
    490    if (joystick->closed || !joystick->hwdata) {
    491        return;
    492    }
    493
    494    if (joystick->hwdata->bXInputDevice) {
    495        SDL_XINPUT_JoystickUpdate(joystick);
    496    } else {
    497        SDL_DINPUT_JoystickUpdate(joystick);
    498    }
    499
    500    if (joystick->hwdata->removed) {
    501        joystick->closed = SDL_TRUE;
    502        joystick->uncentered = SDL_TRUE;
    503    }
    504}
    505
    506/* Function to close a joystick after use */
    507void
    508SDL_SYS_JoystickClose(SDL_Joystick * joystick)
    509{
    510    if (joystick->hwdata->bXInputDevice) {
    511        SDL_XINPUT_JoystickClose(joystick);
    512    } else {
    513        SDL_DINPUT_JoystickClose(joystick);
    514    }
    515
    516    /* free system specific hardware data */
    517    SDL_free(joystick->hwdata);
    518
    519    joystick->closed = SDL_TRUE;
    520}
    521
    522/* Function to perform any system-specific joystick related cleanup */
    523void
    524SDL_SYS_JoystickQuit(void)
    525{
    526    JoyStick_DeviceData *device = SYS_Joystick;
    527
    528    while (device) {
    529        JoyStick_DeviceData *device_next = device->pNext;
    530        SDL_free(device->joystickname);
    531        SDL_free(device);
    532        device = device_next;
    533    }
    534    SYS_Joystick = NULL;
    535
    536    if (s_threadJoystick) {
    537        SDL_LockMutex(s_mutexJoyStickEnum);
    538        s_bJoystickThreadQuit = SDL_TRUE;
    539        SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
    540        SDL_UnlockMutex(s_mutexJoyStickEnum);
    541        SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
    542
    543        SDL_DestroyMutex(s_mutexJoyStickEnum);
    544        SDL_DestroyCond(s_condJoystickThread);
    545        s_condJoystickThread= NULL;
    546        s_mutexJoyStickEnum = NULL;
    547        s_threadJoystick = NULL;
    548    }
    549
    550    SDL_DINPUT_JoystickQuit();
    551    SDL_XINPUT_JoystickQuit();
    552}
    553
    554/* return the stable device guid for this device index */
    555SDL_JoystickGUID
    556SDL_SYS_JoystickGetDeviceGUID(int device_index)
    557{
    558    JoyStick_DeviceData *device = SYS_Joystick;
    559    int index;
    560
    561    for (index = device_index; index > 0; index--)
    562        device = device->pNext;
    563
    564    return device->guid;
    565}
    566
    567SDL_JoystickGUID
    568SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
    569{
    570    return joystick->hwdata->guid;
    571}
    572
    573#endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
    574
    575/* vi: set ts=4 sw=4 expandtab: */