BsLinuxInput.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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. /** Checks is the event handler at the specified input file handle is a mouse. */
  213. bool isMouse(int fileHandle)
  214. {
  215. EventInfo eventInfo;
  216. if(!getEventInfo(fileHandle, eventInfo))
  217. return false;
  218. // Check for mouse buttons
  219. for(auto& entry : eventInfo.buttons)
  220. {
  221. if((entry >= BTN_MOUSE && entry < BTN_JOYSTICK))
  222. return true;
  223. }
  224. return false;
  225. }
  226. /** Checks is the event handler at the specified input file handle is a keyboard. */
  227. bool isKeyboard(int fileHandle)
  228. {
  229. EventInfo eventInfo;
  230. if(!getEventInfo(fileHandle, eventInfo))
  231. return false;
  232. // Check for keyboard keys
  233. for(auto& entry : eventInfo.buttons)
  234. {
  235. if(entry >= KEY_ESC && entry < KEY_KPDOT)
  236. return true;
  237. }
  238. return false;
  239. }
  240. void Input::initRawInput()
  241. {
  242. mPlatformData = bs_new<InputPrivateData>();
  243. // Scan for valid input devices
  244. for(int i = 0; i < 64; ++i )
  245. {
  246. String eventPath = "/dev/input/event" + toString(i);
  247. int file = open(eventPath.c_str(), O_RDONLY |O_NONBLOCK);
  248. if(file == -1)
  249. {
  250. if(errno == EACCES)
  251. LOGERR("Cannot open input device " + eventPath + ". Make sure your user has valid permissions.");
  252. continue;
  253. }
  254. GamepadInfo info;
  255. if(parseGamepadInfo(file, i, info))
  256. {
  257. info.id = (UINT32)mPlatformData->gamepadInfos.size();
  258. mPlatformData->gamepadInfos.push_back(info);
  259. }
  260. else if(isKeyboard(file))
  261. mPlatformData->keyboards.push_back(i);
  262. else if(isMouse(file))
  263. mPlatformData->mice.push_back(i);
  264. close(file);
  265. }
  266. if (getDeviceCount(InputDevice::Keyboard) > 0)
  267. mKeyboard = bs_new<Keyboard>("Keyboard", this);
  268. if (getDeviceCount(InputDevice::Mouse) > 0)
  269. mMouse = bs_new<Mouse>("Mouse", this);
  270. UINT32 numGamepads = getDeviceCount(InputDevice::Gamepad);
  271. for (UINT32 i = 0; i < numGamepads; i++)
  272. mGamepads.push_back(bs_new<Gamepad>(mPlatformData->gamepadInfos[i].name, mPlatformData->gamepadInfos[i], this));
  273. }
  274. void Input::cleanUpRawInput()
  275. {
  276. if (mMouse != nullptr)
  277. bs_delete(mMouse);
  278. if (mKeyboard != nullptr)
  279. bs_delete(mKeyboard);
  280. for (auto& gamepad : mGamepads)
  281. bs_delete(gamepad);
  282. bs_delete(mPlatformData);
  283. }
  284. UINT32 Input::getDeviceCount(InputDevice device) const
  285. {
  286. switch(device)
  287. {
  288. case InputDevice::Keyboard: return 1;
  289. case InputDevice::Mouse: return 1;
  290. case InputDevice::Gamepad: return (UINT32)mPlatformData->gamepadInfos.size();
  291. case InputDevice::Count: return 0;
  292. }
  293. return 0;
  294. }
  295. }