[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 <burelc@amazon.com>
This commit is contained in:
Chris Burel
2021-10-04 15:54:50 -07:00
parent b984335b30
commit ee5ab1770b
2 changed files with 89 additions and 19 deletions
@@ -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<xcb_xkb_use_extension_reply_t> 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<xcb_generic_error_t> 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<xcb_key_press_event_t*>(event);
const auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(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<xcb_key_release_event_t*>(event);
const auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
if (key)
{
QueueRawKeyEvent(*key, false);
}
break;
}
else if (responseType == m_xkbEventCode)
{
const auto* xkbEvent = reinterpret_cast<XcbXkbGenericEventT*>(event);
switch (xkbEvent->xkbType)
{
case XCB_XKB_STATE_NOTIFY:
{
const auto* stateNotifyEvent = reinterpret_cast<xcb_xkb_state_notify_event_t*>(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
@@ -13,6 +13,8 @@
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
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<xkb_context, xkb_context_unref> m_xkbContext;
XcbUniquePtr<xkb_keymap, xkb_keymap_unref> m_xkbKeymap;
XcbUniquePtr<xkb_state, xkb_state_unref> m_xkbState;
int m_coreDeviceId{-1};
uint8_t m_xkbEventCode{0};
bool m_initialized{false};
};
} // namespace AzFramework