xinput_data_fetcher_win.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "device/gamepad/xinput_data_fetcher_win.h"
  5. #include <stddef.h>
  6. #include <string.h>
  7. #include "base/bind.h"
  8. #include "base/callback_helpers.h"
  9. #include "base/strings/stringprintf.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "base/task/single_thread_task_runner.h"
  12. #include "base/trace_event/trace_event.h"
  13. #include "base/win/windows_version.h"
  14. namespace device {
  15. namespace {
  16. // See http://goo.gl/5VSJR. These are not available in all versions of the
  17. // header, but they can be returned from the driver, so we define our own
  18. // versions here.
  19. static const BYTE kDeviceSubTypeGamepad = 1;
  20. static const BYTE kDeviceSubTypeWheel = 2;
  21. static const BYTE kDeviceSubTypeArcadeStick = 3;
  22. static const BYTE kDeviceSubTypeFlightStick = 4;
  23. static const BYTE kDeviceSubTypeDancePad = 5;
  24. static const BYTE kDeviceSubTypeGuitar = 6;
  25. static const BYTE kDeviceSubTypeGuitarAlternate = 7;
  26. static const BYTE kDeviceSubTypeDrumKit = 8;
  27. static const BYTE kDeviceSubTypeGuitarBass = 11;
  28. static const BYTE kDeviceSubTypeArcadePad = 19;
  29. // XInput does not expose the state of the Guide (Xbox) button through the
  30. // XInputGetState method. To access this button, we need to query the gamepad
  31. // state with the undocumented XInputGetStateEx method.
  32. static const LPCSTR kXInputGetStateExOrdinal = (LPCSTR)100;
  33. // Bitmask for the Guide button in XInputGamepadEx.wButtons.
  34. static const int kXInputGamepadGuide = 0x0400;
  35. float NormalizeXInputAxis(SHORT value) {
  36. return ((value + 32768.f) / 32767.5f) - 1.f;
  37. }
  38. const wchar_t* GamepadSubTypeName(BYTE sub_type) {
  39. switch (sub_type) {
  40. case kDeviceSubTypeGamepad:
  41. return L"GAMEPAD";
  42. case kDeviceSubTypeWheel:
  43. return L"WHEEL";
  44. case kDeviceSubTypeArcadeStick:
  45. return L"ARCADE_STICK";
  46. case kDeviceSubTypeFlightStick:
  47. return L"FLIGHT_STICK";
  48. case kDeviceSubTypeDancePad:
  49. return L"DANCE_PAD";
  50. case kDeviceSubTypeGuitar:
  51. return L"GUITAR";
  52. case kDeviceSubTypeGuitarAlternate:
  53. return L"GUITAR_ALTERNATE";
  54. case kDeviceSubTypeDrumKit:
  55. return L"DRUM_KIT";
  56. case kDeviceSubTypeGuitarBass:
  57. return L"GUITAR_BASS";
  58. case kDeviceSubTypeArcadePad:
  59. return L"ARCADE_PAD";
  60. default:
  61. return L"<UNKNOWN>";
  62. }
  63. }
  64. const base::FilePath::CharType* XInputDllFileName() {
  65. // Xinput.h defines filename (XINPUT_DLL) on different Windows versions, but
  66. // Xinput.h specifies it in build time. Approach here uses the same values
  67. // and it is resolving dll filename based on Windows version it is running on.
  68. if (base::win::GetVersion() >= base::win::Version::WIN8) {
  69. // For Windows 8+, XINPUT_DLL is xinput1_4.dll.
  70. return FILE_PATH_LITERAL("xinput1_4.dll");
  71. }
  72. return FILE_PATH_LITERAL("xinput9_1_0.dll");
  73. }
  74. } // namespace
  75. XInputDataFetcherWin::XInputDataFetcherWin() : xinput_available_(false) {}
  76. XInputDataFetcherWin::~XInputDataFetcherWin() {
  77. for (auto& haptic_gamepad : haptics_) {
  78. if (haptic_gamepad)
  79. haptic_gamepad->Shutdown();
  80. }
  81. }
  82. GamepadSource XInputDataFetcherWin::source() {
  83. return Factory::static_source();
  84. }
  85. void XInputDataFetcherWin::OnAddedToProvider() {
  86. xinput_dll_ = base::ScopedNativeLibrary(base::FilePath(XInputDllFileName()));
  87. xinput_available_ = GetXInputDllFunctions();
  88. }
  89. void XInputDataFetcherWin::EnumerateDevices() {
  90. TRACE_EVENT0("GAMEPAD", "EnumerateDevices");
  91. if (xinput_available_) {
  92. for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
  93. // Check to see if the xinput device is connected
  94. XINPUT_CAPABILITIES caps;
  95. DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
  96. xinput_connected_[i] = (res == ERROR_SUCCESS);
  97. if (!xinput_connected_[i]) {
  98. if (haptics_[i])
  99. haptics_[i]->Shutdown();
  100. haptics_[i] = nullptr;
  101. continue;
  102. }
  103. PadState* state = GetPadState(i);
  104. if (!state)
  105. continue; // No slot available for this gamepad.
  106. Gamepad& pad = state->data;
  107. if (!state->is_initialized) {
  108. state->is_initialized = true;
  109. if (!haptics_[i]) {
  110. haptics_[i] =
  111. std::make_unique<XInputHapticGamepadWin>(i, xinput_set_state_);
  112. }
  113. // This is the first time we've seen this device, so do some one-time
  114. // initialization
  115. pad.connected = true;
  116. pad.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
  117. pad.vibration_actuator.not_null = true;
  118. pad.SetID(base::WideToUTF16(
  119. base::StringPrintf(L"Xbox 360 Controller (XInput STANDARD %ls)",
  120. GamepadSubTypeName(caps.SubType))));
  121. pad.mapping = GamepadMapping::kStandard;
  122. }
  123. }
  124. }
  125. }
  126. void XInputDataFetcherWin::GetGamepadData(bool devices_changed_hint) {
  127. TRACE_EVENT0("GAMEPAD", "GetGamepadData");
  128. if (!xinput_available_)
  129. return;
  130. // A note on XInput devices:
  131. // If we got notification that system devices have been updated, then
  132. // run GetCapabilities to update the connected status and the device
  133. // identifier. It can be slow to do to both GetCapabilities and
  134. // GetState on unconnected devices, so we want to avoid a 2-5ms pause
  135. // here by only doing this when the devices are updated (despite
  136. // documentation claiming it's OK to call it any time).
  137. if (devices_changed_hint)
  138. EnumerateDevices();
  139. for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
  140. if (xinput_connected_[i])
  141. GetXInputPadData(i);
  142. }
  143. }
  144. void XInputDataFetcherWin::GetXInputPadData(int i) {
  145. PadState* pad_state = GetPadState(i);
  146. if (!pad_state)
  147. return;
  148. Gamepad& pad = pad_state->data;
  149. // Use XInputGetStateEx if it is available, otherwise fall back to
  150. // XInputGetState. We can use the same struct for both since XInputStateEx
  151. // has identical layout to XINPUT_STATE except for an extra padding member at
  152. // the end.
  153. XInputStateEx state;
  154. memset(&state, 0, sizeof(XInputStateEx));
  155. TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i);
  156. DWORD dwResult;
  157. if (xinput_get_state_ex_)
  158. dwResult = xinput_get_state_ex_(i, &state);
  159. else
  160. dwResult = xinput_get_state_(i, reinterpret_cast<XINPUT_STATE*>(&state));
  161. TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i);
  162. if (dwResult == ERROR_SUCCESS) {
  163. pad.timestamp = CurrentTimeInMicroseconds();
  164. pad.buttons_length = 0;
  165. WORD val = state.Gamepad.wButtons;
  166. #define ADD(b) \
  167. pad.buttons[pad.buttons_length].pressed = (val & (b)) != 0; \
  168. pad.buttons[pad.buttons_length++].value = ((val & (b)) ? 1.f : 0.f);
  169. ADD(XINPUT_GAMEPAD_A);
  170. ADD(XINPUT_GAMEPAD_B);
  171. ADD(XINPUT_GAMEPAD_X);
  172. ADD(XINPUT_GAMEPAD_Y);
  173. ADD(XINPUT_GAMEPAD_LEFT_SHOULDER);
  174. ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER);
  175. pad.buttons[pad.buttons_length].pressed =
  176. state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
  177. pad.buttons[pad.buttons_length++].value =
  178. state.Gamepad.bLeftTrigger / 255.f;
  179. pad.buttons[pad.buttons_length].pressed =
  180. state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
  181. pad.buttons[pad.buttons_length++].value =
  182. state.Gamepad.bRightTrigger / 255.f;
  183. ADD(XINPUT_GAMEPAD_BACK);
  184. ADD(XINPUT_GAMEPAD_START);
  185. ADD(XINPUT_GAMEPAD_LEFT_THUMB);
  186. ADD(XINPUT_GAMEPAD_RIGHT_THUMB);
  187. ADD(XINPUT_GAMEPAD_DPAD_UP);
  188. ADD(XINPUT_GAMEPAD_DPAD_DOWN);
  189. ADD(XINPUT_GAMEPAD_DPAD_LEFT);
  190. ADD(XINPUT_GAMEPAD_DPAD_RIGHT);
  191. if (xinput_get_state_ex_) {
  192. // Only XInputGetStateEx reports the Guide button state.
  193. ADD(kXInputGamepadGuide);
  194. }
  195. #undef ADD
  196. pad.axes_length = 0;
  197. float value = 0.0;
  198. #define ADD(a, factor) \
  199. value = factor * NormalizeXInputAxis(a); \
  200. pad.axes[pad.axes_length++] = value;
  201. // XInput are +up/+right, -down/-left, we want -up/-left.
  202. ADD(state.Gamepad.sThumbLX, 1);
  203. ADD(state.Gamepad.sThumbLY, -1);
  204. ADD(state.Gamepad.sThumbRX, 1);
  205. ADD(state.Gamepad.sThumbRY, -1);
  206. #undef ADD
  207. }
  208. }
  209. void XInputDataFetcherWin::PlayEffect(
  210. int pad_id,
  211. mojom::GamepadHapticEffectType type,
  212. mojom::GamepadEffectParametersPtr params,
  213. mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,
  214. scoped_refptr<base::SequencedTaskRunner> callback_runner) {
  215. if (pad_id < 0 || pad_id >= XUSER_MAX_COUNT) {
  216. RunVibrationCallback(
  217. std::move(callback), std::move(callback_runner),
  218. mojom::GamepadHapticsResult::GamepadHapticsResultError);
  219. return;
  220. }
  221. if (!xinput_available_ || !xinput_connected_[pad_id] ||
  222. haptics_[pad_id] == nullptr) {
  223. RunVibrationCallback(
  224. std::move(callback), std::move(callback_runner),
  225. mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
  226. return;
  227. }
  228. haptics_[pad_id]->PlayEffect(type, std::move(params), std::move(callback),
  229. std::move(callback_runner));
  230. }
  231. void XInputDataFetcherWin::ResetVibration(
  232. int pad_id,
  233. mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback,
  234. scoped_refptr<base::SequencedTaskRunner> callback_runner) {
  235. if (pad_id < 0 || pad_id >= XUSER_MAX_COUNT) {
  236. RunVibrationCallback(
  237. std::move(callback), std::move(callback_runner),
  238. mojom::GamepadHapticsResult::GamepadHapticsResultError);
  239. return;
  240. }
  241. if (!xinput_available_ || !xinput_connected_[pad_id] ||
  242. haptics_[pad_id] == nullptr) {
  243. RunVibrationCallback(
  244. std::move(callback), std::move(callback_runner),
  245. mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
  246. return;
  247. }
  248. haptics_[pad_id]->ResetVibration(std::move(callback),
  249. std::move(callback_runner));
  250. }
  251. bool XInputDataFetcherWin::GetXInputDllFunctions() {
  252. xinput_get_capabilities_ = nullptr;
  253. xinput_get_state_ = nullptr;
  254. xinput_get_state_ex_ = nullptr;
  255. xinput_set_state_ = nullptr;
  256. XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>(
  257. xinput_dll_.GetFunctionPointer("XInputEnable"));
  258. xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
  259. xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
  260. if (!xinput_get_capabilities_)
  261. return false;
  262. // Get undocumented function XInputGetStateEx. If it is not present, fall back
  263. // to XInputGetState.
  264. xinput_get_state_ex_ = reinterpret_cast<XInputGetStateExFunc>(
  265. ::GetProcAddress(xinput_dll_.get(), kXInputGetStateExOrdinal));
  266. if (!xinput_get_state_ex_) {
  267. xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
  268. xinput_dll_.GetFunctionPointer("XInputGetState"));
  269. }
  270. if (!xinput_get_state_ && !xinput_get_state_ex_)
  271. return false;
  272. xinput_set_state_ =
  273. reinterpret_cast<XInputHapticGamepadWin::XInputSetStateFunc>(
  274. xinput_dll_.GetFunctionPointer("XInputSetState"));
  275. if (!xinput_set_state_)
  276. return false;
  277. if (xinput_enable) {
  278. // XInputEnable is unavailable before Win8 and deprecated in Win10.
  279. xinput_enable(true);
  280. }
  281. return true;
  282. }
  283. } // namespace device