| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <BarrierInputKeyboard.h>
- #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboardWindowsScanCodes.h>
- #include <AzCore/std/containers/fixed_unordered_map.h>
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- namespace BarrierInput
- {
- using namespace AzFramework;
- ////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceKeyboardBarrier::InputDeviceKeyboardBarrier(InputDeviceKeyboard& inputDevice)
- : InputDeviceKeyboard::Implementation(inputDevice)
- , m_threadAwareRawKeyEventQueuesById()
- , m_threadAwareRawKeyEventQueuesByIdMutex()
- , m_threadAwareRawTextEventQueue()
- , m_threadAwareRawTextEventQueueMutex()
- , m_hasTextEntryStarted(false)
- {
- RawInputNotificationBusBarrier::Handler::BusConnect();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceKeyboardBarrier::~InputDeviceKeyboardBarrier()
- {
- RawInputNotificationBusBarrier::Handler::BusDisconnect();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- bool InputDeviceKeyboardBarrier::IsConnected() const
- {
- // We could check the validity of the socket connection to the Barrier server
- return true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- bool InputDeviceKeyboardBarrier::HasTextEntryStarted() const
- {
- return m_hasTextEntryStarted;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::TextEntryStart(const InputTextEntryRequests::VirtualKeyboardOptions&)
- {
- m_hasTextEntryStarted = true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::TextEntryStop()
- {
- m_hasTextEntryStarted = false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::TickInputDevice()
- {
- {
- // Queue all key events that were received in the other thread
- AZStd::scoped_lock lock(m_threadAwareRawKeyEventQueuesByIdMutex);
- for (const auto& keyEventQueuesById : m_threadAwareRawKeyEventQueuesById)
- {
- const InputChannelId& inputChannelId = keyEventQueuesById.first;
- for (bool rawKeyState : keyEventQueuesById.second)
- {
- QueueRawKeyEvent(inputChannelId, rawKeyState);
- }
- }
- m_threadAwareRawKeyEventQueuesById.clear();
- }
- {
- // Queue all text events that were received in the other thread
- AZStd::scoped_lock lock(m_threadAwareRawTextEventQueueMutex);
- for (const AZStd::string& rawTextEvent : m_threadAwareRawTextEventQueue)
- {
- #if !defined(ALWAYS_DISPATCH_KEYBOARD_TEXT_INPUT)
- if (!m_hasTextEntryStarted)
- {
- continue;
- }
- #endif // !defined(ALWAYS_DISPATCH_KEYBOARD_TEXT_INPUT)
- QueueRawTextEvent(rawTextEvent);
- }
- m_threadAwareRawTextEventQueue.clear();
- }
- // Process raw event queues once each frame
- ProcessRawEventQueues();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::OnRawKeyboardKeyDownEvent(uint32_t scanCode,
- ModifierMask activeModifiers)
- {
- // Queue key events and text events
- ThreadSafeQueueRawKeyEvent(scanCode, true);
- if (char asciiChar = TranslateRawKeyEventToASCIIChar(scanCode, activeModifiers))
- {
- const AZStd::string text(1, asciiChar);
- ThreadSafeQueueRawTextEvent(text);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::OnRawKeyboardKeyUpEvent(uint32_t scanCode,
- [[maybe_unused]]ModifierMask activeModifiers)
- {
- // Queue key events, not text events
- ThreadSafeQueueRawKeyEvent(scanCode, false);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::OnRawKeyboardKeyRepeatEvent(uint32_t scanCode,
- ModifierMask activeModifiers)
- {
- // Don't queue key events, only text events
- if (char asciiChar = TranslateRawKeyEventToASCIIChar(scanCode, activeModifiers))
- {
- const AZStd::string text(1, asciiChar);
- ThreadSafeQueueRawTextEvent(text);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::ThreadSafeQueueRawKeyEvent(uint32_t scanCode, bool rawKeyState)
- {
- // From observation, Barrier scan codes in the:
- // - Range 0x0-0x7F (0-127) correspond to windows scan codes without the extended bit set
- // - Range 0x100-0x17F (256-383) correspond to windows scan codes with the extended bit set
- const InputChannelId* inputChannelId = nullptr;
- if (scanCode < InputChannelIdByScanCodeTable.size())
- {
- inputChannelId = InputChannelIdByScanCodeTable[scanCode];
- }
- else if (0x100 <= scanCode && scanCode < InputChannelIdByScanCodeWithExtendedPrefixTable.size())
- {
- inputChannelId = InputChannelIdByScanCodeWithExtendedPrefixTable[scanCode - 0x100];
- }
- if (inputChannelId)
- {
- AZStd::scoped_lock lock(m_threadAwareRawKeyEventQueuesByIdMutex);
- m_threadAwareRawKeyEventQueuesById[*inputChannelId].push_back(rawKeyState);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- void InputDeviceKeyboardBarrier::ThreadSafeQueueRawTextEvent(const AZStd::string& textUTF8)
- {
- AZStd::scoped_lock lock(m_threadAwareRawTextEventQueueMutex);
- m_threadAwareRawTextEventQueue.push_back(textUTF8);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- char InputDeviceKeyboardBarrier::TranslateRawKeyEventToASCIIChar(uint32_t scanCode,
- ModifierMask activeModifiers)
- {
- // Map ASCII character pairs keyed by their keyboard scan code, assuming an ANSI mechanical
- // keyboard layout with a standard QWERTY key mapping. The first element of the pair is the
- // character that should be produced if the key is pressed while no shift or caps modifiers
- // are active, while the second element is the character that should be produced if the key
- // is pressed while a shift or caps modifier is active. Required because Barrier only sends
- // raw key events, not translated text input. While we would ideally support the full range
- // of UTF-8 text input, that is beyond the scope of this debug/development only class. Note
- // that this function assumes an ANSI mechanical keyboard layout with a standard QWERTY key
- // mapping, and will not produce correct results if used with other key layouts or mappings.
- static const AZStd::fixed_unordered_map<AZ::u32, AZStd::pair<char, char>, 16, 64> ScanCodeToASCIICharMap =
- {
- { 2, { '1', '!' } },
- { 3, { '2', '@' } },
- { 4, { '3', '#' } },
- { 5, { '4', '$' } },
- { 6, { '5', '%' } },
- { 7, { '6', '^' } },
- { 8, { '7', '&' } },
- { 9, { '8', '*' } },
- { 10, { '9', '(' } },
- { 11, { '0', ')' } },
- { 12, { '-', '_' } },
- { 13, { '=', '+' } },
- { 15, { '\t', '\t' } },
- { 16, { 'q', 'Q' } },
- { 17, { 'w', 'W' } },
- { 18, { 'e', 'E' } },
- { 19, { 'r', 'R' } },
- { 20, { 't', 'T' } },
- { 21, { 'y', 'Y' } },
- { 22, { 'u', 'U' } },
- { 23, { 'i', 'I' } },
- { 24, { 'o', 'O' } },
- { 25, { 'p', 'P' } },
- { 26, { '[', '{' } },
- { 27, { ']', '}' } },
- { 30, { 'a', 'A' } },
- { 31, { 's', 'S' } },
- { 32, { 'd', 'D' } },
- { 33, { 'f', 'F' } },
- { 34, { 'g', 'G' } },
- { 35, { 'h', 'H' } },
- { 36, { 'j', 'J' } },
- { 37, { 'k', 'K' } },
- { 38, { 'l', 'L' } },
- { 39, { ';', ':' } },
- { 40, { '\'', '"' } },
- { 41, { '`', '~' } },
- { 43, { '\\', '|' } },
- { 44, { 'z', 'Z' } },
- { 45, { 'x', 'X' } },
- { 46, { 'c', 'C' } },
- { 47, { 'v', 'V' } },
- { 48, { 'b', 'B' } },
- { 49, { 'n', 'N' } },
- { 50, { 'm', 'M' } },
- { 51, { ',', '<' } },
- { 52, { '.', '>' } },
- { 53, { '/', '?' } },
- { 55, { '*', '*' } },
- { 57, { ' ', ' ' } },
- { 71, { '7', '7' } },
- { 72, { '8', '8' } },
- { 73, { '9', '9' } },
- { 74, { '-', '-' } },
- { 75, { '4', '4' } },
- { 76, { '5', '5' } },
- { 77, { '6', '6' } },
- { 78, { '+', '+' } },
- { 79, { '1', '1' } },
- { 80, { '2', '2' } },
- { 81, { '3', '3' } },
- { 82, { '0', '0' } },
- { 83, { '.', '.' } },
- { 309, { '/', '/' } }
- };
- const auto& it = ScanCodeToASCIICharMap.find(scanCode);
- if (it == ScanCodeToASCIICharMap.end())
- {
- return '\0';
- }
- const bool shiftOrCapsLockActive = (activeModifiers & ModifierMask_Shift) ||
- (activeModifiers & ModifierMask_CapsLock);
- return shiftOrCapsLockActive ? it->second.second : it->second.first;
- }
- AZStd::unique_ptr<AzFramework::InputDeviceKeyboard::Implementation> InputDeviceKeyboardBarrierImplFactory::Create(AzFramework::InputDeviceKeyboard& inputDevice)
- {
- return AZStd::make_unique<InputDeviceKeyboardBarrier>(inputDevice);
- }
- } // namespace BarrierInput
|