sdl

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

SDL_windowsjoystick.c (18761B)


      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 #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_events.h"
     37 #include "SDL_hints.h"
     38 #include "SDL_timer.h"
     39 #include "SDL_mutex.h"
     40 #include "SDL_joystick.h"
     41 #include "../SDL_sysjoystick.h"
     42 #include "../../thread/SDL_systhread.h"
     43 #include "../../core/windows/SDL_windows.h"
     44 #if !defined(__WINRT__)
     45 #include <dbt.h>
     46 #endif
     47 
     48 #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
     49 #include "SDL_windowsjoystick_c.h"
     50 #include "SDL_dinputjoystick_c.h"
     51 #include "SDL_xinputjoystick_c.h"
     52 #include "SDL_rawinputjoystick_c.h"
     53 
     54 #include "../../haptic/windows/SDL_dinputhaptic_c.h"    /* For haptic hot plugging */
     55 #include "../../haptic/windows/SDL_xinputhaptic_c.h"    /* For haptic hot plugging */
     56 
     57 
     58 #ifndef DEVICE_NOTIFY_WINDOW_HANDLE
     59 #define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
     60 #endif
     61 
     62 /* local variables */
     63 static SDL_bool s_bJoystickThread = SDL_FALSE;
     64 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
     65 static SDL_cond *s_condJoystickThread = NULL;
     66 static SDL_mutex *s_mutexJoyStickEnum = NULL;
     67 static SDL_Thread *s_joystickThread = NULL;
     68 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
     69 
     70 JoyStick_DeviceData *SYS_Joystick;    /* array to hold joystick ID values */
     71 
     72 #ifdef __WINRT__
     73 
     74 typedef struct
     75 {
     76     int unused;
     77 } SDL_DeviceNotificationData;
     78 
     79 static void
     80 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
     81 {
     82 }
     83 
     84 static int
     85 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
     86 {
     87     return 0;
     88 }
     89 
     90 static SDL_bool
     91 SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
     92 {
     93     return SDL_FALSE;
     94 }
     95 
     96 #else /* !__WINRT__ */
     97 
     98 typedef struct
     99 {
    100     HRESULT coinitialized;
    101     WNDCLASSEX wincl;
    102     HWND messageWindow;
    103     HDEVNOTIFY hNotify;
    104 } SDL_DeviceNotificationData;
    105 
    106 #define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200
    107 #define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201
    108 
    109 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */
    110 static LRESULT CALLBACK
    111 SDL_PrivateJoystickDetectProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    112 {
    113     switch (msg) {
    114     case WM_DEVICECHANGE:
    115         switch (wParam) {
    116         case DBT_DEVICEARRIVAL:
    117         case DBT_DEVICEREMOVECOMPLETE:
    118             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
    119                 /* notify 300ms and 2 seconds later to ensure all APIs have updated status */
    120                 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL);
    121                 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL);
    122             }
    123             break;
    124         }
    125         return 0;
    126     case WM_TIMER:
    127         if (wParam == IDT_SDL_DEVICE_CHANGE_TIMER_1 ||
    128             wParam == IDT_SDL_DEVICE_CHANGE_TIMER_2) {
    129             KillTimer(hwnd, wParam);
    130             s_bWindowsDeviceChanged = SDL_TRUE;
    131             return 0;
    132         }
    133         break;
    134     }
    135 
    136 #if SDL_JOYSTICK_RAWINPUT
    137     return CallWindowProc(RAWINPUT_WindowProc, hwnd, msg, wParam, lParam);
    138 #else
    139     return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
    140 #endif
    141 }
    142 
    143 static void
    144 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
    145 {
    146 #if SDL_JOYSTICK_RAWINPUT
    147     RAWINPUT_UnregisterNotifications();
    148 #endif
    149 
    150     if (data->hNotify)
    151         UnregisterDeviceNotification(data->hNotify);
    152 
    153     if (data->messageWindow)
    154         DestroyWindow(data->messageWindow);
    155 
    156     UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
    157 
    158     if (data->coinitialized == S_OK) {
    159         WIN_CoUninitialize();
    160     }
    161 }
    162 
    163 static int
    164 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
    165 {
    166     DEV_BROADCAST_DEVICEINTERFACE dbh;
    167     GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
    168 
    169     SDL_zerop(data);
    170 
    171     data->coinitialized = WIN_CoInitialize();
    172 
    173     data->wincl.hInstance = GetModuleHandle(NULL);
    174     data->wincl.lpszClassName = L"Message";
    175     data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc;      /* This function is called by windows */
    176     data->wincl.cbSize = sizeof (WNDCLASSEX);
    177 
    178     if (!RegisterClassEx(&data->wincl)) {
    179         WIN_SetError("Failed to create register class for joystick autodetect");
    180         SDL_CleanupDeviceNotification(data);
    181         return -1;
    182     }
    183 
    184     data->messageWindow = (HWND)CreateWindowEx(0,  L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    185     if (!data->messageWindow) {
    186         WIN_SetError("Failed to create message window for joystick autodetect");
    187         SDL_CleanupDeviceNotification(data);
    188         return -1;
    189     }
    190 
    191     SDL_zero(dbh);
    192     dbh.dbcc_size = sizeof(dbh);
    193     dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    194     dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
    195 
    196     data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
    197     if (!data->hNotify) {
    198         WIN_SetError("Failed to create notify device for joystick autodetect");
    199         SDL_CleanupDeviceNotification(data);
    200         return -1;
    201     }
    202 
    203 #if SDL_JOYSTICK_RAWINPUT
    204     RAWINPUT_RegisterNotifications(data->messageWindow);
    205 #endif
    206     return 0;
    207 }
    208 
    209 static SDL_bool
    210 SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
    211 {
    212     MSG msg;
    213     int lastret = 1;
    214 
    215     if (!data->messageWindow) {
    216         return SDL_FALSE; /* device notifications require a window */
    217     }
    218 
    219     SDL_UnlockMutex(mutex);
    220     while (lastret > 0 && s_bWindowsDeviceChanged == SDL_FALSE) {
    221         lastret = GetMessage(&msg, NULL, 0, 0); /* WM_QUIT causes return value of 0 */
    222         if (lastret > 0) {
    223             TranslateMessage(&msg);
    224             DispatchMessage(&msg);
    225         }
    226     }
    227     SDL_LockMutex(mutex);
    228     return (lastret != -1) ? SDL_TRUE : SDL_FALSE;
    229 }
    230 
    231 #endif /* __WINRT__ */
    232 
    233 static SDL_DeviceNotificationData s_notification_data;
    234 
    235 /* Function/thread to scan the system for joysticks. */
    236 static int
    237 SDL_JoystickThread(void *_data)
    238 {
    239 #if SDL_JOYSTICK_XINPUT
    240     SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT];
    241     SDL_zeroa(bOpenedXInputDevices);
    242 #endif
    243 
    244     if (SDL_CreateDeviceNotification(&s_notification_data) < 0) {
    245         return -1;
    246     }
    247 
    248     SDL_LockMutex(s_mutexJoyStickEnum);
    249     while (s_bJoystickThreadQuit == SDL_FALSE) {
    250         if (SDL_WaitForDeviceNotification(&s_notification_data, s_mutexJoyStickEnum) == SDL_FALSE) {
    251 #if SDL_JOYSTICK_XINPUT
    252             /* WM_DEVICECHANGE not working, poll for new XINPUT controllers */
    253             SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000);
    254             if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
    255                 /* scan for any change in XInput devices */
    256                 Uint8 userId;
    257                 for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
    258                     XINPUT_CAPABILITIES capabilities;
    259                     const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
    260                     const SDL_bool available = (result == ERROR_SUCCESS);
    261                     if (bOpenedXInputDevices[userId] != available) {
    262                         s_bWindowsDeviceChanged = SDL_TRUE;
    263                         bOpenedXInputDevices[userId] = available;
    264                     }
    265                 }
    266             }
    267 #else
    268             /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */
    269             break;
    270 #endif /* SDL_JOYSTICK_XINPUT */
    271         }
    272     }
    273     SDL_UnlockMutex(s_mutexJoyStickEnum);
    274 
    275     SDL_CleanupDeviceNotification(&s_notification_data);
    276 
    277     return 1;
    278 }
    279 
    280 /* spin up the thread to detect hotplug of devices */
    281 static int
    282 SDL_StartJoystickThread(void)
    283 {
    284     s_mutexJoyStickEnum = SDL_CreateMutex();
    285     if (!s_mutexJoyStickEnum) {
    286         return -1;
    287     }
    288 
    289     s_condJoystickThread = SDL_CreateCond();
    290     if (!s_condJoystickThread) {
    291         return -1;
    292     }
    293 
    294     s_bJoystickThreadQuit = SDL_FALSE;
    295     s_joystickThread = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
    296     if (!s_joystickThread) {
    297         return -1;
    298     }
    299     return 0;
    300 }
    301 
    302 static void
    303 SDL_StopJoystickThread(void)
    304 {
    305     if (!s_joystickThread) {
    306         return;
    307     }
    308 
    309     SDL_LockMutex(s_mutexJoyStickEnum);
    310     s_bJoystickThreadQuit = SDL_TRUE;
    311     SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
    312     SDL_UnlockMutex(s_mutexJoyStickEnum);
    313 #ifndef __WINRT__
    314     PostThreadMessage(SDL_GetThreadID(s_joystickThread), WM_QUIT, 0, 0);
    315 #endif
    316     SDL_WaitThread(s_joystickThread, NULL); /* wait for it to bugger off */
    317 
    318     SDL_DestroyCond(s_condJoystickThread);
    319     s_condJoystickThread = NULL;
    320 
    321     SDL_DestroyMutex(s_mutexJoyStickEnum);
    322     s_mutexJoyStickEnum = NULL;
    323 
    324     s_joystickThread = NULL;
    325 }
    326 
    327 void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
    328 {
    329     device->send_add_event = SDL_TRUE;
    330     device->nInstanceID = SDL_GetNextJoystickInstanceID();
    331     device->pNext = SYS_Joystick;
    332     SYS_Joystick = device;
    333 }
    334 
    335 static void WINDOWS_JoystickDetect(void);
    336 static void WINDOWS_JoystickQuit(void);
    337 
    338 /* Function to scan the system for joysticks.
    339  * Joystick 0 should be the system default joystick.
    340  * It should return 0, or -1 on an unrecoverable fatal error.
    341  */
    342 static int
    343 WINDOWS_JoystickInit(void)
    344 {
    345     if (SDL_DINPUT_JoystickInit() < 0) {
    346         WINDOWS_JoystickQuit();
    347         return -1;
    348     }
    349 
    350     if (SDL_XINPUT_JoystickInit() < 0) {
    351         WINDOWS_JoystickQuit();
    352         return -1;
    353     }
    354 
    355     s_bWindowsDeviceChanged = SDL_TRUE; /* force a scan of the system for joysticks this first time */
    356 
    357     WINDOWS_JoystickDetect();
    358 
    359     s_bJoystickThread = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_THREAD, SDL_FALSE);
    360     if (s_bJoystickThread) {
    361         if (SDL_StartJoystickThread() < 0) {
    362             return -1;
    363         }
    364     } else {
    365         if (SDL_CreateDeviceNotification(&s_notification_data) < 0) {
    366             return -1;
    367         }
    368     }
    369     return 0;
    370 }
    371 
    372 /* return the number of joysticks that are connected right now */
    373 static int
    374 WINDOWS_JoystickGetCount(void)
    375 {
    376     int nJoysticks = 0;
    377     JoyStick_DeviceData *device = SYS_Joystick;
    378     while (device) {
    379         nJoysticks++;
    380         device = device->pNext;
    381     }
    382 
    383     return nJoysticks;
    384 }
    385 
    386 /* detect any new joysticks being inserted into the system */
    387 static void
    388 WINDOWS_JoystickDetect(void)
    389 {
    390     int device_index = 0;
    391     JoyStick_DeviceData *pCurList = NULL;
    392 
    393     /* only enum the devices if the joystick thread told us something changed */
    394     if (!s_bWindowsDeviceChanged) {
    395         return;  /* thread hasn't signaled, nothing to do right now. */
    396     }
    397 
    398     if (s_mutexJoyStickEnum) {
    399         SDL_LockMutex(s_mutexJoyStickEnum);
    400     }
    401 
    402     s_bWindowsDeviceChanged = SDL_FALSE;
    403 
    404     pCurList = SYS_Joystick;
    405     SYS_Joystick = NULL;
    406 
    407     /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
    408     SDL_DINPUT_JoystickDetect(&pCurList);
    409 
    410     /* Look for XInput devices. Do this last, so they're first in the final list. */
    411     SDL_XINPUT_JoystickDetect(&pCurList);
    412 
    413     if (s_mutexJoyStickEnum) {
    414         SDL_UnlockMutex(s_mutexJoyStickEnum);
    415     }
    416 
    417     while (pCurList) {
    418         JoyStick_DeviceData *pListNext = NULL;
    419 
    420         if (pCurList->bXInputDevice) {
    421 #if SDL_HAPTIC_XINPUT
    422             SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId);
    423 #endif
    424         } else {
    425 #if SDL_HAPTIC_DINPUT
    426             SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice);
    427 #endif
    428         }
    429 
    430         SDL_PrivateJoystickRemoved(pCurList->nInstanceID);
    431 
    432         pListNext = pCurList->pNext;
    433         SDL_free(pCurList->joystickname);
    434         SDL_free(pCurList);
    435         pCurList = pListNext;
    436     }
    437 
    438     for (device_index = 0, pCurList = SYS_Joystick; pCurList; ++device_index, pCurList = pCurList->pNext) {
    439         if (pCurList->send_add_event) {
    440             if (pCurList->bXInputDevice) {
    441 #if SDL_HAPTIC_XINPUT
    442                 SDL_XINPUT_MaybeAddDevice(pCurList->XInputUserId);
    443 #endif
    444             } else {
    445 #if SDL_HAPTIC_DINPUT
    446                 SDL_DINPUT_MaybeAddDevice(&pCurList->dxdevice);
    447 #endif
    448             }
    449 
    450             SDL_PrivateJoystickAdded(pCurList->nInstanceID);
    451 
    452             pCurList->send_add_event = SDL_FALSE;
    453         }
    454     }
    455 }
    456 
    457 /* Function to get the device-dependent name of a joystick */
    458 static const char *
    459 WINDOWS_JoystickGetDeviceName(int device_index)
    460 {
    461     JoyStick_DeviceData *device = SYS_Joystick;
    462     int index;
    463 
    464     for (index = device_index; index > 0; index--)
    465         device = device->pNext;
    466 
    467     return device->joystickname;
    468 }
    469 
    470 static int
    471 WINDOWS_JoystickGetDevicePlayerIndex(int device_index)
    472 {
    473     JoyStick_DeviceData *device = SYS_Joystick;
    474     int index;
    475 
    476     for (index = device_index; index > 0; index--)
    477         device = device->pNext;
    478 
    479     return device->bXInputDevice ? (int)device->XInputUserId : -1;
    480 }
    481 
    482 static void
    483 WINDOWS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
    484 {
    485 }
    486 
    487 /* return the stable device guid for this device index */
    488 static SDL_JoystickGUID
    489 WINDOWS_JoystickGetDeviceGUID(int device_index)
    490 {
    491     JoyStick_DeviceData *device = SYS_Joystick;
    492     int index;
    493 
    494     for (index = device_index; index > 0; index--)
    495         device = device->pNext;
    496 
    497     return device->guid;
    498 }
    499 
    500 /* Function to perform the mapping between current device instance and this joysticks instance id */
    501 static SDL_JoystickID
    502 WINDOWS_JoystickGetDeviceInstanceID(int device_index)
    503 {
    504     JoyStick_DeviceData *device = SYS_Joystick;
    505     int index;
    506 
    507     for (index = device_index; index > 0; index--)
    508         device = device->pNext;
    509 
    510     return device->nInstanceID;
    511 }
    512 
    513 /* Function to open a joystick for use.
    514    The joystick to open is specified by the device index.
    515    This should fill the nbuttons and naxes fields of the joystick structure.
    516    It returns 0, or -1 if there is an error.
    517  */
    518 static int
    519 WINDOWS_JoystickOpen(SDL_Joystick * joystick, int device_index)
    520 {
    521     JoyStick_DeviceData *device = SYS_Joystick;
    522     int index;
    523 
    524     for (index = device_index; index > 0; index--)
    525         device = device->pNext;
    526 
    527     /* allocate memory for system specific hardware data */
    528     joystick->instance_id = device->nInstanceID;
    529     joystick->hwdata =
    530         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
    531     if (joystick->hwdata == NULL) {
    532         return SDL_OutOfMemory();
    533     }
    534     SDL_zerop(joystick->hwdata);
    535     joystick->hwdata->guid = device->guid;
    536 
    537     if (device->bXInputDevice) {
    538         return SDL_XINPUT_JoystickOpen(joystick, device);
    539     } else {
    540         return SDL_DINPUT_JoystickOpen(joystick, device);
    541     }
    542 }
    543 
    544 static int
    545 WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
    546 {
    547     if (joystick->hwdata->bXInputDevice) {
    548         return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble);
    549     } else {
    550         return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble);
    551     }
    552 }
    553 
    554 static int
    555 WINDOWS_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
    556 {
    557     return SDL_Unsupported();
    558 }
    559 
    560 static SDL_bool
    561 WINDOWS_JoystickHasLED(SDL_Joystick * joystick)
    562 {
    563     return SDL_FALSE;
    564 }
    565 
    566 static int
    567 WINDOWS_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue)
    568 {
    569     return SDL_Unsupported();
    570 }
    571 
    572 static int
    573 WINDOWS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
    574 {
    575     return SDL_Unsupported();
    576 }
    577 
    578 static void
    579 WINDOWS_JoystickUpdate(SDL_Joystick * joystick)
    580 {
    581     if (!joystick->hwdata) {
    582         return;
    583     }
    584 
    585     if (joystick->hwdata->bXInputDevice) {
    586         SDL_XINPUT_JoystickUpdate(joystick);
    587     } else {
    588         SDL_DINPUT_JoystickUpdate(joystick);
    589     }
    590 }
    591 
    592 /* Function to close a joystick after use */
    593 static void
    594 WINDOWS_JoystickClose(SDL_Joystick * joystick)
    595 {
    596     if (joystick->hwdata->bXInputDevice) {
    597         SDL_XINPUT_JoystickClose(joystick);
    598     } else {
    599         SDL_DINPUT_JoystickClose(joystick);
    600     }
    601 
    602     SDL_free(joystick->hwdata);
    603 }
    604 
    605 /* Function to perform any system-specific joystick related cleanup */
    606 static void
    607 WINDOWS_JoystickQuit(void)
    608 {
    609     JoyStick_DeviceData *device = SYS_Joystick;
    610 
    611     while (device) {
    612         JoyStick_DeviceData *device_next = device->pNext;
    613         SDL_free(device->joystickname);
    614         SDL_free(device);
    615         device = device_next;
    616     }
    617     SYS_Joystick = NULL;
    618 
    619     if (s_bJoystickThread) {
    620         SDL_StopJoystickThread();
    621     } else {
    622         SDL_CleanupDeviceNotification(&s_notification_data);
    623     }
    624 
    625     SDL_DINPUT_JoystickQuit();
    626     SDL_XINPUT_JoystickQuit();
    627 
    628     s_bWindowsDeviceChanged = SDL_FALSE;
    629 }
    630 
    631 static SDL_bool
    632 WINDOWS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
    633 {
    634     return SDL_FALSE;
    635 }
    636 
    637 SDL_JoystickDriver SDL_WINDOWS_JoystickDriver =
    638 {
    639     WINDOWS_JoystickInit,
    640     WINDOWS_JoystickGetCount,
    641     WINDOWS_JoystickDetect,
    642     WINDOWS_JoystickGetDeviceName,
    643     WINDOWS_JoystickGetDevicePlayerIndex,
    644     WINDOWS_JoystickSetDevicePlayerIndex,
    645     WINDOWS_JoystickGetDeviceGUID,
    646     WINDOWS_JoystickGetDeviceInstanceID,
    647     WINDOWS_JoystickOpen,
    648     WINDOWS_JoystickRumble,
    649     WINDOWS_JoystickRumbleTriggers,
    650     WINDOWS_JoystickHasLED,
    651     WINDOWS_JoystickSetLED,
    652     WINDOWS_JoystickSetSensorsEnabled,
    653     WINDOWS_JoystickUpdate,
    654     WINDOWS_JoystickClose,
    655     WINDOWS_JoystickQuit,
    656     WINDOWS_JoystickGetGamepadMapping
    657 };
    658 
    659 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
    660 
    661 /* vi: set ts=4 sw=4 expandtab: */