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