Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / gamepad / raw_input_data_fetcher_win.cc
blobe5340bfc075120b7087f49e2d454195d60ac5121
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.
5 #include "content/browser/gamepad/raw_input_data_fetcher_win.h"
7 #include "base/trace_event/trace_event.h"
8 #include "content/common/gamepad_hardware_buffer.h"
9 #include "content/common/gamepad_messages.h"
11 namespace content {
13 using namespace blink;
15 namespace {
17 float NormalizeAxis(long value, long min, long max) {
18 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f;
21 // From the HID Usage Tables specification.
22 USHORT DeviceUsages[] = {
23 0x04, // Joysticks
24 0x05, // Gamepads
25 0x08, // Multi Axis
28 const uint32_t kAxisMinimumUsageNumber = 0x30;
29 const uint32_t kGameControlsUsagePage = 0x05;
31 } // namespace
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) {}
39 RawInputDataFetcher::~RawInputDataFetcher() {
40 DCHECK(!window_);
41 DCHECK(!events_monitored_);
44 void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
45 StopMonitor();
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();
57 return devices.release();
60 void RawInputDataFetcher::StartMonitor() {
61 if (!rawinput_available_ || events_monitored_)
62 return;
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;
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;
83 // Start observing message loop destruction if we start monitoring the first
84 // event.
85 if (!events_monitored_)
86 base::MessageLoop::current()->AddDestructionObserver(this);
88 events_monitored_ = true;
91 void RawInputDataFetcher::StopMonitor() {
92 if (!rawinput_available_ || !events_monitored_)
93 return;
95 // Stop receiving raw input.
96 DCHECK(window_);
97 scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE));
99 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
100 sizeof(RAWINPUTDEVICE))) {
101 PLOG(INFO) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
104 events_monitored_ = false;
105 window_.reset();
106 ClearControllers();
108 // Stop observing message loop destruction if no event is being monitored.
109 base::MessageLoop::current()->RemoveDestructionObserver(this);
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;
120 std::vector<RawGamepadInfo*> RawInputDataFetcher::EnumerateDevices() {
121 std::vector<RawGamepadInfo*> valid_controllers;
123 ClearControllers();
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;
131 DCHECK_EQ(0u, result);
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;
140 DCHECK_EQ(count, result);
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);
152 return valid_controllers;
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;
160 return NULL;
163 RawGamepadInfo* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice) {
164 UINT size = 0;
166 // Do we already have this device in the map?
167 if (GetGamepadInfo(hDevice))
168 return NULL;
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;
177 DCHECK_EQ(0u, result);
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;
188 DCHECK_EQ(size, result);
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;
199 if (!valid_type)
200 return NULL;
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));
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;
219 DCHECK_EQ(0u, result);
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;
228 DCHECK_EQ(size, result);
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;
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);
246 if (!got_product_string)
247 swprintf(gamepad_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad");
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;
256 DCHECK_EQ(0u, result);
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;
267 DCHECK_EQ(size, result);
269 HIDP_CAPS caps;
270 NTSTATUS status = hidp_get_caps_(gamepad_info->preparsed_data, &caps);
271 DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
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);
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);
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);
298 bool mapped_all_axes = true;
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;
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) {
321 for (; next_index < WebGamepad::axesLengthCap; ++next_index) {
322 if (!gamepad_info->axes[next_index].active)
323 break;
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);
334 if (next_index >= WebGamepad::axesLengthCap)
335 break;
339 return gamepad_info.release();
342 void RawInputDataFetcher::UpdateGamepad(
343 RAWINPUT* input,
344 RawGamepadInfo* gamepad_info) {
345 NTSTATUS status;
347 gamepad_info->report_id++;
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;
355 hidp_get_usages_ex_(HidP_Input,
357 NULL,
358 &buttons_length,
359 gamepad_info->preparsed_data,
360 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
361 input->data.hid.dwSizeHid);
363 scoped_ptr<USAGE_AND_PAGE[]> usages(new USAGE_AND_PAGE[buttons_length]);
364 status =
365 hidp_get_usages_ex_(HidP_Input,
367 usages.get(),
368 &buttons_length,
369 gamepad_info->preparsed_data,
370 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
371 input->data.hid.dwSizeHid);
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;
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];
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);
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);
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;
425 DCHECK_EQ(0u, result);
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;
436 DCHECK_EQ(size, result);
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);
445 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
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;
457 default:
458 return false;
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;
471 if (!hid_dll_.is_valid()) return false;
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;
502 return true;
505 } // namespace content