Page MenuHomePhabricator (Chris)

No OneTemporary

Size
14 KB
Referenced Files
None
Subscribers
None
diff --git a/README.md b/README.md
index ba1bbb7..6597ec5 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,26 @@
## UGlobalHotkey
### Decription
-UGlobalHotkey is an extension for Qt framework, which implements global hotkeys functionality for Windows and Linux platforms.
+UGlobalHotkey is an extension for Qt framework, which implements global hotkeys functionality for Windows Linux and MacOSX platforms.
It is written by [bakwc](https://github.com/bakwc), extracted from [Pastexen](https://github.com/bakwc/Pastexen) and turned into a shared library by me.
### Building from source
* You can either open project with QtCreator and press Build button
* Or build it using terminal:
```
qmake
make
```
### Usage example
```
UGlobalHotkeys *hotkeyManager = new UGlobalHotkeys();
hotkeyManager->RegisterHotkey("Ctrl+Shift+F12");
connect(hotkeyManager, &UGlobalHotkeys::Activated, [=](size_t id)
{
qDebug() << "Activated: " << QString::number(id);
});
```
### License
UGlobalHotkey library is licensed as Public Domain, so you are free to do anything with it.
\ No newline at end of file
diff --git a/hotkeymap.h b/hotkeymap.h
index 200f887..2993efe 100644
--- a/hotkeymap.h
+++ b/hotkeymap.h
@@ -1,62 +1,148 @@
#include <QtCore>
-#include "uglobal.h"
#if defined(Q_OS_WIN)
-size_t QtKeyToWin(size_t key) {
+inline 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)
+inline 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;
}
+#elif defined(Q_OS_MAC)
+
+#include "ukeysequence.h"
+#include <Carbon/Carbon.h>
+#include <unordered_map>
+
+struct UKeyData {
+ uint32_t key;
+ uint32_t mods;
+};
+
+static std::unordered_map<uint32_t, uint32_t> 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},
+};
+
+static std::unordered_map<uint32_t, uint32_t> MOD_MAP = {
+ {Qt::Key_Shift, shiftKey},
+ {Qt::Key_Alt, optionKey},
+ {Qt::Key_Control, controlKey},
+ {Qt::Key_Option, optionKey},
+};
+
+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
+ throw UException("Invalid hotkey");
+ for (auto&& mod: mods) {
+ if (MOD_MAP.find(mod) == MOD_MAP.end())
+ throw UException("Invalid hotkey");
+ data.mods += MOD_MAP[mod];
+ }
+ return data;
+}
+
#endif
diff --git a/uglobalhotkey.pro b/uglobalhotkey.pro
index e13c4e2..885d726 100644
--- a/uglobalhotkey.pro
+++ b/uglobalhotkey.pro
@@ -1,25 +1,26 @@
QT = core gui
unix {
QT += gui-private
}
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 \
-unix: LIBS += -lxcb -lxcb-keysyms
+linux: LIBS += -lxcb -lxcb-keysyms
+mac: LIBS += -framework Carbon
diff --git a/uglobalhotkeys.cpp b/uglobalhotkeys.cpp
index 58abc8a..418b7a0 100644
--- a/uglobalhotkeys.cpp
+++ b/uglobalhotkeys.cpp
@@ -1,159 +1,213 @@
#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);
}
+#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
+
void UGlobalHotkeys::registerHotkey(const UKeySequence& keySeq, size_t id) {
if (keySeq.Size() == 0) {
throw UException("Empty hotkeys");
}
+ #if defined(Q_OS_WIN) or 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)) {
qDebug() << "Error activating hotkey!";
} 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
}
void UGlobalHotkeys::unregisterHotkey(size_t id) {
+ #if defined(Q_OS_WIN) or defined(Q_OS_LINUX)
Q_ASSERT(Registered.find(id) != Registered.end() && "Unregistered hotkey");
+ #endif
#if defined(Q_OS_WIN)
- UnregisterHotKey((HWND)winId(), id);
+ unregisterHotKey((HWND)winId(), id);
#elif defined(Q_OS_LINUX)
unregLinuxHotkey(id);
#endif
+ #if defined(Q_OS_WIN) or 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
for (size_t id : Registered)
this->unregisterHotkey(id);
#elif defined(Q_OS_LINUX)
for (size_t id :Registered.keys())
this->unregisterHotkey(id);
#endif
}
UGlobalHotkeys::~UGlobalHotkeys() {
#if defined(Q_OS_WIN)
for (QSet<size_t>::iterator i = Registered.begin(); i != Registered.end(); i++) {
- UnregisterHotKey((HWND)winId(), *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)
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
index d7a0e32..07c6c18 100644
--- a/uglobalhotkeys.h
+++ b/uglobalhotkeys.h
@@ -1,60 +1,68 @@
#pragma once
#include <QWidget>
#include <QAbstractNativeEventFilter>
#include <QSet>
#if defined(Q_OS_LINUX)
#include "xcb/xcb.h"
#include "xcb/xcb_keysyms.h"
+#elif defined(Q_OS_MAC)
+#include <Carbon/Carbon.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);
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)
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
+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<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;
+ #elif defined(Q_OS_MAC)
+ QHash<size_t, EventHotKeyRef> HotkeyRefs;
#endif
};

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jan 30, 3:16 PM (5 d, 9 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55443
Default Alt Text
(14 KB)

Event Timeline