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