You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
7.0 KiB
309 lines
7.0 KiB
#include "qhotkey.h" |
|
#include "qhotkey_p.h" |
|
#include <qt_windows.h> |
|
#include <algorithm> |
|
#include <QDebug> |
|
#include <QList> |
|
#include <QTimer> |
|
|
|
#define HKEY_ID(nativeShortcut) (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000) |
|
|
|
#if !defined(MOD_NOREPEAT) |
|
#define MOD_NOREPEAT 0x4000 |
|
#endif |
|
|
|
class QHotkeyPrivateWin : public QHotkeyPrivate |
|
{ |
|
public: |
|
QHotkeyPrivateWin(); |
|
// QAbstractNativeEventFilter interface |
|
bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override; |
|
|
|
protected: |
|
void pollForHotkeyRelease(); |
|
// QHotkeyPrivate interface |
|
quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE; |
|
quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE; |
|
bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; |
|
bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; |
|
|
|
private: |
|
static QString formatWinError(DWORD winError); |
|
QTimer pollTimer; |
|
QList<QHotkey::NativeShortcut> polledShortcuts; |
|
}; |
|
NATIVE_INSTANCE(QHotkeyPrivateWin) |
|
|
|
QHotkeyPrivateWin::QHotkeyPrivateWin(){ |
|
pollTimer.setInterval(50); |
|
connect(&pollTimer, &QTimer::timeout, this, &QHotkeyPrivateWin::pollForHotkeyRelease); |
|
} |
|
|
|
bool QHotkeyPrivate::isPlatformSupported() |
|
{ |
|
return true; |
|
} |
|
|
|
bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) |
|
{ |
|
Q_UNUSED(eventType) |
|
Q_UNUSED(result) |
|
|
|
MSG* msg = static_cast<MSG*>(message); |
|
if(msg->message == WM_HOTKEY) { |
|
QHotkey::NativeShortcut shortcut = {HIWORD(msg->lParam), LOWORD(msg->lParam)}; |
|
this->activateShortcut(shortcut); |
|
if (this->polledShortcuts.empty()) |
|
this->pollTimer.start(); |
|
this->polledShortcuts.append(shortcut); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void QHotkeyPrivateWin::pollForHotkeyRelease() |
|
{ |
|
auto it = std::remove_if(this->polledShortcuts.begin(), this->polledShortcuts.end(), [this](const QHotkey::NativeShortcut &shortcut) { |
|
bool pressed = (GetAsyncKeyState(shortcut.key) & (1 << 15)) != 0; |
|
if (!pressed) |
|
this->releaseShortcut(shortcut); |
|
return !pressed; |
|
}); |
|
this->polledShortcuts.erase(it, this->polledShortcuts.end()); |
|
if (this->polledShortcuts.empty()) |
|
this->pollTimer.stop(); |
|
} |
|
|
|
quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool &ok) |
|
{ |
|
ok = true; |
|
if(keycode <= 0xFFFF) {//Try to obtain the key from it's "character" |
|
const SHORT vKey = VkKeyScanW(static_cast<WCHAR>(keycode)); |
|
if(vKey > -1) |
|
return LOBYTE(vKey); |
|
} |
|
|
|
//find key from switch/case --> Only finds a very small subset of keys |
|
switch (keycode) |
|
{ |
|
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_PRINT; |
|
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_CapsLock: |
|
return VK_CAPITAL; |
|
case Qt::Key_NumLock: |
|
return VK_NUMLOCK; |
|
case Qt::Key_ScrollLock: |
|
return VK_SCROLL; |
|
|
|
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_Menu: |
|
return VK_APPS; |
|
case Qt::Key_Help: |
|
return VK_HELP; |
|
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; |
|
case Qt::Key_Mode_switch: |
|
return VK_MODECHANGE; |
|
case Qt::Key_Select: |
|
return VK_SELECT; |
|
case Qt::Key_Printer: |
|
return VK_PRINT; |
|
case Qt::Key_Execute: |
|
return VK_EXECUTE; |
|
case Qt::Key_Sleep: |
|
return VK_SLEEP; |
|
case Qt::Key_Period: |
|
return VK_DECIMAL; |
|
case Qt::Key_Play: |
|
return VK_PLAY; |
|
case Qt::Key_Cancel: |
|
return VK_CANCEL; |
|
|
|
case Qt::Key_Forward: |
|
return VK_BROWSER_FORWARD; |
|
case Qt::Key_Refresh: |
|
return VK_BROWSER_REFRESH; |
|
case Qt::Key_Stop: |
|
return VK_BROWSER_STOP; |
|
case Qt::Key_Search: |
|
return VK_BROWSER_SEARCH; |
|
case Qt::Key_Favorites: |
|
return VK_BROWSER_FAVORITES; |
|
case Qt::Key_HomePage: |
|
return VK_BROWSER_HOME; |
|
|
|
case Qt::Key_LaunchMail: |
|
return VK_LAUNCH_MAIL; |
|
case Qt::Key_LaunchMedia: |
|
return VK_LAUNCH_MEDIA_SELECT; |
|
case Qt::Key_Launch0: |
|
return VK_LAUNCH_APP1; |
|
case Qt::Key_Launch1: |
|
return VK_LAUNCH_APP2; |
|
|
|
case Qt::Key_Massyo: |
|
return VK_OEM_FJ_MASSHOU; |
|
case Qt::Key_Touroku: |
|
return VK_OEM_FJ_TOUROKU; |
|
|
|
default: |
|
if(keycode <= 0xFFFF) |
|
return static_cast<BYTE>(keycode); |
|
else { |
|
ok = false; |
|
return 0; |
|
} |
|
} |
|
} |
|
|
|
quint32 QHotkeyPrivateWin::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) |
|
{ |
|
quint32 nMods = 0; |
|
if (modifiers & Qt::ShiftModifier) |
|
nMods |= MOD_SHIFT; |
|
if (modifiers & Qt::ControlModifier) |
|
nMods |= MOD_CONTROL; |
|
if (modifiers & Qt::AltModifier) |
|
nMods |= MOD_ALT; |
|
if (modifiers & Qt::MetaModifier) |
|
nMods |= MOD_WIN; |
|
ok = true; |
|
return nMods; |
|
} |
|
|
|
bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut) |
|
{ |
|
BOOL ok = RegisterHotKey(NULL, |
|
HKEY_ID(shortcut), |
|
shortcut.modifier + MOD_NOREPEAT, |
|
shortcut.key); |
|
if(ok) |
|
return true; |
|
else { |
|
error = QHotkeyPrivateWin::formatWinError(::GetLastError()); |
|
return false; |
|
} |
|
} |
|
|
|
bool QHotkeyPrivateWin::unregisterShortcut(QHotkey::NativeShortcut shortcut) |
|
{ |
|
BOOL ok = UnregisterHotKey(NULL, HKEY_ID(shortcut)); |
|
if(ok) |
|
return true; |
|
else { |
|
error = QHotkeyPrivateWin::formatWinError(::GetLastError()); |
|
return false; |
|
} |
|
} |
|
|
|
QString QHotkeyPrivateWin::formatWinError(DWORD winError) |
|
{ |
|
wchar_t *buffer = NULL; |
|
DWORD num = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
|
NULL, |
|
winError, |
|
0, |
|
(LPWSTR)&buffer, |
|
0, |
|
NULL); |
|
if(buffer) { |
|
QString res = QString::fromWCharArray(buffer, num); |
|
LocalFree(buffer); |
|
return res; |
|
} else |
|
return QString(); |
|
}
|
|
|