BsLinuxGamepad.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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 "Private/Linux/BsLinuxInput.h"
  6. #include <fcntl.h>
  7. #include <linux/input.h>
  8. namespace bs
  9. {
  10. /** Contains private data for the Linux Gamepad implementation. */
  11. struct Gamepad::Pimpl
  12. {
  13. GamepadInfo info;
  14. INT32 fileHandle;
  15. ButtonCode povState;
  16. bool hasInputFocus;
  17. };
  18. Gamepad::Gamepad(const String& name, const GamepadInfo& gamepadInfo, Input* owner)
  19. : mName(name), mOwner(owner)
  20. {
  21. m = bs_new<Pimpl>();
  22. m->info = gamepadInfo;
  23. m->povState = BC_UNASSIGNED;
  24. m->hasInputFocus = true;
  25. String eventPath = "/dev/input/event" + toString(gamepadInfo.eventHandlerIdx);
  26. m->fileHandle = open(eventPath.c_str(), O_RDWR | O_NONBLOCK);
  27. if(m->fileHandle == -1)
  28. LOGERR("Failed to open input event file handle for device: " + gamepadInfo.name);
  29. }
  30. Gamepad::~Gamepad()
  31. {
  32. if(m->fileHandle != -1)
  33. close(m->fileHandle);
  34. bs_delete(m);
  35. }
  36. void Gamepad::capture()
  37. {
  38. if(m->fileHandle == -1)
  39. return;
  40. struct AxisState
  41. {
  42. bool moved;
  43. INT32 value;
  44. };
  45. AxisState axisState[24];
  46. bs_zero_out(axisState);
  47. input_event events[BUFFER_SIZE_GAMEPAD];
  48. while(true)
  49. {
  50. ssize_t numReadBytes = read(m->fileHandle, &events, sizeof(events));
  51. if(numReadBytes < 0)
  52. break;
  53. if(!m->hasInputFocus)
  54. continue;
  55. UINT32 numEvents = numReadBytes / sizeof(input_event);
  56. for(UINT32 i = 0; i < numEvents; ++i)
  57. {
  58. switch(events[i].type)
  59. {
  60. case EV_KEY:
  61. {
  62. auto findIter = m->info.buttonMap.find(events[i].code);
  63. if(findIter == m->info.buttonMap.end())
  64. continue;
  65. if(events[i].value)
  66. mOwner->_notifyButtonPressed(m->info.id, findIter->second, (UINT64)events[i].time.tv_usec);
  67. else
  68. mOwner->_notifyButtonReleased(m->info.id, findIter->second, (UINT64)events[i].time.tv_usec);
  69. }
  70. break;
  71. case EV_ABS:
  72. {
  73. // Stick or trigger
  74. if(events[i].code <= ABS_BRAKE)
  75. {
  76. const AxisInfo& axisInfo = m->info.axisMap[events[i].code];
  77. if(axisInfo.axisIdx >= 24)
  78. break;
  79. axisState[axisInfo.axisIdx].moved = true;
  80. // Scale range if needed
  81. if(axisInfo.min == Gamepad::MIN_AXIS && axisInfo.max != Gamepad::MAX_AXIS )
  82. axisState[axisInfo.axisIdx].value = events[i].value;
  83. else
  84. {
  85. float range = (float)(axisInfo.max - axisInfo.min);
  86. float normalizedValue = (axisInfo.max - events[i].value) / range;
  87. range = (float)(Gamepad::MAX_AXIS - Gamepad::MIN_AXIS);
  88. axisState[axisInfo.axisIdx].value = Gamepad::MIN_AXIS + (INT32)(normalizedValue * range);
  89. }
  90. }
  91. else if(events[i].code <= ABS_HAT3Y) // POV
  92. {
  93. // Note: We only support a single POV and report events from all POVs as if they were from the
  94. // same source
  95. INT32 povIdx = events[i].code - ABS_HAT0X;
  96. ButtonCode povButton = BC_UNASSIGNED;
  97. if((povIdx & 0x1) == 0) // Even, x axis
  98. {
  99. if(events[i].value == -1)
  100. povButton = BC_GAMEPAD_DPAD_LEFT;
  101. else if(events[i].value == 1)
  102. povButton = BC_GAMEPAD_DPAD_RIGHT;
  103. }
  104. else // Odd, y axis
  105. {
  106. if(events[i].value == -1)
  107. povButton = BC_GAMEPAD_DPAD_UP;
  108. else if(events[i].value == 1)
  109. povButton = BC_GAMEPAD_DPAD_DOWN;
  110. }
  111. if(m->povState != povButton)
  112. {
  113. if(m->povState != BC_UNASSIGNED)
  114. mOwner->_notifyButtonReleased(m->info.id, m->povState, (UINT64)events[i].time.tv_usec);
  115. if(povButton != BC_UNASSIGNED)
  116. mOwner->_notifyButtonPressed(m->info.id, povButton, (UINT64)events[i].time.tv_usec);
  117. m->povState = povButton;
  118. }
  119. }
  120. break;
  121. }
  122. default: break;
  123. }
  124. }
  125. }
  126. for(UINT32 i = 0; i < 24; i++)
  127. {
  128. if(axisState[i].moved)
  129. mOwner->_notifyAxisMoved(m->info.id, i, axisState[i].value);
  130. }
  131. }
  132. void Gamepad::changeCaptureContext(UINT64 windowHandle)
  133. {
  134. m->hasInputFocus = windowHandle != (UINT64)-1;
  135. }
  136. }