1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 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 #include "xinputcheck.h"
23
24 #include <string.h>
25 #include <stdlib.h>
26
27 // XInputCheck is an XInput check abstracted away from SDL.
28 //
29 // If you need to update this file, please arrange the code such that
30 // it is possible to copy/paste directly from SDL. This makes code
31 // sharing easier! Right now, the SDL_IsXInputDevice is directly
32 // copied from SDL.
33 //
34 // Below, we have a few preprocessor defines, that allow the SDL functions
35 // we use, to run in our environment.
36 //
37 // After that, there is an implementation of the SDL_XINPUT_Enabled
38 // that always returns true.
39 //
40 // Then comes the implementation of SDL_IsXInputDevice, which is copied
41 // directly from SDL.
42 //
43 // At the bottom of the file, you'll find our wrapper interfaces --
44 // the public API for the XInputCheck library.
45 //
46 // Again, if you ever need to change anything in the SDL functions, well.
47 // Think again! If you find a bug, feel free to fix it, but report it
48 // upstream as well.
49 // If at all possible, try to use wrapper functions to implement "new"
50 // functionality (such as XInputCheck_ClearDeviceCache, for clearing
51 // the internal cache).
52
53 // Preprocessor helpers for running SDL code as-is.
54 #define SDL_bool bool
55 #define SDL_TRUE true
56 #define SDL_FALSE false
57 #define SDL_free free
58 #define SDL_malloc malloc
59 #define SDL_OutOfMemory abort
60 #define SDL_strstr strstr
61 #define SDL_memcmp memcmp
62 #define SDL_arraysize(array) (sizeof(array)/sizeof(array[0]))
63
64
65 static SDL_bool
SDL_XINPUT_Enabled()66 SDL_XINPUT_Enabled() {
67 return SDL_TRUE;
68 }
69
70 static PRAWINPUTDEVICELIST SDL_RawDevList = NULL;
71 static UINT SDL_RawDevListCount = 0;
72
73 static SDL_bool
SDL_IsXInputDevice(const GUID * pGuidProductFromDirectInput)74 SDL_IsXInputDevice(const GUID* pGuidProductFromDirectInput)
75 {
76 static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
77 static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
78 static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
79 static GUID IID_XOneWiredGamepad = { MAKELONG(0x045E, 0x02FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
80 static GUID IID_XOneWirelessGamepad = { MAKELONG(0x045E, 0x02DD), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
81 static GUID IID_XOneNewWirelessGamepad = { MAKELONG(0x045E, 0x02D1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
82 static GUID IID_XOneSWirelessGamepad = { MAKELONG(0x045E, 0x02EA), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
83 static GUID IID_XOneSBluetoothGamepad = { MAKELONG(0x045E, 0x02E0), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
84 static GUID IID_XOneEliteWirelessGamepad = { MAKELONG(0x045E, 0x02E3), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
85
86 static const GUID *s_XInputProductGUID[] = {
87 &IID_ValveStreamingGamepad,
88 &IID_X360WiredGamepad, /* Microsoft's wired X360 controller for Windows. */
89 &IID_X360WirelessGamepad, /* Microsoft's wireless X360 controller for Windows. */
90 &IID_XOneWiredGamepad, /* Microsoft's wired Xbox One controller for Windows. */
91 &IID_XOneWirelessGamepad, /* Microsoft's wireless Xbox One controller for Windows. */
92 &IID_XOneNewWirelessGamepad, /* Microsoft's updated wireless Xbox One controller (w/ 3.5 mm jack) for Windows. */
93 &IID_XOneSWirelessGamepad, /* Microsoft's wireless Xbox One S controller for Windows. */
94 &IID_XOneSBluetoothGamepad, /* Microsoft's Bluetooth Xbox One S controller for Windows. */
95 &IID_XOneEliteWirelessGamepad /* Microsoft's wireless Xbox One Elite controller for Windows. */
96 };
97
98 size_t iDevice;
99 UINT i;
100
101 if (!SDL_XINPUT_Enabled()) {
102 return SDL_FALSE;
103 }
104
105 /* Check for well known XInput device GUIDs */
106 /* This lets us skip RAWINPUT for popular devices. Also, we need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. */
107 for (iDevice = 0; iDevice < SDL_arraysize(s_XInputProductGUID); ++iDevice) {
108 if (SDL_memcmp(pGuidProductFromDirectInput, s_XInputProductGUID[iDevice], sizeof(GUID)) == 0) {
109 return SDL_TRUE;
110 }
111 }
112
113 /* Go through RAWINPUT (WinXP and later) to find HID devices. */
114 /* Cache this if we end up using it. */
115 if (SDL_RawDevList == NULL) {
116 if ((GetRawInputDeviceList(NULL, &SDL_RawDevListCount, sizeof(RAWINPUTDEVICELIST)) == -1) || (!SDL_RawDevListCount)) {
117 return SDL_FALSE; /* oh well. */
118 }
119
120 SDL_RawDevList = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * SDL_RawDevListCount);
121 if (SDL_RawDevList == NULL) {
122 SDL_OutOfMemory();
123 return SDL_FALSE;
124 }
125
126 if (GetRawInputDeviceList(SDL_RawDevList, &SDL_RawDevListCount, sizeof(RAWINPUTDEVICELIST)) == -1) {
127 SDL_free(SDL_RawDevList);
128 SDL_RawDevList = NULL;
129 return SDL_FALSE; /* oh well. */
130 }
131 }
132
133 for (i = 0; i < SDL_RawDevListCount; i++) {
134 RID_DEVICE_INFO rdi;
135 char devName[128];
136 UINT rdiSize = sizeof(rdi);
137 UINT nameSize = SDL_arraysize(devName);
138
139 rdi.cbSize = sizeof(rdi);
140 if ((SDL_RawDevList[i].dwType == RIM_TYPEHID) &&
141 (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) &&
142 (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == ((LONG)pGuidProductFromDirectInput->Data1)) &&
143 (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) &&
144 (SDL_strstr(devName, "IG_") != NULL)) {
145 return SDL_TRUE;
146 }
147 }
148
149 return SDL_FALSE;
150 }
151
XInputCheck_IsGuidProductXInputDevice(const GUID * pGuidProductFromDirectInput)152 bool XInputCheck_IsGuidProductXInputDevice(const GUID *pGuidProductFromDirectInput) {
153 return SDL_IsXInputDevice(pGuidProductFromDirectInput);
154 }
155
XInputCheck_ClearDeviceCache()156 void XInputCheck_ClearDeviceCache() {
157 SDL_free(SDL_RawDevList);
158 SDL_RawDevList = NULL;
159 SDL_RawDevListCount = 0;
160 }
161