summaryrefslogtreecommitdiffhomepage
path: root/qxt-mini
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2017-02-25 11:27:46 +0100
committerStanislaw Halik <sthalik@misaki.pl>2017-02-25 14:19:59 +0100
commit2826e614fb63448bf5801126e985cc53c1e11b8e (patch)
tree4d530f0fd4a4be846a3d11b623789d053695e0d0 /qxt-mini
parent6145c2082d3d116962dc96602c9ad2d22d62c675 (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.h1
-rw-r--r--qxt-mini/qxtglobalshortcut.cpp45
-rw-r--r--qxt-mini/qxtglobalshortcut.h2
-rw-r--r--qxt-mini/qxtglobalshortcut_x11.cpp218
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