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