Chromium Code Reviews| 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/raw_input_data_fetcher_win.h" | |
| 6 | |
| 7 #include "base/debug/trace_event.h" | |
| 8 //#include "base/win/windows_version.h" | |
| 9 #include "content/common/gamepad_hardware_buffer.h" | |
| 10 #include "content/common/gamepad_messages.h" | |
| 11 | |
| 12 namespace content { | |
| 13 | |
| 14 using namespace blink; | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 float NormalizeAxis(uint32_t value, uint32_t min, uint32_t max) { | |
| 19 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; | |
| 20 } | |
| 21 | |
| 22 // From the HID Usage Tables specification. | |
| 23 USHORT DeviceUsages[] = { | |
| 24 0x04, // Joysticks | |
| 25 0x05, // Gamepads | |
| 26 0x08, // Multi Axis | |
| 27 }; | |
| 28 | |
| 29 const uint32_t kAxisMinimumUsageNumber = 0x30; | |
| 30 | |
| 31 } // namespace | |
| 32 | |
| 33 RawInputDataFetcher::RawInputDataFetcher() | |
| 34 : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll"))) | |
|
scottmg
2014/01/31 20:51:10
can you run this through "git cl format"? there's
| |
| 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 LOG_GETLASTERROR(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 LOG_GETLASTERROR(ERROR) | |
| 79 << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK"; | |
| 80 window_.reset(); | |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 // Start observing message loop destruction if we start monitoring the first | |
| 85 // event. | |
| 86 if (!events_monitored_) | |
| 87 base::MessageLoop::current()->AddDestructionObserver(this); | |
| 88 | |
| 89 events_monitored_ = true; | |
| 90 } | |
| 91 | |
| 92 void RawInputDataFetcher::StopMonitor() { | |
| 93 if (!rawinput_available_ || !events_monitored_) | |
| 94 return; | |
| 95 | |
| 96 // Stop receiving raw input. | |
| 97 DCHECK(window_); | |
| 98 scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE)); | |
| 99 | |
| 100 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages), | |
| 101 sizeof(RAWINPUTDEVICE))) { | |
| 102 LOG_GETLASTERROR(INFO) | |
| 103 << "RegisterRawInputDevices() failed for RIDEV_REMOVE"; | |
| 104 } | |
| 105 | |
| 106 events_monitored_ = false; | |
| 107 window_.reset(); | |
| 108 ClearControllers(); | |
| 109 | |
| 110 // Stop observing message loop destruction if no event is being monitored. | |
| 111 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
| 112 } | |
| 113 | |
| 114 void RawInputDataFetcher::ClearControllers() { | |
| 115 while (!controllers_.empty()) { | |
| 116 RawJoystickInfo* joystick_info = controllers_.begin()->second; | |
| 117 controllers_.erase(joystick_info->handle); | |
| 118 delete joystick_info; | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 std::vector<RawJoystickInfo*> RawInputDataFetcher::EnumerateDevices() { | |
| 123 std::vector<RawJoystickInfo*> valid_controllers; | |
| 124 | |
| 125 ClearControllers(); | |
| 126 | |
| 127 UINT count = 0; | |
| 128 UINT result = GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)); | |
| 129 if (result == -1) { | |
|
scottmg
2014/01/31 20:51:10
UINT == -1? :/
| |
| 130 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceList() failed"; | |
| 131 return valid_controllers; | |
| 132 } | |
| 133 DCHECK_EQ(0u, result); | |
| 134 | |
| 135 scoped_ptr<RAWINPUTDEVICELIST[]> device_list(new RAWINPUTDEVICELIST[count]); | |
| 136 result = GetRawInputDeviceList(device_list.get(), &count, | |
| 137 sizeof(RAWINPUTDEVICELIST)); | |
| 138 if (result == -1) { | |
| 139 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceList() failed"; | |
| 140 return valid_controllers; | |
| 141 } | |
| 142 DCHECK_EQ(count, result); | |
| 143 | |
| 144 for (UINT i = 0; i < count; ++i) { | |
| 145 if (device_list[i].dwType == RIM_TYPEHID) { | |
| 146 HANDLE device_handle = device_list[i].hDevice; | |
| 147 RawJoystickInfo* joystick_info = ParseJoystickInfo(device_handle); | |
| 148 if (joystick_info) { | |
| 149 controllers_[device_handle] = joystick_info; | |
| 150 valid_controllers.push_back(joystick_info); | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 return valid_controllers; | |
| 155 } | |
| 156 | |
| 157 RawJoystickInfo* RawInputDataFetcher::GetJoystickInfo(HANDLE handle) { | |
| 158 std::map<HANDLE, RawJoystickInfo*>::iterator it = controllers_.find(handle); | |
| 159 if (it != controllers_.end()) | |
| 160 return it->second; | |
| 161 | |
| 162 return NULL; | |
| 163 } | |
| 164 | |
| 165 RawJoystickInfo* RawInputDataFetcher::ParseJoystickInfo(HANDLE hDevice) { | |
| 166 UINT size = 0; | |
| 167 | |
| 168 // Do we already have this device in the map? | |
| 169 if (GetJoystickInfo(hDevice)) | |
| 170 return NULL; | |
| 171 | |
| 172 // Query basic device info. | |
| 173 UINT result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, | |
| 174 NULL, &size); | |
| 175 if (result == -1) { | |
| 176 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; | |
| 177 return NULL; | |
| 178 } | |
| 179 DCHECK_EQ(0u, result); | |
| 180 | |
| 181 scoped_ptr<uint8[]> di_buffer(new uint8[size]); | |
| 182 RID_DEVICE_INFO* device_info = | |
| 183 reinterpret_cast<RID_DEVICE_INFO*>(di_buffer.get()); | |
| 184 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, | |
| 185 di_buffer.get(), &size); | |
| 186 if (result == -1) { | |
| 187 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; | |
| 188 return NULL; | |
| 189 } | |
| 190 DCHECK_EQ(size, result); | |
| 191 | |
| 192 // Make sure this device is of a type that we want to observe. | |
| 193 bool valid_type = false; | |
| 194 for(int i = 0; i < arraysize(DeviceUsages); ++i) { | |
| 195 if (device_info->hid.usUsage == DeviceUsages[i]) { | |
| 196 valid_type = true; | |
| 197 break; | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 if (!valid_type) | |
| 202 return NULL; | |
| 203 | |
| 204 scoped_ptr<RawJoystickInfo> joystick_info(new RawJoystickInfo); | |
| 205 joystick_info->handle = hDevice; | |
| 206 joystick_info->report_id = 0; | |
| 207 joystick_info->axes_length = 0; | |
| 208 joystick_info->buttons_length = 0; | |
| 209 | |
| 210 ZeroMemory(joystick_info->buttons, sizeof(joystick_info->buttons)); | |
| 211 ZeroMemory(joystick_info->axes, sizeof(joystick_info->axes)); | |
| 212 | |
| 213 joystick_info->vendor_id = device_info->hid.dwVendorId; | |
| 214 joystick_info->product_id = device_info->hid.dwProductId; | |
| 215 | |
| 216 // Query device name | |
| 217 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, | |
| 218 NULL, &size); | |
| 219 if (result == -1) { | |
| 220 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; | |
| 221 return NULL; | |
| 222 } | |
| 223 DCHECK_EQ(0u, result); | |
| 224 | |
| 225 scoped_ptr<wchar_t[]> name_buffer(new wchar_t[size]); | |
| 226 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, | |
| 227 name_buffer.get(), &size); | |
| 228 if (result == -1) { | |
| 229 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; | |
| 230 return NULL; | |
| 231 } | |
| 232 DCHECK_EQ(size, result); | |
| 233 | |
| 234 // The presence of "IG_" in the device name indicates that this is an XInput | |
| 235 // Gamepad. Skip enumerating these devices and let the XInput path handle it. | |
| 236 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx | |
| 237 if(filter_xinput_ && wcsstr( name_buffer.get(), L"IG_" ) ) { | |
| 238 return NULL; | |
| 239 } | |
| 240 | |
| 241 // Get the friendly device name | |
| 242 BOOLEAN got_product_string = FALSE; | |
| 243 HANDLE hid_handle = CreateFile(name_buffer.get(), GENERIC_READ|GENERIC_WRITE, | |
| 244 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); | |
| 245 if(hid_handle) { | |
| 246 got_product_string = hidd_get_product_string_(hid_handle, joystick_info->id, | |
| 247 sizeof(joystick_info->id)); | |
| 248 CloseHandle(hid_handle); | |
| 249 } | |
| 250 | |
| 251 if (!got_product_string) | |
| 252 swprintf(joystick_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad"); | |
| 253 | |
| 254 // Query device capabilities | |
| 255 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA, | |
| 256 NULL, &size); | |
| 257 if (result == -1) { | |
| 258 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; | |
| 259 return NULL; | |
| 260 } | |
| 261 DCHECK_EQ(0u, result); | |
| 262 | |
| 263 joystick_info->ppd_buffer.reset(new uint8[size]); | |
| 264 joystick_info->preparsed_data = | |
| 265 reinterpret_cast<PHIDP_PREPARSED_DATA>(joystick_info->ppd_buffer.get()); | |
| 266 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA, | |
| 267 joystick_info->ppd_buffer.get(), &size); | |
| 268 if (result == -1) { | |
| 269 LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; | |
| 270 return NULL; | |
| 271 } | |
| 272 DCHECK_EQ(size, result); | |
| 273 | |
| 274 HIDP_CAPS caps; | |
| 275 NTSTATUS status = hidp_get_caps_(joystick_info->preparsed_data, &caps); | |
| 276 DCHECK_EQ(HIDP_STATUS_SUCCESS, status); | |
| 277 | |
| 278 USHORT count = caps.NumberInputButtonCaps; | |
| 279 | |
| 280 if (count > 0) { | |
| 281 scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(new HIDP_BUTTON_CAPS[count]); | |
| 282 status = hidp_get_button_caps_(HidP_Input, button_caps.get(), &count, | |
| 283 joystick_info->preparsed_data); | |
| 284 DCHECK_EQ(HIDP_STATUS_SUCCESS, status); | |
| 285 | |
| 286 joystick_info->button_caps = button_caps[0]; | |
| 287 joystick_info->buttons_length = | |
| 288 joystick_info->button_caps.Range.UsageMax - | |
| 289 joystick_info->button_caps.Range.UsageMin + 1; | |
| 290 } | |
| 291 | |
| 292 count = caps.NumberInputValueCaps; | |
| 293 scoped_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[count]); | |
| 294 status = hidp_get_value_caps_(HidP_Input, axes_caps.get(), &count, | |
| 295 joystick_info->preparsed_data); | |
| 296 | |
| 297 for (UINT i = 0; i < count; i++) { | |
| 298 uint32_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber; | |
| 299 if (axis_index < WebGamepad::axesLengthCap) { | |
| 300 joystick_info->axes[axis_index].caps = axes_caps[i]; | |
| 301 joystick_info->axes[axis_index].value = 0; | |
| 302 joystick_info->axes_length = | |
| 303 std::max(joystick_info->axes_length, axis_index + 1); | |
| 304 } | |
| 305 } | |
| 306 | |
| 307 return joystick_info.release(); | |
| 308 } | |
| 309 | |
| 310 void RawInputDataFetcher::UpdateJoystick( | |
| 311 RAWINPUT* input, | |
| 312 RawJoystickInfo* joystick_info) { | |
| 313 NTSTATUS status; | |
| 314 | |
| 315 joystick_info->report_id++; | |
| 316 | |
| 317 // Query button state | |
| 318 if (joystick_info->buttons_length) { | |
| 319 ULONG buttons_length = joystick_info->buttons_length; | |
| 320 scoped_ptr<USAGE[]> usages(new USAGE[buttons_length]); | |
| 321 status = hidp_get_usages_(HidP_Input, | |
| 322 joystick_info->button_caps.UsagePage, 0, usages.get(), &buttons_length, | |
| 323 joystick_info->preparsed_data, | |
| 324 reinterpret_cast<PCHAR>(input->data.hid.bRawData), | |
| 325 input->data.hid.dwSizeHid); | |
| 326 if (status == HIDP_STATUS_SUCCESS) { | |
| 327 ZeroMemory(joystick_info->buttons, sizeof(joystick_info->buttons)); | |
| 328 ULONG button_index; | |
| 329 for(ULONG i = 0; i < buttons_length; i++) { | |
| 330 button_index = usages[i] - joystick_info->button_caps.Range.UsageMin; | |
| 331 joystick_info->buttons[button_index] = true; | |
| 332 } | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 // Query axis state | |
| 337 ULONG axis_value = 0; | |
| 338 for(uint32_t i = 0; i < joystick_info->axes_length; i++) { | |
| 339 RawJoystickAxis* axis = &joystick_info->axes[i]; | |
| 340 status = hidp_get_usage_value_(HidP_Input, axis->caps.UsagePage, 0, | |
| 341 axis->caps.Range.UsageMin, &axis_value, joystick_info->preparsed_data, | |
| 342 reinterpret_cast<PCHAR>(input->data.hid.bRawData), | |
| 343 input->data.hid.dwSizeHid); | |
| 344 if (status == HIDP_STATUS_SUCCESS) { | |
| 345 axis->value = NormalizeAxis(axis_value, | |
| 346 axis->caps.LogicalMin, axis->caps.LogicalMax); | |
| 347 } | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 LRESULT RawInputDataFetcher::OnInput(HRAWINPUT input_handle) { | |
| 352 // Get the size of the input record. | |
| 353 UINT size = 0; | |
| 354 UINT result = GetRawInputData( | |
| 355 input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); | |
| 356 if (result == -1) { | |
| 357 LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed"; | |
| 358 return 0; | |
| 359 } | |
| 360 DCHECK_EQ(0u, result); | |
| 361 | |
| 362 // Retrieve the input record itself. | |
| 363 scoped_ptr<uint8[]> buffer(new uint8[size]); | |
| 364 RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get()); | |
| 365 result = GetRawInputData( | |
| 366 input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER)); | |
| 367 if (result == -1) { | |
| 368 LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed"; | |
| 369 return 0; | |
| 370 } | |
| 371 DCHECK_EQ(size, result); | |
| 372 | |
| 373 // Notify the observer about events generated locally. | |
| 374 if (input->header.dwType == RIM_TYPEHID && input->header.hDevice != NULL) { | |
| 375 RawJoystickInfo* joystick = GetJoystickInfo(input->header.hDevice); | |
| 376 if (joystick) | |
| 377 UpdateJoystick(input, joystick); | |
| 378 } | |
| 379 | |
| 380 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER)); | |
| 381 } | |
| 382 | |
| 383 bool RawInputDataFetcher::HandleMessage(UINT message, | |
| 384 WPARAM wparam, | |
| 385 LPARAM lparam, | |
| 386 LRESULT* result) { | |
| 387 switch (message) { | |
| 388 case WM_INPUT: | |
| 389 *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam)); | |
| 390 return true; | |
| 391 | |
| 392 default: | |
| 393 return false; | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 bool RawInputDataFetcher::GetHidDllFunctions() { | |
| 398 hidp_get_caps_ = NULL; | |
| 399 hidp_get_button_caps_ = NULL; | |
| 400 hidp_get_value_caps_ = NULL; | |
| 401 hidp_get_usages_ = NULL; | |
| 402 hidp_get_usage_value_ = NULL; | |
|
scottmg
2014/01/31 20:51:10
is hid.dll always available? add if (!hid_dll_.is_
| |
| 403 | |
| 404 hidp_get_caps_ = reinterpret_cast<HidPGetCapsFunc>( | |
| 405 hid_dll_.GetFunctionPointer("HidP_GetCaps")); | |
| 406 if (!hidp_get_caps_) | |
| 407 return false; | |
| 408 hidp_get_button_caps_ = reinterpret_cast<HidPGetButtonCapsFunc>( | |
| 409 hid_dll_.GetFunctionPointer("HidP_GetButtonCaps")); | |
| 410 if (!hidp_get_button_caps_) | |
| 411 return false; | |
| 412 hidp_get_value_caps_ = reinterpret_cast<HidPGetValueCapsFunc>( | |
| 413 hid_dll_.GetFunctionPointer("HidP_GetValueCaps")); | |
| 414 if (!hidp_get_value_caps_) | |
| 415 return false; | |
| 416 hidp_get_usages_ = reinterpret_cast<HidPGetUsagesFunc>( | |
| 417 hid_dll_.GetFunctionPointer("HidP_GetUsages")); | |
| 418 if (!hidp_get_usages_) | |
| 419 return false; | |
| 420 hidp_get_usage_value_ = reinterpret_cast<HidPGetUsageValueFunc>( | |
| 421 hid_dll_.GetFunctionPointer("HidP_GetUsageValue")); | |
| 422 if (!hidp_get_usage_value_) | |
| 423 return false; | |
| 424 hidd_get_product_string_ = reinterpret_cast<HidDGetStringFunc>( | |
| 425 hid_dll_.GetFunctionPointer("HidD_GetProductString")); | |
| 426 if (!hidd_get_product_string_) | |
| 427 return false; | |
| 428 | |
| 429 return true; | |
| 430 } | |
| 431 | |
| 432 } // namespace content | |
| OLD | NEW |