| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <string.h> | |
| 9 | |
| 10 #include "base/strings/stringprintf.h" | |
| 11 #include "base/trace_event/trace_event.h" | |
| 12 #include "base/win/windows_version.h" | |
| 13 #include "content/common/gamepad_hardware_buffer.h" | |
| 14 #include "content/common/gamepad_messages.h" | |
| 15 | |
| 16 namespace content { | |
| 17 | |
| 18 using namespace blink; | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // See http://goo.gl/5VSJR. These are not available in all versions of the | |
| 23 // header, but they can be returned from the driver, so we define our own | |
| 24 // versions here. | |
| 25 static const BYTE kDeviceSubTypeGamepad = 1; | |
| 26 static const BYTE kDeviceSubTypeWheel = 2; | |
| 27 static const BYTE kDeviceSubTypeArcadeStick = 3; | |
| 28 static const BYTE kDeviceSubTypeFlightStick = 4; | |
| 29 static const BYTE kDeviceSubTypeDancePad = 5; | |
| 30 static const BYTE kDeviceSubTypeGuitar = 6; | |
| 31 static const BYTE kDeviceSubTypeGuitarAlternate = 7; | |
| 32 static const BYTE kDeviceSubTypeDrumKit = 8; | |
| 33 static const BYTE kDeviceSubTypeGuitarBass = 11; | |
| 34 static const BYTE kDeviceSubTypeArcadePad = 19; | |
| 35 | |
| 36 float NormalizeXInputAxis(SHORT value) { | |
| 37 return ((value + 32768.f) / 32767.5f) - 1.f; | |
| 38 } | |
| 39 | |
| 40 const WebUChar* GamepadSubTypeName(BYTE sub_type) { | |
| 41 switch (sub_type) { | |
| 42 case kDeviceSubTypeGamepad: return L"GAMEPAD"; | |
| 43 case kDeviceSubTypeWheel: return L"WHEEL"; | |
| 44 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; | |
| 45 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; | |
| 46 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; | |
| 47 case kDeviceSubTypeGuitar: return L"GUITAR"; | |
| 48 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; | |
| 49 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; | |
| 50 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; | |
| 51 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; | |
| 52 default: return L"<UNKNOWN>"; | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 const WebUChar* XInputDllFileName() { | |
| 57 // Xinput.h defines filename (XINPUT_DLL) on different Windows versions, but | |
| 58 // Xinput.h specifies it in build time. Approach here uses the same values | |
| 59 // and it is resolving dll filename based on Windows version it is running on. | |
| 60 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { | |
| 61 // For Windows 8 and 10, XINPUT_DLL is xinput1_4.dll. | |
| 62 return FILE_PATH_LITERAL("xinput1_4.dll"); | |
| 63 } else if (base::win::GetVersion() >= base::win::VERSION_WIN7) { | |
| 64 return FILE_PATH_LITERAL("xinput9_1_0.dll"); | |
| 65 } else { | |
| 66 NOTREACHED(); | |
| 67 return nullptr; | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 } // namespace | |
| 72 | |
| 73 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() | |
| 74 : xinput_dll_(base::FilePath(XInputDllFileName())), | |
| 75 xinput_available_(GetXInputDllFunctions()) { | |
| 76 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
| 77 platform_pad_state_[i].status = DISCONNECTED; | |
| 78 pad_state_[i].mapper = NULL; | |
| 79 pad_state_[i].axis_mask = 0; | |
| 80 pad_state_[i].button_mask = 0; | |
| 81 } | |
| 82 | |
| 83 raw_input_fetcher_.reset(new RawInputDataFetcher()); | |
| 84 raw_input_fetcher_->StartMonitor(); | |
| 85 } | |
| 86 | |
| 87 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { | |
| 88 raw_input_fetcher_->StopMonitor(); | |
| 89 } | |
| 90 | |
| 91 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const { | |
| 92 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
| 93 if (platform_pad_state_[i].status == DISCONNECTED) | |
| 94 return i; | |
| 95 } | |
| 96 return -1; | |
| 97 } | |
| 98 | |
| 99 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const { | |
| 100 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
| 101 if (platform_pad_state_[i].status == XINPUT_CONNECTED && | |
| 102 platform_pad_state_[i].xinput_index == index) | |
| 103 return true; | |
| 104 } | |
| 105 return false; | |
| 106 } | |
| 107 | |
| 108 bool GamepadPlatformDataFetcherWin::HasRawInputGamepad( | |
| 109 const HANDLE handle) const { | |
| 110 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
| 111 if (platform_pad_state_[i].status == RAWINPUT_CONNECTED && | |
| 112 platform_pad_state_[i].raw_input_handle == handle) | |
| 113 return true; | |
| 114 } | |
| 115 return false; | |
| 116 } | |
| 117 | |
| 118 void GamepadPlatformDataFetcherWin::EnumerateDevices() { | |
| 119 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); | |
| 120 | |
| 121 // Mark all disconnected pads DISCONNECTED. | |
| 122 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
| 123 if (!pad_state_[i].data.connected) | |
| 124 platform_pad_state_[i].status = DISCONNECTED; | |
| 125 } | |
| 126 | |
| 127 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { | |
| 128 if (HasXInputGamepad(i)) | |
| 129 continue; | |
| 130 int pad_index = FirstAvailableGamepadId(); | |
| 131 if (pad_index == -1) | |
| 132 return; // We can't add any more gamepads. | |
| 133 WebGamepad& pad = pad_state_[pad_index].data; | |
| 134 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) { | |
| 135 platform_pad_state_[pad_index].status = XINPUT_CONNECTED; | |
| 136 platform_pad_state_[pad_index].xinput_index = i; | |
| 137 pad_state_[pad_index].mapper = NULL; | |
| 138 pad_state_[pad_index].axis_mask = 0; | |
| 139 pad_state_[pad_index].button_mask = 0; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 if (raw_input_fetcher_->Available()) { | |
| 144 std::vector<RawGamepadInfo*> raw_inputs = | |
| 145 raw_input_fetcher_->EnumerateDevices(); | |
| 146 for (size_t i = 0; i < raw_inputs.size(); ++i) { | |
| 147 RawGamepadInfo* gamepad = raw_inputs[i]; | |
| 148 if (gamepad->buttons_length == 0 && gamepad->axes_length == 0) | |
| 149 continue; | |
| 150 if (HasRawInputGamepad(gamepad->handle)) | |
| 151 continue; | |
| 152 int pad_index = FirstAvailableGamepadId(); | |
| 153 if (pad_index == -1) | |
| 154 return; | |
| 155 WebGamepad& pad = pad_state_[pad_index].data; | |
| 156 pad.connected = true; | |
| 157 PadState& state = pad_state_[pad_index]; | |
| 158 PlatformPadState& platform_state = platform_pad_state_[pad_index]; | |
| 159 platform_state.status = RAWINPUT_CONNECTED; | |
| 160 platform_state.raw_input_handle = gamepad->handle; | |
| 161 | |
| 162 std::string vendor = base::StringPrintf("%04x", gamepad->vendor_id); | |
| 163 std::string product = base::StringPrintf("%04x", gamepad->product_id); | |
| 164 state.mapper = GetGamepadStandardMappingFunction(vendor, product); | |
| 165 state.axis_mask = 0; | |
| 166 state.button_mask = 0; | |
| 167 | |
| 168 swprintf(pad.id, WebGamepad::idLengthCap, | |
| 169 L"%ls (%lsVendor: %04x Product: %04x)", | |
| 170 gamepad->id, state.mapper ? L"STANDARD GAMEPAD " : L"", | |
| 171 gamepad->vendor_id, gamepad->product_id); | |
| 172 | |
| 173 if (state.mapper) | |
| 174 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard"); | |
| 175 else | |
| 176 pad.mapping[0] = 0; | |
| 177 } | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, | |
| 182 bool devices_changed_hint) { | |
| 183 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); | |
| 184 | |
| 185 if (!xinput_available_ && | |
| 186 !raw_input_fetcher_->Available()) { | |
| 187 pads->length = 0; | |
| 188 return; | |
| 189 } | |
| 190 | |
| 191 // A note on XInput devices: | |
| 192 // If we got notification that system devices have been updated, then | |
| 193 // run GetCapabilities to update the connected status and the device | |
| 194 // identifier. It can be slow to do to both GetCapabilities and | |
| 195 // GetState on unconnected devices, so we want to avoid a 2-5ms pause | |
| 196 // here by only doing this when the devices are updated (despite | |
| 197 // documentation claiming it's OK to call it any time). | |
| 198 if (devices_changed_hint) | |
| 199 EnumerateDevices(); | |
| 200 | |
| 201 pads->length = 0; | |
| 202 | |
| 203 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
| 204 // We rely on device_changed and GetCapabilities to tell us that | |
| 205 // something's been connected, but we will mark as disconnected if | |
| 206 // Get___PadState returns that we've lost the pad. | |
| 207 if (!pad_state_[i].data.connected) | |
| 208 continue; | |
| 209 | |
| 210 if (platform_pad_state_[i].status == XINPUT_CONNECTED) | |
| 211 GetXInputPadData(i, &pad_state_[i].data); | |
| 212 else if (platform_pad_state_[i].status == RAWINPUT_CONNECTED) | |
| 213 GetRawInputPadData(i, &pad_state_[i].data); | |
| 214 | |
| 215 MapAndSanitizeGamepadData(&pad_state_[i], &pads->items[i]); | |
| 216 | |
| 217 if (pads->items[i].connected) | |
| 218 pads->length++; | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 void GamepadPlatformDataFetcherWin::PauseHint(bool pause) { | |
| 223 if (pause) | |
| 224 raw_input_fetcher_->StopMonitor(); | |
| 225 else | |
| 226 raw_input_fetcher_->StartMonitor(); | |
| 227 } | |
| 228 | |
| 229 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( | |
| 230 int i, | |
| 231 WebGamepad* pad) const { | |
| 232 DCHECK(pad); | |
| 233 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i); | |
| 234 XINPUT_CAPABILITIES caps; | |
| 235 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); | |
| 236 if (res == ERROR_DEVICE_NOT_CONNECTED) { | |
| 237 pad->connected = false; | |
| 238 return false; | |
| 239 } else { | |
| 240 pad->connected = true; | |
| 241 swprintf(pad->id, | |
| 242 WebGamepad::idLengthCap, | |
| 243 L"Xbox 360 Controller (XInput STANDARD %ls)", | |
| 244 GamepadSubTypeName(caps.SubType)); | |
| 245 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard"); | |
| 246 return true; | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 void GamepadPlatformDataFetcherWin::GetXInputPadData( | |
| 251 int i, | |
| 252 WebGamepad* pad) { | |
| 253 XINPUT_STATE state; | |
| 254 memset(&state, 0, sizeof(XINPUT_STATE)); | |
| 255 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | |
| 256 DWORD dwResult = xinput_get_state_(platform_pad_state_[i].xinput_index, | |
| 257 &state); | |
| 258 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | |
| 259 | |
| 260 if (dwResult == ERROR_SUCCESS) { | |
| 261 pad->timestamp = state.dwPacketNumber; | |
| 262 pad->buttonsLength = 0; | |
| 263 WORD val = state.Gamepad.wButtons; | |
| 264 #define ADD(b) pad->buttons[pad->buttonsLength].pressed = (val & (b)) != 0; \ | |
| 265 pad->buttons[pad->buttonsLength++].value = ((val & (b)) ? 1.f : 0.f); | |
| 266 ADD(XINPUT_GAMEPAD_A); | |
| 267 ADD(XINPUT_GAMEPAD_B); | |
| 268 ADD(XINPUT_GAMEPAD_X); | |
| 269 ADD(XINPUT_GAMEPAD_Y); | |
| 270 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | |
| 271 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | |
| 272 | |
| 273 pad->buttons[pad->buttonsLength].pressed = | |
| 274 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; | |
| 275 pad->buttons[pad->buttonsLength++].value = | |
| 276 state.Gamepad.bLeftTrigger / 255.f; | |
| 277 | |
| 278 pad->buttons[pad->buttonsLength].pressed = | |
| 279 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; | |
| 280 pad->buttons[pad->buttonsLength++].value = | |
| 281 state.Gamepad.bRightTrigger / 255.f; | |
| 282 | |
| 283 ADD(XINPUT_GAMEPAD_BACK); | |
| 284 ADD(XINPUT_GAMEPAD_START); | |
| 285 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | |
| 286 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | |
| 287 ADD(XINPUT_GAMEPAD_DPAD_UP); | |
| 288 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | |
| 289 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | |
| 290 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | |
| 291 #undef ADD | |
| 292 pad->axesLength = 0; | |
| 293 | |
| 294 float value = 0.0; | |
| 295 #define ADD(a, factor) value = factor * NormalizeXInputAxis(a); \ | |
| 296 pad->axes[pad->axesLength++] = value; | |
| 297 | |
| 298 // XInput are +up/+right, -down/-left, we want -up/-left. | |
| 299 ADD(state.Gamepad.sThumbLX, 1); | |
| 300 ADD(state.Gamepad.sThumbLY, -1); | |
| 301 ADD(state.Gamepad.sThumbRX, 1); | |
| 302 ADD(state.Gamepad.sThumbRY, -1); | |
| 303 #undef ADD | |
| 304 } else { | |
| 305 pad->connected = false; | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 void GamepadPlatformDataFetcherWin::GetRawInputPadData( | |
| 310 int index, | |
| 311 WebGamepad* pad) { | |
| 312 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo( | |
| 313 platform_pad_state_[index].raw_input_handle); | |
| 314 if (!gamepad) { | |
| 315 pad->connected = false; | |
| 316 return; | |
| 317 } | |
| 318 | |
| 319 pad->timestamp = gamepad->report_id; | |
| 320 pad->buttonsLength = gamepad->buttons_length; | |
| 321 pad->axesLength = gamepad->axes_length; | |
| 322 | |
| 323 for (unsigned int i = 0; i < pad->buttonsLength; i++) { | |
| 324 pad->buttons[i].pressed = gamepad->buttons[i]; | |
| 325 pad->buttons[i].value = gamepad->buttons[i] ? 1.0 : 0.0; | |
| 326 } | |
| 327 | |
| 328 for (unsigned int i = 0; i < pad->axesLength; i++) | |
| 329 pad->axes[i] = gamepad->axes[i].value; | |
| 330 } | |
| 331 | |
| 332 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { | |
| 333 xinput_get_capabilities_ = NULL; | |
| 334 xinput_get_state_ = NULL; | |
| 335 XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>( | |
| 336 xinput_dll_.GetFunctionPointer("XInputEnable")); | |
| 337 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>( | |
| 338 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); | |
| 339 if (!xinput_get_capabilities_) | |
| 340 return false; | |
| 341 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>( | |
| 342 xinput_dll_.GetFunctionPointer("XInputGetState")); | |
| 343 if (!xinput_get_state_) | |
| 344 return false; | |
| 345 if (xinput_enable) { | |
| 346 // XInputEnable is unavailable before Win8 and deprecated in Win10. | |
| 347 xinput_enable(true); | |
| 348 } | |
| 349 return true; | |
| 350 } | |
| 351 | |
| 352 } // namespace content | |
| OLD | NEW |