SDL  2.0
SDL_windowsjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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_events.h"
41 #include "SDL_hints.h"
42 #include "SDL_joystick.h"
43 #include "../SDL_sysjoystick.h"
44 #include "../../thread/SDL_systhread.h"
45 #include "../../core/windows/SDL_windows.h"
46 #if !defined(__WINRT__)
47 #include <dbt.h>
48 #endif
49 
50 #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
51 #include "SDL_windowsjoystick_c.h"
52 #include "SDL_dinputjoystick_c.h"
53 #include "SDL_xinputjoystick_c.h"
54 
55 #include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */
56 #include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */
57 
58 
59 #ifndef DEVICE_NOTIFY_WINDOW_HANDLE
60 #define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
61 #endif
62 
63 /* local variables */
64 static SDL_bool s_bDeviceAdded = SDL_FALSE;
65 static SDL_bool s_bDeviceRemoved = SDL_FALSE;
66 static SDL_JoystickID s_nInstanceID = -1;
67 static SDL_cond *s_condJoystickThread = NULL;
68 static SDL_mutex *s_mutexJoyStickEnum = NULL;
69 static SDL_Thread *s_threadJoystick = NULL;
70 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
71 
72 JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */
73 
74 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
75 
76 #ifdef __WINRT__
77 
78 typedef struct
79 {
80  int unused;
81 } SDL_DeviceNotificationData;
82 
83 static void
84 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
85 {
86 }
87 
88 static int
89 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
90 {
91  return 0;
92 }
93 
94 static void
95 SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
96 {
97 }
98 
99 #else /* !__WINRT__ */
100 
101 typedef struct
102 {
103  HRESULT coinitialized;
104  WNDCLASSEX wincl;
105  HWND messageWindow;
106  HDEVNOTIFY hNotify;
107 } SDL_DeviceNotificationData;
108 
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  if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
119  s_bWindowsDeviceChanged = SDL_TRUE;
120  }
121  break;
122  case DBT_DEVICEREMOVECOMPLETE:
123  if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
124  s_bWindowsDeviceChanged = SDL_TRUE;
125  }
126  break;
127  }
128  return 0;
129  }
130 
131  return DefWindowProc (hwnd, message, wParam, lParam);
132 }
133 
134 static void
135 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
136 {
137  if (data->hNotify)
138  UnregisterDeviceNotification(data->hNotify);
139 
140  if (data->messageWindow)
141  DestroyWindow(data->messageWindow);
142 
143  UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
144 
145  if (data->coinitialized == S_OK) {
147  }
148 }
149 
150 static int
151 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
152 {
153  DEV_BROADCAST_DEVICEINTERFACE dbh;
154  GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
155 
156  SDL_zerop(data);
157 
158  data->coinitialized = WIN_CoInitialize();
159 
160  data->wincl.hInstance = GetModuleHandle(NULL);
161  data->wincl.lpszClassName = L"Message";
162  data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */
163  data->wincl.cbSize = sizeof (WNDCLASSEX);
164 
165  if (!RegisterClassEx(&data->wincl)) {
166  WIN_SetError("Failed to create register class for joystick autodetect");
167  SDL_CleanupDeviceNotification(data);
168  return -1;
169  }
170 
171  data->messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
172  if (!data->messageWindow) {
173  WIN_SetError("Failed to create message window for joystick autodetect");
174  SDL_CleanupDeviceNotification(data);
175  return -1;
176  }
177 
178  SDL_zero(dbh);
179  dbh.dbcc_size = sizeof(dbh);
180  dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
181  dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
182 
183  data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
184  if (!data->hNotify) {
185  WIN_SetError("Failed to create notify device for joystick autodetect");
186  SDL_CleanupDeviceNotification(data);
187  return -1;
188  }
189  return 0;
190 }
191 
192 static void
193 SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
194 {
195  MSG msg;
196 
197  if (!data->messageWindow) {
198  return;
199  }
200 
201  while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) {
202  if (GetMessage(&msg, data->messageWindow, 0, 0) != 0) {
203  TranslateMessage(&msg);
204  DispatchMessage(&msg);
205  }
206  }
207 }
208 
209 #endif /* __WINRT__ */
210 
211 /* Function/thread to scan the system for joysticks. */
212 static int
213 SDL_JoystickThread(void *_data)
214 {
215  SDL_DeviceNotificationData notification_data;
216 
217 #if SDL_JOYSTICK_XINPUT
218  SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT];
219  SDL_zero(bOpenedXInputDevices);
220 #endif
221 
222  if (SDL_CreateDeviceNotification(&notification_data) < 0) {
223  return -1;
224  }
225 
226  SDL_LockMutex(s_mutexJoyStickEnum);
227  while (s_bJoystickThreadQuit == SDL_FALSE) {
228  SDL_bool bXInputChanged = SDL_FALSE;
229 
230  SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300);
231 
232  SDL_CheckDeviceNotification(&notification_data);
233 
234 #if SDL_JOYSTICK_XINPUT
235  if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
236  /* scan for any change in XInput devices */
237  Uint8 userId;
238  for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
239  XINPUT_CAPABILITIES capabilities;
240  const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
241  const SDL_bool available = (result == ERROR_SUCCESS);
242  if (bOpenedXInputDevices[userId] != available) {
243  bXInputChanged = SDL_TRUE;
244  bOpenedXInputDevices[userId] = available;
245  }
246  }
247  }
248 #endif /* SDL_JOYSTICK_XINPUT */
249 
250  if (s_bWindowsDeviceChanged || bXInputChanged) {
251  SDL_UnlockMutex(s_mutexJoyStickEnum); /* let main thread go while we SDL_Delay(). */
252  SDL_Delay(300); /* wait for direct input to find out about this device */
253  SDL_LockMutex(s_mutexJoyStickEnum);
254 
255  s_bDeviceRemoved = SDL_TRUE;
256  s_bDeviceAdded = SDL_TRUE;
257  s_bWindowsDeviceChanged = SDL_FALSE;
258  }
259  }
260  SDL_UnlockMutex(s_mutexJoyStickEnum);
261 
262  SDL_CleanupDeviceNotification(&notification_data);
263 
264  return 1;
265 }
266 
268 {
269  device->send_add_event = SDL_TRUE;
270  device->nInstanceID = ++s_nInstanceID;
271  device->pNext = SYS_Joystick;
272  SYS_Joystick = device;
273 
274  s_bDeviceAdded = SDL_TRUE;
275 }
276 
277 /* Function to scan the system for joysticks.
278  * Joystick 0 should be the system default joystick.
279  * It should return 0, or -1 on an unrecoverable fatal error.
280  */
281 int
283 {
284  if (SDL_DINPUT_JoystickInit() < 0) {
286  return -1;
287  }
288 
289  if (SDL_XINPUT_JoystickInit() < 0) {
291  return -1;
292  }
293 
294  s_mutexJoyStickEnum = SDL_CreateMutex();
295  s_condJoystickThread = SDL_CreateCond();
296  s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
297 
299 
300  if (!s_threadJoystick) {
301  /* spin up the thread to detect hotplug of devices */
302  s_bJoystickThreadQuit = SDL_FALSE;
303  s_threadJoystick = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
304  }
305  return SDL_SYS_NumJoysticks();
306 }
307 
308 /* return the number of joysticks that are connected right now */
309 int
311 {
312  int nJoysticks = 0;
314  while (device) {
315  nJoysticks++;
316  device = device->pNext;
317  }
318 
319  return nJoysticks;
320 }
321 
322 /* detect any new joysticks being inserted into the system */
323 void
325 {
326  JoyStick_DeviceData *pCurList = NULL;
327 
328  /* only enum the devices if the joystick thread told us something changed */
329  if (!s_bDeviceAdded && !s_bDeviceRemoved) {
330  return; /* thread hasn't signaled, nothing to do right now. */
331  }
332 
333  SDL_LockMutex(s_mutexJoyStickEnum);
334 
335  s_bDeviceAdded = SDL_FALSE;
336  s_bDeviceRemoved = SDL_FALSE;
337 
338  pCurList = SYS_Joystick;
339  SYS_Joystick = NULL;
340 
341  /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
342  SDL_DINPUT_JoystickDetect(&pCurList);
343 
344  /* Look for XInput devices. Do this last, so they're first in the final list. */
345  SDL_XINPUT_JoystickDetect(&pCurList);
346 
347  SDL_UnlockMutex(s_mutexJoyStickEnum);
348 
349  while (pCurList) {
350  JoyStick_DeviceData *pListNext = NULL;
351 
352  if (pCurList->bXInputDevice) {
354  } else {
356  }
357 
359 
360  pListNext = pCurList->pNext;
361  SDL_free(pCurList->joystickname);
362  SDL_free(pCurList);
363  pCurList = pListNext;
364  }
365 
366  if (s_bDeviceAdded) {
367  JoyStick_DeviceData *pNewJoystick;
368  int device_index = 0;
369  s_bDeviceAdded = SDL_FALSE;
370  pNewJoystick = SYS_Joystick;
371  while (pNewJoystick) {
372  if (pNewJoystick->send_add_event) {
373  if (pNewJoystick->bXInputDevice) {
375  } else {
376  SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
377  }
378 
379  SDL_PrivateJoystickAdded(device_index);
380 
381  pNewJoystick->send_add_event = SDL_FALSE;
382  }
383  device_index++;
384  pNewJoystick = pNewJoystick->pNext;
385  }
386  }
387 }
388 
389 /* Function to get the device-dependent name of a joystick */
390 const char *
391 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
392 {
394 
395  for (; device_index > 0; device_index--)
396  device = device->pNext;
397 
398  return device->joystickname;
399 }
400 
401 /* Function to perform the mapping between current device instance and this joysticks instance id */
403 SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
404 {
406  int index;
407 
408  for (index = device_index; index > 0; index--)
409  device = device->pNext;
410 
411  return device->nInstanceID;
412 }
413 
414 /* Function to open a joystick for use.
415  The joystick to open is specified by the device index.
416  This should fill the nbuttons and naxes fields of the joystick structure.
417  It returns 0, or -1 if there is an error.
418  */
419 int
420 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
421 {
422  JoyStick_DeviceData *joystickdevice = SYS_Joystick;
423 
424  for (; device_index > 0; device_index--)
425  joystickdevice = joystickdevice->pNext;
426 
427  /* allocate memory for system specific hardware data */
428  joystick->instance_id = joystickdevice->nInstanceID;
429  joystick->hwdata =
430  (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
431  if (joystick->hwdata == NULL) {
432  return SDL_OutOfMemory();
433  }
434  SDL_zerop(joystick->hwdata);
435  joystick->hwdata->guid = joystickdevice->guid;
436 
437  if (joystickdevice->bXInputDevice) {
438  return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
439  } else {
440  return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
441  }
442 }
443 
444 /* return true if this joystick is plugged in right now */
445 SDL_bool
446 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
447 {
448  return joystick->hwdata && !joystick->hwdata->removed;
449 }
450 
451 void
452 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
453 {
454  if (!joystick->hwdata || joystick->hwdata->removed) {
455  return;
456  }
457 
458  if (joystick->hwdata->bXInputDevice) {
459  SDL_XINPUT_JoystickUpdate(joystick);
460  } else {
461  SDL_DINPUT_JoystickUpdate(joystick);
462  }
463 
464  if (joystick->hwdata->removed) {
465  joystick->force_recentering = SDL_TRUE;
466  }
467 }
468 
469 /* Function to close a joystick after use */
470 void
471 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
472 {
473  if (joystick->hwdata->bXInputDevice) {
474  SDL_XINPUT_JoystickClose(joystick);
475  } else {
476  SDL_DINPUT_JoystickClose(joystick);
477  }
478 
479  SDL_free(joystick->hwdata);
480 }
481 
482 /* Function to perform any system-specific joystick related cleanup */
483 void
485 {
487 
488  while (device) {
489  JoyStick_DeviceData *device_next = device->pNext;
490  SDL_free(device->joystickname);
491  SDL_free(device);
492  device = device_next;
493  }
494  SYS_Joystick = NULL;
495 
496  if (s_threadJoystick) {
497  SDL_LockMutex(s_mutexJoyStickEnum);
498  s_bJoystickThreadQuit = SDL_TRUE;
499  SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
500  SDL_UnlockMutex(s_mutexJoyStickEnum);
501  SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
502 
503  SDL_DestroyMutex(s_mutexJoyStickEnum);
504  SDL_DestroyCond(s_condJoystickThread);
505  s_condJoystickThread= NULL;
506  s_mutexJoyStickEnum = NULL;
507  s_threadJoystick = NULL;
508  }
509 
512 
513  s_bDeviceAdded = SDL_FALSE;
514  s_bDeviceRemoved = SDL_FALSE;
515 }
516 
517 /* return the stable device guid for this device index */
519 SDL_SYS_JoystickGetDeviceGUID(int device_index)
520 {
522  int index;
523 
524  for (index = device_index; index > 0; index--)
525  device = device->pNext;
526 
527  return device->guid;
528 }
529 
531 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
532 {
533  return joystick->hwdata->guid;
534 }
535 
536 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
537 
538 /* 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:547
GLuint64EXT * result
int SDL_SYS_NumJoysticks()
GLuint GLsizei const GLchar * message
int SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
struct JoyStick_DeviceData * pNext
#define SDL_CreateMutex
void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
int SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
int SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance)
#define SDL_DestroyCond
int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
void SDL_SYS_JoystickQuit(void)
#define SDL_zerop(x)
Definition: SDL_stdinc.h:360
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
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:72
HRESULT WIN_CoInitialize(void)
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:143
void SDL_free(void *mem)
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
void SDL_SYS_JoystickDetect()
SDL_bool SDL_XINPUT_Enabled(void)
void SDL_PrivateJoystickAdded(int device_index)
Definition: SDL_joystick.c:501
#define SDL_zero(x)
Definition: SDL_stdinc.h:359
const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index)
#define SDL_CreateCond
#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)
#define SDL_Delay
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
int SDL_SYS_JoystickInit(void)
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:130
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_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_DINPUT_JoystickQuit(void)