Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F134662
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
14 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c22d4af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+# C++ objects and libs
+
+*.slo
+*.lo
+*.o
+*.a
+*.la
+*.lai
+*.so
+*.dll
+*.dylib
+
+# Qt-es
+
+*.pro.user
+*.pro.user.*
+moc_*.cpp
+qrc_*.cpp
+Makefile
+*-build-*
+build
+build*
+*build
diff --git a/hotkeymap.h b/hotkeymap.h
new file mode 100644
index 0000000..200f887
--- /dev/null
+++ b/hotkeymap.h
@@ -0,0 +1,62 @@
+#include <QtCore>
+#include "uglobal.h"
+
+#if defined(Q_OS_WIN)
+size_t QtKeyToWin(size_t key) {
+ // TODO: other maping or full keys list
+
+ if (key >= 0x01000030 && key <= 0x01000047) {
+ return VK_F1 + (key - Qt::Key_F1);
+ }
+
+ return key;
+}
+#elif defined(Q_OS_LINUX)
+#include "ukeysequence.h"
+#include "xcb/xcb.h"
+#include "xcb/xcb_keysyms.h"
+#include "X11/keysym.h"
+
+struct UKeyData {
+ int key;
+ int mods;
+};
+
+UKeyData QtKeyToLinux(const UKeySequence &keySeq)
+{
+ UKeyData data = {0, 0};
+
+ auto key = keySeq.GetSimpleKeys();
+ if (key.size() > 0)
+ data.key = key[0];
+ else
+ throw UException("Invalid hotkey");
+
+ // Key conversion
+ // Qt's F keys need conversion
+ if (data.key >= Qt::Key_F1 && data.key <= Qt::Key_F35) {
+ 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 {
+ throw UException("Invalid hotkey: key conversion is not defined");
+ }
+
+ // 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;
+}
+#endif
diff --git a/uexception.cpp b/uexception.cpp
new file mode 100644
index 0000000..e29107a
--- /dev/null
+++ b/uexception.cpp
@@ -0,0 +1,13 @@
+#include "uexception.h"
+
+UException::UException(const QString& message) throw()
+ : Message(message.toLocal8Bit())
+{
+}
+
+UException::~UException() throw () {
+}
+
+const char* UException::what() const throw () {
+ return Message.data();
+}
diff --git a/uexception.h b/uexception.h
new file mode 100644
index 0000000..c580b90
--- /dev/null
+++ b/uexception.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <exception>
+#include <QString>
+#include <QByteArray>
+#include "uglobal.h"
+
+class UGLOBALHOTKEY_EXPORT UException : public std::exception
+{
+public:
+ UException(const QString& message) throw();
+ const char* what() const throw();
+ ~UException() throw ();
+private:
+ QByteArray Message;
+};
diff --git a/uglobal.h b/uglobal.h
new file mode 100644
index 0000000..e847e19
--- /dev/null
+++ b/uglobal.h
@@ -0,0 +1,12 @@
+#ifndef UGLOBAL_H
+#define UGLOBAL_H
+
+#include <QtCore/QtGlobal>
+
+#if defined(UGLOBALHOTKEY_LIBRARY)
+# define UGLOBALHOTKEY_EXPORT Q_DECL_EXPORT
+#else
+# define UGLOBALHOTKEY_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // UGLOBAL_H
diff --git a/uglobalhotkey.pro b/uglobalhotkey.pro
new file mode 100644
index 0000000..ae1e2ec
--- /dev/null
+++ b/uglobalhotkey.pro
@@ -0,0 +1,21 @@
+QT = core gui
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = UGlobalHotkey
+TEMPLATE = lib
+QMAKE_CXXFLAGS += -std=c++11
+
+DEFINES += UGLOBALHOTKEY_LIBRARY
+
+HEADERS += \
+ ukeysequence.h \
+ uglobalhotkeys.h \
+ uexception.h \
+ hotkeymap.h \
+ uglobal.h
+
+SOURCES += \
+ ukeysequence.cpp \
+ uglobalhotkeys.cpp \
+ uexception.cpp \
+
diff --git a/uglobalhotkeys.cpp b/uglobalhotkeys.cpp
new file mode 100644
index 0000000..cefdde0
--- /dev/null
+++ b/uglobalhotkeys.cpp
@@ -0,0 +1,148 @@
+#include <QtCore>
+#if defined(Q_OS_WIN)
+#include <windows.h>
+#elif defined(Q_OS_LINUX)
+#include <QWindow>
+#include <qpa/qplatformnativeinterface.h>
+#include <QApplication>
+#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
+}
+
+void UGlobalHotkeys::RegisterHotkey(const QString& keySeq, size_t id) {
+ RegisterHotkey(UKeySequence(keySeq), id);
+}
+
+void UGlobalHotkeys::RegisterHotkey(const UKeySequence& keySeq, size_t id) {
+ if (keySeq.Size() == 0) {
+ throw UException("Empty hotkeys");
+ }
+ if (Registered.find(id) != Registered.end()) {
+ UnregisterHotkey(id);
+ }
+ #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)) {
+ qDebug() << "Error activating hotkey!";
+ } else {
+ Registered.insert(id);
+ }
+ #elif defined(Q_OS_LINUX)
+ regLinuxHotkey(keySeq, id);
+ #endif
+}
+
+void UGlobalHotkeys::UnregisterHotkey(size_t id) {
+ Q_ASSERT(Registered.find(id) != Registered.end() && "Unregistered hotkey");
+ #if defined(Q_OS_WIN)
+ UnregisterHotKey((HWND)winId(), id);
+ #elif defined(Q_OS_LINUX)
+ unregLinuxHotkey(id);
+ #endif
+ Registered.remove(id);
+}
+
+UGlobalHotkeys::~UGlobalHotkeys() {
+ #if defined(Q_OS_WIN)
+ for (QSet<size_t>::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_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)
+
+bool UGlobalHotkeys::nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
+ Q_UNUSED(eventType);
+ Q_UNUSED(result);
+ return linuxEvent(static_cast<xcb_generic_event_t*>(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);
+
+ 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/uglobalhotkeys.h b/uglobalhotkeys.h
new file mode 100644
index 0000000..eb699a4
--- /dev/null
+++ b/uglobalhotkeys.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <QWidget>
+#include <QAbstractNativeEventFilter>
+#include <QSet>
+
+#if defined(Q_OS_LINUX)
+#include "xcb/xcb.h"
+#include "xcb/xcb_keysyms.h"
+#endif
+
+#include "ukeysequence.h"
+#include "uexception.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);
+ void RegisterHotkey(const QString& keySeq, size_t id = 1);
+ void RegisterHotkey(const UKeySequence& keySeq, size_t id = 1);
+ void UnregisterHotkey(size_t id = 1);
+ ~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)
+ bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
+ bool linuxEvent(xcb_generic_event_t *message);
+ void regLinuxHotkey(const UKeySequence& keySeq, size_t id);
+ void unregLinuxHotkey(size_t id);
+ #endif
+signals:
+ void Activated(size_t id);
+private:
+ #if defined(Q_OS_WIN)
+ QSet<size_t> Registered;
+ #elif defined(Q_OS_LINUX)
+ QHash<size_t, UHotkeyData> Registered;
+ xcb_connection_t* X11Connection;
+ xcb_window_t X11Wid;
+ xcb_key_symbols_t* X11KeySymbs;
+ #endif
+};
diff --git a/ukeysequence.cpp b/ukeysequence.cpp
new file mode 100644
index 0000000..c44c6d8
--- /dev/null
+++ b/ukeysequence.cpp
@@ -0,0 +1,140 @@
+#include "ukeysequence.h"
+
+#include <QDebug>
+
+UKeySequence::UKeySequence(QObject *parent)
+ : QObject(parent)
+{
+}
+
+UKeySequence::UKeySequence(const QString& str, QObject *parent)
+ : QObject(parent)
+{
+ FromString(str);
+}
+
+bool IsModifier(int key) {
+ return (key == Qt::Key_Shift ||
+ key == Qt::Key_Control ||
+ key == Qt::Key_Alt);
+}
+
+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";
+ }
+ QKeySequence seq(key);
+ return seq.toString();
+}
+
+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<int> simpleKeys = GetSimpleKeys();
+ QVector<int> 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<int> UKeySequence::GetSimpleKeys() const {
+ QVector<int> result;
+ for (int i = 0; i < Keys.size(); i++) {
+ if (!IsModifier(Keys[i])) {
+ result.push_back(Keys[i]);
+ }
+ }
+ return result;
+}
+
+QVector<int> UKeySequence::GetModifiers() const {
+ QVector<int> result;
+ for (int i = 0; i < Keys.size(); i++) {
+ if (IsModifier(Keys[i])) {
+ result.push_back(Keys[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(",")) {
+ throw UException("Wrong key");
+ }
+
+ 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) {
+ throw UException("Wrong key");
+ }
+ AddKey(seq[0]);
+}
+
+void UKeySequence::AddKey(int key) {
+ if (key <= 0) {
+ return;
+ }
+ for (int i = 0; i < Keys.size(); i++) {
+ if (Keys[i] == key) {
+ return;
+ }
+ }
+ qDebug() << "Key added: " << key;
+ Keys.push_back(key);
+}
+
+void UKeySequence::AddKey(const QKeyEvent* event) {
+ AddKey(event->key());
+ AddModifiers(event->modifiers());
+}
diff --git a/ukeysequence.h b/ukeysequence.h
new file mode 100644
index 0000000..9e5939a
--- /dev/null
+++ b/ukeysequence.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <QObject>
+#include <QString>
+#include <QVector>
+#include <QStringList>
+#include <QKeyEvent>
+
+#include "uexception.h"
+#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(int key);
+ void AddKey(const QString& key);
+ void AddModifiers(Qt::KeyboardModifiers mod);
+ void AddKey(const QKeyEvent* event);
+ inline size_t Size() const {
+ return Keys.size();
+ }
+ inline int operator [] (size_t n) const {
+ if ((int)n > Keys.size()) {
+ throw UException("Out of bounds");
+ }
+ return Keys[n];
+ }
+ QVector<int> GetSimpleKeys() const;
+ QVector<int> GetModifiers() const;
+private:
+ QVector<int> Keys;
+};
+
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jun 18, 12:12 AM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70586
Default Alt Text
(14 KB)
Attached To
Mode
R65 cKaiser's UGlobalHotKey
Attached
Detach File
Event Timeline