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 | 
