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