/**************************************************************************** ** Copyright (c) 2006 - 2011, the LibQxt project. ** See the Qxt AUTHORS file for a list of authors and copyright holders. ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** * Neither the name of the LibQxt project nor the ** names of its contributors may be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** ** *****************************************************************************/ #include "qxtglobalshortcut_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "keymapper_x11.h" namespace { const QVector maskModifiers = QVector() << 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask); typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event); class QxtX11ErrorHandler { public: static bool error; static int qxtX11ErrorHandler(Display *display, XErrorEvent *event) { Q_UNUSED(display); switch (event->error_code) { case BadAccess: case BadValue: case BadWindow: if (event->request_code == 33 /* X_GrabKey */ || event->request_code == 34 /* X_UngrabKey */) { error = true; //TODO: //char errstr[256]; //XGetErrorText(dpy, err->error_code, errstr, 256); } } return 0; } QxtX11ErrorHandler() { error = false; m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler); } ~QxtX11ErrorHandler() { XSetErrorHandler(m_previousErrorHandler); } private: X11ErrorHandler m_previousErrorHandler; }; bool QxtX11ErrorHandler::error = false; class QxtX11Data { public: QxtX11Data() { QPlatformNativeInterface *native = qApp->platformNativeInterface(); //void *display = native->nativeResourceForScreen(QByteArray("display"), QGuiApplication::primaryScreen()); //m_display = reinterpret_cast(display); m_display = QX11Info::display(); } bool isValid() { return m_display != nullptr; } Display *display() { Q_ASSERT(isValid()); return m_display; } Window rootWindow() { return DefaultRootWindow(display()); } bool grabKey(quint32 keycode, quint32 modifiers, Window window) { QxtX11ErrorHandler errorHandler; for (int i = 0; !errorHandler.error && i < maskModifiers.size(); ++i) { XGrabKey(display(), keycode, modifiers | maskModifiers[i], window, True, GrabModeAsync, GrabModeAsync); } if (errorHandler.error) { ungrabKey(keycode, modifiers, window); return false; } return true; } bool ungrabKey(quint32 keycode, quint32 modifiers, Window window) { QxtX11ErrorHandler errorHandler; foreach (quint32 maskMods, maskModifiers) { XUngrabKey(display(), keycode, modifiers | maskMods, window); } return !errorHandler.error; } private: Display *m_display; }; } // namespace bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, void *message, long *result) { Q_UNUSED(result); xcb_key_press_event_t *kev = nullptr; if (eventType == "xcb_generic_event_t") { xcb_generic_event_t *ev = static_cast(message); if ((ev->response_type & 127) == XCB_KEY_PRESS) kev = static_cast(message); } if (kev != nullptr) { unsigned int keycode = kev->detail; unsigned int keystate = 0; if(kev->state & XCB_MOD_MASK_1) keystate |= Mod1Mask; if(kev->state & XCB_MOD_MASK_CONTROL) keystate |= ControlMask; if(kev->state & XCB_MOD_MASK_4) keystate |= Mod4Mask; if(kev->state & XCB_MOD_MASK_SHIFT) keystate |= ShiftMask; activateShortcut(keycode, // Mod1Mask == Alt, Mod4Mask == Meta keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask)); } return false; } quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { // ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask quint32 native = 0; if (modifiers & Qt::ShiftModifier) native |= ShiftMask; if (modifiers & Qt::ControlModifier) native |= ControlMask; if (modifiers & Qt::AltModifier) native |= Mod1Mask; if (modifiers & Qt::MetaModifier) native |= Mod4Mask; // TODO: resolve these? //if (modifiers & Qt::MetaModifier) //if (modifiers & Qt::KeypadModifier) //if (modifiers & Qt::GroupSwitchModifier) return native; } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) { // (davidsansome) Try the table from QKeyMapper first - this seems to be // the only way to get Keysyms for the media keys. unsigned int keysym = 0; int i = 0; while (KeyTbl[i]) { if (KeyTbl[i+1] == static_cast(key)) { keysym = KeyTbl[i]; break; } i += 2; } // If that didn't work then fall back on XStringToKeysym if (!keysym) { keysym = XStringToKeysym(QKeySequence(key).toString().toLatin1().data()); if (keysym == NoSymbol) keysym = static_cast(key); } QxtX11Data x11; if (!x11.isValid()) return 0; return XKeysymToKeycode(x11.display(), keysym); } bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { QxtX11Data x11; return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow()); } bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { QxtX11Data x11; return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow()); }