SDL  2.0
SDL_windowsjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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_JoystickID s_nInstanceID = -1;
65 static SDL_cond *s_condJoystickThread = NULL;
66 static SDL_mutex *s_mutexJoyStickEnum = NULL;
67 static SDL_Thread *s_threadJoystick = NULL;
68 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
69 
70 JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */
71 
72 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
73 
74 #ifdef __WINRT__
75 
76 typedef struct
77 {
78  int unused;
79 } SDL_DeviceNotificationData;
80 
81 static void
82 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
83 {
84 }
85 
86 static int
87 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
88 {
89  return 0;
90 }
91 
92 static SDL_bool
93 SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
94 {
95  return SDL_FALSE;
96 }
97 
98 #else /* !__WINRT__ */
99 
100 typedef struct
101 {
102  HRESULT coinitialized;
103  WNDCLASSEX wincl;
104  HWND messageWindow;
105  HDEVNOTIFY hNotify;
106 } SDL_DeviceNotificationData;
107 
108 #define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200
109 #define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201
110 
111 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */
112 static LRESULT CALLBACK
113 SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
114 {
115  switch (message) {
116  case WM_DEVICECHANGE:
117  switch (wParam) {
118  case DBT_DEVICEARRIVAL:
119  case DBT_DEVICEREMOVECOMPLETE:
120  if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
121  /* notify 300ms and 2 seconds later to ensure all APIs have updated status */
122  SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL);
123  SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL);
124  }
125  break;
126  }
127  return 0;
128  case WM_TIMER:
129  KillTimer(hwnd, wParam);
130  s_bWindowsDeviceChanged = SDL_TRUE;
131  return 0;
132  }
133 
134  return DefWindowProc (hwnd, message, wParam, lParam);
135 }
136 
137 static void
138 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
139 {
140  if (data->hNotify)
141  UnregisterDeviceNotification(data->hNotify);
142 
143  if (data->messageWindow)
144  DestroyWindow(data->messageWindow);
145 
146  UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
147 
148  if (data->coinitialized == S_OK) {
150  }
151 }
152 
153 static int
154 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
155 {
156  DEV_BROADCAST_DEVICEINTERFACE dbh;
157  GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
158 
159  SDL_zerop(data);
160 
161  data->coinitialized = WIN_CoInitialize();
162 
163  data->wincl.hInstance = GetModuleHandle(NULL);
164  data->wincl.lpszClassName = L"Message";
165  data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */
166  data->wincl.cbSize = sizeof (WNDCLASSEX);
167 
168  if (!RegisterClassEx(&data->wincl)) {
169  WIN_SetError("Failed to create register class for joystick autodetect");
170  SDL_CleanupDeviceNotification(data);
171  return -1;
172  }
173 
174  data->messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
175  if (!data->messageWindow) {
176  WIN_SetError("Failed to create message window for joystick autodetect");
177  SDL_CleanupDeviceNotification(data);
178  return -1;
179  }
180 
181  SDL_zero(dbh);
182  dbh.dbcc_size = sizeof(dbh);
183  dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
184  dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
185 
186  data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
187  if (!data->hNotify) {
188  WIN_SetError("Failed to create notify device for joystick autodetect");
189  SDL_CleanupDeviceNotification(data);
190  return -1;
191  }
192  return 0;
193 }
194 
195 static SDL_bool
196 SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
197 {
198  MSG msg;
199  int lastret = 1;
200 
201  if (!data->messageWindow) {
202  return SDL_FALSE; /* device notifications require a window */
203  }
204 
205  SDL_UnlockMutex(mutex);
206  while (lastret > 0 && s_bWindowsDeviceChanged == SDL_FALSE) {
207  lastret = GetMessage(&msg, NULL, 0, 0); /* WM_QUIT causes return value of 0 */
208  if (lastret > 0) {
209  TranslateMessage(&msg);
210  DispatchMessage(&msg);
211  }
212  }
213  SDL_LockMutex(mutex);
214  return (lastret != -1) ? SDL_TRUE : SDL_FALSE;
215 }
216 
217 #endif /* __WINRT__ */
218 
219 /* Function/thread to scan the system for joysticks. */
220 static int
221 SDL_JoystickThread(void *_data)
222 {
223  SDL_DeviceNotificationData notification_data;
224 
225 #if SDL_JOYSTICK_XINPUT
226  SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT];
227  SDL_zero(bOpenedXInputDevices);
228 #endif
229 
230  if (SDL_CreateDeviceNotification(&notification_data) < 0) {
231  return -1;
232  }
233 
234  SDL_LockMutex(s_mutexJoyStickEnum);
235  while (s_bJoystickThreadQuit == SDL_FALSE) {
236  SDL_bool bXInputChanged = SDL_FALSE;
237 
238  if (SDL_WaitForDeviceNotification(&notification_data, s_mutexJoyStickEnum) == SDL_FALSE) {
239 #if SDL_JOYSTICK_XINPUT
240  /* WM_DEVICECHANGE not working, poll for new XINPUT controllers */
241  SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000);
242  if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
243  /* scan for any change in XInput devices */
244  Uint8 userId;
245  for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
246  XINPUT_CAPABILITIES capabilities;
247  const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
248  const SDL_bool available = (result == ERROR_SUCCESS);
249  if (bOpenedXInputDevices[userId] != available) {
250  bXInputChanged = SDL_TRUE;
251  bOpenedXInputDevices[userId] = available;
252  }
253  }
254  }
255 #else
256  /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */
257  break;
258 #endif /* SDL_JOYSTICK_XINPUT */
259  }
260 
261  if (s_bWindowsDeviceChanged || bXInputChanged) {
262  s_bDeviceRemoved = SDL_TRUE;
263  s_bDeviceAdded = SDL_TRUE;
264  s_bWindowsDeviceChanged = SDL_FALSE;
265  }
266  }
267  SDL_UnlockMutex(s_mutexJoyStickEnum);
268 
269  SDL_CleanupDeviceNotification(&notification_data);
270 
271  return 1;
272 }
273 
275 {
276  device->send_add_event = SDL_TRUE;
277  device->nInstanceID = ++s_nInstanceID;
278  device->pNext = SYS_Joystick;
279  SYS_Joystick = device;
280 
281  s_bDeviceAdded = SDL_TRUE;
282 }
283 
284 /* Function to scan the system for joysticks.
285  * Joystick 0 should be the system default joystick.
286  * It should return 0, or -1 on an unrecoverable fatal error.
287  */
288 int
290 {
291  if (SDL_DINPUT_JoystickInit() < 0) {
293  return -1;
294  }
295 
296  if (SDL_XINPUT_JoystickInit() < 0) {
298  return -1;
299  }
300 
301  s_mutexJoyStickEnum = SDL_CreateMutex();
302  s_condJoystickThread = SDL_CreateCond();
303  s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
304 
306 
307  if (!s_threadJoystick) {
308  /* spin up the thread to detect hotplug of devices */
309  s_bJoystickThreadQuit = SDL_FALSE;
310  s_threadJoystick = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
311  }
312  return SDL_SYS_NumJoysticks();
313 }
314 
315 /* return the number of joysticks that are connected right now */
316 int
318 {
319  int nJoysticks = 0;
321  while (device) {
322  nJoysticks++;
323  device = device->pNext;
324  }
325 
326  return nJoysticks;
327 }
328 
329 /* detect any new joysticks being inserted into the system */
330 void
332 {
333  JoyStick_DeviceData *pCurList = NULL;
334 
335  /* only enum the devices if the joystick thread told us something changed */
336  if (!s_bDeviceAdded && !s_bDeviceRemoved) {
337  return; /* thread hasn't signaled, nothing to do right now. */
338  }
339 
340  SDL_LockMutex(s_mutexJoyStickEnum);
341 
342  s_bDeviceAdded = SDL_FALSE;
343  s_bDeviceRemoved = SDL_FALSE;
344 
345  pCurList = SYS_Joystick;
346  SYS_Joystick = NULL;
347 
348  /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
349  SDL_DINPUT_JoystickDetect(&pCurList);
350 
351  /* Look for XInput devices. Do this last, so they're first in the final list. */
352  SDL_XINPUT_JoystickDetect(&pCurList);
353 
354  SDL_UnlockMutex(s_mutexJoyStickEnum);
355 
356  while (pCurList) {
357  JoyStick_DeviceData *pListNext = NULL;
358 
359  if (pCurList->bXInputDevice) {
361  } else {
363  }
364 
366 
367  pListNext = pCurList->pNext;
368  SDL_free(pCurList->joystickname);
369  SDL_free(pCurList);
370  pCurList = pListNext;
371  }
372 
373  if (s_bDeviceAdded) {
374  JoyStick_DeviceData *pNewJoystick;
375  int device_index = 0;
376  s_bDeviceAdded = SDL_FALSE;
377  pNewJoystick = SYS_Joystick;
378  while (pNewJoystick) {
379  if (pNewJoystick->send_add_event) {
380  if (pNewJoystick->bXInputDevice) {
382  } else {
383  SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
384  }
385 
386  SDL_PrivateJoystickAdded(device_index);
387 
388  pNewJoystick->send_add_event = SDL_FALSE;
389  }
390  device_index++;
391  pNewJoystick = pNewJoystick->pNext;
392  }
393  }
394 }
395 
396 /* Function to get the device-dependent name of a joystick */
397 const char *
398 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
399 {
401 
402  for (; device_index > 0; device_index--)
403  device = device->pNext;
404 
405  return device->joystickname;
406 }
407 
408 /* Function to perform the mapping between current device instance and this joysticks instance id */
410 SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
411 {
413  int index;
414 
415  for (index = device_index; index > 0; index--)
416  device = device->pNext;
417 
418  return device->nInstanceID;
419 }
420 
421 /* Function to open a joystick for use.
422  The joystick to open is specified by the device index.
423  This should fill the nbuttons and naxes fields of the joystick structure.
424  It returns 0, or -1 if there is an error.
425  */
426 int
427 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
428 {
429  JoyStick_DeviceData *joystickdevice = SYS_Joystick;
430 
431  for (; device_index > 0; device_index--)
432  joystickdevice = joystickdevice->pNext;
433 
434  /* allocate memory for system specific hardware data */
435  joystick->instance_id = joystickdevice->nInstanceID;
436  joystick->hwdata =
437  (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
438  if (joystick->hwdata == NULL) {
439  return SDL_OutOfMemory();
440  }
441  SDL_zerop(joystick->hwdata);
442  joystick->hwdata->guid = joystickdevice->guid;
443 
444  if (joystickdevice->bXInputDevice) {
445  return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
446  } else {
447  return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
448  }
449 }
450 
451 /* return true if this joystick is plugged in right now */
452 SDL_bool
453 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
454 {
455  return joystick->hwdata && !joystick->hwdata->removed;
456 }
457 
458 void
459 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
460 {
461  if (!joystick->hwdata || joystick->hwdata->removed) {
462  return;
463  }
464 
465  if (joystick->hwdata->bXInputDevice) {
466  SDL_XINPUT_JoystickUpdate(joystick);
467  } else {
468  SDL_DINPUT_JoystickUpdate(joystick);
469  }
470 
471  if (joystick->hwdata->removed) {
472  joystick->force_recentering = SDL_TRUE;
473  }
474 }
475 
476 /* Function to close a joystick after use */
477 void
478 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
479 {
480  if (joystick->hwdata->bXInputDevice) {
481  SDL_XINPUT_JoystickClose(joystick);
482  } else {
483  SDL_DINPUT_JoystickClose(joystick);
484  }
485 
486  SDL_free(joystick->hwdata);
487 }
488 
489 /* Function to perform any system-specific joystick related cleanup */
490 void
492 {
494 
495  while (device) {
496  JoyStick_DeviceData *device_next = device->pNext;
497  SDL_free(device->joystickname);
498  SDL_free(device);
499  device = device_next;
500  }
501  SYS_Joystick = NULL;
502 
503  if (s_threadJoystick) {
504  SDL_LockMutex(s_mutexJoyStickEnum);
505  s_bJoystickThreadQuit = SDL_TRUE;
506  SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
507  SDL_UnlockMutex(s_mutexJoyStickEnum);
508 #ifndef __WINRT__
509  PostThreadMessage(SDL_GetThreadID(s_threadJoystick), WM_QUIT, 0, 0);
510 #endif
511  SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
512 
513  SDL_DestroyMutex(s_mutexJoyStickEnum);
514  SDL_DestroyCond(s_condJoystickThread);
515  s_condJoystickThread= NULL;
516  s_mutexJoyStickEnum = NULL;
517  s_threadJoystick = NULL;
518  }
519 
522 
523  s_bDeviceAdded = SDL_FALSE;
524  s_bDeviceRemoved = SDL_FALSE;
525 }
526 
527 /* return the stable device guid for this device index */
529 SDL_SYS_JoystickGetDeviceGUID(int device_index)
530 {
532  int index;
533 
534  for (index = device_index; index > 0; index--)
535  device = device->pNext;
536 
537  return device->guid;
538 }
539 
541 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
542 {
543  return joystick->hwdata->guid;
544 }
545 
546 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
547 
548 /* 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:638
GLuint64EXT * result
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
void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
int SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
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)
void SDL_SYS_JoystickQuit(void)
static SDL_mutex * mutex
Definition: testlock.c:23
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
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:427
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:157
#define SDL_free
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
SDL_bool SDL_XINPUT_Enabled(void)
void SDL_PrivateJoystickAdded(int device_index)
Definition: SDL_joystick.c:595
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index)
#define SDL_CreateCond
#define SDL_GetThreadID
#define SDL_CondBroadcast
#define S_OK
Definition: SDL_directx.h:47
void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
GLuint index
int SDL_XINPUT_JoystickInit(void)
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
int SDL_SYS_JoystickInit(void)
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
void WIN_CoUninitialize(void)
int SDL_SYS_NumJoysticks(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_SYS_GetInstanceIdOfDeviceIndex(int device_index)
#define SDL_UnlockMutex
void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick)
int SDL_SYS_JoystickOpen(SDL_Joystick *joystick, int device_index)
#define SDL_malloc
void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick)
DIDEVICEINSTANCE dxdevice
void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
#define SDL_WaitThread
void SDL_SYS_JoystickDetect(void)
void SDL_DINPUT_JoystickQuit(void)