123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- // Copyright 2020 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 "device/gamepad/wgi_data_fetcher_win.h"
- #include <stdint.h>
- #include <wrl/event.h>
- #include <algorithm>
- #include <memory>
- #include <string>
- #include "base/containers/cxx20_erase.h"
- #include "base/containers/flat_map.h"
- #include "base/sequence_checker.h"
- #include "base/strings/string_util_win.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/win/core_winrt_util.h"
- #include "base/win/hstring_reference.h"
- #include "base/win/windows_version.h"
- #include "device/base/event_utils_winrt.h"
- #include "device/gamepad/dualshock4_controller.h"
- #include "device/gamepad/gamepad_id_list.h"
- #include "device/gamepad/gamepad_standard_mappings.h"
- #include "device/gamepad/nintendo_controller.h"
- #include "device/gamepad/wgi_gamepad_device.h"
- namespace device {
- namespace {
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IRawGameController>
- GetRawGameController(ABI::Windows::Gaming::Input::IGamepad* gamepad,
- WgiDataFetcherWin::GetActivationFactoryFunction
- get_activation_factory_function_) {
- base::win::ScopedHString raw_game_controller_string =
- base::win::ScopedHString::Create(
- RuntimeClass_Windows_Gaming_Input_RawGameController);
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IRawGameControllerStatics>
- raw_game_controller_statics;
- HRESULT hr = get_activation_factory_function_(
- raw_game_controller_string.get(),
- IID_PPV_ARGS(&raw_game_controller_statics));
- if (FAILED(hr))
- return nullptr;
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGameController>
- game_controller;
- hr = gamepad->QueryInterface(IID_PPV_ARGS(&game_controller));
- if (FAILED(hr))
- return nullptr;
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IRawGameController>
- raw_game_controller;
- hr = raw_game_controller_statics->FromGameController(game_controller.Get(),
- &raw_game_controller);
- if (FAILED(hr))
- return nullptr;
- return raw_game_controller;
- }
- GamepadId GetGamepadId(const std::u16string& product_name,
- ABI::Windows::Gaming::Input::IGamepad* gamepad,
- WgiDataFetcherWin::GetActivationFactoryFunction
- get_activation_factory_function) {
- std::string product_name_string = base::UTF16ToUTF8(product_name);
- HRESULT hr = S_OK;
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IRawGameController>
- raw_game_controller =
- GetRawGameController(gamepad, get_activation_factory_function);
- if (!raw_game_controller) {
- return GamepadId::kUnknownGamepad;
- }
- uint16_t vendor_id;
- hr = raw_game_controller->get_HardwareVendorId(&vendor_id);
- if (FAILED(hr)) {
- return GamepadId::kUnknownGamepad;
- }
- uint16_t product_id;
- hr = raw_game_controller->get_HardwareProductId(&product_id);
- if (FAILED(hr)) {
- return GamepadId::kUnknownGamepad;
- }
- return GamepadIdList::Get().GetGamepadId(product_name_string, vendor_id,
- product_id);
- }
- // Check if the gamepad should be added by Windows.Gaming.Input. In the
- // situation that a Nintendo or Dualshock4 gamepad is connected, there are
- // dedicated data fetchers designed for these gamepads.
- // We want to let those data fetchers handle the gamepad input instead.
- bool ShouldEnumerateGamepad(GamepadId gamepad_id) {
- if (gamepad_id == GamepadId::kUnknownGamepad) {
- return false;
- }
- if (NintendoController::IsNintendoController(gamepad_id)) {
- // Nintendo devices are handled by the Nintendo data fetcher.
- return false;
- }
- if (Dualshock4Controller::IsDualshock4(gamepad_id)) {
- // Dualshock4 devices are handled by the RawInput data fetcher.
- return false;
- }
- return true;
- }
- // Checks if the provided gamepad has paddles and returns the available
- // quantity. If the Windows version is less then WIN10_RS1 (WIN10.0.14393.0),
- // this function returns 0, since IGamepad2 interface will not be available.
- uint32_t GetPaddleNumber(
- const Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad>&
- gamepad) {
- uint32_t num_paddles = 0;
- if (base::win::GetVersion() < base::win::Version::WIN10_RS1) {
- return num_paddles;
- }
- static constexpr ABI::Windows::Gaming::Input::GamepadButtons kPaddles[] = {
- ABI::Windows::Gaming::Input::GamepadButtons::GamepadButtons_Paddle1,
- ABI::Windows::Gaming::Input::GamepadButtons::GamepadButtons_Paddle2,
- ABI::Windows::Gaming::Input::GamepadButtons::GamepadButtons_Paddle3,
- ABI::Windows::Gaming::Input::GamepadButtons::GamepadButtons_Paddle4};
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad2> gamepad2;
- HRESULT hr = gamepad->QueryInterface(IID_PPV_ARGS(&gamepad2));
- if (hr == S_OK) {
- ABI::Windows::Gaming::Input::GameControllerButtonLabel button_label;
- for (const auto& paddle : kPaddles) {
- hr = gamepad2->GetButtonLabel(paddle, &button_label);
- if (hr == S_OK &&
- button_label !=
- ABI::Windows::Gaming::Input::GameControllerButtonLabel::
- GameControllerButtonLabel_None) {
- ++num_paddles;
- }
- }
- }
- return num_paddles;
- }
- bool HasTriggerRumbleSupport(GamepadId gamepad_id) {
- // Xbox One USB
- return gamepad_id == GamepadId::kMicrosoftProduct02d1 ||
- // Xbox One USB 2015 Firmware
- gamepad_id == GamepadId::kMicrosoftProduct02dd ||
- // Xbox One S Bluetooth 2016 Firmware
- gamepad_id == GamepadId::kMicrosoftProduct02fd ||
- // Xbox One S Bluetooth 2021 Firmware
- gamepad_id == GamepadId::kMicrosoftProduct0b20 ||
- // Xbox One S USB
- gamepad_id == GamepadId::kMicrosoftProduct02ea ||
- // Xbox One S Bluetooth
- gamepad_id == GamepadId::kMicrosoftProduct02e0 ||
- // Xbox Series X USB
- gamepad_id == GamepadId::kMicrosoftProduct0b12 ||
- // Xbox Series X Bluetooth
- gamepad_id == GamepadId::kMicrosoftProduct0b13 ||
- // Xbox One Elite USB
- gamepad_id == GamepadId::kMicrosoftProduct02e3 ||
- // Xbox One Elite Series 2 USB
- gamepad_id == GamepadId::kMicrosoftProduct0b00 ||
- // Xbox One Elite Series 2 Bluetooth
- gamepad_id == GamepadId::kMicrosoftProduct0b05 ||
- // Xbox Elite Series 2 Bluetooth 2021 Firmware
- gamepad_id == GamepadId::kMicrosoftProduct0b22;
- }
- } // namespace
- WgiDataFetcherWin::WgiDataFetcherWin() {
- DETACH_FROM_SEQUENCE(sequence_checker_);
- // If a callback has been overridden previously, we'll give preference to it.
- if (GetActivationFactoryFunctionCallback()) {
- get_activation_factory_function_ =
- GetActivationFactoryFunctionCallback().Run();
- } else {
- get_activation_factory_function_ = &base::win::RoGetActivationFactory;
- }
- }
- WgiDataFetcherWin::~WgiDataFetcherWin() {
- UnregisterEventHandlers();
- for (auto& map_entry : devices_) {
- if (map_entry.second) {
- map_entry.second->Shutdown();
- }
- }
- }
- GamepadSource WgiDataFetcherWin::source() {
- return Factory::static_source();
- }
- void WgiDataFetcherWin::OnAddedToProvider() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!base::win::HStringReference::ResolveCoreWinRTStringDelayload()) {
- initialization_state_ =
- InitializationState::kCoreWinrtStringDelayLoadFailed;
- return;
- }
- HRESULT hr = get_activation_factory_function_(
- base::win::HStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad)
- .Get(),
- IID_PPV_ARGS(&gamepad_statics_));
- if (FAILED(hr)) {
- initialization_state_ = InitializationState::kRoGetActivationFactoryFailed;
- return;
- }
- // Create a Windows::Foundation::IEventHandler that runs a
- // base::RepeatingCallback() on the gamepad polling thread when a gamepad
- // is added or removed. This callback stores the current sequence task runner
- // and weak pointer, so those two objects would remain active until the
- // callback returns.
- added_event_token_ = AddEventHandler(
- gamepad_statics_.Get(),
- &ABI::Windows::Gaming::Input::IGamepadStatics::add_GamepadAdded,
- base::BindRepeating(&WgiDataFetcherWin::OnGamepadAdded,
- weak_factory_.GetWeakPtr()));
- if (!added_event_token_) {
- initialization_state_ = InitializationState::kAddGamepadAddedFailed;
- UnregisterEventHandlers();
- return;
- }
- removed_event_token_ = AddEventHandler(
- gamepad_statics_.Get(),
- &ABI::Windows::Gaming::Input::IGamepadStatics::add_GamepadRemoved,
- base::BindRepeating(&WgiDataFetcherWin::OnGamepadRemoved,
- weak_factory_.GetWeakPtr()));
- if (!removed_event_token_) {
- initialization_state_ = InitializationState::kAddGamepadRemovedFailed;
- UnregisterEventHandlers();
- return;
- }
- initialization_state_ = InitializationState::kInitialized;
- }
- void WgiDataFetcherWin::OnGamepadAdded(
- IInspectable* /* sender */,
- ABI::Windows::Gaming::Input::IGamepad* gamepad) {
- // While base::win::AddEventHandler stores the sequence_task_runner in the
- // callback function object, it post the task back to the same sequence - the
- // gamepad polling thread when the callback is returned on a different thread
- // from the IGamepadStatics COM API. Thus `OnGamepadAdded` is also running on
- // gamepad polling thread, it is the only thread that is able to access the
- // `devices_` object, making it thread-safe.
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (initialization_state_ != InitializationState::kInitialized)
- return;
- const std::u16string display_name = GetGamepadDisplayName(gamepad);
- GamepadId gamepad_id =
- GetGamepadId(display_name, gamepad, get_activation_factory_function_);
- if (!ShouldEnumerateGamepad(gamepad_id)) {
- return;
- }
- int source_id = next_source_id_++;
- PadState* state = GetPadState(source_id);
- if (!state)
- return;
- state->is_initialized = true;
- Gamepad& pad = state->data;
- pad.SetID(display_name);
- pad.connected = true;
- if (HasTriggerRumbleSupport(gamepad_id)) {
- pad.vibration_actuator.type = GamepadHapticActuatorType::kTriggerRumble;
- } else {
- pad.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
- }
- pad.vibration_actuator.not_null = true;
- pad.mapping = GamepadMapping::kStandard;
- devices_[source_id] = std::make_unique<WgiGamepadDevice>(gamepad);
- }
- void WgiDataFetcherWin::OnGamepadRemoved(
- IInspectable* /* sender */,
- ABI::Windows::Gaming::Input::IGamepad* gamepad) {
- // While ::device::AddEventHandler stores the sequence_task_runner in the
- // callback function object, it post the task back to the same sequence - the
- // gamepad polling thread when the callback is returned on a different thread
- // from the IGamepadStatics COM API. Thus `OnGamepadRemoved` is also running
- // on gamepad polling thread, it is the only thread that is able to access the
- // `devices_` object, making it thread-safe.
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(initialization_state_, InitializationState::kInitialized);
- base::EraseIf(devices_, [=](const auto& map_entry) {
- if (map_entry.second->GetGamepad().Get() == gamepad) {
- map_entry.second->Shutdown();
- return true;
- }
- return false;
- });
- }
- void WgiDataFetcherWin::GetGamepadData(bool devices_changed_hint) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- for (const auto& map_entry : devices_) {
- PadState* state = GetPadState(map_entry.first);
- if (!state)
- continue;
- ABI::Windows::Gaming::Input::GamepadReading reading;
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad> gamepad =
- map_entry.second->GetGamepad();
- if (FAILED(gamepad->GetCurrentReading(&reading)))
- continue;
- Gamepad& pad = state->data;
- pad.timestamp = CurrentTimeInMicroseconds();
- const uint32_t num_paddles = GetPaddleNumber(gamepad);
- if (num_paddles == 0) {
- pad.buttons_length = BUTTON_INDEX_COUNT - 1; // No meta.
- } else {
- pad.buttons_length = BUTTON_INDEX_COUNT + num_paddles;
- }
- static constexpr struct {
- int button_index;
- ABI::Windows::Gaming::Input::GamepadButtons wgi_button_mask;
- } kButtonMappings[] = {
- {BUTTON_INDEX_PRIMARY, ABI::Windows::Gaming::Input::GamepadButtons_A},
- {BUTTON_INDEX_SECONDARY, ABI::Windows::Gaming::Input::GamepadButtons_B},
- {BUTTON_INDEX_TERTIARY, ABI::Windows::Gaming::Input::GamepadButtons_X},
- {BUTTON_INDEX_QUATERNARY,
- ABI::Windows::Gaming::Input::GamepadButtons_Y},
- {BUTTON_INDEX_LEFT_SHOULDER,
- ABI::Windows::Gaming::Input::GamepadButtons_LeftShoulder},
- {BUTTON_INDEX_RIGHT_SHOULDER,
- ABI::Windows::Gaming::Input::GamepadButtons_RightShoulder},
- {BUTTON_INDEX_BACK_SELECT,
- ABI::Windows::Gaming::Input::GamepadButtons_View},
- {BUTTON_INDEX_START, ABI::Windows::Gaming::Input::GamepadButtons_Menu},
- {BUTTON_INDEX_LEFT_THUMBSTICK,
- ABI::Windows::Gaming::Input::GamepadButtons_LeftThumbstick},
- {BUTTON_INDEX_RIGHT_THUMBSTICK,
- ABI::Windows::Gaming::Input::GamepadButtons_RightThumbstick},
- {BUTTON_INDEX_DPAD_UP,
- ABI::Windows::Gaming::Input::GamepadButtons_DPadUp},
- {BUTTON_INDEX_DPAD_DOWN,
- ABI::Windows::Gaming::Input::GamepadButtons_DPadDown},
- {BUTTON_INDEX_DPAD_LEFT,
- ABI::Windows::Gaming::Input::GamepadButtons_DPadLeft},
- {BUTTON_INDEX_DPAD_RIGHT,
- ABI::Windows::Gaming::Input::GamepadButtons_DPadRight},
- {BUTTON_INDEX_META + 1,
- ABI::Windows::Gaming::Input::GamepadButtons_Paddle1},
- {BUTTON_INDEX_META + 2,
- ABI::Windows::Gaming::Input::GamepadButtons_Paddle2},
- {BUTTON_INDEX_META + 3,
- ABI::Windows::Gaming::Input::GamepadButtons_Paddle3},
- {BUTTON_INDEX_META + 4,
- ABI::Windows::Gaming::Input::GamepadButtons_Paddle4},
- };
- for (const auto& button : kButtonMappings) {
- if (reading.Buttons & button.wgi_button_mask) {
- pad.buttons[button.button_index].pressed = true;
- pad.buttons[button.button_index].value = 1.0f;
- } else {
- pad.buttons[button.button_index].pressed = false;
- pad.buttons[button.button_index].value = 0.0f;
- }
- }
- pad.buttons[BUTTON_INDEX_LEFT_TRIGGER].pressed =
- reading.LeftTrigger > GamepadButton::kDefaultButtonPressedThreshold;
- pad.buttons[BUTTON_INDEX_LEFT_TRIGGER].value = reading.LeftTrigger;
- pad.buttons[BUTTON_INDEX_RIGHT_TRIGGER].pressed =
- reading.RightTrigger > GamepadButton::kDefaultButtonPressedThreshold;
- pad.buttons[BUTTON_INDEX_RIGHT_TRIGGER].value = reading.RightTrigger;
- pad.axes_length = AXIS_INDEX_COUNT;
- // Invert the Y thumbstick axes to match the Standard Gamepad. WGI
- // thumbstick axes use +up/+right but the Standard Gamepad uses
- // +down/+right.
- pad.axes[AXIS_INDEX_LEFT_STICK_X] = reading.LeftThumbstickX;
- pad.axes[AXIS_INDEX_LEFT_STICK_Y] = reading.LeftThumbstickY * -1.0f;
- pad.axes[AXIS_INDEX_RIGHT_STICK_X] = reading.RightThumbstickX;
- pad.axes[AXIS_INDEX_RIGHT_STICK_Y] = reading.RightThumbstickY * -1.0f;
- }
- }
- void WgiDataFetcherWin::PlayEffect(
- int source_id,
- mojom::GamepadHapticEffectType type,
- mojom::GamepadEffectParametersPtr params,
- mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,
- scoped_refptr<base::SequencedTaskRunner> callback_runner) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto map_entry = devices_.find(source_id);
- if (map_entry == devices_.end()) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
- return;
- }
- map_entry->second->PlayEffect(type, std::move(params), std::move(callback),
- std::move(callback_runner));
- }
- void WgiDataFetcherWin::ResetVibration(
- int source_id,
- mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback,
- scoped_refptr<base::SequencedTaskRunner> callback_runner) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto map_entry = devices_.find(source_id);
- if (map_entry == devices_.end()) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
- return;
- }
- map_entry->second->ResetVibration(std::move(callback),
- std::move(callback_runner));
- }
- // static
- void WgiDataFetcherWin::OverrideActivationFactoryFunctionForTesting(
- WgiDataFetcherWin::ActivationFactoryFunctionCallback callback) {
- GetActivationFactoryFunctionCallback() = callback;
- }
- // static
- WgiDataFetcherWin::ActivationFactoryFunctionCallback&
- WgiDataFetcherWin::GetActivationFactoryFunctionCallback() {
- static base::NoDestructor<
- WgiDataFetcherWin::ActivationFactoryFunctionCallback>
- instance;
- return *instance;
- }
- WgiDataFetcherWin::InitializationState
- WgiDataFetcherWin::GetInitializationState() const {
- return initialization_state_;
- }
- std::u16string WgiDataFetcherWin::GetGamepadDisplayName(
- ABI::Windows::Gaming::Input::IGamepad* gamepad) {
- static constexpr char16_t kDefaultDisplayName[] = u"Unknown Gamepad";
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IRawGameController>
- raw_game_controller = ::device::GetRawGameController(
- gamepad, get_activation_factory_function_);
- if (!raw_game_controller) {
- return kDefaultDisplayName;
- }
- Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IRawGameController2>
- raw_game_controller2;
- if (FAILED(raw_game_controller.As(&raw_game_controller2))) {
- return kDefaultDisplayName;
- }
- HSTRING display_name;
- if (FAILED(raw_game_controller2->get_DisplayName(&display_name))) {
- return kDefaultDisplayName;
- }
- base::win::ScopedHString scoped_display_name(display_name);
- return base::AsString16(scoped_display_name.Get());
- }
- void WgiDataFetcherWin::UnregisterEventHandlers() {
- if (added_event_token_) {
- HRESULT hr =
- gamepad_statics_->remove_GamepadAdded(added_event_token_.value());
- if (FAILED(hr)) {
- DLOG(ERROR) << "Removing GamepadAdded Handler failed: "
- << logging::SystemErrorCodeToString(hr);
- }
- }
- if (removed_event_token_) {
- HRESULT hr =
- gamepad_statics_->remove_GamepadRemoved(removed_event_token_.value());
- if (FAILED(hr)) {
- DLOG(ERROR) << "Removing GamepadRemoved Handler failed: "
- << logging::SystemErrorCodeToString(hr);
- }
- }
- }
- } // namespace device
|