123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- // Copyright (c) 2012 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_data_fetcher_win.h"
- #include <stddef.h>
- #include <string.h>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/strings/stringprintf.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/trace_event/trace_event.h"
- #include "base/win/windows_version.h"
- namespace device {
- namespace {
- // See http://goo.gl/5VSJR. These are not available in all versions of the
- // header, but they can be returned from the driver, so we define our own
- // versions here.
- static const BYTE kDeviceSubTypeGamepad = 1;
- static const BYTE kDeviceSubTypeWheel = 2;
- static const BYTE kDeviceSubTypeArcadeStick = 3;
- static const BYTE kDeviceSubTypeFlightStick = 4;
- static const BYTE kDeviceSubTypeDancePad = 5;
- static const BYTE kDeviceSubTypeGuitar = 6;
- static const BYTE kDeviceSubTypeGuitarAlternate = 7;
- static const BYTE kDeviceSubTypeDrumKit = 8;
- static const BYTE kDeviceSubTypeGuitarBass = 11;
- static const BYTE kDeviceSubTypeArcadePad = 19;
- // XInput does not expose the state of the Guide (Xbox) button through the
- // XInputGetState method. To access this button, we need to query the gamepad
- // state with the undocumented XInputGetStateEx method.
- static const LPCSTR kXInputGetStateExOrdinal = (LPCSTR)100;
- // Bitmask for the Guide button in XInputGamepadEx.wButtons.
- static const int kXInputGamepadGuide = 0x0400;
- float NormalizeXInputAxis(SHORT value) {
- return ((value + 32768.f) / 32767.5f) - 1.f;
- }
- const wchar_t* GamepadSubTypeName(BYTE sub_type) {
- switch (sub_type) {
- case kDeviceSubTypeGamepad:
- return L"GAMEPAD";
- case kDeviceSubTypeWheel:
- return L"WHEEL";
- case kDeviceSubTypeArcadeStick:
- return L"ARCADE_STICK";
- case kDeviceSubTypeFlightStick:
- return L"FLIGHT_STICK";
- case kDeviceSubTypeDancePad:
- return L"DANCE_PAD";
- case kDeviceSubTypeGuitar:
- return L"GUITAR";
- case kDeviceSubTypeGuitarAlternate:
- return L"GUITAR_ALTERNATE";
- case kDeviceSubTypeDrumKit:
- return L"DRUM_KIT";
- case kDeviceSubTypeGuitarBass:
- return L"GUITAR_BASS";
- case kDeviceSubTypeArcadePad:
- return L"ARCADE_PAD";
- default:
- return L"<UNKNOWN>";
- }
- }
- const base::FilePath::CharType* XInputDllFileName() {
- // Xinput.h defines filename (XINPUT_DLL) on different Windows versions, but
- // Xinput.h specifies it in build time. Approach here uses the same values
- // and it is resolving dll filename based on Windows version it is running on.
- if (base::win::GetVersion() >= base::win::Version::WIN8) {
- // For Windows 8+, XINPUT_DLL is xinput1_4.dll.
- return FILE_PATH_LITERAL("xinput1_4.dll");
- }
- return FILE_PATH_LITERAL("xinput9_1_0.dll");
- }
- } // namespace
- XInputDataFetcherWin::XInputDataFetcherWin() : xinput_available_(false) {}
- XInputDataFetcherWin::~XInputDataFetcherWin() {
- for (auto& haptic_gamepad : haptics_) {
- if (haptic_gamepad)
- haptic_gamepad->Shutdown();
- }
- }
- GamepadSource XInputDataFetcherWin::source() {
- return Factory::static_source();
- }
- void XInputDataFetcherWin::OnAddedToProvider() {
- xinput_dll_ = base::ScopedNativeLibrary(base::FilePath(XInputDllFileName()));
- xinput_available_ = GetXInputDllFunctions();
- }
- void XInputDataFetcherWin::EnumerateDevices() {
- TRACE_EVENT0("GAMEPAD", "EnumerateDevices");
- if (xinput_available_) {
- for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
- // Check to see if the xinput device is connected
- XINPUT_CAPABILITIES caps;
- DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
- xinput_connected_[i] = (res == ERROR_SUCCESS);
- if (!xinput_connected_[i]) {
- if (haptics_[i])
- haptics_[i]->Shutdown();
- haptics_[i] = nullptr;
- continue;
- }
- PadState* state = GetPadState(i);
- if (!state)
- continue; // No slot available for this gamepad.
- Gamepad& pad = state->data;
- if (!state->is_initialized) {
- state->is_initialized = true;
- if (!haptics_[i]) {
- 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;
- pad.SetID(base::WideToUTF16(
- base::StringPrintf(L"Xbox 360 Controller (XInput STANDARD %ls)",
- GamepadSubTypeName(caps.SubType))));
- pad.mapping = GamepadMapping::kStandard;
- }
- }
- }
- }
- void XInputDataFetcherWin::GetGamepadData(bool devices_changed_hint) {
- TRACE_EVENT0("GAMEPAD", "GetGamepadData");
- if (!xinput_available_)
- return;
- // A note on XInput devices:
- // If we got notification that system devices have been updated, then
- // run GetCapabilities to update the connected status and the device
- // identifier. It can be slow to do to both GetCapabilities and
- // GetState on unconnected devices, so we want to avoid a 2-5ms pause
- // here by only doing this when the devices are updated (despite
- // documentation claiming it's OK to call it any time).
- if (devices_changed_hint)
- EnumerateDevices();
- for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
- if (xinput_connected_[i])
- GetXInputPadData(i);
- }
- }
- void XInputDataFetcherWin::GetXInputPadData(int i) {
- PadState* pad_state = GetPadState(i);
- if (!pad_state)
- return;
- Gamepad& pad = pad_state->data;
- // Use XInputGetStateEx if it is available, otherwise fall back to
- // XInputGetState. We can use the same struct for both since XInputStateEx
- // has identical layout to XINPUT_STATE except for an extra padding member at
- // the end.
- XInputStateEx state;
- memset(&state, 0, sizeof(XInputStateEx));
- TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i);
- DWORD dwResult;
- if (xinput_get_state_ex_)
- dwResult = xinput_get_state_ex_(i, &state);
- else
- dwResult = xinput_get_state_(i, reinterpret_cast<XINPUT_STATE*>(&state));
- TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i);
- if (dwResult == ERROR_SUCCESS) {
- pad.timestamp = CurrentTimeInMicroseconds();
- pad.buttons_length = 0;
- WORD val = state.Gamepad.wButtons;
- #define ADD(b) \
- pad.buttons[pad.buttons_length].pressed = (val & (b)) != 0; \
- pad.buttons[pad.buttons_length++].value = ((val & (b)) ? 1.f : 0.f);
- ADD(XINPUT_GAMEPAD_A);
- ADD(XINPUT_GAMEPAD_B);
- ADD(XINPUT_GAMEPAD_X);
- ADD(XINPUT_GAMEPAD_Y);
- ADD(XINPUT_GAMEPAD_LEFT_SHOULDER);
- ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER);
- pad.buttons[pad.buttons_length].pressed =
- state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
- pad.buttons[pad.buttons_length++].value =
- state.Gamepad.bLeftTrigger / 255.f;
- pad.buttons[pad.buttons_length].pressed =
- state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
- pad.buttons[pad.buttons_length++].value =
- state.Gamepad.bRightTrigger / 255.f;
- ADD(XINPUT_GAMEPAD_BACK);
- ADD(XINPUT_GAMEPAD_START);
- ADD(XINPUT_GAMEPAD_LEFT_THUMB);
- ADD(XINPUT_GAMEPAD_RIGHT_THUMB);
- ADD(XINPUT_GAMEPAD_DPAD_UP);
- ADD(XINPUT_GAMEPAD_DPAD_DOWN);
- ADD(XINPUT_GAMEPAD_DPAD_LEFT);
- ADD(XINPUT_GAMEPAD_DPAD_RIGHT);
- if (xinput_get_state_ex_) {
- // Only XInputGetStateEx reports the Guide button state.
- ADD(kXInputGamepadGuide);
- }
- #undef ADD
- pad.axes_length = 0;
- float value = 0.0;
- #define ADD(a, factor) \
- value = factor * NormalizeXInputAxis(a); \
- pad.axes[pad.axes_length++] = value;
- // XInput are +up/+right, -down/-left, we want -up/-left.
- ADD(state.Gamepad.sThumbLX, 1);
- ADD(state.Gamepad.sThumbLY, -1);
- ADD(state.Gamepad.sThumbRX, 1);
- ADD(state.Gamepad.sThumbRY, -1);
- #undef ADD
- }
- }
- void XInputDataFetcherWin::PlayEffect(
- int pad_id,
- mojom::GamepadHapticEffectType type,
- mojom::GamepadEffectParametersPtr params,
- mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,
- scoped_refptr<base::SequencedTaskRunner> callback_runner) {
- if (pad_id < 0 || pad_id >= XUSER_MAX_COUNT) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultError);
- return;
- }
- if (!xinput_available_ || !xinput_connected_[pad_id] ||
- haptics_[pad_id] == nullptr) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
- return;
- }
- haptics_[pad_id]->PlayEffect(type, std::move(params), std::move(callback),
- std::move(callback_runner));
- }
- void XInputDataFetcherWin::ResetVibration(
- int pad_id,
- mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback,
- scoped_refptr<base::SequencedTaskRunner> callback_runner) {
- if (pad_id < 0 || pad_id >= XUSER_MAX_COUNT) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultError);
- return;
- }
- if (!xinput_available_ || !xinput_connected_[pad_id] ||
- haptics_[pad_id] == nullptr) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
- return;
- }
- haptics_[pad_id]->ResetVibration(std::move(callback),
- std::move(callback_runner));
- }
- bool XInputDataFetcherWin::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>(
- xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
- if (!xinput_get_capabilities_)
- return false;
- // Get undocumented function XInputGetStateEx. If it is not present, fall back
- // to XInputGetState.
- xinput_get_state_ex_ = reinterpret_cast<XInputGetStateExFunc>(
- ::GetProcAddress(xinput_dll_.get(), kXInputGetStateExOrdinal));
- if (!xinput_get_state_ex_) {
- xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
- xinput_dll_.GetFunctionPointer("XInputGetState"));
- }
- 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);
- }
- return true;
- }
- } // namespace device
|