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(¬ification_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(¬ification_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(¬ification_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