310 lines
7.0 KiB
C++
310 lines
7.0 KiB
C++
#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();
|
|
}
|