summaryrefslogtreecommitdiffhomepage
path: root/qxt-mini/qxtglobalshortcut_x11.cpp
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2017-05-30 20:08:49 +0200
committerStanislaw Halik <sthalik@misaki.pl>2017-05-30 20:08:49 +0200
commitf018bf4beeb15f346177dcee2b9e1a023627e7c4 (patch)
tree94aa371f97ff9e6a5b5db9c3804103aa782fe1ae /qxt-mini/qxtglobalshortcut_x11.cpp
parentc52b5ad0a8340012fab7982f0c670a46c3e5d93d (diff)
qxt-mini: finish up X11 shortcuts
It didn't work without XLookupString.
Diffstat (limited to 'qxt-mini/qxtglobalshortcut_x11.cpp')
-rw-r--r--qxt-mini/qxtglobalshortcut_x11.cpp459
1 files changed, 283 insertions, 176 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