123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- #include "qhotkey.h"
- #include "qhotkey_p.h"
- #include <QDebug>
- #include <QThreadStorage>
- #include <QTimer>
- #include <QX11Info>
- #include <X11/Xlib.h>
- #include <xcb/xcb.h>
- // compability to pre Qt 5.8
- #ifndef Q_FALLTHROUGH
- #define Q_FALLTHROUGH() (void)0
- #endif
- class QHotkeyPrivateX11 : public QHotkeyPrivate
- {
- public:
- // QAbstractNativeEventFilter interface
- bool nativeEventFilter(const QByteArray& eventType,
- void* message,
- long* result) Q_DECL_OVERRIDE;
- protected:
- // QHotkeyPrivate interface
- quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE;
- quint32 nativeModifiers(Qt::KeyboardModifiers modifiers,
- bool& ok) Q_DECL_OVERRIDE;
- static QString getX11String(Qt::Key keycode);
- bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
- bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
- private:
- static const QVector<quint32> specialModifiers;
- static const quint32 validModsMask;
- xcb_key_press_event_t prevHandledEvent;
- xcb_key_press_event_t prevEvent;
- static QString formatX11Error(Display* display, int errorCode);
- class HotkeyErrorHandler
- {
- public:
- HotkeyErrorHandler();
- ~HotkeyErrorHandler();
- static bool hasError;
- static QString errorString;
- private:
- XErrorHandler prevHandler;
- static int handleError(Display* display, XErrorEvent* error);
- };
- };
- NATIVE_INSTANCE(QHotkeyPrivateX11)
- bool QHotkeyPrivate::isPlatformSupported()
- {
- return QX11Info::isPlatformX11();
- }
- const QVector<quint32> QHotkeyPrivateX11::specialModifiers = { 0,
- Mod2Mask,
- LockMask,
- (Mod2Mask |
- LockMask) };
- const quint32 QHotkeyPrivateX11::validModsMask =
- ShiftMask | ControlMask | Mod1Mask | Mod4Mask;
- bool QHotkeyPrivateX11::nativeEventFilter(const QByteArray& eventType,
- void* message,
- long* result)
- {
- Q_UNUSED(eventType)
- Q_UNUSED(result)
- auto* genericEvent = static_cast<xcb_generic_event_t*>(message);
- if (genericEvent->response_type == XCB_KEY_PRESS) {
- xcb_key_press_event_t keyEvent =
- *static_cast<xcb_key_press_event_t*>(message);
- this->prevEvent = keyEvent;
- if (this->prevHandledEvent.response_type == XCB_KEY_RELEASE) {
- if (this->prevHandledEvent.time == keyEvent.time)
- return false;
- }
- this->activateShortcut(
- { keyEvent.detail,
- keyEvent.state & QHotkeyPrivateX11::validModsMask });
- } else if (genericEvent->response_type == XCB_KEY_RELEASE) {
- xcb_key_release_event_t keyEvent =
- *static_cast<xcb_key_release_event_t*>(message);
- this->prevEvent = keyEvent;
- QTimer::singleShot(50, [this, keyEvent] {
- if (this->prevEvent.time == keyEvent.time &&
- this->prevEvent.response_type == keyEvent.response_type &&
- this->prevEvent.detail == keyEvent.detail) {
- this->releaseShortcut(
- { keyEvent.detail,
- keyEvent.state & QHotkeyPrivateX11::validModsMask });
- }
- });
- this->prevHandledEvent = keyEvent;
- }
- return false;
- }
- QString QHotkeyPrivateX11::getX11String(Qt::Key keycode)
- {
- switch (keycode) {
- case Qt::Key_MediaLast:
- case Qt::Key_MediaPrevious:
- return QStringLiteral("XF86AudioPrev");
- case Qt::Key_MediaNext:
- return QStringLiteral("XF86AudioNext");
- case Qt::Key_MediaPause:
- case Qt::Key_MediaPlay:
- case Qt::Key_MediaTogglePlayPause:
- return QStringLiteral("XF86AudioPlay");
- case Qt::Key_MediaRecord:
- return QStringLiteral("XF86AudioRecord");
- case Qt::Key_MediaStop:
- return QStringLiteral("XF86AudioStop");
- default:
- return QKeySequence(keycode).toString(QKeySequence::NativeText);
- }
- }
- quint32 QHotkeyPrivateX11::nativeKeycode(Qt::Key keycode, bool& ok)
- {
- QString keyString = getX11String(keycode);
- KeySym keysym = XStringToKeysym(keyString.toLatin1().constData());
- if (keysym == NoSymbol) {
- // not found -> just use the key
- if (keycode <= 0xFFFF)
- keysym = keycode;
- else
- return 0;
- }
- if (QX11Info::isPlatformX11()) {
- auto res = XKeysymToKeycode(QX11Info::display(), keysym);
- if (res != 0)
- ok = true;
- return res;
- }
- return 0;
- }
- quint32 QHotkeyPrivateX11::nativeModifiers(Qt::KeyboardModifiers modifiers,
- bool& ok)
- {
- quint32 nMods = 0;
- if (modifiers & Qt::ShiftModifier)
- nMods |= ShiftMask;
- if (modifiers & Qt::ControlModifier)
- nMods |= ControlMask;
- if (modifiers & Qt::AltModifier)
- nMods |= Mod1Mask;
- if (modifiers & Qt::MetaModifier)
- nMods |= Mod4Mask;
- ok = true;
- return nMods;
- }
- bool QHotkeyPrivateX11::registerShortcut(QHotkey::NativeShortcut shortcut)
- {
- Display* display = QX11Info::display();
- if (!display || !QX11Info::isPlatformX11())
- return false;
- HotkeyErrorHandler errorHandler;
- for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) {
- XGrabKey(display,
- shortcut.key,
- shortcut.modifier | specialMod,
- DefaultRootWindow(display),
- True,
- GrabModeAsync,
- GrabModeAsync);
- }
- XSync(display, False);
- if (errorHandler.hasError) {
- error = errorHandler.errorString;
- this->unregisterShortcut(shortcut);
- return false;
- }
- return true;
- }
- bool QHotkeyPrivateX11::unregisterShortcut(QHotkey::NativeShortcut shortcut)
- {
- Display* display = QX11Info::display();
- if (!display)
- return false;
- HotkeyErrorHandler errorHandler;
- for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) {
- XUngrabKey(display,
- shortcut.key,
- shortcut.modifier | specialMod,
- XDefaultRootWindow(display));
- }
- XSync(display, False);
- if (HotkeyErrorHandler::hasError) {
- error = HotkeyErrorHandler::errorString;
- return false;
- }
- return true;
- }
- QString QHotkeyPrivateX11::formatX11Error(Display* display, int errorCode)
- {
- char errStr[256];
- XGetErrorText(display, errorCode, errStr, 256);
- return QString::fromLatin1(errStr);
- }
- // ---------- QHotkeyPrivateX11::HotkeyErrorHandler implementation ----------
- bool QHotkeyPrivateX11::HotkeyErrorHandler::hasError = false;
- QString QHotkeyPrivateX11::HotkeyErrorHandler::errorString;
- QHotkeyPrivateX11::HotkeyErrorHandler::HotkeyErrorHandler()
- {
- prevHandler = XSetErrorHandler(&HotkeyErrorHandler::handleError);
- }
- QHotkeyPrivateX11::HotkeyErrorHandler::~HotkeyErrorHandler()
- {
- XSetErrorHandler(prevHandler);
- hasError = false;
- errorString.clear();
- }
- int QHotkeyPrivateX11::HotkeyErrorHandler::handleError(Display* display,
- XErrorEvent* error)
- {
- switch (error->error_code) {
- case BadAccess:
- case BadValue:
- case BadWindow:
- if (error->request_code == 33 || // grab key
- error->request_code == 34) { // ungrab key
- hasError = true;
- errorString =
- QHotkeyPrivateX11::formatX11Error(display, error->error_code);
- return 1;
- }
- Q_FALLTHROUGH();
- // fall through
- default:
- return 0;
- }
- }
|