src/joystick/windows/SDL_windowsjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 26 May 2015 06:27:46 -0700
changeset 9619 b94b6d0bff0f
parent 9433 bd062398b648
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Updated the copyright year to 2015
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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  * 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         s_bJoystickThreadQuit = SDL_FALSE;
   305         /* spin up the thread to detect hotplug of devices */
   306 #if defined(__WIN32__) && !defined(HAVE_LIBC)
   307 #undef SDL_CreateThread
   308 #if SDL_DYNAMIC_API
   309         s_threadJoystick= SDL_CreateThread_REAL(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL);
   310 #else
   311         s_threadJoystick= SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL);
   312 #endif
   313 #else
   314         s_threadJoystick = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL);
   315 #endif
   316     }
   317     return SDL_SYS_NumJoysticks();
   318 }
   319 
   320 /* return the number of joysticks that are connected right now */
   321 int
   322 SDL_SYS_NumJoysticks()
   323 {
   324     int nJoysticks = 0;
   325     JoyStick_DeviceData *device = SYS_Joystick;
   326     while (device) {
   327         nJoysticks++;
   328         device = device->pNext;
   329     }
   330 
   331     return nJoysticks;
   332 }
   333 
   334 /* detect any new joysticks being inserted into the system */
   335 void
   336 SDL_SYS_JoystickDetect()
   337 {
   338     JoyStick_DeviceData *pCurList = NULL;
   339 #if !SDL_EVENTS_DISABLED
   340     SDL_Event event;
   341 #endif
   342 
   343     /* only enum the devices if the joystick thread told us something changed */
   344     if (!s_bDeviceAdded && !s_bDeviceRemoved) {
   345         return;  /* thread hasn't signaled, nothing to do right now. */
   346     }
   347 
   348     SDL_LockMutex(s_mutexJoyStickEnum);
   349 
   350     s_bDeviceAdded = SDL_FALSE;
   351     s_bDeviceRemoved = SDL_FALSE;
   352 
   353     pCurList = SYS_Joystick;
   354     SYS_Joystick = NULL;
   355 
   356     /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
   357     SDL_DINPUT_JoystickDetect(&pCurList);
   358 
   359     /* Look for XInput devices. Do this last, so they're first in the final list. */
   360     SDL_XINPUT_JoystickDetect(&pCurList);
   361 
   362     SDL_UnlockMutex(s_mutexJoyStickEnum);
   363 
   364     while (pCurList) {
   365         JoyStick_DeviceData *pListNext = NULL;
   366 
   367         if (pCurList->bXInputDevice) {
   368             SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId);
   369         } else {
   370             SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice);
   371         }
   372 
   373 #if !SDL_EVENTS_DISABLED
   374         SDL_zero(event);
   375         event.type = SDL_JOYDEVICEREMOVED;
   376 
   377         if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   378             event.jdevice.which = pCurList->nInstanceID;
   379             if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   380                 SDL_PushEvent(&event);
   381             }
   382         }
   383 #endif /* !SDL_EVENTS_DISABLED */
   384 
   385         pListNext = pCurList->pNext;
   386         SDL_free(pCurList->joystickname);
   387         SDL_free(pCurList);
   388         pCurList = pListNext;
   389     }
   390 
   391     if (s_bDeviceAdded) {
   392         JoyStick_DeviceData *pNewJoystick;
   393         int device_index = 0;
   394         s_bDeviceAdded = SDL_FALSE;
   395         pNewJoystick = SYS_Joystick;
   396         while (pNewJoystick) {
   397             if (pNewJoystick->send_add_event) {
   398                 if (pNewJoystick->bXInputDevice) {
   399                     SDL_XINPUT_MaybeAddDevice(pNewJoystick->XInputUserId);
   400                 } else {
   401                     SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
   402                 }
   403 
   404 #if !SDL_EVENTS_DISABLED
   405                 SDL_zero(event);
   406                 event.type = SDL_JOYDEVICEADDED;
   407 
   408                 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   409                     event.jdevice.which = device_index;
   410                     if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   411                         SDL_PushEvent(&event);
   412                     }
   413                 }
   414 #endif /* !SDL_EVENTS_DISABLED */
   415                 pNewJoystick->send_add_event = SDL_FALSE;
   416             }
   417             device_index++;
   418             pNewJoystick = pNewJoystick->pNext;
   419         }
   420     }
   421 }
   422 
   423 /* Function to get the device-dependent name of a joystick */
   424 const char *
   425 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   426 {
   427     JoyStick_DeviceData *device = SYS_Joystick;
   428 
   429     for (; device_index > 0; device_index--)
   430         device = device->pNext;
   431 
   432     return device->joystickname;
   433 }
   434 
   435 /* Function to perform the mapping between current device instance and this joysticks instance id */
   436 SDL_JoystickID
   437 SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   438 {
   439     JoyStick_DeviceData *device = SYS_Joystick;
   440     int index;
   441 
   442     for (index = device_index; index > 0; index--)
   443         device = device->pNext;
   444 
   445     return device->nInstanceID;
   446 }
   447 
   448 /* Function to open a joystick for use.
   449    The joystick to open is specified by the device index.
   450    This should fill the nbuttons and naxes fields of the joystick structure.
   451    It returns 0, or -1 if there is an error.
   452  */
   453 int
   454 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   455 {
   456     JoyStick_DeviceData *joystickdevice = SYS_Joystick;
   457 
   458     for (; device_index > 0; device_index--)
   459         joystickdevice = joystickdevice->pNext;
   460 
   461     /* allocate memory for system specific hardware data */
   462     joystick->instance_id = joystickdevice->nInstanceID;
   463     joystick->hwdata =
   464         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
   465     if (joystick->hwdata == NULL) {
   466         return SDL_OutOfMemory();
   467     }
   468     SDL_zerop(joystick->hwdata);
   469     joystick->hwdata->guid = joystickdevice->guid;
   470 
   471     if (joystickdevice->bXInputDevice) {
   472         return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
   473     } else {
   474         return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
   475     }
   476 }
   477 
   478 /* return true if this joystick is plugged in right now */
   479 SDL_bool 
   480 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
   481 {
   482     return joystick->hwdata && !joystick->hwdata->removed;
   483 }
   484 
   485 void
   486 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   487 {
   488     if (!joystick->hwdata || joystick->hwdata->removed) {
   489         return;
   490     }
   491 
   492     if (joystick->hwdata->bXInputDevice) {
   493         SDL_XINPUT_JoystickUpdate(joystick);
   494     } else {
   495         SDL_DINPUT_JoystickUpdate(joystick);
   496     }
   497 
   498     if (joystick->hwdata->removed) {
   499         joystick->force_recentering = SDL_TRUE;
   500     }
   501 }
   502 
   503 /* Function to close a joystick after use */
   504 void
   505 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   506 {
   507     if (joystick->hwdata->bXInputDevice) {
   508         SDL_XINPUT_JoystickClose(joystick);
   509     } else {
   510         SDL_DINPUT_JoystickClose(joystick);
   511     }
   512 
   513     SDL_free(joystick->hwdata);
   514 }
   515 
   516 /* Function to perform any system-specific joystick related cleanup */
   517 void
   518 SDL_SYS_JoystickQuit(void)
   519 {
   520     JoyStick_DeviceData *device = SYS_Joystick;
   521 
   522     while (device) {
   523         JoyStick_DeviceData *device_next = device->pNext;
   524         SDL_free(device->joystickname);
   525         SDL_free(device);
   526         device = device_next;
   527     }
   528     SYS_Joystick = NULL;
   529 
   530     if (s_threadJoystick) {
   531         SDL_LockMutex(s_mutexJoyStickEnum);
   532         s_bJoystickThreadQuit = SDL_TRUE;
   533         SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
   534         SDL_UnlockMutex(s_mutexJoyStickEnum);
   535         SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
   536 
   537         SDL_DestroyMutex(s_mutexJoyStickEnum);
   538         SDL_DestroyCond(s_condJoystickThread);
   539         s_condJoystickThread= NULL;
   540         s_mutexJoyStickEnum = NULL;
   541         s_threadJoystick = NULL;
   542     }
   543 
   544     SDL_DINPUT_JoystickQuit();
   545     SDL_XINPUT_JoystickQuit();
   546 }
   547 
   548 /* return the stable device guid for this device index */
   549 SDL_JoystickGUID
   550 SDL_SYS_JoystickGetDeviceGUID(int device_index)
   551 {
   552     JoyStick_DeviceData *device = SYS_Joystick;
   553     int index;
   554 
   555     for (index = device_index; index > 0; index--)
   556         device = device->pNext;
   557 
   558     return device->guid;
   559 }
   560 
   561 SDL_JoystickGUID
   562 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   563 {
   564     return joystick->hwdata->guid;
   565 }
   566 
   567 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
   568 
   569 /* vi: set ts=4 sw=4 expandtab: */