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