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