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 #include <dinput.h> |
| 8 #include <dinputd.h> |
| 9 |
7 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| 11 #include "base/stringprintf.h" |
8 #include "content/common/gamepad_messages.h" | 12 #include "content/common/gamepad_messages.h" |
9 #include "content/common/gamepad_hardware_buffer.h" | 13 #include "content/common/gamepad_hardware_buffer.h" |
10 | 14 |
| 15 // This was removed from the Windows 8 SDK for some reason. |
| 16 // We need it so we can get state for axes without worrying if they |
| 17 // exist. |
| 18 #ifndef DIDFT_OPTIONAL |
| 19 #define DIDFT_OPTIONAL 0x80000000 |
| 20 #endif |
| 21 |
11 namespace content { | 22 namespace content { |
12 | 23 |
13 using namespace WebKit; | 24 using namespace WebKit; |
14 | 25 |
15 namespace { | 26 namespace { |
16 | 27 |
17 // See http://goo.gl/5VSJR. These are not available in all versions of the | 28 // 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 | 29 // header, but they can be returned from the driver, so we define our own |
19 // versions here. | 30 // versions here. |
20 static const BYTE kDeviceSubTypeGamepad = 1; | 31 static const BYTE kDeviceSubTypeGamepad = 1; |
21 static const BYTE kDeviceSubTypeWheel = 2; | 32 static const BYTE kDeviceSubTypeWheel = 2; |
22 static const BYTE kDeviceSubTypeArcadeStick = 3; | 33 static const BYTE kDeviceSubTypeArcadeStick = 3; |
23 static const BYTE kDeviceSubTypeFlightStick = 4; | 34 static const BYTE kDeviceSubTypeFlightStick = 4; |
24 static const BYTE kDeviceSubTypeDancePad = 5; | 35 static const BYTE kDeviceSubTypeDancePad = 5; |
25 static const BYTE kDeviceSubTypeGuitar = 6; | 36 static const BYTE kDeviceSubTypeGuitar = 6; |
26 static const BYTE kDeviceSubTypeGuitarAlternate = 7; | 37 static const BYTE kDeviceSubTypeGuitarAlternate = 7; |
27 static const BYTE kDeviceSubTypeDrumKit = 8; | 38 static const BYTE kDeviceSubTypeDrumKit = 8; |
28 static const BYTE kDeviceSubTypeGuitarBass = 11; | 39 static const BYTE kDeviceSubTypeGuitarBass = 11; |
29 static const BYTE kDeviceSubTypeArcadePad = 19; | 40 static const BYTE kDeviceSubTypeArcadePad = 19; |
30 | 41 |
31 float NormalizeAxis(SHORT value) { | 42 float NormalizeXInputAxis(SHORT value) { |
32 return ((value + 32768.f) / 32767.5f) - 1.f; | 43 return ((value + 32768.f) / 32767.5f) - 1.f; |
33 } | 44 } |
34 | 45 |
35 const WebUChar* const GamepadSubTypeName(BYTE sub_type) { | 46 const WebUChar* const GamepadSubTypeName(BYTE sub_type) { |
36 switch (sub_type) { | 47 switch (sub_type) { |
37 case kDeviceSubTypeGamepad: return L"GAMEPAD"; | 48 case kDeviceSubTypeGamepad: return L"GAMEPAD"; |
38 case kDeviceSubTypeWheel: return L"WHEEL"; | 49 case kDeviceSubTypeWheel: return L"WHEEL"; |
39 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; | 50 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; |
40 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; | 51 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; |
41 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; | 52 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; |
42 case kDeviceSubTypeGuitar: return L"GUITAR"; | 53 case kDeviceSubTypeGuitar: return L"GUITAR"; |
43 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; | 54 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; |
44 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; | 55 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; |
45 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; | 56 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; |
46 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; | 57 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; |
47 default: return L"<UNKNOWN>"; | 58 default: return L"<UNKNOWN>"; |
48 } | 59 } |
49 } | 60 } |
50 | 61 |
| 62 bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad, |
| 63 std::string* vendor, |
| 64 std::string* product) { |
| 65 DIPROPDWORD prop; |
| 66 prop.diph.dwSize = sizeof(DIPROPDWORD); |
| 67 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); |
| 68 prop.diph.dwObj = 0; |
| 69 prop.diph.dwHow = DIPH_DEVICE; |
| 70 |
| 71 if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &prop.diph))) |
| 72 return false; |
| 73 *vendor = base::StringPrintf("%04x", LOWORD(prop.dwData)); |
| 74 *product = base::StringPrintf("%04x", HIWORD(prop.dwData)); |
| 75 return true; |
| 76 } |
| 77 |
| 78 // Sets the deadzone value for all axes of a gamepad. |
| 79 // deadzone values range from 0 (no deadzone) to 10,000 (entire range |
| 80 // is dead). |
| 81 bool SetDirectInputDeadZone(IDirectInputDevice8* gamepad, |
| 82 int deadzone) { |
| 83 DIPROPDWORD prop; |
| 84 prop.diph.dwSize = sizeof(DIPROPDWORD); |
| 85 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); |
| 86 prop.diph.dwObj = 0; |
| 87 prop.diph.dwHow = DIPH_DEVICE; |
| 88 prop.dwData = deadzone; |
| 89 return SUCCEEDED(gamepad->SetProperty(DIPROP_DEADZONE, &prop.diph)); |
| 90 } |
| 91 |
| 92 struct InternalDirectInputDevice { |
| 93 IDirectInputDevice8* gamepad; |
| 94 GamepadStandardMappingFunction mapper; |
| 95 wchar_t id[WebGamepad::idLengthCap]; |
| 96 GUID guid; |
| 97 }; |
| 98 |
| 99 struct EnumDevicesContext { |
| 100 IDirectInput8* directinput_interface; |
| 101 std::vector<InternalDirectInputDevice>* directinput_devices; |
| 102 }; |
| 103 |
| 104 // We define our own data format structure to attempt to get as many |
| 105 // axes as possible. |
| 106 struct JoyData { |
| 107 long axes[10]; |
| 108 char buttons[24]; |
| 109 DWORD pov; // Often used for D-pads. |
| 110 }; |
| 111 |
| 112 BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance, |
| 113 void* context) { |
| 114 EnumDevicesContext* ctxt = reinterpret_cast<EnumDevicesContext*>(context); |
| 115 IDirectInputDevice8* gamepad; |
| 116 |
| 117 if (FAILED(ctxt->directinput_interface->CreateDevice(instance->guidInstance, |
| 118 &gamepad, |
| 119 NULL))) |
| 120 return DIENUM_CONTINUE; |
| 121 |
| 122 gamepad->Acquire(); |
| 123 |
| 124 #define MAKE_AXIS(i) \ |
| 125 {0, FIELD_OFFSET(JoyData, axes) + 4 * i, \ |
| 126 DIDFT_AXIS | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} |
| 127 #define MAKE_BUTTON(i) \ |
| 128 {&GUID_Button, FIELD_OFFSET(JoyData, buttons) + i, \ |
| 129 DIDFT_BUTTON | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} |
| 130 #define MAKE_POV() \ |
| 131 {&GUID_POV, FIELD_OFFSET(JoyData, pov), DIDFT_POV | DIDFT_OPTIONAL, 0} |
| 132 DIOBJECTDATAFORMAT rgodf[] = { |
| 133 MAKE_AXIS(0), |
| 134 MAKE_AXIS(1), |
| 135 MAKE_AXIS(2), |
| 136 MAKE_AXIS(3), |
| 137 MAKE_AXIS(4), |
| 138 MAKE_AXIS(5), |
| 139 MAKE_AXIS(6), |
| 140 MAKE_AXIS(7), |
| 141 MAKE_AXIS(8), |
| 142 MAKE_AXIS(9), |
| 143 MAKE_BUTTON(0), |
| 144 MAKE_BUTTON(1), |
| 145 MAKE_BUTTON(2), |
| 146 MAKE_BUTTON(3), |
| 147 MAKE_BUTTON(4), |
| 148 MAKE_BUTTON(5), |
| 149 MAKE_BUTTON(6), |
| 150 MAKE_BUTTON(7), |
| 151 MAKE_BUTTON(8), |
| 152 MAKE_BUTTON(9), |
| 153 MAKE_BUTTON(10), |
| 154 MAKE_BUTTON(11), |
| 155 MAKE_BUTTON(12), |
| 156 MAKE_BUTTON(13), |
| 157 MAKE_BUTTON(14), |
| 158 MAKE_BUTTON(15), |
| 159 MAKE_BUTTON(16), |
| 160 MAKE_POV(), |
| 161 }; |
| 162 #undef MAKE_AXIS |
| 163 #undef MAKE_BUTTON |
| 164 #undef MAKE_POV |
| 165 |
| 166 DIDATAFORMAT df = { |
| 167 sizeof (DIDATAFORMAT), |
| 168 sizeof (DIOBJECTDATAFORMAT), |
| 169 DIDF_ABSAXIS, |
| 170 sizeof (JoyData), |
| 171 sizeof (rgodf) / sizeof (rgodf[0]), |
| 172 rgodf |
| 173 }; |
| 174 |
| 175 // If we can't set the data format on the device, don't add it to our |
| 176 // list, since we won't know how to read data from it. |
| 177 if (FAILED(gamepad->SetDataFormat(&df))) { |
| 178 gamepad->Release(); |
| 179 return DIENUM_CONTINUE; |
| 180 } |
| 181 |
| 182 InternalDirectInputDevice device; |
| 183 device.guid = instance->guidInstance; |
| 184 device.gamepad = gamepad; |
| 185 std::string vendor; |
| 186 std::string product; |
| 187 if (!GetDirectInputVendorProduct(gamepad, &vendor, &product)) { |
| 188 gamepad->Release(); |
| 189 return DIENUM_CONTINUE; |
| 190 } |
| 191 |
| 192 // Set the dead zone to 10% of the axis length for all axes. This |
| 193 // gives us a larger space for what's "neutral" so the controls don't |
| 194 // slowly drift. |
| 195 SetDirectInputDeadZone(gamepad, 1000); |
| 196 device.mapper = GetGamepadStandardMappingFunction(vendor, product); |
| 197 if (device.mapper) { |
| 198 base::swprintf(device.id, |
| 199 WebGamepad::idLengthCap, |
| 200 L"STANDARD GAMEPAD (%ls)", |
| 201 instance->tszProductName); |
| 202 ctxt->directinput_devices->push_back(device); |
| 203 } else { |
| 204 gamepad->Release(); |
| 205 } |
| 206 return DIENUM_CONTINUE; |
| 207 } |
| 208 |
51 } // namespace | 209 } // namespace |
52 | 210 |
53 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() | 211 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() |
54 : xinput_dll_(base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), | 212 : xinput_dll_(base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), |
55 xinput_available_(GetXinputDllFunctions()) { | 213 xinput_available_(GetXInputDllFunctions()) { |
| 214 directinput_available_ = SUCCEEDED(DirectInput8Create( |
| 215 GetModuleHandle(NULL), |
| 216 DIRECTINPUT_VERSION, |
| 217 IID_IDirectInput8, |
| 218 reinterpret_cast<void**>(&directinput_interface_), |
| 219 NULL)); |
| 220 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) |
| 221 pad_state_[i].status = DISCONNECTED; |
56 } | 222 } |
57 | 223 |
58 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { | 224 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { |
59 } | 225 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 226 if (pad_state_[i].status == DIRECTINPUT_CONNECTED) |
| 227 pad_state_[i].directinput_gamepad->Release(); |
| 228 } |
| 229 } |
| 230 |
| 231 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const { |
| 232 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 233 if (pad_state_[i].status == DISCONNECTED) |
| 234 return i; |
| 235 } |
| 236 return -1; |
| 237 } |
| 238 |
| 239 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const { |
| 240 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 241 if (pad_state_[i].status == XINPUT_CONNECTED && |
| 242 pad_state_[i].xinput_index == index) |
| 243 return true; |
| 244 } |
| 245 return false; |
| 246 } |
| 247 |
| 248 bool GamepadPlatformDataFetcherWin::HasDirectInputGamepad( |
| 249 const GUID& guid) const { |
| 250 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 251 if (pad_state_[i].status == DIRECTINPUT_CONNECTED && |
| 252 pad_state_[i].guid == guid) |
| 253 return true; |
| 254 } |
| 255 return false; |
| 256 } |
| 257 |
| 258 void GamepadPlatformDataFetcherWin::EnumerateDevices( |
| 259 WebGamepads* pads) { |
| 260 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); |
| 261 |
| 262 // Mark all disconnected pads DISCONNECTED. |
| 263 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 264 if (!pads->items[i].connected) |
| 265 pad_state_[i].status = DISCONNECTED; |
| 266 } |
| 267 |
| 268 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { |
| 269 if (HasXInputGamepad(i)) |
| 270 continue; |
| 271 int pad_index = FirstAvailableGamepadId(); |
| 272 if (pad_index == -1) |
| 273 return; // We can't add any more gamepads. |
| 274 WebGamepad& pad = pads->items[pad_index]; |
| 275 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) { |
| 276 pad_state_[pad_index].status = XINPUT_CONNECTED; |
| 277 pad_state_[pad_index].xinput_index = i; |
| 278 } |
| 279 } |
| 280 |
| 281 if (directinput_available_) { |
| 282 struct EnumDevicesContext context; |
| 283 std::vector<InternalDirectInputDevice> directinput_gamepads; |
| 284 context.directinput_interface = directinput_interface_; |
| 285 context.directinput_devices = &directinput_gamepads; |
| 286 |
| 287 directinput_interface_->EnumDevices( |
| 288 DI8DEVCLASS_GAMECTRL, |
| 289 &DirectInputEnumDevicesCallback, |
| 290 &context, |
| 291 DIEDFL_ATTACHEDONLY); |
| 292 for (size_t i = 0; i < directinput_gamepads.size(); ++i) { |
| 293 if (HasDirectInputGamepad(directinput_gamepads[i].guid)) { |
| 294 directinput_gamepads[i].gamepad->Release(); |
| 295 continue; |
| 296 } |
| 297 int pad_index = FirstAvailableGamepadId(); |
| 298 if (pad_index == -1) |
| 299 return; |
| 300 WebGamepad& pad = pads->items[pad_index]; |
| 301 pad.connected = true; |
| 302 wcscpy_s(pad.id, WebGamepad::idLengthCap, directinput_gamepads[i].id); |
| 303 PadState& state = pad_state_[pad_index]; |
| 304 state.status = DIRECTINPUT_CONNECTED; |
| 305 state.guid = directinput_gamepads[i].guid; |
| 306 state.directinput_gamepad = directinput_gamepads[i].gamepad; |
| 307 state.mapper = directinput_gamepads[i].mapper; |
| 308 } |
| 309 } |
| 310 } |
| 311 |
60 | 312 |
61 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, | 313 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, |
62 bool devices_changed_hint) { | 314 bool devices_changed_hint) { |
63 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); | 315 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); |
64 | 316 |
65 // If there's no XInput DLL on the system, early out so that we don't | 317 if (!xinput_available_ && !directinput_available_) { |
66 // call any other XInput functions. | |
67 if (!xinput_available_) { | |
68 pads->length = 0; | 318 pads->length = 0; |
69 return; | 319 return; |
70 } | 320 } |
71 | 321 |
72 pads->length = WebGamepads::itemsLengthCap; | 322 // A note on XInput devices: |
73 | |
74 // If we got notification that system devices have been updated, then | 323 // If we got notification that system devices have been updated, then |
75 // run GetCapabilities to update the connected status and the device | 324 // run GetCapabilities to update the connected status and the device |
76 // identifier. It can be slow to do to both GetCapabilities and | 325 // 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 | 326 // 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 | 327 // here by only doing this when the devices are updated (despite |
79 // documentation claiming it's OK to call it any time). | 328 // documentation claiming it's OK to call it any time). |
80 if (devices_changed_hint) { | 329 if (devices_changed_hint) |
81 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 330 EnumerateDevices(pads); |
82 WebGamepad& pad = pads->items[i]; | 331 |
83 TRACE_EVENT1("GAMEPAD", "GetCapabilities", "id", i); | 332 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
84 XINPUT_CAPABILITIES caps; | 333 WebGamepad& pad = pads->items[i]; |
85 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); | 334 if (pad_state_[i].status == XINPUT_CONNECTED) |
86 if (res == ERROR_DEVICE_NOT_CONNECTED) { | 335 GetXInputPadData(i, &pad); |
87 pad.connected = false; | 336 else if (pad_state_[i].status == DIRECTINPUT_CONNECTED) |
88 } else { | 337 GetDirectInputPadData(i, &pad); |
89 pad.connected = true; | 338 } |
90 base::swprintf(pad.id, | 339 pads->length = WebGamepads::itemsLengthCap; |
91 WebGamepad::idLengthCap, | 340 } |
92 L"Xbox 360 Controller (XInput STANDARD %ls)", | 341 |
93 GamepadSubTypeName(caps.SubType)); | 342 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( |
94 } | 343 int i, |
| 344 WebGamepad* pad) const { |
| 345 DCHECK(pad); |
| 346 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i); |
| 347 XINPUT_CAPABILITIES caps; |
| 348 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); |
| 349 if (res == ERROR_DEVICE_NOT_CONNECTED) { |
| 350 pad->connected = false; |
| 351 return false; |
| 352 } else { |
| 353 pad->connected = true; |
| 354 base::swprintf(pad->id, |
| 355 WebGamepad::idLengthCap, |
| 356 L"Xbox 360 Controller (XInput STANDARD %ls)", |
| 357 GamepadSubTypeName(caps.SubType)); |
| 358 return true; |
| 359 } |
| 360 } |
| 361 |
| 362 void GamepadPlatformDataFetcherWin::GetXInputPadData( |
| 363 int i, |
| 364 WebGamepad* pad) { |
| 365 // We rely on device_changed and GetCapabilities to tell us that |
| 366 // something's been connected, but we will mark as disconnected if |
| 367 // GetState returns that we've lost the pad. |
| 368 if (!pad->connected) |
| 369 return; |
| 370 |
| 371 XINPUT_STATE state; |
| 372 memset(&state, 0, sizeof(XINPUT_STATE)); |
| 373 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); |
| 374 DWORD dwResult = xinput_get_state_(pad_state_[i].xinput_index, &state); |
| 375 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); |
| 376 |
| 377 if (dwResult == ERROR_SUCCESS) { |
| 378 pad->timestamp = state.dwPacketNumber; |
| 379 pad->buttonsLength = 0; |
| 380 #define ADD(b) pad->buttons[pad->buttonsLength++] = \ |
| 381 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); |
| 382 ADD(XINPUT_GAMEPAD_A); |
| 383 ADD(XINPUT_GAMEPAD_B); |
| 384 ADD(XINPUT_GAMEPAD_X); |
| 385 ADD(XINPUT_GAMEPAD_Y); |
| 386 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); |
| 387 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); |
| 388 pad->buttons[pad->buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; |
| 389 pad->buttons[pad->buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; |
| 390 ADD(XINPUT_GAMEPAD_BACK); |
| 391 ADD(XINPUT_GAMEPAD_START); |
| 392 ADD(XINPUT_GAMEPAD_LEFT_THUMB); |
| 393 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); |
| 394 ADD(XINPUT_GAMEPAD_DPAD_UP); |
| 395 ADD(XINPUT_GAMEPAD_DPAD_DOWN); |
| 396 ADD(XINPUT_GAMEPAD_DPAD_LEFT); |
| 397 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); |
| 398 #undef ADD |
| 399 pad->axesLength = 0; |
| 400 // XInput are +up/+right, -down/-left, we want -up/-left. |
| 401 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbLX); |
| 402 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbLY); |
| 403 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbRX); |
| 404 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbRY); |
| 405 } else { |
| 406 pad->connected = false; |
| 407 } |
| 408 } |
| 409 |
| 410 void GamepadPlatformDataFetcherWin::GetDirectInputPadData( |
| 411 int index, |
| 412 WebGamepad* pad) { |
| 413 if (!pad->connected) |
| 414 return; |
| 415 |
| 416 IDirectInputDevice8* gamepad = pad_state_[index].directinput_gamepad; |
| 417 if (FAILED(gamepad->Poll())) { |
| 418 // Polling didn't work, try acquiring the gamepad. |
| 419 if (FAILED(gamepad->Acquire())) { |
| 420 pad->buttonsLength = 0; |
| 421 pad->axesLength = 0; |
| 422 return; |
95 } | 423 } |
96 } | 424 // Try polling again. |
97 | 425 if (FAILED(gamepad->Poll())) { |
98 // We've updated the connection state if necessary, now update the actual | 426 pad->buttonsLength = 0; |
99 // data for the devices that are connected. | 427 pad->axesLength = 0; |
100 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 428 return; |
101 WebGamepad& pad = pads->items[i]; | |
102 | |
103 // We rely on device_changed and GetCapabilities to tell us that | |
104 // something's been connected, but we will mark as disconnected if | |
105 // GetState returns that we've lost the pad. | |
106 if (!pad.connected) | |
107 continue; | |
108 | |
109 XINPUT_STATE state; | |
110 memset(&state, 0, sizeof(XINPUT_STATE)); | |
111 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | |
112 DWORD dwResult = xinput_get_state_(i, &state); | |
113 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | |
114 | |
115 if (dwResult == ERROR_SUCCESS) { | |
116 pad.timestamp = state.dwPacketNumber; | |
117 pad.buttonsLength = 0; | |
118 #define ADD(b) pad.buttons[pad.buttonsLength++] = \ | |
119 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); | |
120 ADD(XINPUT_GAMEPAD_A); | |
121 ADD(XINPUT_GAMEPAD_B); | |
122 ADD(XINPUT_GAMEPAD_X); | |
123 ADD(XINPUT_GAMEPAD_Y); | |
124 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | |
125 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | |
126 pad.buttons[pad.buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; | |
127 pad.buttons[pad.buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; | |
128 ADD(XINPUT_GAMEPAD_BACK); | |
129 ADD(XINPUT_GAMEPAD_START); | |
130 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | |
131 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | |
132 ADD(XINPUT_GAMEPAD_DPAD_UP); | |
133 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | |
134 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | |
135 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | |
136 #undef ADD | |
137 pad.axesLength = 0; | |
138 // XInput are +up/+right, -down/-left, we want -up/-left. | |
139 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbLX); | |
140 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbLY); | |
141 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbRX); | |
142 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbRY); | |
143 } else { | |
144 pad.connected = false; | |
145 } | 429 } |
146 } | 430 } |
147 } | 431 JoyData state; |
148 | 432 if (FAILED(gamepad->GetDeviceState(sizeof(JoyData), &state))) { |
149 bool GamepadPlatformDataFetcherWin::GetXinputDllFunctions() { | 433 pad->connected = false; |
| 434 return; |
| 435 } |
| 436 |
| 437 WebGamepad raw; |
| 438 raw.connected = true; |
| 439 for (int i = 0; i < 16; i++) |
| 440 raw.buttons[i] = (state.buttons[i] & 0x80) ? 1.0 : 0.0; |
| 441 |
| 442 // We map the POV (often a D-pad) into the buttons 16-19. |
| 443 // DirectInput gives pov measurements in hundredths of degrees, |
| 444 // clockwise from "North". |
| 445 // We use 22.5 degree slices so we can handle diagonal D-raw presses. |
| 446 static const int arc_segment = 2250; // 22.5 degrees = 1/16 circle |
| 447 if (state.pov > arc_segment && state.pov < 7 * arc_segment) |
| 448 raw.buttons[19] = 1.0; |
| 449 else |
| 450 raw.buttons[19] = 0.0; |
| 451 |
| 452 if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment) |
| 453 raw.buttons[17] = 1.0; |
| 454 else |
| 455 raw.buttons[17] = 0.0; |
| 456 |
| 457 if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment) |
| 458 raw.buttons[18] = 1.0; |
| 459 else |
| 460 raw.buttons[18] = 0.0; |
| 461 |
| 462 if (state.pov < 3 * arc_segment || |
| 463 (state.pov > 13 * arc_segment && state.pov < 36000)) |
| 464 raw.buttons[16] = 1.0; |
| 465 else |
| 466 raw.buttons[16] = 0.0; |
| 467 |
| 468 for (int i = 0; i < 10; i++) |
| 469 raw.axes[i] = state.axes[i]; |
| 470 pad_state_[index].mapper(raw, pad); |
| 471 } |
| 472 |
| 473 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { |
150 xinput_get_capabilities_ = NULL; | 474 xinput_get_capabilities_ = NULL; |
151 xinput_get_state_ = NULL; | 475 xinput_get_state_ = NULL; |
152 xinput_enable_ = static_cast<XInputEnableFunc>( | 476 xinput_enable_ = static_cast<XInputEnableFunc>( |
153 xinput_dll_.GetFunctionPointer("XInputEnable")); | 477 xinput_dll_.GetFunctionPointer("XInputEnable")); |
154 if (!xinput_enable_) | 478 if (!xinput_enable_) |
155 return false; | 479 return false; |
156 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>( | 480 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>( |
157 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); | 481 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); |
158 if (!xinput_get_capabilities_) | 482 if (!xinput_get_capabilities_) |
159 return false; | 483 return false; |
160 xinput_get_state_ = static_cast<XInputGetStateFunc>( | 484 xinput_get_state_ = static_cast<XInputGetStateFunc>( |
161 xinput_dll_.GetFunctionPointer("XInputGetState")); | 485 xinput_dll_.GetFunctionPointer("XInputGetState")); |
162 if (!xinput_get_state_) | 486 if (!xinput_get_state_) |
163 return false; | 487 return false; |
164 xinput_enable_(true); | 488 xinput_enable_(true); |
165 return true; | 489 return true; |
166 } | 490 } |
167 | 491 |
168 } // namespace content | 492 } // namespace content |
OLD | NEW |