Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(552)

Side by Side Diff: content/browser/gamepad/gamepad_platform_data_fetcher_win.cc

Issue 12260011: Support DirectInput gamepads on Windows. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698