summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbkeyboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbkeyboard.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp219
1 files changed, 187 insertions, 32 deletions
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 6bbc3c18ca..907dd0f1b6 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -40,16 +40,17 @@
****************************************************************************/
#include "qxcbkeyboard.h"
-
+#include "qxcbwindow.h"
+#include "qxcbscreen.h"
#include <xcb/xcb_keysyms.h>
-
#include <X11/keysym.h>
-
#include <QtGui/QWindowSystemInterface>
#include <QtCore/QTextCodec>
-
+#include <private/qguiapplication_p.h>
#include <stdio.h>
+#include <qplatforminputcontext_qpa.h>
+
#ifndef XK_ISO_Left_Tab
#define XK_ISO_Left_Tab 0xFE20
#endif
@@ -898,12 +899,9 @@ QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers,
QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
: QXcbObject(connection)
- , m_alt_mask(0)
- , m_super_mask(0)
- , m_hyper_mask(0)
- , m_meta_mask(0)
{
m_key_symbols = xcb_key_symbols_alloc(xcb_connection());
+ setupModifiers();
}
QXcbKeyboard::~QXcbKeyboard()
@@ -911,18 +909,113 @@ QXcbKeyboard::~QXcbKeyboard()
xcb_key_symbols_free(m_key_symbols);
}
-// #define XCB_KEYBOARD_DEBUG
+void QXcbKeyboard::setupModifiers()
+{
+ m_alt_mask = 0;
+ m_super_mask = 0;
+ m_hyper_mask = 0;
+ m_meta_mask = 0;
+ m_mode_switch_mask = 0;
+ m_num_lock_mask = 0;
+ m_caps_lock_mask = 0;
+
+ 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("xcb keyboard: failed to get modifier mapping");
+ free(error);
+ return;
+ }
+
+ // Figure out the modifier mapping, ICCCM 6.6
+ typedef QPair<uint, xcb_keycode_t *> SymCodes;
+ QList<SymCodes> modKeyCodes;
+
+ // for Alt and Meta L and R are the same
+ modKeyCodes << SymCodes(XK_Alt_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Alt_L));
+ modKeyCodes << SymCodes(XK_Meta_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Meta_L));
+ modKeyCodes << SymCodes(XK_Super_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Super_L));
+ modKeyCodes << SymCodes(XK_Super_R, xcb_key_symbols_get_keycode(m_key_symbols, XK_Super_R));
+ modKeyCodes << SymCodes(XK_Hyper_L, xcb_key_symbols_get_keycode(m_key_symbols, XK_Hyper_L));
+ modKeyCodes << SymCodes(XK_Hyper_R, xcb_key_symbols_get_keycode(m_key_symbols, XK_Hyper_R));
+ modKeyCodes << SymCodes(XK_Num_Lock, xcb_key_symbols_get_keycode(m_key_symbols, XK_Num_Lock));
+ modKeyCodes << SymCodes(XK_Mode_switch, xcb_key_symbols_get_keycode(m_key_symbols, XK_Mode_switch));
+ modKeyCodes << SymCodes(XK_Caps_Lock, xcb_key_symbols_get_keycode(m_key_symbols, XK_Caps_Lock));
+
+ xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply);
+ const int w = modMapReply->keycodes_per_modifier;
+ for (int i = 0; i < modKeyCodes.count(); ++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.at(i).second;
+ while (itk && *itk != XCB_NO_SYMBOL)
+ if (*itk++ == keyCode)
+ setMask(modKeyCodes.at(i).first, mask);
+ }
+ }
+ }
-void QXcbKeyboard::handleKeyEvent(QWidget *widget, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time)
+ for (int i = 0; i < modKeyCodes.count(); ++i)
+ free(modKeyCodes.at(i).second);
+ free(modMapReply);
+}
+
+void QXcbKeyboard::setMask(uint sym, uint mask)
{
- int col = state & XCB_MOD_MASK_SHIFT ? 1 : 0;
+ if (m_alt_mask == 0
+ && m_meta_mask != mask
+ && m_super_mask != mask
+ && m_hyper_mask != mask
+ && (sym == XK_Alt_L || sym == XK_Alt_R))
+ m_alt_mask = mask;
+
+ if (m_meta_mask == 0
+ && m_alt_mask != mask
+ && m_super_mask != mask
+ && m_hyper_mask != mask
+ && (sym == XK_Meta_L || sym == XK_Meta_R))
+ m_meta_mask = mask;
+
+ if (m_super_mask == 0
+ && m_alt_mask != mask
+ && m_meta_mask != mask
+ && m_hyper_mask != mask
+ && (sym == XK_Super_L || sym == XK_Super_R))
+ m_super_mask = mask;
+
+ if (m_hyper_mask == 0
+ && m_alt_mask != mask
+ && m_meta_mask != mask
+ && m_super_mask != mask
+ && (sym == XK_Hyper_L || sym == XK_Hyper_R))
+ m_hyper_mask = mask;
+
+ if (m_mode_switch_mask == 0
+ && m_alt_mask != mask
+ && m_meta_mask != mask
+ && m_super_mask != mask
+ && m_hyper_mask != mask
+ && sym == XK_Mode_switch)
+ m_mode_switch_mask = mask;
+
+ if (m_num_lock_mask == 0 && sym == XK_Num_Lock)
+ m_num_lock_mask = mask;
+
+ if (m_caps_lock_mask == 0 && sym == XK_Caps_Lock)
+ m_caps_lock_mask = mask;
+}
- const int altGrOffset = 4;
- if (state & 128)
- col += altGrOffset;
+// #define XCB_KEYBOARD_DEBUG
+void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycode_t code,
+ quint16 state, xcb_timestamp_t time)
+{
Q_XCB_NOOP(connection());
-
#ifdef XCB_KEYBOARD_DEBUG
printf("key code: %d, state: %d, syms: ", code, state);
for (int i = 0; i <= 5; ++i) {
@@ -931,43 +1024,105 @@ void QXcbKeyboard::handleKeyEvent(QWidget *widget, QEvent::Type type, xcb_keycod
printf("\n");
#endif
- Q_XCB_NOOP(connection());
+ QByteArray chars;
+ xcb_keysym_t sym = lookupString(window, state, code, type, &chars);
+
+
+ if (QObject* inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext()) {
+ bool retval;
+ QMetaObject::invokeMethod(inputContext, "x11FilterEvent", Qt::DirectConnection,
+ Q_RETURN_ARG(bool, retval),
+ Q_ARG(uint, sym),
+ Q_ARG(uint, code),
+ Q_ARG(uint, state),
+ Q_ARG(bool, type == QEvent::KeyPress));
+ if (retval)
+ return;
+ }
+ Qt::KeyboardModifiers modifiers;
+ int qtcode = 0;
+ int count = chars.count();
+ QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count);
+ QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers,
+ code, 0, state, string.left(count));
+}
+
+#ifdef XCB_USE_XLIB
+extern "C" {
+ int XLookupString(void *event, char *buf, int count, void *keysym, void *comp);
+}
+typedef struct { // must match XKeyEvent in Xlib.h
+ int type;
+ unsigned long serial;
+ int send_event;
+ void *display;
+ unsigned long window;
+ unsigned long root;
+ unsigned long subwindow;
+ unsigned long time;
+ int x, y;
+ int x_root, y_root;
+ unsigned int state;
+ unsigned int keycode;
+ int same_screen;
+} FakeXKeyEvent;
+#endif
+
+xcb_keysym_t QXcbKeyboard::lookupString(QWindow *window, uint state, xcb_keycode_t code,
+ QEvent::Type type, QByteArray *chars)
+{
+#ifdef XCB_USE_XLIB
+
+ xcb_keysym_t sym = XCB_NO_SYMBOL;
+ chars->resize(512);
+ FakeXKeyEvent event;
+ memset(&event, 0, sizeof(event));
+ event.type = (type == QEvent::KeyRelease ? 3 : 2);
+ event.display = connection()->xlib_display();
+ event.window = static_cast<QXcbWindow *>(window->handle())->xcb_window();
+ event.root = connection()->screens().at(0)->root();
+ event.state = state;
+ event.keycode = code;
+ int count = XLookupString(&event, chars->data(), chars->size(), &sym, 0);
+ chars->resize(count);
+ return sym;
+
+#else
+
+ // No XLookupString available. The following is really incomplete...
+
+ int col = state & XCB_MOD_MASK_SHIFT ? 1 : 0;
+ const int altGrOffset = 4;
+ if (state & 128)
+ col += altGrOffset;
xcb_keysym_t sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col);
if (sym == XCB_NO_SYMBOL)
sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col ^ 0x1);
-
if (state & XCB_MOD_MASK_LOCK && sym <= 0x7f && isprint(sym)) {
if (isupper(sym))
sym = tolower(sym);
else
sym = toupper(sym);
}
+ return sym;
- Q_XCB_NOOP(connection());
-
- QByteArray chars;
-
- Qt::KeyboardModifiers modifiers;
- int qtcode = 0;
- int count = 0;
-
- QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count);
-
- QWindowSystemInterface::handleExtendedKeyEvent(widget, time, type, qtcode, modifiers, code, 0, state, string.left(count));
+#endif
}
-void QXcbKeyboard::handleKeyPressEvent(QWidget *widget, const xcb_key_press_event_t *event)
+void QXcbKeyboard::handleKeyPressEvent(QXcbWindow *window, const xcb_key_press_event_t *event)
{
- handleKeyEvent(widget, QEvent::KeyPress, event->detail, event->state, event->time);
+ window->updateNetWmUserTime(event->time);
+ handleKeyEvent(window->window(), QEvent::KeyPress, event->detail, event->state, event->time);
}
-void QXcbKeyboard::handleKeyReleaseEvent(QWidget *widget, const xcb_key_release_event_t *event)
+void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindow *window, const xcb_key_release_event_t *event)
{
- handleKeyEvent(widget, QEvent::KeyRelease, event->detail, event->state, event->time);
+ handleKeyEvent(window->window(), QEvent::KeyRelease, event->detail, event->state, event->time);
}
void QXcbKeyboard::handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event)
{
xcb_refresh_keyboard_mapping(m_key_symbols, const_cast<xcb_mapping_notify_event_t *>(event));
+ setupModifiers();
}