diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2017-02-25 11:27:46 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2017-02-25 14:19:59 +0100 |
commit | 2826e614fb63448bf5801126e985cc53c1e11b8e (patch) | |
tree | 4d530f0fd4a4be846a3d11b623789d053695e0d0 /qxt-mini | |
parent | 6145c2082d3d116962dc96602c9ad2d22d62c675 (diff) |
qxt-mini: refcount identical shortcuts rather than dropping them
Only for Linux. The OSX code should stay intact however.
Diffstat (limited to 'qxt-mini')
-rw-r--r-- | qxt-mini/qxtglobal.h | 1 | ||||
-rw-r--r-- | qxt-mini/qxtglobalshortcut.cpp | 45 | ||||
-rw-r--r-- | qxt-mini/qxtglobalshortcut.h | 2 | ||||
-rw-r--r-- | qxt-mini/qxtglobalshortcut_x11.cpp | 218 |
4 files changed, 201 insertions, 65 deletions
diff --git a/qxt-mini/qxtglobal.h b/qxt-mini/qxtglobal.h index 913da556..6446b0ee 100644 --- a/qxt-mini/qxtglobal.h +++ b/qxt-mini/qxtglobal.h @@ -174,7 +174,6 @@ public: qxt_p_ptr = pub; } -protected: inline PUB& qxt_p() { return *qxt_p_ptr; diff --git a/qxt-mini/qxtglobalshortcut.cpp b/qxt-mini/qxtglobalshortcut.cpp index f0a020ff..bf3ed33d 100644 --- a/qxt-mini/qxtglobalshortcut.cpp +++ b/qxt-mini/qxtglobalshortcut.cpp @@ -32,6 +32,7 @@ #include "qxtglobalshortcut_p.h" #include <QAbstractEventDispatcher> #include <QtDebug> +#include <QApplication> #ifndef Q_OS_MAC int QxtGlobalShortcutPrivate::ref = 0; @@ -59,19 +60,27 @@ QxtGlobalShortcutPrivate::~QxtGlobalShortcutPrivate() } } #endif // Q_OS_MAC + unsetShortcut(); } bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut) { + (void) unsetShortcut(); + if (shortcut.toString() == "") return false; - Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier; - key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((shortcut[0] ^ allMods) & shortcut[0]); + Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier; + key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key(shortcut[0] & ~allMods); mods = shortcut.isEmpty() ? Qt::KeyboardModifiers(0) : Qt::KeyboardModifiers(shortcut[0] & allMods); + const quint32 nativeKey = nativeKeycode(key); const quint32 nativeMods = nativeModifiers(mods); const bool res = registerShortcut(nativeKey, nativeMods); if (res) +#ifndef Q_OS_MAC + shortcuts.insertMulti(qMakePair(nativeKey, nativeMods), &qxt_p()); +#else shortcuts.insert(qMakePair(nativeKey, nativeMods), &qxt_p()); +#endif else qWarning() << "QxtGlobalShortcut failed to register:" << QKeySequence(key + mods).toString(); return res; @@ -79,15 +88,36 @@ bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut) bool QxtGlobalShortcutPrivate::unsetShortcut() { + if (key == Qt::Key(0)) + return true; + bool res = false; const quint32 nativeKey = nativeKeycode(key); const quint32 nativeMods = nativeModifiers(mods); +#ifdef Q_OS_MAC if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == &qxt_p()) res = unregisterShortcut(nativeKey, nativeMods); if (res) shortcuts.remove(qMakePair(nativeKey, nativeMods)); else qWarning() << "QxtGlobalShortcut failed to unregister:" << QKeySequence(key + mods).toString(); +#else + auto list = shortcuts.values(qMakePair(nativeKey, nativeMods)); + bool found = false; + for (auto it = list.begin(); it != list.end(); it++) + { + if (*it == &qxt_p()) + { + found = true; + shortcuts.erase(it); + break; + } + + } + if (!found) + qDebug() << "qxt-mini: can't find shortcut for" << key << mods; + res = unregisterShortcut(nativeKey, nativeMods); +#endif key = Qt::Key(0); mods = Qt::KeyboardModifiers(0); return res; @@ -95,9 +125,20 @@ bool QxtGlobalShortcutPrivate::unsetShortcut() void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods) { +#ifndef Q_OS_MAC + auto list = shortcuts.values(qMakePair(nativeKey, nativeMods)); + + for (auto it = list.begin(); it != list.end(); it++) + { + auto ptr = *it; + if (ptr->isEnabled()) + ptr->activated(); + } +#else QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods)); if (shortcut && shortcut->isEnabled()) emit shortcut->activated(); +#endif } /*! diff --git a/qxt-mini/qxtglobalshortcut.h b/qxt-mini/qxtglobalshortcut.h index 641c07c9..bf26180f 100644 --- a/qxt-mini/qxtglobalshortcut.h +++ b/qxt-mini/qxtglobalshortcut.h @@ -46,7 +46,7 @@ class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject public: explicit QxtGlobalShortcut(QObject* parent = 0); explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0); - virtual ~QxtGlobalShortcut(); + ~QxtGlobalShortcut() override; QKeySequence shortcut() const; bool setShortcut(const QKeySequence& shortcut); diff --git a/qxt-mini/qxtglobalshortcut_x11.cpp b/qxt-mini/qxtglobalshortcut_x11.cpp index ac744162..dad12b56 100644 --- a/qxt-mini/qxtglobalshortcut_x11.cpp +++ b/qxt-mini/qxtglobalshortcut_x11.cpp @@ -30,20 +30,115 @@ ** <http://libqxt.org> <foundation@libqxt.org> *****************************************************************************/ +#include <QHash> +#include <QMutex> +#include <QMutexLocker> #include <QDebug> -#include <QVector> + #include <QApplication> // include private header for great justice -sh 20131015 #include <X11/Xlib.h> #include <xcb/xcb.h> #include "qplatformnativeinterface.h" +#include "compat/util.hpp" -namespace { +typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event); -const QVector<quint32> maskModifiers = QVector<quint32>() - << 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask); +struct keybinding final +{ + quint32 code; + int refcnt; -typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event); + static QHash<quint32, keybinding> list; + static QMutex lock; + + static bool incf(quint32 code); + static bool decf(quint32 code); + + ~keybinding(); + +private: + keybinding(quint32 code); +}; + +bool operator==(const keybinding& k1, const keybinding& k2) +{ + return k1.code == k2.code; +} + +inline bool operator!=(const keybinding& k1, const keybinding& k2) +{ + return !(k1 == k2); +} + +uint qHash(const keybinding& k) +{ + return qHash(k.code); +} + +uint qHash(const keybinding& k, uint seed) +{ + return qHash(k.code, seed); +} + +keybinding::keybinding(quint32 code) : code(code), refcnt(0) +{ +} + +keybinding::~keybinding() +{ +} + +bool keybinding::incf(quint32 code) +{ + QMutexLocker l(&lock); + + keybinding k = list.value(code, keybinding(code)); + + const bool ret = k.refcnt == 0; + + if (ret) + { + qDebug() << "qxt-mini: registered keybinding" << code; + } + + k.refcnt++; + list.insert(code, k); + + qDebug() << "qxt-mini: incf: refcount for" << code << "now" << k.refcnt; + + return ret; +} + +bool keybinding::decf(quint32 code) +{ + QMutexLocker l(&lock); + + auto it = list.find(code); + + 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<quint32, keybinding> keybinding::list; +QMutex keybinding::lock; class QxtX11ErrorHandler { public: @@ -58,12 +153,9 @@ public: case BadValue: case BadWindow: if (event->request_code == 33 /* X_GrabKey */ || - event->request_code == 34 /* X_UngrabKey */) + event->request_code == 34 /* X_UngrabKey */) { error = true; - //TODO: - //char errstr[256]; - //XGetErrorText(dpy, err->error_code, errstr, 256); } } return 0; @@ -90,14 +182,10 @@ class QxtX11Data { public: QxtX11Data() { -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) - m_display = QX11Info::display(); -#else QPlatformNativeInterface *native = qApp->platformNativeInterface(); void *display = native->nativeResourceForScreen(QByteArray("display"), QGuiApplication::primaryScreen()); m_display = reinterpret_cast<Display *>(display); -#endif } bool isValid() @@ -116,51 +204,39 @@ public: return DefaultRootWindow(display()); } - bool grabKey(quint32 keycode, quint32 modifiers, Window window) + bool grabKey(quint32 keycode, Window window) { - QxtX11ErrorHandler errorHandler; + if (keybinding::incf(keycode)) + { + QxtX11ErrorHandler errorHandler; - for (int i = 0; !errorHandler.error && i < maskModifiers.size(); ++i) { - XGrabKey(display(), keycode, modifiers | maskModifiers[i], window, True, + XGrabKey(display(), keycode, AnyModifier, window, True, GrabModeAsync, GrabModeAsync); - } - if (errorHandler.error) { - qWarning() << "grab key error for" << "keycode" << keycode; - ungrabKey(keycode, modifiers, window); - return false; + if (errorHandler.error) { + ungrabKey(keycode, window); + return false; + } } return true; } - bool ungrabKey(quint32 keycode, quint32 modifiers, Window window) + bool ungrabKey(quint32 keycode, Window window) { - QxtX11ErrorHandler errorHandler; - - foreach (quint32 maskMods, maskModifiers) { - XUngrabKey(display(), keycode, modifiers | maskMods, window); + if (keybinding::decf(keycode)) + { + QxtX11ErrorHandler errorHandler; + XUngrabKey(display(), keycode, AnyModifier, window); + return !errorHandler.error; } - - return !errorHandler.error; + return true; } private: Display *m_display; }; -} // namespace - -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) -bool QxtGlobalShortcutPrivate::eventFilter(void *message) -{ - XEvent *event = static_cast<XEvent *>(message); - if (event->type == KeyPress) - { - XKeyEvent *key = reinterpret_cast<XKeyEvent *>(event); - unsigned int keycode = key->keycode; - unsigned int keystate = key->state; -#else bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, void *message, long *result) { @@ -176,39 +252,50 @@ bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, if (kev != 0) { unsigned int keycode = kev->detail; unsigned int keystate = 0; - if(kev->state & XCB_MOD_MASK_1) + if(kev->state & XCB_MOD_MASK_1) // alt keystate |= Mod1Mask; - if(kev->state & XCB_MOD_MASK_CONTROL) + if(kev->state & XCB_MOD_MASK_CONTROL) // ctrl keystate |= ControlMask; - if(kev->state & XCB_MOD_MASK_4) + if(kev->state & XCB_MOD_MASK_4) // super aka win key keystate |= Mod4Mask; - if(kev->state & XCB_MOD_MASK_SHIFT) + if(kev->state & XCB_MOD_MASK_SHIFT) //shift keystate |= ShiftMask; + if(kev->state & XCB_MOD_MASK_2) //numlock + keystate |= Mod2Mask; +#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 + activateShortcut(keycode, // Mod1Mask == Alt, Mod4Mask == Meta - keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask)); + keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask | Mod2Mask)); } return false; } quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { - // ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask + // XXX TODO make a lookup table quint32 native = 0; - if (modifiers & Qt::ShiftModifier) - native |= ShiftMask; - if (modifiers & Qt::ControlModifier) - native |= ControlMask; 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 0 + if (modifiers & Qt::MetaModifier) // dunno the native mask + native |= Mod4Mask; +#endif + if (modifiers & Qt::KeypadModifier) // numlock + native |= Mod2Mask; - // TODO: resolve these? - //if (modifiers & Qt::MetaModifier) - //if (modifiers & Qt::KeypadModifier) - //if (modifiers & Qt::GroupSwitchModifier) return native; } @@ -218,22 +305,31 @@ quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) if (!x11.isValid()) return 0; - KeySym keysym = XStringToKeysym(QKeySequence(key).toString().toLatin1().data()); + QByteArray tmp(QKeySequence(key).toString().toLatin1()); + + + KeySym keysym = XStringToKeysym(tmp.data()); if (keysym == NoSymbol) keysym = static_cast<ushort>(key); - return XKeysymToKeycode(x11.display(), keysym); + const quint32 ret = XKeysymToKeycode(x11.display(), keysym); + + qDebug() << "key is" << key << QKeySequence(key).toString(QKeySequence::PortableText) << ret; + + return ret; } -bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) +bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, unused(quint32, nativeMods)) { + qDebug() << "register" << nativeKey; QxtX11Data x11; - return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow()); + return x11.isValid() && x11.grabKey(nativeKey, x11.rootWindow()); } -bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) +bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, unused(quint32, nativeMods)) { + qDebug() << "unregister" << nativeKey; QxtX11Data x11; - return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow()); + return x11.isValid() && x11.ungrabKey(nativeKey, x11.rootWindow()); } #endif |