Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(851)

Unified Diff: content/browser/gamepad/raw_input_data_fetcher_win.cc

Issue 135523006: Implemented Gamepad support via Raw Input on Windows (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Features, fixes, and cleanups Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698