diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbkeyboard.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.cpp | 219 |
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(); } |