Skip to content

Commit

Permalink
Add haptics for XInput gamepads on Windows
Browse files Browse the repository at this point in the history
BUG=749295

Change-Id: I033a299bbd6c405fdb9fedefd18f36c21d3b8043
Reviewed-on: https://chromium-review.googlesource.com/764428
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#528488}
  • Loading branch information
Matt Reynolds authored and Commit Bot committed Jan 10, 2018
1 parent fb4c31f commit cff883a
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 7 deletions.
2 changes: 2 additions & 0 deletions device/gamepad/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ component("gamepad") {
"xbox_controller_mac.mm",
"xbox_data_fetcher_mac.cc",
"xbox_data_fetcher_mac.h",
"xinput_haptic_gamepad_win.cc",
"xinput_haptic_gamepad_win.h",
]

deps = [
Expand Down
64 changes: 59 additions & 5 deletions device/gamepad/gamepad_platform_data_fetcher_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ const UChar* XInputDllFileName() {
GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin()
: xinput_available_(false) {}

GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() {
}
GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() = default;

GamepadSource GamepadPlatformDataFetcherWin::source() {
return Factory::static_source();
Expand All @@ -108,9 +107,11 @@ void GamepadPlatformDataFetcherWin::EnumerateDevices() {
// Check to see if the xinput device is connected
XINPUT_CAPABILITIES caps;
DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
xinuput_connected_[i] = (res == ERROR_SUCCESS);
if (!xinuput_connected_[i])
xinput_connected_[i] = (res == ERROR_SUCCESS);
if (!xinput_connected_[i]) {
haptics_[i] = nullptr;
continue;
}

PadState* state = GetPadState(i);
if (!state)
Expand All @@ -119,9 +120,16 @@ void GamepadPlatformDataFetcherWin::EnumerateDevices() {
Gamepad& pad = state->data;

if (state->active_state == GAMEPAD_NEWLY_ACTIVE) {
haptics_[i] =
std::make_unique<XInputHapticGamepadWin>(i, xinput_set_state_);

// This is the first time we've seen this device, so do some one-time
// initialization
pad.connected = true;

pad.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
pad.vibration_actuator.not_null = true;

swprintf(pad.id, Gamepad::kIdLengthCap,
L"Xbox 360 Controller (XInput STANDARD %ls)",
GamepadSubTypeName(caps.SubType));
Expand All @@ -148,7 +156,7 @@ void GamepadPlatformDataFetcherWin::GetGamepadData(bool devices_changed_hint) {
EnumerateDevices();

for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
if (xinuput_connected_[i])
if (xinput_connected_[i])
GetXInputPadData(i);
}
}
Expand Down Expand Up @@ -227,10 +235,51 @@ void GamepadPlatformDataFetcherWin::GetXInputPadData(int i) {
}
}

void GamepadPlatformDataFetcherWin::PlayEffect(
int pad_id,
mojom::GamepadHapticEffectType type,
mojom::GamepadEffectParametersPtr params,
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) {
if (pad_id < 0 || pad_id >= XUSER_MAX_COUNT) {
std::move(callback).Run(
mojom::GamepadHapticsResult::GamepadHapticsResultError);
return;
}

if (!xinput_available_ || !xinput_connected_[pad_id] ||
haptics_[pad_id] == nullptr) {
std::move(callback).Run(
mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
return;
}

haptics_[pad_id]->PlayEffect(type, std::move(params), std::move(callback));
}

void GamepadPlatformDataFetcherWin::ResetVibration(
int pad_id,
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) {
if (pad_id < 0 || pad_id >= XUSER_MAX_COUNT) {
std::move(callback).Run(
mojom::GamepadHapticsResult::GamepadHapticsResultError);
return;
}

if (!xinput_available_ || !xinput_connected_[pad_id] ||
haptics_[pad_id] == nullptr) {
std::move(callback).Run(
mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
return;
}

haptics_[pad_id]->ResetVibration(std::move(callback));
}

bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
xinput_get_capabilities_ = nullptr;
xinput_get_state_ = nullptr;
xinput_get_state_ex_ = nullptr;
xinput_set_state_ = nullptr;
XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>(
xinput_dll_.GetFunctionPointer("XInputEnable"));
xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
Expand All @@ -249,6 +298,11 @@ bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {

if (!xinput_get_state_ && !xinput_get_state_ex_)
return false;
xinput_set_state_ =
reinterpret_cast<XInputHapticGamepadWin::XInputSetStateFunc>(
xinput_dll_.GetFunctionPointer("XInputSetState"));
if (!xinput_set_state_)
return false;
if (xinput_enable) {
// XInputEnable is unavailable before Win8 and deprecated in Win10.
xinput_enable(true);
Expand Down
18 changes: 16 additions & 2 deletions device/gamepad/gamepad_platform_data_fetcher_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/gamepad_standard_mappings.h"
#include "device/gamepad/public/cpp/gamepads.h"
#include "device/gamepad/xinput_haptic_gamepad_win.h"

namespace device {

Expand Down Expand Up @@ -57,8 +58,19 @@ class GamepadPlatformDataFetcherWin : public GamepadDataFetcher {

GamepadSource source() override;

// GamepadDataFetcher implementation.
void GetGamepadData(bool devices_changed_hint) override;

void PlayEffect(
int pad_index,
mojom::GamepadHapticEffectType,
mojom::GamepadEffectParametersPtr,
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback) override;

void ResetVibration(
int pad_index,
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback) override;

private:
void OnAddedToProvider() override;

Expand All @@ -85,12 +97,14 @@ class GamepadPlatformDataFetcherWin : public GamepadDataFetcher {
bool xinput_available_;

// Function pointers to XInput functionality, retrieved in
// |GetXinputDllFunctions|.
// |GetXInputDllFunctions|.
XInputGetCapabilitiesFunc xinput_get_capabilities_;
XInputGetStateFunc xinput_get_state_;
XInputGetStateExFunc xinput_get_state_ex_;
XInputHapticGamepadWin::XInputSetStateFunc xinput_set_state_;

bool xinuput_connected_[XUSER_MAX_COUNT];
bool xinput_connected_[XUSER_MAX_COUNT];
std::unique_ptr<XInputHapticGamepadWin> haptics_[XUSER_MAX_COUNT];

DISALLOW_COPY_AND_ASSIGN(GamepadPlatformDataFetcherWin);
};
Expand Down
35 changes: 35 additions & 0 deletions device/gamepad/xinput_haptic_gamepad_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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 "device/gamepad/xinput_haptic_gamepad_win.h"

namespace {
const long kRumbleMagnitudeMax = 0xffff;
} // namespace

namespace device {

XInputHapticGamepadWin::XInputHapticGamepadWin(
int pad_id,
XInputSetStateFunc xinput_set_state)
: pad_id_(pad_id), xinput_set_state_(xinput_set_state) {}

XInputHapticGamepadWin::~XInputHapticGamepadWin() = default;

void XInputHapticGamepadWin::SetVibration(double strong_magnitude,
double weak_magnitude) {
if (pad_id_ < 0 || pad_id_ > XUSER_MAX_COUNT || xinput_set_state_ == nullptr)
return;
XINPUT_VIBRATION vibration;
vibration.wLeftMotorSpeed =
static_cast<long>(strong_magnitude * kRumbleMagnitudeMax);
vibration.wRightMotorSpeed =
static_cast<long>(weak_magnitude * kRumbleMagnitudeMax);

TRACE_EVENT_BEGIN1("GAMEPAD", "XInputSetState", "id", pad_id_);
xinput_set_state_(pad_id_, &vibration);
TRACE_EVENT_END1("GAMEPAD", "XInputSetState", "id", pad_id_);
}

} // namespace device
32 changes: 32 additions & 0 deletions device/gamepad/xinput_haptic_gamepad_win.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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.

#ifndef DEVICE_GAMEPAD_XINPUT_HAPTIC_GAMEPAD_WIN_
#define DEVICE_GAMEPAD_XINPUT_HAPTIC_GAMEPAD_WIN_

#include <Unknwn.h>
#include <XInput.h>

#include "device/gamepad/abstract_haptic_gamepad.h"

namespace device {

class XInputHapticGamepadWin : public AbstractHapticGamepad {
public:
typedef DWORD(WINAPI* XInputSetStateFunc)(DWORD dwUserIndex,
XINPUT_VIBRATION* pVibration);

XInputHapticGamepadWin(int pad_id, XInputSetStateFunc xinput_set_state);
~XInputHapticGamepadWin() override;

void SetVibration(double strong_magnitude, double weak_magnitude) override;

private:
int pad_id_;
XInputSetStateFunc xinput_set_state_;
};

} // namespace device

#endif // DEVICE_GAMEPAD_EVDEV_HAPTIC_GAMEPAD_WIN_

0 comments on commit cff883a

Please sign in to comment.