123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "raw_input_gamepad_device_win.h"
- // NOTE: <hidsdi.h> must be included before <hidpi.h>. clang-format will want to
- // reorder them.
- // clang-format off
- extern "C" {
- #include <hidsdi.h>
- #include <hidpi.h>
- }
- // clang-format on
- #include "base/cxx17_backports.h"
- #include "base/strings/string_util_win.h"
- #include "base/strings/sys_string_conversions.h"
- #include "device/gamepad/dualshock4_controller.h"
- #include "device/gamepad/gamepad_blocklist.h"
- #include "device/gamepad/gamepad_data_fetcher.h"
- #include "device/gamepad/hid_haptic_gamepad.h"
- #include "device/gamepad/hid_writer_win.h"
- namespace device {
- namespace {
- constexpr uint32_t kGenericDesktopUsagePage = 0x01;
- constexpr uint32_t kGameControlsUsagePage = 0x05;
- constexpr uint32_t kButtonUsagePage = 0x09;
- constexpr uint32_t kConsumerUsagePage = 0x0c;
- constexpr uint32_t kAxisMinimumUsageNumber = 0x30;
- constexpr uint32_t kSystemMainMenuUsageNumber = 0x85;
- constexpr uint32_t kPowerUsageNumber = 0x30;
- constexpr uint32_t kSearchUsageNumber = 0x0221;
- constexpr uint32_t kHomeUsageNumber = 0x0223;
- constexpr uint32_t kBackUsageNumber = 0x0224;
- // The fetcher will collect all HID usages from the Button usage page and any
- // additional usages listed below.
- constexpr struct SpecialUsages {
- const uint16_t usage_page;
- const uint16_t usage;
- } kSpecialUsages[] = {
- // Xbox One S pre-FW update reports Xbox button as SystemMainMenu over BT.
- {kGenericDesktopUsagePage, kSystemMainMenuUsageNumber},
- // Power is used for the Guide button on the Nvidia Shield 2015 gamepad.
- {kConsumerUsagePage, kPowerUsageNumber},
- // Search is used for the Guide button on the Nvidia Shield 2017 gamepad.
- {kConsumerUsagePage, kSearchUsageNumber},
- // Start, Back, and Guide buttons are often reported as Consumer Home or
- // Back.
- {kConsumerUsagePage, kHomeUsageNumber},
- {kConsumerUsagePage, kBackUsageNumber},
- };
- constexpr size_t kSpecialUsagesLen = std::size(kSpecialUsages);
- // Scales |value| from the range |min| <= x <= |max| to a Standard Gamepad axis
- // value in the range -1.0 <= x <= 1.0.
- template <class T>
- float NormalizeAxis(T value, T min, T max) {
- return (2.0f * (value - min) / static_cast<float>(max - min)) - 1.0f;
- }
- // Returns a 32-bit mask with the lowest |bits| bits set.
- unsigned long GetBitmask(unsigned short bits) {
- return (1 << bits) - 1;
- }
- } // namespace
- RawInputGamepadDeviceWin::RawInputGamepadDeviceWin(HANDLE device_handle,
- int source_id)
- : handle_(device_handle),
- source_id_(source_id),
- last_update_timestamp_(GamepadDataFetcher::CurrentTimeInMicroseconds()),
- button_indices_used_(Gamepad::kButtonsLengthCap, false) {
- ::ZeroMemory(buttons_, sizeof(buttons_));
- ::ZeroMemory(axes_, sizeof(axes_));
- is_valid_ = QueryDeviceInfo();
- if (!is_valid_)
- return;
- const std::string product_name = base::SysWideToUTF8(product_string_);
- const GamepadId gamepad_id =
- GamepadIdList::Get().GetGamepadId(product_name, vendor_id_, product_id_);
- if (Dualshock4Controller::IsDualshock4(gamepad_id)) {
- // Dualshock4 has different behavior over USB and Bluetooth, but the
- // RawInput API does not indicate which transport is in use. Detect the
- // transport type by inspecting the version number reported by the device.
- GamepadBusType bus_type =
- Dualshock4Controller::BusTypeFromVersionNumber(version_number_);
- dualshock4_ = std::make_unique<Dualshock4Controller>(
- gamepad_id, bus_type, std::make_unique<HidWriterWin>(handle_));
- } else if (HidHapticGamepad::IsHidHaptic(vendor_id_, product_id_)) {
- hid_haptics_ = HidHapticGamepad::Create(
- vendor_id_, product_id_, std::make_unique<HidWriterWin>(handle_));
- }
- }
- RawInputGamepadDeviceWin::~RawInputGamepadDeviceWin() = default;
- // static
- bool RawInputGamepadDeviceWin::IsGamepadUsageId(uint16_t usage) {
- return usage == kGenericDesktopJoystick || usage == kGenericDesktopGamePad ||
- usage == kGenericDesktopMultiAxisController;
- }
- void RawInputGamepadDeviceWin::DoShutdown() {
- if (dualshock4_)
- dualshock4_->Shutdown();
- dualshock4_.reset();
- if (hid_haptics_)
- hid_haptics_->Shutdown();
- hid_haptics_.reset();
- }
- void RawInputGamepadDeviceWin::UpdateGamepad(RAWINPUT* input) {
- NTSTATUS status;
- if (dualshock4_) {
- // Handle Dualshock4 input reports that do not specify HID gamepad usages in
- // the report descriptor.
- uint8_t report_id = input->data.hid.bRawData[0];
- auto report = base::make_span(input->data.hid.bRawData + 1,
- input->data.hid.dwSizeHid);
- Gamepad pad;
- if (dualshock4_->ProcessInputReport(report_id, report, &pad)) {
- for (size_t i = 0; i < Gamepad::kAxesLengthCap; ++i)
- axes_[i].value = pad.axes[i];
- for (size_t i = 0; i < Gamepad::kButtonsLengthCap; ++i)
- buttons_[i] = pad.buttons[i].pressed;
- last_update_timestamp_ = GamepadDataFetcher::CurrentTimeInMicroseconds();
- return;
- }
- }
- // Query button state.
- if (buttons_length_ > 0) {
- // Clear the button state
- ::ZeroMemory(buttons_, sizeof(buttons_));
- ULONG buttons_length = 0;
- HidP_GetUsagesEx(HidP_Input, 0, nullptr, &buttons_length, preparsed_data_,
- reinterpret_cast<PCHAR>(input->data.hid.bRawData),
- input->data.hid.dwSizeHid);
- std::unique_ptr<USAGE_AND_PAGE[]> usages(
- new USAGE_AND_PAGE[buttons_length]);
- status = HidP_GetUsagesEx(HidP_Input, 0, usages.get(), &buttons_length,
- preparsed_data_,
- reinterpret_cast<PCHAR>(input->data.hid.bRawData),
- input->data.hid.dwSizeHid);
- if (status == HIDP_STATUS_SUCCESS) {
- // Set each reported button to true.
- for (size_t j = 0; j < buttons_length; j++) {
- uint16_t usage_page = usages[j].UsagePage;
- uint16_t usage = usages[j].Usage;
- if (usage_page == kButtonUsagePage && usage > 0) {
- size_t button_index = static_cast<size_t>(usage - 1);
- if (button_index < Gamepad::kButtonsLengthCap)
- buttons_[button_index] = true;
- } else if (usage_page != kButtonUsagePage &&
- !special_button_map_.empty()) {
- for (size_t special_index = 0; special_index < kSpecialUsagesLen;
- ++special_index) {
- int button_index = special_button_map_[special_index];
- if (button_index < 0)
- continue;
- const auto& special = kSpecialUsages[special_index];
- if (usage_page == special.usage_page && usage == special.usage)
- buttons_[button_index] = true;
- }
- }
- }
- }
- }
- // Update axis state.
- for (uint32_t axis_index = 0; axis_index < axes_length_; ++axis_index)
- UpdateAxisValue(axis_index, *input);
- last_update_timestamp_ = GamepadDataFetcher::CurrentTimeInMicroseconds();
- }
- void RawInputGamepadDeviceWin::ReadPadState(Gamepad* pad) const {
- DCHECK(pad);
- pad->timestamp = last_update_timestamp_;
- pad->buttons_length = buttons_length_;
- pad->axes_length = axes_length_;
- pad->axes_used = axes_used_;
- for (unsigned int i = 0; i < buttons_length_; i++) {
- pad->buttons[i].used = button_indices_used_[i];
- pad->buttons[i].pressed = buttons_[i];
- pad->buttons[i].value = buttons_[i] ? 1.0 : 0.0;
- }
- for (unsigned int i = 0; i < axes_length_; i++)
- pad->axes[i] = axes_[i].value;
- }
- bool RawInputGamepadDeviceWin::SupportsVibration() const {
- return dualshock4_ || hid_haptics_;
- }
- void RawInputGamepadDeviceWin::SetVibration(
- mojom::GamepadEffectParametersPtr params) {
- if (dualshock4_)
- dualshock4_->SetVibration(std::move(params));
- else if (hid_haptics_)
- hid_haptics_->SetVibration(std::move(params));
- }
- bool RawInputGamepadDeviceWin::QueryDeviceInfo() {
- // Fetch HID properties (RID_DEVICE_INFO_HID) for this device. This includes
- // |vendor_id_|, |product_id_|, |version_number_|, and |usage_|.
- if (!QueryHidInfo())
- return false;
- // Make sure this device is of a type that we want to observe.
- if (!IsGamepadUsageId(usage_))
- return false;
- // Filter out devices that have gamepad-like HID usages but aren't gamepads.
- if (GamepadIsExcluded(vendor_id_, product_id_))
- return false;
- // Fetch the device's |name_| (RIDI_DEVICENAME).
- if (!QueryDeviceName())
- return false;
- // From the name we can guess at the bus type. PCI HID devices have "VEN" and
- // "DEV" instead of "VID" and "PID". PCI HID devices are typically not
- // gamepads and are ignored.
- // Example PCI device name: \\?\HID#VEN_1234&DEV_ABCD
- // TODO(crbug/881539): Potentially allow PCI HID devices to be enumerated, but
- // prefer known gamepads when there is contention.
- std::wstring pci_prefix = L"\\\\?\\HID#VEN_";
- if (!name_.compare(0, pci_prefix.size(), pci_prefix))
- return false;
- // Filter out the virtual digitizer, which was observed after remote desktop.
- // See https://crbug.com/961774 for details.
- if (name_ == L"\\\\?\\VIRTUAL_DIGITIZER")
- return false;
- // We can now use the name to query the OS for a file handle that is used to
- // read the product string from the device. If the OS does not return a valid
- // handle this gamepad is invalid.
- auto hid_handle = OpenHidHandle();
- if (!hid_handle.IsValid())
- return false;
- // Fetch the human-friendly |product_string_|, if available.
- if (!QueryProductString(hid_handle))
- product_string_ = L"Unknown Gamepad";
- // Fetch information about the buttons and axes on this device. This sets
- // |buttons_length_| and |axes_length_| to their correct values and populates
- // |axes_| with capabilities info.
- if (!QueryDeviceCapabilities())
- return false;
- // Gamepads must have at least one button or axis.
- if (buttons_length_ == 0 && axes_length_ == 0)
- return false;
- return true;
- }
- bool RawInputGamepadDeviceWin::QueryHidInfo() {
- UINT size = 0;
- UINT result =
- ::GetRawInputDeviceInfo(handle_, RIDI_DEVICEINFO, nullptr, &size);
- if (result == static_cast<UINT>(-1)) {
- PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
- return false;
- }
- DCHECK_EQ(0u, result);
- std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
- result =
- ::GetRawInputDeviceInfo(handle_, RIDI_DEVICEINFO, buffer.get(), &size);
- if (result == static_cast<UINT>(-1)) {
- PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
- return false;
- }
- DCHECK_EQ(size, result);
- RID_DEVICE_INFO* device_info =
- reinterpret_cast<RID_DEVICE_INFO*>(buffer.get());
- DCHECK_EQ(device_info->dwType, static_cast<DWORD>(RIM_TYPEHID));
- vendor_id_ = static_cast<uint16_t>(device_info->hid.dwVendorId);
- product_id_ = static_cast<uint16_t>(device_info->hid.dwProductId);
- version_number_ = static_cast<uint16_t>(device_info->hid.dwVersionNumber);
- usage_ = device_info->hid.usUsage;
- return true;
- }
- bool RawInputGamepadDeviceWin::QueryDeviceName() {
- UINT size = 0;
- UINT result =
- ::GetRawInputDeviceInfo(handle_, RIDI_DEVICENAME, nullptr, &size);
- if (result == static_cast<UINT>(-1)) {
- PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
- return false;
- }
- DCHECK_EQ(0u, result);
- std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
- result =
- ::GetRawInputDeviceInfo(handle_, RIDI_DEVICENAME, buffer.get(), &size);
- if (result == static_cast<UINT>(-1)) {
- PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
- return false;
- }
- DCHECK_EQ(size, result);
- name_ = buffer.get();
- return true;
- }
- bool RawInputGamepadDeviceWin::QueryProductString(
- base::win::ScopedHandle& hid_handle) {
- DCHECK(hid_handle.IsValid());
- // HidD_GetProductString may return successfully even if it didn't write to
- // the buffer. Ensure the buffer is zeroed before calling
- // HidD_GetProductString. See https://crbug.com/1205511.
- std::wstring buffer;
- if (!HidD_GetProductString(hid_handle.Get(),
- base::WriteInto(&buffer, Gamepad::kIdLengthCap),
- Gamepad::kIdLengthCap)) {
- return false;
- }
- // Remove trailing NUL characters.
- buffer = std::wstring(base::TrimString(buffer, base::WStringPiece(L"\0", 1),
- base::TRIM_TRAILING));
- // The product string cannot be empty.
- if (buffer.empty())
- return false;
- product_string_ = std::move(buffer);
- return true;
- }
- base::win::ScopedHandle RawInputGamepadDeviceWin::OpenHidHandle() {
- return base::win::ScopedHandle(::CreateFile(
- name_.c_str(), GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE, /*lpSecurityAttributes=*/nullptr,
- OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/nullptr));
- }
- bool RawInputGamepadDeviceWin::QueryDeviceCapabilities() {
- UINT size = 0;
- UINT result =
- ::GetRawInputDeviceInfo(handle_, RIDI_PREPARSEDDATA, nullptr, &size);
- if (result == static_cast<UINT>(-1)) {
- PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
- return false;
- }
- DCHECK_EQ(0u, result);
- ppd_buffer_.reset(new uint8_t[size]);
- preparsed_data_ = reinterpret_cast<PHIDP_PREPARSED_DATA>(ppd_buffer_.get());
- result = ::GetRawInputDeviceInfo(handle_, RIDI_PREPARSEDDATA,
- ppd_buffer_.get(), &size);
- if (result == static_cast<UINT>(-1)) {
- PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
- return false;
- }
- DCHECK_EQ(size, result);
- HIDP_CAPS caps;
- NTSTATUS status = HidP_GetCaps(preparsed_data_, &caps);
- DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
- QueryButtonCapabilities(caps.NumberInputButtonCaps);
- QueryAxisCapabilities(caps.NumberInputValueCaps);
- return true;
- }
- void RawInputGamepadDeviceWin::QueryButtonCapabilities(uint16_t button_count) {
- if (button_count > 0) {
- std::vector<HIDP_BUTTON_CAPS> button_caps(button_count);
- NTSTATUS status = HidP_GetButtonCaps(HidP_Input, button_caps.data(),
- &button_count, preparsed_data_);
- DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
- // Collect all inputs from the Button usage page.
- QueryNormalButtonCapabilities(button_caps);
- // Check for common gamepad buttons that are not on the Button usage page.
- QuerySpecialButtonCapabilities(button_caps);
- }
- }
- void RawInputGamepadDeviceWin::QueryNormalButtonCapabilities(
- base::span<const HIDP_BUTTON_CAPS> button_caps) {
- // Collect all inputs from the Button usage page and assign button indices
- // based on the usage value.
- for (const auto& item : button_caps) {
- uint16_t usage_min = item.Range.UsageMin;
- uint16_t usage_max = item.Range.UsageMax;
- if (usage_min == 0 || usage_max == 0)
- continue;
- size_t button_index_min = static_cast<size_t>(usage_min - 1);
- size_t button_index_max = static_cast<size_t>(usage_max - 1);
- if (item.UsagePage == kButtonUsagePage &&
- button_index_min < Gamepad::kButtonsLengthCap) {
- button_index_max =
- std::min(Gamepad::kButtonsLengthCap - 1, button_index_max);
- buttons_length_ = std::max(buttons_length_, button_index_max + 1);
- for (size_t button_index = button_index_min;
- button_index <= button_index_max; ++button_index) {
- button_indices_used_[button_index] = true;
- }
- }
- }
- }
- void RawInputGamepadDeviceWin::QuerySpecialButtonCapabilities(
- base::span<const HIDP_BUTTON_CAPS> button_caps) {
- // Check for common gamepad buttons that are not on the Button usage page.
- std::vector<bool> has_special_usage(kSpecialUsagesLen, false);
- size_t unmapped_button_count = 0;
- for (const auto& item : button_caps) {
- uint16_t usage_min = item.Range.UsageMin;
- uint16_t usage_max = item.Range.UsageMax;
- for (size_t special_index = 0; special_index < kSpecialUsagesLen;
- ++special_index) {
- const auto& special = kSpecialUsages[special_index];
- if (item.UsagePage == special.usage_page && usage_min <= special.usage &&
- usage_max >= special.usage) {
- has_special_usage[special_index] = true;
- ++unmapped_button_count;
- }
- }
- }
- special_button_map_.clear();
- if (unmapped_button_count > 0) {
- // Insert special buttons at unused button indices.
- special_button_map_.resize(kSpecialUsagesLen, -1);
- size_t button_index = 0;
- for (size_t special_index = 0; special_index < kSpecialUsagesLen;
- ++special_index) {
- if (!has_special_usage[special_index])
- continue;
- // Advance to the next unused button index.
- while (button_index < Gamepad::kButtonsLengthCap &&
- button_indices_used_[button_index]) {
- ++button_index;
- }
- if (button_index >= Gamepad::kButtonsLengthCap)
- break;
- special_button_map_[special_index] = button_index;
- button_indices_used_[button_index] = true;
- ++button_index;
- if (--unmapped_button_count == 0)
- break;
- }
- buttons_length_ = std::max(buttons_length_, button_index);
- }
- }
- void RawInputGamepadDeviceWin::QueryAxisCapabilities(uint16_t axis_count) {
- std::unique_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[axis_count]);
- HidP_GetValueCaps(HidP_Input, axes_caps.get(), &axis_count, preparsed_data_);
- bool mapped_all_axes = true;
- for (size_t i = 0; i < axis_count; i++) {
- size_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
- if (axis_index < Gamepad::kAxesLengthCap && !axes_[axis_index].active) {
- axes_[axis_index].caps = axes_caps[i];
- axes_[axis_index].value = 0;
- axes_[axis_index].active = true;
- axes_[axis_index].bitmask = GetBitmask(axes_caps[i].BitSize);
- axes_length_ = std::max(axes_length_, axis_index + 1);
- axes_used_ |= 1 << axis_index;
- } else {
- mapped_all_axes = false;
- }
- }
- if (!mapped_all_axes) {
- // For axes whose usage puts them outside the standard axesLengthCap range.
- size_t next_index = 0;
- for (size_t i = 0; i < axis_count; i++) {
- size_t usage = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
- if (usage >= Gamepad::kAxesLengthCap &&
- axes_caps[i].UsagePage <= kGameControlsUsagePage) {
- for (; next_index < Gamepad::kAxesLengthCap; ++next_index) {
- if (!axes_[next_index].active)
- break;
- }
- if (next_index < Gamepad::kAxesLengthCap) {
- axes_[next_index].caps = axes_caps[i];
- axes_[next_index].value = 0;
- axes_[next_index].active = true;
- axes_[next_index].bitmask = GetBitmask(axes_caps[i].BitSize);
- axes_length_ = std::max(axes_length_, next_index + 1);
- axes_used_ |= 1 << next_index;
- }
- }
- if (next_index >= Gamepad::kAxesLengthCap)
- break;
- }
- }
- }
- void RawInputGamepadDeviceWin::UpdateAxisValue(size_t axis_index,
- RAWINPUT& input) {
- DCHECK_LT(axis_index, Gamepad::kAxesLengthCap);
- // RawInput gamepad axes are normalized according to the information provided
- // in the HID report descriptor. Each HID report item must specify a Logical
- // Minimum and Logical Maximum to define the domain of allowable values for
- // the item. An item may optionally specify a Units definition to indicate
- // that the item represents a real-world value measured in those units. Items
- // with a Units definition should also specify Physical Minimum and Physical
- // Maximum, which are the Logical bounds transformed into Physical units.
- //
- // For gamepads, it is common for joystick and trigger axis items to not
- // specify Units. However, D-pad items typically do specify Units. An 8-way
- // directional pad, when implemented as a Hat Switch axis, reports a logical
- // value from 0 to 7 that corresponds to a physical value from 0 degrees (N)
- // clockwise to 315 degrees (NW).
- //
- // When a Hat Switch is in its Null State (no interaction) it reports a value
- // outside the Logical range, often 8. Normalizing the out-of-bounds Null
- // State value yields an invalid axis value greater than +1.0. This invalid
- // axis value must be preserved so that downstream consumers can detect when
- // the Hat Switch is reporting a Null State value.
- //
- // When an item provides Physical bounds, prefer to use
- // HidP_GetScaledUsageValue to retrieve the item's value in real-world units
- // and normalize using the Physical bounds. If the Physical bounds are invalid
- // or HidP_GetScaledUsageValue fails, use HidP_GetUsageValue to retrieve the
- // logical value and normalize using the Logical bounds.
- auto& axis = axes_[axis_index];
- if (axis.caps.PhysicalMin < axis.caps.PhysicalMax) {
- LONG scaled_axis_value = 0;
- if (HidP_GetScaledUsageValue(
- HidP_Input, axis.caps.UsagePage, /*LinkCollection=*/0,
- axis.caps.Range.UsageMin, &scaled_axis_value, preparsed_data_,
- reinterpret_cast<PCHAR>(input.data.hid.bRawData),
- input.data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS) {
- axis.value = NormalizeAxis(scaled_axis_value, axis.caps.PhysicalMin,
- axis.caps.PhysicalMax);
- return;
- }
- }
- ULONG axis_value = 0;
- if (HidP_GetUsageValue(HidP_Input, axis.caps.UsagePage, /*LinkCollection=*/0,
- axis.caps.Range.UsageMin, &axis_value, preparsed_data_,
- reinterpret_cast<PCHAR>(input.data.hid.bRawData),
- input.data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS) {
- axis.value = NormalizeAxis(axis_value & axis.bitmask,
- axis.caps.LogicalMin & axis.bitmask,
- axis.caps.LogicalMax & axis.bitmask);
- return;
- }
- axis.value = 0.0f;
- }
- base::WeakPtr<AbstractHapticGamepad> RawInputGamepadDeviceWin::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
- } // namespace device
|