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