diff options
Diffstat (limited to 'tests/manual/x11vkbwrapper/handlekeyevents.cpp')
-rw-r--r-- | tests/manual/x11vkbwrapper/handlekeyevents.cpp | 171 |
1 files changed, 152 insertions, 19 deletions
diff --git a/tests/manual/x11vkbwrapper/handlekeyevents.cpp b/tests/manual/x11vkbwrapper/handlekeyevents.cpp index 0c3c85a4..c6b5c9f3 100644 --- a/tests/manual/x11vkbwrapper/handlekeyevents.cpp +++ b/tests/manual/x11vkbwrapper/handlekeyevents.cpp @@ -39,11 +39,13 @@ extern "C" { #include <xdo.h> #include <X11/extensions/XTest.h> +#include "atspi/atspi.h" } namespace { const bool KKeyPress = true; const bool KKeyRelease = false; +const int NoKeySymFound = 63; // Undefine define KeyRelease from X.h. // Enables using QEvent::KeyRelease type in eventFilter. @@ -104,6 +106,7 @@ HandleKeyEvents::~HandleKeyEvents() bool HandleKeyEvents::init() { + m_temporaryKeyCodes = QHash<Qt::Key, int>(); m_xdo = xdo_new(nullptr); return m_xdo != nullptr; } @@ -117,7 +120,7 @@ bool HandleKeyEvents::init() */ bool HandleKeyEvents::eventFilter(QObject *watched, QEvent *event) { - Q_UNUSED( watched ); + Q_UNUSED( watched ) const auto type = event->type(); if (type == QEvent::MouseButtonRelease && !QGuiApplication::inputMethod()->isVisible()) { @@ -153,12 +156,12 @@ bool HandleKeyEvents::eventFilter(QObject *watched, QEvent *event) 2000); /** a Key strings as Emojis ":-), <3". */ } else if (str.length() > 1) { - xdo_enter_text_window(m_xdo, CURRENTWINDOW, - str.toLatin1(), - 2000); + for (auto sKey : str) { + sendKeyWithAtspi(nullptr, sKey); + } /** a Normal Keys. */ - } else if (!str.isEmpty()) { - keyTap(str); + } else if (key != Qt::Key_Shift) { + sendKeyWithAtspi(keyEvent, QString("")); } } return false; @@ -166,24 +169,67 @@ bool HandleKeyEvents::eventFilter(QObject *watched, QEvent *event) /** * @brief HandleKeyEvents::keyTap + * @param keyEvent a QKeyEvent pointer * @param key a Key as a string */ -void HandleKeyEvents::keyTap(const QString &key) const +void HandleKeyEvents::keyTap(const QKeyEvent *keyEvent, const QString &key) +{ + qCDebug(lcHandleKeyEvents) << Q_FUNC_INFO; + + /** Scratch space for temporary keycode bindings */ + int scratchKeyCode = 0; + KeyCode keyCode = 0x0; + + if (keyEvent) { + auto qtKey = static_cast<Qt::Key>(keyEvent->key()); + if (m_temporaryKeyCodes.contains(qtKey)) { + scratchKeyCode = m_temporaryKeyCodes.value(qtKey); + } else { + scratchKeyCode = getTemporaryKeyCode(); + m_temporaryKeyCodes.insert(qtKey, scratchKeyCode); + } + + /** find the keysym for the given unicode char */ + const QString str = qtKeyCodes2.indexOf(qtKey) >= 0 ? "U"+ QString::number(keyEvent->key(), 16 ) : + "U"+ keyEvent->text().toLatin1().toHex(); + const KeySym sym = XStringToKeysym(str.toUtf8().data()); + if (sym && sym != NoKeySymFound) { + remapScratchKeyCode(sym, scratchKeyCode); + keyCode = static_cast<KeyCode>(scratchKeyCode); + } else { + keyCode = getUnicodeKeyCode(keyEvent->text(), scratchKeyCode); + } + keyClick(keyCode, keyEvent->text()); + } else { + keyCode = getUnicodeKeyCode(key, getTemporaryKeyCode()); + keyClick(keyCode, key); + } + + /** Revert Keymapping */ + remapScratchKeyCode(NoSymbol, scratchKeyCode); +} + +/** + * @brief HandleKeyEvents::keyClick + * @param key + * @param keyText Key as a string + */ +void HandleKeyEvents::keyClick(const KeyCode key, const QString &keyText) const { qCDebug(lcHandleKeyEvents) << Q_FUNC_INFO; - bool shiftDown = false; - KeyCode keyCode = getUnicodeKeycode(key); KeyCode shift = XKeysymToKeycode(m_xdo->xdpy, XK_Shift_L); + bool shiftDown = false; + /** Press a Shift button down if capital letter. */ - if (key.length() == 1 && (key.toLower() != key || xUpKeyCodes.count(key))) { + if (keyText.length() == 1 && (keyText.toLower() != keyText || xUpKeyCodes.count(keyText))) { keyPressRelease(shift, KKeyPress); shiftDown = true; } /** A Key press */ - keyPressRelease(keyCode, KKeyPress); + keyPressRelease(key, KKeyPress); /** A Key release */ - keyPressRelease(keyCode, KKeyRelease); + keyPressRelease(key, KKeyRelease); /** Release a Shift button if capital letter. */ if (shiftDown) { @@ -205,12 +251,12 @@ void HandleKeyEvents::keyPressRelease(const KeyCode key, const bool eventType) c } /** - * @brief HandleKeyEvents::getUnicodeKeycode + * @brief HandleKeyEvents::getUnicodeKeyCode * Get a correct key mapping for a key. * @param key a Key as a string * @return Keycode */ -KeyCode HandleKeyEvents::getUnicodeKeycode(const QString &key) const +KeyCode HandleKeyEvents::getUnicodeKeyCode(const QString &key, int scratchKeyCode) const { qCDebug(lcHandleKeyEvents) << Q_FUNC_INFO; KeyCode code = 0; @@ -221,12 +267,99 @@ KeyCode HandleKeyEvents::getUnicodeKeycode(const QString &key) const auto xUpKeyIter = xKeyCodes.find(key); code = XKeysymToKeycode(m_xdo->xdpy, xUpKeyIter->second); } else { - auto unicodeIter = unicodeKeySymbols.find(key); - quint32 unicodeKey = unicodeIter != unicodeKeySymbols.end() ? unicodeIter->second : 0; - KeySym keysyms[] = {unicodeKey}; - XChangeKeyboardMapping(m_xdo->xdpy, 999, 1, static_cast<KeySym*>(keysyms), 1); - code = static_cast<KeyCode>(999); + const KeySym sym = unicodeKeySymbols.find(key)->second; + remapScratchKeyCode(sym, scratchKeyCode); + code = static_cast<KeyCode>(scratchKeyCode); } return code; } +/** + * @brief HandleKeyEvents::remapScratchKeyCode + * Remap the requested KeySym + * @param sym KeySymbol + * @param scratchKeyCode unused keycode to use for remapping + */ +void HandleKeyEvents::remapScratchKeyCode(const KeySym sym, int scratchKeyCode) const +{ + qCDebug(lcHandleKeyEvents) << Q_FUNC_INFO; + KeySym keysyms[] = {sym, sym}; + /** Remap */ + XChangeKeyboardMapping(m_xdo->xdpy, scratchKeyCode, 2, keysyms, 1); +} + +/** + * @brief HandleKeyEvents::sendKeyWithAtspi + * To send a ordinary keys via atspi(D-Bus). It is faster than a XTestFakeKeyEvent + * @param keyEvent KeyEvent + * @param key Key as string + */ +void HandleKeyEvents::sendKeyWithAtspi(const QKeyEvent *keyEvent, const QString key) +{ + qCDebug(lcHandleKeyEvents) << Q_FUNC_INFO; + const QString str = keyEvent != nullptr ? keyEvent->text() : key; + if (str.isEmpty()) { + return; + } + + const KeySym sym = unicodeKeySymbolsForAtspi.find(str)->second; + if (sym == 0) { + keyTap(keyEvent, QString("")); + } else { + GError *error = nullptr; + if (!atspi_generate_keyboard_event(static_cast<long>(sym), nullptr, ATSPI_KEY_SYM, &error)) { + qCDebug(lcHandleKeyEvents) << "Error message: " << error->message; + } + } +} + +/** + * @brief HandleKeyEvents::getTemporaryKeyCode + * @return + */ +int HandleKeyEvents::getTemporaryKeyCode() +{ + qCDebug(lcHandleKeyEvents) << Q_FUNC_INFO; + KeySym *keySyms = nullptr; + int keySymsPerKeyCode = 0; + + /** Scratch space for temporary keycode bindings */ + int scratchKeyCode = 0; + int keyCodeLow = 0; + int keyCodeHigh = 0; + + /** get the range of keycodes usually from 8 - 255 */ + XDisplayKeycodes(m_xdo->xdpy, &keyCodeLow, &keyCodeHigh); + /** get all the mapped keysyms availabl */ + const KeyCode keyCodeL = static_cast<KeyCode>(keyCodeLow); + keySyms = XGetKeyboardMapping( + m_xdo->xdpy, + keyCodeL, + keyCodeHigh - keyCodeLow, + &keySymsPerKeyCode); + + /** find unused keycode for unmapped keysyms so we can + hook up our own keycode and map every keysym on it + so we just need to 'click' our once unmapped keycode */ + int i; + for (i = keyCodeLow; i <= keyCodeHigh; i++) { + int keyIsEmpty = 1; + for (int j = 0; j < keySymsPerKeyCode; j++) { + const int symindex = (i - keyCodeLow) * keySymsPerKeyCode + j; + if (keySyms[symindex] != 0) { + keyIsEmpty = 0; + } else { + break; + } + } + if (keyIsEmpty) { + scratchKeyCode = i; + break; + } + } + XFree(keySyms); + XFlush(m_xdo->xdpy); + + return scratchKeyCode; +} + |