0

[Gamepad] Implement dual-rumble effect for WGI gamepads

This CL implements the dual-rumble effect for Windows.Gaming.Input (WGI)
gamepads. It does so by making WgiGamepadDevice inherit from
AbstractHapticGamepad and by making every WGI gamepad object to have a

1: 1 association with a WgiGamepadDevice object.
Change-Id: I67284a3a2c8be0d78e71c59b667836291a03d11d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3347270
Reviewed-by: Matt Reynolds <mattreynolds@chromium.org>
Commit-Queue: Gabriel Brito <gabrielbrito@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#956500}
This commit is contained in:
Gabriel Brito
2022-01-07 14:44:10 +00:00
committed by Chromium LUCI CQ
parent f03f9a3c09
commit 0b7e2c62ad
8 changed files with 281 additions and 103 deletions

@ -81,6 +81,8 @@ component("gamepad") {
"raw_input_gamepad_device_win.h",
"wgi_data_fetcher_win.cc",
"wgi_data_fetcher_win.h",
"wgi_gamepad_device.cc",
"wgi_gamepad_device.h",
"xinput_data_fetcher_win.cc",
"xinput_data_fetcher_win.h",
"xinput_haptic_gamepad_win.cc",

@ -24,14 +24,17 @@ FakeIGamepad::~FakeIGamepad() = default;
HRESULT WINAPI FakeIGamepad::get_Vibration(
ABI::Windows::Gaming::Input::GamepadVibration* gamepad_vibration) {
NOTIMPLEMENTED();
return E_NOTIMPL;
gamepad_vibration->LeftMotor = fake_gamepad_vibration_.LeftMotor;
gamepad_vibration->LeftTrigger = fake_gamepad_vibration_.LeftTrigger;
gamepad_vibration->RightMotor = fake_gamepad_vibration_.RightMotor;
gamepad_vibration->RightTrigger = fake_gamepad_vibration_.RightTrigger;
return S_OK;
}
HRESULT WINAPI FakeIGamepad::put_Vibration(
ABI::Windows::Gaming::Input::GamepadVibration gamepad_vibration) {
NOTIMPLEMENTED();
return E_NOTIMPL;
fake_gamepad_vibration_ = gamepad_vibration;
return S_OK;
}
HRESULT WINAPI FakeIGamepad::GetCurrentReading(

@ -77,6 +77,7 @@ class FakeIGamepad final
bool has_paddles_ = false;
ABI::Windows::Gaming::Input::GamepadReading fake_gamepad_reading_;
ABI::Windows::Gaming::Input::GamepadVibration fake_gamepad_vibration_;
};
} // namespace device

@ -7,10 +7,12 @@
#include <stdint.h>
#include <wrl/event.h>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#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"
@ -23,6 +25,8 @@
#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 {
@ -150,6 +154,11 @@ WgiDataFetcherWin::WgiDataFetcherWin() {
WgiDataFetcherWin::~WgiDataFetcherWin() {
UnregisterEventHandlers();
for (auto& map_entry : devices_) {
if (map_entry.second) {
map_entry.second->Shutdown();
}
}
}
GamepadSource WgiDataFetcherWin::source() {
@ -211,7 +220,7 @@ void WgiDataFetcherWin::OnGamepadAdded(
// 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
// `gamepads_` object, making it thread-safe.
// `devices_` object, making it thread-safe.
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (initialization_state_ != InitializationState::kInitialized)
@ -232,9 +241,9 @@ void WgiDataFetcherWin::OnGamepadAdded(
pad.SetID(display_name);
pad.connected = true;
pad.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
pad.vibration_actuator.not_null = false;
pad.vibration_actuator.not_null = true;
pad.mapping = GamepadMapping::kStandard;
gamepads_.push_back({source_id, gamepad});
devices_[source_id] = std::make_unique<WgiGamepadDevice>(gamepad);
}
void WgiDataFetcherWin::OnGamepadRemoved(
@ -245,26 +254,29 @@ void WgiDataFetcherWin::OnGamepadRemoved(
// 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
// `gamepads_` object, making it thread-safe.
// `devices_` object, making it thread-safe.
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(initialization_state_, InitializationState::kInitialized);
base::EraseIf(gamepads_,
[=](const WindowsGamingInputControllerMapping& mapping) {
return mapping.gamepad.Get() == gamepad;
});
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& gamepad_mapping : gamepads_) {
PadState* state = GetPadState(gamepad_mapping.source_id);
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 =
gamepad_mapping.gamepad;
map_entry.second->GetGamepad();
if (FAILED(gamepad->GetCurrentReading(&reading)))
continue;
@ -345,6 +357,44 @@ void WgiDataFetcherWin::GetGamepadData(bool devices_changed_hint) {
}
}
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) {
@ -360,11 +410,6 @@ WgiDataFetcherWin::GetActivationFactoryFunctionCallback() {
return *instance;
}
const std::vector<WgiDataFetcherWin::WindowsGamingInputControllerMapping>&
WgiDataFetcherWin::GetGamepadsForTesting() const {
return gamepads_;
}
WgiDataFetcherWin::InitializationState
WgiDataFetcherWin::GetInitializationState() const {
return initialization_state_;
@ -416,34 +461,4 @@ void WgiDataFetcherWin::UnregisterEventHandlers() {
}
}
WgiDataFetcherWin::WindowsGamingInputControllerMapping::
WindowsGamingInputControllerMapping(
int input_source_id,
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad>
input_gamepad)
: source_id(input_source_id), gamepad(input_gamepad) {}
WgiDataFetcherWin::WindowsGamingInputControllerMapping::
WindowsGamingInputControllerMapping(
const WgiDataFetcherWin::WindowsGamingInputControllerMapping& other) =
default;
WgiDataFetcherWin::WindowsGamingInputControllerMapping&
WgiDataFetcherWin::WindowsGamingInputControllerMapping::
WindowsGamingInputControllerMapping::operator=(
const WgiDataFetcherWin::WindowsGamingInputControllerMapping& other) =
default;
WgiDataFetcherWin::WindowsGamingInputControllerMapping::
WindowsGamingInputControllerMapping(
WgiDataFetcherWin::WindowsGamingInputControllerMapping&& other) =
default;
WgiDataFetcherWin::WindowsGamingInputControllerMapping&
WgiDataFetcherWin::WindowsGamingInputControllerMapping::operator=(
WgiDataFetcherWin::WindowsGamingInputControllerMapping&&) = default;
WgiDataFetcherWin::WindowsGamingInputControllerMapping::
~WindowsGamingInputControllerMapping() = default;
} // namespace device

@ -10,9 +10,16 @@
#include <Windows.Gaming.Input.h>
#include <wrl/event.h>
#include <memory>
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "device/gamepad/public/mojom/gamepad.mojom.h"
#include "device/gamepad/wgi_gamepad_device.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace device {
@ -20,31 +27,6 @@ namespace device {
class DEVICE_GAMEPAD_EXPORT WgiDataFetcherWin final
: public GamepadDataFetcher {
public:
struct WindowsGamingInputControllerMapping {
public:
WindowsGamingInputControllerMapping(
int input_source_id,
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad>
input_gamepad);
WindowsGamingInputControllerMapping(
const WindowsGamingInputControllerMapping& other);
WindowsGamingInputControllerMapping(
WindowsGamingInputControllerMapping&& other);
WindowsGamingInputControllerMapping& operator=(
const WindowsGamingInputControllerMapping& other);
WindowsGamingInputControllerMapping& operator=(
WindowsGamingInputControllerMapping&&);
~WindowsGamingInputControllerMapping();
int source_id;
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad> gamepad;
};
enum class InitializationState {
kUninitialized,
kInitialized,
@ -72,6 +54,15 @@ class DEVICE_GAMEPAD_EXPORT WgiDataFetcherWin final
GamepadSource source() override;
void OnAddedToProvider() override;
void GetGamepadData(bool devices_changed_hint) override;
void PlayEffect(int source_id,
mojom::GamepadHapticEffectType,
mojom::GamepadEffectParametersPtr,
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback,
scoped_refptr<base::SequencedTaskRunner>) override;
void ResetVibration(
int source_id,
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback,
scoped_refptr<base::SequencedTaskRunner>) override;
// Set fake ActivationFunction for test to avoid dependencies on the OS API.
using ActivationFactoryFunctionCallback =
@ -79,8 +70,9 @@ class DEVICE_GAMEPAD_EXPORT WgiDataFetcherWin final
static void OverrideActivationFactoryFunctionForTesting(
ActivationFactoryFunctionCallback callback);
const std::vector<WindowsGamingInputControllerMapping>&
GetGamepadsForTesting() const;
// Used to store gamepad devices indexed by its source id.
using DeviceMap = base::flat_map<int, std::unique_ptr<WgiGamepadDevice>>;
const DeviceMap& GetGamepadsForTesting() const { return devices_; }
InitializationState GetInitializationState() const;
@ -111,7 +103,7 @@ class DEVICE_GAMEPAD_EXPORT WgiDataFetcherWin final
InitializationState initialization_state_ =
InitializationState::kUninitialized;
std::vector<WindowsGamingInputControllerMapping> gamepads_;
DeviceMap devices_;
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepadStatics>
gamepad_statics_;

@ -4,7 +4,9 @@
#include "device/gamepad/wgi_data_fetcher_win.h"
#include "base/bind.h"
#include "base/containers/fixed_flat_map.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/run_loop.h"
@ -18,10 +20,13 @@
#include "device/gamepad/gamepad_provider.h"
#include "device/gamepad/gamepad_standard_mappings.h"
#include "device/gamepad/public/cpp/gamepad.h"
#include "device/gamepad/public/mojom/gamepad.mojom.h"
#include "device/gamepad/public/mojom/gamepad_hardware_buffer.h"
#include "device/gamepad/test_support/fake_igamepad.h"
#include "device/gamepad/test_support/fake_igamepad_statics.h"
#include "device/gamepad/test_support/fake_winrt_wgi_environment.h"
#include "device/gamepad/wgi_data_fetcher_win.h"
#include "device/gamepad/wgi_gamepad_device.h"
#include "services/device/device_service_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -42,6 +47,11 @@ constexpr unsigned int kGamepadButtonsLength = 16;
constexpr unsigned int kGamepadWithPaddlesButtonsLength = 21;
constexpr unsigned int kGamepadAxesLength = 4;
constexpr double kDurationMillis = 1.0;
constexpr double kZeroStartDelayMillis = 0.0;
constexpr double kStrongMagnitude = 1.0; // 100% intensity.
constexpr double kWeakMagnitude = 0.5; // 50% intensity.
constexpr ErrorCode kErrors[] = {
ErrorCode::kErrorWgiRawGameControllerActivateFailed,
ErrorCode::kErrorWgiRawGameControllerFromGameControllerFailed,
@ -140,7 +150,7 @@ class WgiDataFetcherWinTest : public DeviceServiceTestBase {
EXPECT_TRUE(pad_state->is_initialized);
Gamepad& pad = pad_state->data;
EXPECT_TRUE(pad.connected);
EXPECT_FALSE(pad.vibration_actuator.not_null);
EXPECT_TRUE(pad.vibration_actuator.not_null);
}
void CheckGamepadRemoved() {
@ -286,7 +296,41 @@ class WgiDataFetcherWinTest : public DeviceServiceTestBase {
WgiDataFetcherWin& fetcher() const { return *fetcher_; }
// Gets called after PlayEffect or ResetVibration.
void HapticsCallback(mojom::GamepadHapticsResult result) {
haptics_callback_count_++;
haptics_callback_result_ = result;
}
void SimulateDualRumbleEffect(int pad_index) {
base::RunLoop run_loop;
provider_->PlayVibrationEffectOnce(
pad_index,
mojom::GamepadHapticEffectType::GamepadHapticEffectTypeDualRumble,
mojom::GamepadEffectParameters::New(kDurationMillis,
kZeroStartDelayMillis,
kStrongMagnitude, kWeakMagnitude),
base::BindOnce(&WgiDataFetcherWinTest::HapticsCallback,
base::Unretained(this))
.Then(run_loop.QuitClosure()));
FlushPollingThread();
run_loop.Run();
}
void SimulateResetVibration(int pad_index) {
base::RunLoop run_loop;
provider_->ResetVibrationActuator(
pad_index, base::BindOnce(&WgiDataFetcherWinTest::HapticsCallback,
base::Unretained(this))
.Then(run_loop.QuitClosure()));
FlushPollingThread();
run_loop.Run();
}
protected:
int haptics_callback_count_ = 0;
mojom::GamepadHapticsResult haptics_callback_result_ =
mojom::GamepadHapticsResult::GamepadHapticsResultError;
std::unique_ptr<GamepadProvider> provider_;
std::unique_ptr<FakeWinrtWgiEnvironment> wgi_environment_;
@ -300,7 +344,7 @@ TEST_F(WgiDataFetcherWinTest, AddAndRemoveWgiGamepad) {
// Check initial number of connected gamepad and WGI initialization status.
EXPECT_EQ(fetcher().GetInitializationState(),
device::WgiDataFetcherWin::InitializationState::kInitialized);
WgiDataFetcherWin::InitializationState::kInitialized);
EXPECT_TRUE(fetcher().GetGamepadsForTesting().empty());
const auto fake_gamepad = Microsoft::WRL::Make<FakeIGamepad>();
@ -321,10 +365,10 @@ TEST_F(WgiDataFetcherWinTest, AddAndRemoveWgiGamepad) {
FlushPollingThread();
// Assert that the gamepad has been added to the DataFetcher.
const std::vector<WgiDataFetcherWin::WindowsGamingInputControllerMapping>&
gamepads = fetcher().GetGamepadsForTesting();
const base::flat_map<int, std::unique_ptr<WgiGamepadDevice>>& gamepads =
fetcher().GetGamepadsForTesting();
ASSERT_EQ(gamepads.size(), 1u);
CheckGamepadAdded(fetcher().GetPadState(gamepads.front().source_id));
CheckGamepadAdded(fetcher().GetPadState(gamepads.begin()->first));
// Simulate the gamepad removing behavior, and make the gamepad-removing
// callback return on a different thread, demonstrated the multi-threaded
@ -344,9 +388,8 @@ TEST_F(WgiDataFetcherWinTest, AddGamepadAddedEventHandlerErrorHandling) {
SetUpTestEnv(ErrorCode::kGamepadAddGamepadAddedFailed);
// Check WGI initialization status.
EXPECT_EQ(
fetcher().GetInitializationState(),
device::WgiDataFetcherWin::InitializationState::kAddGamepadAddedFailed);
EXPECT_EQ(fetcher().GetInitializationState(),
WgiDataFetcherWin::InitializationState::kAddGamepadAddedFailed);
auto* gamepad_statics = FakeIGamepadStatics::GetInstance();
EXPECT_EQ(gamepad_statics->GetGamepadAddedEventHandlerCount(), 0u);
}
@ -357,9 +400,8 @@ TEST_F(WgiDataFetcherWinTest, AddGamepadRemovedEventHandlerErrorHandling) {
SetUpTestEnv(ErrorCode::kGamepadAddGamepadRemovedFailed);
// Check WGI initialization status.
EXPECT_EQ(
fetcher().GetInitializationState(),
device::WgiDataFetcherWin::InitializationState::kAddGamepadRemovedFailed);
EXPECT_EQ(fetcher().GetInitializationState(),
WgiDataFetcherWin::InitializationState::kAddGamepadRemovedFailed);
auto* gamepad_statics = FakeIGamepadStatics::GetInstance();
EXPECT_EQ(gamepad_statics->GetGamepadRemovedEventHandlerCount(), 0u);
}
@ -370,9 +412,9 @@ TEST_F(WgiDataFetcherWinTest, WgiGamepadActivationFactoryErrorHandling) {
SetUpTestEnv(ErrorCode::kErrorWgiGamepadActivateFailed);
// Check WGI initialization status.
EXPECT_EQ(fetcher().GetInitializationState(),
device::WgiDataFetcherWin::InitializationState::
kRoGetActivationFactoryFailed);
EXPECT_EQ(
fetcher().GetInitializationState(),
WgiDataFetcherWin::InitializationState::kRoGetActivationFactoryFailed);
}
// If RawGameController2::get_DisplayName fails when calling
@ -392,10 +434,10 @@ TEST_F(WgiDataFetcherWinTest, FailuretoGetDisplayNameOnGamepadAdded) {
FlushPollingThread();
// Assert that the gamepad has not been added to the DataFetcher.
const std::vector<WgiDataFetcherWin::WindowsGamingInputControllerMapping>&
gamepads = fetcher().GetGamepadsForTesting();
const base::flat_map<int, std::unique_ptr<WgiGamepadDevice>>& gamepads =
fetcher().GetGamepadsForTesting();
ASSERT_EQ(gamepads.size(), 1u);
PadState* pad = fetcher().GetPadState(gamepads.front().source_id);
PadState* pad = fetcher().GetPadState(gamepads.begin()->first);
std::u16string display_id(pad->data.id);
EXPECT_EQ(kDefaultDisplayName, display_id);
CheckGamepadAdded(pad);
@ -406,7 +448,7 @@ TEST_F(WgiDataFetcherWinTest, VerifyGamepadInput) {
// Check initial number of connected gamepad and WGI initialization status.
EXPECT_EQ(fetcher().GetInitializationState(),
device::WgiDataFetcherWin::InitializationState::kInitialized);
WgiDataFetcherWin::InitializationState::kInitialized);
auto* fake_gamepad_statics = FakeIGamepadStatics::GetInstance();
const auto fake_gamepad = Microsoft::WRL::Make<FakeIGamepad>();
@ -449,6 +491,55 @@ TEST_F(WgiDataFetcherWinTest, VerifyGamepadInput) {
/*has_paddles*/ true);
}
TEST_F(WgiDataFetcherWinTest, PlayDualRumbleEffect) {
SetUpTestEnv();
// Check initial number of connected gamepad and WGI initialization status.
EXPECT_EQ(fetcher().GetInitializationState(),
WgiDataFetcherWin::InitializationState::kInitialized);
auto* fake_gamepad_statics = FakeIGamepadStatics::GetInstance();
const auto fake_gamepad = Microsoft::WRL::Make<FakeIGamepad>();
// Add a simulated WGI device.
provider_->Resume();
fake_gamepad_statics->SimulateGamepadAdded(
fake_gamepad, kHardwareProductId, kHardwareVendorId, kGamepadDisplayName);
SimulateDualRumbleEffect(/*pad_index=*/0);
EXPECT_EQ(haptics_callback_count_, 1);
EXPECT_EQ(haptics_callback_result_,
mojom::GamepadHapticsResult::GamepadHapticsResultComplete);
ABI::Windows::Gaming::Input::GamepadVibration fake_gamepad_vibration;
fake_gamepad->get_Vibration(&fake_gamepad_vibration);
EXPECT_EQ(fake_gamepad_vibration.LeftMotor, kStrongMagnitude);
EXPECT_EQ(fake_gamepad_vibration.RightMotor, kWeakMagnitude);
EXPECT_EQ(fake_gamepad_vibration.LeftTrigger, 0.0f);
EXPECT_EQ(fake_gamepad_vibration.RightTrigger, 0.0f);
// Calling ResetVibration sets the vibration intensity to 0 for both motors.
SimulateResetVibration(/*pad_index=*/0);
EXPECT_EQ(haptics_callback_count_, 2);
EXPECT_EQ(haptics_callback_result_,
mojom::GamepadHapticsResult::GamepadHapticsResultComplete);
fake_gamepad->get_Vibration(&fake_gamepad_vibration);
EXPECT_EQ(fake_gamepad_vibration.LeftMotor, 0.0f);
EXPECT_EQ(fake_gamepad_vibration.RightMotor, 0.0f);
EXPECT_EQ(fake_gamepad_vibration.LeftTrigger, 0.0f);
EXPECT_EQ(fake_gamepad_vibration.RightTrigger, 0.0f);
// Attempting to call haptics methods on invalid pad_id's will return a result
// of type GamepadHapticsResultNotSupported.
fake_gamepad_statics->SimulateGamepadRemoved(fake_gamepad);
SimulateDualRumbleEffect(/*pad_index=*/0);
EXPECT_EQ(haptics_callback_count_, 3);
EXPECT_EQ(haptics_callback_result_,
mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
SimulateResetVibration(/*pad_index=*/0);
EXPECT_EQ(haptics_callback_count_, 4);
EXPECT_EQ(haptics_callback_result_,
mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
}
// When an error happens when calling GamepadGetCurrentReading, the state in
// the shared buffer will not be modified.
TEST_F(WgiDataFetcherWinTest, WgiGamepadGetCurrentReadingError) {
@ -456,7 +547,7 @@ TEST_F(WgiDataFetcherWinTest, WgiGamepadGetCurrentReadingError) {
// Check initial number of connected gamepad and WGI initialization status.
EXPECT_EQ(fetcher().GetInitializationState(),
device::WgiDataFetcherWin::InitializationState::kInitialized);
WgiDataFetcherWin::InitializationState::kInitialized);
auto* fake_gamepad_statics = FakeIGamepadStatics::GetInstance();
const auto fake_gamepad = Microsoft::WRL::Make<FakeIGamepad>();
@ -502,7 +593,7 @@ TEST_F(WgiDataFetcherWinTest, WgiGamepadGetButtonLabelError) {
// Check initial number of connected gamepad and WGI initialization status.
EXPECT_EQ(fetcher().GetInitializationState(),
device::WgiDataFetcherWin::InitializationState::kInitialized);
WgiDataFetcherWin::InitializationState::kInitialized);
auto* fake_gamepad_statics = FakeIGamepadStatics::GetInstance();
const auto fake_gamepad_with_paddles = Microsoft::WRL::Make<FakeIGamepad>();
@ -564,8 +655,8 @@ TEST_F(WgiDataFetcherWinTest, ShouldNotEnumerateControllers) {
FlushPollingThread();
// Assert that the gamepad has not been added to the DataFetcher.
const std::vector<WgiDataFetcherWin::WindowsGamingInputControllerMapping>&
gamepads = fetcher().GetGamepadsForTesting();
const base::flat_map<int, std::unique_ptr<WgiGamepadDevice>>& gamepads =
fetcher().GetGamepadsForTesting();
EXPECT_EQ(gamepads.size(), 0u);
}
@ -589,8 +680,8 @@ TEST_P(WgiDataFetcherWinErrorTest, GamepadShouldNotbeEnumerated) {
FlushPollingThread();
// Assert that the gamepad has not been added to the DataFetcher.
const std::vector<WgiDataFetcherWin::WindowsGamingInputControllerMapping>&
gamepads = fetcher().GetGamepadsForTesting();
const base::flat_map<int, std::unique_ptr<WgiGamepadDevice>>& gamepads =
fetcher().GetGamepadsForTesting();
EXPECT_EQ(gamepads.size(), 0u);
}
INSTANTIATE_TEST_SUITE_P(WgiDataFetcherWinErrorTests,

@ -0,0 +1,31 @@
// Copyright 2022 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_gamepad_device.h"
#include "base/trace_event/trace_event.h"
namespace device {
WgiGamepadDevice::WgiGamepadDevice(
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad> gamepad)
: gamepad_(std::move(gamepad)) {}
WgiGamepadDevice::~WgiGamepadDevice() = default;
void WgiGamepadDevice::SetVibration(double strong_magnitude,
double weak_magnitude) {
ABI::Windows::Gaming::Input::GamepadVibration vibration = {
.LeftMotor = strong_magnitude,
.RightMotor = weak_magnitude,
};
HRESULT hr = gamepad_->put_Vibration(vibration);
DCHECK(SUCCEEDED(hr));
}
base::WeakPtr<AbstractHapticGamepad> WgiGamepadDevice::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace device

@ -0,0 +1,43 @@
// Copyright 2022 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.
#ifndef DEVICE_GAMEPAD_WGI_GAMEPAD_DEVICE_H_
#define DEVICE_GAMEPAD_WGI_GAMEPAD_DEVICE_H_
#include <Windows.Gaming.Input.h>
#include <wrl/client.h>
#include <wrl/event.h>
#include "base/memory/weak_ptr.h"
#include "base/win/core_winrt_util.h"
#include "device/gamepad/abstract_haptic_gamepad.h"
namespace device {
class DEVICE_GAMEPAD_EXPORT WgiGamepadDevice final
: public AbstractHapticGamepad {
public:
explicit WgiGamepadDevice(
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad> gamepad);
WgiGamepadDevice(const WgiGamepadDevice& other) = delete;
WgiGamepadDevice& operator=(const WgiGamepadDevice& other) = delete;
~WgiGamepadDevice() override;
// AbstractHapticGamepad implementation.
void SetVibration(double strong_magnitude, double weak_magnitude) override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad> GetGamepad() {
return gamepad_;
}
private:
Microsoft::WRL::ComPtr<ABI::Windows::Gaming::Input::IGamepad> gamepad_;
base::WeakPtrFactory<WgiGamepadDevice> weak_factory_{this};
};
} // namespace device
#endif // DEVICE_GAMEPAD_WGI_GAMEPAD_DEVICE_H_