1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 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 */
22 /*
23 RAWINPUT Joystick API for better handling XInput-capable devices on Windows.
24
25 XInput is limited to 4 devices.
26 Windows.Gaming.Input does not get inputs from XBox One controllers when not in the foreground.
27 DirectInput does not get inputs from XBox One controllers when not in the foreground, nor rumble or accurate triggers.
28 RawInput does not get rumble or accurate triggers.
29
30 So, combine them as best we can!
31 */
32 #include "../../SDL_internal.h"
33
34 #if SDL_JOYSTICK_RAWINPUT
35
36 #include "SDL_assert.h"
37 #include "SDL_endian.h"
38 #include "SDL_hints.h"
39 #include "../SDL_sysjoystick.h"
40 #include "../../core/windows/SDL_windows.h"
41 #include "../hidapi/SDL_hidapijoystick_c.h"
42
43 #ifndef SDL_JOYSTICK_HIDAPI_XBOX360
44 #error RAWINPUT requires the XBOX360 HIDAPI driver
45 #endif
46
47 #ifndef RIDEV_EXINPUTSINK
48 #define RIDEV_EXINPUTSINK 0x00001000
49 #define RIDEV_DEVNOTIFY 0x00002000
50 #endif
51
52 #ifndef WM_INPUT_DEVICE_CHANGE
53 #define WM_INPUT_DEVICE_CHANGE 0x00FE
54 #endif
55 #ifndef WM_INPUT
56 #define WM_INPUT 0x00FF
57 #endif
58 #ifndef GIDC_ARRIVAL
59 #define GIDC_ARRIVAL 1
60 #define GIDC_REMOVAL 2
61 #endif
62
63
64 /* #define DEBUG_RAWINPUT */
65
66 #define USB_PACKET_LENGTH 64
67
68 #define SDL_callocStruct(type) (type *)SDL_calloc(1, sizeof(type))
69 #define SDL_callocStructs(type, count) (type *)SDL_calloc((count), sizeof(type))
70
71 #define USAGE_PAGE_GENERIC_DESKTOP 0x0001
72 #define USAGE_JOYSTICK 0x0004
73 #define USAGE_GAMEPAD 0x0005
74 #define USAGE_MULTIAXISCONTROLLER 0x0008
75
76
77 /* external variables referenced. */
78 extern HWND SDL_HelperWindow;
79
80
81 static SDL_HIDAPI_DeviceDriver *SDL_RAWINPUT_drivers[] = {
82 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
83 &SDL_HIDAPI_DriverXbox360,
84 #endif
85 };
86
87 static SDL_bool SDL_RAWINPUT_inited = SDL_FALSE;
88 static int SDL_RAWINPUT_numjoysticks = 0;
89 static SDL_bool SDL_RAWINPUT_need_pump = SDL_TRUE;
90
91 static void RAWINPUT_JoystickDetect(void);
92 static void RAWINPUT_PumpMessages(void);
93 static SDL_bool RAWINPUT_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version);
94 static void RAWINPUT_JoystickClose(SDL_Joystick * joystick);
95
96 typedef struct _SDL_RAWINPUT_Device
97 {
98 SDL_atomic_t refcount;
99 char *name;
100 Uint16 vendor_id;
101 Uint16 product_id;
102 Uint16 version;
103 SDL_JoystickGUID guid;
104 Uint16 usage_page;
105 Uint16 usage;
106 SDL_HIDAPI_Device hiddevice;
107 SDL_HIDAPI_DeviceDriver *driver;
108
109 HANDLE hDevice;
110 SDL_Joystick *joystick;
111 SDL_JoystickID joystick_id;
112
113 struct _SDL_RAWINPUT_Device *next;
114 } SDL_RAWINPUT_Device;
115
116 struct joystick_hwdata
117 {
118 void *reserved; /* reserving a value here to ensure the new SDL_hidapijoystick.c code never dereferences this */
119 SDL_RAWINPUT_Device *device;
120 };
121
122 SDL_RAWINPUT_Device *SDL_RAWINPUT_devices;
123
124 static const Uint16 subscribed_devices[] = {
125 USAGE_GAMEPAD,
126 /* Don't need Joystick for any devices we're handling here (XInput-capable)
127 USAGE_JOYSTICK,
128 USAGE_MULTIAXISCONTROLLER,
129 */
130 };
131
RAWINPUT_AllXInputDevicesSupported()132 SDL_bool RAWINPUT_AllXInputDevicesSupported() {
133 UINT i, device_count = 0;
134 PRAWINPUTDEVICELIST devices;
135 SDL_bool any_unsupported = SDL_FALSE;
136
137 if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!device_count)) {
138 return SDL_FALSE;
139 }
140
141 devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * device_count);
142 if (devices == NULL) {
143 return SDL_FALSE;
144 }
145
146 if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) {
147 SDL_free(devices);
148 return SDL_FALSE;
149 }
150
151 for (i = 0; i < device_count; i++) {
152 RID_DEVICE_INFO rdi;
153 char devName[128];
154 UINT rdiSize = sizeof(rdi);
155 UINT nameSize = SDL_arraysize(devName);
156
157 rdi.cbSize = sizeof(rdi);
158 if ((devices[i].dwType == RIM_TYPEHID) &&
159 (GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) &&
160 (GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) &&
161 (SDL_strstr(devName, "IG_") != NULL)
162 ) {
163 /* XInput-capable */
164 if (!RAWINPUT_IsDeviceSupported((Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber)) {
165 /* But not supported, probably Valve virtual controller */
166 any_unsupported = SDL_TRUE;
167 }
168 }
169 }
170 SDL_free(devices);
171 if (any_unsupported) {
172 /* This happens with Valve virtual controllers that shows up in the RawInputDeviceList, but do not
173 generate WM_INPUT events, so we must use XInput or DInput to read from it, and with XInput if we
174 have some supported and some not, we can't easily tell which device is actually showing up in
175 RawInput, so we must just disable RawInput for now. Additionally, if these unsupported devices
176 are locally connected, they still show up in RawInput under a *different* HID path, with
177 different vendor/product IDs, so there's no way to reconcile. */
178 #ifdef DEBUG_RAWINPUT
179 SDL_Log("Found some supported and some unsupported XInput devices, disabling RawInput\n");
180 #endif
181 return SDL_FALSE;
182 }
183 return SDL_TRUE;
184 }
185
186 static int
RAWINPUT_JoystickInit(void)187 RAWINPUT_JoystickInit(void)
188 {
189 int ii;
190 RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)];
191 SDL_assert(!SDL_RAWINPUT_inited);
192 SDL_assert(SDL_HelperWindow);
193
194 if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE))
195 return -1;
196
197 if (!RAWINPUT_AllXInputDevicesSupported()) {
198 return -1;
199 }
200
201 for (ii = 0; ii < SDL_arraysize(subscribed_devices); ii++) {
202 rid[ii].usUsagePage = USAGE_PAGE_GENERIC_DESKTOP;
203 rid[ii].usUsage = subscribed_devices[ii];
204 rid[ii].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; /* Receive messages when in background, including device add/remove */
205 rid[ii].hwndTarget = SDL_HelperWindow;
206 }
207
208 if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) {
209 SDL_SetError("Couldn't initialize RAWINPUT");
210 return -1;
211 }
212
213 SDL_RAWINPUT_inited = SDL_TRUE;
214
215 RAWINPUT_JoystickDetect();
216 RAWINPUT_PumpMessages();
217 return 0;
218 }
219
220 static int
RAWINPUT_JoystickGetCount(void)221 RAWINPUT_JoystickGetCount(void)
222 {
223 return SDL_RAWINPUT_numjoysticks;
224 }
225
226 static SDL_RAWINPUT_Device *
RAWINPUT_AcquireDevice(SDL_RAWINPUT_Device * device)227 RAWINPUT_AcquireDevice(SDL_RAWINPUT_Device *device)
228 {
229 SDL_AtomicIncRef(&device->refcount);
230 return device;
231 }
232
233 static void
RAWINPUT_ReleaseDevice(SDL_RAWINPUT_Device * device)234 RAWINPUT_ReleaseDevice(SDL_RAWINPUT_Device *device)
235 {
236 if (SDL_AtomicDecRef(&device->refcount)) {
237 SDL_free(device->name);
238 SDL_free(device);
239 }
240 }
241
242 static SDL_RAWINPUT_Device *
RAWINPUT_DeviceFromHandle(HANDLE hDevice)243 RAWINPUT_DeviceFromHandle(HANDLE hDevice)
244 {
245 SDL_RAWINPUT_Device *curr;
246
247 for (curr = SDL_RAWINPUT_devices; curr; curr = curr->next) {
248 if (curr->hDevice == hDevice)
249 return curr;
250 }
251 return NULL;
252 }
253
254 static SDL_HIDAPI_DeviceDriver *
RAWINPUT_GetDeviceDriver(SDL_RAWINPUT_Device * device)255 RAWINPUT_GetDeviceDriver(SDL_RAWINPUT_Device *device)
256 {
257 int i;
258 SDL_GameControllerType type;
259
260 if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
261 return NULL;
262 }
263
264 if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
265 return NULL;
266 }
267 if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
268 return NULL;
269 }
270
271 type = SDL_GetJoystickGameControllerType("", device->vendor_id, device->product_id, -1, 0, 0, 0);
272
273 for (i = 0; i < SDL_arraysize(SDL_RAWINPUT_drivers); ++i) {
274 SDL_HIDAPI_DeviceDriver *driver = SDL_RAWINPUT_drivers[i];
275 if (/*driver->enabled && */driver->IsSupportedDevice(NULL, type, device->vendor_id, device->product_id, device->version, -1, 0, 0, 0)) {
276 return driver;
277 }
278 }
279 return NULL;
280 }
281
282 static void
RAWINPUT_AddDevice(HANDLE hDevice)283 RAWINPUT_AddDevice(HANDLE hDevice)
284 {
285 #define CHECK(exp) { if(!(exp)) goto err; }
286 SDL_RAWINPUT_Device *device = NULL;
287 RID_DEVICE_INFO rdi;
288 UINT rdi_size = sizeof(rdi);
289 char dev_name[128];
290 UINT name_size = SDL_arraysize(dev_name);
291 const char *name;
292 SDL_RAWINPUT_Device *curr, *last;
293
294 SDL_assert(!RAWINPUT_DeviceFromHandle(hDevice));
295
296 /* Figure out what kind of device it is */
297 CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICEINFO, &rdi, &rdi_size) != (UINT)-1);
298 CHECK(rdi.dwType == RIM_TYPEHID);
299
300 /* Get the device "name" (HID Path) */
301 CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICENAME, dev_name, &name_size) != (UINT)-1);
302 /* Only take XInput-capable devices */
303 CHECK(SDL_strstr(dev_name, "IG_") != NULL);
304
305 CHECK(device = SDL_callocStruct(SDL_RAWINPUT_Device));
306 device->hDevice = hDevice;
307 device->vendor_id = (Uint16)rdi.hid.dwVendorId;
308 device->product_id = (Uint16)rdi.hid.dwProductId;
309 device->version = (Uint16)rdi.hid.dwVersionNumber;
310 device->usage = rdi.hid.usUsage;
311 device->usage_page = rdi.hid.usUsagePage;
312
313 {
314 const Uint16 vendor = device->vendor_id;
315 const Uint16 product = device->product_id;
316 const Uint16 version = device->version;
317 Uint16 *guid16 = (Uint16 *)device->guid.data;
318
319 *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
320 *guid16++ = 0;
321 *guid16++ = SDL_SwapLE16(vendor);
322 *guid16++ = 0;
323 *guid16++ = SDL_SwapLE16(product);
324 *guid16++ = 0;
325 *guid16++ = SDL_SwapLE16(version);
326 *guid16++ = 0;
327
328 /* Note that this is a RAWINPUT device for special handling elsewhere */
329 device->guid.data[14] = 'r';
330 device->guid.data[15] = 0;
331 }
332
333 CHECK(device->driver = RAWINPUT_GetDeviceDriver(device));
334
335 name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
336 if (name) {
337 device->name = SDL_strdup(name);
338 } else {
339 char *manufacturer_string = NULL;
340 char *product_string = NULL;
341 HMODULE hHID;
342
343 hHID = LoadLibrary( TEXT( "hid.dll" ) );
344 if (hHID) {
345 typedef BOOLEAN (WINAPI * HidD_GetStringFunc)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength);
346 HidD_GetStringFunc GetManufacturerString = (HidD_GetStringFunc)GetProcAddress(hHID, "HidD_GetManufacturerString");
347 HidD_GetStringFunc GetProductString = (HidD_GetStringFunc)GetProcAddress(hHID, "HidD_GetProductString");
348 if (GetManufacturerString && GetProductString) {
349 HANDLE hFile = CreateFileA(dev_name, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
350 if (hFile != INVALID_HANDLE_VALUE) {
351 WCHAR string[128];
352
353 if (GetManufacturerString(hFile, string, sizeof(string))) {
354 manufacturer_string = WIN_StringToUTF8(string);
355 }
356 if (GetProductString(hFile, string, sizeof(string))) {
357 product_string = WIN_StringToUTF8(string);
358 }
359 CloseHandle(hFile);
360 }
361 }
362 FreeLibrary(hHID);
363 }
364
365 device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
366
367 if (manufacturer_string) {
368 SDL_free(manufacturer_string);
369 }
370 if (product_string) {
371 SDL_free(product_string);
372 }
373 }
374
375 #ifdef DEBUG_RAWINPUT
376 SDL_Log("Adding RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle 0x%.8x\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice);
377 #endif
378
379 /* Add it to the list */
380 RAWINPUT_AcquireDevice(device);
381 for (curr = SDL_RAWINPUT_devices, last = NULL; curr; last = curr, curr = curr->next) {
382 continue;
383 }
384 if (last) {
385 last->next = device;
386 } else {
387 SDL_RAWINPUT_devices = device;
388 }
389
390 ++SDL_RAWINPUT_numjoysticks;
391 /* HIDAPI_JoystickConnected calls SDL_GetNextJoystickInstanceID() and SDL_PrivateJoystickAdded(), and calls back in to us, so
392 the device list must be updated before calling this. */
393 CHECK(HIDAPI_JoystickConnected(&device->hiddevice, &device->joystick_id, SDL_TRUE));
394 /* Old: CHECK(device->driver->InitDevice(&device->hiddevice)); But, we need the joystick_id */
395
396 return;
397
398 err:
399 if (device) {
400 if (device->name)
401 SDL_free(device->name);
402 SDL_free(device);
403 }
404 }
405
406 static void
RAWINPUT_DelDevice(SDL_RAWINPUT_Device * device,SDL_bool send_event)407 RAWINPUT_DelDevice(SDL_RAWINPUT_Device *device, SDL_bool send_event)
408 {
409 SDL_RAWINPUT_Device *curr, *last;
410 for (curr = SDL_RAWINPUT_devices, last = NULL; curr; last = curr, curr = curr->next) {
411 if (curr == device) {
412 if (last) {
413 last->next = curr->next;
414 } else {
415 SDL_RAWINPUT_devices = curr->next;
416 }
417 --SDL_RAWINPUT_numjoysticks;
418
419 /* Calls SDL_PrivateJoystickRemoved() */
420 HIDAPI_JoystickDisconnected(&device->hiddevice, device->joystick_id, SDL_TRUE);
421
422 #ifdef DEBUG_RAWINPUT
423 SDL_Log("Removing RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle 0x%.8x\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice);
424 #endif
425 RAWINPUT_ReleaseDevice(device);
426 return;
427 }
428 }
429 }
430
431 static void
RAWINPUT_PumpMessages(void)432 RAWINPUT_PumpMessages(void)
433 {
434 if (SDL_RAWINPUT_need_pump) {
435 MSG msg;
436 while (PeekMessage(&msg, SDL_HelperWindow, WM_INPUT, WM_INPUT, PM_REMOVE)) {
437 TranslateMessage(&msg);
438 DispatchMessage(&msg);
439 }
440 SDL_RAWINPUT_need_pump = SDL_FALSE;
441 }
442 }
443
444 static void
RAWINPUT_UpdateDeviceList(void)445 RAWINPUT_UpdateDeviceList(void)
446 {
447 MSG msg;
448 /* In theory, want only WM_INPUT_DEVICE_CHANGE messages here, but PeekMessage returns nothing unless you also ask
449 for WM_INPUT */
450 while (PeekMessage(&msg, SDL_HelperWindow, WM_INPUT_DEVICE_CHANGE, WM_INPUT, PM_REMOVE)) {
451 TranslateMessage(&msg);
452 DispatchMessage(&msg);
453 }
454 }
455
456 static SDL_bool
RAWINPUT_IsDeviceSupported(Uint16 vendor_id,Uint16 product_id,Uint16 version)457 RAWINPUT_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version)
458 {
459 int i;
460
461 SDL_GameControllerType type = SDL_GetJoystickGameControllerType("", vendor_id, product_id, -1, 0, 0, 0);
462
463 for (i = 0; i < SDL_arraysize(SDL_RAWINPUT_drivers); ++i) {
464 SDL_HIDAPI_DeviceDriver *driver = SDL_RAWINPUT_drivers[i];
465 /* Ignoring driver->enabled here, and elsewhere in this file, as the if the driver is enabled by disabling HID,
466 we still want RawInput to use it. If we end up with more than one RawInput driver, we may need to rework
467 how the hints interact (separate enabled state, perhaps).
468 */
469 if (/*driver->enabled && */driver->IsSupportedDevice(NULL, type, vendor_id, product_id, version, -1, 0, 0, 0)) {
470 return SDL_TRUE;
471 }
472 }
473 return SDL_FALSE;
474 }
475
476 SDL_bool
RAWINPUT_IsDevicePresent(Uint16 vendor_id,Uint16 product_id,Uint16 version)477 RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
478 {
479 SDL_RAWINPUT_Device *device;
480
481 /* Don't update the device list for devices we know aren't supported */
482 if (!RAWINPUT_IsDeviceSupported(vendor_id, product_id, version)) {
483 return SDL_FALSE;
484 }
485
486 /* Make sure the device list is completely up to date when we check for device presence */
487 RAWINPUT_UpdateDeviceList();
488
489 device = SDL_RAWINPUT_devices;
490 while (device) {
491 if (device->vendor_id == vendor_id && device->product_id == product_id) {
492 return SDL_TRUE;
493 }
494 device = device->next;
495 }
496 return SDL_FALSE;
497 }
498
499 static void
RAWINPUT_JoystickDetect(void)500 RAWINPUT_JoystickDetect(void)
501 {
502 int i;
503 /* Just ensure the window's add/remove messages have been pumped */
504 RAWINPUT_UpdateDeviceList();
505
506 for (i = 0; i < SDL_arraysize(SDL_RAWINPUT_drivers); ++i) {
507 SDL_HIDAPI_DeviceDriver *driver = SDL_RAWINPUT_drivers[i];
508 /* Running PostUpdate here only if it's *not* enabled (and ran elsewhere) */
509 if (!driver->enabled && driver->PostUpdate) {
510 driver->PostUpdate();
511 }
512 }
513 SDL_RAWINPUT_need_pump = SDL_TRUE;
514 }
515
516 static SDL_RAWINPUT_Device *
RAWINPUT_GetJoystickByIndex(int device_index,SDL_JoystickID * pJoystickID)517 RAWINPUT_GetJoystickByIndex(int device_index, SDL_JoystickID *pJoystickID)
518 {
519 SDL_RAWINPUT_Device *device = SDL_RAWINPUT_devices;
520 while (device) {
521 if (device->driver) {
522 SDL_assert(device->hiddevice.num_joysticks == 1);
523 if (device_index < device->hiddevice.num_joysticks) {
524 if (pJoystickID) {
525 *pJoystickID = device->hiddevice.joysticks[device_index];
526 }
527 return device;
528 }
529 device_index -= device->hiddevice.num_joysticks;
530 }
531 device = device->next;
532 }
533 return NULL;
534 }
535
536 static const char *
RAWINPUT_JoystickGetDeviceName(int device_index)537 RAWINPUT_JoystickGetDeviceName(int device_index)
538 {
539 return RAWINPUT_GetJoystickByIndex(device_index, NULL)->name;
540 }
541
542 static int
RAWINPUT_JoystickGetDevicePlayerIndex(int device_index)543 RAWINPUT_JoystickGetDevicePlayerIndex(int device_index)
544 {
545 SDL_RAWINPUT_Device *device;
546 SDL_JoystickID instance_id;
547 int player_index = -1;
548
549 device = RAWINPUT_GetJoystickByIndex(device_index, &instance_id);
550 if (device && device->driver) {
551 player_index = device->driver->GetDevicePlayerIndex(&device->hiddevice, instance_id);
552 }
553
554 return player_index;
555 }
556
557 static void
RAWINPUT_JoystickSetDevicePlayerIndex(int device_index,int player_index)558 RAWINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index)
559 {
560 SDL_RAWINPUT_Device *device;
561 SDL_JoystickID instance_id;
562
563 device = RAWINPUT_GetJoystickByIndex(device_index, &instance_id);
564 if (device) {
565 device->driver->SetDevicePlayerIndex(&device->hiddevice, instance_id, player_index);
566 }
567 }
568
569
570 static SDL_JoystickGUID
RAWINPUT_JoystickGetDeviceGUID(int device_index)571 RAWINPUT_JoystickGetDeviceGUID(int device_index)
572 {
573 return RAWINPUT_GetJoystickByIndex(device_index, NULL)->guid;
574 }
575
576 static SDL_JoystickID
RAWINPUT_JoystickGetDeviceInstanceID(int device_index)577 RAWINPUT_JoystickGetDeviceInstanceID(int device_index)
578 {
579 SDL_JoystickID instance_id = -1;
580 RAWINPUT_GetJoystickByIndex(device_index, &instance_id);
581 return instance_id;
582 }
583
584 static int
RAWINPUT_JoystickOpen(SDL_Joystick * joystick,int device_index)585 RAWINPUT_JoystickOpen(SDL_Joystick * joystick, int device_index)
586 {
587 SDL_RAWINPUT_Device *device = RAWINPUT_GetJoystickByIndex(device_index, NULL);
588 struct joystick_hwdata *hwdata = SDL_callocStruct(struct joystick_hwdata);
589
590 if (!hwdata) {
591 return SDL_OutOfMemory();
592 }
593
594 if (!device->driver->OpenJoystick(&device->hiddevice, joystick)) {
595 SDL_free(hwdata);
596 return -1;
597 }
598
599 hwdata->reserved = (void*)-1; /* crash if some code slips by that tries to use this */
600 hwdata->device = RAWINPUT_AcquireDevice(device);
601 device->joystick = joystick;
602
603 joystick->hwdata = hwdata;
604
605 return 0;
606 }
607
608 static int
RAWINPUT_JoystickRumble(SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)609 RAWINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
610 {
611 struct joystick_hwdata *hwdata = joystick->hwdata;
612 SDL_RAWINPUT_Device *device = hwdata->device;
613
614 return device->driver->RumbleJoystick(&device->hiddevice, joystick, low_frequency_rumble, high_frequency_rumble);
615 }
616
617 static void
RAWINPUT_JoystickUpdate(SDL_Joystick * joystick)618 RAWINPUT_JoystickUpdate(SDL_Joystick * joystick)
619 {
620 struct joystick_hwdata *hwdata;
621 SDL_RAWINPUT_Device *device;
622 /* Ensure data messages have been pumped */
623 RAWINPUT_PumpMessages();
624 hwdata = joystick->hwdata;
625 device = hwdata->device;
626
627 device->driver->UpdateDevice(&device->hiddevice);
628 }
629
630 static void
RAWINPUT_JoystickClose(SDL_Joystick * joystick)631 RAWINPUT_JoystickClose(SDL_Joystick * joystick)
632 {
633 struct joystick_hwdata *hwdata = joystick->hwdata;
634
635 if (hwdata) {
636 SDL_RAWINPUT_Device *device;
637
638 device = hwdata->device;
639 if (device) {
640 SDL_assert(device->joystick == joystick);
641 device->driver->CloseJoystick(&device->hiddevice, joystick);
642 device->joystick = NULL;
643 RAWINPUT_ReleaseDevice(device);
644 }
645
646 SDL_free(hwdata);
647 joystick->hwdata = NULL;
648 }
649 }
650
RAWINPUT_WindowProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)651 LRESULT RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
652 {
653 if (!SDL_RAWINPUT_inited)
654 return -1;
655
656 switch (msg)
657 {
658 case WM_INPUT_DEVICE_CHANGE:
659 {
660 HANDLE hDevice = (HANDLE)lParam;
661 switch (wParam) {
662 case GIDC_ARRIVAL:
663 RAWINPUT_AddDevice(hDevice);
664 break;
665 case GIDC_REMOVAL: {
666 SDL_RAWINPUT_Device *device;
667 device = RAWINPUT_DeviceFromHandle(hDevice);
668 if (device) {
669 RAWINPUT_DelDevice(device, SDL_TRUE);
670 }
671 } break;
672 default:
673 return 0;
674 }
675 }
676 return 0;
677 case WM_INPUT:
678 {
679 Uint8 data[sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + USB_PACKET_LENGTH];
680 UINT buffer_size = SDL_arraysize(data);
681
682 if ((int)GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &buffer_size, sizeof(RAWINPUTHEADER)) > 0) {
683 PRAWINPUT raw_input = (PRAWINPUT)data;
684 SDL_RAWINPUT_Device *device = RAWINPUT_DeviceFromHandle(raw_input->header.hDevice);
685 if (device) {
686 SDL_HIDAPI_DeviceDriver *driver = device->driver;
687 SDL_Joystick *joystick = device->joystick;
688 if (joystick) {
689 driver->HandleStatePacketFromRAWINPUT(&device->hiddevice, joystick, &raw_input->data.hid.bRawData[1], raw_input->data.hid.dwSizeHid - 1);
690 }
691 }
692 }
693 }
694 return 0;
695 }
696 return -1;
697 }
698
699 static void
RAWINPUT_JoystickQuit(void)700 RAWINPUT_JoystickQuit(void)
701 {
702 int ii;
703 RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)];
704
705 if (!SDL_RAWINPUT_inited)
706 return;
707
708 for (ii = 0; ii < SDL_arraysize(subscribed_devices); ii++) {
709 rid[ii].usUsagePage = USAGE_PAGE_GENERIC_DESKTOP;
710 rid[ii].usUsage = subscribed_devices[ii];
711 rid[ii].dwFlags = RIDEV_REMOVE;
712 rid[ii].hwndTarget = NULL;
713 }
714
715 if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) {
716 SDL_Log("Couldn't un-register RAWINPUT");
717 }
718
719 while (SDL_RAWINPUT_devices) {
720 RAWINPUT_DelDevice(SDL_RAWINPUT_devices, SDL_FALSE);
721 }
722
723 SDL_RAWINPUT_numjoysticks = 0;
724
725 SDL_RAWINPUT_inited = SDL_FALSE;
726 }
727
728 static SDL_bool
RAWINPUT_JoystickGetGamepadMapping(int device_index,SDL_GamepadMapping * out)729 RAWINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
730 {
731 return SDL_FALSE;
732 }
733
734 SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver =
735 {
736 RAWINPUT_JoystickInit,
737 RAWINPUT_JoystickGetCount,
738 RAWINPUT_JoystickDetect,
739 RAWINPUT_JoystickGetDeviceName,
740 RAWINPUT_JoystickGetDevicePlayerIndex,
741 RAWINPUT_JoystickSetDevicePlayerIndex,
742 RAWINPUT_JoystickGetDeviceGUID,
743 RAWINPUT_JoystickGetDeviceInstanceID,
744 RAWINPUT_JoystickOpen,
745 RAWINPUT_JoystickRumble,
746 RAWINPUT_JoystickUpdate,
747 RAWINPUT_JoystickClose,
748 RAWINPUT_JoystickQuit,
749 RAWINPUT_JoystickGetGamepadMapping
750 };
751
752 #endif /* SDL_JOYSTICK_RAWINPUT */
753
754 /* vi: set ts=4 sw=4 expandtab: */
755