1 #include "qhotkey.h"
2 #include "qhotkey_p.h"
3 #include <qt_windows.h>
4 #include <QDebug>
5 #include <QTimer>
6
7 #define HKEY_ID(nativeShortcut) (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000)
8
9 #if !defined(MOD_NOREPEAT)
10 #define MOD_NOREPEAT 0x4000
11 #endif
12
13 class QHotkeyPrivateWin : public QHotkeyPrivate
14 {
15 public:
16 QHotkeyPrivateWin();
17 // QAbstractNativeEventFilter interface
18 bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override;
19
20 protected:
21 void pollForHotkeyRelease();
22 // QHotkeyPrivate interface
23 quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE;
24 quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE;
25 bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
26 bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
27
28 private:
29 static QString formatWinError(DWORD winError);
30 QTimer pollTimer;
31 QHotkey::NativeShortcut polledShortcut;
32 };
NATIVE_INSTANCE(QHotkeyPrivateWin)33 NATIVE_INSTANCE(QHotkeyPrivateWin)
34
35 QHotkeyPrivateWin::QHotkeyPrivateWin(){
36 pollTimer.setInterval(50);
37 connect(&pollTimer, &QTimer::timeout, this, &QHotkeyPrivateWin::pollForHotkeyRelease);
38 }
39
isPlatformSupported()40 bool QHotkeyPrivate::isPlatformSupported()
41 {
42 return true;
43 }
44
nativeEventFilter(const QByteArray & eventType,void * message,_NATIVE_EVENT_RESULT * result)45 bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result)
46 {
47 Q_UNUSED(eventType)
48 Q_UNUSED(result)
49
50 MSG* msg = static_cast<MSG*>(message);
51 if(msg->message == WM_HOTKEY) {
52 QHotkey::NativeShortcut shortcut = {HIWORD(msg->lParam), LOWORD(msg->lParam)};
53 this->activateShortcut(shortcut);
54 this->polledShortcut = shortcut;
55 this->pollTimer.start();
56 }
57
58 return false;
59 }
60
pollForHotkeyRelease()61 void QHotkeyPrivateWin::pollForHotkeyRelease()
62 {
63 bool pressed = (GetAsyncKeyState(this->polledShortcut.key) & (1 << 15)) != 0;
64 if(!pressed) {
65 this->pollTimer.stop();
66 this->releaseShortcut(this->polledShortcut);
67 }
68 }
69
nativeKeycode(Qt::Key keycode,bool & ok)70 quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool &ok)
71 {
72 ok = true;
73 if(keycode <= 0xFFFF) {//Try to obtain the key from it's "character"
74 const SHORT vKey = VkKeyScanW(static_cast<WCHAR>(keycode));
75 if(vKey > -1)
76 return LOBYTE(vKey);
77 }
78
79 //find key from switch/case --> Only finds a very small subset of keys
80 switch (keycode)
81 {
82 case Qt::Key_Escape:
83 return VK_ESCAPE;
84 case Qt::Key_Tab:
85 case Qt::Key_Backtab:
86 return VK_TAB;
87 case Qt::Key_Backspace:
88 return VK_BACK;
89 case Qt::Key_Return:
90 case Qt::Key_Enter:
91 return VK_RETURN;
92 case Qt::Key_Insert:
93 return VK_INSERT;
94 case Qt::Key_Delete:
95 return VK_DELETE;
96 case Qt::Key_Pause:
97 return VK_PAUSE;
98 case Qt::Key_Print:
99 return VK_PRINT;
100 case Qt::Key_Clear:
101 return VK_CLEAR;
102 case Qt::Key_Home:
103 return VK_HOME;
104 case Qt::Key_End:
105 return VK_END;
106 case Qt::Key_Left:
107 return VK_LEFT;
108 case Qt::Key_Up:
109 return VK_UP;
110 case Qt::Key_Right:
111 return VK_RIGHT;
112 case Qt::Key_Down:
113 return VK_DOWN;
114 case Qt::Key_PageUp:
115 return VK_PRIOR;
116 case Qt::Key_PageDown:
117 return VK_NEXT;
118 case Qt::Key_CapsLock:
119 return VK_CAPITAL;
120 case Qt::Key_NumLock:
121 return VK_NUMLOCK;
122 case Qt::Key_ScrollLock:
123 return VK_SCROLL;
124
125 case Qt::Key_F1:
126 return VK_F1;
127 case Qt::Key_F2:
128 return VK_F2;
129 case Qt::Key_F3:
130 return VK_F3;
131 case Qt::Key_F4:
132 return VK_F4;
133 case Qt::Key_F5:
134 return VK_F5;
135 case Qt::Key_F6:
136 return VK_F6;
137 case Qt::Key_F7:
138 return VK_F7;
139 case Qt::Key_F8:
140 return VK_F8;
141 case Qt::Key_F9:
142 return VK_F9;
143 case Qt::Key_F10:
144 return VK_F10;
145 case Qt::Key_F11:
146 return VK_F11;
147 case Qt::Key_F12:
148 return VK_F12;
149 case Qt::Key_F13:
150 return VK_F13;
151 case Qt::Key_F14:
152 return VK_F14;
153 case Qt::Key_F15:
154 return VK_F15;
155 case Qt::Key_F16:
156 return VK_F16;
157 case Qt::Key_F17:
158 return VK_F17;
159 case Qt::Key_F18:
160 return VK_F18;
161 case Qt::Key_F19:
162 return VK_F19;
163 case Qt::Key_F20:
164 return VK_F20;
165 case Qt::Key_F21:
166 return VK_F21;
167 case Qt::Key_F22:
168 return VK_F22;
169 case Qt::Key_F23:
170 return VK_F23;
171 case Qt::Key_F24:
172 return VK_F24;
173
174 case Qt::Key_Menu:
175 return VK_APPS;
176 case Qt::Key_Help:
177 return VK_HELP;
178 case Qt::Key_MediaNext:
179 return VK_MEDIA_NEXT_TRACK;
180 case Qt::Key_MediaPrevious:
181 return VK_MEDIA_PREV_TRACK;
182 case Qt::Key_MediaPlay:
183 return VK_MEDIA_PLAY_PAUSE;
184 case Qt::Key_MediaStop:
185 return VK_MEDIA_STOP;
186 case Qt::Key_VolumeDown:
187 return VK_VOLUME_DOWN;
188 case Qt::Key_VolumeUp:
189 return VK_VOLUME_UP;
190 case Qt::Key_VolumeMute:
191 return VK_VOLUME_MUTE;
192 case Qt::Key_Mode_switch:
193 return VK_MODECHANGE;
194 case Qt::Key_Select:
195 return VK_SELECT;
196 case Qt::Key_Printer:
197 return VK_PRINT;
198 case Qt::Key_Execute:
199 return VK_EXECUTE;
200 case Qt::Key_Sleep:
201 return VK_SLEEP;
202 case Qt::Key_Period:
203 return VK_DECIMAL;
204 case Qt::Key_Play:
205 return VK_PLAY;
206 case Qt::Key_Cancel:
207 return VK_CANCEL;
208
209 case Qt::Key_Forward:
210 return VK_BROWSER_FORWARD;
211 case Qt::Key_Refresh:
212 return VK_BROWSER_REFRESH;
213 case Qt::Key_Stop:
214 return VK_BROWSER_STOP;
215 case Qt::Key_Search:
216 return VK_BROWSER_SEARCH;
217 case Qt::Key_Favorites:
218 return VK_BROWSER_FAVORITES;
219 case Qt::Key_HomePage:
220 return VK_BROWSER_HOME;
221
222 case Qt::Key_LaunchMail:
223 return VK_LAUNCH_MAIL;
224 case Qt::Key_LaunchMedia:
225 return VK_LAUNCH_MEDIA_SELECT;
226 case Qt::Key_Launch0:
227 return VK_LAUNCH_APP1;
228 case Qt::Key_Launch1:
229 return VK_LAUNCH_APP2;
230
231 case Qt::Key_Massyo:
232 return VK_OEM_FJ_MASSHOU;
233 case Qt::Key_Touroku:
234 return VK_OEM_FJ_TOUROKU;
235
236 default:
237 if(keycode <= 0xFFFF)
238 return (byte)keycode;
239 else {
240 ok = false;
241 return 0;
242 }
243 }
244 }
245
nativeModifiers(Qt::KeyboardModifiers modifiers,bool & ok)246 quint32 QHotkeyPrivateWin::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok)
247 {
248 quint32 nMods = 0;
249 if (modifiers & Qt::ShiftModifier)
250 nMods |= MOD_SHIFT;
251 if (modifiers & Qt::ControlModifier)
252 nMods |= MOD_CONTROL;
253 if (modifiers & Qt::AltModifier)
254 nMods |= MOD_ALT;
255 if (modifiers & Qt::MetaModifier)
256 nMods |= MOD_WIN;
257 ok = true;
258 return nMods;
259 }
260
registerShortcut(QHotkey::NativeShortcut shortcut)261 bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut)
262 {
263 BOOL ok = RegisterHotKey(NULL,
264 HKEY_ID(shortcut),
265 shortcut.modifier + MOD_NOREPEAT,
266 shortcut.key);
267 if(ok)
268 return true;
269 else {
270 error = QHotkeyPrivateWin::formatWinError(::GetLastError());
271 return false;
272 }
273 }
274
unregisterShortcut(QHotkey::NativeShortcut shortcut)275 bool QHotkeyPrivateWin::unregisterShortcut(QHotkey::NativeShortcut shortcut)
276 {
277 BOOL ok = UnregisterHotKey(NULL, HKEY_ID(shortcut));
278 if(ok)
279 return true;
280 else {
281 error = QHotkeyPrivateWin::formatWinError(::GetLastError());
282 return false;
283 }
284 }
285
formatWinError(DWORD winError)286 QString QHotkeyPrivateWin::formatWinError(DWORD winError)
287 {
288 wchar_t *buffer = NULL;
289 DWORD num = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
290 NULL,
291 winError,
292 0,
293 (LPWSTR)&buffer,
294 0,
295 NULL);
296 if(buffer) {
297 QString res = QString::fromWCharArray(buffer, num);
298 LocalFree(buffer);
299 return res;
300 } else
301 return QString();
302 }
303