1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2021 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_events.h"
37 #include "SDL_hints.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 #include "SDL_rawinputjoystick_c.h"
53 
54 #include "../../haptic/windows/SDL_dinputhaptic_c.h"    /* For haptic hot plugging */
55 #include "../../haptic/windows/SDL_xinputhaptic_c.h"    /* For haptic hot plugging */
56 
57 
58 #ifndef DEVICE_NOTIFY_WINDOW_HANDLE
59 #define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
60 #endif
61 
62 /* CM_Register_Notification definitions */
63 
64 #define CR_SUCCESS (0x00000000)
65 
66 DECLARE_HANDLE(HCMNOTIFICATION);
67 typedef HCMNOTIFICATION* PHCMNOTIFICATION;
68 
69 typedef enum _CM_NOTIFY_FILTER_TYPE {
70     CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE = 0,
71     CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE,
72     CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE,
73     CM_NOTIFY_FILTER_TYPE_MAX
74 } CM_NOTIFY_FILTER_TYPE, * PCM_NOTIFY_FILTER_TYPE;
75 
76 typedef struct _CM_NOTIFY_FILTER {
77     DWORD cbSize;
78     DWORD Flags;
79     CM_NOTIFY_FILTER_TYPE FilterType;
80     DWORD Reserved;
81     union {
82         struct {
83             GUID ClassGuid;
84         } DeviceInterface;
85         struct {
86             HANDLE  hTarget;
87         } DeviceHandle;
88         struct {
89             WCHAR InstanceId[200];
90         } DeviceInstance;
91     } u;
92 } CM_NOTIFY_FILTER, * PCM_NOTIFY_FILTER;
93 
94 typedef enum _CM_NOTIFY_ACTION {
95     CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL = 0,
96     CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL,
97     CM_NOTIFY_ACTION_DEVICEQUERYREMOVE,
98     CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED,
99     CM_NOTIFY_ACTION_DEVICEREMOVEPENDING,
100     CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE,
101     CM_NOTIFY_ACTION_DEVICECUSTOMEVENT,
102     CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED,
103     CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED,
104     CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
105     CM_NOTIFY_ACTION_MAX
106 } CM_NOTIFY_ACTION, * PCM_NOTIFY_ACTION;
107 
108 typedef struct _CM_NOTIFY_EVENT_DATA {
109     CM_NOTIFY_FILTER_TYPE FilterType;
110     DWORD Reserved;
111     union {
112         struct {
113             GUID ClassGuid;
114             WCHAR SymbolicLink[ANYSIZE_ARRAY];
115         } DeviceInterface;
116         struct {
117             GUID EventGuid;
118             LONG NameOffset;
119             DWORD DataSize;
120             BYTE Data[ANYSIZE_ARRAY];
121         } DeviceHandle;
122         struct {
123             WCHAR InstanceId[ANYSIZE_ARRAY];
124         } DeviceInstance;
125     } u;
126 } CM_NOTIFY_EVENT_DATA, * PCM_NOTIFY_EVENT_DATA;
127 
128 typedef DWORD (CALLBACK *PCM_NOTIFY_CALLBACK)(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize);
129 
130 typedef DWORD (WINAPI *CM_Register_NotificationFunc)(PCM_NOTIFY_FILTER pFilter, PVOID pContext, PCM_NOTIFY_CALLBACK pCallback, PHCMNOTIFICATION pNotifyContext);
131 typedef DWORD (WINAPI *CM_Unregister_NotificationFunc)(HCMNOTIFICATION NotifyContext);
132 
133 /* local variables */
134 static SDL_bool s_bJoystickThread = SDL_FALSE;
135 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
136 static SDL_cond *s_condJoystickThread = NULL;
137 static SDL_mutex *s_mutexJoyStickEnum = NULL;
138 static SDL_Thread *s_joystickThread = NULL;
139 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
140 static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
141 
142 JoyStick_DeviceData *SYS_Joystick;    /* array to hold joystick ID values */
143 
144 #ifndef __WINRT__
145 static HMODULE cfgmgr32_lib_handle;
146 static CM_Register_NotificationFunc CM_Register_Notification;
147 static CM_Unregister_NotificationFunc CM_Unregister_Notification;
148 static HCMNOTIFICATION s_DeviceNotificationFuncHandle;
149 
150 static DWORD CALLBACK
SDL_DeviceNotificationFunc(HCMNOTIFICATION hNotify,PVOID context,CM_NOTIFY_ACTION action,PCM_NOTIFY_EVENT_DATA eventData,DWORD event_data_size)151 SDL_DeviceNotificationFunc(HCMNOTIFICATION hNotify, PVOID context, CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA eventData, DWORD event_data_size)
152 {
153     if (action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
154         action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) {
155         s_bWindowsDeviceChanged = SDL_TRUE;
156     }
157     return ERROR_SUCCESS;
158 }
159 
160 static void
SDL_CleanupDeviceNotificationFunc(void)161 SDL_CleanupDeviceNotificationFunc(void)
162 {
163     if (cfgmgr32_lib_handle) {
164         if (s_DeviceNotificationFuncHandle) {
165             CM_Unregister_Notification(s_DeviceNotificationFuncHandle);
166             s_DeviceNotificationFuncHandle = NULL;
167         }
168 
169         FreeLibrary(cfgmgr32_lib_handle);
170         cfgmgr32_lib_handle = NULL;
171     }
172 }
173 
174 static SDL_bool
SDL_CreateDeviceNotificationFunc(void)175 SDL_CreateDeviceNotificationFunc(void)
176 {
177     cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
178     if (cfgmgr32_lib_handle) {
179         CM_Register_Notification = (CM_Register_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Register_Notification");
180         CM_Unregister_Notification = (CM_Unregister_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Unregister_Notification");
181         if (CM_Register_Notification && CM_Unregister_Notification) {
182             CM_NOTIFY_FILTER notify_filter;
183 
184             SDL_zero(notify_filter);
185             notify_filter.cbSize = sizeof(notify_filter);
186             notify_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
187             notify_filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_HID;
188             if (CM_Register_Notification(&notify_filter, NULL, SDL_DeviceNotificationFunc, &s_DeviceNotificationFuncHandle) == CR_SUCCESS) {
189                 return SDL_TRUE;
190             }
191         }
192     }
193 
194     SDL_CleanupDeviceNotificationFunc();
195     return SDL_FALSE;
196 }
197 
198 typedef struct
199 {
200     HRESULT coinitialized;
201     WNDCLASSEX wincl;
202     HWND messageWindow;
203     HDEVNOTIFY hNotify;
204 } SDL_DeviceNotificationData;
205 
206 #define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200
207 #define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201
208 
209 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */
210 static LRESULT CALLBACK
SDL_PrivateJoystickDetectProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)211 SDL_PrivateJoystickDetectProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
212 {
213     switch (msg) {
214     case WM_DEVICECHANGE:
215         switch (wParam) {
216         case DBT_DEVICEARRIVAL:
217         case DBT_DEVICEREMOVECOMPLETE:
218             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
219                 /* notify 300ms and 2 seconds later to ensure all APIs have updated status */
220                 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL);
221                 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL);
222             }
223             break;
224         }
225         return 0;
226     case WM_TIMER:
227         if (wParam == IDT_SDL_DEVICE_CHANGE_TIMER_1 ||
228             wParam == IDT_SDL_DEVICE_CHANGE_TIMER_2) {
229             KillTimer(hwnd, wParam);
230             s_bWindowsDeviceChanged = SDL_TRUE;
231             return 0;
232         }
233         break;
234     }
235 
236 #if SDL_JOYSTICK_RAWINPUT
237     return CallWindowProc(RAWINPUT_WindowProc, hwnd, msg, wParam, lParam);
238 #else
239     return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
240 #endif
241 }
242 
243 static void
SDL_CleanupDeviceNotification(SDL_DeviceNotificationData * data)244 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
245 {
246 #if SDL_JOYSTICK_RAWINPUT
247     RAWINPUT_UnregisterNotifications();
248 #endif
249 
250     if (data->hNotify)
251         UnregisterDeviceNotification(data->hNotify);
252 
253     if (data->messageWindow)
254         DestroyWindow(data->messageWindow);
255 
256     UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
257 
258     if (data->coinitialized == S_OK) {
259         WIN_CoUninitialize();
260     }
261 }
262 
263 static int
SDL_CreateDeviceNotification(SDL_DeviceNotificationData * data)264 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
265 {
266     DEV_BROADCAST_DEVICEINTERFACE dbh;
267 
268     SDL_zerop(data);
269 
270     data->coinitialized = WIN_CoInitialize();
271 
272     data->wincl.hInstance = GetModuleHandle(NULL);
273     data->wincl.lpszClassName = TEXT("Message");
274     data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc;      /* This function is called by windows */
275     data->wincl.cbSize = sizeof (WNDCLASSEX);
276 
277     if (!RegisterClassEx(&data->wincl)) {
278         WIN_SetError("Failed to create register class for joystick autodetect");
279         SDL_CleanupDeviceNotification(data);
280         return -1;
281     }
282 
283     data->messageWindow = (HWND)CreateWindowEx(0,  TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
284     if (!data->messageWindow) {
285         WIN_SetError("Failed to create message window for joystick autodetect");
286         SDL_CleanupDeviceNotification(data);
287         return -1;
288     }
289 
290     SDL_zero(dbh);
291     dbh.dbcc_size = sizeof(dbh);
292     dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
293     dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
294 
295     data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
296     if (!data->hNotify) {
297         WIN_SetError("Failed to create notify device for joystick autodetect");
298         SDL_CleanupDeviceNotification(data);
299         return -1;
300     }
301 
302 #if SDL_JOYSTICK_RAWINPUT
303     RAWINPUT_RegisterNotifications(data->messageWindow);
304 #endif
305     return 0;
306 }
307 
308 static SDL_bool
SDL_WaitForDeviceNotification(SDL_DeviceNotificationData * data,SDL_mutex * mutex)309 SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
310 {
311     MSG msg;
312     int lastret = 1;
313 
314     if (!data->messageWindow) {
315         return SDL_FALSE; /* device notifications require a window */
316     }
317 
318     SDL_UnlockMutex(mutex);
319     while (lastret > 0 && s_bWindowsDeviceChanged == SDL_FALSE) {
320         lastret = GetMessage(&msg, NULL, 0, 0); /* WM_QUIT causes return value of 0 */
321         if (lastret > 0) {
322             TranslateMessage(&msg);
323             DispatchMessage(&msg);
324         }
325     }
326     SDL_LockMutex(mutex);
327     return (lastret != -1) ? SDL_TRUE : SDL_FALSE;
328 }
329 
330 static SDL_DeviceNotificationData s_notification_data;
331 
332 /* Function/thread to scan the system for joysticks. */
333 static int
SDL_JoystickThread(void * _data)334 SDL_JoystickThread(void *_data)
335 {
336 #if SDL_JOYSTICK_XINPUT
337     SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT];
338     SDL_zeroa(bOpenedXInputDevices);
339 #endif
340 
341     if (SDL_CreateDeviceNotification(&s_notification_data) < 0) {
342         return -1;
343     }
344 
345     SDL_LockMutex(s_mutexJoyStickEnum);
346     while (s_bJoystickThreadQuit == SDL_FALSE) {
347         if (SDL_WaitForDeviceNotification(&s_notification_data, s_mutexJoyStickEnum) == SDL_FALSE) {
348 #if SDL_JOYSTICK_XINPUT
349             /* WM_DEVICECHANGE not working, poll for new XINPUT controllers */
350             SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000);
351             if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
352                 /* scan for any change in XInput devices */
353                 Uint8 userId;
354                 for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
355                     XINPUT_CAPABILITIES capabilities;
356                     const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
357                     const SDL_bool available = (result == ERROR_SUCCESS);
358                     if (bOpenedXInputDevices[userId] != available) {
359                         s_bWindowsDeviceChanged = SDL_TRUE;
360                         bOpenedXInputDevices[userId] = available;
361                     }
362                 }
363             }
364 #else
365             /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */
366             break;
367 #endif /* SDL_JOYSTICK_XINPUT */
368         }
369     }
370     SDL_UnlockMutex(s_mutexJoyStickEnum);
371 
372     SDL_CleanupDeviceNotification(&s_notification_data);
373 
374     return 1;
375 }
376 
377 /* spin up the thread to detect hotplug of devices */
378 static int
SDL_StartJoystickThread(void)379 SDL_StartJoystickThread(void)
380 {
381     s_mutexJoyStickEnum = SDL_CreateMutex();
382     if (!s_mutexJoyStickEnum) {
383         return -1;
384     }
385 
386     s_condJoystickThread = SDL_CreateCond();
387     if (!s_condJoystickThread) {
388         return -1;
389     }
390 
391     s_bJoystickThreadQuit = SDL_FALSE;
392     s_joystickThread = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
393     if (!s_joystickThread) {
394         return -1;
395     }
396     return 0;
397 }
398 
399 static void
SDL_StopJoystickThread(void)400 SDL_StopJoystickThread(void)
401 {
402     if (!s_joystickThread) {
403         return;
404     }
405 
406     SDL_LockMutex(s_mutexJoyStickEnum);
407     s_bJoystickThreadQuit = SDL_TRUE;
408     SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
409     SDL_UnlockMutex(s_mutexJoyStickEnum);
410     PostThreadMessage(SDL_GetThreadID(s_joystickThread), WM_QUIT, 0, 0);
411     SDL_WaitThread(s_joystickThread, NULL); /* wait for it to bugger off */
412 
413     SDL_DestroyCond(s_condJoystickThread);
414     s_condJoystickThread = NULL;
415 
416     SDL_DestroyMutex(s_mutexJoyStickEnum);
417     s_mutexJoyStickEnum = NULL;
418 
419     s_joystickThread = NULL;
420 }
421 
422 #endif /* !__WINRT__ */
423 
WINDOWS_AddJoystickDevice(JoyStick_DeviceData * device)424 void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
425 {
426     device->send_add_event = SDL_TRUE;
427     device->nInstanceID = SDL_GetNextJoystickInstanceID();
428     device->pNext = SYS_Joystick;
429     SYS_Joystick = device;
430 }
431 
432 static void WINDOWS_JoystickDetect(void);
433 static void WINDOWS_JoystickQuit(void);
434 
435 /* Function to scan the system for joysticks.
436  * Joystick 0 should be the system default joystick.
437  * It should return 0, or -1 on an unrecoverable fatal error.
438  */
439 static int
WINDOWS_JoystickInit(void)440 WINDOWS_JoystickInit(void)
441 {
442     if (SDL_DINPUT_JoystickInit() < 0) {
443         WINDOWS_JoystickQuit();
444         return -1;
445     }
446 
447     if (SDL_XINPUT_JoystickInit() < 0) {
448         WINDOWS_JoystickQuit();
449         return -1;
450     }
451 
452     s_bWindowsDeviceChanged = SDL_TRUE; /* force a scan of the system for joysticks this first time */
453 
454     WINDOWS_JoystickDetect();
455 
456 #ifndef __WINRT__
457     SDL_CreateDeviceNotificationFunc();
458 
459     s_bJoystickThread = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_THREAD, SDL_FALSE);
460     if (s_bJoystickThread) {
461         if (SDL_StartJoystickThread() < 0) {
462             return -1;
463         }
464     } else {
465         if (SDL_CreateDeviceNotification(&s_notification_data) < 0) {
466             return -1;
467         }
468     }
469 #endif
470     return 0;
471 }
472 
473 /* return the number of joysticks that are connected right now */
474 static int
WINDOWS_JoystickGetCount(void)475 WINDOWS_JoystickGetCount(void)
476 {
477     int nJoysticks = 0;
478     JoyStick_DeviceData *device = SYS_Joystick;
479     while (device) {
480         nJoysticks++;
481         device = device->pNext;
482     }
483 
484     return nJoysticks;
485 }
486 
487 /* detect any new joysticks being inserted into the system */
488 static void
WINDOWS_JoystickDetect(void)489 WINDOWS_JoystickDetect(void)
490 {
491     int device_index = 0;
492     JoyStick_DeviceData *pCurList = NULL;
493 
494     /* only enum the devices if the joystick thread told us something changed */
495     if (!s_bWindowsDeviceChanged) {
496         return;  /* thread hasn't signaled, nothing to do right now. */
497     }
498 
499     if (s_mutexJoyStickEnum) {
500         SDL_LockMutex(s_mutexJoyStickEnum);
501     }
502 
503     s_bWindowsDeviceChanged = SDL_FALSE;
504 
505     pCurList = SYS_Joystick;
506     SYS_Joystick = NULL;
507 
508     /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
509     SDL_DINPUT_JoystickDetect(&pCurList);
510 
511     /* Look for XInput devices. Do this last, so they're first in the final list. */
512     SDL_XINPUT_JoystickDetect(&pCurList);
513 
514     if (s_mutexJoyStickEnum) {
515         SDL_UnlockMutex(s_mutexJoyStickEnum);
516     }
517 
518     while (pCurList) {
519         JoyStick_DeviceData *pListNext = NULL;
520 
521         if (pCurList->bXInputDevice) {
522 #if SDL_HAPTIC_XINPUT
523             SDL_XINPUT_HapticMaybeRemoveDevice(pCurList->XInputUserId);
524 #endif
525         } else {
526 #if SDL_HAPTIC_DINPUT
527             SDL_DINPUT_HapticMaybeRemoveDevice(&pCurList->dxdevice);
528 #endif
529         }
530 
531         SDL_PrivateJoystickRemoved(pCurList->nInstanceID);
532 
533         pListNext = pCurList->pNext;
534         SDL_free(pCurList->joystickname);
535         SDL_free(pCurList);
536         pCurList = pListNext;
537     }
538 
539     for (device_index = 0, pCurList = SYS_Joystick; pCurList; ++device_index, pCurList = pCurList->pNext) {
540         if (pCurList->send_add_event) {
541             if (pCurList->bXInputDevice) {
542 #if SDL_HAPTIC_XINPUT
543                 SDL_XINPUT_HapticMaybeAddDevice(pCurList->XInputUserId);
544 #endif
545             } else {
546 #if SDL_HAPTIC_DINPUT
547                 SDL_DINPUT_HapticMaybeAddDevice(&pCurList->dxdevice);
548 #endif
549             }
550 
551             SDL_PrivateJoystickAdded(pCurList->nInstanceID);
552 
553             pCurList->send_add_event = SDL_FALSE;
554         }
555     }
556 }
557 
558 /* Function to get the device-dependent name of a joystick */
559 static const char *
WINDOWS_JoystickGetDeviceName(int device_index)560 WINDOWS_JoystickGetDeviceName(int device_index)
561 {
562     JoyStick_DeviceData *device = SYS_Joystick;
563     int index;
564 
565     for (index = device_index; index > 0; index--)
566         device = device->pNext;
567 
568     return device->joystickname;
569 }
570 
571 static int
WINDOWS_JoystickGetDevicePlayerIndex(int device_index)572 WINDOWS_JoystickGetDevicePlayerIndex(int device_index)
573 {
574     JoyStick_DeviceData *device = SYS_Joystick;
575     int index;
576 
577     for (index = device_index; index > 0; index--)
578         device = device->pNext;
579 
580     return device->bXInputDevice ? (int)device->XInputUserId : -1;
581 }
582 
583 static void
WINDOWS_JoystickSetDevicePlayerIndex(int device_index,int player_index)584 WINDOWS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
585 {
586 }
587 
588 /* return the stable device guid for this device index */
589 static SDL_JoystickGUID
WINDOWS_JoystickGetDeviceGUID(int device_index)590 WINDOWS_JoystickGetDeviceGUID(int device_index)
591 {
592     JoyStick_DeviceData *device = SYS_Joystick;
593     int index;
594 
595     for (index = device_index; index > 0; index--)
596         device = device->pNext;
597 
598     return device->guid;
599 }
600 
601 /* Function to perform the mapping between current device instance and this joysticks instance id */
602 static SDL_JoystickID
WINDOWS_JoystickGetDeviceInstanceID(int device_index)603 WINDOWS_JoystickGetDeviceInstanceID(int device_index)
604 {
605     JoyStick_DeviceData *device = SYS_Joystick;
606     int index;
607 
608     for (index = device_index; index > 0; index--)
609         device = device->pNext;
610 
611     return device->nInstanceID;
612 }
613 
614 /* Function to open a joystick for use.
615    The joystick to open is specified by the device index.
616    This should fill the nbuttons and naxes fields of the joystick structure.
617    It returns 0, or -1 if there is an error.
618  */
619 static int
WINDOWS_JoystickOpen(SDL_Joystick * joystick,int device_index)620 WINDOWS_JoystickOpen(SDL_Joystick *joystick, int device_index)
621 {
622     JoyStick_DeviceData *device = SYS_Joystick;
623     int index;
624 
625     for (index = device_index; index > 0; index--)
626         device = device->pNext;
627 
628     /* allocate memory for system specific hardware data */
629     joystick->instance_id = device->nInstanceID;
630     joystick->hwdata =
631         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
632     if (joystick->hwdata == NULL) {
633         return SDL_OutOfMemory();
634     }
635     SDL_zerop(joystick->hwdata);
636     joystick->hwdata->guid = device->guid;
637 
638     if (device->bXInputDevice) {
639         return SDL_XINPUT_JoystickOpen(joystick, device);
640     } else {
641         return SDL_DINPUT_JoystickOpen(joystick, device);
642     }
643 }
644 
645 static int
WINDOWS_JoystickRumble(SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)646 WINDOWS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
647 {
648     if (joystick->hwdata->bXInputDevice) {
649         return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble);
650     } else {
651         return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble);
652     }
653 }
654 
655 static int
WINDOWS_JoystickRumbleTriggers(SDL_Joystick * joystick,Uint16 left_rumble,Uint16 right_rumble)656 WINDOWS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
657 {
658     return SDL_Unsupported();
659 }
660 
661 static Uint32
WINDOWS_JoystickGetCapabilities(SDL_Joystick * joystick)662 WINDOWS_JoystickGetCapabilities(SDL_Joystick *joystick)
663 {
664     if (joystick->hwdata->bXInputDevice) {
665         return SDL_XINPUT_JoystickGetCapabilities(joystick);
666     } else {
667         return SDL_DINPUT_JoystickGetCapabilities(joystick);
668     }
669 }
670 
671 static int
WINDOWS_JoystickSetLED(SDL_Joystick * joystick,Uint8 red,Uint8 green,Uint8 blue)672 WINDOWS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
673 {
674     return SDL_Unsupported();
675 }
676 
677 static int
WINDOWS_JoystickSendEffect(SDL_Joystick * joystick,const void * data,int size)678 WINDOWS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
679 {
680     return SDL_Unsupported();
681 }
682 
683 static int
WINDOWS_JoystickSetSensorsEnabled(SDL_Joystick * joystick,SDL_bool enabled)684 WINDOWS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
685 {
686     return SDL_Unsupported();
687 }
688 
689 static void
WINDOWS_JoystickUpdate(SDL_Joystick * joystick)690 WINDOWS_JoystickUpdate(SDL_Joystick *joystick)
691 {
692     if (!joystick->hwdata) {
693         return;
694     }
695 
696     if (joystick->hwdata->bXInputDevice) {
697         SDL_XINPUT_JoystickUpdate(joystick);
698     } else {
699         SDL_DINPUT_JoystickUpdate(joystick);
700     }
701 }
702 
703 /* Function to close a joystick after use */
704 static void
WINDOWS_JoystickClose(SDL_Joystick * joystick)705 WINDOWS_JoystickClose(SDL_Joystick *joystick)
706 {
707     if (joystick->hwdata->bXInputDevice) {
708         SDL_XINPUT_JoystickClose(joystick);
709     } else {
710         SDL_DINPUT_JoystickClose(joystick);
711     }
712 
713     SDL_free(joystick->hwdata);
714 }
715 
716 /* Function to perform any system-specific joystick related cleanup */
717 static void
WINDOWS_JoystickQuit(void)718 WINDOWS_JoystickQuit(void)
719 {
720     JoyStick_DeviceData *device = SYS_Joystick;
721 
722     while (device) {
723         JoyStick_DeviceData *device_next = device->pNext;
724         SDL_free(device->joystickname);
725         SDL_free(device);
726         device = device_next;
727     }
728     SYS_Joystick = NULL;
729 
730 #ifndef __WINRT__
731     if (s_bJoystickThread) {
732         SDL_StopJoystickThread();
733     } else {
734         SDL_CleanupDeviceNotification(&s_notification_data);
735     }
736 
737     SDL_CleanupDeviceNotificationFunc();
738 #endif
739 
740     SDL_DINPUT_JoystickQuit();
741     SDL_XINPUT_JoystickQuit();
742 
743     s_bWindowsDeviceChanged = SDL_FALSE;
744 }
745 
746 static SDL_bool
WINDOWS_JoystickGetGamepadMapping(int device_index,SDL_GamepadMapping * out)747 WINDOWS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
748 {
749     return SDL_FALSE;
750 }
751 
752 SDL_JoystickDriver SDL_WINDOWS_JoystickDriver =
753 {
754     WINDOWS_JoystickInit,
755     WINDOWS_JoystickGetCount,
756     WINDOWS_JoystickDetect,
757     WINDOWS_JoystickGetDeviceName,
758     WINDOWS_JoystickGetDevicePlayerIndex,
759     WINDOWS_JoystickSetDevicePlayerIndex,
760     WINDOWS_JoystickGetDeviceGUID,
761     WINDOWS_JoystickGetDeviceInstanceID,
762     WINDOWS_JoystickOpen,
763     WINDOWS_JoystickRumble,
764     WINDOWS_JoystickRumbleTriggers,
765     WINDOWS_JoystickGetCapabilities,
766     WINDOWS_JoystickSetLED,
767     WINDOWS_JoystickSendEffect,
768     WINDOWS_JoystickSetSensorsEnabled,
769     WINDOWS_JoystickUpdate,
770     WINDOWS_JoystickClose,
771     WINDOWS_JoystickQuit,
772     WINDOWS_JoystickGetGamepadMapping
773 };
774 
775 #else
776 
777 #if SDL_JOYSTICK_RAWINPUT
778 /* The RAWINPUT driver needs the device notification setup above */
779 #error SDL_JOYSTICK_RAWINPUT requires SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT
780 #endif
781 
782 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
783 
784 /* vi: set ts=4 sw=4 expandtab: */
785