SDL  2.0
SDL_windowsjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 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
81 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
82 {
83 }
84 
85 static int
86 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
87 {
88  return 0;
89 }
90 
91 static SDL_bool
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
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
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) {
149  }
150 }
151 
152 static int
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
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
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_zero(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 
274 {
275  device->send_add_event = SDL_TRUE;
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
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
319 WINDOWS_JoystickGetCount(void)
320 {
321  int nJoysticks = 0;
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
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) {
363  } else {
365  }
366 
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) {
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 *
400 WINDOWS_JoystickGetDeviceName(int device_index)
401 {
403 
404  for (; device_index > 0; device_index--)
405  device = device->pNext;
406 
407  return device->joystickname;
408 }
409 
410 static int
411 WINDOWS_JoystickGetDevicePlayerIndex(int device_index)
412 {
414  int index;
415 
416  for (index = device_index; index > 0; index--)
417  device = device->pNext;
418 
419  return device->bXInputDevice ? (int)device->XInputUserId : -1;
420 }
421 
422 /* return the stable device guid for this device index */
423 static SDL_JoystickGUID
424 WINDOWS_JoystickGetDeviceGUID(int device_index)
425 {
427  int index;
428 
429  for (index = device_index; index > 0; index--)
430  device = device->pNext;
431 
432  return device->guid;
433 }
434 
435 /* Function to perform the mapping between current device instance and this joysticks instance id */
436 static SDL_JoystickID
437 WINDOWS_JoystickGetDeviceInstanceID(int device_index)
438 {
440  int index;
441 
442  for (index = device_index; index > 0; index--)
443  device = device->pNext;
444 
445  return device->nInstanceID;
446 }
447 
448 /* Function to open a joystick for use.
449  The joystick to open is specified by the device index.
450  This should fill the nbuttons and naxes fields of the joystick structure.
451  It returns 0, or -1 if there is an error.
452  */
453 static int
454 WINDOWS_JoystickOpen(SDL_Joystick * joystick, int device_index)
455 {
456  JoyStick_DeviceData *joystickdevice = SYS_Joystick;
457 
458  for (; device_index > 0; device_index--)
459  joystickdevice = joystickdevice->pNext;
460 
461  /* allocate memory for system specific hardware data */
462  joystick->instance_id = joystickdevice->nInstanceID;
463  joystick->hwdata =
464  (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
465  if (joystick->hwdata == NULL) {
466  return SDL_OutOfMemory();
467  }
468  SDL_zerop(joystick->hwdata);
469  joystick->hwdata->guid = joystickdevice->guid;
470 
471  if (joystickdevice->bXInputDevice) {
472  return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
473  } else {
474  return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
475  }
476 }
477 
478 static int
479 WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
480 {
481  if (joystick->hwdata->bXInputDevice) {
482  return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
483  } else {
484  return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
485  }
486 }
487 
488 static void
489 WINDOWS_JoystickUpdate(SDL_Joystick * joystick)
490 {
491  if (!joystick->hwdata) {
492  return;
493  }
494 
495  if (joystick->hwdata->bXInputDevice) {
496  SDL_XINPUT_JoystickUpdate(joystick);
497  } else {
498  SDL_DINPUT_JoystickUpdate(joystick);
499  }
500 }
501 
502 /* Function to close a joystick after use */
503 static void
504 WINDOWS_JoystickClose(SDL_Joystick * joystick)
505 {
506  if (joystick->hwdata->bXInputDevice) {
507  SDL_XINPUT_JoystickClose(joystick);
508  } else {
509  SDL_DINPUT_JoystickClose(joystick);
510  }
511 
512  SDL_free(joystick->hwdata);
513 }
514 
515 /* Function to perform any system-specific joystick related cleanup */
516 static void
517 WINDOWS_JoystickQuit(void)
518 {
520 
521  while (device) {
522  JoyStick_DeviceData *device_next = device->pNext;
523  SDL_free(device->joystickname);
524  SDL_free(device);
525  device = device_next;
526  }
527  SYS_Joystick = NULL;
528 
529  if (s_threadJoystick) {
530  SDL_LockMutex(s_mutexJoyStickEnum);
531  s_bJoystickThreadQuit = SDL_TRUE;
532  SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
533  SDL_UnlockMutex(s_mutexJoyStickEnum);
534 #ifndef __WINRT__
535  PostThreadMessage(SDL_GetThreadID(s_threadJoystick), WM_QUIT, 0, 0);
536 #endif
537  SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
538 
539  SDL_DestroyMutex(s_mutexJoyStickEnum);
540  SDL_DestroyCond(s_condJoystickThread);
541  s_condJoystickThread= NULL;
542  s_mutexJoyStickEnum = NULL;
543  s_threadJoystick = NULL;
544  }
545 
548 
549  s_bDeviceAdded = SDL_FALSE;
550  s_bDeviceRemoved = SDL_FALSE;
551 }
552 
554 {
555  WINDOWS_JoystickInit,
556  WINDOWS_JoystickGetCount,
557  WINDOWS_JoystickDetect,
558  WINDOWS_JoystickGetDeviceName,
559  WINDOWS_JoystickGetDevicePlayerIndex,
560  WINDOWS_JoystickGetDeviceGUID,
561  WINDOWS_JoystickGetDeviceInstanceID,
562  WINDOWS_JoystickOpen,
563  WINDOWS_JoystickRumble,
564  WINDOWS_JoystickUpdate,
565  WINDOWS_JoystickClose,
566  WINDOWS_JoystickQuit,
567 };
568 
569 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
570 
571 /* vi: set ts=4 sw=4 expandtab: */
JoyStick_DeviceData * SYS_Joystick
#define SDL_LockMutex
int SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:805
GLuint64EXT * result
int SDL_DINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
GLuint GLsizei const GLchar * message
int SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
static int available()
Definition: video.c:356
struct JoyStick_DeviceData * pNext
#define SDL_CreateMutex
int SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
uint16_t Uint16
Definition: SDL_stdinc.h:191
int SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
#define SDL_DestroyCond
int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
static SDL_mutex * mutex
Definition: testlock.c:23
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
SDL_JoystickDriver SDL_WINDOWS_JoystickDriver
static SDL_AudioDeviceID device
Definition: loopwave.c:37
int SDL_DINPUT_JoystickInit(void)
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
int SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
#define SDL_CondWaitTimeout
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
HRESULT WIN_CoInitialize(void)
uint8_t Uint8
Definition: SDL_stdinc.h:179
#define SDL_free
int SDL_XINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
SDL_bool SDL_XINPUT_Enabled(void)
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
#define SDL_CreateCond
#define SDL_GetThreadID
#define SDL_CondBroadcast
#define S_OK
Definition: SDL_directx.h:47
GLuint index
int SDL_XINPUT_JoystickInit(void)
void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:755
#define NULL
Definition: begin_code.h:167
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
void WIN_CoUninitialize(void)
int WIN_SetError(const char *prefix)
void SDL_XINPUT_JoystickQuit(void)
#define SDL_DestroyMutex
void SDL_DINPUT_JoystickUpdate(SDL_Joystick *joystick)
void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:163
uint32_t Uint32
Definition: SDL_stdinc.h:203
#define SDL_UnlockMutex
void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick)
#define SDL_malloc
void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick)
DIDEVICEINSTANCE dxdevice
void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
#define SDL_WaitThread
void SDL_DINPUT_JoystickQuit(void)