Implementing Global Hotkey Support in QT on Windows

If your application needs a global hotkey support, QT will leave you hanging. Fortunately, on high-level this is a rather simple function:

Code
bool registerHotkey(QKeySequence keySequence) {
auto key = Qt::Key(keySequence[0] & static_cast<int>(~Qt::KeyboardModifierMask));
auto modifiers = Qt::KeyboardModifiers(keySequence[0] & static_cast<int>(Qt::KeyboardModifierMask));

return nativeRegisterHotkey(key, modifiers);
}

Essentially the only work is splitting key and modifiers into their own variables and then having platform-specific code handling the nasty bits.

For Windows, this is just a simple conversion of modifiers and keys followed by a call to RegisterHotKey API:

Code
bool nativeRegisterHotkey(Qt::Key key, Qt::KeyboardModifiers modifiers) {
uint modValue = 0;
if (modifiers & Qt::AltModifier) { modValue += MOD_ALT; }
if (modifiers & Qt::ControlModifier) { modValue += MOD_CONTROL; }
if (modifiers & Qt::ShiftModifier) { modValue += MOD_SHIFT; }

uint keyValue;
if (((key >= Qt::Key_A) && (key <= Qt::Key_Z)) || ((key >= Qt::Key_0) && (key <= Qt::Key_9))) {
keyValue = key;
} else if ((key >= Qt::Key_F1) && (key <= Qt::Key_F24)) {
keyValue = VK_F1 + (key - Qt::Key_F1);
} else {
return false; //unsupported key
}

return RegisterHotKey(nullptr, _hotkeyId, modValue, keyValue);
}
&#91;/type&#93;

But this alone is nothing without actual <a href="https://doc.qt.io/qt-5/qabstractnativeeventfilter.html">QAbstractNativeEventFilter</a>-based event filter. Here we need to intercept message and emit the signal:
[type title="Code"]
bool nativeEventFilter(const QByteArray&, void* message, long*) {
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_HOTKEY) {
if (msg->wParam == static_cast<WPARAM>(_hotkeyId)) {
emit activated();
return true;
}
}
return false;
}

Mind you, this is a rather incomplete and simplified example. Full code (supporting both Windows and Linux) is available for download.

To use it, just assign instance to a long living variable, register a key sequence, and hook into activated signal:

Code
_hotkey = new Hotkey();
_hotkey->registerHotkey(QKeySequence { "Ctrl+Shift+F1" });
connect(_hotkey, SIGNAL(activated()), this, SLOT(onActivated()));

PS: X11 variant of this code is available here.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.