[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:
+84
-19
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user