src/joystick/windows/SDL_windowsjoystick.c
author David Ludwig
Wed, 15 Oct 2014 15:50:35 -0400
changeset 9166 e5a2e31d4bee
parent 8988 1deb2998dbae
child 9311 a4b031e28de8
permissions -rw-r--r--
WinRT build fix
     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 */
    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  * 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  */
   285 int
   286 SDL_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 */
   322 int
   323 SDL_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 */
   336 void
   337 SDL_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 */
   425 const char *
   426 SDL_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 */
   437 SDL_JoystickID
   438 SDL_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  */
   454 int
   455 SDL_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 */
   481 SDL_bool 
   482 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
   483 {
   484     return !joystick->closed && !joystick->hwdata->removed;
   485 }
   486 
   487 void
   488 SDL_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 */
   507 void
   508 SDL_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 */
   523 void
   524 SDL_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 */
   555 SDL_JoystickGUID
   556 SDL_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 
   567 SDL_JoystickGUID
   568 SDL_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: */