1 // Copyright 2014 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/raw_input_data_fetcher_win.h" 6 7 #include "base/debug/trace_event.h" 8 #include "content/common/gamepad_hardware_buffer.h" 9 #include "content/common/gamepad_messages.h" 10 11 namespace content { 12 13 using namespace blink; 14 15 namespace { 16 17 float NormalizeAxis(long value, long min, long max) { 18 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; 19 } 20 21 // From the HID Usage Tables specification. 22 USHORT DeviceUsages[] = { 23 0x04, // Joysticks 24 0x05, // Gamepads 25 0x08, // Multi Axis 26 }; 27 28 const uint32_t kAxisMinimumUsageNumber = 0x30; 29 const uint32_t kGameControlsUsagePage = 0x05; 30 31 } // namespace 32 33 RawInputDataFetcher::RawInputDataFetcher() 34 : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll"))) 35 , rawinput_available_(GetHidDllFunctions()) 36 , filter_xinput_(true) 37 , events_monitored_(false) {} 38 39 RawInputDataFetcher::~RawInputDataFetcher() { 40 DCHECK(!window_); 41 DCHECK(!events_monitored_); 42 } 43 44 void RawInputDataFetcher::WillDestroyCurrentMessageLoop() { 45 StopMonitor(); 46 } 47 48 RAWINPUTDEVICE* RawInputDataFetcher::GetRawInputDevices(DWORD flags) { 49 int usage_count = arraysize(DeviceUsages); 50 scoped_ptr<RAWINPUTDEVICE[]> devices(new RAWINPUTDEVICE[usage_count]); 51 for (int i = 0; i < usage_count; ++i) { 52 devices[i].dwFlags = flags; 53 devices[i].usUsagePage = 1; 54 devices[i].usUsage = DeviceUsages[i]; 55 devices[i].hwndTarget = window_->hwnd(); 56 } 57 return devices.release(); 58 } 59 60 void RawInputDataFetcher::StartMonitor() { 61 if (!rawinput_available_ || events_monitored_) 62 return; 63 64 if (!window_) { 65 window_.reset(new base::win::MessageWindow()); 66 if (!window_->Create(base::Bind(&RawInputDataFetcher::HandleMessage, 67 base::Unretained(this)))) { 68 PLOG(ERROR) << "Failed to create the raw input window"; 69 window_.reset(); 70 return; 71 } 72 } 73 74 // Register to receive raw HID input. 75 scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_INPUTSINK)); 76 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages), 77 sizeof(RAWINPUTDEVICE))) { 78 PLOG(ERROR) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK"; 79 window_.reset(); 80 return; 81 } 82 83 // Start observing message loop destruction if we start monitoring the first 84 // event. 85 if (!events_monitored_) 86 base::MessageLoop::current()->AddDestructionObserver(this); 87 88 events_monitored_ = true; 89 } 90 91 void RawInputDataFetcher::StopMonitor() { 92 if (!rawinput_available_ || !events_monitored_) 93 return; 94 95 // Stop receiving raw input. 96 DCHECK(window_); 97 scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE)); 98 99 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages), 100 sizeof(RAWINPUTDEVICE))) { 101 PLOG(INFO) << "RegisterRawInputDevices() failed for RIDEV_REMOVE"; 102 } 103 104 events_monitored_ = false; 105 window_.reset(); 106 ClearControllers(); 107 108 // Stop observing message loop destruction if no event is being monitored. 109 base::MessageLoop::current()->RemoveDestructionObserver(this); 110 } 111 112 void RawInputDataFetcher::ClearControllers() { 113 while (!controllers_.empty()) { 114 RawGamepadInfo* gamepad_info = controllers_.begin()->second; 115 controllers_.erase(gamepad_info->handle); 116 delete gamepad_info; 117 } 118 } 119 120 std::vector<RawGamepadInfo*> RawInputDataFetcher::EnumerateDevices() { 121 std::vector<RawGamepadInfo*> valid_controllers; 122 123 ClearControllers(); 124 125 UINT count = 0; 126 UINT result = GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)); 127 if (result == static_cast<UINT>(-1)) { 128 PLOG(ERROR) << "GetRawInputDeviceList() failed"; 129 return valid_controllers; 130 } 131 DCHECK_EQ(0u, result); 132 133 scoped_ptr<RAWINPUTDEVICELIST[]> device_list(new RAWINPUTDEVICELIST[count]); 134 result = GetRawInputDeviceList(device_list.get(), &count, 135 sizeof(RAWINPUTDEVICELIST)); 136 if (result == static_cast<UINT>(-1)) { 137 PLOG(ERROR) << "GetRawInputDeviceList() failed"; 138 return valid_controllers; 139 } 140 DCHECK_EQ(count, result); 141 142 for (UINT i = 0; i < count; ++i) { 143 if (device_list[i].dwType == RIM_TYPEHID) { 144 HANDLE device_handle = device_list[i].hDevice; 145 RawGamepadInfo* gamepad_info = ParseGamepadInfo(device_handle); 146 if (gamepad_info) { 147 controllers_[device_handle] = gamepad_info; 148 valid_controllers.push_back(gamepad_info); 149 } 150 } 151 } 152 return valid_controllers; 153 } 154 155 RawGamepadInfo* RawInputDataFetcher::GetGamepadInfo(HANDLE handle) { 156 std::map<HANDLE, RawGamepadInfo*>::iterator it = controllers_.find(handle); 157 if (it != controllers_.end()) 158 return it->second; 159 160 return NULL; 161 } 162 163 RawGamepadInfo* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice) { 164 UINT size = 0; 165 166 // Do we already have this device in the map? 167 if (GetGamepadInfo(hDevice)) 168 return NULL; 169 170 // Query basic device info. 171 UINT result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, 172 NULL, &size); 173 if (result == static_cast<UINT>(-1)) { 174 PLOG(ERROR) << "GetRawInputDeviceInfo() failed"; 175 return NULL; 176 } 177 DCHECK_EQ(0u, result); 178 179 scoped_ptr<uint8[]> di_buffer(new uint8[size]); 180 RID_DEVICE_INFO* device_info = 181 reinterpret_cast<RID_DEVICE_INFO*>(di_buffer.get()); 182 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, 183 di_buffer.get(), &size); 184 if (result == static_cast<UINT>(-1)) { 185 PLOG(ERROR) << "GetRawInputDeviceInfo() failed"; 186 return NULL; 187 } 188 DCHECK_EQ(size, result); 189 190 // Make sure this device is of a type that we want to observe. 191 bool valid_type = false; 192 for (int i = 0; i < arraysize(DeviceUsages); ++i) { 193 if (device_info->hid.usUsage == DeviceUsages[i]) { 194 valid_type = true; 195 break; 196 } 197 } 198 199 if (!valid_type) 200 return NULL; 201 202 scoped_ptr<RawGamepadInfo> gamepad_info(new RawGamepadInfo); 203 gamepad_info->handle = hDevice; 204 gamepad_info->report_id = 0; 205 gamepad_info->vendor_id = device_info->hid.dwVendorId; 206 gamepad_info->product_id = device_info->hid.dwProductId; 207 gamepad_info->buttons_length = 0; 208 ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons)); 209 gamepad_info->axes_length = 0; 210 ZeroMemory(gamepad_info->axes, sizeof(gamepad_info->axes)); 211 212 // Query device identifier 213 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, 214 NULL, &size); 215 if (result == static_cast<UINT>(-1)) { 216 PLOG(ERROR) << "GetRawInputDeviceInfo() failed"; 217 return NULL; 218 } 219 DCHECK_EQ(0u, result); 220 221 scoped_ptr<wchar_t[]> name_buffer(new wchar_t[size]); 222 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, 223 name_buffer.get(), &size); 224 if (result == static_cast<UINT>(-1)) { 225 PLOG(ERROR) << "GetRawInputDeviceInfo() failed"; 226 return NULL; 227 } 228 DCHECK_EQ(size, result); 229 230 // The presence of "IG_" in the device name indicates that this is an XInput 231 // Gamepad. Skip enumerating these devices and let the XInput path handle it. 232 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx 233 if (filter_xinput_ && wcsstr( name_buffer.get(), L"IG_" ) ) 234 return NULL; 235 236 // Get a friendly device name 237 BOOLEAN got_product_string = FALSE; 238 HANDLE hid_handle = CreateFile(name_buffer.get(), GENERIC_READ|GENERIC_WRITE, 239 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); 240 if (hid_handle) { 241 got_product_string = hidd_get_product_string_(hid_handle, gamepad_info->id, 242 sizeof(gamepad_info->id)); 243 CloseHandle(hid_handle); 244 } 245 246 if (!got_product_string) 247 swprintf(gamepad_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad"); 248 249 // Query device capabilities. 250 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA, 251 NULL, &size); 252 if (result == static_cast<UINT>(-1)) { 253 PLOG(ERROR) << "GetRawInputDeviceInfo() failed"; 254 return NULL; 255 } 256 DCHECK_EQ(0u, result); 257 258 gamepad_info->ppd_buffer.reset(new uint8[size]); 259 gamepad_info->preparsed_data = 260 reinterpret_cast<PHIDP_PREPARSED_DATA>(gamepad_info->ppd_buffer.get()); 261 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA, 262 gamepad_info->ppd_buffer.get(), &size); 263 if (result == static_cast<UINT>(-1)) { 264 PLOG(ERROR) << "GetRawInputDeviceInfo() failed"; 265 return NULL; 266 } 267 DCHECK_EQ(size, result); 268 269 HIDP_CAPS caps; 270 NTSTATUS status = hidp_get_caps_(gamepad_info->preparsed_data, &caps); 271 DCHECK_EQ(HIDP_STATUS_SUCCESS, status); 272 273 // Query button information. 274 USHORT count = caps.NumberInputButtonCaps; 275 if (count > 0) { 276 scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(new HIDP_BUTTON_CAPS[count]); 277 status = hidp_get_button_caps_( 278 HidP_Input, button_caps.get(), &count, gamepad_info->preparsed_data); 279 DCHECK_EQ(HIDP_STATUS_SUCCESS, status); 280 281 for (uint32_t i = 0; i < count; ++i) { 282 if (button_caps[i].Range.UsageMin <= WebGamepad::buttonsLengthCap) { 283 uint32_t max_index = 284 std::min(WebGamepad::buttonsLengthCap, 285 static_cast<size_t>(button_caps[i].Range.UsageMax)); 286 gamepad_info->buttons_length = std::max( 287 gamepad_info->buttons_length, max_index); 288 } 289 } 290 } 291 292 // Query axis information. 293 count = caps.NumberInputValueCaps; 294 scoped_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[count]); 295 status = hidp_get_value_caps_(HidP_Input, axes_caps.get(), &count, 296 gamepad_info->preparsed_data); 297 298 bool mapped_all_axes = true; 299 300 for (UINT i = 0; i < count; i++) { 301 uint32_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber; 302 if (axis_index < WebGamepad::axesLengthCap) { 303 gamepad_info->axes[axis_index].caps = axes_caps[i]; 304 gamepad_info->axes[axis_index].value = 0; 305 gamepad_info->axes[axis_index].active = true; 306 gamepad_info->axes_length = 307 std::max(gamepad_info->axes_length, axis_index + 1); 308 } else { 309 mapped_all_axes = false; 310 } 311 } 312 313 if (!mapped_all_axes) { 314 // For axes who's usage puts them outside the standard axesLengthCap range. 315 uint32_t next_index = 0; 316 for (UINT i = 0; i < count; i++) { 317 uint32_t usage = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber; 318 if (usage >= WebGamepad::axesLengthCap && 319 axes_caps[i].UsagePage <= kGameControlsUsagePage) { 320 321 for (; next_index < WebGamepad::axesLengthCap; ++next_index) { 322 if (!gamepad_info->axes[next_index].active) 323 break; 324 } 325 if (next_index < WebGamepad::axesLengthCap) { 326 gamepad_info->axes[next_index].caps = axes_caps[i]; 327 gamepad_info->axes[next_index].value = 0; 328 gamepad_info->axes[next_index].active = true; 329 gamepad_info->axes_length = 330 std::max(gamepad_info->axes_length, next_index + 1); 331 } 332 } 333 334 if (next_index >= WebGamepad::axesLengthCap) 335 break; 336 } 337 } 338 339 return gamepad_info.release(); 340 } 341 342 void RawInputDataFetcher::UpdateGamepad( 343 RAWINPUT* input, 344 RawGamepadInfo* gamepad_info) { 345 NTSTATUS status; 346 347 gamepad_info->report_id++; 348 349 // Query button state. 350 if (gamepad_info->buttons_length) { 351 // Clear the button state 352 ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons)); 353 ULONG buttons_length = 0; 354 355 hidp_get_usages_ex_(HidP_Input, 356 0, 357 NULL, 358 &buttons_length, 359 gamepad_info->preparsed_data, 360 reinterpret_cast<PCHAR>(input->data.hid.bRawData), 361 input->data.hid.dwSizeHid); 362 363 scoped_ptr<USAGE_AND_PAGE[]> usages(new USAGE_AND_PAGE[buttons_length]); 364 status = 365 hidp_get_usages_ex_(HidP_Input, 366 0, 367 usages.get(), 368 &buttons_length, 369 gamepad_info->preparsed_data, 370 reinterpret_cast<PCHAR>(input->data.hid.bRawData), 371 input->data.hid.dwSizeHid); 372 373 if (status == HIDP_STATUS_SUCCESS) { 374 // Set each reported button to true. 375 for (uint32_t j = 0; j < buttons_length; j++) { 376 int32_t button_index = usages[j].Usage - 1; 377 if (button_index >= 0 && 378 button_index < blink::WebGamepad::buttonsLengthCap) 379 gamepad_info->buttons[button_index] = true; 380 } 381 } 382 } 383 384 // Query axis state. 385 ULONG axis_value = 0; 386 LONG scaled_axis_value = 0; 387 for (uint32_t i = 0; i < gamepad_info->axes_length; i++) { 388 RawGamepadAxis* axis = &gamepad_info->axes[i]; 389 390 // If the min is < 0 we have to query the scaled value, otherwise we need 391 // the normal unscaled value. 392 if (axis->caps.LogicalMin < 0) { 393 status = hidp_get_scaled_usage_value_(HidP_Input, axis->caps.UsagePage, 0, 394 axis->caps.Range.UsageMin, &scaled_axis_value, 395 gamepad_info->preparsed_data, 396 reinterpret_cast<PCHAR>(input->data.hid.bRawData), 397 input->data.hid.dwSizeHid); 398 if (status == HIDP_STATUS_SUCCESS) { 399 axis->value = NormalizeAxis(scaled_axis_value, 400 axis->caps.LogicalMin, axis->caps.LogicalMax); 401 } 402 } else { 403 status = hidp_get_usage_value_(HidP_Input, axis->caps.UsagePage, 0, 404 axis->caps.Range.UsageMin, &axis_value, 405 gamepad_info->preparsed_data, 406 reinterpret_cast<PCHAR>(input->data.hid.bRawData), 407 input->data.hid.dwSizeHid); 408 if (status == HIDP_STATUS_SUCCESS) { 409 axis->value = NormalizeAxis(axis_value, 410 axis->caps.LogicalMin, axis->caps.LogicalMax); 411 } 412 } 413 } 414 } 415 416 LRESULT RawInputDataFetcher::OnInput(HRAWINPUT input_handle) { 417 // Get the size of the input record. 418 UINT size = 0; 419 UINT result = GetRawInputData( 420 input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); 421 if (result == static_cast<UINT>(-1)) { 422 PLOG(ERROR) << "GetRawInputData() failed"; 423 return 0; 424 } 425 DCHECK_EQ(0u, result); 426 427 // Retrieve the input record. 428 scoped_ptr<uint8[]> buffer(new uint8[size]); 429 RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get()); 430 result = GetRawInputData( 431 input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER)); 432 if (result == static_cast<UINT>(-1)) { 433 PLOG(ERROR) << "GetRawInputData() failed"; 434 return 0; 435 } 436 DCHECK_EQ(size, result); 437 438 // Notify the observer about events generated locally. 439 if (input->header.dwType == RIM_TYPEHID && input->header.hDevice != NULL) { 440 RawGamepadInfo* gamepad = GetGamepadInfo(input->header.hDevice); 441 if (gamepad) 442 UpdateGamepad(input, gamepad); 443 } 444 445 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER)); 446 } 447 448 bool RawInputDataFetcher::HandleMessage(UINT message, 449 WPARAM wparam, 450 LPARAM lparam, 451 LRESULT* result) { 452 switch (message) { 453 case WM_INPUT: 454 *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam)); 455 return true; 456 457 default: 458 return false; 459 } 460 } 461 462 bool RawInputDataFetcher::GetHidDllFunctions() { 463 hidp_get_caps_ = NULL; 464 hidp_get_button_caps_ = NULL; 465 hidp_get_value_caps_ = NULL; 466 hidp_get_usages_ex_ = NULL; 467 hidp_get_usage_value_ = NULL; 468 hidp_get_scaled_usage_value_ = NULL; 469 hidd_get_product_string_ = NULL; 470 471 if (!hid_dll_.is_valid()) return false; 472 473 hidp_get_caps_ = reinterpret_cast<HidPGetCapsFunc>( 474 hid_dll_.GetFunctionPointer("HidP_GetCaps")); 475 if (!hidp_get_caps_) 476 return false; 477 hidp_get_button_caps_ = reinterpret_cast<HidPGetButtonCapsFunc>( 478 hid_dll_.GetFunctionPointer("HidP_GetButtonCaps")); 479 if (!hidp_get_button_caps_) 480 return false; 481 hidp_get_value_caps_ = reinterpret_cast<HidPGetValueCapsFunc>( 482 hid_dll_.GetFunctionPointer("HidP_GetValueCaps")); 483 if (!hidp_get_value_caps_) 484 return false; 485 hidp_get_usages_ex_ = reinterpret_cast<HidPGetUsagesExFunc>( 486 hid_dll_.GetFunctionPointer("HidP_GetUsagesEx")); 487 if (!hidp_get_usages_ex_) 488 return false; 489 hidp_get_usage_value_ = reinterpret_cast<HidPGetUsageValueFunc>( 490 hid_dll_.GetFunctionPointer("HidP_GetUsageValue")); 491 if (!hidp_get_usage_value_) 492 return false; 493 hidp_get_scaled_usage_value_ = reinterpret_cast<HidPGetScaledUsageValueFunc>( 494 hid_dll_.GetFunctionPointer("HidP_GetScaledUsageValue")); 495 if (!hidp_get_scaled_usage_value_) 496 return false; 497 hidd_get_product_string_ = reinterpret_cast<HidDGetStringFunc>( 498 hid_dll_.GetFunctionPointer("HidD_GetProductString")); 499 if (!hidd_get_product_string_) 500 return false; 501 502 return true; 503 } 504 505 } // namespace content 506