Chromium Code Reviews| Index: content/browser/gamepad/raw_input_data_fetcher_win.cc |
| diff --git a/content/browser/gamepad/raw_input_data_fetcher_win.cc b/content/browser/gamepad/raw_input_data_fetcher_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6ac1e3eb3f4243ee4cea1922109e479ae5df540b |
| --- /dev/null |
| +++ b/content/browser/gamepad/raw_input_data_fetcher_win.cc |
| @@ -0,0 +1,432 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/browser/gamepad/raw_input_data_fetcher_win.h" |
| + |
| +#include "base/debug/trace_event.h" |
| +//#include "base/win/windows_version.h" |
| +#include "content/common/gamepad_hardware_buffer.h" |
| +#include "content/common/gamepad_messages.h" |
| + |
| +namespace content { |
| + |
| +using namespace blink; |
| + |
| +namespace { |
| + |
| +float NormalizeAxis(uint32_t value, uint32_t min, uint32_t max) { |
| + return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; |
| +} |
| + |
| +// From the HID Usage Tables specification. |
| +USHORT DeviceUsages[] = { |
| + 0x04, // Joysticks |
| + 0x05, // Gamepads |
| + 0x08, // Multi Axis |
| +}; |
| + |
| +const uint32_t kAxisMinimumUsageNumber = 0x30; |
| + |
| +} // namespace |
| + |
| +RawInputDataFetcher::RawInputDataFetcher() |
| + : 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
|
| + , rawinput_available_(GetHidDllFunctions()) |
| + , filter_xinput_(true) |
| + , events_monitored_(false) {} |
| + |
| +RawInputDataFetcher::~RawInputDataFetcher() { |
| + DCHECK(!window_); |
| + DCHECK(!events_monitored_); |
| +} |
| + |
| +void RawInputDataFetcher::WillDestroyCurrentMessageLoop() { |
| + StopMonitor(); |
| +} |
| + |
| +RAWINPUTDEVICE* RawInputDataFetcher::GetRawInputDevices(DWORD flags) { |
| + int usage_count = arraysize(DeviceUsages); |
| + scoped_ptr<RAWINPUTDEVICE[]> devices(new RAWINPUTDEVICE[usage_count]); |
| + for(int i = 0; i < usage_count; ++i) { |
| + devices[i].dwFlags = flags; |
| + devices[i].usUsagePage = 1; |
| + devices[i].usUsage = DeviceUsages[i]; |
| + devices[i].hwndTarget = window_->hwnd(); |
| + } |
| + return devices.release(); |
| +} |
| + |
| +void RawInputDataFetcher::StartMonitor() { |
| + if (!rawinput_available_ || events_monitored_) |
| + return; |
| + |
| + if (!window_) { |
| + window_.reset(new base::win::MessageWindow()); |
| + if (!window_->Create(base::Bind(&RawInputDataFetcher::HandleMessage, |
| + base::Unretained(this)))) { |
| + LOG_GETLASTERROR(ERROR) << "Failed to create the raw input window"; |
| + window_.reset(); |
| + return; |
| + } |
| + } |
| + |
| + // Register to receive raw HID input. |
| + scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_INPUTSINK)); |
| + if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages), |
| + sizeof(RAWINPUTDEVICE))) { |
| + LOG_GETLASTERROR(ERROR) |
| + << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK"; |
| + window_.reset(); |
| + return; |
| + } |
| + |
| + // Start observing message loop destruction if we start monitoring the first |
| + // event. |
| + if (!events_monitored_) |
| + base::MessageLoop::current()->AddDestructionObserver(this); |
| + |
| + events_monitored_ = true; |
| +} |
| + |
| +void RawInputDataFetcher::StopMonitor() { |
| + if (!rawinput_available_ || !events_monitored_) |
| + return; |
| + |
| + // Stop receiving raw input. |
| + DCHECK(window_); |
| + scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE)); |
| + |
| + if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages), |
| + sizeof(RAWINPUTDEVICE))) { |
| + LOG_GETLASTERROR(INFO) |
| + << "RegisterRawInputDevices() failed for RIDEV_REMOVE"; |
| + } |
| + |
| + events_monitored_ = false; |
| + window_.reset(); |
| + ClearControllers(); |
| + |
| + // Stop observing message loop destruction if no event is being monitored. |
| + base::MessageLoop::current()->RemoveDestructionObserver(this); |
| +} |
| + |
| +void RawInputDataFetcher::ClearControllers() { |
| + while (!controllers_.empty()) { |
| + RawJoystickInfo* joystick_info = controllers_.begin()->second; |
| + controllers_.erase(joystick_info->handle); |
| + delete joystick_info; |
| + } |
| +} |
| + |
| +std::vector<RawJoystickInfo*> RawInputDataFetcher::EnumerateDevices() { |
| + std::vector<RawJoystickInfo*> valid_controllers; |
| + |
| + ClearControllers(); |
| + |
| + UINT count = 0; |
| + UINT result = GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)); |
| + if (result == -1) { |
|
scottmg
2014/01/31 20:51:10
UINT == -1? :/
|
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceList() failed"; |
| + return valid_controllers; |
| + } |
| + DCHECK_EQ(0u, result); |
| + |
| + scoped_ptr<RAWINPUTDEVICELIST[]> device_list(new RAWINPUTDEVICELIST[count]); |
| + result = GetRawInputDeviceList(device_list.get(), &count, |
| + sizeof(RAWINPUTDEVICELIST)); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceList() failed"; |
| + return valid_controllers; |
| + } |
| + DCHECK_EQ(count, result); |
| + |
| + for (UINT i = 0; i < count; ++i) { |
| + if (device_list[i].dwType == RIM_TYPEHID) { |
| + HANDLE device_handle = device_list[i].hDevice; |
| + RawJoystickInfo* joystick_info = ParseJoystickInfo(device_handle); |
| + if (joystick_info) { |
| + controllers_[device_handle] = joystick_info; |
| + valid_controllers.push_back(joystick_info); |
| + } |
| + } |
| + } |
| + return valid_controllers; |
| +} |
| + |
| +RawJoystickInfo* RawInputDataFetcher::GetJoystickInfo(HANDLE handle) { |
| + std::map<HANDLE, RawJoystickInfo*>::iterator it = controllers_.find(handle); |
| + if (it != controllers_.end()) |
| + return it->second; |
| + |
| + return NULL; |
| +} |
| + |
| +RawJoystickInfo* RawInputDataFetcher::ParseJoystickInfo(HANDLE hDevice) { |
| + UINT size = 0; |
| + |
| + // Do we already have this device in the map? |
| + if (GetJoystickInfo(hDevice)) |
| + return NULL; |
| + |
| + // Query basic device info. |
| + UINT result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, |
| + NULL, &size); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; |
| + return NULL; |
| + } |
| + DCHECK_EQ(0u, result); |
| + |
| + scoped_ptr<uint8[]> di_buffer(new uint8[size]); |
| + RID_DEVICE_INFO* device_info = |
| + reinterpret_cast<RID_DEVICE_INFO*>(di_buffer.get()); |
| + result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, |
| + di_buffer.get(), &size); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; |
| + return NULL; |
| + } |
| + DCHECK_EQ(size, result); |
| + |
| + // Make sure this device is of a type that we want to observe. |
| + bool valid_type = false; |
| + for(int i = 0; i < arraysize(DeviceUsages); ++i) { |
| + if (device_info->hid.usUsage == DeviceUsages[i]) { |
| + valid_type = true; |
| + break; |
| + } |
| + } |
| + |
| + if (!valid_type) |
| + return NULL; |
| + |
| + scoped_ptr<RawJoystickInfo> joystick_info(new RawJoystickInfo); |
| + joystick_info->handle = hDevice; |
| + joystick_info->report_id = 0; |
| + joystick_info->axes_length = 0; |
| + joystick_info->buttons_length = 0; |
| + |
| + ZeroMemory(joystick_info->buttons, sizeof(joystick_info->buttons)); |
| + ZeroMemory(joystick_info->axes, sizeof(joystick_info->axes)); |
| + |
| + joystick_info->vendor_id = device_info->hid.dwVendorId; |
| + joystick_info->product_id = device_info->hid.dwProductId; |
| + |
| + // Query device name |
| + result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, |
| + NULL, &size); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; |
| + return NULL; |
| + } |
| + DCHECK_EQ(0u, result); |
| + |
| + scoped_ptr<wchar_t[]> name_buffer(new wchar_t[size]); |
| + result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, |
| + name_buffer.get(), &size); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; |
| + return NULL; |
| + } |
| + DCHECK_EQ(size, result); |
| + |
| + // The presence of "IG_" in the device name indicates that this is an XInput |
| + // Gamepad. Skip enumerating these devices and let the XInput path handle it. |
| + // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx |
| + if(filter_xinput_ && wcsstr( name_buffer.get(), L"IG_" ) ) { |
| + return NULL; |
| + } |
| + |
| + // Get the friendly device name |
| + BOOLEAN got_product_string = FALSE; |
| + HANDLE hid_handle = CreateFile(name_buffer.get(), GENERIC_READ|GENERIC_WRITE, |
| + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); |
| + if(hid_handle) { |
| + got_product_string = hidd_get_product_string_(hid_handle, joystick_info->id, |
| + sizeof(joystick_info->id)); |
| + CloseHandle(hid_handle); |
| + } |
| + |
| + if (!got_product_string) |
| + swprintf(joystick_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad"); |
| + |
| + // Query device capabilities |
| + result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA, |
| + NULL, &size); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; |
| + return NULL; |
| + } |
| + DCHECK_EQ(0u, result); |
| + |
| + joystick_info->ppd_buffer.reset(new uint8[size]); |
| + joystick_info->preparsed_data = |
| + reinterpret_cast<PHIDP_PREPARSED_DATA>(joystick_info->ppd_buffer.get()); |
| + result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA, |
| + joystick_info->ppd_buffer.get(), &size); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputDeviceInfo() failed"; |
| + return NULL; |
| + } |
| + DCHECK_EQ(size, result); |
| + |
| + HIDP_CAPS caps; |
| + NTSTATUS status = hidp_get_caps_(joystick_info->preparsed_data, &caps); |
| + DCHECK_EQ(HIDP_STATUS_SUCCESS, status); |
| + |
| + USHORT count = caps.NumberInputButtonCaps; |
| + |
| + if (count > 0) { |
| + scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(new HIDP_BUTTON_CAPS[count]); |
| + status = hidp_get_button_caps_(HidP_Input, button_caps.get(), &count, |
| + joystick_info->preparsed_data); |
| + DCHECK_EQ(HIDP_STATUS_SUCCESS, status); |
| + |
| + joystick_info->button_caps = button_caps[0]; |
| + joystick_info->buttons_length = |
| + joystick_info->button_caps.Range.UsageMax - |
| + joystick_info->button_caps.Range.UsageMin + 1; |
| + } |
| + |
| + count = caps.NumberInputValueCaps; |
| + scoped_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[count]); |
| + status = hidp_get_value_caps_(HidP_Input, axes_caps.get(), &count, |
| + joystick_info->preparsed_data); |
| + |
| + for (UINT i = 0; i < count; i++) { |
| + uint32_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber; |
| + if (axis_index < WebGamepad::axesLengthCap) { |
| + joystick_info->axes[axis_index].caps = axes_caps[i]; |
| + joystick_info->axes[axis_index].value = 0; |
| + joystick_info->axes_length = |
| + std::max(joystick_info->axes_length, axis_index + 1); |
| + } |
| + } |
| + |
| + return joystick_info.release(); |
| +} |
| + |
| +void RawInputDataFetcher::UpdateJoystick( |
| + RAWINPUT* input, |
| + RawJoystickInfo* joystick_info) { |
| + NTSTATUS status; |
| + |
| + joystick_info->report_id++; |
| + |
| + // Query button state |
| + if (joystick_info->buttons_length) { |
| + ULONG buttons_length = joystick_info->buttons_length; |
| + scoped_ptr<USAGE[]> usages(new USAGE[buttons_length]); |
| + status = hidp_get_usages_(HidP_Input, |
| + joystick_info->button_caps.UsagePage, 0, usages.get(), &buttons_length, |
| + joystick_info->preparsed_data, |
| + reinterpret_cast<PCHAR>(input->data.hid.bRawData), |
| + input->data.hid.dwSizeHid); |
| + if (status == HIDP_STATUS_SUCCESS) { |
| + ZeroMemory(joystick_info->buttons, sizeof(joystick_info->buttons)); |
| + ULONG button_index; |
| + for(ULONG i = 0; i < buttons_length; i++) { |
| + button_index = usages[i] - joystick_info->button_caps.Range.UsageMin; |
| + joystick_info->buttons[button_index] = true; |
| + } |
| + } |
| + } |
| + |
| + // Query axis state |
| + ULONG axis_value = 0; |
| + for(uint32_t i = 0; i < joystick_info->axes_length; i++) { |
| + RawJoystickAxis* axis = &joystick_info->axes[i]; |
| + status = hidp_get_usage_value_(HidP_Input, axis->caps.UsagePage, 0, |
| + axis->caps.Range.UsageMin, &axis_value, joystick_info->preparsed_data, |
| + reinterpret_cast<PCHAR>(input->data.hid.bRawData), |
| + input->data.hid.dwSizeHid); |
| + if (status == HIDP_STATUS_SUCCESS) { |
| + axis->value = NormalizeAxis(axis_value, |
| + axis->caps.LogicalMin, axis->caps.LogicalMax); |
| + } |
| + } |
| +} |
| + |
| +LRESULT RawInputDataFetcher::OnInput(HRAWINPUT input_handle) { |
| + // Get the size of the input record. |
| + UINT size = 0; |
| + UINT result = GetRawInputData( |
| + input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed"; |
| + return 0; |
| + } |
| + DCHECK_EQ(0u, result); |
| + |
| + // Retrieve the input record itself. |
| + scoped_ptr<uint8[]> buffer(new uint8[size]); |
| + RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get()); |
| + result = GetRawInputData( |
| + input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER)); |
| + if (result == -1) { |
| + LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed"; |
| + return 0; |
| + } |
| + DCHECK_EQ(size, result); |
| + |
| + // Notify the observer about events generated locally. |
| + if (input->header.dwType == RIM_TYPEHID && input->header.hDevice != NULL) { |
| + RawJoystickInfo* joystick = GetJoystickInfo(input->header.hDevice); |
| + if (joystick) |
| + UpdateJoystick(input, joystick); |
| + } |
| + |
| + return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER)); |
| +} |
| + |
| +bool RawInputDataFetcher::HandleMessage(UINT message, |
| + WPARAM wparam, |
| + LPARAM lparam, |
| + LRESULT* result) { |
| + switch (message) { |
| + case WM_INPUT: |
| + *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam)); |
| + return true; |
| + |
| + default: |
| + return false; |
| + } |
| +} |
| + |
| +bool RawInputDataFetcher::GetHidDllFunctions() { |
| + hidp_get_caps_ = NULL; |
| + hidp_get_button_caps_ = NULL; |
| + hidp_get_value_caps_ = NULL; |
| + hidp_get_usages_ = NULL; |
| + hidp_get_usage_value_ = NULL; |
|
scottmg
2014/01/31 20:51:10
is hid.dll always available? add if (!hid_dll_.is_
|
| + |
| + hidp_get_caps_ = reinterpret_cast<HidPGetCapsFunc>( |
| + hid_dll_.GetFunctionPointer("HidP_GetCaps")); |
| + if (!hidp_get_caps_) |
| + return false; |
| + hidp_get_button_caps_ = reinterpret_cast<HidPGetButtonCapsFunc>( |
| + hid_dll_.GetFunctionPointer("HidP_GetButtonCaps")); |
| + if (!hidp_get_button_caps_) |
| + return false; |
| + hidp_get_value_caps_ = reinterpret_cast<HidPGetValueCapsFunc>( |
| + hid_dll_.GetFunctionPointer("HidP_GetValueCaps")); |
| + if (!hidp_get_value_caps_) |
| + return false; |
| + hidp_get_usages_ = reinterpret_cast<HidPGetUsagesFunc>( |
| + hid_dll_.GetFunctionPointer("HidP_GetUsages")); |
| + if (!hidp_get_usages_) |
| + return false; |
| + hidp_get_usage_value_ = reinterpret_cast<HidPGetUsageValueFunc>( |
| + hid_dll_.GetFunctionPointer("HidP_GetUsageValue")); |
| + if (!hidp_get_usage_value_) |
| + return false; |
| + hidd_get_product_string_ = reinterpret_cast<HidDGetStringFunc>( |
| + hid_dll_.GetFunctionPointer("HidD_GetProductString")); |
| + if (!hidd_get_product_string_) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +} // namespace content |