#include "qhotkey.h" #include "qhotkey_p.h" #include #include #include #include #include #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 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(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(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(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(); }