diff options
Diffstat (limited to 'qxt-mini')
| -rw-r--r-- | qxt-mini/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | qxt-mini/lang/de_DE.ts | 4 | ||||
| -rw-r--r-- | qxt-mini/lang/zh_CN.ts | 4 | ||||
| -rw-r--r-- | qxt-mini/powerset.hpp | 100 | ||||
| -rw-r--r-- | qxt-mini/qplatformnativeinterface.h | 99 | ||||
| -rw-r--r-- | qxt-mini/qxtglobal.h | 12 | ||||
| -rw-r--r-- | qxt-mini/qxtglobalshortcut.cpp | 76 | ||||
| -rw-r--r-- | qxt-mini/qxtglobalshortcut.h | 12 | ||||
| -rw-r--r-- | qxt-mini/qxtglobalshortcut_mac.cpp | 31 | ||||
| -rw-r--r-- | qxt-mini/qxtglobalshortcut_p.h | 8 | ||||
| -rw-r--r-- | qxt-mini/qxtglobalshortcut_x11.cpp | 461 | ||||
| -rw-r--r-- | qxt-mini/x11-keymap.cpp | 314 | ||||
| -rw-r--r-- | qxt-mini/x11-keymap.hpp | 38 |
13 files changed, 844 insertions, 326 deletions
diff --git a/qxt-mini/CMakeLists.txt b/qxt-mini/CMakeLists.txt index d09cbdfa..1b2496f6 100644 --- a/qxt-mini/CMakeLists.txt +++ b/qxt-mini/CMakeLists.txt @@ -1,8 +1,13 @@ if(UNIX OR APPLE) + set(self opentrack-qxt-mini) + include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) otr_module(qxt-mini NO-COMPAT BIN) - if(NOT APPLE) - target_link_libraries(opentrack-qxt-mini X11) + if(APPLE) + find_library(CoreFoundation CoreFoundation) + find_library(Carbon Carbon) + target_link_options(${self} PUBLIC -framework Carbon -I ${CoreFoundation}) else() - set_property(TARGET opentrack-qxt-mini APPEND_STRING PROPERTY LINK_FLAGS "-framework Carbon -framework CoreFoundation") + otr_pkgconfig(${self} x11 xcb xproto) endif() + target_compile_definitions(${self} PRIVATE -DQXT_BUILD) endif() diff --git a/qxt-mini/lang/de_DE.ts b/qxt-mini/lang/de_DE.ts new file mode 100644 index 00000000..1552582e --- /dev/null +++ b/qxt-mini/lang/de_DE.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_DE"> +</TS> diff --git a/qxt-mini/lang/zh_CN.ts b/qxt-mini/lang/zh_CN.ts new file mode 100644 index 00000000..e5ca8aa9 --- /dev/null +++ b/qxt-mini/lang/zh_CN.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="zh_CN"> +</TS> diff --git a/qxt-mini/powerset.hpp b/qxt-mini/powerset.hpp new file mode 100644 index 00000000..901ff0c7 --- /dev/null +++ b/qxt-mini/powerset.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include <type_traits> +#include <cinttypes> +#include <vector> +#include <array> +#include <iterator> + +#include <QString> +#include <QVariant> + +template<typename t, int M, typename size_type_ = std::uintptr_t> +struct powerset final +{ + static_assert(std::is_integral_v<size_type_>, ""); + + using size_type = size_type_; + + static_assert(M > 0, ""); + static_assert(M < sizeof(size_type[8]), ""); + static_assert((std::is_unsigned_v<size_type>) || M < sizeof(size_type)*8 - 1, ""); + + using N = std::integral_constant<size_type, (size_type(1) << size_type(M))-1>; + static_assert((N::value & (N::value + 1)) == 0, ""); + + 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>; + + operator const sets_type&() const { return sets_; } + operator sets_type&() { return sets_; } + + const sets_type& sets() const { return sets_; } + sets_type& sets() { return sets_; } + + set_type& operator[](unsigned k) { return sets_[k]; } + const set_type& operator[](unsigned k) const { return sets_[k]; } + + const set_type& elements() const { return elements_; } + set_type& elements() { return elements_; } + + 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: + sets_type sets_; + set_type elements_; +}; + +template<typename t, typename... xs> +inline auto make_powerset(const t& arg, const xs&... args) +{ + 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; + + p ret; + vec v; + v.reserve(len()); + + const typename p::set_type ts {{arg, static_cast<t>(args)...}}; + + ret.elements() = std::vector<t>(std::begin(ts), std::end(ts)); + + // no nullary set + for (size_type i = 0; i < len(); i++) + { + v.clear(); + size_type k = 1; + for (const t& x : ts) + { + if ((i+1) & k) + v.push_back(std::move(x)); + k <<= 1; + } + + ret[i] = vec(std::begin(v), std::end(v)); + ret[i].shrink_to_fit(); + } + + return ret; +} diff --git a/qxt-mini/qplatformnativeinterface.h b/qxt-mini/qplatformnativeinterface.h deleted file mode 100644 index eaa24a9e..00000000 --- a/qxt-mini/qplatformnativeinterface.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPLATFORMNATIVEINTERFACE_H -#define QPLATFORMNATIVEINTERFACE_H - -// -// W A R N I N G -// ------------- -// -// This file is part of the QPA API and is not meant to be used -// in applications. Usage of this API may make your code -// source and binary incompatible with future versions of Qt. -// - -#include <QtGui/qwindowdefs.h> -#include <QtCore/QObject> -#include <QtCore/QVariant> - -QT_BEGIN_NAMESPACE - - -class QOpenGLContext; -class QScreen; -class QWindow; -class QPlatformWindow; -class QBackingStore; - -class Q_GUI_EXPORT QPlatformNativeInterface : public QObject -{ - Q_OBJECT -public: - virtual void *nativeResourceForIntegration(const QByteArray &resource); - virtual void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context); - virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen); - virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); - virtual void *nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *backingStore); - - typedef void * (*NativeResourceForIntegrationFunction)(); - typedef void * (*NativeResourceForContextFunction)(QOpenGLContext *context); - typedef void * (*NativeResourceForScreenFunction)(QScreen *screen); - typedef void * (*NativeResourceForWindowFunction)(QWindow *window); - typedef void * (*NativeResourceForBackingStoreFunction)(QBackingStore *backingStore); - virtual NativeResourceForIntegrationFunction nativeResourceFunctionForIntegration(const QByteArray &resource); - virtual NativeResourceForContextFunction nativeResourceFunctionForContext(const QByteArray &resource); - virtual NativeResourceForScreenFunction nativeResourceFunctionForScreen(const QByteArray &resource); - virtual NativeResourceForWindowFunction nativeResourceFunctionForWindow(const QByteArray &resource); - virtual NativeResourceForBackingStoreFunction nativeResourceFunctionForBackingStore(const QByteArray &resource); - - virtual QVariantMap windowProperties(QPlatformWindow *window) const; - virtual QVariant windowProperty(QPlatformWindow *window, const QString &name) const; - virtual QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const; - virtual void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value); - -Q_SIGNALS: - void windowPropertyChanged(QPlatformWindow *window, const QString &propertyName); -}; - -QT_END_NAMESPACE - -#endif // QPLATFORMNATIVEINTERFACE_H diff --git a/qxt-mini/qxtglobal.h b/qxt-mini/qxtglobal.h index 6446b0ee..5ac72fb7 100644 --- a/qxt-mini/qxtglobal.h +++ b/qxt-mini/qxtglobal.h @@ -53,10 +53,18 @@ #ifdef BUILD_qxt_mini # define QXT_BUILD -# define BUILD_QXT_GUI -# define BUILD_QXT #endif +#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) +# if defined(BUILD_QXT_GUI) +# define QXT_EXPORT Q_DECL_EXPORT +# else +# define QXT_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_EXPORT +#endif // BUILD_QXT_GUI + #ifdef QXT_BUILD # if defined(BUILD_QXT_CORE) # define QXT_CORE_EXPORT Q_DECL_EXPORT diff --git a/qxt-mini/qxtglobalshortcut.cpp b/qxt-mini/qxtglobalshortcut.cpp index 298472b5..dec11dc4 100644 --- a/qxt-mini/qxtglobalshortcut.cpp +++ b/qxt-mini/qxtglobalshortcut.cpp @@ -31,19 +31,16 @@ #include "qxtglobalshortcut_p.h" -#include "compat/util.hpp" - #include <QAbstractEventDispatcher> #include <QApplication> #include <QtDebug> #include <QtGlobal> -QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts; +#ifdef __GNUG__ +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif -struct QxtGlobalShortcutPrivate::event_filter_installer -{ - static void ensure_event_filter(); -}; +QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts; void QxtGlobalShortcutPrivate::event_filter_installer::ensure_event_filter() { @@ -53,21 +50,20 @@ void QxtGlobalShortcutPrivate::event_filter_installer::ensure_event_filter() { static QxtGlobalShortcutPrivate filter(QxtGlobalShortcutPrivate::tag {}); static bool installed = - (instance->installNativeEventFilter(&filter), - true); - Q_UNUSED(installed); + ((void)instance->installNativeEventFilter(&filter), true); + Q_UNUSED(installed) } #endif } QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate(QxtGlobalShortcutPrivate::tag) : - enabled(false), key(Qt::Key(0)), mods(Qt::NoModifier) + keystate(false), enabled(false), key(Qt::Key(0)), mods(Qt::NoModifier) { - qDebug() << "qxt-mini: adding event filter"; + //qDebug() << "qxt-mini: adding event filter"; } QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate() : - enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier) + keystate(false), enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier) { QxtGlobalShortcutPrivate::event_filter_installer::ensure_event_filter(); } @@ -83,8 +79,8 @@ bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut) if (shortcut.toString() == "") return false; 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); + key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((unsigned)shortcut[0] & ~allMods); + mods = shortcut.isEmpty() ? Qt::KeyboardModifier(0) : Qt::KeyboardModifiers((unsigned)shortcut[0] & allMods); const quint32 nativeKey = nativeKeycode(key); const quint32 nativeMods = nativeModifiers(mods); @@ -98,7 +94,7 @@ bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut) #endif } else - qWarning() << "QxtGlobalShortcut failed to register:" << QKeySequence(key + mods).toString(); + qWarning() << "QxtGlobalShortcut failed to register:" << shortcut; return res; } @@ -107,10 +103,10 @@ 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 + bool res = false; if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == &qxt_p()) res = unregisterShortcut(nativeKey, nativeMods); if (res) @@ -136,32 +132,56 @@ bool QxtGlobalShortcutPrivate::unsetShortcut() } if (!found) qDebug() << "qxt-mini: can't find shortcut for" << key << mods; - res = unregisterShortcut(nativeKey, nativeMods); + bool res = unregisterShortcut(nativeKey, nativeMods); #endif key = Qt::Key(0); - mods = Qt::KeyboardModifiers(0); + mods = Qt::KeyboardModifier(0); return res; } -void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods) +void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods, bool is_down) { #ifndef Q_OS_MAC using IT = decltype(shortcuts.end()); const auto pair = qMakePair(nativeKey, nativeMods); IT it = shortcuts.find(pair); + bool once = false; + for (; it != shortcuts.end(); it++) { if (it.key() != pair) // DO NOT REMOVE break; + auto ptr = *it; + auto& priv = ptr->qxt_d(); + + if (priv.keystate == is_down) + { + continue; + } + + if (!once) + { + once = true; + qDebug() << "qxt-mini:" << (is_down ? "keydown" : "keyup") << priv.key << priv.mods; + } + + priv.keystate = is_down; + if (ptr->isEnabled()) - emit ptr->activated(); + emit ptr->activated(is_down); } #else QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods)); - if (shortcut && shortcut->isEnabled()) - emit shortcut->activated(); + + if (shortcut) + { + shortcut->qxt_d().keystate = false; + + if (shortcut->isEnabled()) + emit shortcut->activated(is_down); + } #endif } @@ -186,7 +206,7 @@ void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativ */ /*! - \fn QxtGlobalShortcut::activated() + \fn void QxtGlobalShortcut::activated(bool keydown=true) This signal is emitted when the user types the shortcut's key sequence. @@ -199,7 +219,7 @@ void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativ QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent) : QObject(parent) { - QXT_INIT_PRIVATE(QxtGlobalShortcut); + QXT_INIT_PRIVATE(QxtGlobalShortcut) } /*! @@ -208,7 +228,7 @@ QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent) QxtGlobalShortcut::QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent) : QObject(parent) { - QXT_INIT_PRIVATE(QxtGlobalShortcut); + QXT_INIT_PRIVATE(QxtGlobalShortcut) setShortcut(shortcut); } @@ -225,7 +245,7 @@ QxtGlobalShortcut::~QxtGlobalShortcut() \property QxtGlobalShortcut::shortcut \brief the shortcut key sequence - \bold {Note:} Notice that corresponding key press and release events are not + \note Notice that corresponding key press and release events are not delivered for registered global shortcuts even if they are disabled. Also, comma separated key sequences are not supported. Only the first part is used: @@ -237,7 +257,7 @@ QxtGlobalShortcut::~QxtGlobalShortcut() */ QKeySequence QxtGlobalShortcut::shortcut() const { - return QKeySequence(qxt_d().key | qxt_d().mods); + return QKeySequence((int)(qxt_d().key | qxt_d().mods)); } bool QxtGlobalShortcut::setShortcut(const QKeySequence& shortcut) diff --git a/qxt-mini/qxtglobalshortcut.h b/qxt-mini/qxtglobalshortcut.h index bf26180f..9f8dcf9e 100644 --- a/qxt-mini/qxtglobalshortcut.h +++ b/qxt-mini/qxtglobalshortcut.h @@ -1,4 +1,4 @@ -#ifndef QXTGLOBALSHORTCUT_H +#pragma once /**************************************************************************** ** Copyright (c) 2006 - 2011, the LibQxt project. ** See the Qxt AUTHORS file for a list of authors and copyright holders. @@ -43,9 +43,11 @@ class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) + bool keystate; + public: - explicit QxtGlobalShortcut(QObject* parent = 0); - explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0); + explicit QxtGlobalShortcut(QObject* parent = nullptr); + explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = nullptr); ~QxtGlobalShortcut() override; QKeySequence shortcut() const; @@ -58,7 +60,5 @@ public Q_SLOTS: void setDisabled(bool disabled = true); Q_SIGNALS: - void activated(); + void activated(bool keydown = true); }; - -#endif // QXTGLOBALSHORTCUT_H diff --git a/qxt-mini/qxtglobalshortcut_mac.cpp b/qxt-mini/qxtglobalshortcut_mac.cpp index c6b20fa2..c91d763d 100644 --- a/qxt-mini/qxtglobalshortcut_mac.cpp +++ b/qxt-mini/qxtglobalshortcut_mac.cpp @@ -49,13 +49,14 @@ OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, { Q_UNUSED(nextHandler); Q_UNUSED(data); - if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) + if (GetEventClass(event) == kEventClassKeyboard && (GetEventKind(event) == kEventHotKeyPressed || GetEventKind(event) == kEventHotKeyReleased)) { EventHotKeyID keyID; GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID); Identifier id = keyIDs.key(keyID.id); - if(id != Identifier()) - QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first); + if(id != Identifier()) { + QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first, GetEventKind(event) == kEventHotKeyPressed); + } } return noErr; } @@ -76,11 +77,11 @@ quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifier return native; } -quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) +quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key keys) { UTF16Char ch; // Constants found in NSEvent.h from AppKit.framework - switch (key) + switch (keys) { case Qt::Key_Return: return kVK_Return; @@ -170,11 +171,11 @@ quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) ; } - if (key == Qt::Key_Escape) ch = 27; - else if (key == Qt::Key_Return) ch = 13; - else if (key == Qt::Key_Enter) ch = 3; - else if (key == Qt::Key_Tab) ch = 9; - else ch = key; + if (keys == Qt::Key_Escape) ch = 27; + else if (keys == Qt::Key_Return) ch = 13; + else if (keys == Qt::Key_Enter) ch = 3; + else if (keys == Qt::Key_Tab) ch = 9; + else ch = keys; CFDataRef currentLayoutData; TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); @@ -233,10 +234,12 @@ bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativ if (!qxt_mac_handler_installed) { qxt_mac_handler_installed = true; - EventTypeSpec t; - t.eventClass = kEventClassKeyboard; - t.eventKind = kEventHotKeyPressed; - InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL); + EventTypeSpec t[2]; + t[0].eventClass = kEventClassKeyboard; + t[0].eventKind = kEventHotKeyPressed; + t[1].eventClass = kEventClassKeyboard; + t[1].eventKind = kEventHotKeyReleased; + InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 2, t, NULL, NULL); } EventHotKeyID keyID; diff --git a/qxt-mini/qxtglobalshortcut_p.h b/qxt-mini/qxtglobalshortcut_p.h index 1835f956..be708ba6 100644 --- a/qxt-mini/qxtglobalshortcut_p.h +++ b/qxt-mini/qxtglobalshortcut_p.h @@ -45,6 +45,7 @@ public: QxtGlobalShortcutPrivate(); ~QxtGlobalShortcutPrivate() override; + bool keystate; bool enabled; Qt::Key key; Qt::KeyboardModifiers mods; @@ -53,13 +54,12 @@ public: bool unsetShortcut(); static bool error; - static bool eventFilter(void* message); bool nativeEventFilter(const QByteArray & eventType, void * message, long * result) override; - static void activateShortcut(quint32 nativeKey, quint32 nativeMods); + static void activateShortcut(quint32 nativeKey, quint32 nativeMods, bool is_down); private: - struct event_filter_installer; + struct event_filter_installer { static void ensure_event_filter(); }; friend struct event_filter_installer; struct tag {}; explicit QxtGlobalShortcutPrivate(tag); @@ -71,8 +71,6 @@ private: static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods); static QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> shortcuts; - - static void ensure_event_filter(); }; #endif // QXTGLOBALSHORTCUT_P_H diff --git a/qxt-mini/qxtglobalshortcut_x11.cpp b/qxt-mini/qxtglobalshortcut_x11.cpp index a2e47ade..01894cfc 100644 --- a/qxt-mini/qxtglobalshortcut_x11.cpp +++ b/qxt-mini/qxtglobalshortcut_x11.cpp @@ -30,42 +30,234 @@ ** <http://libqxt.org> <foundation@libqxt.org> *****************************************************************************/ +// qt must go first or #error #include <QHash> #include <QMutex> -#include <QMutexLocker> #include <QDebug> - +#include <QPair> +#include <QKeyEvent> #include <QApplication> -// include private header for great justice -sh 20131015 +#include <qpa/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" -static constexpr quint32 AllMods = ShiftMask|LockMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask; +#include "powerset.hpp" -typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event); +#include <iterator> +#include <vector> +#include <type_traits> +#include <utility> +#include <cinttypes> +#include <array> + +static auto evil_mods = make_powerset(LockMask, Mod5Mask, Mod2Mask); + +static inline quint32 filter_evil_mods(quint32 mods) +{ + for (auto mod : evil_mods.elements()) + { + mods &= quint32(~mod); + } + return mods; +} + +using pair = QPair<quint32, quint32>; struct keybinding final { - quint32 code; + keybinding(const keybinding&) = default; + keybinding& operator=(const keybinding&) = default; + + quint32 code, mods; int refcnt; - static QHash<quint32, keybinding> list; + static QHash<pair, keybinding> list; static QMutex lock; - static bool incf(quint32 code); - static bool decf(quint32 code); + static bool incf(quint32 code, quint32 mods); + static bool decf(quint32 code, quint32 mods); ~keybinding(); private: - keybinding(quint32 code); + 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: + 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; + } + } + return 0; + } + + QxtX11ErrorHandler() + { + error = false; + m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler); + } + + ~QxtX11ErrorHandler() + { + XSetErrorHandler(m_previousErrorHandler); + } + +private: + X11ErrorHandler m_previousErrorHandler; +}; + +class QxtX11Data { +public: + QxtX11Data() + { + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + void *display = native->nativeResourceForScreen(QByteArray("display"), + QGuiApplication::primaryScreen()); + m_display = reinterpret_cast<Display *>(display); + } + + bool isValid() + { + return m_display; + } + + Display *display() + { + Q_ASSERT(isValid()); + return m_display; + } + + Window rootWindow() + { + return DefaultRootWindow(display()); + } + + bool grabKey(quint32 code, quint32 mods) + { + const std::vector<pair> keycodes = native_key(Qt::Key(code), Qt::KeyboardModifiers(mods)); + bool ret = true; + + for (const pair& x : keycodes) + { + auto [ native_sym, native_mods ] = x; + int native_code = XKeysymToKeycode(display(), native_sym); + + if (keybinding::incf((unsigned)native_code, native_mods)) + { + QxtX11ErrorHandler errorHandler; + + XGrabKey(display(), native_code, native_mods, rootWindow(), True, GrabModeAsync, GrabModeAsync); + + for (const auto& set : evil_mods.sets()) + { + quint32 m = native_mods; + + for (int value : set) + m |= (unsigned)value; + + XGrabKey(display(), native_code, m, rootWindow(), True, GrabModeAsync, GrabModeAsync); + } + + if (errorHandler.error) + { + qDebug() << "qxt-mini: error while binding to" << code << mods; + for (const auto& set : evil_mods.sets()) + { + quint32 m = mods; + + for (int value : set) + m |= (unsigned)value; + + XUngrabKey(display(), native_code, m, rootWindow()); + } + ret = false; + } + } + } + + return ret; + } + + bool ungrabKey(quint32 code, quint32 mods) + { + const std::vector<pair> keycodes = native_key(Qt::Key(code), Qt::KeyboardModifiers(mods)); + bool ret = true; + + for (const pair& x : keycodes) + { + auto [ native_sym, native_mods ] = x; + int native_code = XKeysymToKeycode(display(), native_sym); + + if (keybinding::decf((unsigned)native_code, native_mods)) + { + QxtX11ErrorHandler errorHandler; + XUngrabKey(display(), native_code, native_mods, rootWindow()); + + for (const auto& set : evil_mods.sets()) + { + quint32 m = mods; + + for (int value : set) + m |= (unsigned)value; + + XUngrabKey(display(), native_code, m, rootWindow()); + } + + if (errorHandler.error) + { + qDebug() << "qxt-mini: error while unbinding" << code << mods; + ret = false; + } + } + } + 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; + return k1.code == k2.code && k1.mods == k2.mods; } inline bool operator!=(const keybinding& k1, const keybinding& k2) @@ -75,15 +267,17 @@ inline bool operator!=(const keybinding& k1, const keybinding& k2) uint qHash(const keybinding& k) { - return qHash(k.code); + return uint(k.code * 41) ^ qHash(k.mods); } uint qHash(const keybinding& k, uint seed) { - return qHash(k.code, seed); + return qHash(uint(k.code * 41) ^ qHash(k.mods), seed); } -keybinding::keybinding(quint32 code) : code(code), refcnt(0) +keybinding::keybinding(quint32 code, quint32 mods) : + code(code), mods(mods), + refcnt(0) { } @@ -91,11 +285,11 @@ keybinding::~keybinding() { } -bool keybinding::incf(quint32 code) +bool keybinding::incf(quint32 code, quint32 mods) { QMutexLocker l(&lock); - keybinding k = list.value(code, keybinding(code)); + keybinding k = list.value(pair(code, mods), keybinding(code, mods)); const bool ret = k.refcnt == 0; @@ -105,18 +299,18 @@ bool keybinding::incf(quint32 code) } k.refcnt++; - list.insert(code, k); + list.insert(pair(code, mods), k); //qDebug() << "qxt-mini: incf: refcount for" << code << "now" << k.refcnt; return ret; } -bool keybinding::decf(quint32 code) +bool keybinding::decf(quint32 code, quint32 mods) { QMutexLocker l(&lock); - auto it = list.find(code); + auto it = list.find(pair(code, mods)); if (it == list.end()) { @@ -139,196 +333,125 @@ bool keybinding::decf(quint32 code) return false; } -QHash<quint32, keybinding> keybinding::list; +QHash<pair, keybinding> keybinding::list; QMutex keybinding::lock; -class QxtX11ErrorHandler { -public: - static bool error; +bool QxtX11ErrorHandler::error = false; + +bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, + void *message, long *) +{ + QxtX11Data x11; + + if (!x11.isValid()) + return false; - static int qxtX11ErrorHandler(Display *display, XErrorEvent *event) { - Q_UNUSED(display); - switch (event->error_code) + static bool once_ = false; + if (!once_) { - case BadAccess: - case BadValue: - case BadWindow: - if (event->request_code == 33 /* X_GrabKey */ || - event->request_code == 34 /* X_UngrabKey */) - { - error = true; - } - } - return 0; - } + once_ = true; + Bool val = False; - QxtX11ErrorHandler() - { - error = false; - m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler); - } + (void) XkbSetDetectableAutoRepeat(x11.display(), True, &val); - ~QxtX11ErrorHandler() - { - XSetErrorHandler(m_previousErrorHandler); + if (!val) + qDebug() << "qxt-mini: can't fix x11 autorepeat"; + } } -private: - X11ErrorHandler m_previousErrorHandler; -}; + bool is_release = false; -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 *>(display); - } + static_assert(std::is_same_v<xcb_key_press_event_t, xcb_key_release_event_t>); - bool isValid() - { - return m_display != 0; + xcb_key_press_event_t *kev = nullptr; + if (eventType == "xcb_generic_event_t") { + xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message); + switch (ev->response_type & 127) + { + case XCB_KEY_RELEASE: + is_release = true; + [[fallthrough]]; + case XCB_KEY_PRESS: + kev = static_cast<xcb_key_press_event_t *>(message); + break; + + default: + break; + } } - Display *display() - { - Q_ASSERT(isValid()); - return m_display; - } + if (kev) { +#if 0 + using event_type = decltype((xcb_key_press_event_t{}).detail); - Window rootWindow() - { - return DefaultRootWindow(display()); - } + static event_type prev_event = 0; + static bool prev_is_release = false; - bool grabKey(quint32 keycode, Window window) - { - if (keybinding::incf(keycode)) + if (is_release == prev_is_release && + prev_event != 0 && + prev_event == kev->detail) { - QxtX11ErrorHandler errorHandler; + // ignore repeated keystrokes + return false; + } + else + { + prev_event = kev->detail; + prev_is_release = is_release; + } +#endif - XGrabKey(display(), keycode, AnyModifier, window, True, - GrabModeAsync, GrabModeAsync); + unsigned int keycode = kev->detail; - if (errorHandler.error) { - ungrabKey(keycode, window); - return false; - } - } + if (keycode == 0) + return false; - return true; - } + quint32 keystate = xcb_mods_to_x11(kev->state); - bool ungrabKey(quint32 keycode, Window window) - { - if (keybinding::decf(keycode)) - { - QxtX11ErrorHandler errorHandler; - XUngrabKey(display(), keycode, AnyModifier, window); - return !errorHandler.error; - } - return true; - } + keystate = filter_evil_mods(keystate); -private: - Display *m_display; -}; + auto [ sym, sym2 ] = keycode_to_keysym(x11.display(), keycode, keystate, kev); -bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, - void *message, long *result) -{ - Q_UNUSED(result); + Qt::Key k; Qt::KeyboardModifiers mods; - xcb_key_press_event_t *kev = 0; - if (eventType == "xcb_generic_event_t") { - xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message); - if ((ev->response_type & 127) == XCB_KEY_PRESS) - kev = static_cast<xcb_key_press_event_t *>(message); - } + { + std::tie(k, mods) = x11_key_to_qt(x11.display(), (quint32)sym, keystate); - if (kev != 0) { - unsigned int keycode = kev->detail; - unsigned int keystate = 0; - if(kev->state & XCB_MOD_MASK_1) // alt - keystate |= Mod1Mask; - if(kev->state & XCB_MOD_MASK_CONTROL) // ctrl - keystate |= ControlMask; - if(kev->state & XCB_MOD_MASK_4) // super aka win key - keystate |= Mod4Mask; - 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 + if (k != 0) + activateShortcut(k, mods, !is_release); + } - activateShortcut(keycode, keystate); + { + std::tie(k, mods) = x11_key_to_qt(x11.display(), (quint32)sym2, keystate); + + if (k != 0) + activateShortcut(k, mods, !is_release); + } } return false; } quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { - // XXX TODO make a lookup table - 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 0 - if (modifiers & Qt::MetaModifier) // dunno the native mask - native |= Mod4Mask; -#endif - if (modifiers & Qt::KeypadModifier) // numlock - native |= Mod2Mask; - - native &= AllMods; - - return native; + modifiers = x11_mods_to_qt(filter_evil_mods(qt_mods_to_x11(modifiers))); + return quint32(modifiers); } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) { - QxtX11Data x11; - if (!x11.isValid()) - return 0; - - QByteArray tmp(QKeySequence(key).toString().toLatin1()); - - KeySym keysym = XStringToKeysym(tmp.data()); - if (keysym == NoSymbol) - keysym = static_cast<ushort>(key); - - const quint32 ret = XKeysymToKeycode(x11.display(), keysym); - - //qDebug() << "key is" << key << QKeySequence(key).toString(QKeySequence::PortableText) << ret; - - return ret; + return quint32(key); } -bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, unused(quint32, nativeMods)) +bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { QxtX11Data x11; - return x11.isValid() && x11.grabKey(nativeKey, x11.rootWindow()); + return x11.isValid() && x11.grabKey(nativeKey, nativeMods); } -bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, unused(quint32, nativeMods)) +bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { QxtX11Data x11; - return x11.isValid() && x11.ungrabKey(nativeKey, x11.rootWindow()); + return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods); } #endif diff --git a/qxt-mini/x11-keymap.cpp b/qxt-mini/x11-keymap.cpp new file mode 100644 index 00000000..fed01cc2 --- /dev/null +++ b/qxt-mini/x11-keymap.cpp @@ -0,0 +1,314 @@ +#include "x11-keymap.hpp" + +#if !defined __APPLE__ && !defined _WIN32 + +#include <QMutex> + +#define XK_MISCELLANY +#define XK_LATIN1 + +#include <X11/keysymdef.h> + + +struct tt { + Qt::Key qt; + quint32 keysym; +}; + +static const tt keymap[] = +{ + { Qt::Key_Return, XK_Return }, + { Qt::Key_Enter, XK_Return }, + { Qt::Key_Delete, XK_Delete }, + { Qt::Key_Pause, XK_Pause }, + { Qt::Key_SysReq, XK_Sys_Req }, + { Qt::Key_Home, XK_Home }, + { Qt::Key_Insert, XK_Insert }, + { Qt::Key_End, XK_End }, + { Qt::Key_Left, XK_Left }, + { Qt::Key_Up, XK_Up }, + { Qt::Key_Right, XK_Right }, + { Qt::Key_Down, XK_Down }, + { Qt::Key_PageUp, XK_Prior }, + { Qt::Key_PageDown, XK_Next }, + { Qt::Key_Tab, XK_Tab }, + + { Qt::Key_F1, XK_F1 }, + { Qt::Key_F2, XK_F2 }, + { Qt::Key_F3, XK_F3 }, + { Qt::Key_F4, XK_F4 }, + { Qt::Key_F5, XK_F5 }, + { Qt::Key_F6, XK_F6 }, + { Qt::Key_F7, XK_F7 }, + { Qt::Key_F8, XK_F8 }, + { Qt::Key_F9, XK_F9 }, + { Qt::Key_F10, XK_F10 }, + { Qt::Key_F11, XK_F11 }, + { Qt::Key_F12, XK_F12 }, + + { Qt::Key_Space, XK_space }, + { Qt::Key_QuoteDbl, XK_quotedbl }, + +#if 1 + { Qt::Key_Exclam, XK_exclam }, + { Qt::Key_At, XK_at }, + { Qt::Key_NumberSign, XK_numbersign }, + { Qt::Key_Dollar, XK_dollar }, + { Qt::Key_Percent, XK_percent }, + { Qt::Key_AsciiCircum, XK_asciicircum }, + { Qt::Key_Ampersand, XK_ampersand }, + { Qt::Key_Asterisk, XK_asterisk }, + { Qt::Key_ParenLeft, XK_parenleft }, + { Qt::Key_ParenRight, XK_parenright }, +#else + { Qt::Key_Exclam, XK_1 }, + { Qt::Key_At, XK_2 }, + { Qt::Key_NumberSign, XK_3 }, + { Qt::Key_Dollar, XK_4 }, + { Qt::Key_Percent, XK_5 }, + { Qt::Key_AsciiCircum, XK_6 }, + { Qt::Key_Ampersand, XK_7 }, + { Qt::Key_Asterisk, XK_8 }, + { Qt::Key_ParenLeft, XK_9 }, + { Qt::Key_ParenRight, XK_0 }, +#endif + { Qt::Key_Minus, XK_minus }, + { Qt::Key_Equal, XK_equal }, + { Qt::Key_Apostrophe, XK_apostrophe }, + { Qt::Key_Plus, XK_plus }, + { Qt::Key_Comma, XK_comma }, + { Qt::Key_Period, XK_period }, + { Qt::Key_Slash, XK_slash }, + + { Qt::Key_0, XK_0 }, + { Qt::Key_1, XK_1 }, + { Qt::Key_2, XK_2 }, + { Qt::Key_3, XK_3 }, + { Qt::Key_4, XK_4 }, + { Qt::Key_5, XK_5 }, + { Qt::Key_6, XK_6 }, + { Qt::Key_7, XK_7 }, + { Qt::Key_8, XK_8 }, + { Qt::Key_9, XK_9 }, + + { Qt::Key_Colon, XK_colon }, + { Qt::Key_Semicolon, XK_semicolon }, + { Qt::Key_Less, XK_less }, + { Qt::Key_Greater, XK_greater }, + { Qt::Key_Question, XK_question }, + + { Qt::Key_A, XK_a }, + { Qt::Key_B, XK_b }, + { Qt::Key_C, XK_c }, + { Qt::Key_D, XK_d }, + { Qt::Key_E, XK_e }, + { Qt::Key_F, XK_f }, + { Qt::Key_G, XK_g }, + { Qt::Key_H, XK_h }, + { Qt::Key_I, XK_i }, + { Qt::Key_J, XK_j }, + { Qt::Key_K, XK_k }, + { Qt::Key_L, XK_l }, + { Qt::Key_M, XK_m }, + { Qt::Key_N, XK_n }, + { Qt::Key_O, XK_o }, + { Qt::Key_P, XK_p }, + { Qt::Key_Q, XK_q }, + { Qt::Key_R, XK_r }, + { Qt::Key_S, XK_s }, + { Qt::Key_T, XK_t }, + { Qt::Key_U, XK_u }, + { Qt::Key_V, XK_v }, + { Qt::Key_W, XK_w }, + { Qt::Key_X, XK_x }, + { Qt::Key_Y, XK_y }, + { Qt::Key_Z, XK_z }, + + { Qt::Key_BracketLeft, XK_bracketleft }, + { Qt::Key_Backslash, XK_backslash }, + { Qt::Key_BracketRight, XK_bracketright }, + { Qt::Key_Underscore, XK_underscore }, + { Qt::Key_QuoteLeft, XK_grave }, +#if 0 +}; +static tt numpad_keymap[] = { +#endif + { Qt::Key_0, XK_KP_0 }, + { Qt::Key_1, XK_KP_1 }, + { Qt::Key_2, XK_KP_2 }, + { Qt::Key_3, XK_KP_3 }, + { Qt::Key_4, XK_KP_4 }, + { Qt::Key_5, XK_KP_5 }, + { Qt::Key_6, XK_KP_6 }, + { Qt::Key_7, XK_KP_7 }, + { Qt::Key_8, XK_KP_8 }, + { Qt::Key_9, XK_KP_9 }, + + { Qt::Key_Space, XK_KP_Space }, + { Qt::Key_Tab, XK_KP_Tab }, + { Qt::Key_F1, XK_KP_F1 }, + { Qt::Key_F2, XK_KP_F2 }, + { Qt::Key_F3, XK_KP_F3 }, + { Qt::Key_F4, XK_KP_F4 }, + + { Qt::Key_1, XK_KP_End }, + { Qt::Key_2, XK_KP_Down }, + { Qt::Key_3, XK_KP_Page_Down }, + { Qt::Key_4, XK_KP_Left }, + { Qt::Key_5, XK_KP_Begin }, + { Qt::Key_6, XK_KP_Right }, + { Qt::Key_7, XK_KP_Home }, + { Qt::Key_8, XK_KP_Up }, + { Qt::Key_9, XK_KP_Page_Up }, + { Qt::Key_0, XK_KP_Insert }, + + { Qt::Key_Delete, XK_KP_Delete }, + { Qt::Key_Equal, XK_KP_Equal }, + { Qt::Key_Asterisk, XK_KP_Multiply }, + { Qt::Key_Plus, XK_KP_Add }, + { Qt::Key_Comma, XK_KP_Separator }, + { Qt::Key_Minus, XK_KP_Subtract }, + { Qt::Key_Period, XK_KP_Decimal }, + { Qt::Key_Slash, XK_KP_Divide }, + + { Qt::Key_ScrollLock, XK_Scroll_Lock }, +}; + +quint32 qt_mods_to_x11(Qt::KeyboardModifiers modifiers) +{ + quint32 mods = 0; + + if (modifiers & Qt::AltModifier) + mods |= Mod1Mask; + if (modifiers & Qt::ControlModifier) + mods |= ControlMask; + if (modifiers & Qt::ShiftModifier) + mods |= ShiftMask; + if (modifiers & Qt::KeypadModifier) + mods |= Mod2Mask; + if (modifiers & Qt::MetaModifier) // Super aka Windows key + mods |= Mod4Mask; + + return mods; +} + +std::vector<quint32> qt_key_to_x11(Display*, Qt::Key k, Qt::KeyboardModifiers) +{ + std::vector<quint32> ret; + + for (const tt& tuple : keymap) + { + Qt::Key k_ = tuple.qt; + unsigned keycode = tuple.keysym; + + if (k == k_) + ret.push_back(keycode); + } + + if (ret.size() == 0) + qDebug() << "qxt-mini: no keysym for" << k; + + return ret; +} + +Qt::KeyboardModifiers x11_mods_to_qt(quint32 mods) +{ + int ret{0}; + + if (mods & Mod1Mask) + ret |= Qt::AltModifier; + if (mods & ControlMask) + ret |= Qt::ControlModifier; + if (mods & Mod4Mask) + ret |= Qt::MetaModifier; + if (mods & ShiftMask) + ret |= Qt::ShiftModifier; + + return {ret}; +} + +std::tuple<Qt::Key, Qt::KeyboardModifiers> x11_key_to_qt(Display* disp, quint32 keycode, quint32 mods) +{ + (void)disp; + using t = std::tuple<Qt::Key, Qt::KeyboardModifiers>; + + for (const tt& tuple : keymap) + { + const Qt::Key k = tuple.qt; + + if (keycode == tuple.keysym) + return t(k, x11_mods_to_qt(mods)); + } + + return t(Qt::Key(0), Qt::KeyboardModifier(0)); +} + + +QPair<KeySym, KeySym> keycode_to_keysym(Display* disp, + quint32 keycode, quint32 keystate, + xcb_key_press_event_t const* kev) +{ + using pair = QPair<quint32, quint32>; + + static QMutex lock; + static QHash<pair, QPair<KeySym, KeySym>> values; + + QMutexLocker l(&lock); + + auto it = values.find(pair(keycode, keystate)); + + if (it != values.end()) + return *it; + + XKeyEvent ev{}; + ev.serial = kev->sequence; + ev.send_event = False; + ev.display = disp; + ev.root = kev->root; + ev.subwindow = kev->child; + ev.window = kev->event; + ev.time = kev->time; + ev.x = kev->event_x; + ev.y = kev->event_x; + ev.x_root = kev->root_x; + ev.y_root = kev->root_y; + ev.state = keystate; + ev.keycode = keycode; + ev.same_screen = kev->same_screen; + + static char bytes[255+1]; + KeySym sym = 0; + int len = XLookupString(&ev, bytes, 255, &sym, nullptr); + if (len <= 0 || len > 255) + { + len = 0; + sym = 0; + } + + KeySym sym2 = XLookupKeysym(&ev, 0); + + QPair<KeySym, KeySym> ret(sym, sym2); + + values[pair(keycode, keystate)] = ret; + + return ret; +} + +quint32 xcb_mods_to_x11(quint32 mods) +{ + unsigned keystate = 0; + + if(mods & XCB_MOD_MASK_1) // alt + keystate |= Mod1Mask; + if(mods & XCB_MOD_MASK_CONTROL) // ctrl + keystate |= ControlMask; + if(mods & XCB_MOD_MASK_4) // super aka win key + keystate |= Mod4Mask; + if(mods & XCB_MOD_MASK_SHIFT) //shift + keystate |= ShiftMask; + if(mods & XCB_MOD_MASK_2) // numlock + keystate |= Mod2Mask; + + return keystate; +} +#endif diff --git a/qxt-mini/x11-keymap.hpp b/qxt-mini/x11-keymap.hpp new file mode 100644 index 00000000..a6f2a061 --- /dev/null +++ b/qxt-mini/x11-keymap.hpp @@ -0,0 +1,38 @@ +#pragma once + +#if !defined __APPLE__ && !defined _WIN32 + +#undef QXT_X11_INCLUDED +#define QXT_X11_INCLUDED + +#include <Qt> +#include <QDebug> +#include <QHash> +#include <QPair> + +#include "qxtglobal.h" + +#include <vector> +#include <tuple> + +#include <xcb/xcb.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +std::vector<quint32> +qt_key_to_x11(Display* disp, Qt::Key k, Qt::KeyboardModifiers m); + +std::tuple<Qt::Key, Qt::KeyboardModifiers> +x11_key_to_qt(Display* disp, quint32 keycode, quint32 mods); + +QPair<KeySym, KeySym> +keycode_to_keysym(Display* disp, + quint32 keycode, quint32 keystate, + xcb_key_press_event_t const* kev); + +quint32 qt_mods_to_x11(Qt::KeyboardModifiers modifiers); + +Qt::KeyboardModifiers x11_mods_to_qt(quint32 mods); +quint32 xcb_mods_to_x11(quint32 mods); + +#endif |
