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