Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / gamepad / raw_input_data_fetcher_win.cc
blob8b3b34fb00637f734783b3c57e505f22d0580419
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 unsigned long GetBitmask(unsigned short bits) {
22 return (1 << bits) - 1;
25 // From the HID Usage Tables specification.
26 USHORT DeviceUsages[] = {
27 0x04, // Joysticks
28 0x05, // Gamepads
29 0x08, // Multi Axis
32 const uint32_t kAxisMinimumUsageNumber = 0x30;
33 const uint32_t kGameControlsUsagePage = 0x05;
35 } // namespace
37 RawGamepadInfo::RawGamepadInfo() {
40 RawGamepadInfo::~RawGamepadInfo() {
43 RawInputDataFetcher::RawInputDataFetcher()
44 : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll"))),
45 rawinput_available_(GetHidDllFunctions()),
46 filter_xinput_(true),
47 events_monitored_(false) {
50 RawInputDataFetcher::~RawInputDataFetcher() {
51 ClearControllers();
52 DCHECK(!window_);
53 DCHECK(!events_monitored_);
56 void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
57 StopMonitor();
60 RAWINPUTDEVICE* RawInputDataFetcher::GetRawInputDevices(DWORD flags) {
61 int usage_count = arraysize(DeviceUsages);
62 scoped_ptr<RAWINPUTDEVICE[]> devices(new RAWINPUTDEVICE[usage_count]);
63 for (int i = 0; i < usage_count; ++i) {
64 devices[i].dwFlags = flags;
65 devices[i].usUsagePage = 1;
66 devices[i].usUsage = DeviceUsages[i];
67 devices[i].hwndTarget = (flags & RIDEV_REMOVE) ? 0 : window_->hwnd();
69 return devices.release();
72 void RawInputDataFetcher::StartMonitor() {
73 if (!rawinput_available_ || events_monitored_)
74 return;
76 if (!window_) {
77 window_.reset(new base::win::MessageWindow());
78 if (!window_->Create(base::Bind(&RawInputDataFetcher::HandleMessage,
79 base::Unretained(this)))) {
80 PLOG(ERROR) << "Failed to create the raw input window";
81 window_.reset();
82 return;
86 // Register to receive raw HID input.
87 scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_INPUTSINK));
88 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
89 sizeof(RAWINPUTDEVICE))) {
90 PLOG(ERROR) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK";
91 window_.reset();
92 return;
95 // Start observing message loop destruction if we start monitoring the first
96 // event.
97 if (!events_monitored_)
98 base::MessageLoop::current()->AddDestructionObserver(this);
100 events_monitored_ = true;
103 void RawInputDataFetcher::StopMonitor() {
104 if (!rawinput_available_ || !events_monitored_)
105 return;
107 // Stop receiving raw input.
108 DCHECK(window_);
109 scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE));
111 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
112 sizeof(RAWINPUTDEVICE))) {
113 PLOG(INFO) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
116 events_monitored_ = false;
117 window_.reset();
119 // Stop observing message loop destruction if no event is being monitored.
120 base::MessageLoop::current()->RemoveDestructionObserver(this);
123 void RawInputDataFetcher::ClearControllers() {
124 while (!controllers_.empty()) {
125 RawGamepadInfo* gamepad_info = controllers_.begin()->second;
126 controllers_.erase(gamepad_info->handle);
127 delete gamepad_info;
131 std::vector<RawGamepadInfo*> RawInputDataFetcher::EnumerateDevices() {
132 std::vector<RawGamepadInfo*> valid_controllers;
134 ClearControllers();
136 UINT count = 0;
137 UINT result = GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST));
138 if (result == static_cast<UINT>(-1)) {
139 PLOG(ERROR) << "GetRawInputDeviceList() failed";
140 return valid_controllers;
142 DCHECK_EQ(0u, result);
144 scoped_ptr<RAWINPUTDEVICELIST[]> device_list(new RAWINPUTDEVICELIST[count]);
145 result = GetRawInputDeviceList(device_list.get(), &count,
146 sizeof(RAWINPUTDEVICELIST));
147 if (result == static_cast<UINT>(-1)) {
148 PLOG(ERROR) << "GetRawInputDeviceList() failed";
149 return valid_controllers;
151 DCHECK_EQ(count, result);
153 for (UINT i = 0; i < count; ++i) {
154 if (device_list[i].dwType == RIM_TYPEHID) {
155 HANDLE device_handle = device_list[i].hDevice;
156 RawGamepadInfo* gamepad_info = ParseGamepadInfo(device_handle);
157 if (gamepad_info) {
158 controllers_[device_handle] = gamepad_info;
159 valid_controllers.push_back(gamepad_info);
163 return valid_controllers;
166 RawGamepadInfo* RawInputDataFetcher::GetGamepadInfo(HANDLE handle) {
167 std::map<HANDLE, RawGamepadInfo*>::iterator it = controllers_.find(handle);
168 if (it != controllers_.end())
169 return it->second;
171 return NULL;
174 RawGamepadInfo* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice) {
175 UINT size = 0;
177 // Do we already have this device in the map?
178 if (GetGamepadInfo(hDevice))
179 return NULL;
181 // Query basic device info.
182 UINT result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO,
183 NULL, &size);
184 if (result == static_cast<UINT>(-1)) {
185 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
186 return NULL;
188 DCHECK_EQ(0u, result);
190 scoped_ptr<uint8[]> di_buffer(new uint8[size]);
191 RID_DEVICE_INFO* device_info =
192 reinterpret_cast<RID_DEVICE_INFO*>(di_buffer.get());
193 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO,
194 di_buffer.get(), &size);
195 if (result == static_cast<UINT>(-1)) {
196 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
197 return NULL;
199 DCHECK_EQ(size, result);
201 // Make sure this device is of a type that we want to observe.
202 bool valid_type = false;
203 for (int i = 0; i < arraysize(DeviceUsages); ++i) {
204 if (device_info->hid.usUsage == DeviceUsages[i]) {
205 valid_type = true;
206 break;
210 if (!valid_type)
211 return NULL;
213 scoped_ptr<RawGamepadInfo> gamepad_info(new RawGamepadInfo);
214 gamepad_info->handle = hDevice;
215 gamepad_info->report_id = 0;
216 gamepad_info->vendor_id = device_info->hid.dwVendorId;
217 gamepad_info->product_id = device_info->hid.dwProductId;
218 gamepad_info->buttons_length = 0;
219 ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons));
220 gamepad_info->axes_length = 0;
221 ZeroMemory(gamepad_info->axes, sizeof(gamepad_info->axes));
223 // Query device identifier
224 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME,
225 NULL, &size);
226 if (result == static_cast<UINT>(-1)) {
227 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
228 return NULL;
230 DCHECK_EQ(0u, result);
232 scoped_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
233 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME,
234 name_buffer.get(), &size);
235 if (result == static_cast<UINT>(-1)) {
236 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
237 return NULL;
239 DCHECK_EQ(size, result);
241 // The presence of "IG_" in the device name indicates that this is an XInput
242 // Gamepad. Skip enumerating these devices and let the XInput path handle it.
243 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx
244 if (filter_xinput_ && wcsstr( name_buffer.get(), L"IG_" ) )
245 return NULL;
247 // Get a friendly device name
248 BOOLEAN got_product_string = FALSE;
249 HANDLE hid_handle = CreateFile(name_buffer.get(), GENERIC_READ|GENERIC_WRITE,
250 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
251 if (hid_handle) {
252 got_product_string = hidd_get_product_string_(hid_handle, gamepad_info->id,
253 sizeof(gamepad_info->id));
254 CloseHandle(hid_handle);
257 if (!got_product_string)
258 swprintf(gamepad_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad");
260 // Query device capabilities.
261 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA,
262 NULL, &size);
263 if (result == static_cast<UINT>(-1)) {
264 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
265 return NULL;
267 DCHECK_EQ(0u, result);
269 gamepad_info->ppd_buffer.reset(new uint8[size]);
270 gamepad_info->preparsed_data =
271 reinterpret_cast<PHIDP_PREPARSED_DATA>(gamepad_info->ppd_buffer.get());
272 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA,
273 gamepad_info->ppd_buffer.get(), &size);
274 if (result == static_cast<UINT>(-1)) {
275 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
276 return NULL;
278 DCHECK_EQ(size, result);
280 HIDP_CAPS caps;
281 NTSTATUS status = hidp_get_caps_(gamepad_info->preparsed_data, &caps);
282 DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
284 // Query button information.
285 USHORT count = caps.NumberInputButtonCaps;
286 if (count > 0) {
287 scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(new HIDP_BUTTON_CAPS[count]);
288 status = hidp_get_button_caps_(
289 HidP_Input, button_caps.get(), &count, gamepad_info->preparsed_data);
290 DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
292 for (uint32_t i = 0; i < count; ++i) {
293 if (button_caps[i].Range.UsageMin <= WebGamepad::buttonsLengthCap) {
294 uint32_t max_index =
295 std::min(WebGamepad::buttonsLengthCap,
296 static_cast<size_t>(button_caps[i].Range.UsageMax));
297 gamepad_info->buttons_length = std::max(
298 gamepad_info->buttons_length, max_index);
303 // Query axis information.
304 count = caps.NumberInputValueCaps;
305 scoped_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[count]);
306 status = hidp_get_value_caps_(HidP_Input, axes_caps.get(), &count,
307 gamepad_info->preparsed_data);
309 bool mapped_all_axes = true;
311 for (UINT i = 0; i < count; i++) {
312 uint32_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
313 if (axis_index < WebGamepad::axesLengthCap) {
314 gamepad_info->axes[axis_index].caps = axes_caps[i];
315 gamepad_info->axes[axis_index].value = 0;
316 gamepad_info->axes[axis_index].active = true;
317 gamepad_info->axes[axis_index].bitmask = GetBitmask(axes_caps[i].BitSize);
318 gamepad_info->axes_length =
319 std::max(gamepad_info->axes_length, axis_index + 1);
320 } else {
321 mapped_all_axes = false;
325 if (!mapped_all_axes) {
326 // For axes who's usage puts them outside the standard axesLengthCap range.
327 uint32_t next_index = 0;
328 for (UINT i = 0; i < count; i++) {
329 uint32_t usage = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
330 if (usage >= WebGamepad::axesLengthCap &&
331 axes_caps[i].UsagePage <= kGameControlsUsagePage) {
333 for (; next_index < WebGamepad::axesLengthCap; ++next_index) {
334 if (!gamepad_info->axes[next_index].active)
335 break;
337 if (next_index < WebGamepad::axesLengthCap) {
338 gamepad_info->axes[next_index].caps = axes_caps[i];
339 gamepad_info->axes[next_index].value = 0;
340 gamepad_info->axes[next_index].active = true;
341 gamepad_info->axes[next_index].bitmask = GetBitmask(
342 axes_caps[i].BitSize);
343 gamepad_info->axes_length =
344 std::max(gamepad_info->axes_length, next_index + 1);
348 if (next_index >= WebGamepad::axesLengthCap)
349 break;
353 return gamepad_info.release();
356 void RawInputDataFetcher::UpdateGamepad(
357 RAWINPUT* input,
358 RawGamepadInfo* gamepad_info) {
359 NTSTATUS status;
361 gamepad_info->report_id++;
363 // Query button state.
364 if (gamepad_info->buttons_length) {
365 // Clear the button state
366 ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons));
367 ULONG buttons_length = 0;
369 hidp_get_usages_ex_(HidP_Input,
371 NULL,
372 &buttons_length,
373 gamepad_info->preparsed_data,
374 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
375 input->data.hid.dwSizeHid);
377 scoped_ptr<USAGE_AND_PAGE[]> usages(new USAGE_AND_PAGE[buttons_length]);
378 status =
379 hidp_get_usages_ex_(HidP_Input,
381 usages.get(),
382 &buttons_length,
383 gamepad_info->preparsed_data,
384 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
385 input->data.hid.dwSizeHid);
387 if (status == HIDP_STATUS_SUCCESS) {
388 // Set each reported button to true.
389 for (uint32_t j = 0; j < buttons_length; j++) {
390 int32_t button_index = usages[j].Usage - 1;
391 if (button_index >= 0 &&
392 button_index < blink::WebGamepad::buttonsLengthCap)
393 gamepad_info->buttons[button_index] = true;
398 // Query axis state.
399 ULONG axis_value = 0;
400 LONG scaled_axis_value = 0;
401 for (uint32_t i = 0; i < gamepad_info->axes_length; i++) {
402 RawGamepadAxis* axis = &gamepad_info->axes[i];
404 // If the min is < 0 we have to query the scaled value, otherwise we need
405 // the normal unscaled value.
406 if (axis->caps.LogicalMin < 0) {
407 status = hidp_get_scaled_usage_value_(HidP_Input, axis->caps.UsagePage, 0,
408 axis->caps.Range.UsageMin, &scaled_axis_value,
409 gamepad_info->preparsed_data,
410 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
411 input->data.hid.dwSizeHid);
412 if (status == HIDP_STATUS_SUCCESS) {
413 axis->value = NormalizeAxis(scaled_axis_value,
414 axis->caps.PhysicalMin, axis->caps.PhysicalMax);
416 } else {
417 status = hidp_get_usage_value_(HidP_Input, axis->caps.UsagePage, 0,
418 axis->caps.Range.UsageMin, &axis_value,
419 gamepad_info->preparsed_data,
420 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
421 input->data.hid.dwSizeHid);
422 if (status == HIDP_STATUS_SUCCESS) {
423 axis->value = NormalizeAxis(axis_value & axis->bitmask,
424 axis->caps.LogicalMin & axis->bitmask,
425 axis->caps.LogicalMax & axis->bitmask);
431 LRESULT RawInputDataFetcher::OnInput(HRAWINPUT input_handle) {
432 // Get the size of the input record.
433 UINT size = 0;
434 UINT result = GetRawInputData(
435 input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
436 if (result == static_cast<UINT>(-1)) {
437 PLOG(ERROR) << "GetRawInputData() failed";
438 return 0;
440 DCHECK_EQ(0u, result);
442 // Retrieve the input record.
443 scoped_ptr<uint8[]> buffer(new uint8[size]);
444 RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
445 result = GetRawInputData(
446 input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER));
447 if (result == static_cast<UINT>(-1)) {
448 PLOG(ERROR) << "GetRawInputData() failed";
449 return 0;
451 DCHECK_EQ(size, result);
453 // Notify the observer about events generated locally.
454 if (input->header.dwType == RIM_TYPEHID && input->header.hDevice != NULL) {
455 RawGamepadInfo* gamepad = GetGamepadInfo(input->header.hDevice);
456 if (gamepad)
457 UpdateGamepad(input, gamepad);
460 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
463 bool RawInputDataFetcher::HandleMessage(UINT message,
464 WPARAM wparam,
465 LPARAM lparam,
466 LRESULT* result) {
467 switch (message) {
468 case WM_INPUT:
469 *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
470 return true;
472 default:
473 return false;
477 bool RawInputDataFetcher::GetHidDllFunctions() {
478 hidp_get_caps_ = NULL;
479 hidp_get_button_caps_ = NULL;
480 hidp_get_value_caps_ = NULL;
481 hidp_get_usages_ex_ = NULL;
482 hidp_get_usage_value_ = NULL;
483 hidp_get_scaled_usage_value_ = NULL;
484 hidd_get_product_string_ = NULL;
486 if (!hid_dll_.is_valid()) return false;
488 hidp_get_caps_ = reinterpret_cast<HidPGetCapsFunc>(
489 hid_dll_.GetFunctionPointer("HidP_GetCaps"));
490 if (!hidp_get_caps_)
491 return false;
492 hidp_get_button_caps_ = reinterpret_cast<HidPGetButtonCapsFunc>(
493 hid_dll_.GetFunctionPointer("HidP_GetButtonCaps"));
494 if (!hidp_get_button_caps_)
495 return false;
496 hidp_get_value_caps_ = reinterpret_cast<HidPGetValueCapsFunc>(
497 hid_dll_.GetFunctionPointer("HidP_GetValueCaps"));
498 if (!hidp_get_value_caps_)
499 return false;
500 hidp_get_usages_ex_ = reinterpret_cast<HidPGetUsagesExFunc>(
501 hid_dll_.GetFunctionPointer("HidP_GetUsagesEx"));
502 if (!hidp_get_usages_ex_)
503 return false;
504 hidp_get_usage_value_ = reinterpret_cast<HidPGetUsageValueFunc>(
505 hid_dll_.GetFunctionPointer("HidP_GetUsageValue"));
506 if (!hidp_get_usage_value_)
507 return false;
508 hidp_get_scaled_usage_value_ = reinterpret_cast<HidPGetScaledUsageValueFunc>(
509 hid_dll_.GetFunctionPointer("HidP_GetScaledUsageValue"));
510 if (!hidp_get_scaled_usage_value_)
511 return false;
512 hidd_get_product_string_ = reinterpret_cast<HidDGetStringFunc>(
513 hid_dll_.GetFunctionPointer("HidD_GetProductString"));
514 if (!hidd_get_product_string_)
515 return false;
517 return true;
520 } // namespace content