Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
14 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Thu, Jun 18, 12:12 AM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70586
Default Alt Text
(14 KB)

Event Timeline