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