From ee5ab1770bdd4d6beeae1117a763934affd13617 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Mon, 4 Oct 2021 15:54:50 -0700 Subject: [PATCH] [XCB] Respond to keyboard state changes This allows the keyboard to correctly store modifier keys, as the keyboard state changes. Signed-off-by: Chris Burel --- .../AzFramework/XcbInputDeviceKeyboard.cpp | 103 ++++++++++++++---- .../Xcb/AzFramework/XcbInputDeviceKeyboard.h | 5 + 2 files changed, 89 insertions(+), 19 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp index c971c3b793..d9db239522 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp @@ -20,6 +20,15 @@ namespace AzFramework { + // xcb-xkb does not provide a generic event type, so we define our own. + // These fields are enough to get to the xkbType field, which can then be + // read to typecast the event to the right concrete type. + struct XcbXkbGenericEventT + { + uint8_t response_type; + uint8_t xkbType; + }; + XcbInputDeviceKeyboard::XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice) : InputDeviceKeyboard::Implementation(inputDevice) { @@ -39,19 +48,22 @@ namespace AzFramework return; } - XcbStdFreePtr xkbUseExtensionReply{ - xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr) - }; - if (!xkbUseExtensionReply) + int initializeXkbExtensionSuccess = xkb_x11_setup_xkb_extension( + connection, + 1, + 0, + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + nullptr, + nullptr, + &m_xkbEventCode, + nullptr + ); + + if (!initializeXkbExtensionSuccess) { AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension"); return; } - if (!xkbUseExtensionReply->supported) - { - AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension"); - return; - } m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection); @@ -59,6 +71,43 @@ namespace AzFramework m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)); m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId)); + const uint16_t affectMap = + XCB_XKB_MAP_PART_KEY_TYPES + | XCB_XKB_MAP_PART_KEY_SYMS + | XCB_XKB_MAP_PART_MODIFIER_MAP + | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS + | XCB_XKB_MAP_PART_KEY_ACTIONS + | XCB_XKB_MAP_PART_KEY_BEHAVIORS + | XCB_XKB_MAP_PART_VIRTUAL_MODS + | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP + ; + + const uint16_t selectedEvents = + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY + | XCB_XKB_EVENT_TYPE_MAP_NOTIFY + | XCB_XKB_EVENT_TYPE_STATE_NOTIFY + ; + + XcbStdFreePtr error{xcb_request_check( + connection, + xcb_xkb_select_events( + connection, + /* deviceSpec = */ XCB_XKB_ID_USE_CORE_KBD, + /* affectWhich = */ selectedEvents, + /* clear = */ 0, + /* selectAll = */ selectedEvents, + /* affectMap = */ affectMap, + /* map = */ affectMap, + /* details = */ nullptr + ) + )}; + + if (error) + { + AZ_Warning("ApplicationLinux", false, "failed to select notify events from XKB"); + return; + } + m_initialized = true; } @@ -93,30 +142,38 @@ namespace AzFramework return; } - switch (event->response_type & ~0x80) + const auto responseType = event->response_type & ~0x80; + if (responseType == XCB_KEY_PRESS) { - case XCB_KEY_PRESS: - { - auto* keyPress = reinterpret_cast(event); + const auto* keyPress = reinterpret_cast(event); - const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail); - if (key) + if (const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail)) { QueueRawKeyEvent(*key, true); } - break; } - case XCB_KEY_RELEASE: + else if (responseType == XCB_KEY_RELEASE) { - auto* keyRelease = reinterpret_cast(event); + const auto* keyRelease = reinterpret_cast(event); const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail); if (key) { QueueRawKeyEvent(*key, false); } - break; } + else if (responseType == m_xkbEventCode) + { + const auto* xkbEvent = reinterpret_cast(event); + switch (xkbEvent->xkbType) + { + case XCB_XKB_STATE_NOTIFY: + { + const auto* stateNotifyEvent = reinterpret_cast(event); + UpdateState(stateNotifyEvent); + break; + } + } } } @@ -268,4 +325,12 @@ namespace AzFramework default: return nullptr; } } + + void XcbInputDeviceKeyboard::UpdateState(const xcb_xkb_state_notify_event_t* state) + { + if (m_initialized) + { + xkb_state_update_mask(m_xkbState.get(), state->baseMods, state->latchedMods, state->lockedMods, state->baseGroup, state->latchedGroup, state->lockedGroup); + } + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h index de65941a67..9adc9ad758 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h @@ -13,6 +13,8 @@ #include #include +struct xcb_xkb_state_notify_event_t; + namespace AzFramework { class XcbInputDeviceKeyboard @@ -37,10 +39,13 @@ namespace AzFramework private: [[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const; + void UpdateState(const xcb_xkb_state_notify_event_t* state); + XcbUniquePtr m_xkbContext; XcbUniquePtr m_xkbKeymap; XcbUniquePtr m_xkbState; int m_coreDeviceId{-1}; + uint8_t m_xkbEventCode{0}; bool m_initialized{false}; }; } // namespace AzFramework