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. */
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 */
62static SDL_bool s_bDeviceAdded = SDL_FALSE;
63static SDL_bool s_bDeviceRemoved = SDL_FALSE;
64static SDL_cond *s_condJoystickThread = NULL;
65static SDL_mutex *s_mutexJoyStickEnum = NULL;
66static SDL_Thread *s_threadJoystick = NULL;
67static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
68
69JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */
70
71static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
72
73#ifdef __WINRT__
74
75typedef struct
76{
77 int unused;
78} SDL_DeviceNotificationData;
79
80static void
81SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
82{
83}
84
85static int
86SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
87{
88 return 0;
89}
90
91static SDL_bool
92SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
93{
94 return SDL_FALSE;
95}
96
97#else /* !__WINRT__ */
98
99typedef 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 */
111static LRESULT CALLBACK
112SDL_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
136static void
137SDL_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
152static int
153SDL_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
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
194static SDL_bool
195SDL_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
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 }
213 return (lastret != -1) ? SDL_TRUE : SDL_FALSE;
214}
215
216#endif /* __WINRT__ */
217
218/* Function/thread to scan the system for joysticks. */
219static int
220SDL_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;
276 device->nInstanceID = SDL_GetNextJoystickInstanceID();
277 device->pNext = SYS_Joystick;
279
280 s_bDeviceAdded = SDL_TRUE;
281}
282
283static void WINDOWS_JoystickDetect(void);
284static 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 */
290static int
291WINDOWS_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 */
318static int
319WINDOWS_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 */
332static void
333WINDOWS_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;
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
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 */
399static const char *
400WINDOWS_JoystickGetDeviceName(int device_index)
401{
403
404 for (; device_index > 0; device_index--)
405 device = device->pNext;
406
407 return device->joystickname;
408}
409
410static int
411WINDOWS_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 */
423static SDL_JoystickGUID
424WINDOWS_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 */
436static SDL_JoystickID
437WINDOWS_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 */
453static int
454WINDOWS_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
478static int
479WINDOWS_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
488static void
489WINDOWS_JoystickUpdate(SDL_Joystick * joystick)
490{
491 if (!joystick->hwdata) {
492 return;
493 }
494
495 if (joystick->hwdata->bXInputDevice) {
497 } else {
499 }
500}
501
502/* Function to close a joystick after use */
503static void
504WINDOWS_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 */
516static void
517WINDOWS_JoystickQuit(void)
518{
520
521 while (device) {
522 JoyStick_DeviceData *device_next = device->pNext;
523 SDL_free(device->joystickname);
525 device = device_next;
526 }
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: */
int SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance)
int SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance)
int SDL_DINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
void SDL_DINPUT_JoystickQuit(void)
int SDL_DINPUT_JoystickInit(void)
void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick)
int SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
void SDL_DINPUT_JoystickUpdate(SDL_Joystick *joystick)
#define S_OK
Definition: SDL_directx.h:47
#define SDL_GetThreadID
#define SDL_CondBroadcast
#define SDL_CreateCond
#define SDL_LockMutex
#define SDL_malloc
#define SDL_CreateMutex
#define SDL_free
#define SDL_CondWaitTimeout
#define SDL_WaitThread
#define SDL_DestroyCond
#define SDL_DestroyMutex
#define SDL_UnlockMutex
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:805
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:755
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:163
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLuint64EXT * result
GLuint index
GLuint GLsizei const GLchar * message
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
uint32_t Uint32
Definition: SDL_stdinc.h:203
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
uint16_t Uint16
Definition: SDL_stdinc.h:191
uint8_t Uint8
Definition: SDL_stdinc.h:179
SDL_JoystickDriver SDL_WINDOWS_JoystickDriver
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
HRESULT WIN_CoInitialize(void)
void WIN_CoUninitialize(void)
int WIN_SetError(const char *prefix)
void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
JoyStick_DeviceData * SYS_Joystick
int SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
int SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
void SDL_XINPUT_JoystickQuit(void)
int SDL_XINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
SDL_bool SDL_XINPUT_Enabled(void)
void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick)
int SDL_XINPUT_JoystickInit(void)
#define NULL
Definition: begin_code.h:167
static SDL_AudioDeviceID device
Definition: loopwave.c:37
DIDEVICEINSTANCE dxdevice
struct JoyStick_DeviceData * pNext
static SDL_mutex * mutex
Definition: testlock.c:23
static int available()
Definition: video.c:356