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_thread.h"
39 #include "SDL_timer.h"
40 #include "SDL_mutex.h"
41 #include "SDL_events.h"
42 #include "SDL_hints.h"
43 #include "SDL_joystick.h"
44 #include "../SDL_sysjoystick.h"
45 #if !SDL_EVENTS_DISABLED
46 #include "../../events/SDL_events_c.h"
47 #endif
48 #include "../../core/windows/SDL_windows.h"
49 #if !defined(__WINRT__)
50 #include <dbt.h>
51 #endif
52 
53 #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
54 #include "SDL_windowsjoystick_c.h"
55 #include "SDL_dinputjoystick_c.h"
56 #include "SDL_xinputjoystick_c.h"
57 
58 #include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */
59 #include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */
60 
61 
62 #ifndef DEVICE_NOTIFY_WINDOW_HANDLE
63 #define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
64 #endif
65 
66 /* local variables */
67 static SDL_bool s_bDeviceAdded = SDL_FALSE;
68 static SDL_bool s_bDeviceRemoved = SDL_FALSE;
69 static SDL_JoystickID s_nInstanceID = -1;
70 static SDL_cond *s_condJoystickThread = NULL;
71 static SDL_mutex *s_mutexJoyStickEnum = NULL;
72 static SDL_Thread *s_threadJoystick = NULL;
73 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
74 
75 JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */
76 
77 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
78 
79 #ifdef __WINRT__
80 
81 typedef struct
82 {
83  int unused;
84 } SDL_DeviceNotificationData;
85 
86 static void
87 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
88 {
89 }
90 
91 static int
92 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
93 {
94  return 0;
95 }
96 
97 static void
98 SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
99 {
100 }
101 
102 #else /* !__WINRT__ */
103 
104 typedef struct
105 {
106  HRESULT coinitialized;
107  WNDCLASSEX wincl;
108  HWND messageWindow;
109  HDEVNOTIFY hNotify;
110 } SDL_DeviceNotificationData;
111 
112 
113 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */
114 static LRESULT CALLBACK
115 SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
116 {
117  switch (message) {
118  case WM_DEVICECHANGE:
119  switch (wParam) {
120  case DBT_DEVICEARRIVAL:
121  if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
122  s_bWindowsDeviceChanged = SDL_TRUE;
123  }
124  break;
125  case DBT_DEVICEREMOVECOMPLETE:
126  if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
127  s_bWindowsDeviceChanged = SDL_TRUE;
128  }
129  break;
130  }
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 void
196 SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
197 {
198  MSG msg;
199 
200  if (!data->messageWindow) {
201  return;
202  }
203 
204  while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) {
205  if (GetMessage(&msg, data->messageWindow, 0, 0) != 0) {
206  TranslateMessage(&msg);
207  DispatchMessage(&msg);
208  }
209  }
210 }
211 
212 #endif /* __WINRT__ */
213 
214 /* Function/thread to scan the system for joysticks. */
215 static int
216 SDL_JoystickThread(void *_data)
217 {
218  SDL_DeviceNotificationData notification_data;
219 
220 #if SDL_JOYSTICK_XINPUT
221  SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT];
222  SDL_zero(bOpenedXInputDevices);
223 #endif
224 
225  if (SDL_CreateDeviceNotification(&notification_data) < 0) {
226  return -1;
227  }
228 
229  SDL_LockMutex(s_mutexJoyStickEnum);
230  while (s_bJoystickThreadQuit == SDL_FALSE) {
231  SDL_bool bXInputChanged = SDL_FALSE;
232 
233  SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300);
234 
235  SDL_CheckDeviceNotification(&notification_data);
236 
237 #if SDL_JOYSTICK_XINPUT
238  if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
239  /* scan for any change in XInput devices */
240  Uint8 userId;
241  for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
242  XINPUT_CAPABILITIES capabilities;
243  const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
244  const SDL_bool available = (result == ERROR_SUCCESS);
245  if (bOpenedXInputDevices[userId] != available) {
246  bXInputChanged = SDL_TRUE;
247  bOpenedXInputDevices[userId] = available;
248  }
249  }
250  }
251 #endif /* SDL_JOYSTICK_XINPUT */
252 
253  if (s_bWindowsDeviceChanged || bXInputChanged) {
254  SDL_UnlockMutex(s_mutexJoyStickEnum); /* let main thread go while we SDL_Delay(). */
255  SDL_Delay(300); /* wait for direct input to find out about this device */
256  SDL_LockMutex(s_mutexJoyStickEnum);
257 
258  s_bDeviceRemoved = SDL_TRUE;
259  s_bDeviceAdded = SDL_TRUE;
260  s_bWindowsDeviceChanged = SDL_FALSE;
261  }
262  }
263  SDL_UnlockMutex(s_mutexJoyStickEnum);
264 
265  SDL_CleanupDeviceNotification(&notification_data);
266 
267  return 1;
268 }
269 
271 {
272  device->send_add_event = SDL_TRUE;
273  device->nInstanceID = ++s_nInstanceID;
274  device->pNext = SYS_Joystick;
275  SYS_Joystick = device;
276 
277  s_bDeviceAdded = SDL_TRUE;
278 }
279 
280 /* Function to scan the system for joysticks.
281  * Joystick 0 should be the system default joystick.
282  * It should return 0, or -1 on an unrecoverable fatal error.
283  */
284 int
286 {
287  if (SDL_DINPUT_JoystickInit() < 0) {
289  return -1;
290  }
291 
292  if (SDL_XINPUT_JoystickInit() < 0) {
294  return -1;
295  }
296 
297  s_mutexJoyStickEnum = SDL_CreateMutex();
298  s_condJoystickThread = SDL_CreateCond();
299  s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
300 
302 
303  if (!s_threadJoystick) {
304  s_bJoystickThreadQuit = SDL_FALSE;
305  /* spin up the thread to detect hotplug of devices */
306 #if defined(__WIN32__) && !defined(HAVE_LIBC)
307 #undef SDL_CreateThread
308 #if SDL_DYNAMIC_API
309  s_threadJoystick= SDL_CreateThread_REAL(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL);
310 #else
311  s_threadJoystick= SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL);
312 #endif
313 #else
314  s_threadJoystick = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL);
315 #endif
316  }
317  return SDL_SYS_NumJoysticks();
318 }
319 
320 /* return the number of joysticks that are connected right now */
321 int
323 {
324  int nJoysticks = 0;
326  while (device) {
327  nJoysticks++;
328  device = device->pNext;
329  }
330 
331  return nJoysticks;
332 }
333 
334 /* detect any new joysticks being inserted into the system */
335 void
337 {
338  JoyStick_DeviceData *pCurList = NULL;
339 #if !SDL_EVENTS_DISABLED
341 #endif
342 
343  /* only enum the devices if the joystick thread told us something changed */
344  if (!s_bDeviceAdded && !s_bDeviceRemoved) {
345  return; /* thread hasn't signaled, nothing to do right now. */
346  }
347 
348  SDL_LockMutex(s_mutexJoyStickEnum);
349 
350  s_bDeviceAdded = SDL_FALSE;
351  s_bDeviceRemoved = SDL_FALSE;
352 
353  pCurList = SYS_Joystick;
354  SYS_Joystick = NULL;
355 
356  /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
357  SDL_DINPUT_JoystickDetect(&pCurList);
358 
359  /* Look for XInput devices. Do this last, so they're first in the final list. */
360  SDL_XINPUT_JoystickDetect(&pCurList);
361 
362  SDL_UnlockMutex(s_mutexJoyStickEnum);
363 
364  while (pCurList) {
365  JoyStick_DeviceData *pListNext = NULL;
366 
367  if (pCurList->bXInputDevice) {
369  } else {
371  }
372 
373 #if !SDL_EVENTS_DISABLED
374  SDL_zero(event);
375  event.type = SDL_JOYDEVICEREMOVED;
376 
377  if (SDL_GetEventState(event.type) == SDL_ENABLE) {
378  event.jdevice.which = pCurList->nInstanceID;
379  if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
380  SDL_PushEvent(&event);
381  }
382  }
383 #endif /* !SDL_EVENTS_DISABLED */
384 
385  pListNext = pCurList->pNext;
386  SDL_free(pCurList->joystickname);
387  SDL_free(pCurList);
388  pCurList = pListNext;
389  }
390 
391  if (s_bDeviceAdded) {
392  JoyStick_DeviceData *pNewJoystick;
393  int device_index = 0;
394  s_bDeviceAdded = SDL_FALSE;
395  pNewJoystick = SYS_Joystick;
396  while (pNewJoystick) {
397  if (pNewJoystick->send_add_event) {
398  if (pNewJoystick->bXInputDevice) {
400  } else {
401  SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
402  }
403 
404 #if !SDL_EVENTS_DISABLED
405  SDL_zero(event);
406  event.type = SDL_JOYDEVICEADDED;
407 
408  if (SDL_GetEventState(event.type) == SDL_ENABLE) {
409  event.jdevice.which = device_index;
410  if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
411  SDL_PushEvent(&event);
412  }
413  }
414 #endif /* !SDL_EVENTS_DISABLED */
415  pNewJoystick->send_add_event = SDL_FALSE;
416  }
417  device_index++;
418  pNewJoystick = pNewJoystick->pNext;
419  }
420  }
421 }
422 
423 /* Function to get the device-dependent name of a joystick */
424 const char *
425 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
426 {
428 
429  for (; device_index > 0; device_index--)
430  device = device->pNext;
431 
432  return device->joystickname;
433 }
434 
435 /* Function to perform the mapping between current device instance and this joysticks instance id */
437 SDL_SYS_GetInstanceIdOfDeviceIndex(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 int
454 SDL_SYS_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 /* return true if this joystick is plugged in right now */
479 SDL_bool
480 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
481 {
482  return joystick->hwdata && !joystick->hwdata->removed;
483 }
484 
485 void
486 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
487 {
488  if (!joystick->hwdata || joystick->hwdata->removed) {
489  return;
490  }
491 
492  if (joystick->hwdata->bXInputDevice) {
493  SDL_XINPUT_JoystickUpdate(joystick);
494  } else {
495  SDL_DINPUT_JoystickUpdate(joystick);
496  }
497 
498  if (joystick->hwdata->removed) {
499  joystick->force_recentering = SDL_TRUE;
500  }
501 }
502 
503 /* Function to close a joystick after use */
504 void
505 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
506 {
507  if (joystick->hwdata->bXInputDevice) {
508  SDL_XINPUT_JoystickClose(joystick);
509  } else {
510  SDL_DINPUT_JoystickClose(joystick);
511  }
512 
513  SDL_free(joystick->hwdata);
514 }
515 
516 /* Function to perform any system-specific joystick related cleanup */
517 void
519 {
521 
522  while (device) {
523  JoyStick_DeviceData *device_next = device->pNext;
524  SDL_free(device->joystickname);
525  SDL_free(device);
526  device = device_next;
527  }
528  SYS_Joystick = NULL;
529 
530  if (s_threadJoystick) {
531  SDL_LockMutex(s_mutexJoyStickEnum);
532  s_bJoystickThreadQuit = SDL_TRUE;
533  SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
534  SDL_UnlockMutex(s_mutexJoyStickEnum);
535  SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
536 
537  SDL_DestroyMutex(s_mutexJoyStickEnum);
538  SDL_DestroyCond(s_condJoystickThread);
539  s_condJoystickThread= NULL;
540  s_mutexJoyStickEnum = NULL;
541  s_threadJoystick = NULL;
542  }
543 
546 }
547 
548 /* return the stable device guid for this device index */
550 SDL_SYS_JoystickGetDeviceGUID(int device_index)
551 {
553  int index;
554 
555  for (index = device_index; index > 0; index--)
556  device = device->pNext;
557 
558  return device->guid;
559 }
560 
562 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
563 {
564  return joystick->hwdata->guid;
565 }
566 
567 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
568 
569 /* vi: set ts=4 sw=4 expandtab: */
JoyStick_DeviceData * SYS_Joystick
#define SDL_LockMutex
int SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance)
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
#define SDL_ENABLE
Definition: SDL_events.h:718
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:356
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
SDL_bool
Definition: SDL_stdinc.h:126
int SDL_DINPUT_JoystickInit(void)
int SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
#define SDL_CondWaitTimeout
#define SDL_GetEventState(type)
Definition: SDL_events.h:731
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:139
struct _cl_event * event
void SDL_free(void *mem)
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
SDL_EventFilter SDL_EventOK
Definition: SDL_events.c:40
#define SDL_PushEvent
void SDL_SYS_JoystickDetect()
SDL_bool SDL_XINPUT_Enabled(void)
#define SDL_zero(x)
Definition: SDL_stdinc.h:355
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
#define SDL_CreateThread
int SDL_XINPUT_JoystickInit(void)
#define SDL_Delay
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
int SDL_SYS_JoystickInit(void)
void * SDL_EventOKParam
Definition: SDL_events.c:41
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
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)
General event structure.
Definition: SDL_events.h:521
#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)
Uint32 type
Definition: SDL_events.h:523
#define SDL_WaitThread
void SDL_DINPUT_JoystickQuit(void)