Home | History | Annotate | Download | only in gamepad
      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 
     11 namespace content {
     12 
     13 using namespace blink;
     14 
     15 namespace {
     16 
     17 float 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.
     22 USHORT DeviceUsages[] = {
     23   0x04,  // Joysticks
     24   0x05,  // Gamepads
     25   0x08,  // Multi Axis
     26 };
     27 
     28 const uint32_t kAxisMinimumUsageNumber = 0x30;
     29 const uint32_t kGameControlsUsagePage = 0x05;
     30 
     31 }   // namespace
     32 
     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) {}
     38 
     39 RawInputDataFetcher::~RawInputDataFetcher() {
     40   DCHECK(!window_);
     41   DCHECK(!events_monitored_);
     42 }
     43 
     44 void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
     45   StopMonitor();
     46 }
     47 
     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();
     56   }
     57   return devices.release();
     58 }
     59 
     60 void 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 
     91 void 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 
    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;
    117   }
    118 }
    119 
    120 std::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 
    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;
    159 
    160   return NULL;
    161 }
    162 
    163 RawGamepadInfo* 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 
    342 void 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 
    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;
    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 
    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;
    456 
    457     default:
    458       return false;
    459   }
    460 }
    461 
    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;
    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