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