BarrierInputKeyboard.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <BarrierInputKeyboard.h>
  9. #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboardWindowsScanCodes.h>
  10. #include <AzCore/std/containers/fixed_unordered_map.h>
  11. ////////////////////////////////////////////////////////////////////////////////////////////////////
  12. namespace BarrierInput
  13. {
  14. using namespace AzFramework;
  15. ////////////////////////////////////////////////////////////////////////////////////////////////
  16. InputDeviceKeyboard::Implementation* InputDeviceKeyboardBarrier::Create(InputDeviceKeyboard& inputDevice)
  17. {
  18. return aznew InputDeviceKeyboardBarrier(inputDevice);
  19. }
  20. ////////////////////////////////////////////////////////////////////////////////////////////////
  21. InputDeviceKeyboardBarrier::InputDeviceKeyboardBarrier(InputDeviceKeyboard& inputDevice)
  22. : InputDeviceKeyboard::Implementation(inputDevice)
  23. , m_threadAwareRawKeyEventQueuesById()
  24. , m_threadAwareRawKeyEventQueuesByIdMutex()
  25. , m_threadAwareRawTextEventQueue()
  26. , m_threadAwareRawTextEventQueueMutex()
  27. , m_hasTextEntryStarted(false)
  28. {
  29. RawInputNotificationBusBarrier::Handler::BusConnect();
  30. }
  31. ////////////////////////////////////////////////////////////////////////////////////////////////
  32. InputDeviceKeyboardBarrier::~InputDeviceKeyboardBarrier()
  33. {
  34. RawInputNotificationBusBarrier::Handler::BusDisconnect();
  35. }
  36. ////////////////////////////////////////////////////////////////////////////////////////////////
  37. bool InputDeviceKeyboardBarrier::IsConnected() const
  38. {
  39. // We could check the validity of the socket connection to the Barrier server
  40. return true;
  41. }
  42. ////////////////////////////////////////////////////////////////////////////////////////////////
  43. bool InputDeviceKeyboardBarrier::HasTextEntryStarted() const
  44. {
  45. return m_hasTextEntryStarted;
  46. }
  47. ////////////////////////////////////////////////////////////////////////////////////////////////
  48. void InputDeviceKeyboardBarrier::TextEntryStart(const InputTextEntryRequests::VirtualKeyboardOptions&)
  49. {
  50. m_hasTextEntryStarted = true;
  51. }
  52. ////////////////////////////////////////////////////////////////////////////////////////////////
  53. void InputDeviceKeyboardBarrier::TextEntryStop()
  54. {
  55. m_hasTextEntryStarted = false;
  56. }
  57. ////////////////////////////////////////////////////////////////////////////////////////////////
  58. void InputDeviceKeyboardBarrier::TickInputDevice()
  59. {
  60. {
  61. // Queue all key events that were received in the other thread
  62. AZStd::scoped_lock lock(m_threadAwareRawKeyEventQueuesByIdMutex);
  63. for (const auto& keyEventQueuesById : m_threadAwareRawKeyEventQueuesById)
  64. {
  65. const InputChannelId& inputChannelId = keyEventQueuesById.first;
  66. for (bool rawKeyState : keyEventQueuesById.second)
  67. {
  68. QueueRawKeyEvent(inputChannelId, rawKeyState);
  69. }
  70. }
  71. m_threadAwareRawKeyEventQueuesById.clear();
  72. }
  73. {
  74. // Queue all text events that were received in the other thread
  75. AZStd::scoped_lock lock(m_threadAwareRawTextEventQueueMutex);
  76. for (const AZStd::string& rawTextEvent : m_threadAwareRawTextEventQueue)
  77. {
  78. #if !defined(ALWAYS_DISPATCH_KEYBOARD_TEXT_INPUT)
  79. if (!m_hasTextEntryStarted)
  80. {
  81. continue;
  82. }
  83. #endif // !defined(ALWAYS_DISPATCH_KEYBOARD_TEXT_INPUT)
  84. QueueRawTextEvent(rawTextEvent);
  85. }
  86. m_threadAwareRawTextEventQueue.clear();
  87. }
  88. // Process raw event queues once each frame
  89. ProcessRawEventQueues();
  90. }
  91. ////////////////////////////////////////////////////////////////////////////////////////////////
  92. void InputDeviceKeyboardBarrier::OnRawKeyboardKeyDownEvent(uint32_t scanCode,
  93. ModifierMask activeModifiers)
  94. {
  95. // Queue key events and text events
  96. ThreadSafeQueueRawKeyEvent(scanCode, true);
  97. if (char asciiChar = TranslateRawKeyEventToASCIIChar(scanCode, activeModifiers))
  98. {
  99. const AZStd::string text(1, asciiChar);
  100. ThreadSafeQueueRawTextEvent(text);
  101. }
  102. }
  103. ////////////////////////////////////////////////////////////////////////////////////////////////
  104. void InputDeviceKeyboardBarrier::OnRawKeyboardKeyUpEvent(uint32_t scanCode,
  105. [[maybe_unused]]ModifierMask activeModifiers)
  106. {
  107. // Queue key events, not text events
  108. ThreadSafeQueueRawKeyEvent(scanCode, false);
  109. }
  110. ////////////////////////////////////////////////////////////////////////////////////////////////
  111. void InputDeviceKeyboardBarrier::OnRawKeyboardKeyRepeatEvent(uint32_t scanCode,
  112. ModifierMask activeModifiers)
  113. {
  114. // Don't queue key events, only text events
  115. if (char asciiChar = TranslateRawKeyEventToASCIIChar(scanCode, activeModifiers))
  116. {
  117. const AZStd::string text(1, asciiChar);
  118. ThreadSafeQueueRawTextEvent(text);
  119. }
  120. }
  121. ////////////////////////////////////////////////////////////////////////////////////////////////
  122. void InputDeviceKeyboardBarrier::ThreadSafeQueueRawKeyEvent(uint32_t scanCode, bool rawKeyState)
  123. {
  124. // From observation, Barrier scan codes in the:
  125. // - Range 0x0-0x7F (0-127) correspond to windows scan codes without the extended bit set
  126. // - Range 0x100-0x17F (256-383) correspond to windows scan codes with the extended bit set
  127. const InputChannelId* inputChannelId = nullptr;
  128. if (scanCode < InputChannelIdByScanCodeTable.size())
  129. {
  130. inputChannelId = InputChannelIdByScanCodeTable[scanCode];
  131. }
  132. else if (0x100 <= scanCode && scanCode < InputChannelIdByScanCodeWithExtendedPrefixTable.size())
  133. {
  134. inputChannelId = InputChannelIdByScanCodeWithExtendedPrefixTable[scanCode - 0x100];
  135. }
  136. if (inputChannelId)
  137. {
  138. AZStd::scoped_lock lock(m_threadAwareRawKeyEventQueuesByIdMutex);
  139. m_threadAwareRawKeyEventQueuesById[*inputChannelId].push_back(rawKeyState);
  140. }
  141. }
  142. ////////////////////////////////////////////////////////////////////////////////////////////////
  143. void InputDeviceKeyboardBarrier::ThreadSafeQueueRawTextEvent(const AZStd::string& textUTF8)
  144. {
  145. AZStd::scoped_lock lock(m_threadAwareRawTextEventQueueMutex);
  146. m_threadAwareRawTextEventQueue.push_back(textUTF8);
  147. }
  148. ////////////////////////////////////////////////////////////////////////////////////////////////
  149. char InputDeviceKeyboardBarrier::TranslateRawKeyEventToASCIIChar(uint32_t scanCode,
  150. ModifierMask activeModifiers)
  151. {
  152. // Map ASCII character pairs keyed by their keyboard scan code, assuming an ANSI mechanical
  153. // keyboard layout with a standard QWERTY key mapping. The first element of the pair is the
  154. // character that should be produced if the key is pressed while no shift or caps modifiers
  155. // are active, while the second element is the character that should be produced if the key
  156. // is pressed while a shift or caps modifier is active. Required because Barrier only sends
  157. // raw key events, not translated text input. While we would ideally support the full range
  158. // of UTF-8 text input, that is beyond the scope of this debug/development only class. Note
  159. // that this function assumes an ANSI mechanical keyboard layout with a standard QWERTY key
  160. // mapping, and will not produce correct results if used with other key layouts or mappings.
  161. static const AZStd::fixed_unordered_map<AZ::u32, AZStd::pair<char, char>, 16, 64> ScanCodeToASCIICharMap =
  162. {
  163. { 2, { '1', '!' } },
  164. { 3, { '2', '@' } },
  165. { 4, { '3', '#' } },
  166. { 5, { '4', '$' } },
  167. { 6, { '5', '%' } },
  168. { 7, { '6', '^' } },
  169. { 8, { '7', '&' } },
  170. { 9, { '8', '*' } },
  171. { 10, { '9', '(' } },
  172. { 11, { '0', ')' } },
  173. { 12, { '-', '_' } },
  174. { 13, { '=', '+' } },
  175. { 15, { '\t', '\t' } },
  176. { 16, { 'q', 'Q' } },
  177. { 17, { 'w', 'W' } },
  178. { 18, { 'e', 'E' } },
  179. { 19, { 'r', 'R' } },
  180. { 20, { 't', 'T' } },
  181. { 21, { 'y', 'Y' } },
  182. { 22, { 'u', 'U' } },
  183. { 23, { 'i', 'I' } },
  184. { 24, { 'o', 'O' } },
  185. { 25, { 'p', 'P' } },
  186. { 26, { '[', '{' } },
  187. { 27, { ']', '}' } },
  188. { 30, { 'a', 'A' } },
  189. { 31, { 's', 'S' } },
  190. { 32, { 'd', 'D' } },
  191. { 33, { 'f', 'F' } },
  192. { 34, { 'g', 'G' } },
  193. { 35, { 'h', 'H' } },
  194. { 36, { 'j', 'J' } },
  195. { 37, { 'k', 'K' } },
  196. { 38, { 'l', 'L' } },
  197. { 39, { ';', ':' } },
  198. { 40, { '\'', '"' } },
  199. { 41, { '`', '~' } },
  200. { 43, { '\\', '|' } },
  201. { 44, { 'z', 'Z' } },
  202. { 45, { 'x', 'X' } },
  203. { 46, { 'c', 'C' } },
  204. { 47, { 'v', 'V' } },
  205. { 48, { 'b', 'B' } },
  206. { 49, { 'n', 'N' } },
  207. { 50, { 'm', 'M' } },
  208. { 51, { ',', '<' } },
  209. { 52, { '.', '>' } },
  210. { 53, { '/', '?' } },
  211. { 55, { '*', '*' } },
  212. { 57, { ' ', ' ' } },
  213. { 71, { '7', '7' } },
  214. { 72, { '8', '8' } },
  215. { 73, { '9', '9' } },
  216. { 74, { '-', '-' } },
  217. { 75, { '4', '4' } },
  218. { 76, { '5', '5' } },
  219. { 77, { '6', '6' } },
  220. { 78, { '+', '+' } },
  221. { 79, { '1', '1' } },
  222. { 80, { '2', '2' } },
  223. { 81, { '3', '3' } },
  224. { 82, { '0', '0' } },
  225. { 83, { '.', '.' } },
  226. { 309, { '/', '/' } }
  227. };
  228. const auto& it = ScanCodeToASCIICharMap.find(scanCode);
  229. if (it == ScanCodeToASCIICharMap.end())
  230. {
  231. return '\0';
  232. }
  233. const bool shiftOrCapsLockActive = (activeModifiers & ModifierMask_Shift) ||
  234. (activeModifiers & ModifierMask_CapsLock);
  235. return shiftOrCapsLockActive ? it->second.second : it->second.first;
  236. }
  237. } // namespace BarrierInput