src/joystick/windows/SDL_windowsjoystick.c
author Philipp Wiesemann
Fri, 16 Jan 2015 23:07:10 +0100
changeset 9311 a4b031e28de8
parent 9166 e5a2e31d4bee
child 9380 07b7c1005a23
permissions -rw-r--r--
Fixed wrong documentation in joystick implementation source.
     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  * 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 index field of the joystick.
   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->closed = SDL_FALSE;
   464     joystick->hwdata =
   465         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
   466     if (joystick->hwdata == NULL) {
   467         return SDL_OutOfMemory();
   468     }
   469     SDL_zerop(joystick->hwdata);
   470     joystick->hwdata->guid = joystickdevice->guid;
   471 
   472     if (joystickdevice->bXInputDevice) {
   473         return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
   474     } else {
   475         return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
   476     }
   477 }
   478 
   479 /* return true if this joystick is plugged in right now */
   480 SDL_bool 
   481 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
   482 {
   483     return !joystick->closed && !joystick->hwdata->removed;
   484 }
   485 
   486 void
   487 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   488 {
   489     if (joystick->closed || !joystick->hwdata) {
   490         return;
   491     }
   492 
   493     if (joystick->hwdata->bXInputDevice) {
   494         SDL_XINPUT_JoystickUpdate(joystick);
   495     } else {
   496         SDL_DINPUT_JoystickUpdate(joystick);
   497     }
   498 
   499     if (joystick->hwdata->removed) {
   500         joystick->closed = SDL_TRUE;
   501         joystick->uncentered = SDL_TRUE;
   502     }
   503 }
   504 
   505 /* Function to close a joystick after use */
   506 void
   507 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   508 {
   509     if (joystick->hwdata->bXInputDevice) {
   510         SDL_XINPUT_JoystickClose(joystick);
   511     } else {
   512         SDL_DINPUT_JoystickClose(joystick);
   513     }
   514 
   515     /* free system specific hardware data */
   516     SDL_free(joystick->hwdata);
   517 
   518     joystick->closed = SDL_TRUE;
   519 }
   520 
   521 /* Function to perform any system-specific joystick related cleanup */
   522 void
   523 SDL_SYS_JoystickQuit(void)
   524 {
   525     JoyStick_DeviceData *device = SYS_Joystick;
   526 
   527     while (device) {
   528         JoyStick_DeviceData *device_next = device->pNext;
   529         SDL_free(device->joystickname);
   530         SDL_free(device);
   531         device = device_next;
   532     }
   533     SYS_Joystick = NULL;
   534 
   535     if (s_threadJoystick) {
   536         SDL_LockMutex(s_mutexJoyStickEnum);
   537         s_bJoystickThreadQuit = SDL_TRUE;
   538         SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
   539         SDL_UnlockMutex(s_mutexJoyStickEnum);
   540         SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
   541 
   542         SDL_DestroyMutex(s_mutexJoyStickEnum);
   543         SDL_DestroyCond(s_condJoystickThread);
   544         s_condJoystickThread= NULL;
   545         s_mutexJoyStickEnum = NULL;
   546         s_threadJoystick = NULL;
   547     }
   548 
   549     SDL_DINPUT_JoystickQuit();
   550     SDL_XINPUT_JoystickQuit();
   551 }
   552 
   553 /* return the stable device guid for this device index */
   554 SDL_JoystickGUID
   555 SDL_SYS_JoystickGetDeviceGUID(int device_index)
   556 {
   557     JoyStick_DeviceData *device = SYS_Joystick;
   558     int index;
   559 
   560     for (index = device_index; index > 0; index--)
   561         device = device->pNext;
   562 
   563     return device->guid;
   564 }
   565 
   566 SDL_JoystickGUID
   567 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   568 {
   569     return joystick->hwdata->guid;
   570 }
   571 
   572 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
   573 
   574 /* vi: set ts=4 sw=4 expandtab: */