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