BsWin32Gamepad.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Input/BsGamepad.h"
  4. #include "Input/BsInput.h"
  5. #include "Win32/BsWin32Input.h"
  6. #include "Error/BsException.h"
  7. #undef DIJOFS_BUTTON
  8. #define DIJOFS_BUTTON(n) (FIELD_OFFSET(DIJOYSTATE2, rgbButtons) + (n))
  9. namespace bs
  10. {
  11. /** Contains state of a POV (DPad). */
  12. struct POVState
  13. {
  14. ButtonCode code;
  15. bool pressed;
  16. };
  17. /** Contains private data for the Win32 Gamepad implementation. */
  18. struct Gamepad::Pimpl
  19. {
  20. IDirectInput8* directInput;
  21. IDirectInputDevice8* gamepad;
  22. GamepadInfo info;
  23. DWORD coopSettings;
  24. HWND hWnd;
  25. POVState povState[4];
  26. INT32 axisState[6]; // Only for XInput
  27. bool buttonState[16]; // Only for XInput
  28. };
  29. /**
  30. * Initializes DirectInput gamepad device for a window with the specified handle. Only input from that window will be
  31. * reported.
  32. */
  33. void initializeDirectInput(Gamepad::Pimpl* m, HWND hWnd)
  34. {
  35. DIPROPDWORD dipdw;
  36. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  37. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  38. dipdw.diph.dwObj = 0;
  39. dipdw.diph.dwHow = DIPH_DEVICE;
  40. dipdw.dwData = DI_BUFFER_SIZE_GAMEPAD;
  41. if (FAILED(m->directInput->CreateDevice(m->info.guidInstance, &m->gamepad, nullptr)))
  42. BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to create device.");
  43. if (FAILED(m->gamepad->SetDataFormat(&c_dfDIJoystick2)))
  44. BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to set format.");
  45. if (FAILED(m->gamepad->SetCooperativeLevel(hWnd, m->coopSettings)))
  46. BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to set coop level.");
  47. if (FAILED(m->gamepad->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
  48. BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to set property.");
  49. HRESULT hr = m->gamepad->Acquire();
  50. if (FAILED(hr) && hr != DIERR_OTHERAPPHASPRIO)
  51. BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to acquire device.");
  52. m->hWnd = hWnd;
  53. }
  54. /** Releases DirectInput resources for the provided device */
  55. void releaseDirectInput(Gamepad::Pimpl* m)
  56. {
  57. if(m->gamepad)
  58. {
  59. m->gamepad->Unacquire();
  60. m->gamepad->Release();
  61. m->gamepad = nullptr;
  62. }
  63. }
  64. /** Handles a DirectInput POV event. */
  65. void handlePOV(Input* owner, Gamepad::Pimpl* m, int pov, DIDEVICEOBJECTDATA& di)
  66. {
  67. if (LOWORD(di.dwData) == 0xFFFF)
  68. {
  69. // Centered, release any buttons
  70. if (m->povState[pov].pressed)
  71. {
  72. owner->_notifyButtonReleased(m->info.id, m->povState[pov].code, di.dwTimeStamp);
  73. m->povState[pov].pressed = false;
  74. }
  75. }
  76. else
  77. {
  78. POVState newPOVState;
  79. bs_zero_out(newPOVState);
  80. switch (di.dwData)
  81. {
  82. case 0:
  83. newPOVState.code = BC_GAMEPAD_DPAD_UP;
  84. newPOVState.pressed = true;
  85. break;
  86. case 4500:
  87. newPOVState.code = BC_GAMEPAD_DPAD_UPRIGHT;
  88. newPOVState.pressed = true;
  89. break;
  90. case 9000:
  91. newPOVState.code = BC_GAMEPAD_DPAD_RIGHT;
  92. newPOVState.pressed = true;
  93. break;
  94. case 13500:
  95. newPOVState.code = BC_GAMEPAD_DPAD_DOWNRIGHT;
  96. newPOVState.pressed = true;
  97. break;
  98. case 18000:
  99. newPOVState.code = BC_GAMEPAD_DPAD_DOWN;
  100. newPOVState.pressed = true;
  101. break;
  102. case 22500:
  103. newPOVState.code = BC_GAMEPAD_DPAD_DOWNLEFT;
  104. newPOVState.pressed = true;
  105. break;
  106. case 27000:
  107. newPOVState.code = BC_GAMEPAD_DPAD_LEFT;
  108. newPOVState.pressed = true;
  109. break;
  110. case 31500:
  111. newPOVState.code = BC_GAMEPAD_DPAD_UPLEFT;
  112. newPOVState.pressed = true;
  113. break;
  114. }
  115. // Button was pressed
  116. if (newPOVState.pressed)
  117. {
  118. // Another button was previously pressed
  119. if (m->povState[pov].pressed)
  120. {
  121. // If its a different button, release the old one and press the new one
  122. if (m->povState[pov].code != newPOVState.code)
  123. {
  124. owner->_notifyButtonReleased(m->info.id, m->povState[pov].code, di.dwTimeStamp);
  125. owner->_notifyButtonPressed(m->info.id, newPOVState.code, di.dwTimeStamp);
  126. m->povState[pov].code = newPOVState.code;
  127. }
  128. }
  129. else
  130. {
  131. owner->_notifyButtonPressed(m->info.id, newPOVState.code, di.dwTimeStamp);
  132. m->povState[pov].code = newPOVState.code;
  133. m->povState[pov].pressed = true;
  134. }
  135. }
  136. }
  137. }
  138. /** Converts a DirectInput or XInput button code to Banshee ButtonCode. */
  139. ButtonCode gamepadButtonToButtonCode(INT32 code)
  140. {
  141. switch (code)
  142. {
  143. case 0:
  144. return BC_GAMEPAD_DPAD_UP;
  145. case 1:
  146. return BC_GAMEPAD_DPAD_DOWN;
  147. case 2:
  148. return BC_GAMEPAD_DPAD_LEFT;
  149. case 3:
  150. return BC_GAMEPAD_DPAD_RIGHT;
  151. case 4:
  152. return BC_GAMEPAD_START;
  153. case 5:
  154. return BC_GAMEPAD_BACK;
  155. case 6:
  156. return BC_GAMEPAD_LS;
  157. case 7:
  158. return BC_GAMEPAD_RS;
  159. case 8:
  160. return BC_GAMEPAD_LB;
  161. case 9:
  162. return BC_GAMEPAD_RB;
  163. case 10:
  164. return BC_GAMEPAD_BTN1;
  165. case 11:
  166. return BC_GAMEPAD_LS;
  167. case 12:
  168. return BC_GAMEPAD_A;
  169. case 13:
  170. return BC_GAMEPAD_B;
  171. case 14:
  172. return BC_GAMEPAD_X;
  173. case 15:
  174. return BC_GAMEPAD_Y;
  175. }
  176. return (ButtonCode)(BC_GAMEPAD_BTN1 + (code - 15));
  177. }
  178. Gamepad::Gamepad(const String& name, const GamepadInfo& gamepadInfo, Input* owner)
  179. : mName(name), mOwner(owner)
  180. {
  181. InputPrivateData* pvtData = owner->_getPrivateData();
  182. m = bs_new<Pimpl>();
  183. m->directInput = pvtData->directInput;
  184. m->coopSettings = pvtData->mouseSettings;
  185. m->info = gamepadInfo;
  186. m->gamepad = nullptr;
  187. m->hWnd = (HWND)owner->_getWindowHandle();
  188. bs_zero_out(m->povState);
  189. bs_zero_out(m->axisState);
  190. bs_zero_out(m->buttonState);
  191. if(!m->info.isXInput)
  192. initializeDirectInput(m, m->hWnd);
  193. }
  194. Gamepad::~Gamepad()
  195. {
  196. releaseDirectInput(m);
  197. bs_delete(m);
  198. }
  199. void Gamepad::capture()
  200. {
  201. if (m->hWnd == (HWND)-1 || m->gamepad == nullptr)
  202. return;
  203. if(m->info.isXInput)
  204. {
  205. XINPUT_STATE inputState;
  206. if (XInputGetState((DWORD)m->info.xInputDev, &inputState) != ERROR_SUCCESS)
  207. memset(&inputState, 0, sizeof(inputState));
  208. // Sticks and triggers
  209. struct AxisState
  210. {
  211. bool moved;
  212. INT32 value;
  213. };
  214. AxisState axisState[6];
  215. bs_zero_out(axisState);
  216. // Note: Order of axes must match InputAxis enum
  217. // Left stick
  218. axisState[0].value = (int)inputState.Gamepad.sThumbLX;
  219. axisState[1].value = -(int)inputState.Gamepad.sThumbLY;
  220. // Right stick
  221. axisState[2].value = (int)inputState.Gamepad.sThumbRX;
  222. axisState[3].value = -(int)inputState.Gamepad.sThumbRY;
  223. // Left trigger
  224. axisState[4].value = std::min((int)inputState.Gamepad.bLeftTrigger * 129, MAX_AXIS);
  225. // Right trigger
  226. axisState[5].value = std::min((int)inputState.Gamepad.bRightTrigger * 129, MAX_AXIS);
  227. for (UINT32 i = 0; i < 6; i++)
  228. {
  229. axisState[i].moved = axisState[i].value != m->axisState[i];
  230. m->axisState[i] = axisState[i].value;
  231. }
  232. // DPAD (POV)
  233. ButtonCode dpadButton = BC_UNASSIGNED;
  234. if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0)
  235. dpadButton = BC_GAMEPAD_DPAD_UP;
  236. else if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0)
  237. dpadButton = BC_GAMEPAD_DPAD_DOWN;
  238. if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0 )
  239. dpadButton = BC_GAMEPAD_DPAD_LEFT;
  240. else if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0)
  241. dpadButton = BC_GAMEPAD_DPAD_RIGHT;
  242. if(dpadButton != BC_UNASSIGNED) // Pressed
  243. {
  244. // Another button was previously pressed
  245. if (m->povState[0].pressed)
  246. {
  247. // If its a different button, release the old one and press the new one
  248. if (m->povState[0].code != dpadButton)
  249. {
  250. mOwner->_notifyButtonReleased(m->info.id, m->povState[0].code, GetTickCount64());
  251. mOwner->_notifyButtonPressed(m->info.id, dpadButton, GetTickCount64());
  252. m->povState[0].code = dpadButton;
  253. }
  254. }
  255. else
  256. {
  257. mOwner->_notifyButtonPressed(m->info.id, dpadButton, GetTickCount64());
  258. m->povState[0].code = dpadButton;
  259. m->povState[0].pressed = true;
  260. }
  261. }
  262. else
  263. {
  264. if (m->povState[0].pressed)
  265. {
  266. mOwner->_notifyButtonReleased(m->info.id, m->povState[0].code, GetTickCount64());
  267. m->povState[0].pressed = false;
  268. }
  269. }
  270. // Buttons
  271. for (UINT32 i = 0; i < 16; i++)
  272. {
  273. bool buttonState = (inputState.Gamepad.wButtons & (1 << i)) != 0;
  274. if(buttonState != m->buttonState[i])
  275. {
  276. if (buttonState)
  277. mOwner->_notifyButtonPressed(m->info.id, gamepadButtonToButtonCode(i), GetTickCount64());
  278. else
  279. mOwner->_notifyButtonReleased(m->info.id, gamepadButtonToButtonCode(i), GetTickCount64());
  280. m->buttonState[i] = buttonState;
  281. }
  282. }
  283. for (int i = 0; i < 6; ++i)
  284. {
  285. if (!axisState[i].moved)
  286. continue;
  287. mOwner->_notifyAxisMoved(m->info.id, i + (int)InputAxis::MouseZ, axisState[i].value);
  288. }
  289. }
  290. else // DirectInput
  291. {
  292. DIDEVICEOBJECTDATA diBuff[DI_BUFFER_SIZE_GAMEPAD];
  293. DWORD numEntries = DI_BUFFER_SIZE_GAMEPAD;
  294. HRESULT hr = m->gamepad->Poll();
  295. if (hr == DI_OK)
  296. hr = m->gamepad->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diBuff, &numEntries, 0);
  297. if (hr != DI_OK)
  298. {
  299. hr = m->gamepad->Acquire();
  300. while (hr == DIERR_INPUTLOST)
  301. hr = m->gamepad->Acquire();
  302. m->gamepad->Poll();
  303. hr = m->gamepad->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diBuff, &numEntries, 0);
  304. if (FAILED(hr))
  305. return;
  306. }
  307. struct AxisState
  308. {
  309. bool moved;
  310. INT32 value;
  311. };
  312. AxisState axisState[24];
  313. bs_zero_out(axisState);
  314. // Note: Not reporting slider or POV events
  315. for (UINT32 i = 0; i < numEntries; ++i)
  316. {
  317. switch (diBuff[i].dwOfs)
  318. {
  319. case DIJOFS_POV(0):
  320. handlePOV(mOwner, m, 0, diBuff[i]);
  321. break;
  322. case DIJOFS_POV(1):
  323. handlePOV(mOwner, m, 1, diBuff[i]);
  324. break;
  325. case DIJOFS_POV(2):
  326. handlePOV(mOwner, m, 2, diBuff[i]);
  327. break;
  328. case DIJOFS_POV(3):
  329. handlePOV(mOwner, m, 3, diBuff[i]);
  330. break;
  331. default:
  332. // Button event
  333. if (diBuff[i].dwOfs >= DIJOFS_BUTTON(0) && diBuff[i].dwOfs < DIJOFS_BUTTON(128))
  334. {
  335. int button = diBuff[i].dwOfs - DIJOFS_BUTTON(0);
  336. if ((diBuff[i].dwData & 0x80) != 0)
  337. mOwner->_notifyButtonPressed(m->info.id, gamepadButtonToButtonCode(button), diBuff[i].dwTimeStamp);
  338. else
  339. mOwner->_notifyButtonReleased(m->info.id, gamepadButtonToButtonCode(button), diBuff[i].dwTimeStamp);
  340. }
  341. else if ((short)(diBuff[i].uAppData >> 16) == 0x1313) // Axis event
  342. {
  343. int axis = (int)(0x0000FFFF & diBuff[i].uAppData);
  344. if (axis < 24)
  345. {
  346. axisState[axis].moved = true;
  347. axisState[axis].value = diBuff[i].dwData;
  348. }
  349. }
  350. }
  351. }
  352. if (numEntries > 0)
  353. {
  354. for (int i = 0; i < 24; ++i)
  355. {
  356. if (!axisState[i].moved)
  357. continue;
  358. mOwner->_notifyAxisMoved(m->info.id, i + (int)InputAxis::MouseZ, axisState[i].value);
  359. }
  360. }
  361. }
  362. }
  363. void Gamepad::changeCaptureContext(UINT64 windowHandle)
  364. {
  365. HWND newhWnd = (HWND)windowHandle;
  366. if(m->hWnd != newhWnd)
  367. {
  368. releaseDirectInput(m);
  369. if (!m->info.isXInput && windowHandle != (UINT64)-1)
  370. initializeDirectInput(m, newhWnd);
  371. else
  372. m->hWnd = newhWnd;
  373. }
  374. }
  375. }