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

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
scottmg 2013/02/14 04:33:22 this is defined twice (also in the header).
teravest 2013/02/14 17:52:18 Removed.
8 #ifndef _WIN32_DCOM
scottmg 2013/02/14 04:33:22 i'm assuming this is here for some reason, but i d
teravest 2013/02/14 17:52:18 This was present in the DirectX example that FillX
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/stringprintf.h"
22 #include "base/win/scoped_bstr.h"
23 #include "base/win/scoped_com_initializer.h"
8 #include "content/common/gamepad_messages.h" 24 #include "content/common/gamepad_messages.h"
9 #include "content/common/gamepad_hardware_buffer.h" 25 #include "content/common/gamepad_hardware_buffer.h"
10 26
11 namespace content { 27 namespace content {
12 28
13 using namespace WebKit; 29 using namespace WebKit;
14 30
15 namespace { 31 namespace {
16 32
17 // See http://goo.gl/5VSJR. These are not available in all versions of the 33 // 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 34 // header, but they can be returned from the driver, so we define our own
19 // versions here. 35 // versions here.
20 static const BYTE kDeviceSubTypeGamepad = 1; 36 static const BYTE kDeviceSubTypeGamepad = 1;
21 static const BYTE kDeviceSubTypeWheel = 2; 37 static const BYTE kDeviceSubTypeWheel = 2;
22 static const BYTE kDeviceSubTypeArcadeStick = 3; 38 static const BYTE kDeviceSubTypeArcadeStick = 3;
23 static const BYTE kDeviceSubTypeFlightStick = 4; 39 static const BYTE kDeviceSubTypeFlightStick = 4;
24 static const BYTE kDeviceSubTypeDancePad = 5; 40 static const BYTE kDeviceSubTypeDancePad = 5;
25 static const BYTE kDeviceSubTypeGuitar = 6; 41 static const BYTE kDeviceSubTypeGuitar = 6;
26 static const BYTE kDeviceSubTypeGuitarAlternate = 7; 42 static const BYTE kDeviceSubTypeGuitarAlternate = 7;
27 static const BYTE kDeviceSubTypeDrumKit = 8; 43 static const BYTE kDeviceSubTypeDrumKit = 8;
28 static const BYTE kDeviceSubTypeGuitarBass = 11; 44 static const BYTE kDeviceSubTypeGuitarBass = 11;
29 static const BYTE kDeviceSubTypeArcadePad = 19; 45 static const BYTE kDeviceSubTypeArcadePad = 19;
30 46
31 float NormalizeAxis(SHORT value) { 47 float NormalizeXInputAxis(SHORT value) {
32 return ((value + 32768.f) / 32767.5f) - 1.f; 48 return ((value + 32768.f) / 32767.5f) - 1.f;
33 } 49 }
34 50
35 const WebUChar* const GamepadSubTypeName(BYTE sub_type) { 51 const WebUChar* const GamepadSubTypeName(BYTE sub_type) {
36 switch (sub_type) { 52 switch (sub_type) {
37 case kDeviceSubTypeGamepad: return L"GAMEPAD"; 53 case kDeviceSubTypeGamepad: return L"GAMEPAD";
38 case kDeviceSubTypeWheel: return L"WHEEL"; 54 case kDeviceSubTypeWheel: return L"WHEEL";
39 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; 55 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK";
40 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; 56 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK";
41 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; 57 case kDeviceSubTypeDancePad: return L"DANCE_PAD";
42 case kDeviceSubTypeGuitar: return L"GUITAR"; 58 case kDeviceSubTypeGuitar: return L"GUITAR";
43 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; 59 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE";
44 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; 60 case kDeviceSubTypeDrumKit: return L"DRUM_KIT";
45 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; 61 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS";
46 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; 62 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD";
47 default: return L"<UNKNOWN>"; 63 default: return L"<UNKNOWN>";
48 } 64 }
49 } 65 }
50 66
67 bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad,
68 std::string* vendor,
69 std::string* product) {
70 DIPROPDWORD prop;
71 prop.diph.dwSize = sizeof(DIPROPDWORD);
72 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
73 prop.diph.dwObj = 0;
74 prop.diph.dwHow = DIPH_DEVICE;
75
76 if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &prop.diph)))
77 return false;
78 *vendor = base::StringPrintf("%04x", LOWORD(prop.dwData));
79 *product = base::StringPrintf("%04x", HIWORD(prop.dwData));
80 return true;
81 }
82
83 // Sets the deadzone value for all axes of a gamepad.
84 // deadzone values range from 0 (no deadzone) to 10,000 (entire range
85 // is dead).
86 bool SetDirectInputDeadZone(IDirectInputDevice8* gamepad,
87 int deadzone) {
88 DIPROPDWORD prop;
89 prop.diph.dwSize = sizeof(DIPROPDWORD);
90 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
91 prop.diph.dwObj = 0;
92 prop.diph.dwHow = DIPH_DEVICE;
93 prop.dwData = deadzone;
94 return SUCCEEDED(gamepad->SetProperty(DIPROP_DEADZONE, &prop.diph));
95 }
96
97 struct InternalDirectInputDevice {
98 IDirectInputDevice8* gamepad;
99 GamepadStandardMappingFunction mapper;
100 wchar_t id[WebGamepad::idLengthCap];
101 };
102
103 struct EnumDevicesContext {
104 std::vector<uint32>* xinput_devices;
105 IDirectInput8* directinput_interface;
106 std::vector<InternalDirectInputDevice>* directinput_devices;
107 };
108
109 // We define our own data format structure to attempt to get as many
110 // axes as possible.
111 struct JoyData {
112 long axes[10];
113 char buttons[24];
114 DWORD pov; // Often used for D-pads.
115 };
116
117 BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance,
118 void* context) {
119 EnumDevicesContext* ctxt = (EnumDevicesContext*) context;
120 // Skip XInput devices.
121 for (size_t i = 0; i < ctxt->xinput_devices->size(); ++i) {
122 if ((*ctxt->xinput_devices)[i] == instance->guidProduct.Data1)
123 return DIENUM_CONTINUE;
124 }
125 IDirectInputDevice8* gamepad;
126 if (FAILED(ctxt->directinput_interface->CreateDevice(instance->guidInstance,
127 &gamepad,
128 NULL)))
129 return DIENUM_CONTINUE;
130
131 #define MAKE_AXIS(i) \
132 {0, FIELD_OFFSET(JoyData, axes) + 4 * i, \
133 DIDFT_AXIS | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0}
134 #define MAKE_BUTTON(i) \
135 {&GUID_Button, FIELD_OFFSET(JoyData, buttons) + i, \
136 DIDFT_BUTTON | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0}
137 #define MAKE_POV() \
138 {&GUID_POV, FIELD_OFFSET(JoyData, pov), DIDFT_POV | DIDFT_OPTIONAL, 0}
139 DIOBJECTDATAFORMAT rgodf[] = {
140 MAKE_AXIS(0),
141 MAKE_AXIS(1),
142 MAKE_AXIS(2),
143 MAKE_AXIS(3),
144 MAKE_AXIS(4),
145 MAKE_AXIS(5),
146 MAKE_AXIS(6),
147 MAKE_AXIS(7),
148 MAKE_AXIS(8),
149 MAKE_AXIS(9),
150 MAKE_BUTTON(0),
151 MAKE_BUTTON(1),
152 MAKE_BUTTON(2),
153 MAKE_BUTTON(3),
154 MAKE_BUTTON(4),
155 MAKE_BUTTON(5),
156 MAKE_BUTTON(6),
157 MAKE_BUTTON(7),
158 MAKE_BUTTON(8),
159 MAKE_BUTTON(9),
160 MAKE_BUTTON(10),
161 MAKE_BUTTON(11),
162 MAKE_BUTTON(12),
163 MAKE_BUTTON(13),
164 MAKE_BUTTON(14),
165 MAKE_BUTTON(15),
166 MAKE_BUTTON(16),
167 MAKE_POV(),
168 };
169 #undef MAKE_AXIS
170 #undef MAKE_BUTTON
171 #undef MAKE_POV
172
173 DIDATAFORMAT df = {
174 sizeof (DIDATAFORMAT),
175 sizeof (DIOBJECTDATAFORMAT),
176 DIDF_ABSAXIS,
177 sizeof (JoyData),
178 sizeof (rgodf) / sizeof (rgodf[0]),
179 rgodf
180 };
181
182 // If we can't set the data format on the device, don't add it to our
183 // list, since we won't know how to read data from it.
184 if (FAILED(gamepad->SetDataFormat(&df)))
185 return DIENUM_CONTINUE;
186
187 InternalDirectInputDevice device;
188 device.gamepad = gamepad;
189 std::string vendor;
190 std::string product;
191 if (!GetDirectInputVendorProduct(gamepad, &vendor, &product))
192 return DIENUM_CONTINUE;
193
194 wcscpy_s(device.id, WebGamepad::idLengthCap, instance->tszInstanceName);
scottmg 2013/02/14 04:33:22 i think this should be tszProductName not tszInsta
teravest 2013/02/14 17:52:18 Done.
195
196 // Set the dead zone to 10% of the axis length for all axes. This
197 // gives us a larger space for what's "neutral" so the controls don't
198 // slowly drift.
199 SetDirectInputDeadZone(gamepad, 1000);
200 device.mapper = GetGamepadStandardMappingFunction(vendor, product);
scottmg 2013/02/14 04:33:22 if there's a mapper to standard gamepad style, the
teravest 2013/02/14 17:52:18 Done.
201 if (device.mapper)
202 ctxt->directinput_devices->push_back(device);
203 return DIENUM_CONTINUE;
204 }
205
206 // Adapted from the DirectInput samples in the DirectX SDK.
207 // http://goo.gl/53Sfw
208 void FillXInputDeviceList(std::vector<uint32>* xinput_devices) {
209 xinput_devices->clear();
210 base::win::ScopedCOMInitializer com_initializer;
scottmg 2013/02/14 04:33:22 cpu can confirm, but I think this should only be d
teravest 2013/02/14 17:52:18 Deleted FillXInputDeviceList().
211 if (com_initializer.succeeded())
212 return;
scottmg 2013/02/14 04:33:22 return on success?
teravest 2013/02/14 17:52:18 Deleted FillXInputDeviceList().
213
214 // Create WMI
215 IWbemLocator* tmp_wbem_locator = NULL;
216 HRESULT hr = CoCreateInstance(__uuidof(WbemLocator),
217 NULL,
218 CLSCTX_INPROC_SERVER,
219 __uuidof(IWbemLocator),
220 (void**)&tmp_wbem_locator);
221 if (FAILED(hr) || tmp_wbem_locator == NULL)
222 return;
223 scoped_ptr<IWbemLocator> wbem_locator(tmp_wbem_locator);
224
225 // Connect to WMI
226 base::win::ScopedBstr name_space(L"\\\\.\\root\\cimv2");
scottmg 2013/02/14 04:33:22 what a lovely api they've cooked up here. :P
227 IWbemServices* tmp_wbem_services = NULL;
228 hr = wbem_locator->ConnectServer(name_space, NULL, NULL, 0L,
229 0L, NULL, NULL, &tmp_wbem_services);
230 if (FAILED(hr) || tmp_wbem_services == NULL)
231 return;
232 scoped_ptr<IWbemServices> wbem_services(tmp_wbem_services);
233
234 // Switch security level to IMPERSONATE
235 CoSetProxyBlanket(wbem_services.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
236 NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
237 NULL, 0);
238
239 // Get list of Win32_PNPEntity devices
240 base::win::ScopedBstr class_name(L"Win32_PNPEntity");
241 IEnumWbemClassObject* tmp_enum_devices = NULL;
242 hr = wbem_services->CreateInstanceEnum(class_name, 0, NULL,
243 &tmp_enum_devices);
244 if (FAILED(hr) || tmp_enum_devices == NULL)
245 return;
246 scoped_ptr<IEnumWbemClassObject> enum_devices(tmp_enum_devices);
247
248 // Loop over all devices
249 while (true) {
250 unsigned long devices_returned;
scottmg 2013/02/14 04:33:22 i think keep this as ULONG since it's primarily in
teravest 2013/02/14 17:52:18 Deleted FillXInputDeviceList().
251 IWbemClassObject* devices[20] = {0};
252 // Get 20 at a time
253 hr = enum_devices->Next(10000, 20, devices, &devices_returned);
254 if (FAILED(hr))
255 return;
256 if (devices_returned == 0)
257 break;
258
259 for (unsigned i = 0; i < devices_returned; i++) {
scottmg 2013/02/14 04:33:22 ULONG, or at least match type above
teravest 2013/02/14 17:52:18 Deleted FillXInputDeviceList().
260 if (!devices[i])
261 continue;
262 // For each device, get its device ID
263 base::win::ScopedBstr device_id(L"DeviceID");
264 VARIANT var;
265 hr = devices[i]->Get(device_id, 0L, &var, NULL, NULL);
266 if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) {
267 // Check if the device ID contains "IG_". If it does, then it's an
268 // XInput device
269 // Unfortunately this information can not be found by just using
270 // DirectInput
271 if (wcsstr(var.bstrVal, L"IG_")) {
272 // If it does, then get the VID/PID from var.bstrVal
273 DWORD dwPid = 0, dwVid = 0;
274 WCHAR* strVid = wcsstr(var.bstrVal, L"VID_");
275 if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1)
scottmg 2013/02/14 04:33:22 should these be <= 0?
teravest 2013/02/14 17:52:18 Deleted FillXInputDeviceList().
276 dwVid = 0;
277 WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
278 if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1)
279 dwPid = 0;
280 DWORD dwVidPid = MAKELONG(dwVid, dwPid);
281
282 // Add the VID/PID to a linked list
scottmg 2013/02/14 04:33:22 not a linked list
teravest 2013/02/14 17:52:18 Stale comment removed.
283 xinput_devices->push_back(dwVidPid);
284 }
285 }
286 devices[i]->Release();
287 }
288 }
289 }
290
51 } // namespace 291 } // namespace
52 292
53 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() 293 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin()
54 : xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), 294 : xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))),
55 xinput_available_(GetXinputDllFunctions()) { 295 xinput_available_(GetXInputDllFunctions()) {
296 directinput_available_ =
297 SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL),
298 DIRECTINPUT_VERSION,
299 IID_IDirectInput8,
300 (void **)&directinput_interface_,
301 NULL));
56 } 302 }
57 303
58 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { 304 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() {
59 } 305 }
60 306
307 void GamepadPlatformDataFetcherWin::EnumerateDevices(
308 WebGamepads* pads) {
309 TRACE_EVENT0("GAMEPAD", "EnumerateDevices");
310 pad_state_.clear();
311 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
scottmg 2013/02/14 04:33:22 this approach changes what happens on insert and r
teravest 2013/02/14 17:52:18 I figured that XInput devices should always take p
312 PadState s;
313 s.status = DISCONNECTED;
314 pad_state_.push_back(s);
315 }
316
317 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
318 WebGamepad &pad = pads->items[i];
319 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) {
320 pad_state_[i].status = XINPUT_CONNECTED;
321 continue;
322 }
323 }
324
325 if (directinput_available_) {
326 FillXInputDeviceList(&xinput_devices_);
327 struct EnumDevicesContext ctxt;
scottmg 2013/02/14 04:33:22 at least "context" or enum_devices_context if it's
teravest 2013/02/14 17:52:18 Done.
328 std::vector<InternalDirectInputDevice> directinput_gamepads;
329 ctxt.xinput_devices = &xinput_devices_;
330 ctxt.directinput_interface = directinput_interface_;
331 ctxt.directinput_devices = &directinput_gamepads;
332
333 directinput_interface_->EnumDevices(
334 DI8DEVCLASS_GAMECTRL,
335 &DirectInputEnumDevicesCallback,
336 &ctxt,
337 DIEDFL_ATTACHEDONLY);
338
339 // Fill the "disconnected" pad state entries with our DirectInput
340 // gamepads.
341 unsigned pad_state_index = 0;
342 unsigned directinput_index = 0;
343 while (pad_state_index < WebGamepads::itemsLengthCap &&
344 directinput_index < directinput_gamepads.size()) {
345 if (pad_state_[pad_state_index].status != DISCONNECTED) {
scottmg 2013/02/14 04:33:22 i think it would be clearer to have a local = &pad
teravest 2013/02/14 17:52:18 Done.
346 ++pad_state_index;
347 continue;
348 }
349 WebGamepad &pad = pads->items[pad_state_index];
350 pad.connected = true;
351 wcscpy_s(pad.id, WebGamepad::idLengthCap,
352 directinput_gamepads[directinput_index].id);
353 pad_state_[pad_state_index].status = DIRECTINPUT_CONNECTED;
354 pad_state_[pad_state_index].directinput_gamepad =
355 directinput_gamepads[directinput_index].gamepad;
356 pad_state_[pad_state_index].mapper =
357 directinput_gamepads[directinput_index].mapper;
358 ++directinput_index;
359 ++pad_state_index;
360 }
361 }
362 }
363
364
61 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, 365 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads,
62 bool devices_changed_hint) { 366 bool devices_changed_hint) {
63 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); 367 TRACE_EVENT0("GAMEPAD", "GetGamepadData");
64 368
65 // If there's no XInput DLL on the system, early out so that we don't 369 if (!xinput_available_ && !directinput_available_) {
66 // call any other XInput functions.
67 if (!xinput_available_) {
68 pads->length = 0; 370 pads->length = 0;
69 return; 371 return;
70 } 372 }
71 373
72 pads->length = WebGamepads::itemsLengthCap; 374 // A note on XInput devices:
73
74 // If we got notification that system devices have been updated, then 375 // If we got notification that system devices have been updated, then
75 // run GetCapabilities to update the connected status and the device 376 // run GetCapabilities to update the connected status and the device
76 // identifier. It can be slow to do to both GetCapabilities and 377 // 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 378 // 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 379 // here by only doing this when the devices are updated (despite
79 // documentation claiming it's OK to call it any time). 380 // documentation claiming it's OK to call it any time).
80 if (devices_changed_hint) { 381 if (devices_changed_hint)
81 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { 382 EnumerateDevices(pads);
82 WebGamepad& pad = pads->items[i]; 383
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) { 384 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
101 WebGamepad& pad = pads->items[i]; 385 WebGamepad& pad = pads->items[i];
102 386 if (pad_state_[i].status == XINPUT_CONNECTED)
103 // We rely on device_changed and GetCapabilities to tell us that 387 GetXInputPadData(i, &pad);
104 // something's been connected, but we will mark as disconnected if 388 else if (pad_state_[i].status == DIRECTINPUT_CONNECTED)
105 // GetState returns that we've lost the pad. 389 GetDirectInputPadData(i, &pad);
106 if (!pad.connected) 390 }
107 continue; 391 pads->length = WebGamepads::itemsLengthCap;
108 392 }
109 XINPUT_STATE state; 393
110 memset(&state, 0, sizeof(XINPUT_STATE)); 394 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
111 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); 395 int i,
112 DWORD dwResult = xinput_get_state_(i, &state); 396 WebGamepad* pad) const {
113 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); 397 DCHECK(pad);
114 398 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i);
115 if (dwResult == ERROR_SUCCESS) { 399 XINPUT_CAPABILITIES caps;
116 pad.timestamp = state.dwPacketNumber; 400 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
117 pad.buttonsLength = 0; 401 if (res == ERROR_DEVICE_NOT_CONNECTED) {
118 #define ADD(b) pad.buttons[pad.buttonsLength++] = \ 402 pad->connected = false;
119 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); 403 return false;
120 ADD(XINPUT_GAMEPAD_A); 404 } else {
121 ADD(XINPUT_GAMEPAD_B); 405 pad->connected = true;
122 ADD(XINPUT_GAMEPAD_X); 406 base::swprintf(pad->id,
123 ADD(XINPUT_GAMEPAD_Y); 407 WebGamepad::idLengthCap,
124 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); 408 L"Xbox 360 Controller (XInput STANDARD %ls)",
125 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); 409 GamepadSubTypeName(caps.SubType));
126 pad.buttons[pad.buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; 410 return true;
127 pad.buttons[pad.buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; 411 }
128 ADD(XINPUT_GAMEPAD_BACK); 412 }
129 ADD(XINPUT_GAMEPAD_START); 413
130 ADD(XINPUT_GAMEPAD_LEFT_THUMB); 414 void GamepadPlatformDataFetcherWin::GetXInputPadData(
131 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); 415 int i,
132 ADD(XINPUT_GAMEPAD_DPAD_UP); 416 WebGamepad* pad) {
133 ADD(XINPUT_GAMEPAD_DPAD_DOWN); 417 // We rely on device_changed and GetCapabilities to tell us that
134 ADD(XINPUT_GAMEPAD_DPAD_LEFT); 418 // something's been connected, but we will mark as disconnected if
135 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); 419 // GetState returns that we've lost the pad.
420 if (!pad->connected)
421 return;
422
423 XINPUT_STATE state;
424 memset(&state, 0, sizeof(XINPUT_STATE));
425 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i);
426 DWORD dwResult = xinput_get_state_(i, &state);
427 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i);
428
429 if (dwResult == ERROR_SUCCESS) {
430 pad->timestamp = state.dwPacketNumber;
431 pad->buttonsLength = 0;
432 #define ADD(b) pad->buttons[pad->buttonsLength++] = \
433 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0);
434 ADD(XINPUT_GAMEPAD_A);
435 ADD(XINPUT_GAMEPAD_B);
436 ADD(XINPUT_GAMEPAD_X);
437 ADD(XINPUT_GAMEPAD_Y);
438 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER);
439 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER);
440 pad->buttons[pad->buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0;
441 pad->buttons[pad->buttonsLength++] = state.Gamepad.bRightTrigger / 255.0;
442 ADD(XINPUT_GAMEPAD_BACK);
443 ADD(XINPUT_GAMEPAD_START);
444 ADD(XINPUT_GAMEPAD_LEFT_THUMB);
445 ADD(XINPUT_GAMEPAD_RIGHT_THUMB);
446 ADD(XINPUT_GAMEPAD_DPAD_UP);
447 ADD(XINPUT_GAMEPAD_DPAD_DOWN);
448 ADD(XINPUT_GAMEPAD_DPAD_LEFT);
449 ADD(XINPUT_GAMEPAD_DPAD_RIGHT);
136 #undef ADD 450 #undef ADD
137 pad.axesLength = 0; 451 pad->axesLength = 0;
138 // XInput are +up/+right, -down/-left, we want -up/-left. 452 // XInput are +up/+right, -down/-left, we want -up/-left.
139 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbLX); 453 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbLX);
140 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbLY); 454 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbLY);
141 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbRX); 455 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbRX);
142 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbRY); 456 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbRY);
143 } else { 457 } else {
144 pad.connected = false; 458 pad->connected = false;
145 } 459 }
146 } 460 }
147 } 461
148 462 void GamepadPlatformDataFetcherWin::GetDirectInputPadData(
149 bool GamepadPlatformDataFetcherWin::GetXinputDllFunctions() { 463 int index,
464 WebGamepad* pad) {
465 if (!pad->connected)
466 return;
467
468 IDirectInputDevice8* gamepad = pad_state_[index].directinput_gamepad;
469 if (FAILED(gamepad->Poll())) {
470 HRESULT hr;
471 while ((hr = gamepad->Acquire()) == DIERR_INPUTLOST) { }
scottmg 2013/02/14 04:33:22 this loop worries me. is there any guarantee we wo
teravest 2013/02/14 17:52:18 I like that better. Reading through the docs, Acq
472 return;
473 }
474
475 JoyData state;
476 if (FAILED(gamepad->GetDeviceState(sizeof(JoyData), &state))) {
477 pad->connected = false;
478 return;
479 }
480
481 WebGamepad raw;
482 raw.connected = true;
483 for (int i = 0; i < 16; i++)
484 raw.buttons[i] = (state.buttons[i] & 0x80) ? 1.0 : 0.0;
485
486 // We map the POV (often a D-pad) into the buttons 16-19.
scottmg 2013/02/14 04:33:22 this seems like something that should go into the
teravest 2013/02/14 17:52:18 The range of legal values for a POV should always
487 // DirectInput gives pov measurements in hundredths of degrees,
488 // clockwise from "North".
489 // We use 22.5 degree slices so we can handle diagonal D-raw presses.
490 static const int arc_segment = 2250; // 22.5 degrees = 1/16 circle
491 if (state.pov > arc_segment && state.pov < 7 * arc_segment)
492 raw.buttons[19] = 1.0;
493 else
494 raw.buttons[19] = 0.0;
495
496 if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment)
497 raw.buttons[17] = 1.0;
498 else
499 raw.buttons[17] = 0.0;
500
501 if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment)
502 raw.buttons[18] = 1.0;
503 else
504 raw.buttons[18] = 0.0;
505
506 if (state.pov < 3 * arc_segment ||
507 (state.pov > 13 * arc_segment && state.pov < 36000))
508 raw.buttons[16] = 1.0;
509 else
510 raw.buttons[16] = 0.0;
511
512 for (int i = 0; i < 10; i++)
513 raw.axes[i] = state.axes[i];
514
515 pad_state_[index].mapper(raw, pad);
516 }
517
518 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
150 xinput_get_capabilities_ = NULL; 519 xinput_get_capabilities_ = NULL;
151 xinput_get_state_ = NULL; 520 xinput_get_state_ = NULL;
152 xinput_enable_ = static_cast<XInputEnableFunc>( 521 xinput_enable_ = static_cast<XInputEnableFunc>(
153 xinput_dll_.GetFunctionPointer("XInputEnable")); 522 xinput_dll_.GetFunctionPointer("XInputEnable"));
154 if (!xinput_enable_) 523 if (!xinput_enable_)
155 return false; 524 return false;
156 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>( 525 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>(
157 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); 526 xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
158 if (!xinput_get_capabilities_) 527 if (!xinput_get_capabilities_)
159 return false; 528 return false;
160 xinput_get_state_ = static_cast<XInputGetStateFunc>( 529 xinput_get_state_ = static_cast<XInputGetStateFunc>(
161 xinput_dll_.GetFunctionPointer("XInputGetState")); 530 xinput_dll_.GetFunctionPointer("XInputGetState"));
162 if (!xinput_get_state_) 531 if (!xinput_get_state_)
163 return false; 532 return false;
164 xinput_enable_(true); 533 xinput_enable_(true);
165 return true; 534 return true;
166 } 535 }
167 536
168 } // namespace content 537 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698