• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
SDL_CleanupDeviceNotification(SDL_DeviceNotificationData * data)84 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
85 {
86 }
87 
88 static int
SDL_CreateDeviceNotification(SDL_DeviceNotificationData * data)89 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
90 {
91     return 0;
92 }
93 
94 static void
SDL_CheckDeviceNotification(SDL_DeviceNotificationData * data)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
SDL_PrivateJoystickDetectProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)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
SDL_CleanupDeviceNotification(SDL_DeviceNotificationData * data)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
SDL_CreateDeviceNotification(SDL_DeviceNotificationData * data)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
SDL_CheckDeviceNotification(SDL_DeviceNotificationData * data)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
SDL_JoystickThread(void * _data)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 
SDL_SYS_AddJoystickDevice(JoyStick_DeviceData * device)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
SDL_SYS_JoystickInit(void)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
SDL_SYS_NumJoysticks()310 SDL_SYS_NumJoysticks()
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
SDL_SYS_JoystickDetect()324 SDL_SYS_JoystickDetect()
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 *
SDL_SYS_JoystickNameForDeviceIndex(int device_index)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
SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)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
SDL_SYS_JoystickOpen(SDL_Joystick * joystick,int device_index)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
SDL_SYS_JoystickAttached(SDL_Joystick * joystick)446 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
447 {
448     return joystick->hwdata && !joystick->hwdata->removed;
449 }
450 
451 void
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)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
SDL_SYS_JoystickClose(SDL_Joystick * joystick)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
SDL_SYS_JoystickQuit(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
SDL_SYS_JoystickGetDeviceGUID(int device_index)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
SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)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: */
539