xInputDevice.cxx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file xInputDevice.cxx
  10. * @author rdb
  11. * @date 2015-07-15
  12. */
  13. #include "xInputDevice.h"
  14. #if defined(_WIN32) && !defined(CPPPARSER)
  15. #include "gamepadButton.h"
  16. #include "inputDeviceManager.h"
  17. #include "string_utils.h"
  18. #include <XInput.h>
  19. #include <CfgMgr32.h>
  20. #ifndef XUSER_MAX_COUNT
  21. #define XUSER_MAX_COUNT 4
  22. #endif
  23. #ifndef XINPUT_CAPS_FFB_SUPPORTED
  24. #define XINPUT_CAPS_FFB_SUPPORTED 0x0001
  25. #endif
  26. #ifndef XINPUT_CAPS_NO_NAVIGATION
  27. #define XINPUT_CAPS_NO_NAVIGATION 0x0010
  28. #endif
  29. #ifndef BATTERY_DEVTYPE_GAMEPAD
  30. #define BATTERY_DEVTYPE_GAMEPAD 0x00
  31. #endif
  32. #ifndef XINPUT_DEVSUBTYPE_WHEEL
  33. #define XINPUT_DEVSUBTYPE_WHEEL 0x02
  34. #endif
  35. #ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK
  36. #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
  37. #endif
  38. #ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
  39. #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
  40. #endif
  41. #ifndef XINPUT_DEVSUBTYPE_DANCE_PAD
  42. #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
  43. #endif
  44. #ifndef XINPUT_DEVSUBTYPE_GUITAR
  45. #define XINPUT_DEVSUBTYPE_GUITAR 0x06
  46. #endif
  47. #ifndef XINPUT_DEVSUBTYPE_DRUM_KIT
  48. #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
  49. #endif
  50. #ifndef BATTERY_TYPE_DISCONNECTED
  51. #define BATTERY_TYPE_DISCONNECTED 0x00
  52. #endif
  53. #ifndef BATTERY_TYPE_WIRED
  54. #define BATTERY_TYPE_WIRED 0x01
  55. #endif
  56. #ifndef BATTERY_LEVEL_FULL
  57. #define BATTERY_LEVEL_FULL 0x03
  58. #endif
  59. typedef struct _XINPUT_BATTERY_INFORMATION {
  60. BYTE BatteryType;
  61. BYTE BatteryLevel;
  62. } XINPUT_BATTERY_INFORMATION;
  63. // Undocumented, I figured out how this looks by trial and error.
  64. typedef struct _XINPUT_BUSINFO {
  65. WORD VendorID;
  66. WORD ProductID;
  67. WORD RevisionID;
  68. WORD Unknown1; // Unknown - padding?
  69. DWORD InstanceID;
  70. DWORD Unknown2;
  71. WORD Unknown3;
  72. } XINPUT_BUSINFO;
  73. typedef struct _XINPUT_CAPABILITIES_EX {
  74. BYTE Type;
  75. BYTE SubType;
  76. WORD Flags;
  77. XINPUT_GAMEPAD Gamepad;
  78. XINPUT_VIBRATION Vibration;
  79. // The following fields are undocumented.
  80. WORD VendorID;
  81. WORD ProductID;
  82. WORD RevisionID;
  83. WORD Unknown1;
  84. WORD Unknown2;
  85. } XINPUT_CAPABILITIES_EX;
  86. typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *);
  87. typedef DWORD (*pXInputSetState)(DWORD, XINPUT_VIBRATION *);
  88. typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
  89. typedef DWORD (*pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *);
  90. typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
  91. typedef DWORD (*pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *);
  92. static pXInputGetState get_state = nullptr;
  93. static pXInputSetState set_state = nullptr;
  94. static pXInputGetCapabilities get_capabilities = nullptr;
  95. static pXInputGetCapabilitiesEx get_capabilities_ex = nullptr;
  96. static pXInputGetBatteryInformation get_battery_information = nullptr;
  97. static pXInputGetBaseBusInformation get_base_bus_information = nullptr;
  98. bool XInputDevice::_initialized = false;
  99. /**
  100. * Protected constructor. user_index is a number 0-3.
  101. */
  102. XInputDevice::
  103. XInputDevice(DWORD user_index) :
  104. _index(user_index),
  105. _last_packet(-1),
  106. _last_buttons(0) {
  107. nassertv(user_index >= 0 && user_index < XUSER_MAX_COUNT);
  108. _axes.resize(6);
  109. _buttons.resize(16);
  110. }
  111. /**
  112. *
  113. */
  114. XInputDevice::
  115. ~XInputDevice() {
  116. do_set_vibration(0, 0);
  117. }
  118. /**
  119. * Called when a new input device arrives in the InputDeviceManager. This
  120. * method checks whether it matches this XInput device.
  121. */
  122. bool XInputDevice::
  123. check_arrival(const RID_DEVICE_INFO &info, DEVINST inst,
  124. const std::string &name, const std::string &manufacturer) {
  125. LightMutexHolder holder(_lock);
  126. if (_is_connected) {
  127. return false;
  128. }
  129. if (!_initialized) {
  130. nassertr_always(init_xinput(), false);
  131. }
  132. XINPUT_CAPABILITIES_EX caps = {0};
  133. XINPUT_STATE state;
  134. if ((get_capabilities_ex && get_capabilities_ex(1, _index, 0, &caps) != ERROR_SUCCESS) &&
  135. get_capabilities(_index, 0, (XINPUT_CAPABILITIES *)&caps) != ERROR_SUCCESS) {
  136. return false;
  137. }
  138. if (get_state(_index, &state) != ERROR_SUCCESS) {
  139. return false;
  140. }
  141. // Extra check for VID/PID if we have it, just to be sure.
  142. if ((caps.VendorID != 0 && caps.VendorID != info.hid.dwVendorId) ||
  143. (caps.ProductID != 0 && caps.ProductID != info.hid.dwProductId)) {
  144. return false;
  145. }
  146. // Yes, take the name and manufacturer.
  147. if (!name.empty()) {
  148. _name = name;
  149. } else {
  150. _name = "XInput Device #";
  151. _name += format_string(_index + 1);
  152. }
  153. _manufacturer = manufacturer;
  154. if (inst && caps.ProductID == 0 && caps.RevisionID != 0) {
  155. // XInput does not report a product ID for the Xbox 360 wireless adapter.
  156. // Instead, we check that the RevisionID matches.
  157. char buffer[4096];
  158. ULONG buflen = sizeof(buffer);
  159. if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_HARDWAREID, 0, buffer, &buflen, 0) == CR_SUCCESS) {
  160. std::string ids(buffer, buflen);
  161. char revstr[16];
  162. sprintf(revstr, "REV_%04x", caps.RevisionID);
  163. if (ids.find(revstr) == std::string::npos) {
  164. return false;
  165. }
  166. }
  167. }
  168. _is_connected = true;
  169. init_device(caps, state);
  170. _vendor_id = info.hid.dwVendorId;
  171. _product_id = info.hid.dwProductId;
  172. return true;
  173. }
  174. /**
  175. * Called periodically by the InputDeviceManager to detect whether the device
  176. * is currently connected.
  177. * Returns true if the device wasn't connected, but now is.
  178. */
  179. void XInputDevice::
  180. detect(InputDeviceManager *mgr) {
  181. if (!_initialized) {
  182. nassertv_always(init_xinput());
  183. }
  184. bool connected = false;
  185. XINPUT_CAPABILITIES_EX caps = {0};
  186. XINPUT_STATE state;
  187. if (((get_capabilities_ex && get_capabilities_ex(1, _index, 0, &caps) == ERROR_SUCCESS) ||
  188. get_capabilities(_index, 0, (XINPUT_CAPABILITIES *)&caps) == ERROR_SUCCESS) &&
  189. get_state(_index, &state) == ERROR_SUCCESS) {
  190. connected = true;
  191. } else {
  192. connected = false;
  193. }
  194. LightMutexHolder holder(_lock);
  195. if (connected == _is_connected) {
  196. // Nothing changed.
  197. return;
  198. }
  199. _is_connected = connected;
  200. if (connected) {
  201. _name = "XInput Device #";
  202. _name += format_string(_index + 1);
  203. _vendor_id = caps.VendorID;
  204. _product_id = caps.ProductID;
  205. init_device(caps, state);
  206. mgr->add_device(this);
  207. } else {
  208. mgr->remove_device(this);
  209. }
  210. }
  211. /**
  212. * Static method to initialize the XInput library.
  213. */
  214. bool XInputDevice::
  215. init_xinput() {
  216. if (device_cat.is_debug()) {
  217. device_cat.debug() << "Initializing XInput library.\n";
  218. }
  219. _initialized = true;
  220. const char *dll_name = "Xinput1_4.dll";
  221. HMODULE module = LoadLibraryA(dll_name);
  222. // If we didn't find XInput 1.4, fall back to the older 1.3 version.
  223. if (!module) {
  224. if (device_cat.is_debug()) {
  225. device_cat.debug()
  226. << "Xinput1_4.dll not found, falling back to Xinput1_3.dll\n";
  227. }
  228. dll_name = "Xinput1_3.dll";
  229. module = LoadLibraryA(dll_name);
  230. }
  231. if (module) {
  232. if (device_cat.is_debug()) {
  233. device_cat.debug()
  234. << "Successfully loaded " << dll_name << "\n";
  235. }
  236. // Undocumented version (XInputGetStateEx) that includes a
  237. // state bit for the guide button.
  238. get_state = (pXInputGetState)GetProcAddress(module, MAKEINTRESOURCE(100));
  239. if (get_state == nullptr) {
  240. get_state = (pXInputGetState)GetProcAddress(module, "XInputGetState");
  241. if (get_state == nullptr) {
  242. device_cat.error()
  243. << "Failed to find function XInputGetState in " << dll_name << ".\n";
  244. return false;
  245. }
  246. }
  247. set_state = (pXInputSetState)GetProcAddress(module, "XInputSetState");
  248. if (set_state == nullptr) {
  249. device_cat.error()
  250. << "Failed to find function XInputSetState in " << dll_name << ".\n";
  251. return false;
  252. }
  253. get_capabilities = (pXInputGetCapabilities)GetProcAddress(module, "XInputGetCapabilities");
  254. if (get_capabilities == nullptr) {
  255. device_cat.error()
  256. << "Failed to find function XInputGetCapabilities in " << dll_name << ".\n";
  257. return false;
  258. }
  259. get_battery_information = (pXInputGetBatteryInformation)GetProcAddress(module, "XInputGetBatteryInformation");
  260. get_base_bus_information = (pXInputGetBaseBusInformation)GetProcAddress(module, MAKEINTRESOURCE(104));
  261. get_capabilities_ex = (pXInputGetCapabilitiesEx)GetProcAddress(module, MAKEINTRESOURCE(108));
  262. return true;
  263. }
  264. device_cat.error()
  265. << "Failed to load Xinput1_4.dll or Xinput1_3.dll.\n";
  266. return false;
  267. }
  268. /**
  269. * Initializes the device. Called when the device was just connected.
  270. * Assumes either the lock is held or this is called from the constructor.
  271. */
  272. void XInputDevice::
  273. init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) {
  274. nassertv(_initialized);
  275. // It seems that the Xbox One controller is reported as having a DevType of
  276. // zero, at least when I tested in with XInput 1.3 on Windows 7.
  277. //if (caps.Type == XINPUT_DEVTYPE_GAMEPAD) {
  278. // For subtypes and mappings, see this page:
  279. // https://msdn.microsoft.com/en-us/library/windows/desktop/hh405050.aspx
  280. switch (caps.SubType) {
  281. default:
  282. case XINPUT_DEVSUBTYPE_GAMEPAD:
  283. _device_class = DeviceClass::gamepad;
  284. _axes[0].axis = Axis::left_trigger;
  285. _axes[1].axis = Axis::right_trigger;
  286. _axes[2].axis = Axis::left_x;
  287. _axes[3].axis = Axis::left_y;
  288. _axes[4].axis = Axis::right_x;
  289. _axes[5].axis = Axis::right_y;
  290. break;
  291. case XINPUT_DEVSUBTYPE_WHEEL:
  292. _device_class = DeviceClass::steering_wheel;
  293. _axes[0].axis = Axis::brake;
  294. _axes[1].axis = Axis::accelerator;
  295. _axes[2].axis = Axis::wheel;
  296. _axes[3].axis = Axis::none;
  297. _axes[4].axis = Axis::none;
  298. _axes[5].axis = Axis::none;
  299. break;
  300. case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
  301. _device_class = DeviceClass::flight_stick;
  302. _axes[0].axis = Axis::yaw;
  303. _axes[1].axis = Axis::throttle;
  304. _axes[2].axis = Axis::roll;
  305. _axes[3].axis = Axis::pitch;
  306. _axes[4].axis = Axis::none;
  307. _axes[5].axis = Axis::none;
  308. break;
  309. case XINPUT_DEVSUBTYPE_DANCE_PAD:
  310. _device_class = DeviceClass::dance_pad;
  311. _axes[0].axis = Axis::none;
  312. _axes[1].axis = Axis::none;
  313. _axes[2].axis = Axis::none;
  314. _axes[3].axis = Axis::none;
  315. _axes[4].axis = Axis::none;
  316. _axes[5].axis = Axis::none;
  317. break;
  318. }
  319. _axes[0]._scale = 1.0 / 255.0;
  320. _axes[1]._scale = 1.0 / 255.0;
  321. _axes[2]._scale = 1.0 / 32767.5;
  322. _axes[3]._scale = 1.0 / 32767.5;
  323. _axes[4]._scale = 1.0 / 32767.5;
  324. _axes[5]._scale = 1.0 / 32767.5;
  325. _axes[2]._bias = 0.5 / 32767.5;
  326. _axes[3]._bias = 0.5 / 32767.5;
  327. _axes[4]._bias = 0.5 / 32767.5;
  328. _axes[5]._bias = 0.5 / 32767.5;
  329. if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) {
  330. _buttons[0].handle = ButtonHandle::none();
  331. _buttons[1].handle = ButtonHandle::none();
  332. _buttons[2].handle = ButtonHandle::none();
  333. _buttons[3].handle = ButtonHandle::none();
  334. _buttons[4].handle = ButtonHandle::none();
  335. _buttons[5].handle = ButtonHandle::none();
  336. } else {
  337. _buttons[0].handle = GamepadButton::dpad_up();
  338. _buttons[1].handle = GamepadButton::dpad_down();
  339. _buttons[2].handle = GamepadButton::dpad_left();
  340. _buttons[3].handle = GamepadButton::dpad_right();
  341. _buttons[4].handle = GamepadButton::start();
  342. _buttons[5].handle = GamepadButton::back();
  343. }
  344. _buttons[6].handle = GamepadButton::lstick();
  345. _buttons[7].handle = GamepadButton::rstick();
  346. _buttons[8].handle = GamepadButton::lshoulder();
  347. _buttons[9].handle = GamepadButton::rshoulder();
  348. _buttons[10].handle = GamepadButton::guide();
  349. _buttons[11].handle = GamepadButton::face_a();
  350. _buttons[12].handle = GamepadButton::face_b();
  351. _buttons[13].handle = GamepadButton::face_x();
  352. _buttons[14].handle = GamepadButton::face_y();
  353. if (caps.Vibration.wLeftMotorSpeed != 0 ||
  354. caps.Vibration.wRightMotorSpeed != 0) {
  355. enable_feature(Feature::vibration);
  356. }
  357. if (get_battery_information != nullptr) {
  358. XINPUT_BATTERY_INFORMATION batt;
  359. if (get_battery_information(_index, BATTERY_DEVTYPE_GAMEPAD, &batt) == ERROR_SUCCESS) {
  360. if (batt.BatteryType != BATTERY_TYPE_DISCONNECTED &&
  361. batt.BatteryType != BATTERY_TYPE_WIRED) {
  362. // This device has a battery. Report the battery level.
  363. enable_feature(Feature::battery);
  364. _battery_data.level = batt.BatteryLevel;
  365. _battery_data.max_level = BATTERY_LEVEL_FULL;
  366. }
  367. }
  368. }
  369. WORD buttons = state.Gamepad.wButtons;
  370. WORD mask = 1;
  371. for (int i = 0; i < 16; ++i) {
  372. // Set the state without triggering a button event.
  373. _buttons[i]._state = (buttons & mask) ? S_down : S_up;
  374. mask <<= 1;
  375. if (i == 10) {
  376. // XInput skips 0x0800.
  377. mask <<= 1;
  378. }
  379. }
  380. axis_changed(0, state.Gamepad.bLeftTrigger);
  381. axis_changed(1, state.Gamepad.bRightTrigger);
  382. axis_changed(2, state.Gamepad.sThumbLX);
  383. axis_changed(3, state.Gamepad.sThumbLY);
  384. axis_changed(4, state.Gamepad.sThumbRX);
  385. axis_changed(5, state.Gamepad.sThumbRY);
  386. _last_buttons = buttons;
  387. _last_packet = state.dwPacketNumber;
  388. }
  389. /**
  390. * Sets the vibration strength. The first argument controls a low-frequency
  391. * motor, if present, and the latter controls a high-frequency motor.
  392. * The values are within the 0-1 range.
  393. */
  394. void XInputDevice::
  395. do_set_vibration(double strong, double weak) {
  396. nassertv_always(_is_connected);
  397. XINPUT_VIBRATION vibration;
  398. vibration.wLeftMotorSpeed = strong * 0xffff;
  399. vibration.wRightMotorSpeed = weak * 0xffff;
  400. set_state(_index, &vibration);
  401. }
  402. /**
  403. * Polls the input device for new activity, to ensure it contains the latest
  404. * events. This will only have any effect for some types of input devices;
  405. * others may be updated automatically, and this method will be a no-op.
  406. */
  407. void XInputDevice::
  408. do_poll() {
  409. // Not sure why someone would call this on a disconnected device.
  410. if (!_is_connected) {
  411. return;
  412. }
  413. XINPUT_STATE state;
  414. if (get_state(_index, &state) != ERROR_SUCCESS) {
  415. // Device was disconnected.
  416. if (_is_connected) {
  417. _is_connected = false;
  418. InputDeviceManager *mgr = InputDeviceManager::get_global_ptr();
  419. mgr->remove_device(this);
  420. }
  421. return;
  422. }
  423. if (state.dwPacketNumber == _last_packet) {
  424. // No change since last time we asked.
  425. return;
  426. }
  427. // Did any buttons change state?
  428. WORD changed_buttons = _last_buttons ^ state.Gamepad.wButtons;
  429. WORD mask = 1;
  430. for (int i = 0; i < 16; ++i) {
  431. if (changed_buttons & mask) {
  432. button_changed(i, (state.Gamepad.wButtons & mask) != 0);
  433. }
  434. mask <<= 1;
  435. if (i == 10) {
  436. // XInput skips 0x0800.
  437. mask <<= 1;
  438. }
  439. }
  440. axis_changed(0, state.Gamepad.bLeftTrigger);
  441. axis_changed(1, state.Gamepad.bRightTrigger);
  442. axis_changed(2, state.Gamepad.sThumbLX);
  443. axis_changed(3, state.Gamepad.sThumbLY);
  444. axis_changed(4, state.Gamepad.sThumbRX);
  445. axis_changed(5, state.Gamepad.sThumbRY);
  446. _last_buttons = state.Gamepad.wButtons;
  447. _last_packet = state.dwPacketNumber;
  448. }
  449. #endif // _WIN32