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.
4
5#include "content/browser/gamepad/raw_input_data_fetcher_win.h"
6
7#include "base/debug/trace_event.h"
8#include "content/common/gamepad_hardware_buffer.h"
9#include "content/common/gamepad_messages.h"
10
11namespace content {
12
13using namespace blink;
14
15namespace {
16
17float NormalizeAxis(long value, long min, long max) {
18  return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f;
19}
20
21// From the HID Usage Tables specification.
22USHORT DeviceUsages[] = {
23  0x04,  // Joysticks
24  0x05,  // Gamepads
25  0x08,  // Multi Axis
26};
27
28const uint32_t kAxisMinimumUsageNumber = 0x30;
29const uint32_t kGameControlsUsagePage = 0x05;
30
31}   // namespace
32
33RawInputDataFetcher::RawInputDataFetcher()
34    : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll")))
35    , rawinput_available_(GetHidDllFunctions())
36    , filter_xinput_(true)
37    , events_monitored_(false) {}
38
39RawInputDataFetcher::~RawInputDataFetcher() {
40  DCHECK(!window_);
41  DCHECK(!events_monitored_);
42}
43
44void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
45  StopMonitor();
46}
47
48RAWINPUTDEVICE* 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
60void 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      PLOG(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    PLOG(ERROR) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK";
79    window_.reset();
80    return;
81  }
82
83  // Start observing message loop destruction if we start monitoring the first
84  // event.
85  if (!events_monitored_)
86    base::MessageLoop::current()->AddDestructionObserver(this);
87
88  events_monitored_ = true;
89}
90
91void RawInputDataFetcher::StopMonitor() {
92  if (!rawinput_available_ || !events_monitored_)
93    return;
94
95  // Stop receiving raw input.
96  DCHECK(window_);
97  scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE));
98
99  if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
100      sizeof(RAWINPUTDEVICE))) {
101    PLOG(INFO) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
102  }
103
104  events_monitored_ = false;
105  window_.reset();
106  ClearControllers();
107
108  // Stop observing message loop destruction if no event is being monitored.
109  base::MessageLoop::current()->RemoveDestructionObserver(this);
110}
111
112void RawInputDataFetcher::ClearControllers() {
113  while (!controllers_.empty()) {
114    RawGamepadInfo* gamepad_info = controllers_.begin()->second;
115    controllers_.erase(gamepad_info->handle);
116    delete gamepad_info;
117  }
118}
119
120std::vector<RawGamepadInfo*> RawInputDataFetcher::EnumerateDevices() {
121  std::vector<RawGamepadInfo*> valid_controllers;
122
123  ClearControllers();
124
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;
130  }
131  DCHECK_EQ(0u, result);
132
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;
139  }
140  DCHECK_EQ(count, result);
141
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);
149      }
150    }
151  }
152  return valid_controllers;
153}
154
155RawGamepadInfo* RawInputDataFetcher::GetGamepadInfo(HANDLE handle) {
156  std::map<HANDLE, RawGamepadInfo*>::iterator it = controllers_.find(handle);
157  if (it != controllers_.end())
158    return it->second;
159
160  return NULL;
161}
162
163RawGamepadInfo* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice) {
164  UINT size = 0;
165
166  // Do we already have this device in the map?
167  if (GetGamepadInfo(hDevice))
168    return NULL;
169
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;
176  }
177  DCHECK_EQ(0u, result);
178
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;
187  }
188  DCHECK_EQ(size, result);
189
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;
196    }
197  }
198
199  if (!valid_type)
200    return NULL;
201
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));
211
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;
218  }
219  DCHECK_EQ(0u, result);
220
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;
227  }
228  DCHECK_EQ(size, result);
229
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;
235
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);
244  }
245
246  if (!got_product_string)
247    swprintf(gamepad_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad");
248
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;
255  }
256  DCHECK_EQ(0u, result);
257
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;
266  }
267  DCHECK_EQ(size, result);
268
269  HIDP_CAPS caps;
270  NTSTATUS status = hidp_get_caps_(gamepad_info->preparsed_data, &caps);
271  DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
272
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);
280
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);
288      }
289    }
290  }
291
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);
297
298  bool mapped_all_axes = true;
299
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;
310    }
311  }
312
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) {
320
321        for (; next_index < WebGamepad::axesLengthCap; ++next_index) {
322          if (!gamepad_info->axes[next_index].active)
323            break;
324        }
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);
331        }
332      }
333
334      if (next_index >= WebGamepad::axesLengthCap)
335        break;
336    }
337  }
338
339  return gamepad_info.release();
340}
341
342void RawInputDataFetcher::UpdateGamepad(
343    RAWINPUT* input,
344    RawGamepadInfo* gamepad_info) {
345  NTSTATUS status;
346
347  gamepad_info->report_id++;
348
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;
354
355    hidp_get_usages_ex_(HidP_Input,
356                        0,
357                        NULL,
358                        &buttons_length,
359                        gamepad_info->preparsed_data,
360                        reinterpret_cast<PCHAR>(input->data.hid.bRawData),
361                        input->data.hid.dwSizeHid);
362
363    scoped_ptr<USAGE_AND_PAGE[]> usages(new USAGE_AND_PAGE[buttons_length]);
364    status =
365        hidp_get_usages_ex_(HidP_Input,
366                            0,
367                            usages.get(),
368                            &buttons_length,
369                            gamepad_info->preparsed_data,
370                            reinterpret_cast<PCHAR>(input->data.hid.bRawData),
371                            input->data.hid.dwSizeHid);
372
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;
380      }
381    }
382  }
383
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];
389
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);
401      }
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);
411      }
412    }
413  }
414}
415
416LRESULT 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;
424  }
425  DCHECK_EQ(0u, result);
426
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;
435  }
436  DCHECK_EQ(size, result);
437
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);
443  }
444
445  return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
446}
447
448bool 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;
456
457    default:
458      return false;
459  }
460}
461
462bool 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;
470
471  if (!hid_dll_.is_valid()) return false;
472
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;
501
502  return true;
503}
504
505}  // namespace content
506