raw_input_gamepad_device_win.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. // Copyright 2018 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. #include "raw_input_gamepad_device_win.h"
  5. // NOTE: <hidsdi.h> must be included before <hidpi.h>. clang-format will want to
  6. // reorder them.
  7. // clang-format off
  8. extern "C" {
  9. #include <hidsdi.h>
  10. #include <hidpi.h>
  11. }
  12. // clang-format on
  13. #include "base/cxx17_backports.h"
  14. #include "base/strings/string_util_win.h"
  15. #include "base/strings/sys_string_conversions.h"
  16. #include "device/gamepad/dualshock4_controller.h"
  17. #include "device/gamepad/gamepad_blocklist.h"
  18. #include "device/gamepad/gamepad_data_fetcher.h"
  19. #include "device/gamepad/hid_haptic_gamepad.h"
  20. #include "device/gamepad/hid_writer_win.h"
  21. namespace device {
  22. namespace {
  23. constexpr uint32_t kGenericDesktopUsagePage = 0x01;
  24. constexpr uint32_t kGameControlsUsagePage = 0x05;
  25. constexpr uint32_t kButtonUsagePage = 0x09;
  26. constexpr uint32_t kConsumerUsagePage = 0x0c;
  27. constexpr uint32_t kAxisMinimumUsageNumber = 0x30;
  28. constexpr uint32_t kSystemMainMenuUsageNumber = 0x85;
  29. constexpr uint32_t kPowerUsageNumber = 0x30;
  30. constexpr uint32_t kSearchUsageNumber = 0x0221;
  31. constexpr uint32_t kHomeUsageNumber = 0x0223;
  32. constexpr uint32_t kBackUsageNumber = 0x0224;
  33. // The fetcher will collect all HID usages from the Button usage page and any
  34. // additional usages listed below.
  35. constexpr struct SpecialUsages {
  36. const uint16_t usage_page;
  37. const uint16_t usage;
  38. } kSpecialUsages[] = {
  39. // Xbox One S pre-FW update reports Xbox button as SystemMainMenu over BT.
  40. {kGenericDesktopUsagePage, kSystemMainMenuUsageNumber},
  41. // Power is used for the Guide button on the Nvidia Shield 2015 gamepad.
  42. {kConsumerUsagePage, kPowerUsageNumber},
  43. // Search is used for the Guide button on the Nvidia Shield 2017 gamepad.
  44. {kConsumerUsagePage, kSearchUsageNumber},
  45. // Start, Back, and Guide buttons are often reported as Consumer Home or
  46. // Back.
  47. {kConsumerUsagePage, kHomeUsageNumber},
  48. {kConsumerUsagePage, kBackUsageNumber},
  49. };
  50. constexpr size_t kSpecialUsagesLen = std::size(kSpecialUsages);
  51. // Scales |value| from the range |min| <= x <= |max| to a Standard Gamepad axis
  52. // value in the range -1.0 <= x <= 1.0.
  53. template <class T>
  54. float NormalizeAxis(T value, T min, T max) {
  55. return (2.0f * (value - min) / static_cast<float>(max - min)) - 1.0f;
  56. }
  57. // Returns a 32-bit mask with the lowest |bits| bits set.
  58. unsigned long GetBitmask(unsigned short bits) {
  59. return (1 << bits) - 1;
  60. }
  61. } // namespace
  62. RawInputGamepadDeviceWin::RawInputGamepadDeviceWin(HANDLE device_handle,
  63. int source_id)
  64. : handle_(device_handle),
  65. source_id_(source_id),
  66. last_update_timestamp_(GamepadDataFetcher::CurrentTimeInMicroseconds()),
  67. button_indices_used_(Gamepad::kButtonsLengthCap, false) {
  68. ::ZeroMemory(buttons_, sizeof(buttons_));
  69. ::ZeroMemory(axes_, sizeof(axes_));
  70. is_valid_ = QueryDeviceInfo();
  71. if (!is_valid_)
  72. return;
  73. const std::string product_name = base::SysWideToUTF8(product_string_);
  74. const GamepadId gamepad_id =
  75. GamepadIdList::Get().GetGamepadId(product_name, vendor_id_, product_id_);
  76. if (Dualshock4Controller::IsDualshock4(gamepad_id)) {
  77. // Dualshock4 has different behavior over USB and Bluetooth, but the
  78. // RawInput API does not indicate which transport is in use. Detect the
  79. // transport type by inspecting the version number reported by the device.
  80. GamepadBusType bus_type =
  81. Dualshock4Controller::BusTypeFromVersionNumber(version_number_);
  82. dualshock4_ = std::make_unique<Dualshock4Controller>(
  83. gamepad_id, bus_type, std::make_unique<HidWriterWin>(handle_));
  84. } else if (HidHapticGamepad::IsHidHaptic(vendor_id_, product_id_)) {
  85. hid_haptics_ = HidHapticGamepad::Create(
  86. vendor_id_, product_id_, std::make_unique<HidWriterWin>(handle_));
  87. }
  88. }
  89. RawInputGamepadDeviceWin::~RawInputGamepadDeviceWin() = default;
  90. // static
  91. bool RawInputGamepadDeviceWin::IsGamepadUsageId(uint16_t usage) {
  92. return usage == kGenericDesktopJoystick || usage == kGenericDesktopGamePad ||
  93. usage == kGenericDesktopMultiAxisController;
  94. }
  95. void RawInputGamepadDeviceWin::DoShutdown() {
  96. if (dualshock4_)
  97. dualshock4_->Shutdown();
  98. dualshock4_.reset();
  99. if (hid_haptics_)
  100. hid_haptics_->Shutdown();
  101. hid_haptics_.reset();
  102. }
  103. void RawInputGamepadDeviceWin::UpdateGamepad(RAWINPUT* input) {
  104. NTSTATUS status;
  105. if (dualshock4_) {
  106. // Handle Dualshock4 input reports that do not specify HID gamepad usages in
  107. // the report descriptor.
  108. uint8_t report_id = input->data.hid.bRawData[0];
  109. auto report = base::make_span(input->data.hid.bRawData + 1,
  110. input->data.hid.dwSizeHid);
  111. Gamepad pad;
  112. if (dualshock4_->ProcessInputReport(report_id, report, &pad)) {
  113. for (size_t i = 0; i < Gamepad::kAxesLengthCap; ++i)
  114. axes_[i].value = pad.axes[i];
  115. for (size_t i = 0; i < Gamepad::kButtonsLengthCap; ++i)
  116. buttons_[i] = pad.buttons[i].pressed;
  117. last_update_timestamp_ = GamepadDataFetcher::CurrentTimeInMicroseconds();
  118. return;
  119. }
  120. }
  121. // Query button state.
  122. if (buttons_length_ > 0) {
  123. // Clear the button state
  124. ::ZeroMemory(buttons_, sizeof(buttons_));
  125. ULONG buttons_length = 0;
  126. HidP_GetUsagesEx(HidP_Input, 0, nullptr, &buttons_length, preparsed_data_,
  127. reinterpret_cast<PCHAR>(input->data.hid.bRawData),
  128. input->data.hid.dwSizeHid);
  129. std::unique_ptr<USAGE_AND_PAGE[]> usages(
  130. new USAGE_AND_PAGE[buttons_length]);
  131. status = HidP_GetUsagesEx(HidP_Input, 0, usages.get(), &buttons_length,
  132. preparsed_data_,
  133. reinterpret_cast<PCHAR>(input->data.hid.bRawData),
  134. input->data.hid.dwSizeHid);
  135. if (status == HIDP_STATUS_SUCCESS) {
  136. // Set each reported button to true.
  137. for (size_t j = 0; j < buttons_length; j++) {
  138. uint16_t usage_page = usages[j].UsagePage;
  139. uint16_t usage = usages[j].Usage;
  140. if (usage_page == kButtonUsagePage && usage > 0) {
  141. size_t button_index = static_cast<size_t>(usage - 1);
  142. if (button_index < Gamepad::kButtonsLengthCap)
  143. buttons_[button_index] = true;
  144. } else if (usage_page != kButtonUsagePage &&
  145. !special_button_map_.empty()) {
  146. for (size_t special_index = 0; special_index < kSpecialUsagesLen;
  147. ++special_index) {
  148. int button_index = special_button_map_[special_index];
  149. if (button_index < 0)
  150. continue;
  151. const auto& special = kSpecialUsages[special_index];
  152. if (usage_page == special.usage_page && usage == special.usage)
  153. buttons_[button_index] = true;
  154. }
  155. }
  156. }
  157. }
  158. }
  159. // Update axis state.
  160. for (uint32_t axis_index = 0; axis_index < axes_length_; ++axis_index)
  161. UpdateAxisValue(axis_index, *input);
  162. last_update_timestamp_ = GamepadDataFetcher::CurrentTimeInMicroseconds();
  163. }
  164. void RawInputGamepadDeviceWin::ReadPadState(Gamepad* pad) const {
  165. DCHECK(pad);
  166. pad->timestamp = last_update_timestamp_;
  167. pad->buttons_length = buttons_length_;
  168. pad->axes_length = axes_length_;
  169. pad->axes_used = axes_used_;
  170. for (unsigned int i = 0; i < buttons_length_; i++) {
  171. pad->buttons[i].used = button_indices_used_[i];
  172. pad->buttons[i].pressed = buttons_[i];
  173. pad->buttons[i].value = buttons_[i] ? 1.0 : 0.0;
  174. }
  175. for (unsigned int i = 0; i < axes_length_; i++)
  176. pad->axes[i] = axes_[i].value;
  177. }
  178. bool RawInputGamepadDeviceWin::SupportsVibration() const {
  179. return dualshock4_ || hid_haptics_;
  180. }
  181. void RawInputGamepadDeviceWin::SetVibration(
  182. mojom::GamepadEffectParametersPtr params) {
  183. if (dualshock4_)
  184. dualshock4_->SetVibration(std::move(params));
  185. else if (hid_haptics_)
  186. hid_haptics_->SetVibration(std::move(params));
  187. }
  188. bool RawInputGamepadDeviceWin::QueryDeviceInfo() {
  189. // Fetch HID properties (RID_DEVICE_INFO_HID) for this device. This includes
  190. // |vendor_id_|, |product_id_|, |version_number_|, and |usage_|.
  191. if (!QueryHidInfo())
  192. return false;
  193. // Make sure this device is of a type that we want to observe.
  194. if (!IsGamepadUsageId(usage_))
  195. return false;
  196. // Filter out devices that have gamepad-like HID usages but aren't gamepads.
  197. if (GamepadIsExcluded(vendor_id_, product_id_))
  198. return false;
  199. // Fetch the device's |name_| (RIDI_DEVICENAME).
  200. if (!QueryDeviceName())
  201. return false;
  202. // From the name we can guess at the bus type. PCI HID devices have "VEN" and
  203. // "DEV" instead of "VID" and "PID". PCI HID devices are typically not
  204. // gamepads and are ignored.
  205. // Example PCI device name: \\?\HID#VEN_1234&DEV_ABCD
  206. // TODO(crbug/881539): Potentially allow PCI HID devices to be enumerated, but
  207. // prefer known gamepads when there is contention.
  208. std::wstring pci_prefix = L"\\\\?\\HID#VEN_";
  209. if (!name_.compare(0, pci_prefix.size(), pci_prefix))
  210. return false;
  211. // Filter out the virtual digitizer, which was observed after remote desktop.
  212. // See https://crbug.com/961774 for details.
  213. if (name_ == L"\\\\?\\VIRTUAL_DIGITIZER")
  214. return false;
  215. // We can now use the name to query the OS for a file handle that is used to
  216. // read the product string from the device. If the OS does not return a valid
  217. // handle this gamepad is invalid.
  218. auto hid_handle = OpenHidHandle();
  219. if (!hid_handle.IsValid())
  220. return false;
  221. // Fetch the human-friendly |product_string_|, if available.
  222. if (!QueryProductString(hid_handle))
  223. product_string_ = L"Unknown Gamepad";
  224. // Fetch information about the buttons and axes on this device. This sets
  225. // |buttons_length_| and |axes_length_| to their correct values and populates
  226. // |axes_| with capabilities info.
  227. if (!QueryDeviceCapabilities())
  228. return false;
  229. // Gamepads must have at least one button or axis.
  230. if (buttons_length_ == 0 && axes_length_ == 0)
  231. return false;
  232. return true;
  233. }
  234. bool RawInputGamepadDeviceWin::QueryHidInfo() {
  235. UINT size = 0;
  236. UINT result =
  237. ::GetRawInputDeviceInfo(handle_, RIDI_DEVICEINFO, nullptr, &size);
  238. if (result == static_cast<UINT>(-1)) {
  239. PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
  240. return false;
  241. }
  242. DCHECK_EQ(0u, result);
  243. std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
  244. result =
  245. ::GetRawInputDeviceInfo(handle_, RIDI_DEVICEINFO, buffer.get(), &size);
  246. if (result == static_cast<UINT>(-1)) {
  247. PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
  248. return false;
  249. }
  250. DCHECK_EQ(size, result);
  251. RID_DEVICE_INFO* device_info =
  252. reinterpret_cast<RID_DEVICE_INFO*>(buffer.get());
  253. DCHECK_EQ(device_info->dwType, static_cast<DWORD>(RIM_TYPEHID));
  254. vendor_id_ = static_cast<uint16_t>(device_info->hid.dwVendorId);
  255. product_id_ = static_cast<uint16_t>(device_info->hid.dwProductId);
  256. version_number_ = static_cast<uint16_t>(device_info->hid.dwVersionNumber);
  257. usage_ = device_info->hid.usUsage;
  258. return true;
  259. }
  260. bool RawInputGamepadDeviceWin::QueryDeviceName() {
  261. UINT size = 0;
  262. UINT result =
  263. ::GetRawInputDeviceInfo(handle_, RIDI_DEVICENAME, nullptr, &size);
  264. if (result == static_cast<UINT>(-1)) {
  265. PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
  266. return false;
  267. }
  268. DCHECK_EQ(0u, result);
  269. std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
  270. result =
  271. ::GetRawInputDeviceInfo(handle_, RIDI_DEVICENAME, buffer.get(), &size);
  272. if (result == static_cast<UINT>(-1)) {
  273. PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
  274. return false;
  275. }
  276. DCHECK_EQ(size, result);
  277. name_ = buffer.get();
  278. return true;
  279. }
  280. bool RawInputGamepadDeviceWin::QueryProductString(
  281. base::win::ScopedHandle& hid_handle) {
  282. DCHECK(hid_handle.IsValid());
  283. // HidD_GetProductString may return successfully even if it didn't write to
  284. // the buffer. Ensure the buffer is zeroed before calling
  285. // HidD_GetProductString. See https://crbug.com/1205511.
  286. std::wstring buffer;
  287. if (!HidD_GetProductString(hid_handle.Get(),
  288. base::WriteInto(&buffer, Gamepad::kIdLengthCap),
  289. Gamepad::kIdLengthCap)) {
  290. return false;
  291. }
  292. // Remove trailing NUL characters.
  293. buffer = std::wstring(base::TrimString(buffer, base::WStringPiece(L"\0", 1),
  294. base::TRIM_TRAILING));
  295. // The product string cannot be empty.
  296. if (buffer.empty())
  297. return false;
  298. product_string_ = std::move(buffer);
  299. return true;
  300. }
  301. base::win::ScopedHandle RawInputGamepadDeviceWin::OpenHidHandle() {
  302. return base::win::ScopedHandle(::CreateFile(
  303. name_.c_str(), GENERIC_READ | GENERIC_WRITE,
  304. FILE_SHARE_READ | FILE_SHARE_WRITE, /*lpSecurityAttributes=*/nullptr,
  305. OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/nullptr));
  306. }
  307. bool RawInputGamepadDeviceWin::QueryDeviceCapabilities() {
  308. UINT size = 0;
  309. UINT result =
  310. ::GetRawInputDeviceInfo(handle_, RIDI_PREPARSEDDATA, nullptr, &size);
  311. if (result == static_cast<UINT>(-1)) {
  312. PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
  313. return false;
  314. }
  315. DCHECK_EQ(0u, result);
  316. ppd_buffer_.reset(new uint8_t[size]);
  317. preparsed_data_ = reinterpret_cast<PHIDP_PREPARSED_DATA>(ppd_buffer_.get());
  318. result = ::GetRawInputDeviceInfo(handle_, RIDI_PREPARSEDDATA,
  319. ppd_buffer_.get(), &size);
  320. if (result == static_cast<UINT>(-1)) {
  321. PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
  322. return false;
  323. }
  324. DCHECK_EQ(size, result);
  325. HIDP_CAPS caps;
  326. NTSTATUS status = HidP_GetCaps(preparsed_data_, &caps);
  327. DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
  328. QueryButtonCapabilities(caps.NumberInputButtonCaps);
  329. QueryAxisCapabilities(caps.NumberInputValueCaps);
  330. return true;
  331. }
  332. void RawInputGamepadDeviceWin::QueryButtonCapabilities(uint16_t button_count) {
  333. if (button_count > 0) {
  334. std::vector<HIDP_BUTTON_CAPS> button_caps(button_count);
  335. NTSTATUS status = HidP_GetButtonCaps(HidP_Input, button_caps.data(),
  336. &button_count, preparsed_data_);
  337. DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
  338. // Collect all inputs from the Button usage page.
  339. QueryNormalButtonCapabilities(button_caps);
  340. // Check for common gamepad buttons that are not on the Button usage page.
  341. QuerySpecialButtonCapabilities(button_caps);
  342. }
  343. }
  344. void RawInputGamepadDeviceWin::QueryNormalButtonCapabilities(
  345. base::span<const HIDP_BUTTON_CAPS> button_caps) {
  346. // Collect all inputs from the Button usage page and assign button indices
  347. // based on the usage value.
  348. for (const auto& item : button_caps) {
  349. uint16_t usage_min = item.Range.UsageMin;
  350. uint16_t usage_max = item.Range.UsageMax;
  351. if (usage_min == 0 || usage_max == 0)
  352. continue;
  353. size_t button_index_min = static_cast<size_t>(usage_min - 1);
  354. size_t button_index_max = static_cast<size_t>(usage_max - 1);
  355. if (item.UsagePage == kButtonUsagePage &&
  356. button_index_min < Gamepad::kButtonsLengthCap) {
  357. button_index_max =
  358. std::min(Gamepad::kButtonsLengthCap - 1, button_index_max);
  359. buttons_length_ = std::max(buttons_length_, button_index_max + 1);
  360. for (size_t button_index = button_index_min;
  361. button_index <= button_index_max; ++button_index) {
  362. button_indices_used_[button_index] = true;
  363. }
  364. }
  365. }
  366. }
  367. void RawInputGamepadDeviceWin::QuerySpecialButtonCapabilities(
  368. base::span<const HIDP_BUTTON_CAPS> button_caps) {
  369. // Check for common gamepad buttons that are not on the Button usage page.
  370. std::vector<bool> has_special_usage(kSpecialUsagesLen, false);
  371. size_t unmapped_button_count = 0;
  372. for (const auto& item : button_caps) {
  373. uint16_t usage_min = item.Range.UsageMin;
  374. uint16_t usage_max = item.Range.UsageMax;
  375. for (size_t special_index = 0; special_index < kSpecialUsagesLen;
  376. ++special_index) {
  377. const auto& special = kSpecialUsages[special_index];
  378. if (item.UsagePage == special.usage_page && usage_min <= special.usage &&
  379. usage_max >= special.usage) {
  380. has_special_usage[special_index] = true;
  381. ++unmapped_button_count;
  382. }
  383. }
  384. }
  385. special_button_map_.clear();
  386. if (unmapped_button_count > 0) {
  387. // Insert special buttons at unused button indices.
  388. special_button_map_.resize(kSpecialUsagesLen, -1);
  389. size_t button_index = 0;
  390. for (size_t special_index = 0; special_index < kSpecialUsagesLen;
  391. ++special_index) {
  392. if (!has_special_usage[special_index])
  393. continue;
  394. // Advance to the next unused button index.
  395. while (button_index < Gamepad::kButtonsLengthCap &&
  396. button_indices_used_[button_index]) {
  397. ++button_index;
  398. }
  399. if (button_index >= Gamepad::kButtonsLengthCap)
  400. break;
  401. special_button_map_[special_index] = button_index;
  402. button_indices_used_[button_index] = true;
  403. ++button_index;
  404. if (--unmapped_button_count == 0)
  405. break;
  406. }
  407. buttons_length_ = std::max(buttons_length_, button_index);
  408. }
  409. }
  410. void RawInputGamepadDeviceWin::QueryAxisCapabilities(uint16_t axis_count) {
  411. std::unique_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[axis_count]);
  412. HidP_GetValueCaps(HidP_Input, axes_caps.get(), &axis_count, preparsed_data_);
  413. bool mapped_all_axes = true;
  414. for (size_t i = 0; i < axis_count; i++) {
  415. size_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
  416. if (axis_index < Gamepad::kAxesLengthCap && !axes_[axis_index].active) {
  417. axes_[axis_index].caps = axes_caps[i];
  418. axes_[axis_index].value = 0;
  419. axes_[axis_index].active = true;
  420. axes_[axis_index].bitmask = GetBitmask(axes_caps[i].BitSize);
  421. axes_length_ = std::max(axes_length_, axis_index + 1);
  422. axes_used_ |= 1 << axis_index;
  423. } else {
  424. mapped_all_axes = false;
  425. }
  426. }
  427. if (!mapped_all_axes) {
  428. // For axes whose usage puts them outside the standard axesLengthCap range.
  429. size_t next_index = 0;
  430. for (size_t i = 0; i < axis_count; i++) {
  431. size_t usage = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
  432. if (usage >= Gamepad::kAxesLengthCap &&
  433. axes_caps[i].UsagePage <= kGameControlsUsagePage) {
  434. for (; next_index < Gamepad::kAxesLengthCap; ++next_index) {
  435. if (!axes_[next_index].active)
  436. break;
  437. }
  438. if (next_index < Gamepad::kAxesLengthCap) {
  439. axes_[next_index].caps = axes_caps[i];
  440. axes_[next_index].value = 0;
  441. axes_[next_index].active = true;
  442. axes_[next_index].bitmask = GetBitmask(axes_caps[i].BitSize);
  443. axes_length_ = std::max(axes_length_, next_index + 1);
  444. axes_used_ |= 1 << next_index;
  445. }
  446. }
  447. if (next_index >= Gamepad::kAxesLengthCap)
  448. break;
  449. }
  450. }
  451. }
  452. void RawInputGamepadDeviceWin::UpdateAxisValue(size_t axis_index,
  453. RAWINPUT& input) {
  454. DCHECK_LT(axis_index, Gamepad::kAxesLengthCap);
  455. // RawInput gamepad axes are normalized according to the information provided
  456. // in the HID report descriptor. Each HID report item must specify a Logical
  457. // Minimum and Logical Maximum to define the domain of allowable values for
  458. // the item. An item may optionally specify a Units definition to indicate
  459. // that the item represents a real-world value measured in those units. Items
  460. // with a Units definition should also specify Physical Minimum and Physical
  461. // Maximum, which are the Logical bounds transformed into Physical units.
  462. //
  463. // For gamepads, it is common for joystick and trigger axis items to not
  464. // specify Units. However, D-pad items typically do specify Units. An 8-way
  465. // directional pad, when implemented as a Hat Switch axis, reports a logical
  466. // value from 0 to 7 that corresponds to a physical value from 0 degrees (N)
  467. // clockwise to 315 degrees (NW).
  468. //
  469. // When a Hat Switch is in its Null State (no interaction) it reports a value
  470. // outside the Logical range, often 8. Normalizing the out-of-bounds Null
  471. // State value yields an invalid axis value greater than +1.0. This invalid
  472. // axis value must be preserved so that downstream consumers can detect when
  473. // the Hat Switch is reporting a Null State value.
  474. //
  475. // When an item provides Physical bounds, prefer to use
  476. // HidP_GetScaledUsageValue to retrieve the item's value in real-world units
  477. // and normalize using the Physical bounds. If the Physical bounds are invalid
  478. // or HidP_GetScaledUsageValue fails, use HidP_GetUsageValue to retrieve the
  479. // logical value and normalize using the Logical bounds.
  480. auto& axis = axes_[axis_index];
  481. if (axis.caps.PhysicalMin < axis.caps.PhysicalMax) {
  482. LONG scaled_axis_value = 0;
  483. if (HidP_GetScaledUsageValue(
  484. HidP_Input, axis.caps.UsagePage, /*LinkCollection=*/0,
  485. axis.caps.Range.UsageMin, &scaled_axis_value, preparsed_data_,
  486. reinterpret_cast<PCHAR>(input.data.hid.bRawData),
  487. input.data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS) {
  488. axis.value = NormalizeAxis(scaled_axis_value, axis.caps.PhysicalMin,
  489. axis.caps.PhysicalMax);
  490. return;
  491. }
  492. }
  493. ULONG axis_value = 0;
  494. if (HidP_GetUsageValue(HidP_Input, axis.caps.UsagePage, /*LinkCollection=*/0,
  495. axis.caps.Range.UsageMin, &axis_value, preparsed_data_,
  496. reinterpret_cast<PCHAR>(input.data.hid.bRawData),
  497. input.data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS) {
  498. axis.value = NormalizeAxis(axis_value & axis.bitmask,
  499. axis.caps.LogicalMin & axis.bitmask,
  500. axis.caps.LogicalMax & axis.bitmask);
  501. return;
  502. }
  503. axis.value = 0.0f;
  504. }
  505. base::WeakPtr<AbstractHapticGamepad> RawInputGamepadDeviceWin::GetWeakPtr() {
  506. return weak_factory_.GetWeakPtr();
  507. }
  508. } // namespace device