diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2017-05-30 20:08:49 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2017-05-30 20:08:49 +0200 |
commit | f018bf4beeb15f346177dcee2b9e1a023627e7c4 (patch) | |
tree | 94aa371f97ff9e6a5b5db9c3804103aa782fe1ae | |
parent | c52b5ad0a8340012fab7982f0c670a46c3e5d93d (diff) |
qxt-mini: finish up X11 shortcuts
It didn't work without XLookupString.
-rw-r--r-- | qxt-mini/qxtglobalshortcut_x11.cpp | 459 | ||||
-rw-r--r-- | qxt-mini/x11-keymap.cpp | 209 | ||||
-rw-r--r-- | qxt-mini/x11-keymap.hpp | 28 |
3 files changed, 491 insertions, 205 deletions
diff --git a/qxt-mini/qxtglobalshortcut_x11.cpp b/qxt-mini/qxtglobalshortcut_x11.cpp index 01dfe8df..b214a3a0 100644 --- a/qxt-mini/qxtglobalshortcut_x11.cpp +++ b/qxt-mini/qxtglobalshortcut_x11.cpp @@ -30,136 +30,154 @@ ** <http://libqxt.org> <foundation@libqxt.org> *****************************************************************************/ +// qt must go first or #error #include <QHash> #include <QMutex> #include <QDebug> #include <QPair> #include <QKeyEvent> #include <QApplication> +#include "qplatformnativeinterface.h" + +#include "x11-keymap.hpp" #include <X11/Xlib.h> #include <X11/XKBlib.h> #include <xcb/xcb.h> -#include "qplatformnativeinterface.h" #include "compat/util.hpp" #include <iterator> -#include "x11-keymap.hpp" +#include <vector> +#include <type_traits> +#include <utility> +#include <cinttypes> +#include <array> + +template<typename t, int M, typename size_type_ = std::uintptr_t> +struct powerset final +{ + static_assert(std::is_integral<size_type_>::value, ""); -static constexpr quint32 AllMods = ShiftMask|LockMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask; + using size_type = size_type_; -static constexpr quint32 evil_mods[] = { -#if 0 - LockMask, // caps lock - Mod3Mask, // scroll lock -#endif - Mod2Mask, // num lock - Mod5Mask, // altgr + static_assert(M > 0, ""); + static_assert(M < sizeof(size_type[8]), ""); + static_assert(std::is_unsigned<size_type>::value || M < sizeof(size_type)*8 - 1, ""); - Mod2Mask | Mod5Mask, -}; + using N = std::integral_constant<size_type, (size_type(1) << size_type(M))-1>; + static_assert((N::value & (N::value + 1)) == 0, ""); -typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event); + using set_type = std::vector<t>; + using sets_type = std::array<set_type, N::value>; + using element_type = t; + using element_count = std::integral_constant<size_type, N::value>; + using self_type = powerset<t, M>; -using pair = QPair<quint32, quint32>; + operator const sets_type&() const { return sets_; } + operator sets_type&() { return sets_; } -struct keybinding final -{ - quint32 code, mods; - int refcnt; + const sets_type& sets() const { return sets_; } + sets_type& sets() { return sets_; } - static QHash<pair, keybinding> list; - static QMutex lock; + set_type& operator[](unsigned k) { return sets_[k]; } + const set_type& operator[](unsigned k) const { return sets_[k]; } - static bool incf(quint32 code, quint32 mods); - static bool decf(quint32 code, quint32 mods); + const set_type& elements() const { return elements_; } + set_type& elements() { return elements_; } - ~keybinding(); + template<typename = void> + operator QString() const + { + QString str; + unsigned k = 0; + for (const auto& set : sets_) + { + str.append(QStringLiteral("#%1: ").arg(++k)); + for (const auto& x : set) + str.append(QStringLiteral("%1 ").arg(x)); + str.append('\n'); + } + return str.mid(0, str.size() - 1); + } + + powerset() {} private: - keybinding(quint32 code, quint32 mods); + sets_type sets_; + set_type elements_; }; -bool operator==(const keybinding& k1, const keybinding& k2) +template<typename t, typename... xs> +static auto +make_powerset(const t& arg, const xs&... args) { - return k1.code == k2.code && k1.mods == k2.mods; -} + using cnt = std::integral_constant<std::uintptr_t, sizeof...(xs)+1>; + using p = powerset<t, cnt::value>; + using len = typename p::element_count; + using vec = typename p::set_type; + using size_type = typename p::size_type; -inline bool operator!=(const keybinding& k1, const keybinding& k2) -{ - return !(k1 == k2); -} - -uint qHash(const keybinding& k) -{ - return uint(k.code * 41) ^ qHash(k.mods); -} - -uint qHash(const keybinding& k, uint seed) -{ - return qHash(uint(k.code * 41) ^ qHash(k.mods), seed); -} - -keybinding::keybinding(quint32 code, quint32 mods) : - code(code), mods(mods), - refcnt(0) -{ -} + p ret; + vec v; + v.reserve(len()); -keybinding::~keybinding() -{ -} + const typename p::set_type ts {{arg, static_cast<t>(args)...}}; -bool keybinding::incf(quint32 code, quint32 mods) -{ - QMutexLocker l(&lock); - - keybinding k = list.value(pair(code, mods), keybinding(code, mods)); + ret.elements() = std::vector<t>(std::begin(ts), std::end(ts)); - const bool ret = k.refcnt == 0; - - if (ret) + // no nullary set + for (size_type i = 0; i < len(); i++) { - //qDebug() << "qxt-mini: registered keybinding" << code; - } - - k.refcnt++; - list.insert(pair(code, mods), k); + v.clear(); + size_type k = 1; + for (const t& x : ts) + { + if ((i+1) & k) + v.push_back(std::move(x)); + k <<= 1; + } - //qDebug() << "qxt-mini: incf: refcount for" << code << "now" << k.refcnt; + ret[i] = vec(std::begin(v), std::end(v)); + ret[i].shrink_to_fit(); + } return ret; } -bool keybinding::decf(quint32 code, quint32 mods) -{ - QMutexLocker l(&lock); +static auto evil_mods = make_powerset(LockMask, Mod3Mask, Mod5Mask); - auto it = list.find(pair(code, mods)); +static Qt::KeyboardModifiers evil_qt_mods = Qt::KeypadModifier; - if (it == list.end()) +static inline quint32 filter_evil_mods(quint32 mods) +{ + for (auto mod : evil_mods.elements()) { - qWarning() << "qxt-mini: spurious keybinding decf on" << code; - return false; + mods &= quint32(~mod); } + return mods; +} - keybinding& k = *it; - k.refcnt--; +using pair = QPair<quint32, quint32>; - if (k.refcnt == 0) - { - list.erase(it); - //qDebug() << "qxt-mini: removed keybinding" << code; - return true; - } +struct keybinding final +{ + quint32 code, mods; + int refcnt; - //qDebug() << "qxt-mini: decf: refcount for" << code << "now" << k.refcnt; + static QHash<pair, keybinding> list; + static QMutex lock; - return false; -} + static bool incf(quint32 code, quint32 mods); + static bool decf(quint32 code, quint32 mods); -QHash<pair, keybinding> keybinding::list; -QMutex keybinding::lock; + ~keybinding(); + +private: + keybinding(quint32 code, quint32 mods); +}; + +static std::vector<pair> native_key(Qt::Key key, Qt::KeyboardModifiers modifiers); +typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event); class QxtX11ErrorHandler { public: @@ -197,8 +215,6 @@ private: X11ErrorHandler m_previousErrorHandler; }; -bool QxtX11ErrorHandler::error = false; - class QxtX11Data { public: QxtX11Data() @@ -225,95 +241,203 @@ public: return DefaultRootWindow(display()); } - static constexpr quint32 filter_evil_mods(quint32 mods) + bool grabKey(quint32 code, quint32 mods) { - for (quint32 mod : evil_mods) - mods &= ~mod; - return mods; - } + const std::vector<pair> keycodes = native_key(Qt::Key(code), Qt::KeyboardModifiers(mods)); + bool ret = true; - bool grabKey(quint32 code, quint32 mods, Window window) - { - // qDebug() << "grabbing key" << code << mods; + for (pair x : keycodes) + { + int native_code = x.first, native_mods = x.second; - mods = filter_evil_mods(mods); + if (keybinding::incf(native_code, native_mods)) + { + QxtX11ErrorHandler errorHandler; - // qDebug() << "mods now" << mods; + XGrabKey(display(), native_code, native_mods, rootWindow(), True, GrabModeAsync, GrabModeAsync); - if (keybinding::incf(code, mods)) - { - QxtX11ErrorHandler errorHandler; - - XGrabKey(display(), code, mods, window, True, GrabModeAsync, GrabModeAsync); + for (const auto& set : evil_mods.sets()) + { + quint32 m = native_mods; - for (quint32 evil : evil_mods) - { - quint32 m = mods | evil; + for (auto value : set) + m |= value; - XGrabKey(display(), code, m, window, True, GrabModeAsync, GrabModeAsync); - } + XGrabKey(display(), native_code, m, rootWindow(), True, GrabModeAsync, GrabModeAsync); + } - if (errorHandler.error) - { - qDebug() << "qxt-mini: error while binding to" << code << mods; - ungrabKey(code, mods, window); - return false; + if (errorHandler.error) + { + qDebug() << "qxt-mini: error while binding to" << code << mods; + ungrabKey(code, mods); + ret = false; + } } } - return true; + return ret; } - bool ungrabKey(quint32 code, quint32 mods, Window window) + bool ungrabKey(quint32 code, quint32 mods) { - mods = filter_evil_mods(mods); + const std::vector<pair> keycodes = native_key(Qt::Key(code), Qt::KeyboardModifiers(mods)); + bool ret = true; - if (keybinding::decf(code, mods)) + for (pair x : keycodes) { - QxtX11ErrorHandler errorHandler; - XUngrabKey(display(), code, mods, window); + int native_code = x.first, native_mods = x.second; - for (quint32 evil : evil_mods) + if (keybinding::decf(native_code, native_mods)) { - quint32 m = mods | evil; - XUngrabKey(display(), code, m, window); - } + QxtX11ErrorHandler errorHandler; + XUngrabKey(display(), native_code, native_mods, rootWindow()); - if (errorHandler.error) - { - qDebug() << "qxt-mini: error while unbinding" << code << mods; - return false; + for (const auto& set : evil_mods.sets()) + { + quint32 m = mods; + + for (auto value : set) + m |= value; + + XUngrabKey(display(), code, m, rootWindow()); + } + + if (errorHandler.error) + { + qDebug() << "qxt-mini: error while unbinding" << code << mods; + ret = false; + } } } - return true; + return ret; } private: Display *m_display; }; +static std::vector<pair> native_key(Qt::Key key, Qt::KeyboardModifiers modifiers) +{ + std::vector<pair> ret; + + QxtX11Data x11; + if (!x11.isValid()) + return ret; + + std::vector<quint32> keycodes = qt_key_to_x11(x11.display(), key, modifiers); + unsigned mods = qt_mods_to_x11(modifiers); + mods = filter_evil_mods(mods); + + for (quint32 code : keycodes) + ret.push_back(pair(code, mods)); + + return ret; +} + +bool operator==(const keybinding& k1, const keybinding& k2) +{ + return k1.code == k2.code && k1.mods == k2.mods; +} + +inline bool operator!=(const keybinding& k1, const keybinding& k2) +{ + return !(k1 == k2); +} + +uint qHash(const keybinding& k) +{ + return uint(k.code * 41) ^ qHash(k.mods); +} + +uint qHash(const keybinding& k, uint seed) +{ + return qHash(uint(k.code * 41) ^ qHash(k.mods), seed); +} + +keybinding::keybinding(quint32 code, quint32 mods) : + code(code), mods(mods), + refcnt(0) +{ +} + +keybinding::~keybinding() +{ +} + +bool keybinding::incf(quint32 code, quint32 mods) +{ + QMutexLocker l(&lock); + + keybinding k = list.value(pair(code, mods), keybinding(code, mods)); + + const bool ret = k.refcnt == 0; + + if (ret) + { + //qDebug() << "qxt-mini: registered keybinding" << code; + } + + k.refcnt++; + list.insert(pair(code, mods), k); + + //qDebug() << "qxt-mini: incf: refcount for" << code << "now" << k.refcnt; + + return ret; +} + +bool keybinding::decf(quint32 code, quint32 mods) +{ + QMutexLocker l(&lock); + + auto it = list.find(pair(code, mods)); + + if (it == list.end()) + { + qWarning() << "qxt-mini: spurious keybinding decf on" << code; + return false; + } + + keybinding& k = *it; + k.refcnt--; + + if (k.refcnt == 0) + { + list.erase(it); + //qDebug() << "qxt-mini: removed keybinding" << code; + return true; + } + + //qDebug() << "qxt-mini: decf: refcount for" << code << "now" << k.refcnt; + + return false; +} + +QHash<pair, keybinding> keybinding::list; +QMutex keybinding::lock; + +bool QxtX11ErrorHandler::error = false; + bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, - void *message, long *result) + void *message, long *) { - Q_UNUSED(result); + QxtX11Data x11; + + if (!x11.isValid()) + return false; { static bool once_ = false; if (!once_) { - QxtX11Data x11; - if (x11.isValid()) - { - once_ = true; - Bool val; + once_ = true; + Bool val = False; - (void) XkbSetDetectableAutoRepeat(x11.display(), True, &val); + (void) XkbSetDetectableAutoRepeat(x11.display(), True, &val); - if (val) - qDebug() << "qxt-mini: fixed x11 autorepeat"; - else - qDebug() << "qxt-mini: can't fix x11 autorepeat"; - } + if (val) + qDebug() << "qxt-mini: fixed x11 autorepeat"; + else + qDebug() << "qxt-mini: can't fix x11 autorepeat"; } } @@ -357,6 +481,10 @@ bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, #endif unsigned int keycode = kev->detail; + + if (keycode == 0) + return false; + unsigned int keystate = 0; if(kev->state & XCB_MOD_MASK_1) // alt keystate |= Mod1Mask; @@ -366,64 +494,43 @@ bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, keystate |= Mod4Mask; if(kev->state & XCB_MOD_MASK_SHIFT) //shift keystate |= ShiftMask; -#if 0 - if(key->state & XCB_MOD_MASK_3) // alt gr aka right-alt or ctrl+left-alt -- what mask is it? - keystate |= AltGrMask; -#endif + if(kev->state & XCB_MOD_MASK_2) // numlock + keystate |= Mod2Mask; - // qDebug() << "qxt-mini:" << (is_release ? "keyup" : "keydown") << keycode << keystate; + keystate = filter_evil_mods(keystate); - activateShortcut(keycode, keystate, !is_release); + QPair<KeySym, KeySym> sym_ = keycode_to_keysym(x11.display(), keycode, keystate, kev); + KeySym sym = sym_.first; + + Qt::Key k; Qt::KeyboardModifiers mods; + std::tie(k, mods) = x11_key_to_qt(x11.display(), sym, keystate); + + if (k != 0) + activateShortcut(k, mods, !is_release); } return false; } quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { - quint32 native = 0; - if (modifiers & Qt::AltModifier) - native |= Mod1Mask; - if (modifiers & Qt::ControlModifier) - native |= ControlMask; - if (modifiers & Qt::MetaModifier) - native |= Mod4Mask; - if (modifiers & Qt::ShiftModifier) - native |= ShiftMask; - if (modifiers & Qt::KeypadModifier) - native |= Mod2Mask; - if (modifiers & Qt::MetaModifier) // Super aka Windows key - native |= Mod4Mask; - if (modifiers & Qt::KeypadModifier) // numlock - native |= Mod2Mask; - - native &= AllMods; - - return native; + modifiers &= ~evil_qt_mods; + return quint32(modifiers); } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) { - QxtX11Data x11; - - if (x11.isValid()) - return qt_key_to_x11(x11.display(), key); - - return unsigned(-1); + return quint32(key); } bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { QxtX11Data x11; - if (nativeKey == unsigned(-1)) - return false; - return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow()); + return x11.isValid() && x11.grabKey(nativeKey, nativeMods); } bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { QxtX11Data x11; - if (nativeKey == unsigned(-1)) - return false; - return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow()); + return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods); } #endif diff --git a/qxt-mini/x11-keymap.cpp b/qxt-mini/x11-keymap.cpp index b4438a3c..16e55368 100644 --- a/qxt-mini/x11-keymap.cpp +++ b/qxt-mini/x11-keymap.cpp @@ -1,16 +1,21 @@ #include "x11-keymap.hpp" -#ifndef __APPLE__ +#if !defined __APPLE__ && !defined _WIN32 -#include <tuple> +#include <QMutex> #define XK_MISCELLANY #define XK_LATIN1 + #include <X11/keysymdef.h> -using tt = std::tuple<Qt::Key, unsigned>; -static tt keymap[] = +struct tt { + Qt::Key qt; + quint32 keysym; +}; + +static const tt keymap[] = { { Qt::Key_Return, XK_Return }, { Qt::Key_Enter, XK_Return }, @@ -18,6 +23,7 @@ static tt keymap[] = { Qt::Key_Pause, XK_Pause }, { Qt::Key_SysReq, XK_Sys_Req }, { Qt::Key_Home, XK_Home }, + { Qt::Key_Insert, XK_Insert }, { Qt::Key_End, XK_End }, { Qt::Key_Left, XK_Left }, { Qt::Key_Up, XK_Up }, @@ -25,6 +31,7 @@ static tt keymap[] = { Qt::Key_Down, XK_Down }, { Qt::Key_PageUp, XK_Prior }, { Qt::Key_PageDown, XK_Next }, + { Qt::Key_Tab, XK_Tab }, { Qt::Key_F1, XK_F1 }, { Qt::Key_F2, XK_F2 }, @@ -40,24 +47,41 @@ static tt keymap[] = { Qt::Key_F12, XK_F12 }, { Qt::Key_Space, XK_space }, - { Qt::Key_Exclam, XK_exclam }, { Qt::Key_QuoteDbl, XK_quotedbl }, + +#if 1 + { Qt::Key_Exclam, XK_exclam }, + { Qt::Key_At, XK_at }, { Qt::Key_NumberSign, XK_numbersign }, { Qt::Key_Dollar, XK_dollar }, { Qt::Key_Percent, XK_percent }, + { Qt::Key_AsciiCircum, XK_asciicircum }, { Qt::Key_Ampersand, XK_ampersand }, - { Qt::Key_Apostrophe, XK_apostrophe }, + { Qt::Key_Asterisk, XK_asterisk }, { Qt::Key_ParenLeft, XK_parenleft }, { Qt::Key_ParenRight, XK_parenright }, - { Qt::Key_Asterisk, XK_asterisk }, +#else + { Qt::Key_Exclam, XK_1 }, + { Qt::Key_At, XK_2 }, + { Qt::Key_NumberSign, XK_3 }, + { Qt::Key_Dollar, XK_4 }, + { Qt::Key_Percent, XK_5 }, + { Qt::Key_AsciiCircum, XK_6 }, + { Qt::Key_Ampersand, XK_7 }, + { Qt::Key_Asterisk, XK_8 }, + { Qt::Key_ParenLeft, XK_9 }, + { Qt::Key_ParenRight, XK_0 }, +#endif + { Qt::Key_Minus, XK_minus }, + { Qt::Key_Equal, XK_equal }, + { Qt::Key_Apostrophe, XK_apostrophe }, { Qt::Key_Plus, XK_plus }, { Qt::Key_Comma, XK_comma }, - { Qt::Key_Minus, XK_minus }, { Qt::Key_Period, XK_period }, { Qt::Key_Slash, XK_slash }, { Qt::Key_0, XK_0 }, - { Qt::Key_1, XK_1}, + { Qt::Key_1, XK_1 }, { Qt::Key_2, XK_2 }, { Qt::Key_3, XK_3 }, { Qt::Key_4, XK_4 }, @@ -70,10 +94,8 @@ static tt keymap[] = { Qt::Key_Colon, XK_colon }, { Qt::Key_Semicolon, XK_semicolon }, { Qt::Key_Less, XK_less }, - { Qt::Key_Equal, XK_equal }, { Qt::Key_Greater, XK_greater }, { Qt::Key_Question, XK_question }, - { Qt::Key_At, XK_at }, { Qt::Key_A, XK_a }, { Qt::Key_B, XK_b }, @@ -105,32 +127,171 @@ static tt keymap[] = { Qt::Key_BracketLeft, XK_bracketleft }, { Qt::Key_Backslash, XK_backslash }, { Qt::Key_BracketRight, XK_bracketright }, - { Qt::Key_AsciiCircum, XK_asciicircum }, { Qt::Key_Underscore, XK_underscore }, { Qt::Key_QuoteLeft, XK_grave }, +#if 0 +}; +static tt numpad_keymap[] = { +#endif + { Qt::Key_0, XK_KP_0 }, + { Qt::Key_1, XK_KP_1 }, + { Qt::Key_2, XK_KP_2 }, + { Qt::Key_3, XK_KP_3 }, + { Qt::Key_4, XK_KP_4 }, + { Qt::Key_5, XK_KP_5 }, + { Qt::Key_6, XK_KP_6 }, + { Qt::Key_7, XK_KP_7 }, + { Qt::Key_8, XK_KP_8 }, + { Qt::Key_9, XK_KP_9 }, + { Qt::Key_5, XK_KP_Begin }, + { Qt::Key_Space, XK_KP_Space }, + { Qt::Key_Tab, XK_KP_Tab }, + { Qt::Key_F1, XK_KP_F1 }, + { Qt::Key_F2, XK_KP_F2 }, + { Qt::Key_F3, XK_KP_F3 }, + { Qt::Key_F4, XK_KP_F4 }, + { Qt::Key_Home, XK_KP_Home }, + { Qt::Key_End, XK_KP_End }, + { Qt::Key_Left, XK_KP_Left }, + { Qt::Key_Right, XK_KP_Right }, + { Qt::Key_Up, XK_KP_Up }, + { Qt::Key_Down, XK_KP_Down }, + { Qt::Key_PageUp, XK_KP_Page_Up }, + { Qt::Key_PageUp, XK_KP_Prior }, + { Qt::Key_PageDown, XK_KP_Page_Down }, + { Qt::Key_PageDown, XK_KP_Next }, + { Qt::Key_Insert, XK_KP_Insert }, + { Qt::Key_Delete, XK_KP_Delete }, + { Qt::Key_Equal, XK_KP_Equal }, + { Qt::Key_Asterisk, XK_KP_Multiply }, + { Qt::Key_Plus, XK_KP_Add }, + { Qt::Key_Comma, XK_KP_Separator }, + { Qt::Key_Minus, XK_KP_Subtract }, + { Qt::Key_Period, XK_KP_Decimal }, + { Qt::Key_Slash, XK_KP_Divide }, }; -unsigned qt_key_to_x11(Display* disp, Qt::Key k) +QXT_GUI_EXPORT +quint32 qt_mods_to_x11(Qt::KeyboardModifiers modifiers) { - Qt::Key k_; - unsigned keysym; + quint32 mods = 0; + + if (modifiers & Qt::AltModifier) + mods |= Mod1Mask; + if (modifiers & Qt::ControlModifier) + mods |= ControlMask; + if (modifiers & Qt::ShiftModifier) + mods |= ShiftMask; + if (modifiers & Qt::KeypadModifier) + mods |= Mod2Mask; + if (modifiers & Qt::MetaModifier) // Super aka Windows key + mods |= Mod4Mask; + + return mods; +} + +QXT_GUI_EXPORT +std::vector<quint32> qt_key_to_x11(Display*, Qt::Key k, Qt::KeyboardModifiers) +{ + std::vector<quint32> ret; for (const tt& tuple : keymap) { - std::tie(k_, keysym) = tuple; + Qt::Key k_ = tuple.qt; + unsigned keycode = tuple.keysym; if (k == k_) - { - const unsigned ret = XKeysymToKeycode(disp, keysym); + ret.push_back(keycode); + } - if (ret == 0) - return unsigned(-1); + if (ret.size() == 0) + qDebug() << "qxt-mini: no keysym for" << k; - return ret; - } + return ret; +} +QXT_GUI_EXPORT +static Qt::KeyboardModifiers x11_mods_to_qt(quint32 mods) +{ + Qt::KeyboardModifiers ret(0); + + if (mods & Mod1Mask) + ret |= Qt::AltModifier; + if (mods & ControlMask) + ret |= Qt::ControlModifier; + if (mods & Mod4Mask) + ret |= Qt::MetaModifier; + if (mods & ShiftMask) + ret |= Qt::ShiftModifier; + + return ret; +} + +QXT_GUI_EXPORT +std::tuple<Qt::Key, Qt::KeyboardModifiers> x11_key_to_qt(Display* disp, quint32 keycode, quint32 mods) +{ + (void)disp; + using t = std::tuple<Qt::Key, Qt::KeyboardModifiers>; + + for (const tt& tuple : keymap) + { + const Qt::Key k = tuple.qt; + + if (keycode == tuple.keysym) + return t(k, x11_mods_to_qt(mods)); } - qDebug() << "qxt-mini: no keysym for" << k; - return unsigned(-1); + return t(Qt::Key(0), Qt::KeyboardModifiers(0)); +} + + +QXT_GUI_EXPORT +QPair<KeySym, KeySym> keycode_to_keysym(Display* disp, + quint32 keycode, quint32 keystate, + xcb_key_press_event_t const* kev) +{ + using pair = QPair<quint32, quint32>; + + static QMutex lock; + static QHash<pair, QPair<KeySym, KeySym>> values; + + QMutexLocker l(&lock); + + auto it = values.find(pair(keycode, keystate)); + + if (it != values.end()) + return *it; + + XKeyEvent ev{}; + ev.serial = kev->sequence; + ev.send_event = False; + ev.display = disp; + ev.window = kev->root; + ev.subwindow = kev->child; + ev.window = kev->event; + ev.time = kev->time; + ev.x = kev->event_x; + ev.y = kev->event_x; + ev.x_root = kev->root_x; + ev.y_root = kev->root_y; + ev.state = keystate; + ev.keycode = keycode; + ev.same_screen = kev->same_screen; + + static char bytes[255+1]; + KeySym sym = 0; + int len = XLookupString(&ev, bytes, 255, &sym, NULL); + if (len <= 0 || len > 255) + { + len = 0; + sym = 0; + } + + KeySym sym2 = XLookupKeysym(&ev, 0); + + QPair<KeySym, KeySym> ret(sym, sym2); + + values[pair(keycode, keystate)] = ret; + + return ret; } #endif diff --git a/qxt-mini/x11-keymap.hpp b/qxt-mini/x11-keymap.hpp index b35097bc..2c737c77 100644 --- a/qxt-mini/x11-keymap.hpp +++ b/qxt-mini/x11-keymap.hpp @@ -1,15 +1,33 @@ #pragma once -#ifndef BUILD_QXT_MINI -# error "internal header" -#endif +#if !defined __APPLE__ && !defined _WIN32 -#ifndef __APPLE__ +#undef QXT_X11_INCLUDED +#define QXT_X11_INCLUDED #include <Qt> #include <QDebug> +#include <QHash> +#include <QPair> + +#include <vector> +#include <tuple> + +#include <xcb/xcb.h> #include <X11/Xlib.h> +#include <X11/Xutil.h> + +QXT_GUI_EXPORT +std::vector<quint32> qt_key_to_x11(Display* disp, Qt::Key k, Qt::KeyboardModifiers m); + +QXT_GUI_EXPORT +std::tuple<Qt::Key, Qt::KeyboardModifiers> x11_key_to_qt(Display* disp, quint32 keycode, quint32 mods); + +QXT_GUI_EXPORT +QPair<KeySym, KeySym> keycode_to_keysym(Display* disp, quint32 keycode, quint32 keystate, + xcb_key_press_event_t const* kev); -unsigned qt_key_to_x11(Display* disp, Qt::Key k); +QXT_GUI_EXPORT +quint32 qt_mods_to_x11(Qt::KeyboardModifiers modifiers); #endif |