src/joystick/windows/SDL_windowsjoystick.c
author Ryan C. Gordon
Tue, 12 Apr 2016 16:45:10 -0400
changeset 10146 471eb08040ce
parent 9998 f67cf37e9cd4
child 10147 ddbdc9c1b92f
permissions -rw-r--r--
threads: Move SDL's own thread creation to a new internal API.

This allows us to set an explicit stack size (overriding the system default
and the global hint an app might have set), and remove all the macro salsa
for dealing with _beginthreadex and such, as internal threads always set those
to NULL anyhow.

I've taken some guesses on reasonable (and tiny!) stack sizes for our
internal threads, but some of these might turn out to be too small in
practice and need an increase. Most of them are simple functions, though.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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_timer.h"
    39 #include "SDL_mutex.h"
    40 #include "SDL_events.h"
    41 #include "SDL_hints.h"
    42 #include "SDL_joystick.h"
    43 #include "../SDL_sysjoystick.h"
    44 #include "../thread/SDL_systhread.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 */
    67 static SDL_bool s_bDeviceAdded = SDL_FALSE;
    68 static SDL_bool s_bDeviceRemoved = SDL_FALSE;
    69 static SDL_JoystickID s_nInstanceID = -1;
    70 static SDL_cond *s_condJoystickThread = NULL;
    71 static SDL_mutex *s_mutexJoyStickEnum = NULL;
    72 static SDL_Thread *s_threadJoystick = NULL;
    73 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
    74 
    75 JoyStick_DeviceData *SYS_Joystick;    /* array to hold joystick ID values */
    76 
    77 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
    78 
    79 #ifdef __WINRT__
    80 
    81 typedef struct
    82 {
    83     int unused;
    84 } SDL_DeviceNotificationData;
    85 
    86 static void
    87 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
    88 {
    89 }
    90 
    91 static int
    92 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
    93 {
    94     return 0;
    95 }
    96 
    97 static void
    98 SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
    99 {
   100 }
   101 
   102 #else /* !__WINRT__ */
   103 
   104 typedef 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 */
   114 static LRESULT CALLBACK
   115 SDL_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 
   137 static void
   138 SDL_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 
   153 static int
   154 SDL_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 
   195 static void
   196 SDL_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. */
   215 static int
   216 SDL_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 
   270 void 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  * Joystick 0 should be the system default joystick.
   282  * It should return 0, or -1 on an unrecoverable fatal error.
   283  */
   284 int
   285 SDL_SYS_JoystickInit(void)
   286 {
   287     if (SDL_DINPUT_JoystickInit() < 0) {
   288         SDL_SYS_JoystickQuit();
   289         return -1;
   290     }
   291 
   292     if (SDL_XINPUT_JoystickInit() < 0) {
   293         SDL_SYS_JoystickQuit();
   294         return -1;
   295     }
   296 
   297     s_mutexJoyStickEnum = SDL_CreateMutex();
   298     s_condJoystickThread = SDL_CreateCond();
   299     s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
   300 
   301     SDL_SYS_JoystickDetect();
   302 
   303     if (!s_threadJoystick) {
   304         /* spin up the thread to detect hotplug of devices */
   305         s_bJoystickThreadQuit = SDL_FALSE;
   306         s_threadJoystick = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
   307     }
   308     return SDL_SYS_NumJoysticks();
   309 }
   310 
   311 /* return the number of joysticks that are connected right now */
   312 int
   313 SDL_SYS_NumJoysticks()
   314 {
   315     int nJoysticks = 0;
   316     JoyStick_DeviceData *device = SYS_Joystick;
   317     while (device) {
   318         nJoysticks++;
   319         device = device->pNext;
   320     }
   321 
   322     return nJoysticks;
   323 }
   324 
   325 /* detect any new joysticks being inserted into the system */
   326 void
   327 SDL_SYS_JoystickDetect()
   328 {
   329     JoyStick_DeviceData *pCurList = NULL;
   330 #if !SDL_EVENTS_DISABLED
   331     SDL_Event event;
   332 #endif
   333 
   334     /* only enum the devices if the joystick thread told us something changed */
   335     if (!s_bDeviceAdded && !s_bDeviceRemoved) {
   336         return;  /* thread hasn't signaled, nothing to do right now. */
   337     }
   338 
   339     SDL_LockMutex(s_mutexJoyStickEnum);
   340 
   341     s_bDeviceAdded = SDL_FALSE;
   342     s_bDeviceRemoved = SDL_FALSE;
   343 
   344     pCurList = SYS_Joystick;
   345     SYS_Joystick = NULL;
   346 
   347     /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
   348     SDL_DINPUT_JoystickDetect(&pCurList);
   349 
   350     /* Look for XInput devices. Do this last, so they're first in the final list. */
   351     SDL_XINPUT_JoystickDetect(&pCurList);
   352 
   353     SDL_UnlockMutex(s_mutexJoyStickEnum);
   354 
   355     while (pCurList) {
   356         JoyStick_DeviceData *pListNext = NULL;
   357 
   358         if (pCurList->bXInputDevice) {
   359             SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId);
   360         } else {
   361             SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice);
   362         }
   363 
   364 #if !SDL_EVENTS_DISABLED
   365         SDL_zero(event);
   366         event.type = SDL_JOYDEVICEREMOVED;
   367 
   368         if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   369             event.jdevice.which = pCurList->nInstanceID;
   370             if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   371                 SDL_PushEvent(&event);
   372             }
   373         }
   374 #endif /* !SDL_EVENTS_DISABLED */
   375 
   376         pListNext = pCurList->pNext;
   377         SDL_free(pCurList->joystickname);
   378         SDL_free(pCurList);
   379         pCurList = pListNext;
   380     }
   381 
   382     if (s_bDeviceAdded) {
   383         JoyStick_DeviceData *pNewJoystick;
   384         int device_index = 0;
   385         s_bDeviceAdded = SDL_FALSE;
   386         pNewJoystick = SYS_Joystick;
   387         while (pNewJoystick) {
   388             if (pNewJoystick->send_add_event) {
   389                 if (pNewJoystick->bXInputDevice) {
   390                     SDL_XINPUT_MaybeAddDevice(pNewJoystick->XInputUserId);
   391                 } else {
   392                     SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
   393                 }
   394 
   395 #if !SDL_EVENTS_DISABLED
   396                 SDL_zero(event);
   397                 event.type = SDL_JOYDEVICEADDED;
   398 
   399                 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   400                     event.jdevice.which = device_index;
   401                     if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   402                         SDL_PushEvent(&event);
   403                     }
   404                 }
   405 #endif /* !SDL_EVENTS_DISABLED */
   406                 pNewJoystick->send_add_event = SDL_FALSE;
   407             }
   408             device_index++;
   409             pNewJoystick = pNewJoystick->pNext;
   410         }
   411     }
   412 }
   413 
   414 /* Function to get the device-dependent name of a joystick */
   415 const char *
   416 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   417 {
   418     JoyStick_DeviceData *device = SYS_Joystick;
   419 
   420     for (; device_index > 0; device_index--)
   421         device = device->pNext;
   422 
   423     return device->joystickname;
   424 }
   425 
   426 /* Function to perform the mapping between current device instance and this joysticks instance id */
   427 SDL_JoystickID
   428 SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   429 {
   430     JoyStick_DeviceData *device = SYS_Joystick;
   431     int index;
   432 
   433     for (index = device_index; index > 0; index--)
   434         device = device->pNext;
   435 
   436     return device->nInstanceID;
   437 }
   438 
   439 /* Function to open a joystick for use.
   440    The joystick to open is specified by the device index.
   441    This should fill the nbuttons and naxes fields of the joystick structure.
   442    It returns 0, or -1 if there is an error.
   443  */
   444 int
   445 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   446 {
   447     JoyStick_DeviceData *joystickdevice = SYS_Joystick;
   448 
   449     for (; device_index > 0; device_index--)
   450         joystickdevice = joystickdevice->pNext;
   451 
   452     /* allocate memory for system specific hardware data */
   453     joystick->instance_id = joystickdevice->nInstanceID;
   454     joystick->hwdata =
   455         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
   456     if (joystick->hwdata == NULL) {
   457         return SDL_OutOfMemory();
   458     }
   459     SDL_zerop(joystick->hwdata);
   460     joystick->hwdata->guid = joystickdevice->guid;
   461 
   462     if (joystickdevice->bXInputDevice) {
   463         return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
   464     } else {
   465         return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
   466     }
   467 }
   468 
   469 /* return true if this joystick is plugged in right now */
   470 SDL_bool 
   471 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
   472 {
   473     return joystick->hwdata && !joystick->hwdata->removed;
   474 }
   475 
   476 void
   477 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   478 {
   479     if (!joystick->hwdata || joystick->hwdata->removed) {
   480         return;
   481     }
   482 
   483     if (joystick->hwdata->bXInputDevice) {
   484         SDL_XINPUT_JoystickUpdate(joystick);
   485     } else {
   486         SDL_DINPUT_JoystickUpdate(joystick);
   487     }
   488 
   489     if (joystick->hwdata->removed) {
   490         joystick->force_recentering = SDL_TRUE;
   491     }
   492 }
   493 
   494 /* Function to close a joystick after use */
   495 void
   496 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   497 {
   498     if (joystick->hwdata->bXInputDevice) {
   499         SDL_XINPUT_JoystickClose(joystick);
   500     } else {
   501         SDL_DINPUT_JoystickClose(joystick);
   502     }
   503 
   504     SDL_free(joystick->hwdata);
   505 }
   506 
   507 /* Function to perform any system-specific joystick related cleanup */
   508 void
   509 SDL_SYS_JoystickQuit(void)
   510 {
   511     JoyStick_DeviceData *device = SYS_Joystick;
   512 
   513     while (device) {
   514         JoyStick_DeviceData *device_next = device->pNext;
   515         SDL_free(device->joystickname);
   516         SDL_free(device);
   517         device = device_next;
   518     }
   519     SYS_Joystick = NULL;
   520 
   521     if (s_threadJoystick) {
   522         SDL_LockMutex(s_mutexJoyStickEnum);
   523         s_bJoystickThreadQuit = SDL_TRUE;
   524         SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
   525         SDL_UnlockMutex(s_mutexJoyStickEnum);
   526         SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
   527 
   528         SDL_DestroyMutex(s_mutexJoyStickEnum);
   529         SDL_DestroyCond(s_condJoystickThread);
   530         s_condJoystickThread= NULL;
   531         s_mutexJoyStickEnum = NULL;
   532         s_threadJoystick = NULL;
   533     }
   534 
   535     SDL_DINPUT_JoystickQuit();
   536     SDL_XINPUT_JoystickQuit();
   537 }
   538 
   539 /* return the stable device guid for this device index */
   540 SDL_JoystickGUID
   541 SDL_SYS_JoystickGetDeviceGUID(int device_index)
   542 {
   543     JoyStick_DeviceData *device = SYS_Joystick;
   544     int index;
   545 
   546     for (index = device_index; index > 0; index--)
   547         device = device->pNext;
   548 
   549     return device->guid;
   550 }
   551 
   552 SDL_JoystickGUID
   553 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   554 {
   555     return joystick->hwdata->guid;
   556 }
   557 
   558 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
   559 
   560 /* vi: set ts=4 sw=4 expandtab: */