summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGatis Paeglis <gatis.paeglis@digia.com>2013-04-16 20:24:45 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-05-07 22:43:14 +0200
commit43578e1901ab56e6df3e58db21e3e52a28cd9eae (patch)
treecbf7fd9ecedf6f3dd221af7b9adef601a716b621 /src
parent260bfaf056c08330ec2ed292e8d9def550e662ec (diff)
Use X11 core protocol when xkb not available
To have a properly working key input in the xcb plugin in the case when xcb-xkb library is not available we can update the xkb_state struct with the keyboard state information available in the X11 core events. The current modifier state is reported to clients in a number of core protocol events and can be determined using the QueryPointer request. This is how it is done in Weston, Wayland's reference implementation. Note: In case the X server doesn't have a xkb support on it (which is very unlikely), then xkbcommon will only pick up the user's primary layout. The X server with the xkb support stuffs unused bits (13 and 14) of 'state' in the core events with the effective keyboard group, which we can use to determine layout changes. Change-Id: I9f1ef635109870e7412ef1157ca592f3c8f9271c Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp34
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp172
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h44
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro6
4 files changed, 236 insertions, 20 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 34508260d9..7381c8c886 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -299,7 +299,10 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
}
xcb_extension_t *extensions[] = {
- &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id, &xcb_xkb_id,
+ &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id,
+#ifndef QT_NO_XKB
+ &xcb_xkb_id,
+#endif
#ifdef XCB_USE_RENDER
&xcb_render_id,
#endif
@@ -747,6 +750,7 @@ void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev)
m_buttons &= ~translateMouseButton(event->detail);
}
+#ifndef QT_NO_XKB
namespace {
typedef union {
/* All XKB events share these fields. */
@@ -761,6 +765,7 @@ namespace {
xcb_xkb_state_notify_event_t state_notify;
} _xkb_event;
}
+#endif
void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
{
@@ -786,12 +791,21 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_EXPOSE:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
case XCB_BUTTON_PRESS:
+#ifdef QT_NO_XKB
+ m_keyboard->updateXKBStateFromCore(((xcb_button_press_event_t *)event)->state);
+#endif
handleButtonPress(event);
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
case XCB_BUTTON_RELEASE:
+#ifdef QT_NO_XKB
+ m_keyboard->updateXKBStateFromCore(((xcb_button_release_event_t *)event)->state);
+#endif
handleButtonRelease(event);
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
case XCB_MOTION_NOTIFY:
+#ifdef QT_NO_XKB
+ m_keyboard->updateXKBStateFromCore(((xcb_motion_notify_event_t *)event)->state);
+#endif
HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
case XCB_CONFIGURE_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
@@ -805,15 +819,29 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_ENTER_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
case XCB_LEAVE_NOTIFY:
+#ifdef QT_NO_XKB
+ m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state);
+#endif
HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
case XCB_FOCUS_IN:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent);
case XCB_FOCUS_OUT:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent);
case XCB_KEY_PRESS:
+#ifdef QT_NO_XKB
+ m_keyboard->updateXKBStateFromCore(((xcb_key_press_event_t *)event)->state);
+#endif
HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent);
case XCB_KEY_RELEASE:
+#ifdef QT_NO_XKB
+ m_keyboard->updateXKBStateFromCore(((xcb_key_release_event_t *)event)->state);
+#endif
HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent);
+#ifdef QT_NO_XKB
+ case XCB_MAPPING_NOTIFY:
+ m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event);
+ break;
+#endif
case XCB_SELECTION_REQUEST:
{
xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event;
@@ -876,6 +904,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
}
}
handled = true;
+#ifndef QT_NO_XKB
} else if (response_type == xkb_first_event) { // https://bugs.freedesktop.org/show_bug.cgi?id=51295
_xkb_event *xkb_event = reinterpret_cast<_xkb_event *>(event);
if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) {
@@ -892,6 +921,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
break;
}
}
+#endif
}
}
@@ -1574,6 +1604,7 @@ void QXcbConnection::initializeXShape()
void QXcbConnection::initializeXKB()
{
+#ifndef QT_NO_XKB
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xkb_id);
if (!reply || !reply->present) {
xkb_first_event = 0;
@@ -1629,6 +1660,7 @@ void QXcbConnection::initializeXKB()
qWarning() << "Qt: failed to select notify events from xcb-xkb";
return;
}
+#endif
}
#if defined(XCB_USE_EGL)
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index e7dabd3351..f1b26853a1 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -673,8 +673,9 @@ void QXcbKeyboard::updateKeymap()
xkb_state_unref(xkb_state);
xkb_state = new_state;
} else {
- // get initial state from the X server (and keep it up-to-date at all times)
xkb_state = new_state;
+#ifndef QT_NO_XKB
+ // get initial state from the X server (and keep it up-to-date at all times)
xcb_xkb_get_state_cookie_t state;
xcb_xkb_get_state_reply_t *init_state;
@@ -695,9 +696,13 @@ void QXcbKeyboard::updateKeymap()
init_state->latchedGroup,
init_state->lockedGroup);
free(init_state);
+#else
+ updateXKBMods();
+#endif
}
}
+#ifndef QT_NO_XKB
void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state)
{
if (!m_config)
@@ -715,11 +720,74 @@ void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state)
state->lockedGroup);
if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
- qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
+ //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
}
}
}
+#else
+void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
+{
+ if (!m_config)
+ return;
+
+ quint32 modsDepressed, modsLatched, modsLocked;
+ modsDepressed = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_DEPRESSED);
+ modsLatched = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED);
+ modsLocked = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED);
+
+ quint32 xkbMask = xkbModMask(state);
+ xkb_state_component newState;
+ newState = xkb_state_update_mask(xkb_state,
+ modsDepressed & xkbMask,
+ modsLatched & xkbMask,
+ modsLocked & xkbMask,
+ 0,
+ 0,
+ (state >> 13) & 3); // bits 13 and 14 report the state keyboard group
+
+ if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
+ //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
+ }
+}
+
+quint32 QXcbKeyboard::xkbModMask(quint16 state)
+{
+ quint32 xkb_mask = 0;
+
+ if ((state & XCB_MOD_MASK_SHIFT) && xkb_mods.shift != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.shift);
+ if ((state & XCB_MOD_MASK_LOCK) && xkb_mods.lock != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.lock);
+ if ((state & XCB_MOD_MASK_CONTROL) && xkb_mods.control != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.control);
+ if ((state & XCB_MOD_MASK_1) && xkb_mods.mod1 != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.mod1);
+ if ((state & XCB_MOD_MASK_2) && xkb_mods.mod2 != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.mod2);
+ if ((state & XCB_MOD_MASK_3) && xkb_mods.mod3 != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.mod3);
+ if ((state & XCB_MOD_MASK_4) && xkb_mods.mod4 != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.mod4);
+ if ((state & XCB_MOD_MASK_5) && xkb_mods.mod5 != XKB_MOD_INVALID)
+ xkb_mask |= (1 << xkb_mods.mod5);
+
+ return xkb_mask;
+}
+
+void QXcbKeyboard::updateXKBMods()
+{
+ xkb_mods.shift = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT);
+ xkb_mods.lock = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS);
+ xkb_mods.control = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL);
+ xkb_mods.mod1 = xkb_map_mod_get_index(xkb_keymap, "Mod1");
+ xkb_mods.mod2 = xkb_map_mod_get_index(xkb_keymap, "Mod2");
+ xkb_mods.mod3 = xkb_map_mod_get_index(xkb_keymap, "Mod3");
+ xkb_mods.mod4 = xkb_map_mod_get_index(xkb_keymap, "Mod4");
+ xkb_mods.mod5 = xkb_map_mod_get_index(xkb_keymap, "Mod5");
+}
+#endif
+
QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
{
// turn off the modifier bits which doesn't participate in shortcuts
@@ -846,11 +914,14 @@ QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
, xkb_context(0)
, xkb_keymap(0)
, xkb_state(0)
+#ifndef QT_NO_XKB
, core_device_id(0)
+#endif
{
+ updateKeymap();
+#ifndef QT_NO_XKB
if (connection->hasXKB()) {
- updateKeymap();
updateVModMapping();
updateVModToRModMapping();
@@ -871,6 +942,10 @@ QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
core_device_id = device_id->deviceID;
free(device_id);
}
+#else
+ m_key_symbols = xcb_key_symbols_alloc(xcb_connection());
+ updateModifiers();
+#endif
}
QXcbKeyboard::~QXcbKeyboard()
@@ -881,8 +956,12 @@ QXcbKeyboard::~QXcbKeyboard()
xkb_keymap_unref(xkb_keymap);
if (xkb_context)
xkb_context_unref(xkb_context);
+#ifdef QT_NO_XKB
+ xcb_key_symbols_free(m_key_symbols);
+#endif
}
+#ifndef QT_NO_XKB
void QXcbKeyboard::updateVModMapping()
{
xcb_xkb_get_names_cookie_t names_cookie;
@@ -1004,14 +1083,67 @@ void QXcbKeyboard::updateVModToRModMapping()
rmod_masks.altgr = modmap;
}
-#if 0
- qDebug() << "alt: " << rmod_masks.alt;
- qDebug() << "meta: " << rmod_masks.meta;
- qDebug() << "altgr: " << rmod_masks.altgr;
-#endif
-
free(map_reply);
}
+#else
+void QXcbKeyboard::updateModifiers()
+{
+ // The core protocol does not provide a convenient way to determine the mapping
+ // of modifier bits. Clients must retrieve and search the modifier map to determine
+ // the keycodes bound to each modifier, and then retrieve and search the keyboard
+ // mapping to determine the keysyms bound to the keycodes. They must repeat this
+ // process for all modifiers whenever any part of the modifier mapping is changed.
+ memset(&rmod_masks, 0, sizeof(rmod_masks));
+
+ xcb_generic_error_t *error = 0;
+ xcb_connection_t *conn = xcb_connection();
+ xcb_get_modifier_mapping_cookie_t modMapCookie = xcb_get_modifier_mapping(conn);
+ xcb_get_modifier_mapping_reply_t *modMapReply =
+ xcb_get_modifier_mapping_reply(conn, modMapCookie, &error);
+ if (error) {
+ qWarning("Qt: failed to get modifier mapping");
+ free(error);
+ return;
+ }
+
+ // for Alt and Meta L and R are the same
+ static const xcb_keysym_t symbols[] = {
+ XK_Alt_L, XK_Meta_L, XK_Mode_switch
+ };
+ static const size_t numSymbols = sizeof symbols / sizeof *symbols;
+
+ // Figure out the modifier mapping, ICCCM 6.6
+ xcb_keycode_t* modKeyCodes[numSymbols];
+ for (size_t i = 0; i < numSymbols; ++i)
+ modKeyCodes[i] = xcb_key_symbols_get_keycode(m_key_symbols, symbols[i]);
+
+ xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply);
+ const int w = modMapReply->keycodes_per_modifier;
+ for (size_t i = 0; i < numSymbols; ++i) {
+ for (int bit = 0; bit < 8; ++bit) {
+ uint mask = 1 << bit;
+ for (int x = 0; x < w; ++x) {
+ xcb_keycode_t keyCode = modMap[x + bit * w];
+ xcb_keycode_t *itk = modKeyCodes[i];
+ while (itk && *itk != XCB_NO_SYMBOL)
+ if (*itk++ == keyCode) {
+ uint sym = symbols[i];
+ if ((sym == XK_Alt_L || sym == XK_Alt_R))
+ rmod_masks.alt = mask;
+ if ((sym == XK_Meta_L || sym == XK_Meta_R))
+ rmod_masks.meta = mask;
+ if (sym == XK_Mode_switch)
+ rmod_masks.altgr = mask;
+ }
+ }
+ }
+ }
+
+ for (size_t i = 0; i < numSymbols; ++i)
+ free(modKeyCodes[i]);
+ free(modMapReply);
+}
+#endif
class KeyChecker
{
@@ -1076,8 +1208,17 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod
if (!m_config)
return;
-
+ // It is crucial the order of xkb_state_key_get_one_sym &
+ // xkb_state_update_key operations is not reversed!
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, code);
+#ifdef QT_NO_XKB
+ enum xkb_key_direction direction;
+ if (type == QEvent::KeyPress)
+ direction = XKB_KEY_DOWN;
+ else
+ direction = XKB_KEY_UP;
+ xkb_state_update_key(xkb_state, code, direction);
+#endif
QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
QMetaMethod method;
@@ -1194,13 +1335,20 @@ void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindowEventListener *eventListener,
handleKeyEvent(window->window(), QEvent::KeyRelease, event->detail, event->state, event->time);
}
-void QXcbKeyboard::handleMappingNotifyEvent(const xcb_xkb_map_notify_event_t *)
+void QXcbKeyboard::handleMappingNotifyEvent(const void *event)
{
+ updateKeymap();
+#ifdef QT_NO_XKB
+ void *ev = const_cast<void *>(event);
+ xcb_refresh_keyboard_mapping(m_key_symbols, static_cast<xcb_mapping_notify_event_t *>(ev));
+ updateModifiers();
+#else
+ Q_UNUSED(event)
if (connection()->hasXKB()) {
- updateKeymap();
updateVModMapping();
updateVModToRModMapping();
}
+#endif
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index 9c278eeb8c..af6677c20f 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -44,6 +44,10 @@
#include "qxcbobject.h"
+#ifdef QT_NO_XKB
+#include <xcb/xcb_keysyms.h>
+#endif
+
#include <xkbcommon/xkbcommon.h>
#include <QEvent>
@@ -62,14 +66,21 @@ public:
void handleKeyPressEvent(QXcbWindowEventListener *eventListener, const xcb_key_press_event_t *event);
void handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, const xcb_key_release_event_t *event);
- void handleMappingNotifyEvent(const xcb_xkb_map_notify_event_t *event);
+ void handleMappingNotifyEvent(const void *event);
Qt::KeyboardModifiers translateModifiers(int s) const;
- QList<int> possibleKeys(const QKeyEvent *e) const;
void updateKeymap();
- void updateXKBState(xcb_xkb_state_notify_event_t *state);
+ QList<int> possibleKeys(const QKeyEvent *e) const;
+
+#ifdef QT_NO_XKB
+ void updateXKBStateFromCore(quint16 state);
+ void updateXKBMods();
+ quint32 xkbModMask(quint16 state);
+#else
int coreDeviceId() { return core_device_id; }
+ void updateXKBState(xcb_xkb_state_notify_event_t *state);
+#endif
protected:
void handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time);
@@ -80,10 +91,16 @@ protected:
int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, QString text) const;
void readXKBConfig(struct xkb_rule_names *names);
+
+#ifdef QT_NO_XKB
+ void updateModifiers();
+#else
void updateVModMapping();
void updateVModToRModMapping();
+#endif
private:
+ bool m_config;
xcb_keycode_t m_autorepeat_code;
struct xkb_context *xkb_context;
@@ -96,12 +113,27 @@ private:
uint meta;
};
- _mod_masks vmod_masks;
_mod_masks rmod_masks;
- int core_device_id;
+#ifdef QT_NO_XKB
+ xcb_key_symbols_t *m_key_symbols;
+
+ struct _xkb_mods {
+ xkb_mod_index_t shift;
+ xkb_mod_index_t lock;
+ xkb_mod_index_t control;
+ xkb_mod_index_t mod1;
+ xkb_mod_index_t mod2;
+ xkb_mod_index_t mod3;
+ xkb_mod_index_t mod4;
+ xkb_mod_index_t mod5;
+ };
- bool m_config;
+ _xkb_mods xkb_mods;
+#else
+ _mod_masks vmod_masks;
+ int core_device_id;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro
index e7d8802021..e8d52ebf2f 100644
--- a/src/plugins/platforms/xcb/xcb-plugin.pro
+++ b/src/plugins/platforms/xcb/xcb-plugin.pro
@@ -111,7 +111,11 @@ contains(QT_CONFIG, xcb-qt) {
} else {
LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr
!contains(DEFINES, QT_NO_SHAPE):LIBS += -lxcb-shape
- !contains(DEFINES, QT_NO_XKB):LIBS += -lxcb-xkb
+ contains(DEFINES, QT_NO_XKB) {
+ LIBS += -lxcb-keysyms
+ } else {
+ LIBS += -lxcb-xkb
+ }
}
# libxkbcommon