OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" | 5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" |
6 | 6 |
7 #define DIRECTINPUT_VERSION 0x0800 | |
8 #ifndef _WIN32_DCOM | |
9 #define _WIN32_DCOM | |
10 #endif | |
11 | |
12 #include <basetsd.h> | |
13 #include <commctrl.h> | |
14 #include <dinput.h> | |
15 #include <dinputd.h> | |
16 #include <oleauto.h> | |
17 #include <wbemidl.h> | |
18 #include <windows.h> | |
19 | |
7 #include "base/debug/trace_event.h" | 20 #include "base/debug/trace_event.h" |
21 #include "base/string_util.h" | |
8 #include "content/common/gamepad_messages.h" | 22 #include "content/common/gamepad_messages.h" |
9 #include "content/common/gamepad_hardware_buffer.h" | 23 #include "content/common/gamepad_hardware_buffer.h" |
10 | 24 |
11 namespace content { | 25 namespace content { |
12 | 26 |
13 using namespace WebKit; | 27 using namespace WebKit; |
14 | 28 |
15 namespace { | 29 namespace { |
16 | 30 |
17 // See http://goo.gl/5VSJR. These are not available in all versions of the | 31 // See http://goo.gl/5VSJR. These are not available in all versions of the |
18 // header, but they can be returned from the driver, so we define our own | 32 // header, but they can be returned from the driver, so we define our own |
19 // versions here. | 33 // versions here. |
20 static const BYTE kDeviceSubTypeGamepad = 1; | 34 static const BYTE kDeviceSubTypeGamepad = 1; |
21 static const BYTE kDeviceSubTypeWheel = 2; | 35 static const BYTE kDeviceSubTypeWheel = 2; |
22 static const BYTE kDeviceSubTypeArcadeStick = 3; | 36 static const BYTE kDeviceSubTypeArcadeStick = 3; |
23 static const BYTE kDeviceSubTypeFlightStick = 4; | 37 static const BYTE kDeviceSubTypeFlightStick = 4; |
24 static const BYTE kDeviceSubTypeDancePad = 5; | 38 static const BYTE kDeviceSubTypeDancePad = 5; |
25 static const BYTE kDeviceSubTypeGuitar = 6; | 39 static const BYTE kDeviceSubTypeGuitar = 6; |
26 static const BYTE kDeviceSubTypeGuitarAlternate = 7; | 40 static const BYTE kDeviceSubTypeGuitarAlternate = 7; |
27 static const BYTE kDeviceSubTypeDrumKit = 8; | 41 static const BYTE kDeviceSubTypeDrumKit = 8; |
28 static const BYTE kDeviceSubTypeGuitarBass = 11; | 42 static const BYTE kDeviceSubTypeGuitarBass = 11; |
29 static const BYTE kDeviceSubTypeArcadePad = 19; | 43 static const BYTE kDeviceSubTypeArcadePad = 19; |
30 | 44 |
31 float NormalizeAxis(SHORT value) { | 45 float NormalizeXInputAxis(SHORT value) { |
32 return ((value + 32768.f) / 32767.5f) - 1.f; | 46 return ((value + 32768.f) / 32767.5f) - 1.f; |
33 } | 47 } |
34 | 48 |
35 const WebUChar* const GamepadSubTypeName(BYTE sub_type) { | 49 const WebUChar* const GamepadSubTypeName(BYTE sub_type) { |
36 switch (sub_type) { | 50 switch (sub_type) { |
37 case kDeviceSubTypeGamepad: return L"GAMEPAD"; | 51 case kDeviceSubTypeGamepad: return L"GAMEPAD"; |
38 case kDeviceSubTypeWheel: return L"WHEEL"; | 52 case kDeviceSubTypeWheel: return L"WHEEL"; |
39 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; | 53 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; |
40 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; | 54 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; |
41 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; | 55 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; |
42 case kDeviceSubTypeGuitar: return L"GUITAR"; | 56 case kDeviceSubTypeGuitar: return L"GUITAR"; |
43 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; | 57 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; |
44 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; | 58 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; |
45 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; | 59 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; |
46 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; | 60 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; |
47 default: return L"<UNKNOWN>"; | 61 default: return L"<UNKNOWN>"; |
48 } | 62 } |
49 } | 63 } |
50 | 64 |
65 // A nicer version of the DirectX SAFE_RELEASE macro. | |
66 template <class T> | |
67 void SafeRelease(T** p) { | |
68 if (*p) | |
69 (*p)->Release(); | |
70 *p = NULL; | |
71 } | |
72 | |
73 bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad, | |
74 std::string* vendor, | |
75 std::string* product) { | |
76 DIPROPDWORD property; | |
77 property.diph.dwSize = sizeof(DIPROPDWORD); | |
78 property.diph.dwHeaderSize = sizeof(DIPROPHEADER); | |
79 property.diph.dwObj = 0; | |
80 property.diph.dwHow = DIPH_DEVICE; | |
81 | |
82 if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &property.diph))) | |
83 return false; | |
84 int vendorData = LOWORD(property.dwData); | |
85 int productData = HIWORD(property.dwData); | |
86 std::stringstream vendorstream; | |
87 vendorstream << std::setw(4) << std::setfill('0') << std::hex << vendorData; | |
scottmg
2013/02/13 19:19:07
http://google-styleguide.googlecode.com/svn/trunk/
teravest
2013/02/13 21:59:22
Done.
| |
88 *vendor = vendorstream.str(); | |
89 | |
90 std::stringstream productstream; | |
91 productstream << std::setw(4) << std::setfill('0') << std::hex << productData; | |
92 *product = productstream.str(); | |
93 return true; | |
94 } | |
95 | |
96 // Sets the deadzone value for all axes of a gamepad. | |
97 // deadzone values range from 0 (no deadzone) to 10,000 (entire range | |
98 // is dead). | |
99 bool SetDirectInputDeadZone(IDirectInputDevice8* gamepad, | |
100 int deadzone) { | |
101 DIPROPDWORD prop; | |
102 prop.diph.dwSize = sizeof(DIPROPDWORD); | |
103 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); | |
104 prop.diph.dwObj = 0; | |
105 prop.diph.dwHow = DIPH_DEVICE; | |
106 prop.dwData = deadzone; | |
107 return SUCCEEDED(gamepad->SetProperty(DIPROP_DEADZONE, &prop.diph)); | |
108 } | |
109 | |
110 struct InternalDirectInputDevice { | |
111 IDirectInputDevice8* gamepad; | |
112 GamepadStandardMappingFunction mapper; | |
113 wchar_t id[WebGamepad::idLengthCap]; | |
114 }; | |
115 | |
116 struct EnumDevicesContext { | |
117 std::vector<uint32>* xinput_devices; | |
118 IDirectInput8* directinput_interface; | |
119 std::vector<InternalDirectInputDevice>* directinput_devices; | |
120 }; | |
121 | |
122 // We have to define our own structure here; I was unable to link the | |
123 // "global" joydata2 structure from directx. This one is smaller anyway. | |
scottmg
2013/02/13 19:19:07
What? Why?
teravest
2013/02/13 21:59:22
I didn't want to muck around with how we were link
| |
124 struct JoyData { | |
125 long axes[10]; | |
126 char buttons[24]; | |
127 DWORD pov; // Often used for D-pads. | |
128 }; | |
129 | |
130 BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance, | |
131 void* context) { | |
132 EnumDevicesContext* ctxt = (EnumDevicesContext*) context; | |
133 // Skip XInput devices. | |
134 for (size_t i = 0; i < ctxt->xinput_devices->size(); ++i) { | |
135 if ((*ctxt->xinput_devices)[i] == instance->guidProduct.Data1) | |
136 return DIENUM_CONTINUE; | |
137 } | |
138 IDirectInputDevice8* gamepad; | |
139 if (FAILED(ctxt->directinput_interface->CreateDevice(instance->guidInstance, | |
140 &gamepad, | |
141 NULL))) | |
142 return DIENUM_CONTINUE; | |
143 | |
144 #define MAKE_AXIS(i) \ | |
145 {0, FIELD_OFFSET(JoyData, axes) + 4 * i, \ | |
146 DIDFT_AXIS | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} | |
147 #define MAKE_BUTTON(i) \ | |
148 {&GUID_Button, FIELD_OFFSET(JoyData, buttons) + i, \ | |
149 DIDFT_BUTTON | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} | |
150 #define MAKE_POV() \ | |
151 {&GUID_POV, FIELD_OFFSET(JoyData, pov), DIDFT_POV | DIDFT_OPTIONAL, 0} | |
152 DIOBJECTDATAFORMAT rgodf[] = { | |
153 MAKE_AXIS(0), | |
154 MAKE_AXIS(1), | |
155 MAKE_AXIS(2), | |
156 MAKE_AXIS(3), | |
157 MAKE_AXIS(4), | |
158 MAKE_AXIS(5), | |
159 MAKE_AXIS(6), | |
160 MAKE_AXIS(7), | |
161 MAKE_AXIS(8), | |
162 MAKE_AXIS(9), | |
163 MAKE_BUTTON(0), | |
164 MAKE_BUTTON(1), | |
165 MAKE_BUTTON(2), | |
166 MAKE_BUTTON(3), | |
167 MAKE_BUTTON(4), | |
168 MAKE_BUTTON(5), | |
169 MAKE_BUTTON(6), | |
170 MAKE_BUTTON(7), | |
171 MAKE_BUTTON(8), | |
172 MAKE_BUTTON(9), | |
173 MAKE_BUTTON(10), | |
174 MAKE_BUTTON(11), | |
175 MAKE_BUTTON(12), | |
176 MAKE_BUTTON(13), | |
177 MAKE_BUTTON(14), | |
178 MAKE_BUTTON(15), | |
179 MAKE_BUTTON(16), | |
180 MAKE_POV(), | |
181 }; | |
182 #undef MAKE_AXIS | |
183 #undef MAKE_BUTTON | |
184 #undef MAKE_POV | |
185 | |
186 DIDATAFORMAT df = { | |
187 sizeof (DIDATAFORMAT), | |
188 sizeof (DIOBJECTDATAFORMAT), | |
189 DIDF_ABSAXIS, | |
190 sizeof (JoyData), | |
191 sizeof (rgodf) / sizeof (rgodf[0]), | |
192 rgodf | |
193 }; | |
194 | |
195 // If we can't set the data format on the device, don't add it to our | |
196 // list, since we won't know how to read data from it. | |
197 if (FAILED(gamepad->SetDataFormat(&df))) | |
198 return DIENUM_CONTINUE; | |
199 | |
200 InternalDirectInputDevice device; | |
201 device.gamepad = gamepad; | |
202 std::string vendor; | |
203 std::string product; | |
204 if (!GetDirectInputVendorProduct(gamepad, &vendor, &product)) | |
205 return DIENUM_CONTINUE; | |
206 | |
207 wcscpy_s(device.id, WebGamepad::idLengthCap, instance->tszInstanceName); | |
208 | |
209 SetDirectInputDeadZone(gamepad, 1000); | |
scottmg
2013/02/13 19:19:07
Where did this 1000 come from?
teravest
2013/02/13 21:59:22
I've added a comment here explaining what this is.
| |
210 device.mapper = GetGamepadStandardMappingFunction(vendor, product); | |
211 if (device.mapper) | |
212 ctxt->directinput_devices->push_back(device); | |
213 return DIENUM_CONTINUE; | |
214 } | |
215 | |
216 // Adapted from the DirectInput samples in the DirectX SDK. | |
217 // http://goo.gl/53Sfw | |
218 void FillXInputDeviceList(std::vector<uint32>* xinput_devices) { | |
219 xinput_devices->clear(); | |
220 IWbemServices* pIWbemServices = NULL; | |
221 IEnumWbemClassObject* pEnumDevices = NULL; | |
222 IWbemLocator* pIWbemLocator = NULL; | |
223 IWbemClassObject* pDevices[20] = {0}; | |
224 BSTR bstrDeviceID = NULL; | |
225 BSTR bstrClassName = NULL; | |
226 BSTR bstrNamespace = NULL; | |
227 DWORD uReturned = 0; | |
228 bool bCleanupCOM = false; | |
229 UINT iDevice = 0; | |
230 VARIANT var; | |
231 HRESULT hr; | |
232 | |
233 // CoInit if needed | |
234 hr = CoInitialize(NULL); | |
scottmg
2013/02/13 19:19:07
We shouldn't be doing this here.
teravest
2013/02/13 21:59:22
I've changed this to use a ScopedCOMInitializer. O
| |
235 bCleanupCOM = SUCCEEDED(hr); | |
236 | |
237 // Create WMI | |
238 hr = CoCreateInstance(__uuidof(WbemLocator), | |
239 NULL, | |
240 CLSCTX_INPROC_SERVER, | |
241 __uuidof(IWbemLocator), | |
242 (void**)&pIWbemLocator); | |
scottmg
2013/02/13 19:19:07
Please fix all the identifiers to match Chromium s
teravest
2013/02/13 21:59:22
Done.
| |
243 if (FAILED(hr) || pIWbemLocator == NULL ) | |
244 goto LCleanup; | |
245 | |
246 // Create BSTRs for WMI | |
247 bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); | |
248 if (bstrNamespace == NULL) | |
249 goto LCleanup; | |
250 bstrDeviceID = SysAllocString(L"DeviceID"); | |
251 if (bstrDeviceID == NULL) | |
252 goto LCleanup; | |
253 bstrClassName = SysAllocString(L"Win32_PNPEntity"); | |
254 if (bstrClassName == NULL) | |
255 goto LCleanup; | |
256 | |
257 // Connect to WMI | |
258 hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, | |
259 0L, NULL, NULL, &pIWbemServices ); | |
260 if (FAILED(hr) || pIWbemServices == NULL) | |
261 goto LCleanup; | |
262 | |
263 // Switch security level to IMPERSONATE | |
264 CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, | |
265 RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, | |
266 0); | |
267 | |
268 // Get list of Win32_PNPEntity devices | |
269 hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, | |
270 &pEnumDevices); | |
271 if (FAILED(hr) || pEnumDevices == NULL ) | |
272 goto LCleanup; | |
273 | |
274 // Loop over all devices | |
275 while (true) { | |
276 // Get 20 at a time | |
277 hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned); | |
278 if (FAILED(hr)) | |
279 goto LCleanup; | |
280 if (uReturned == 0) | |
281 break; | |
282 | |
283 for (iDevice = 0; iDevice < uReturned; iDevice++) { | |
284 if (!pDevices[iDevice]) | |
285 continue; | |
286 // For each device, get its device ID | |
287 hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, NULL, NULL); | |
288 if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) { | |
289 // Check if the device ID contains "IG_". If it does, then it's an | |
290 // XInput device | |
291 // Unfortunately this information can not be found by just using | |
292 // DirectInput | |
293 if (wcsstr(var.bstrVal, L"IG_")) { | |
294 // If it does, then get the VID/PID from var.bstrVal | |
295 DWORD dwPid = 0, dwVid = 0; | |
296 WCHAR* strVid = wcsstr(var.bstrVal, L"VID_"); | |
297 if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) | |
298 dwVid = 0; | |
299 WCHAR* strPid = wcsstr(var.bstrVal, L"PID_"); | |
300 if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) | |
301 dwPid = 0; | |
302 DWORD dwVidPid = MAKELONG(dwVid, dwPid); | |
303 | |
304 // Add the VID/PID to a linked list | |
305 xinput_devices->push_back(dwVidPid); | |
306 } | |
307 } | |
308 SafeRelease(&pDevices[iDevice]); | |
309 } | |
310 } | |
311 | |
312 LCleanup: | |
313 if (bstrNamespace) | |
314 SysFreeString(bstrNamespace); | |
315 if (bstrDeviceID) | |
316 SysFreeString(bstrDeviceID); | |
317 if (bstrClassName) | |
318 SysFreeString(bstrClassName); | |
319 for (iDevice = 0; iDevice < 20; iDevice++) | |
320 SafeRelease(&pDevices[iDevice]); | |
321 SafeRelease(&pEnumDevices); | |
322 SafeRelease(&pIWbemLocator); | |
323 SafeRelease(&pIWbemServices); | |
324 } | |
325 | |
51 } // namespace | 326 } // namespace |
52 | 327 |
53 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() | 328 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() |
54 : xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), | 329 : xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), |
55 xinput_available_(GetXinputDllFunctions()) { | 330 xinput_available_(GetXInputDllFunctions()), |
331 directinput_dll_(FilePath(FILE_PATH_LITERAL("dinput8.dll"))), | |
332 directinput_available_(GetDirectInputDllFunctions()) { | |
56 } | 333 } |
57 | 334 |
58 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { | 335 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { |
59 } | 336 } |
60 | 337 |
338 void GamepadPlatformDataFetcherWin::EnumerateDevices( | |
339 WebGamepads* pads) { | |
340 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); | |
341 pad_state_.clear(); | |
342 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
343 PadState s; | |
344 s.status = DISCONNECTED; | |
345 pad_state_.push_back(s); | |
346 } | |
347 | |
348 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
349 WebGamepad &pad = pads->items[i]; | |
350 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) { | |
351 pad_state_[i].status = XINPUT_CONNECTED; | |
352 continue; | |
353 } | |
354 } | |
355 | |
356 if (directinput_available_) { | |
357 FillXInputDeviceList(&xinput_devices_); | |
358 struct EnumDevicesContext ctxt; | |
359 std::vector<InternalDirectInputDevice> directinput_gamepads; | |
360 ctxt.xinput_devices = &xinput_devices_; | |
361 ctxt.directinput_interface = directinput_interface_; | |
362 ctxt.directinput_devices = &directinput_gamepads; | |
363 | |
364 directinput_interface_->EnumDevices( | |
365 DI8DEVCLASS_GAMECTRL, | |
366 &DirectInputEnumDevicesCallback, | |
367 &ctxt, | |
368 DIEDFL_ATTACHEDONLY); | |
369 | |
370 // Fill the "disconnected" pad state entries with our DirectInput | |
371 // gamepads. | |
372 unsigned pad_state_index = 0; | |
373 unsigned directinput_index = 0; | |
374 while (pad_state_index < WebGamepads::itemsLengthCap && | |
375 directinput_index < directinput_gamepads.size()) { | |
376 if (pad_state_[pad_state_index].status != DISCONNECTED) { | |
377 ++pad_state_index; | |
378 continue; | |
379 } | |
380 WebGamepad &pad = pads->items[pad_state_index]; | |
381 pad.connected = true; | |
382 wcscpy_s(pad.id, WebGamepad::idLengthCap, | |
383 directinput_gamepads[directinput_index].id); | |
384 pad_state_[pad_state_index].status = DIRECTINPUT_CONNECTED; | |
385 pad_state_[pad_state_index].directinput_gamepad = | |
386 directinput_gamepads[directinput_index].gamepad; | |
387 pad_state_[pad_state_index].mapper = | |
388 directinput_gamepads[directinput_index].mapper; | |
389 ++directinput_index; | |
390 ++pad_state_index; | |
391 } | |
392 } | |
393 } | |
394 | |
395 | |
61 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, | 396 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, |
62 bool devices_changed_hint) { | 397 bool devices_changed_hint) { |
63 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); | 398 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); |
64 | 399 |
65 // If there's no XInput DLL on the system, early out so that we don't | 400 if (!xinput_available_ && !directinput_available_) { |
66 // call any other XInput functions. | |
67 if (!xinput_available_) { | |
68 pads->length = 0; | 401 pads->length = 0; |
69 return; | 402 return; |
70 } | 403 } |
71 | 404 |
72 pads->length = WebGamepads::itemsLengthCap; | 405 // A note on XInput devices: |
73 | |
74 // If we got notification that system devices have been updated, then | 406 // If we got notification that system devices have been updated, then |
75 // run GetCapabilities to update the connected status and the device | 407 // run GetCapabilities to update the connected status and the device |
76 // identifier. It can be slow to do to both GetCapabilities and | 408 // identifier. It can be slow to do to both GetCapabilities and |
77 // GetState on unconnected devices, so we want to avoid a 2-5ms pause | 409 // GetState on unconnected devices, so we want to avoid a 2-5ms pause |
78 // here by only doing this when the devices are updated (despite | 410 // here by only doing this when the devices are updated (despite |
79 // documentation claiming it's OK to call it any time). | 411 // documentation claiming it's OK to call it any time). |
80 if (devices_changed_hint) { | 412 if (devices_changed_hint) |
81 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 413 EnumerateDevices(pads); |
82 WebGamepad& pad = pads->items[i]; | 414 |
83 TRACE_EVENT1("GAMEPAD", "GetCapabilities", "id", i); | |
84 XINPUT_CAPABILITIES caps; | |
85 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); | |
86 if (res == ERROR_DEVICE_NOT_CONNECTED) { | |
87 pad.connected = false; | |
88 } else { | |
89 pad.connected = true; | |
90 base::swprintf(pad.id, | |
91 WebGamepad::idLengthCap, | |
92 L"Xbox 360 Controller (XInput STANDARD %ls)", | |
93 GamepadSubTypeName(caps.SubType)); | |
94 } | |
95 } | |
96 } | |
97 | |
98 // We've updated the connection state if necessary, now update the actual | |
99 // data for the devices that are connected. | |
100 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 415 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
101 WebGamepad& pad = pads->items[i]; | 416 WebGamepad& pad = pads->items[i]; |
102 | 417 if (pad_state_[i].status == XINPUT_CONNECTED) |
103 // We rely on device_changed and GetCapabilities to tell us that | 418 GetXInputPadData(i, &pad); |
104 // something's been connected, but we will mark as disconnected if | 419 else if (pad_state_[i].status == DIRECTINPUT_CONNECTED) |
105 // GetState returns that we've lost the pad. | 420 GetDirectInputPadData(i, &pad); |
106 if (!pad.connected) | 421 } |
107 continue; | 422 pads->length = WebGamepads::itemsLengthCap; |
108 | 423 return; |
scottmg
2013/02/13 19:19:07
nit: remove
teravest
2013/02/13 21:59:22
Done.
| |
109 XINPUT_STATE state; | 424 } |
110 memset(&state, 0, sizeof(XINPUT_STATE)); | 425 |
111 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | 426 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( |
112 DWORD dwResult = xinput_get_state_(i, &state); | 427 int i, |
113 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | 428 WebGamepad* pad) const { |
114 | 429 DCHECK(pad); |
115 if (dwResult == ERROR_SUCCESS) { | 430 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i); |
116 pad.timestamp = state.dwPacketNumber; | 431 XINPUT_CAPABILITIES caps; |
117 pad.buttonsLength = 0; | 432 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); |
118 #define ADD(b) pad.buttons[pad.buttonsLength++] = \ | 433 if (res == ERROR_DEVICE_NOT_CONNECTED) { |
119 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); | 434 pad->connected = false; |
120 ADD(XINPUT_GAMEPAD_A); | 435 return false; |
121 ADD(XINPUT_GAMEPAD_B); | 436 } else { |
122 ADD(XINPUT_GAMEPAD_X); | 437 pad->connected = true; |
123 ADD(XINPUT_GAMEPAD_Y); | 438 base::swprintf(pad->id, |
124 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | 439 WebGamepad::idLengthCap, |
125 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | 440 L"Xbox 360 Controller (XInput STANDARD %ls)", |
126 pad.buttons[pad.buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; | 441 GamepadSubTypeName(caps.SubType)); |
127 pad.buttons[pad.buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; | 442 return true; |
128 ADD(XINPUT_GAMEPAD_BACK); | 443 } |
129 ADD(XINPUT_GAMEPAD_START); | 444 } |
130 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | 445 |
131 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | 446 void GamepadPlatformDataFetcherWin::GetXInputPadData( |
132 ADD(XINPUT_GAMEPAD_DPAD_UP); | 447 int i, |
133 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | 448 WebGamepad* pad) { |
134 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | 449 // We rely on device_changed and GetCapabilities to tell us that |
135 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | 450 // something's been connected, but we will mark as disconnected if |
451 // GetState returns that we've lost the pad. | |
452 if (!pad->connected) | |
453 return; | |
454 | |
455 XINPUT_STATE state; | |
456 memset(&state, 0, sizeof(XINPUT_STATE)); | |
457 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | |
458 DWORD dwResult = xinput_get_state_(i, &state); | |
459 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | |
460 | |
461 if (dwResult == ERROR_SUCCESS) { | |
462 pad->timestamp = state.dwPacketNumber; | |
463 pad->buttonsLength = 0; | |
464 #define ADD(b) pad->buttons[pad->buttonsLength++] = \ | |
465 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); | |
466 ADD(XINPUT_GAMEPAD_A); | |
467 ADD(XINPUT_GAMEPAD_B); | |
468 ADD(XINPUT_GAMEPAD_X); | |
469 ADD(XINPUT_GAMEPAD_Y); | |
470 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | |
471 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | |
472 pad->buttons[pad->buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; | |
473 pad->buttons[pad->buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; | |
474 ADD(XINPUT_GAMEPAD_BACK); | |
475 ADD(XINPUT_GAMEPAD_START); | |
476 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | |
477 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | |
478 ADD(XINPUT_GAMEPAD_DPAD_UP); | |
479 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | |
480 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | |
481 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | |
136 #undef ADD | 482 #undef ADD |
137 pad.axesLength = 0; | 483 pad->axesLength = 0; |
138 // XInput are +up/+right, -down/-left, we want -up/-left. | 484 // XInput are +up/+right, -down/-left, we want -up/-left. |
139 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbLX); | 485 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbLX); |
140 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbLY); | 486 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbLY); |
141 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbRX); | 487 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbRX); |
142 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbRY); | 488 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbRY); |
143 } else { | 489 } else { |
144 pad.connected = false; | 490 pad->connected = false; |
145 } | 491 } |
146 } | 492 } |
147 } | 493 |
148 | 494 void GamepadPlatformDataFetcherWin::GetDirectInputPadData( |
149 bool GamepadPlatformDataFetcherWin::GetXinputDllFunctions() { | 495 int index, |
496 WebGamepad* pad) { | |
497 if (!pad->connected) | |
498 return; | |
499 | |
500 IDirectInputDevice8* gamepad = pad_state_[index].directinput_gamepad; | |
501 if (FAILED(gamepad->Poll())) { | |
502 HRESULT hr; | |
503 while ((hr = gamepad->Acquire()) == DIERR_INPUTLOST) { } | |
504 return; | |
505 } | |
506 | |
507 JoyData state; | |
508 if (FAILED(gamepad->GetDeviceState(sizeof(JoyData), &state))) { | |
509 pad->connected = false; | |
510 return; | |
511 } | |
512 | |
513 WebGamepad raw; | |
514 raw.connected = true; | |
515 for (int i = 0; i < 16; i++) | |
516 raw.buttons[i] = (state.buttons[i] & 0x80) ? 1.0 : 0.0; | |
517 | |
518 // We map the POV (often a D-pad) into the buttons 16-19. | |
519 // DirectInput gives pov measurements in hundredths of degrees, | |
520 // clockwise from "North". | |
521 // We use 22.5 degree slices so we can handle diagonal D-raw presses. | |
522 static const int arc_segment = 2250; // 22.5 degrees = 1/16 circle | |
523 if (state.pov > arc_segment && state.pov < 7 * arc_segment) | |
524 raw.buttons[19] = 1; | |
525 else | |
526 raw.buttons[19] = 0; | |
527 | |
528 if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment) | |
529 raw.buttons[17] = 1; | |
530 else | |
531 raw.buttons[17] = 0; | |
532 | |
533 if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment) | |
534 raw.buttons[18] = 1; | |
535 else | |
536 raw.buttons[18] = 0; | |
537 | |
538 if (state.pov < 3 * arc_segment || | |
539 (state.pov > 13 * arc_segment && state.pov < 36000)) | |
540 raw.buttons[16] = 1; | |
541 else | |
542 raw.buttons[16] = 0; | |
543 | |
544 for (int i = 0; i < 10; i++) | |
545 raw.axes[i] = state.axes[i]; | |
546 | |
547 pad_state_[index].mapper(raw, pad); | |
548 } | |
549 | |
550 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { | |
150 xinput_get_capabilities_ = NULL; | 551 xinput_get_capabilities_ = NULL; |
151 xinput_get_state_ = NULL; | 552 xinput_get_state_ = NULL; |
152 xinput_enable_ = static_cast<XInputEnableFunc>( | 553 xinput_enable_ = static_cast<XInputEnableFunc>( |
153 xinput_dll_.GetFunctionPointer("XInputEnable")); | 554 xinput_dll_.GetFunctionPointer("XInputEnable")); |
154 if (!xinput_enable_) | 555 if (!xinput_enable_) |
155 return false; | 556 return false; |
156 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>( | 557 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>( |
157 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); | 558 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); |
158 if (!xinput_get_capabilities_) | 559 if (!xinput_get_capabilities_) |
159 return false; | 560 return false; |
160 xinput_get_state_ = static_cast<XInputGetStateFunc>( | 561 xinput_get_state_ = static_cast<XInputGetStateFunc>( |
161 xinput_dll_.GetFunctionPointer("XInputGetState")); | 562 xinput_dll_.GetFunctionPointer("XInputGetState")); |
162 if (!xinput_get_state_) | 563 if (!xinput_get_state_) |
163 return false; | 564 return false; |
164 xinput_enable_(true); | 565 xinput_enable_(true); |
165 return true; | 566 return true; |
166 } | 567 } |
167 | 568 |
569 bool GamepadPlatformDataFetcherWin::GetDirectInputDllFunctions() { | |
570 directinput_create_ = static_cast<DirectInputCreateFunc>( | |
scottmg
2013/02/13 19:19:07
According to http://msdn.microsoft.com/en-us/libra
teravest
2013/02/13 21:59:22
Cool. I'm still trying to figure out how to do thi
| |
571 directinput_dll_.GetFunctionPointer("DirectInput8Create")); | |
572 if (!directinput_create_) | |
573 return false; | |
574 if (FAILED(directinput_create_(GetModuleHandle(NULL), | |
575 DIRECTINPUT_VERSION, | |
576 IID_IDirectInput8, | |
577 (void **)&directinput_interface_, | |
578 NULL))) | |
579 return false; | |
580 return true; | |
581 } | |
582 | |
168 } // namespace content | 583 } // namespace content |
OLD | NEW |