From f5cbc2c8888a4b13adaee26bcb1e3d2e5ca87672 Mon Sep 17 00:00:00 2001 From: Joel Smith Date: Tue, 5 Apr 2022 11:12:22 -0700 Subject: [PATCH] replace uglobalhotkey submodule with localized version (#117) --- .gitmodules | 3 - ashirt.pro | 20 +- src/helpers/hotkeys/hotkeymap.h | 296 +++++++++++++++++++++++++ src/helpers/hotkeys/uglobal.h | 14 ++ src/helpers/hotkeys/uglobalhotkeys.cpp | 237 ++++++++++++++++++++ src/helpers/hotkeys/uglobalhotkeys.h | 77 +++++++ src/helpers/hotkeys/ukeysequence.cpp | 135 +++++++++++ src/helpers/hotkeys/ukeysequence.h | 73 ++++++ src/hotkeymanager.cpp | 1 + src/hotkeymanager.h | 2 +- src/traymanager.cpp | 2 +- src/traymanager.h | 2 +- tools/UGlobalHotkey | 1 - 13 files changed, 854 insertions(+), 9 deletions(-) delete mode 100644 .gitmodules create mode 100644 src/helpers/hotkeys/hotkeymap.h create mode 100644 src/helpers/hotkeys/uglobal.h create mode 100644 src/helpers/hotkeys/uglobalhotkeys.cpp create mode 100644 src/helpers/hotkeys/uglobalhotkeys.h create mode 100644 src/helpers/hotkeys/ukeysequence.cpp create mode 100644 src/helpers/hotkeys/ukeysequence.h delete mode 160000 tools/UGlobalHotkey diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 29d582d..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "tools/UGlobalHotkey"] - path = tools/UGlobalHotkey - url = https://github.com/JoelAtDeluxe/UGlobalHotkey.git diff --git a/ashirt.pro b/ashirt.pro index 3c82f94..70f0d5b 100644 --- a/ashirt.pro +++ b/ashirt.pro @@ -1,4 +1,4 @@ -QT += core gui network sql widgets +QT += core gui network sql widgets gui-private CONFIG += c++11 TEMPLATE = app @@ -66,6 +66,8 @@ SOURCES += \ src/forms/getinfo/getinfo.cpp \ src/forms/porting/porting_dialog.cpp \ src/helpers/clipboard/clipboardhelper.cpp \ + src/helpers/hotkeys/uglobalhotkeys.cpp \ + src/helpers/hotkeys/ukeysequence.cpp \ src/models/codeblock.cpp \ src/helpers/multipartparser.cpp \ src/hotkeymanager.cpp \ @@ -111,6 +113,10 @@ HEADERS += \ src/forms/porting/porting_dialog.h \ src/helpers/clipboard/clipboardhelper.h \ src/helpers/constants.h \ + src/helpers/hotkeys/hotkeymap.h \ + src/helpers/hotkeys/uglobal.h \ + src/helpers/hotkeys/uglobalhotkeys.h \ + src/helpers/hotkeys/ukeysequence.h \ src/helpers/request_builder.h \ src/helpers/system_helpers.h \ src/helpers/ui_helpers.h \ @@ -137,7 +143,17 @@ HEADERS += \ src/forms/evidence/evidencemanager.h \ src/forms/settings/settings.h -include(tools/UGlobalHotkey/uglobalhotkey.pri) +linux: LIBS += -lxcb -lxcb-keysyms +mac: LIBS += -framework Carbon + +windows { + *-g++* { + LIBS += -luser32 + } + *-msvc* { + LIBS += user32.lib + } +} macx { ICON = icons/ashirt.icns diff --git a/src/helpers/hotkeys/hotkeymap.h b/src/helpers/hotkeys/hotkeymap.h new file mode 100644 index 0000000..951b8c6 --- /dev/null +++ b/src/helpers/hotkeys/hotkeymap.h @@ -0,0 +1,296 @@ +#include + +#if defined(Q_OS_WIN) +inline size_t QtKeyToWin(Qt::Key key) +{ + switch ((Qt::Key)key) { + case Qt::Key_Escape: + return VK_ESCAPE; + case Qt::Key_Tab: + case Qt::Key_Backtab: + return VK_TAB; + case Qt::Key_Backspace: + return VK_BACK; + case Qt::Key_Return: + case Qt::Key_Enter: + return VK_RETURN; + case Qt::Key_Insert: + return VK_INSERT; + case Qt::Key_Delete: + return VK_DELETE; + case Qt::Key_Pause: + return VK_PAUSE; + case Qt::Key_Print: + return VK_SNAPSHOT; + case Qt::Key_Clear: + return VK_CLEAR; + case Qt::Key_Home: + return VK_HOME; + case Qt::Key_End: + return VK_END; + case Qt::Key_Left: + return VK_LEFT; + case Qt::Key_Up: + return VK_UP; + case Qt::Key_Right: + return VK_RIGHT; + case Qt::Key_Down: + return VK_DOWN; + case Qt::Key_PageUp: + return VK_PRIOR; + case Qt::Key_PageDown: + return VK_NEXT; + case Qt::Key_F1: + return VK_F1; + case Qt::Key_F2: + return VK_F2; + case Qt::Key_F3: + return VK_F3; + case Qt::Key_F4: + return VK_F4; + case Qt::Key_F5: + return VK_F5; + case Qt::Key_F6: + return VK_F6; + case Qt::Key_F7: + return VK_F7; + case Qt::Key_F8: + return VK_F8; + case Qt::Key_F9: + return VK_F9; + case Qt::Key_F10: + return VK_F10; + case Qt::Key_F11: + return VK_F11; + case Qt::Key_F12: + return VK_F12; + case Qt::Key_F13: + return VK_F13; + case Qt::Key_F14: + return VK_F14; + case Qt::Key_F15: + return VK_F15; + case Qt::Key_F16: + return VK_F16; + case Qt::Key_F17: + return VK_F17; + case Qt::Key_F18: + return VK_F18; + case Qt::Key_F19: + return VK_F19; + case Qt::Key_F20: + return VK_F20; + case Qt::Key_F21: + return VK_F21; + case Qt::Key_F22: + return VK_F22; + case Qt::Key_F23: + return VK_F23; + case Qt::Key_F24: + return VK_F24; + case Qt::Key_Space: + return VK_SPACE; + case Qt::Key_Asterisk: + return VK_MULTIPLY; + case Qt::Key_Plus: + return VK_ADD; + case Qt::Key_Comma: + return VK_SEPARATOR; + case Qt::Key_Minus: + return VK_SUBTRACT; + case Qt::Key_Slash: + return VK_DIVIDE; + case Qt::Key_MediaNext: + return VK_MEDIA_NEXT_TRACK; + case Qt::Key_MediaPrevious: + return VK_MEDIA_PREV_TRACK; + case Qt::Key_MediaPlay: + return VK_MEDIA_PLAY_PAUSE; + case Qt::Key_MediaStop: + return VK_MEDIA_STOP; + case Qt::Key_VolumeDown: + return VK_VOLUME_DOWN; + case Qt::Key_VolumeUp: + return VK_VOLUME_UP; + case Qt::Key_VolumeMute: + return VK_VOLUME_MUTE; + } + + if (key >= 0x01000030 && key <= 0x01000047) { + return VK_F1 + (key - Qt::Key_F1); + } + + return key; +} +#elif defined(Q_OS_LINUX) + +#include "ukeysequence.h" +#include +#include "xcb/xcb.h" +#include "xcb/xcb_keysyms.h" +#include "X11/keysym.h" + +struct UKeyData { + int key; + int mods; +}; + +static std::unordered_map KEY_MAP = { + {Qt::Key_Escape, XK_Escape}, + {Qt::Key_Tab, XK_Tab}, + {Qt::Key_Backspace, XK_BackSpace}, + {Qt::Key_Return, XK_Return}, + {Qt::Key_Enter, XK_Return}, + {Qt::Key_Insert, XK_Insert}, + {Qt::Key_Delete, XK_Delete}, + {Qt::Key_Pause, XK_Pause}, + {Qt::Key_Print, XK_Print}, + {Qt::Key_SysReq, XK_Sys_Req}, + {Qt::Key_Clear, XK_Clear}, + {Qt::Key_Home, XK_Home}, + {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_Page_Up}, + {Qt::Key_PageDown, XK_Page_Down} +}; + +inline UKeyData QtKeyToLinux(const UKeySequence &keySeq) +{ + UKeyData data = {0, 0}; + + auto key = keySeq.getSimpleKeys(); + if (key.size() > 0) { + data.key = key[0]; + } else { + qWarning() << "Invalid hotkey"; + return data; + } + // Key conversion + // Misc Keys + if (KEY_MAP.find(key[0]) != KEY_MAP.end()) { + data.key = KEY_MAP[key[0]]; + } else if (data.key >= Qt::Key_F1 && data.key <= Qt::Key_F35) { // Qt's F keys need conversion + const size_t DIFF = Qt::Key_F1 - XK_F1; + data.key -= DIFF; + } else if (data.key >= Qt::Key_Space && data.key <= Qt::Key_QuoteLeft) { + // conversion is not necessary, if the value in the range Qt::Key_Space - Qt::Key_QuoteLeft + } else { + qWarning() << "Invalid hotkey: key conversion is not defined"; + return data; + } + + // Modifiers conversion + auto mods = keySeq.getModifiers(); + + for (auto i : mods) { + if (i == Qt::Key_Shift) + data.mods |= XCB_MOD_MASK_SHIFT; + else if (i == Qt::Key_Control) + data.mods |= XCB_MOD_MASK_CONTROL; + else if (i == Qt::Key_Alt) + data.mods |= XCB_MOD_MASK_1; + else if (i == Qt::Key_Meta) + data.mods |= XCB_MOD_MASK_4; // ! + } + + return data; +} +#elif defined(Q_OS_MAC) + +#include "ukeysequence.h" +#include +#include + +struct UKeyData { + uint32_t key; + uint32_t mods; +}; + +static std::unordered_map KEY_MAP = { + {Qt::Key_A, kVK_ANSI_A}, + {Qt::Key_B, kVK_ANSI_B}, + {Qt::Key_C, kVK_ANSI_C}, + {Qt::Key_D, kVK_ANSI_D}, + {Qt::Key_E, kVK_ANSI_E}, + {Qt::Key_F, kVK_ANSI_F}, + {Qt::Key_G, kVK_ANSI_G}, + {Qt::Key_H, kVK_ANSI_H}, + {Qt::Key_I, kVK_ANSI_I}, + {Qt::Key_J, kVK_ANSI_J}, + {Qt::Key_K, kVK_ANSI_K}, + {Qt::Key_L, kVK_ANSI_L}, + {Qt::Key_M, kVK_ANSI_M}, + {Qt::Key_N, kVK_ANSI_N}, + {Qt::Key_O, kVK_ANSI_O}, + {Qt::Key_P, kVK_ANSI_P}, + {Qt::Key_Q, kVK_ANSI_Q}, + {Qt::Key_R, kVK_ANSI_R}, + {Qt::Key_S, kVK_ANSI_S}, + {Qt::Key_T, kVK_ANSI_T}, + {Qt::Key_U, kVK_ANSI_U}, + {Qt::Key_V, kVK_ANSI_V}, + {Qt::Key_W, kVK_ANSI_W}, + {Qt::Key_X, kVK_ANSI_X}, + {Qt::Key_Y, kVK_ANSI_Y}, + {Qt::Key_Z, kVK_ANSI_Z}, + {Qt::Key_0, kVK_ANSI_0}, + {Qt::Key_1, kVK_ANSI_1}, + {Qt::Key_2, kVK_ANSI_2}, + {Qt::Key_3, kVK_ANSI_3}, + {Qt::Key_4, kVK_ANSI_4}, + {Qt::Key_5, kVK_ANSI_5}, + {Qt::Key_6, kVK_ANSI_6}, + {Qt::Key_7, kVK_ANSI_7}, + {Qt::Key_8, kVK_ANSI_8}, + {Qt::Key_9, kVK_ANSI_9}, + {Qt::Key_F1, kVK_F1}, + {Qt::Key_F2, kVK_F2}, + {Qt::Key_F3, kVK_F3}, + {Qt::Key_F4, kVK_F4}, + {Qt::Key_F5, kVK_F5}, + {Qt::Key_F6, kVK_F6}, + {Qt::Key_F7, kVK_F7}, + {Qt::Key_F8, kVK_F8}, + {Qt::Key_F9, kVK_F9}, + {Qt::Key_F10, kVK_F10}, + {Qt::Key_F11, kVK_F11}, + {Qt::Key_F12, kVK_F12}, + {Qt::Key_F13, kVK_F13}, + {Qt::Key_F14, kVK_F14}, + {Qt::Key_Print, kVK_F14}, +}; + +static std::unordered_map MOD_MAP = { + {Qt::Key_Shift, shiftKey}, + {Qt::Key_Alt, optionKey}, + {Qt::Key_Control, controlKey}, + {Qt::Key_Option, optionKey}, + {Qt::Key_Meta, cmdKey}, +}; + +inline UKeyData QtKeyToMac(const UKeySequence &keySeq) +{ + UKeyData data = {0, 0}; + auto key = keySeq.getSimpleKeys(); + auto mods = keySeq.getModifiers(); + + if (key.size() == 1 && KEY_MAP.find(key[0]) != KEY_MAP.end()) { + data.key = KEY_MAP[key[0]]; + } else { + qWarning() << "Invalid hotkey"; + return data; + } + + for (auto && mod : mods) { + if (MOD_MAP.find(mod) == MOD_MAP.end()) + return data; + + data.mods += MOD_MAP[mod]; + } + return data; +} + +#endif diff --git a/src/helpers/hotkeys/uglobal.h b/src/helpers/hotkeys/uglobal.h new file mode 100644 index 0000000..434d2a0 --- /dev/null +++ b/src/helpers/hotkeys/uglobal.h @@ -0,0 +1,14 @@ +#ifndef UGLOBAL_H +#define UGLOBAL_H + +#include + +#if defined(UGLOBALHOTKEY_LIBRARY) +# define UGLOBALHOTKEY_EXPORT Q_DECL_EXPORT +#elif defined(UGLOBALHOTKEY_NOEXPORT) +# define UGLOBALHOTKEY_EXPORT +#else +# define UGLOBALHOTKEY_EXPORT Q_DECL_IMPORT +#endif + +#endif // UGLOBAL_H diff --git a/src/helpers/hotkeys/uglobalhotkeys.cpp b/src/helpers/hotkeys/uglobalhotkeys.cpp new file mode 100644 index 0000000..0320b06 --- /dev/null +++ b/src/helpers/hotkeys/uglobalhotkeys.cpp @@ -0,0 +1,237 @@ +#include +#if defined(Q_OS_WIN) +#include +#elif defined(Q_OS_LINUX) +#include +#include +#include +#endif + +#include "hotkeymap.h" +#include "uglobalhotkeys.h" + +UGlobalHotkeys::UGlobalHotkeys(QWidget *parent) + : QWidget(parent) +{ +#if defined(Q_OS_LINUX) + qApp->installNativeEventFilter(this); + QWindow wndw; + void *v = qApp->platformNativeInterface()->nativeResourceForWindow("connection", &wndw); + X11Connection = (xcb_connection_t *)v; + X11Wid = xcb_setup_roots_iterator(xcb_get_setup(X11Connection)).data->root; + X11KeySymbs = xcb_key_symbols_alloc(X11Connection); +#endif +} + +bool UGlobalHotkeys::registerHotkey(const QString &keySeq, size_t id) +{ + return registerHotkey(UKeySequence(keySeq), id); +} + +#if defined(Q_OS_MAC) +OSStatus macHotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) +{ + Q_UNUSED(nextHandler); + EventHotKeyID hkCom; + GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, + sizeof(hkCom), NULL, &hkCom); + size_t id = hkCom.id; + + UGlobalHotkeys *caller = (UGlobalHotkeys *)userData; + caller->onHotkeyPressed(id); + return noErr; +} +#endif + +bool UGlobalHotkeys::registerHotkey(const UKeySequence &keySeq, size_t id) +{ + if (keySeq.size() == 0) { + return false; + } +#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) + if (Registered.find(id) != Registered.end()) { + unregisterHotkey(id); + } +#endif +#if defined(Q_OS_WIN) + size_t winMod = 0; + size_t key = VK_F2; + + for (size_t i = 0; i != keySeq.size(); i++) { + if (keySeq[i] == Qt::Key_Control) { + winMod |= MOD_CONTROL; + } else if (keySeq[i] == Qt::Key_Alt) { + winMod |= MOD_ALT; + } else if (keySeq[i] == Qt::Key_Shift) { + winMod |= MOD_SHIFT; + } else if (keySeq[i] == Qt::Key_Meta) { + winMod |= MOD_WIN; + } else { + key = QtKeyToWin(keySeq[i]); + } + } + + if (!RegisterHotKey((HWND)winId(), id, winMod, key)) { + return false; + } else { + Registered.insert(id); + } +#elif defined(Q_OS_LINUX) + regLinuxHotkey(keySeq, id); +#endif +#if defined(Q_OS_MAC) + unregisterHotkey(id); + + EventHotKeyRef gMyHotKeyRef; + EventHotKeyID gMyHotKeyID; + EventTypeSpec eventType; + eventType.eventClass = kEventClassKeyboard; + eventType.eventKind = kEventHotKeyPressed; + + InstallApplicationEventHandler(&macHotkeyHandler, 1, &eventType, this, NULL); + + gMyHotKeyID.signature = uint32_t(id); + gMyHotKeyID.id = uint32_t(id); + + UKeyData macKey = QtKeyToMac(keySeq); + + RegisterEventHotKey(macKey.key, macKey.mods, gMyHotKeyID, + GetApplicationEventTarget(), 0, &gMyHotKeyRef); + + HotkeyRefs[id] = gMyHotKeyRef; +#endif + + return true; +} + +void UGlobalHotkeys::unregisterHotkey(size_t id) +{ +#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) + Q_ASSERT(Registered.find(id) != Registered.end() && "Unregistered hotkey"); +#endif +#if defined(Q_OS_WIN) + UnregisterHotKey((HWND)winId(), id); +#elif defined(Q_OS_LINUX) + unregLinuxHotkey(id); +#endif +#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) + Registered.remove(id); +#endif +#if defined(Q_OS_MAC) + if (HotkeyRefs.find(id) != HotkeyRefs.end()) { + UnregisterEventHotKey(HotkeyRefs[id]); + } +#endif +} + +void UGlobalHotkeys::unregisterAllHotkeys() +{ +#ifdef Q_OS_WIN + foreach (const size_t id, Registered) { + this->unregisterHotkey(id); + } +#elif defined(Q_OS_LINUX) + foreach (const size_t id, Registered.keys()) { + this->unregisterHotkey(id); + } +#elif defined(Q_OS_MAC) + for (auto ref : HotkeyRefs) { + UnregisterEventHotKey(ref); + } +#endif +} + +UGlobalHotkeys::~UGlobalHotkeys() +{ +#if defined(Q_OS_WIN) + for (QSet::iterator i = Registered.begin(); i != Registered.end(); ++i) { + UnregisterHotKey((HWND)winId(), *i); + } +#elif defined(Q_OS_LINUX) + xcb_key_symbols_free(X11KeySymbs); +#endif +} + +#if defined(Q_OS_MAC) +void UGlobalHotkeys::onHotkeyPressed(size_t id) +{ + emit activated(id); +} +#endif + +#if defined(Q_OS_WIN) +bool UGlobalHotkeys::winEvent(MSG *message, long *result) +{ + Q_UNUSED(result); + if (message->message == WM_HOTKEY) { + size_t id = message->wParam; + Q_ASSERT(Registered.find(id) != Registered.end() && "Unregistered hotkey"); + emit activated(id); + } + return false; +} + +bool UGlobalHotkeys::nativeEvent(const QByteArray &eventType, + void *message, long *result) +{ + Q_UNUSED(eventType); + return winEvent((MSG *)message, result); +} + +#elif defined(Q_OS_LINUX) + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool UGlobalHotkeys::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) +#else +bool UGlobalHotkeys::nativeEventFilter(const QByteArray &eventType, void *message, long *result) +#endif +{ + Q_UNUSED(eventType); + Q_UNUSED(result); + return linuxEvent(static_cast(message)); +} + +bool UGlobalHotkeys::linuxEvent(xcb_generic_event_t *message) +{ + if ((message->response_type & ~0x80) == XCB_KEY_PRESS) { + xcb_key_press_event_t *ev = (xcb_key_press_event_t *)message; + auto ind = Registered.key({ev->detail, (ev->state & ~XCB_MOD_MASK_2)}); + + if (ind == 0) // this is not hotkeys + return false; + + emit activated(ind); + return true; + } + return false; +} + +void UGlobalHotkeys::regLinuxHotkey(const UKeySequence &keySeq, size_t id) +{ + UHotkeyData data; + UKeyData keyData = QtKeyToLinux(keySeq); + + xcb_keycode_t *keyC = xcb_key_symbols_get_keycode(X11KeySymbs, keyData.key); + + if (keyC == XCB_NO_SYMBOL) { // 0x0 + qWarning() << "Cannot find symbol"; + return; + } + + data.keyCode = *keyC; + data.mods = keyData.mods; + + xcb_grab_key(X11Connection, 1, X11Wid, data.mods, data.keyCode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + // NumLk + xcb_grab_key(X11Connection, 1, X11Wid, data.mods | XCB_MOD_MASK_2, data.keyCode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + + Registered.insert(id, data); +} + +void UGlobalHotkeys::unregLinuxHotkey(size_t id) +{ + UHotkeyData data = Registered.take(id); + xcb_ungrab_key(X11Connection, data.keyCode, X11Wid, data.mods); + xcb_ungrab_key(X11Connection, data.keyCode, X11Wid, data.mods | XCB_MOD_MASK_2); +} +#endif diff --git a/src/helpers/hotkeys/uglobalhotkeys.h b/src/helpers/hotkeys/uglobalhotkeys.h new file mode 100644 index 0000000..755eb1e --- /dev/null +++ b/src/helpers/hotkeys/uglobalhotkeys.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include + +#if defined(Q_OS_LINUX) +#include "xcb/xcb.h" +#include "xcb/xcb_keysyms.h" +#elif defined(Q_OS_MAC) +#include +#endif + +#include "ukeysequence.h" +#include "uglobal.h" + +#if defined(Q_OS_LINUX) +struct UHotkeyData { + xcb_keycode_t keyCode; + int mods; + bool operator ==(const UHotkeyData &data) const + { + return data.keyCode == this->keyCode && data.mods == this->mods; + } +}; +#endif + +class UGLOBALHOTKEY_EXPORT UGlobalHotkeys : public QWidget +#if defined(Q_OS_LINUX) + , public QAbstractNativeEventFilter +#endif +{ + Q_OBJECT + +public: + explicit UGlobalHotkeys(QWidget *parent = 0); + bool registerHotkey(const QString &keySeq, size_t id = 1); + bool registerHotkey(const UKeySequence &keySeq, size_t id = 1); + void unregisterHotkey(size_t id = 1); + void unregisterAllHotkeys(); + ~UGlobalHotkeys(); + +protected: +#if defined(Q_OS_WIN) + bool winEvent(MSG *message, long *result); + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#elif defined(Q_OS_LINUX) + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; +#else + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; +#endif + bool linuxEvent(xcb_generic_event_t *message); + void regLinuxHotkey(const UKeySequence &keySeq, size_t id); + void unregLinuxHotkey(size_t id); +#endif + +public: +#if defined (Q_OS_MAC) + void onHotkeyPressed(size_t id); +#endif +signals: + void activated(size_t id); + +private: +#if defined(Q_OS_WIN) + QSet Registered; +#elif defined(Q_OS_LINUX) + QHash Registered; + xcb_connection_t *X11Connection; + xcb_window_t X11Wid; + xcb_key_symbols_t *X11KeySymbs; +#elif defined(Q_OS_MAC) + QHash HotkeyRefs; +#endif +}; diff --git a/src/helpers/hotkeys/ukeysequence.cpp b/src/helpers/hotkeys/ukeysequence.cpp new file mode 100644 index 0000000..2dc16c4 --- /dev/null +++ b/src/helpers/hotkeys/ukeysequence.cpp @@ -0,0 +1,135 @@ +#include "ukeysequence.h" + +#include + +UKeySequence::UKeySequence(QObject *parent) + : QObject(parent) +{ +} + +UKeySequence::UKeySequence(const QString &str, QObject *parent) + : QObject(parent) +{ + fromString(str); +} + +void UKeySequence::fromString(const QString &str) +{ + QStringList keys = str.split('+'); + for (int i = 0; i < keys.size(); i++) { + addKey(keys[i]); + } +} + +QString UKeySequence::toString() +{ + QVector simpleKeys = getSimpleKeys(); + QVector modifiers = getModifiers(); + QStringList result; + for (int i = 0; i < modifiers.size(); i++) { + result.push_back(keyToStr(modifiers[i])); + } + for (int i = 0; i < simpleKeys.size(); i++) { + result.push_back(keyToStr(simpleKeys[i])); + } + return result.join('+'); +} + +QVector UKeySequence::getSimpleKeys() const +{ + QVector result; + for (int i = 0; i < mKeys.size(); i++) { + if (!isModifier(mKeys[i])) { + result.push_back(mKeys[i]); + } + } + return result; +} + +QVector UKeySequence::getModifiers() const +{ + QVector result; + for (int i = 0; i < mKeys.size(); i++) { + if (isModifier(mKeys[i])) { + result.push_back(mKeys[i]); + } + } + return result; +} + +void UKeySequence::addModifiers(Qt::KeyboardModifiers mod) +{ + if (mod == Qt::NoModifier) { + return; + } + if (mod & Qt::ShiftModifier) { + addKey(Qt::Key_Shift); + } + if (mod & Qt::ControlModifier) { + addKey(Qt::Key_Control); + } + if (mod & Qt::AltModifier) { + addKey(Qt::Key_Alt); + } + if (mod & Qt::MetaModifier) { + addKey(Qt::Key_Meta); + } +} + +void UKeySequence::addKey(const QString &key) +{ + if (key.contains("+") || key.contains(",")) { + qWarning() << "Wrong key"; + return; + } + + QString mod = key.toLower(); + // qDebug() << "mod: " << mod; + if (mod == "alt") { + addKey(Qt::Key_Alt); + return; + } + if (mod == "shift" || mod == "shft") { + addKey(Qt::Key_Shift); + return; + } + if (mod == "control" || mod == "ctrl") { + addKey(Qt::Key_Control); + return; + } + if (mod == "win" || mod == "meta") { + addKey(Qt::Key_Meta); + return; + } + QKeySequence seq(key); + if (seq.count() != 1) { + qWarning() << "Wrong key"; + return; + } + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + addKey((Qt::Key) seq[0]); +#else + addKey(seq[0].key()); +#endif +} + +void UKeySequence::addKey(Qt::Key key) +{ + if (key <= 0) { + return; + } + for (int i = 0; i < mKeys.size(); i++) { + if (mKeys[i] == key) { + return; + } + } + // qDebug() << "Key added: " << key; + mKeys.push_back(key); +} + +void UKeySequence::addKey(const QKeyEvent *event) +{ + addKey((Qt::Key) event->key()); + addModifiers(event->modifiers()); +} diff --git a/src/helpers/hotkeys/ukeysequence.h b/src/helpers/hotkeys/ukeysequence.h new file mode 100644 index 0000000..725ec5a --- /dev/null +++ b/src/helpers/hotkeys/ukeysequence.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "uglobal.h" + +class UGLOBALHOTKEY_EXPORT UKeySequence : public QObject +{ + Q_OBJECT + +public: + explicit UKeySequence(QObject *parent = 0); + explicit UKeySequence(const QString &str, QObject *parent = 0); + + void fromString(const QString &str); + QString toString(); + void addKey(Qt::Key key); + void addKey(const QString &key); + void addModifiers(Qt::KeyboardModifiers mod); + void addKey(const QKeyEvent *event); + + inline size_t size() const + { + return mKeys.size(); + } + inline Qt::Key operator [](size_t n) const + { + if ((int)n > mKeys.size()) { + return Qt::Key_unknown; + } + + return mKeys[n]; + } + + QVector getSimpleKeys() const; + QVector getModifiers() const; + +private: + QVector mKeys; + + inline static bool isModifier(Qt::Key key) + { + return (key == Qt::Key_Shift || + key == Qt::Key_Control || + key == Qt::Key_Alt || + key == Qt::Key_Meta); + } + + inline static QString keyToStr(int key) + { + if (key == Qt::Key_Shift) { + return "Shift"; + } + if (key == Qt::Key_Control) { + return "Ctrl"; + } + if (key == Qt::Key_Alt) { + return "Alt"; + } + if (key == Qt::Key_Meta) { + return "Meta"; + } + + QKeySequence seq(key); + return seq.toString(); + } + +}; + diff --git a/src/hotkeymanager.cpp b/src/hotkeymanager.cpp index 84fbf8b..8fa6f8b 100644 --- a/src/hotkeymanager.cpp +++ b/src/hotkeymanager.cpp @@ -8,6 +8,7 @@ #include "appconfig.h" #include "appsettings.h" +#include "helpers/hotkeys/uglobalhotkeys.h" HotkeyManager::HotkeyManager() { hotkeyManager = new UGlobalHotkeys(); diff --git a/src/hotkeymanager.h b/src/hotkeymanager.h index 1eaada4..ea3289d 100644 --- a/src/hotkeymanager.h +++ b/src/hotkeymanager.h @@ -7,7 +7,7 @@ #include #include "helpers/screenshot.h" -#include "tools/UGlobalHotkey/uglobalhotkeys.h" +#include "helpers/hotkeys/uglobalhotkeys.h" /** * @brief The HotkeyManager class registers and unregisters hotkeys against the operating system. diff --git a/src/traymanager.cpp b/src/traymanager.cpp index e8eb752..341982a 100644 --- a/src/traymanager.cpp +++ b/src/traymanager.cpp @@ -23,9 +23,9 @@ #include "helpers/netman.h" #include "helpers/screenshot.h" #include "helpers/constants.h" +#include "helpers/hotkeys/uglobalhotkeys.h" #include "hotkeymanager.h" #include "models/codeblock.h" -#include "tools/UGlobalHotkey/uglobalhotkeys.h" #include "porting/system_manifest.h" // Tray icons are handled differently between different OS and desktop diff --git a/src/traymanager.h b/src/traymanager.h index 365398f..647fd6b 100644 --- a/src/traymanager.h +++ b/src/traymanager.h @@ -16,7 +16,7 @@ #include "forms/settings/settings.h" #include "helpers/screenshot.h" #include "hotkeymanager.h" -#include "tools/UGlobalHotkey/uglobalhotkeys.h" +#include "helpers/hotkeys/uglobalhotkeys.h" #include "forms/add_operation/createoperation.h" #ifndef QT_NO_SYSTEMTRAYICON diff --git a/tools/UGlobalHotkey b/tools/UGlobalHotkey deleted file mode 160000 index e53db08..0000000 --- a/tools/UGlobalHotkey +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e53db08a970694c4bb92e8ec4f2a4173a4125d37