BsLinuxInput.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Input/BsInput.h"
  4. #include "Linux/BsLinuxInput.h"
  5. #include "Input/BsMouse.h"
  6. #include "Input/BsKeyboard.h"
  7. #include "Input/BsGamepad.h"
  8. #include <fcntl.h>
  9. #include <linux/input.h>
  10. namespace bs
  11. {
  12. /** Information about events reported from a specific input event device. */
  13. struct EventInfo
  14. {
  15. Vector<INT32> buttons;
  16. Vector<INT32> relAxes;
  17. Vector<INT32> absAxes;
  18. Vector<INT32> hats;
  19. };
  20. /** Checks is the bit at the specified location in a byte array is set. */
  21. bool isBitSet(UINT8 bits[], UINT32 bit)
  22. {
  23. return ((bits[bit/8] >> (bit%8)) & 1) != 0;
  24. }
  25. /** Returns information about an input event device attached to he provided file handle. */
  26. bool getEventInfo(int fileHandle, EventInfo& eventInfo)
  27. {
  28. UINT8 eventBits[1 + EV_MAX/8];
  29. bs_zero_out(eventBits);
  30. if (ioctl(fileHandle, EVIOCGBIT(0, sizeof(eventBits)), eventBits) == -1)
  31. return false;
  32. for (UINT32 i = 0; i < EV_MAX; i++)
  33. {
  34. if(isBitSet(eventBits, i))
  35. {
  36. if(i == EV_ABS)
  37. {
  38. UINT8 absAxisBits[1 + ABS_MAX/8];
  39. bs_zero_out(absAxisBits);
  40. if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(absAxisBits)), absAxisBits) == -1)
  41. {
  42. LOGERR("Could not read device absolute axis features.");
  43. continue;
  44. }
  45. for (UINT32 j = 0; j < ABS_MAX; j++)
  46. {
  47. if(isBitSet(absAxisBits, j))
  48. {
  49. if(j >= ABS_HAT0X && j <= ABS_HAT3Y)
  50. eventInfo.hats.push_back(j);
  51. else
  52. eventInfo.absAxes.push_back(j);
  53. }
  54. }
  55. }
  56. else if(i == EV_REL)
  57. {
  58. UINT8 relAxisBits[1 + REL_MAX/8];
  59. bs_zero_out(relAxisBits);
  60. if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(relAxisBits)), relAxisBits) == -1)
  61. {
  62. LOGERR("Could not read device relative axis features.");
  63. continue;
  64. }
  65. for (UINT32 j = 0; j < REL_MAX; j++)
  66. {
  67. if(isBitSet(relAxisBits, j))
  68. eventInfo.relAxes.push_back(j);
  69. }
  70. }
  71. else if(i == EV_KEY)
  72. {
  73. UINT8 keyBits[1 + KEY_MAX/8];
  74. bs_zero_out(keyBits);
  75. if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(keyBits)), keyBits) == -1)
  76. {
  77. LOGERR("Could not read device key features.");
  78. continue;
  79. }
  80. for (UINT32 j = 0; j < KEY_MAX; j++)
  81. {
  82. if(isBitSet(keyBits, j))
  83. eventInfo.buttons.push_back(j);
  84. }
  85. }
  86. }
  87. }
  88. return true;
  89. }
  90. /** Converts a Linux button code to Banshee ButtonCode. */
  91. ButtonCode gamepadMapCommonButton(INT32 code)
  92. {
  93. // Note: Assuming XBox controller layout here
  94. switch (code)
  95. {
  96. case BTN_TRIGGER_HAPPY1:
  97. return BC_GAMEPAD_DPAD_LEFT;
  98. case BTN_TRIGGER_HAPPY2:
  99. return BC_GAMEPAD_DPAD_RIGHT;
  100. case BTN_TRIGGER_HAPPY3:
  101. return BC_GAMEPAD_DPAD_UP;
  102. case BTN_TRIGGER_HAPPY4:
  103. return BC_GAMEPAD_DPAD_DOWN;
  104. case BTN_START:
  105. return BC_GAMEPAD_START;
  106. case BTN_SELECT:
  107. return BC_GAMEPAD_BACK;
  108. case BTN_THUMBL:
  109. return BC_GAMEPAD_LS;
  110. case BTN_THUMBR:
  111. return BC_GAMEPAD_RS;
  112. case BTN_TL:
  113. return BC_GAMEPAD_LB;
  114. case BTN_TR:
  115. return BC_GAMEPAD_RB;
  116. case BTN_A:
  117. return BC_GAMEPAD_A;
  118. case BTN_B:
  119. return BC_GAMEPAD_B;
  120. case BTN_X:
  121. return BC_GAMEPAD_X;
  122. case BTN_Y:
  123. return BC_GAMEPAD_Y;
  124. }
  125. return BC_UNASSIGNED;
  126. }
  127. /**
  128. * Maps an absolute axis as reported by the Linux system, to a Banshee axis. This will be one of the InputAxis enum
  129. * members, or -1 if it cannot be mapped.
  130. */
  131. INT32 gamepadMapCommonAxis(INT32 axis)
  132. {
  133. switch(axis)
  134. {
  135. case ABS_X: return (INT32)InputAxis::LeftStickX;
  136. case ABS_Y: return (INT32)InputAxis::LeftStickY;
  137. case ABS_RX: return (INT32)InputAxis::RightStickX;
  138. case ABS_RY: return (INT32)InputAxis::RightStickY;
  139. case ABS_Z: return (INT32)InputAxis::LeftTrigger;
  140. case ABS_RZ: return (INT32)InputAxis::RightTrigger;
  141. }
  142. return -1;
  143. }
  144. /**
  145. * Returns true if the input event attached to the specified file handle is a gamepad,
  146. * and populates the gamepad info structure. Returns false otherwise.
  147. */
  148. bool parseGamepadInfo(int fileHandle, int eventHandlerIdx, GamepadInfo& info)
  149. {
  150. EventInfo eventInfo;
  151. if(!getEventInfo(fileHandle, eventInfo))
  152. return false;
  153. bool isGamepad = false;
  154. // Check for gamepad buttons
  155. UINT32 unknownButtonIdx = 0;
  156. for(auto& entry : eventInfo.buttons)
  157. {
  158. if((entry >= BTN_JOYSTICK && entry < BTN_GAMEPAD)
  159. || (entry >= BTN_GAMEPAD && entry < BTN_DIGI)
  160. || (entry >= BTN_WHEEL && entry < KEY_OK))
  161. {
  162. ButtonCode bc = gamepadMapCommonButton(entry);
  163. if(bc == BC_UNASSIGNED)
  164. {
  165. // Map to unnamed buttons
  166. if(unknownButtonIdx < 20)
  167. {
  168. bc = (ButtonCode)((INT32)BC_GAMEPAD_BTN1 + unknownButtonIdx);
  169. info.buttonMap[entry] = bc;
  170. unknownButtonIdx++;
  171. }
  172. }
  173. else
  174. info.buttonMap[entry] = bc;
  175. isGamepad = true;
  176. }
  177. }
  178. if(isGamepad)
  179. {
  180. info.eventHandlerIdx = eventHandlerIdx;
  181. // Get device name
  182. char name[128];
  183. if (ioctl(fileHandle, EVIOCGNAME(sizeof(name)), name) != -1)
  184. info.name = String(name);
  185. else
  186. LOGERR("Could not read device name.");
  187. // Get axis ranges
  188. UINT32 unknownAxisIdx = 0;
  189. for(auto& entry : eventInfo.absAxes)
  190. {
  191. AxisInfo& axisInfo = info.axisMap[entry];
  192. axisInfo.min = Gamepad::MIN_AXIS;
  193. axisInfo.max = Gamepad::MAX_AXIS;
  194. input_absinfo absinfo;
  195. if (ioctl(fileHandle, EVIOCGABS(entry), &absinfo) == -1)
  196. {
  197. LOGERR("Could not read absolute axis device features.");
  198. continue;
  199. }
  200. axisInfo.min = absinfo.minimum;
  201. axisInfo.max = absinfo.maximum;
  202. axisInfo.axisIdx = gamepadMapCommonAxis(entry);
  203. if(axisInfo.axisIdx == -1)
  204. {
  205. axisInfo.axisIdx = (INT32)InputAxis::Count + unknownAxisIdx;
  206. unknownAxisIdx++;
  207. }
  208. }
  209. }
  210. return isGamepad;
  211. }
  212. void Input::initRawInput()
  213. {
  214. mPlatformData = bs_new<InputPrivateData>();
  215. // Scan for valid input devices
  216. for(int i = 0; i < 64; ++i )
  217. {
  218. String eventPath = "/dev/input/event" + toString(i);
  219. int file = open(eventPath.c_str(), O_RDONLY |O_NONBLOCK);
  220. if(file == -1)
  221. {
  222. if(errno == EACCES)
  223. LOGERR("Cannot open input device " + eventPath + ". Make sure your user has valid permissions.");
  224. continue;
  225. }
  226. GamepadInfo info;
  227. if(parseGamepadInfo(file, i, info))
  228. {
  229. info.id = (UINT32)mPlatformData->gamepadInfos.size();
  230. mPlatformData->gamepadInfos.push_back(info);
  231. }
  232. close(file);
  233. }
  234. mKeyboard = bs_new<Keyboard>("Keyboard", this);
  235. mMouse = bs_new<Mouse>("Mouse", this);
  236. UINT32 numGamepads = getDeviceCount(InputDevice::Gamepad);
  237. for (UINT32 i = 0; i < numGamepads; i++)
  238. mGamepads.push_back(bs_new<Gamepad>(mPlatformData->gamepadInfos[i].name, mPlatformData->gamepadInfos[i], this));
  239. }
  240. void Input::cleanUpRawInput()
  241. {
  242. if (mMouse != nullptr)
  243. bs_delete(mMouse);
  244. if (mKeyboard != nullptr)
  245. bs_delete(mKeyboard);
  246. for (auto& gamepad : mGamepads)
  247. bs_delete(gamepad);
  248. bs_delete(mPlatformData);
  249. }
  250. UINT32 Input::getDeviceCount(InputDevice device) const
  251. {
  252. switch(device)
  253. {
  254. case InputDevice::Keyboard: return 1;
  255. case InputDevice::Mouse: return 1;
  256. case InputDevice::Gamepad: return (UINT32)mPlatformData->gamepadInfos.size();
  257. case InputDevice::Count: return 0;
  258. }
  259. return 0;
  260. }
  261. }