Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowskeymapper.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
9
10#include <QtGui/qguiapplication.h>
11#include <QtGui/qwindow.h>
12#include <qpa/qwindowsysteminterface.h>
13#include <private/qguiapplication_p.h>
14#include <private/qhighdpiscaling_p.h>
15#include <QtGui/qevent.h>
16#include <QtGui/private/qwindowsguieventdispatcher_p.h>
17#include <QtCore/private/qdebug_p.h>
18#include <QtCore/private/qtools_p.h>
19
20#if defined(WM_APPCOMMAND)
21# ifndef FAPPCOMMAND_MOUSE
22# define FAPPCOMMAND_MOUSE 0x8000
23# endif
24# ifndef FAPPCOMMAND_KEY
25# define FAPPCOMMAND_KEY 0
26# endif
27# ifndef FAPPCOMMAND_OEM
28# define FAPPCOMMAND_OEM 0x1000
29# endif
30# ifndef FAPPCOMMAND_MASK
31# define FAPPCOMMAND_MASK 0xF000
32# endif
33# ifndef GET_APPCOMMAND_LPARAM
34# define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
35# endif
36# ifndef GET_DEVICE_LPARAM
37# define GET_DEVICE_LPARAM(lParam) ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK))
38# endif
39# ifndef GET_MOUSEORKEY_LPARAM
40# define GET_MOUSEORKEY_LPARAM GET_DEVICE_LPARAM
41# endif
42# ifndef GET_FLAGS_LPARAM
43# define GET_FLAGS_LPARAM(lParam) (LOWORD(lParam))
44# endif
45# ifndef GET_KEYSTATE_LPARAM
46# define GET_KEYSTATE_LPARAM(lParam) GET_FLAGS_LPARAM(lParam)
47# endif
48#endif
49
51
52/*!
53 \class QWindowsKeyMapper
54 \brief Translates Windows keys to QWindowSystemInterface events.
55 \internal
56
57 In addition, handles some special keys to display system menus, etc.
58 The code originates from \c qkeymapper_win.cpp.
59*/
60
61static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state);
62
63QWindowsKeyMapper::QWindowsKeyMapper()
64 : m_useRTLExtensions(false), m_keyGrabber(nullptr)
65{
66 memset(keyLayout, 0, sizeof(keyLayout));
67 auto *app = static_cast<QGuiApplication *>(QGuiApplication::instance());
68 QObject::connect(app, &QGuiApplication::applicationStateChanged,
69 app, clearKeyRecorderOnApplicationInActive);
71}
72
74
75#ifndef LANG_PASHTO
76#define LANG_PASHTO 0x63
77#endif
78#ifndef LANG_SYRIAC
79#define LANG_SYRIAC 0x5a
80#endif
81#ifndef LANG_DIVEHI
82#define LANG_DIVEHI 0x65
83#endif
84#ifndef VK_OEM_PLUS
85#define VK_OEM_PLUS 0xBB
86#endif
87#ifndef VK_OEM_3
88#define VK_OEM_3 0xC0
89#endif
90
91// Get scancode from the given message
92static constexpr quint32 getScancode(const MSG &msg)
93{
94 const auto keyFlags = HIWORD(msg.lParam);
95 quint32 scancode = LOBYTE(keyFlags);
96 // if extended-key flag is on, the scan code consists of a sequence of two bytes,
97 // where the first byte has a value of 0xe0.
98 if ((keyFlags & KF_EXTENDED) != 0)
99 scancode |= 0xE000;
100 return scancode;
101}
102
103// Key recorder ------------------------------------------------------------------------[ start ] --
104struct KeyRecord {
105 KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {}
107
108 int code;
109 int ascii;
110 int state;
112};
113
114// We need to record the pressed keys in order to decide, whether the key event is an autorepeat
115// event. As soon as its state changes, the chain of autorepeat events will be broken.
116static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers...
118{
119 inline KeyRecord *findKey(int code, bool remove);
120 inline void storeKey(int code, int ascii, int state, const QString& text);
121 inline void clearKeys();
122
123 int nrecs = 0;
124 KeyRecord deleted_record; // A copy of last entry removed from records[]
126};
128
129static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state)
130{
131 if (state == Qt::ApplicationInactive)
133}
134
135KeyRecord *KeyRecorder::findKey(int code, bool remove)
136{
137 KeyRecord *result = nullptr;
138 for (int i = 0; i < nrecs; ++i) {
139 if (records[i].code == code) {
140 if (remove) {
141 deleted_record = records[i];
142 // Move rest down, and decrease count
143 while (i + 1 < nrecs) {
144 records[i] = records[i + 1];
145 ++i;
146 }
147 --nrecs;
148 result = &deleted_record;
149 } else {
150 result = &records[i];
151 }
152 break;
153 }
154 }
155 return result;
156}
157
158void KeyRecorder::storeKey(int code, int ascii, int state, const QString& text)
159{
160 Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS,
161 "Internal KeyRecorder",
162 "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS");
163
165 qWarning("Qt: Internal keyboard buffer overflow");
166 return;
167 }
168 records[nrecs++] = KeyRecord(code,ascii,state,text);
169}
170
172{
173 nrecs = 0;
174}
175// Key recorder --------------------------------------------------------------------------[ end ] --
176
177
178// Key translation ---------------------------------------------------------------------[ start ] --
179// Meaning of values:
180// 0 = Character output key, needs keyboard driver mapping
181// Key_unknown = Unknown Virtual Key, no translation possible, ignore
182static const uint KeyTbl[] = { // Keyboard mapping table
183 // Dec | Hex | Windows Virtual key
184 Qt::Key_unknown, // 0 0x00
185 Qt::Key_unknown, // 1 0x01 VK_LBUTTON | Left mouse button
186 Qt::Key_unknown, // 2 0x02 VK_RBUTTON | Right mouse button
187 Qt::Key_Cancel, // 3 0x03 VK_CANCEL | Control-Break processing
188 Qt::Key_unknown, // 4 0x04 VK_MBUTTON | Middle mouse button
189 Qt::Key_unknown, // 5 0x05 VK_XBUTTON1 | X1 mouse button
190 Qt::Key_unknown, // 6 0x06 VK_XBUTTON2 | X2 mouse button
191 Qt::Key_unknown, // 7 0x07 -- unassigned --
192 Qt::Key_Backspace, // 8 0x08 VK_BACK | BackSpace key
193 Qt::Key_Tab, // 9 0x09 VK_TAB | Tab key
194 Qt::Key_unknown, // 10 0x0A -- reserved --
195 Qt::Key_unknown, // 11 0x0B -- reserved --
196 Qt::Key_Clear, // 12 0x0C VK_CLEAR | Clear key
197 Qt::Key_Return, // 13 0x0D VK_RETURN | Enter key
198 Qt::Key_unknown, // 14 0x0E -- unassigned --
199 Qt::Key_unknown, // 15 0x0F -- unassigned --
200 Qt::Key_Shift, // 16 0x10 VK_SHIFT | Shift key
201 Qt::Key_Control, // 17 0x11 VK_CONTROL | Ctrl key
202 Qt::Key_Alt, // 18 0x12 VK_MENU | Alt key
203 Qt::Key_Pause, // 19 0x13 VK_PAUSE | Pause key
204 Qt::Key_CapsLock, // 20 0x14 VK_CAPITAL | Caps-Lock
205 Qt::Key_unknown, // 21 0x15 VK_KANA / VK_HANGUL | IME Kana or Hangul mode
206 Qt::Key_unknown, // 22 0x16 -- unassigned --
207 Qt::Key_unknown, // 23 0x17 VK_JUNJA | IME Junja mode
208 Qt::Key_unknown, // 24 0x18 VK_FINAL | IME final mode
209 Qt::Key_unknown, // 25 0x19 VK_HANJA / VK_KANJI | IME Hanja or Kanji mode
210 Qt::Key_unknown, // 26 0x1A -- unassigned --
211 Qt::Key_Escape, // 27 0x1B VK_ESCAPE | Esc key
212 Qt::Key_unknown, // 28 0x1C VK_CONVERT | IME convert
213 Qt::Key_unknown, // 29 0x1D VK_NONCONVERT | IME non-convert
214 Qt::Key_unknown, // 30 0x1E VK_ACCEPT | IME accept
215 Qt::Key_Mode_switch,// 31 0x1F VK_MODECHANGE | IME mode change request
216 Qt::Key_Space, // 32 0x20 VK_SPACE | Spacebar
217 Qt::Key_PageUp, // 33 0x21 VK_PRIOR | Page Up key
218 Qt::Key_PageDown, // 34 0x22 VK_NEXT | Page Down key
219 Qt::Key_End, // 35 0x23 VK_END | End key
220 Qt::Key_Home, // 36 0x24 VK_HOME | Home key
221 Qt::Key_Left, // 37 0x25 VK_LEFT | Left arrow key
222 Qt::Key_Up, // 38 0x26 VK_UP | Up arrow key
223 Qt::Key_Right, // 39 0x27 VK_RIGHT | Right arrow key
224 Qt::Key_Down, // 40 0x28 VK_DOWN | Down arrow key
225 Qt::Key_Select, // 41 0x29 VK_SELECT | Select key
226 Qt::Key_Printer, // 42 0x2A VK_PRINT | Print key
227 Qt::Key_Execute, // 43 0x2B VK_EXECUTE | Execute key
228 Qt::Key_Print, // 44 0x2C VK_SNAPSHOT | Print Screen key
229 Qt::Key_Insert, // 45 0x2D VK_INSERT | Ins key
230 Qt::Key_Delete, // 46 0x2E VK_DELETE | Del key
231 Qt::Key_Help, // 47 0x2F VK_HELP | Help key
232 0, // 48 0x30 (VK_0) | 0 key
233 0, // 49 0x31 (VK_1) | 1 key
234 0, // 50 0x32 (VK_2) | 2 key
235 0, // 51 0x33 (VK_3) | 3 key
236 0, // 52 0x34 (VK_4) | 4 key
237 0, // 53 0x35 (VK_5) | 5 key
238 0, // 54 0x36 (VK_6) | 6 key
239 0, // 55 0x37 (VK_7) | 7 key
240 0, // 56 0x38 (VK_8) | 8 key
241 0, // 57 0x39 (VK_9) | 9 key
242 Qt::Key_unknown, // 58 0x3A -- unassigned --
243 Qt::Key_unknown, // 59 0x3B -- unassigned --
244 Qt::Key_unknown, // 60 0x3C -- unassigned --
245 Qt::Key_unknown, // 61 0x3D -- unassigned --
246 Qt::Key_unknown, // 62 0x3E -- unassigned --
247 Qt::Key_unknown, // 63 0x3F -- unassigned --
248 Qt::Key_unknown, // 64 0x40 -- unassigned --
249 0, // 65 0x41 (VK_A) | A key
250 0, // 66 0x42 (VK_B) | B key
251 0, // 67 0x43 (VK_C) | C key
252 0, // 68 0x44 (VK_D) | D key
253 0, // 69 0x45 (VK_E) | E key
254 0, // 70 0x46 (VK_F) | F key
255 0, // 71 0x47 (VK_G) | G key
256 0, // 72 0x48 (VK_H) | H key
257 0, // 73 0x49 (VK_I) | I key
258 0, // 74 0x4A (VK_J) | J key
259 0, // 75 0x4B (VK_K) | K key
260 0, // 76 0x4C (VK_L) | L key
261 0, // 77 0x4D (VK_M) | M key
262 0, // 78 0x4E (VK_N) | N key
263 0, // 79 0x4F (VK_O) | O key
264 0, // 80 0x50 (VK_P) | P key
265 0, // 81 0x51 (VK_Q) | Q key
266 0, // 82 0x52 (VK_R) | R key
267 0, // 83 0x53 (VK_S) | S key
268 0, // 84 0x54 (VK_T) | T key
269 0, // 85 0x55 (VK_U) | U key
270 0, // 86 0x56 (VK_V) | V key
271 0, // 87 0x57 (VK_W) | W key
272 0, // 88 0x58 (VK_X) | X key
273 0, // 89 0x59 (VK_Y) | Y key
274 0, // 90 0x5A (VK_Z) | Z key
275 Qt::Key_Meta, // 91 0x5B VK_LWIN | Left Windows - MS Natural kbd
276 Qt::Key_Meta, // 92 0x5C VK_RWIN | Right Windows - MS Natural kbd
277 Qt::Key_Menu, // 93 0x5D VK_APPS | Application key-MS Natural kbd
278 Qt::Key_unknown, // 94 0x5E -- reserved --
279 Qt::Key_Sleep, // 95 0x5F VK_SLEEP
280 Qt::Key_0, // 96 0x60 VK_NUMPAD0 | Numeric keypad 0 key
281 Qt::Key_1, // 97 0x61 VK_NUMPAD1 | Numeric keypad 1 key
282 Qt::Key_2, // 98 0x62 VK_NUMPAD2 | Numeric keypad 2 key
283 Qt::Key_3, // 99 0x63 VK_NUMPAD3 | Numeric keypad 3 key
284 Qt::Key_4, // 100 0x64 VK_NUMPAD4 | Numeric keypad 4 key
285 Qt::Key_5, // 101 0x65 VK_NUMPAD5 | Numeric keypad 5 key
286 Qt::Key_6, // 102 0x66 VK_NUMPAD6 | Numeric keypad 6 key
287 Qt::Key_7, // 103 0x67 VK_NUMPAD7 | Numeric keypad 7 key
288 Qt::Key_8, // 104 0x68 VK_NUMPAD8 | Numeric keypad 8 key
289 Qt::Key_9, // 105 0x69 VK_NUMPAD9 | Numeric keypad 9 key
290 Qt::Key_Asterisk, // 106 0x6A VK_MULTIPLY | Multiply key
291 Qt::Key_Plus, // 107 0x6B VK_ADD | Add key
292 Qt::Key_unknown, // 108 0x6C VK_SEPARATOR | Separator key (locale-dependent)
293 Qt::Key_Minus, // 109 0x6D VK_SUBTRACT | Subtract key
294 Qt::Key_unknown, // 110 0x6E VK_DECIMAL | Decimal key (locale-dependent)
295 Qt::Key_Slash, // 111 0x6F VK_DIVIDE | Divide key
296 Qt::Key_F1, // 112 0x70 VK_F1 | F1 key
297 Qt::Key_F2, // 113 0x71 VK_F2 | F2 key
298 Qt::Key_F3, // 114 0x72 VK_F3 | F3 key
299 Qt::Key_F4, // 115 0x73 VK_F4 | F4 key
300 Qt::Key_F5, // 116 0x74 VK_F5 | F5 key
301 Qt::Key_F6, // 117 0x75 VK_F6 | F6 key
302 Qt::Key_F7, // 118 0x76 VK_F7 | F7 key
303 Qt::Key_F8, // 119 0x77 VK_F8 | F8 key
304 Qt::Key_F9, // 120 0x78 VK_F9 | F9 key
305 Qt::Key_F10, // 121 0x79 VK_F10 | F10 key
306 Qt::Key_F11, // 122 0x7A VK_F11 | F11 key
307 Qt::Key_F12, // 123 0x7B VK_F12 | F12 key
308 Qt::Key_F13, // 124 0x7C VK_F13 | F13 key
309 Qt::Key_F14, // 125 0x7D VK_F14 | F14 key
310 Qt::Key_F15, // 126 0x7E VK_F15 | F15 key
311 Qt::Key_F16, // 127 0x7F VK_F16 | F16 key
312 Qt::Key_F17, // 128 0x80 VK_F17 | F17 key
313 Qt::Key_F18, // 129 0x81 VK_F18 | F18 key
314 Qt::Key_F19, // 130 0x82 VK_F19 | F19 key
315 Qt::Key_F20, // 131 0x83 VK_F20 | F20 key
316 Qt::Key_F21, // 132 0x84 VK_F21 | F21 key
317 Qt::Key_F22, // 133 0x85 VK_F22 | F22 key
318 Qt::Key_F23, // 134 0x86 VK_F23 | F23 key
319 Qt::Key_F24, // 135 0x87 VK_F24 | F24 key
320 Qt::Key_unknown, // 136 0x88 -- unassigned --
321 Qt::Key_unknown, // 137 0x89 -- unassigned --
322 Qt::Key_unknown, // 138 0x8A -- unassigned --
323 Qt::Key_unknown, // 139 0x8B -- unassigned --
324 Qt::Key_unknown, // 140 0x8C -- unassigned --
325 Qt::Key_unknown, // 141 0x8D -- unassigned --
326 Qt::Key_unknown, // 142 0x8E -- unassigned --
327 Qt::Key_unknown, // 143 0x8F -- unassigned --
328 Qt::Key_NumLock, // 144 0x90 VK_NUMLOCK | Num Lock key
329 Qt::Key_ScrollLock, // 145 0x91 VK_SCROLL | Scroll Lock key
330 // Fujitsu/OASYS kbd --------------------
331 0, //Qt::Key_Jisho, // 146 0x92 VK_OEM_FJ_JISHO | 'Dictionary' key /
332 // VK_OEM_NEC_EQUAL = key on numpad on NEC PC-9800 kbd
333 Qt::Key_Massyo, // 147 0x93 VK_OEM_FJ_MASSHOU | 'Unregister word' key
334 Qt::Key_Touroku, // 148 0x94 VK_OEM_FJ_TOUROKU | 'Register word' key
335 0, //Qt::Key_Oyayubi_Left,//149 0x95 VK_OEM_FJ_LOYA | 'Left OYAYUBI' key
336 0, //Qt::Key_Oyayubi_Right,//150 0x96 VK_OEM_FJ_ROYA | 'Right OYAYUBI' key
337 Qt::Key_unknown, // 151 0x97 -- unassigned --
338 Qt::Key_unknown, // 152 0x98 -- unassigned --
339 Qt::Key_unknown, // 153 0x99 -- unassigned --
340 Qt::Key_unknown, // 154 0x9A -- unassigned --
341 Qt::Key_unknown, // 155 0x9B -- unassigned --
342 Qt::Key_unknown, // 156 0x9C -- unassigned --
343 Qt::Key_unknown, // 157 0x9D -- unassigned --
344 Qt::Key_unknown, // 158 0x9E -- unassigned --
345 Qt::Key_unknown, // 159 0x9F -- unassigned --
346 Qt::Key_Shift, // 160 0xA0 VK_LSHIFT | Left Shift key
347 Qt::Key_Shift, // 161 0xA1 VK_RSHIFT | Right Shift key
348 Qt::Key_Control, // 162 0xA2 VK_LCONTROL | Left Ctrl key
349 Qt::Key_Control, // 163 0xA3 VK_RCONTROL | Right Ctrl key
350 Qt::Key_Alt, // 164 0xA4 VK_LMENU | Left Menu key
351 Qt::Key_Alt, // 165 0xA5 VK_RMENU | Right Menu key
352 Qt::Key_Back, // 166 0xA6 VK_BROWSER_BACK | Browser Back key
353 Qt::Key_Forward, // 167 0xA7 VK_BROWSER_FORWARD | Browser Forward key
354 Qt::Key_Refresh, // 168 0xA8 VK_BROWSER_REFRESH | Browser Refresh key
355 Qt::Key_Stop, // 169 0xA9 VK_BROWSER_STOP | Browser Stop key
356 Qt::Key_Search, // 170 0xAA VK_BROWSER_SEARCH | Browser Search key
357 Qt::Key_Favorites, // 171 0xAB VK_BROWSER_FAVORITES| Browser Favorites key
358 Qt::Key_HomePage, // 172 0xAC VK_BROWSER_HOME | Browser Start and Home key
359 Qt::Key_VolumeMute, // 173 0xAD VK_VOLUME_MUTE | Volume Mute key
360 Qt::Key_VolumeDown, // 174 0xAE VK_VOLUME_DOWN | Volume Down key
361 Qt::Key_VolumeUp, // 175 0xAF VK_VOLUME_UP | Volume Up key
362 Qt::Key_MediaNext, // 176 0xB0 VK_MEDIA_NEXT_TRACK | Next Track key
363 Qt::Key_MediaPrevious, //177 0xB1 VK_MEDIA_PREV_TRACK | Previous Track key
364 Qt::Key_MediaStop, // 178 0xB2 VK_MEDIA_STOP | Stop Media key
365 Qt::Key_MediaTogglePlayPause,
366 // 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key
367 Qt::Key_LaunchMail, // 180 0xB4 VK_LAUNCH_MAIL | Start Mail key
368 Qt::Key_LaunchMedia,// 181 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
369 Qt::Key_Launch0, // 182 0xB6 VK_LAUNCH_APP1 | Start Application 1 key
370 Qt::Key_Launch1, // 183 0xB7 VK_LAUNCH_APP2 | Start Application 2 key
371 Qt::Key_unknown, // 184 0xB8 -- reserved --
372 Qt::Key_unknown, // 185 0xB9 -- reserved --
373 0, // 186 0xBA VK_OEM_1 | ';:' for US
374 0, // 187 0xBB VK_OEM_PLUS | '+' any country
375 0, // 188 0xBC VK_OEM_COMMA | ',' any country
376 0, // 189 0xBD VK_OEM_MINUS | '-' any country
377 0, // 190 0xBE VK_OEM_PERIOD | '.' any country
378 0, // 191 0xBF VK_OEM_2 | '/?' for US
379 0, // 192 0xC0 VK_OEM_3 | '`~' for US
380 Qt::Key_unknown, // 193 0xC1 -- reserved --
381 Qt::Key_unknown, // 194 0xC2 -- reserved --
382 Qt::Key_unknown, // 195 0xC3 -- reserved --
383 Qt::Key_unknown, // 196 0xC4 -- reserved --
384 Qt::Key_unknown, // 197 0xC5 -- reserved --
385 Qt::Key_unknown, // 198 0xC6 -- reserved --
386 Qt::Key_unknown, // 199 0xC7 -- reserved --
387 Qt::Key_unknown, // 200 0xC8 -- reserved --
388 Qt::Key_unknown, // 201 0xC9 -- reserved --
389 Qt::Key_unknown, // 202 0xCA -- reserved --
390 Qt::Key_unknown, // 203 0xCB -- reserved --
391 Qt::Key_unknown, // 204 0xCC -- reserved --
392 Qt::Key_unknown, // 205 0xCD -- reserved --
393 Qt::Key_unknown, // 206 0xCE -- reserved --
394 Qt::Key_unknown, // 207 0xCF -- reserved --
395 Qt::Key_unknown, // 208 0xD0 -- reserved --
396 Qt::Key_unknown, // 209 0xD1 -- reserved --
397 Qt::Key_unknown, // 210 0xD2 -- reserved --
398 Qt::Key_unknown, // 211 0xD3 -- reserved --
399 Qt::Key_unknown, // 212 0xD4 -- reserved --
400 Qt::Key_unknown, // 213 0xD5 -- reserved --
401 Qt::Key_unknown, // 214 0xD6 -- reserved --
402 Qt::Key_unknown, // 215 0xD7 -- reserved --
403 Qt::Key_unknown, // 216 0xD8 -- unassigned --
404 Qt::Key_unknown, // 217 0xD9 -- unassigned --
405 Qt::Key_unknown, // 218 0xDA -- unassigned --
406 0, // 219 0xDB VK_OEM_4 | '[{' for US
407 0, // 220 0xDC VK_OEM_5 | '\|' for US
408 0, // 221 0xDD VK_OEM_6 | ']}' for US
409 0, // 222 0xDE VK_OEM_7 | ''"' for US
410 0, // 223 0xDF VK_OEM_8
411 Qt::Key_unknown, // 224 0xE0 -- reserved --
412 Qt::Key_unknown, // 225 0xE1 VK_OEM_AX | 'AX' key on Japanese AX kbd
413 Qt::Key_unknown, // 226 0xE2 VK_OEM_102 | "<>" or "\|" on RT 102-key kbd
414 Qt::Key_unknown, // 227 0xE3 VK_ICO_HELP | Help key on ICO
415 Qt::Key_unknown, // 228 0xE4 VK_ICO_00 | 00 key on ICO
416 Qt::Key_unknown, // 229 0xE5 VK_PROCESSKEY | IME Process key
417 Qt::Key_unknown, // 230 0xE6 VK_ICO_CLEAR |
418 Qt::Key_unknown, // 231 0xE7 VK_PACKET | Unicode char as keystrokes
419 Qt::Key_unknown, // 232 0xE8 -- unassigned --
420 // Nokia/Ericsson definitions ---------------
421 Qt::Key_unknown, // 233 0xE9 VK_OEM_RESET
422 Qt::Key_unknown, // 234 0xEA VK_OEM_JUMP
423 Qt::Key_unknown, // 235 0xEB VK_OEM_PA1
424 Qt::Key_unknown, // 236 0xEC VK_OEM_PA2
425 Qt::Key_unknown, // 237 0xED VK_OEM_PA3
426 Qt::Key_unknown, // 238 0xEE VK_OEM_WSCTRL
427 Qt::Key_unknown, // 239 0xEF VK_OEM_CUSEL
428 Qt::Key_unknown, // 240 0xF0 VK_OEM_ATTN
429 Qt::Key_unknown, // 241 0xF1 VK_OEM_FINISH
430 Qt::Key_unknown, // 242 0xF2 VK_OEM_COPY
431 Qt::Key_unknown, // 243 0xF3 VK_OEM_AUTO
432 Qt::Key_unknown, // 244 0xF4 VK_OEM_ENLW
433 Qt::Key_unknown, // 245 0xF5 VK_OEM_BACKTAB
434 Qt::Key_unknown, // 246 0xF6 VK_ATTN | Attn key
435 Qt::Key_unknown, // 247 0xF7 VK_CRSEL | CrSel key
436 Qt::Key_unknown, // 248 0xF8 VK_EXSEL | ExSel key
437 Qt::Key_unknown, // 249 0xF9 VK_EREOF | Erase EOF key
438 Qt::Key_Play, // 250 0xFA VK_PLAY | Play key
439 Qt::Key_Zoom, // 251 0xFB VK_ZOOM | Zoom key
440 Qt::Key_unknown, // 252 0xFC VK_NONAME | Reserved
441 Qt::Key_unknown, // 253 0xFD VK_PA1 | PA1 key
442 Qt::Key_Clear, // 254 0xFE VK_OEM_CLEAR | Clear key
443 0
444};
445
446static const uint CmdTbl[] = { // Multimedia keys mapping table
447 // Dec | Hex | AppCommand
448 Qt::Key_unknown, // 0 0x00
449 Qt::Key_Back, // 1 0x01 APPCOMMAND_BROWSER_BACKWARD
450 Qt::Key_Forward, // 2 0x02 APPCOMMAND_BROWSER_FORWARD
451 Qt::Key_Refresh, // 3 0x03 APPCOMMAND_BROWSER_REFRESH
452 Qt::Key_Stop, // 4 0x04 APPCOMMAND_BROWSER_STOP
453 Qt::Key_Search, // 5 0x05 APPCOMMAND_BROWSER_SEARCH
454 Qt::Key_Favorites, // 6 0x06 APPCOMMAND_BROWSER_FAVORITES
455 Qt::Key_Home, // 7 0x07 APPCOMMAND_BROWSER_HOME
456 Qt::Key_VolumeMute, // 8 0x08 APPCOMMAND_VOLUME_MUTE
457 Qt::Key_VolumeDown, // 9 0x09 APPCOMMAND_VOLUME_DOWN
458 Qt::Key_VolumeUp, // 10 0x0a APPCOMMAND_VOLUME_UP
459 Qt::Key_MediaNext, // 11 0x0b APPCOMMAND_MEDIA_NEXTTRACK
460 Qt::Key_MediaPrevious, // 12 0x0c APPCOMMAND_MEDIA_PREVIOUSTRACK
461 Qt::Key_MediaStop, // 13 0x0d APPCOMMAND_MEDIA_STOP
462 Qt::Key_MediaTogglePlayPause, // 14 0x0e APPCOMMAND_MEDIA_PLAYPAUSE
463 Qt::Key_LaunchMail, // 15 0x0f APPCOMMAND_LAUNCH_MAIL
464 Qt::Key_LaunchMedia, // 16 0x10 APPCOMMAND_LAUNCH_MEDIA_SELECT
465 Qt::Key_Launch0, // 17 0x11 APPCOMMAND_LAUNCH_APP1
466 Qt::Key_Launch1, // 18 0x12 APPCOMMAND_LAUNCH_APP2
467 Qt::Key_BassDown, // 19 0x13 APPCOMMAND_BASS_DOWN
468 Qt::Key_BassBoost, // 20 0x14 APPCOMMAND_BASS_BOOST
469 Qt::Key_BassUp, // 21 0x15 APPCOMMAND_BASS_UP
470 Qt::Key_TrebleDown, // 22 0x16 APPCOMMAND_TREBLE_DOWN
471 Qt::Key_TrebleUp, // 23 0x17 APPCOMMAND_TREBLE_UP
472 Qt::Key_MicMute, // 24 0x18 APPCOMMAND_MICROPHONE_VOLUME_MUTE
473 Qt::Key_MicVolumeDown, // 25 0x19 APPCOMMAND_MICROPHONE_VOLUME_DOWN
474 Qt::Key_MicVolumeUp, // 26 0x1a APPCOMMAND_MICROPHONE_VOLUME_UP
475 Qt::Key_Help, // 27 0x1b APPCOMMAND_HELP
476 Qt::Key_Find, // 28 0x1c APPCOMMAND_FIND
477 Qt::Key_New, // 29 0x1d APPCOMMAND_NEW
478 Qt::Key_Open, // 30 0x1e APPCOMMAND_OPEN
479 Qt::Key_Close, // 31 0x1f APPCOMMAND_CLOSE
480 Qt::Key_Save, // 32 0x20 APPCOMMAND_SAVE
481 Qt::Key_Printer, // 33 0x21 APPCOMMAND_PRINT
482 Qt::Key_Undo, // 34 0x22 APPCOMMAND_UNDO
483 Qt::Key_Redo, // 35 0x23 APPCOMMAND_REDO
484 Qt::Key_Copy, // 36 0x24 APPCOMMAND_COPY
485 Qt::Key_Cut, // 37 0x25 APPCOMMAND_CUT
486 Qt::Key_Paste, // 38 0x26 APPCOMMAND_PASTE
487 Qt::Key_Reply, // 39 0x27 APPCOMMAND_REPLY_TO_MAIL
488 Qt::Key_MailForward, // 40 0x28 APPCOMMAND_FORWARD_MAIL
489 Qt::Key_Send, // 41 0x29 APPCOMMAND_SEND_MAIL
490 Qt::Key_Spell, // 42 0x2a APPCOMMAND_SPELL_CHECK
491 Qt::Key_unknown, // 43 0x2b APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE
492 Qt::Key_unknown, // 44 0x2c APPCOMMAND_MIC_ON_OFF_TOGGLE
493 Qt::Key_unknown, // 45 0x2d APPCOMMAND_CORRECTION_LIST
494 Qt::Key_MediaPlay, // 46 0x2e APPCOMMAND_MEDIA_PLAY
495 Qt::Key_MediaPause, // 47 0x2f APPCOMMAND_MEDIA_PAUSE
496 Qt::Key_MediaRecord, // 48 0x30 APPCOMMAND_MEDIA_RECORD
497 Qt::Key_AudioForward, // 49 0x31 APPCOMMAND_MEDIA_FAST_FORWARD
498 Qt::Key_AudioRewind, // 50 0x32 APPCOMMAND_MEDIA_REWIND
499 Qt::Key_ChannelDown, // 51 0x33 APPCOMMAND_MEDIA_CHANNEL_DOWN
500 Qt::Key_ChannelUp // 52 0x34 APPCOMMAND_MEDIA_CHANNEL_UP
501};
502
503// Possible modifier states.
504// NOTE: The order of these states match the order in QWindowsKeyMapper::updatePossibleKeyCodes()!
505static const Qt::KeyboardModifiers ModsTbl[] = {
506 Qt::NoModifier, // 0
507 Qt::ShiftModifier, // 1
508 Qt::ControlModifier, // 2
509 Qt::ControlModifier | Qt::ShiftModifier, // 3
510 Qt::AltModifier, // 4
511 Qt::AltModifier | Qt::ShiftModifier, // 5
512 Qt::AltModifier | Qt::ControlModifier, // 6
513 Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
514 Qt::NoModifier, // Fall-back to raw Key_*
515};
516static const size_t NumMods = sizeof ModsTbl / sizeof *ModsTbl;
517static_assert((NumMods == KeyboardLayoutItem::NumQtKeys));
518
519#ifndef QT_NO_DEBUG_STREAM
520QDebug operator<<(QDebug d, const KeyboardLayoutItem &k)
521{
522 QDebugStateSaver saver(d);
523 d.nospace();
524 d << "KeyboardLayoutItem(";
525 if (k.exists) {
526 for (size_t i = 0; i < NumMods; ++i) {
527 if (const quint32 qtKey = k.qtKey[i]) {
528 d << '[' << i << ' ';
529 QtDebugUtils::formatQFlags(d, ModsTbl[i]);
530 d << ' ' << Qt::hex << Qt::showbase << qtKey << Qt::dec << Qt::noshowbase << ' ';
531 QtDebugUtils::formatQEnum(d, Qt::Key(qtKey));
532 if (qtKey >= 32 && qtKey < 128)
533 d << " '" << char(qtKey) << '\'';
534 if (k.deadkeys & (1<<i))
535 d << " deadkey";
536 d << "] ";
537 }
538 }
539 }
540 d << ')';
541 return d;
542}
543#endif // QT_NO_DEBUG_STREAM
544
545/**
546 Remap return or action key to select key for windows mobile.
547*/
548inline quint32 winceKeyBend(quint32 keyCode)
549{
550 return KeyTbl[keyCode];
551}
552
553// Translate a VK into a Qt key code, or unicode character
554static inline quint32 toKeyOrUnicode(quint32 vk, quint32 scancode, unsigned char *kbdBuffer, bool *isDeadkey = nullptr)
555{
556 Q_ASSERT(vk > 0 && vk < 256);
557 quint32 code = 0;
558 QChar unicodeBuffer[5];
559 int res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
560 // When Ctrl modifier is used ToUnicode does not return correct values. In order to assign the
561 // right key the control modifier is removed for just that function if the previous call failed.
562 if (res == 0 && kbdBuffer[VK_CONTROL]) {
563 const unsigned char controlState = kbdBuffer[VK_CONTROL];
564 kbdBuffer[VK_CONTROL] = 0;
565 res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
566 kbdBuffer[VK_CONTROL] = controlState;
567 }
568 if (res)
569 code = unicodeBuffer[0].toUpper().unicode();
570
571 // Qt::Key_*'s are not encoded below 0x20, so try again, and DEL keys (0x7f) is encoded with a
572 // proper Qt::Key_ code
573 if (code < 0x20 || code == 0x7f) // Handles res==0 too
574 code = winceKeyBend(vk);
575
576 if (isDeadkey)
577 *isDeadkey = (res == -1);
578
579 return code == Qt::Key_unknown ? 0 : code;
580}
581
582static inline int asciiToKeycode(char a, int state)
583{
584 a = QtMiscUtils::toAsciiUpper(a);
585 if ((state & Qt::ControlModifier) != 0) {
586 if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_
587 a += '@'; // to @..A..Z.._
588 }
589 return a & 0xff;
590}
591
592// Key translation -----------------------------------------------------------------------[ end ]---
593
594
595// Keyboard map private ----------------------------------------------------------------[ start ]---
596
597void QWindowsKeyMapper::deleteLayouts()
598{
599 for (KeyboardLayoutItem &k : keyLayout)
600 k.exists = false;
601}
602
604{
605 deleteLayouts();
606
607 /* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout()
608 * returns a DWORD. */
609
610 LCID newLCID = MAKELCID(quintptr(GetKeyboardLayout(0)), SORT_DEFAULT);
611// keyboardInputLocale = qt_localeFromLCID(newLCID);
612
613 bool bidi = false;
614 wchar_t LCIDFontSig[16];
615 if (GetLocaleInfo(newLCID, LOCALE_FONTSIGNATURE, LCIDFontSig, sizeof(LCIDFontSig) / sizeof(wchar_t))
616 && (LCIDFontSig[7] & wchar_t(0x0800)))
617 bidi = true;
618
619 keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight;
620 m_seenAltGr = false;
621}
622
623// Helper function that is used when obtaining the list of characters that can be produced by one key and
624// every possible combination of modifiers
625inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt)
626{
627 kbd[VK_LSHIFT ] = (shift ? 0x80 : 0);
628 kbd[VK_SHIFT ] = (shift ? 0x80 : 0);
629 kbd[VK_LCONTROL] = (ctrl ? 0x80 : 0);
630 kbd[VK_CONTROL ] = (ctrl ? 0x80 : 0);
631 kbd[VK_RMENU ] = (alt ? 0x80 : 0);
632 kbd[VK_MENU ] = (alt ? 0x80 : 0);
633}
634
635// Adds the msg's key to keyLayout if it is not yet present there
636void QWindowsKeyMapper::updateKeyMap(const MSG &msg)
637{
638 unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
639 GetKeyboardState(kbdBuffer);
640 const quint32 scancode = getScancode(msg);
641 updatePossibleKeyCodes(kbdBuffer, scancode, quint32(msg.wParam));
642}
643
644// Fills keyLayout for that vk_key. Values are all characters one can type using that key
645// (in connection with every combination of modifiers) and whether these "characters" are
646// dead keys.
647void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode,
648 quint32 vk_key)
649{
650 if (!vk_key || (keyLayout[vk_key].exists && !keyLayout[vk_key].dirty))
651 return;
652
653 // Copy keyboard state, so we can modify and query output for each possible permutation
654 unsigned char buffer[256];
655 memcpy(buffer, kbdBuffer, sizeof(buffer));
656 // Always 0, as Windows doesn't treat these as modifiers;
657 buffer[VK_LWIN ] = 0;
658 buffer[VK_RWIN ] = 0;
659 buffer[VK_CAPITAL ] = 0;
660 buffer[VK_NUMLOCK ] = 0;
661 buffer[VK_SCROLL ] = 0;
662 // Always 0, since we'll only change the other versions
663 buffer[VK_RSHIFT ] = 0;
664 buffer[VK_RCONTROL] = 0;
665 buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph
666
667 // keyLayout contains the actual characters which can be written using the vk_key together with the
668 // different modifiers. '2' together with shift will for example cause the character
669 // to be @ for a US key layout (thus keyLayout[vk_key].qtKey[1] will be @). In addition to that
670 // it stores whether the resulting key is a dead key as these keys have to be handled later.
671 bool isDeadKey = false;
672 keyLayout[vk_key].deadkeys = 0;
673 keyLayout[vk_key].dirty = false;
674 keyLayout[vk_key].exists = true;
675 setKbdState(buffer, false, false, false);
676 keyLayout[vk_key].qtKey[0] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
677 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x01 : 0;
678 setKbdState(buffer, true, false, false);
679 keyLayout[vk_key].qtKey[1] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
680 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x02 : 0;
681 setKbdState(buffer, false, true, false);
682 keyLayout[vk_key].qtKey[2] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
683 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x04 : 0;
684 setKbdState(buffer, true, true, false);
685 keyLayout[vk_key].qtKey[3] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
686 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x08 : 0;
687 setKbdState(buffer, false, false, true);
688 keyLayout[vk_key].qtKey[4] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
689 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x10 : 0;
690 setKbdState(buffer, true, false, true);
691 keyLayout[vk_key].qtKey[5] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
692 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x20 : 0;
693 setKbdState(buffer, false, true, true);
694 keyLayout[vk_key].qtKey[6] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
695 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x40 : 0;
696 setKbdState(buffer, true, true, true);
697 keyLayout[vk_key].qtKey[7] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
698 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x80 : 0;
699 // Add a fall back key for layouts which don't do composition and show non-latin1 characters
700 quint32 fallbackKey = winceKeyBend(vk_key);
701 if (!fallbackKey || fallbackKey == Qt::Key_unknown) {
702 fallbackKey = 0;
703 if (vk_key != keyLayout[vk_key].qtKey[0] && vk_key != keyLayout[vk_key].qtKey[1]
704 && vk_key < 0x5B && vk_key > 0x2F)
705 fallbackKey = vk_key;
706 }
707 keyLayout[vk_key].qtKey[8] = fallbackKey;
708
709 // If one of the values inserted into the keyLayout above, can be considered a dead key, we have
710 // to run the workaround below.
711 if (keyLayout[vk_key].deadkeys) {
712 // Push a Space, then the original key through the low-level ToAscii functions.
713 // We do this because these functions (ToAscii / ToUnicode) will alter the internal state of
714 // the keyboard driver By doing the following, we set the keyboard driver state back to what
715 // it was before we wrecked it with the code above.
716 // We need to push the space with an empty keystate map, since the driver checks the map for
717 // transitions in modifiers, so this helps us capture all possible deadkeys.
718 unsigned char emptyBuffer[256];
719 memset(emptyBuffer, 0, sizeof(emptyBuffer));
720 ::ToAscii(VK_SPACE, 0, emptyBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
721 ::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
722 }
723 qCDebug(lcQpaEvents) << __FUNCTION__ << "for virtual key="
724 << Qt::hex << Qt::showbase << vk_key << Qt::dec << Qt::noshowbase << keyLayout[vk_key];
725}
726
727static inline QString messageKeyText(const MSG &msg)
728{
729 const QChar ch = QChar(ushort(msg.wParam));
730 return ch.isNull() ? QString() : QString(ch);
731}
732
733[[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd)
734{
735 const UINT dpi = GetDpiForWindow(hwnd);
736 const int captionHeight = GetSystemMetricsForDpi(SM_CYCAPTION, dpi);
737 if (IsZoomed(hwnd))
738 return captionHeight;
739 // The frame height should also be taken into account if the window
740 // is not maximized.
741 const int frameHeight = GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi)
742 + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
743 return captionHeight + frameHeight;
744}
745
746[[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags)
747{
748 static constexpr const Qt::WindowFlags titleBarHints =
749 Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint;
750 return (flags & Qt::WindowSystemMenuHint) && (flags & Qt::WindowTitleHint) && !(flags & titleBarHints)
751 && (flags & (Qt::FramelessWindowHint | Qt::CustomizeWindowHint));
752}
753
754static void showSystemMenu(QWindow* w)
755{
756 QWindow *topLevel = QWindowsWindow::topLevelOf(w);
757 HWND topLevelHwnd = QWindowsWindow::handleOf(topLevel);
758 HMENU menu = GetSystemMenu(topLevelHwnd, FALSE);
759 if (!menu)
760 return; // no menu for this window
761
762#define enabled (MF_BYCOMMAND | MFS_ENABLED)
763#define disabled (MF_BYCOMMAND | MFS_GRAYED)
764
765 EnableMenuItem(menu, SC_MINIMIZE, (topLevel->flags() & Qt::WindowMinimizeButtonHint) ? enabled : disabled);
766 const bool maximized = IsZoomed(topLevelHwnd);
767
768 EnableMenuItem(menu, SC_MAXIMIZE, !(topLevel->flags() & Qt::WindowMaximizeButtonHint) || maximized ? disabled : enabled);
769
770 // We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check
771 // this and our menu here would be out-of-sync with the menu produced by mouse-click on the
772 // System Menu, or right-click on the title bar.
773 EnableMenuItem(menu, SC_SIZE, (topLevel->flags() & Qt::MSWindowsFixedSizeDialogHint) || maximized ? disabled : enabled);
774 EnableMenuItem(menu, SC_MOVE, maximized ? disabled : enabled);
775 EnableMenuItem(menu, SC_CLOSE, enabled);
776 EnableMenuItem(menu, SC_RESTORE, maximized ? enabled : disabled);
777
778 // Highlight the first entry in the menu, this is what native Win32 applications usually do.
779 HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_HILITE);
780
781 // Set bold on close menu item
782 SetMenuDefaultItem(menu, SC_CLOSE, FALSE);
783
784#undef enabled
785#undef disabled
786
787 const QPoint pos = QHighDpi::toNativePixels(topLevel->geometry().topLeft(), topLevel);
788 const int titleBarOffset = isSystemMenuOffsetNeeded(topLevel->flags()) ? getTitleBarHeight(topLevelHwnd) : 0;
789 const int ret = TrackPopupMenuEx(menu,
790 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
791 pos.x(), pos.y() + titleBarOffset,
792 topLevelHwnd,
793 nullptr);
794
795 // Remove the highlight of the restore menu item, otherwise when the user right-clicks
796 // on the title bar, the popuped system menu will also highlight the restore item, which
797 // is not appropriate, it should only be highlighted if the menu is brought up by keyboard.
798 HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_UNHILITE);
799
800 if (ret)
801 qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, WPARAM(ret), 0);
802}
803
804static inline void sendExtendedPressRelease(QWindow *w, unsigned long timestamp, int k,
805 Qt::KeyboardModifiers mods,
806 quint32 nativeScanCode,
807 quint32 nativeVirtualKey,
808 quint32 nativeModifiers,
809 const QString & text = QString(),
810 bool autorep = false,
811 ushort count = 1)
812{
813 QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
814 QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
815}
816
817/*!
818 \brief To be called from the window procedure.
819*/
820
821bool QWindowsKeyMapper::translateKeyEvent(QWindow *widget, HWND hwnd,
822 const MSG &msg, LRESULT *result)
823{
824 *result = 0;
825
826 // Reset layout map when system keyboard layout is changed
827 if (msg.message == WM_INPUTLANGCHANGE) {
829 return true;
830 }
831
832#if defined(WM_APPCOMMAND)
833 if (msg.message == WM_APPCOMMAND)
834 return translateMultimediaKeyEventInternal(widget, msg);
835#endif
836
837 // WM_(IME_)CHAR messages already contain the character in question so there is
838 // no need to fiddle with our key map. In any other case add this key to the
839 // keymap if it is not present yet.
840 if (msg.message != WM_CHAR && msg.message != WM_IME_CHAR)
841 updateKeyMap(msg);
842
843 MSG peekedMsg;
844 // consume dead chars?(for example, typing '`','a' resulting in a-accent).
845 if (PeekMessage(&peekedMsg, hwnd, 0, 0, PM_NOREMOVE) && peekedMsg.message == WM_DEADCHAR)
846 return true;
847
848 return translateKeyEventInternal(widget, msg, false, result);
849}
850
851bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, const MSG &msg)
852{
853#if defined(WM_APPCOMMAND)
854 const int cmd = GET_APPCOMMAND_LPARAM(msg.lParam);
855 // QTBUG-57198, do not send mouse-synthesized commands as key events in addition
856 bool skipPressRelease = false;
857 switch (GET_DEVICE_LPARAM(msg.lParam)) {
858 case FAPPCOMMAND_MOUSE:
859 return false;
860 case FAPPCOMMAND_KEY:
861 // QTBUG-62838, use WM_KEYDOWN/WM_KEYUP for commands that are reflected
862 // in VK(s) like VK_MEDIA_NEXT_TRACK, to get correct codes and autorepeat.
863 // Don't do that for APPCOMMAND_BROWSER_HOME as that one does not trigger two events.
864 if (cmd != APPCOMMAND_BROWSER_HOME)
865 skipPressRelease = true;
866 break;
867 }
868
869 const int dwKeys = GET_KEYSTATE_LPARAM(msg.lParam);
870 int state = 0;
871 state |= (dwKeys & MK_SHIFT ? int(Qt::ShiftModifier) : 0);
872 state |= (dwKeys & MK_CONTROL ? int(Qt::ControlModifier) : 0);
873
874 QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
875
876 if (cmd < 0 || cmd > 52)
877 return false;
878
879 const int qtKey = int(CmdTbl[cmd]);
880 if (!skipPressRelease)
881 sendExtendedPressRelease(receiver, msg.time, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
882 // QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise,
883 // the keys are not passed to the active media player.
884# if QT_CONFIG(shortcut)
885 const QKeySequence sequence(Qt::Modifier(state) | Qt::Key(qtKey));
886 return QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence);
887# else
888 return false;
889# endif
890#else
891 Q_UNREACHABLE();
892 return false;
893#endif
894}
895
896// QTBUG-69317: Check for AltGr found on some keyboards
897// which is a sequence of left Ctrl (SYSKEY) + right Menu (Alt).
898static bool isAltGr(MSG *msg)
899{
900 enum : LONG_PTR { RightFlag = 0x1000000 };
901 if (msg->wParam != VK_CONTROL || (msg->lParam & RightFlag) != 0
902 || (msg->message != WM_KEYDOWN && msg->message != WM_SYSKEYUP)) {
903 return false;
904 }
905 const UINT expectedMessage = msg->message == WM_SYSKEYUP
906 ? WM_KEYUP : msg->message;
907 MSG peekedMsg;
908 if (PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_NOREMOVE) == FALSE
909 || peekedMsg.message != expectedMessage || peekedMsg.wParam != VK_MENU
910 || (peekedMsg.lParam & RightFlag) == 0) {
911 return false;
912 }
913 *msg = peekedMsg;
914 PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_REMOVE);
915 return true;
916}
917
918bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
919 bool /* grab */, LRESULT *lResult)
920{
921 const bool altGr = m_detectAltGrModifier && isAltGr(&msg);
922 if (altGr)
923 m_seenAltGr = true;
924 const UINT msgType = msg.message;
925
926 const quint32 scancode = getScancode(msg);
927 auto vk_key = quint32(msg.wParam);
928 quint32 nModifiers = 0;
929
930 QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
931
932 // Map native modifiers to some bit representation
933 nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0);
934 nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0);
935 nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0);
936 nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0);
937 nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0);
938 nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0);
939 nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0);
940 nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0);
941 // Add Lock keys to the same bits
942 nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0);
943 nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0);
944 nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0);
945
946 if (msg.lParam & ExtendedKey)
947 nModifiers |= msg.lParam & ExtendedKey;
948
949 // Get the modifier states (may be altered later, depending on key code)
950 int state = 0;
951 state |= (nModifiers & ShiftAny ? int(Qt::ShiftModifier) : 0);
952 state |= (nModifiers & AltLeft ? int(Qt::AltModifier) : 0);
953 if ((nModifiers & AltRight) != 0)
954 state |= m_seenAltGr ? Qt::GroupSwitchModifier : Qt::AltModifier;
955 if ((nModifiers & ControlAny) != 0 && (state & Qt::GroupSwitchModifier) == 0)
956 state |= Qt::ControlModifier;
957 state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0);
958 // A multi-character key or a Input method character
959 // not found by our look-ahead
960 if (msgType == WM_CHAR || msgType == WM_IME_CHAR) {
961 sendExtendedPressRelease(receiver, msg.time, 0, Qt::KeyboardModifier(state), scancode, 0, nModifiers, messageKeyText(msg), false);
962 return true;
963 }
964
965 // Enable Alt accelerators ("&File") on menus
966 if (msgType == WM_SYSKEYDOWN && (nModifiers & AltAny) != 0 && GetMenu(msg.hwnd) != nullptr)
967 return false;
968 if (msgType == WM_SYSKEYUP && nModifiers == 0 && GetMenu(msg.hwnd) != nullptr)
969 return false;
970
971 bool result = false;
972 // handle Directionality changes (BiDi) with RTL extensions
973 if (m_useRTLExtensions) {
974 static int dirStatus = 0;
975 if (!dirStatus && state == Qt::ControlModifier
976 && msg.wParam == VK_CONTROL
977 && msgType == WM_KEYDOWN) {
978 if (GetKeyState(VK_LCONTROL) < 0)
979 dirStatus = VK_LCONTROL;
980 else if (GetKeyState(VK_RCONTROL) < 0)
981 dirStatus = VK_RCONTROL;
982 } else if (dirStatus) {
983 if (msgType == WM_KEYDOWN) {
984 if (msg.wParam == VK_SHIFT) {
985 if (dirStatus == VK_LCONTROL && GetKeyState(VK_LSHIFT) < 0)
986 dirStatus = VK_LSHIFT;
987 else if (dirStatus == VK_RCONTROL && GetKeyState(VK_RSHIFT) < 0)
988 dirStatus = VK_RSHIFT;
989 } else {
990 dirStatus = 0;
991 }
992 } else if (msgType == WM_KEYUP) {
993 if (dirStatus == VK_LSHIFT
994 && ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL))
995 || (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) {
996 sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_L, {},
997 scancode, vk_key, nModifiers, QString(), false);
998 result = true;
999 dirStatus = 0;
1000 } else if (dirStatus == VK_RSHIFT
1001 && ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL))
1002 || (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) {
1003 sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_R, {},
1004 scancode, vk_key, nModifiers, QString(), false);
1005 result = true;
1006 dirStatus = 0;
1007 } else {
1008 dirStatus = 0;
1009 }
1010 } else {
1011 dirStatus = 0;
1012 }
1013 }
1014 } // RTL
1015
1016 // IME will process these keys, so simply return
1017 if (msg.wParam == VK_PROCESSKEY)
1018 return true;
1019
1020 // Ignore invalid virtual keycodes (see bugs 127424, QTBUG-3630)
1021 if (msg.wParam == 0 || msg.wParam == 0xFF)
1022 return true;
1023
1024 // Translate VK_* (native) -> Key_* (Qt) keys
1025 int modifiersIndex = 0;
1026 modifiersIndex |= (nModifiers & ShiftAny ? 0x1 : 0);
1027 modifiersIndex |= (nModifiers & ControlAny ? 0x2 : 0);
1028 modifiersIndex |= (nModifiers & AltAny ? 0x4 : 0);
1029
1030 // Note: For the resulting key, AltGr is equivalent to Alt + Ctrl (as
1031 // opposed to Linux); hence no entry in KeyboardLayoutItem is required
1032 int code = keyLayout[vk_key].qtKey[modifiersIndex];
1033
1034 // If the bit 24 of lParm is set you received a enter,
1035 // otherwise a Return. (This is the extended key bit)
1036 if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000))
1037 code = Qt::Key_Enter;
1038 else if (altGr)
1039 code = Qt::Key_AltGr;
1040
1041 // Invert state logic:
1042 // If the key actually pressed is a modifier key, then we remove its modifier key from the
1043 // state, since a modifier-key can't have itself as a modifier
1044 if (code == Qt::Key_Control)
1045 state = state ^ Qt::ControlModifier;
1046 else if (code == Qt::Key_Shift)
1047 state = state ^ Qt::ShiftModifier;
1048 else if (code == Qt::Key_Alt)
1049 state = state ^ Qt::AltModifier;
1050 else if (code == Qt::Key_AltGr)
1051 state = state ^ Qt::GroupSwitchModifier;
1052
1053 // All cursor keys without extended bit
1054 if (!(msg.lParam & 0x1000000)) {
1055 switch (code) {
1056 case Qt::Key_Left:
1057 case Qt::Key_Right:
1058 case Qt::Key_Up:
1059 case Qt::Key_Down:
1060 case Qt::Key_PageUp:
1061 case Qt::Key_PageDown:
1062 case Qt::Key_Home:
1063 case Qt::Key_End:
1064 case Qt::Key_Insert:
1065 case Qt::Key_Delete:
1066 case Qt::Key_Asterisk:
1067 case Qt::Key_Plus:
1068 case Qt::Key_Minus:
1069 case Qt::Key_Period:
1070 case Qt::Key_Comma:
1071 case Qt::Key_0:
1072 case Qt::Key_1:
1073 case Qt::Key_2:
1074 case Qt::Key_3:
1075 case Qt::Key_4:
1076 case Qt::Key_5:
1077 case Qt::Key_6:
1078 case Qt::Key_7:
1079 case Qt::Key_8:
1080 case Qt::Key_9:
1081 state |= ((msg.wParam >= '0' && msg.wParam <= '9')
1082 || (msg.wParam >= VK_OEM_PLUS && msg.wParam <= VK_OEM_3))
1083 ? 0 : int(Qt::KeypadModifier);
1084 Q_FALLTHROUGH();
1085 default:
1086 if (uint(msg.lParam) == 0x004c0001 || uint(msg.lParam) == 0xc04c0001)
1087 state |= Qt::KeypadModifier;
1088 break;
1089 }
1090 }
1091 // Other keys with with extended bit
1092 else {
1093 switch (code) {
1094 case Qt::Key_Enter:
1095 case Qt::Key_Slash:
1096 case Qt::Key_NumLock:
1097 state |= Qt::KeypadModifier;
1098 break;
1099 default:
1100 break;
1101 }
1102 }
1103
1104 // KEYDOWN ---------------------------------------------------------------------------------
1105 if (msgType == WM_KEYDOWN || msgType == WM_IME_KEYDOWN || msgType == WM_SYSKEYDOWN) {
1106 // Get the last record of this key press, so we can validate the current state
1107 // The record is not removed from the list
1108 KeyRecord *rec = key_recorder.findKey(int(msg.wParam), false);
1109
1110 // If rec's state doesn't match the current state, something has changed behind our back
1111 // (Consumed by modal widget is one possibility) So, remove the record from the list
1112 // This will stop the auto-repeat of the key, should a modifier change, for example
1113 if (rec && rec->state != state) {
1114 key_recorder.findKey(int(msg.wParam), true);
1115 rec = nullptr;
1116 }
1117
1118 // Find unicode character from Windows Message Queue
1119 MSG wm_char;
1120 UINT charType = (msgType == WM_KEYDOWN
1121 ? WM_CHAR
1122 : msgType == WM_IME_KEYDOWN ? WM_IME_CHAR : WM_SYSCHAR);
1123
1124 QChar uch;
1125 if (PeekMessage(&wm_char, nullptr, charType, charType, PM_REMOVE)) {
1126 if (QWindowsContext::filterNativeEvent(&wm_char, lResult))
1127 return true;
1128 if (receiver && QWindowsContext::filterNativeEvent(receiver, &wm_char, lResult))
1129 return true;
1130 // Found a ?_CHAR
1131 uch = QChar(ushort(wm_char.wParam));
1132 if (uch.isHighSurrogate()) {
1133 m_lastHighSurrogate = uch;
1134 return true;
1135 }
1136 if (uch.isLowSurrogate() && !m_lastHighSurrogate.isNull()) {
1137 if (QObject *focusObject = QGuiApplication::focusObject()) {
1138 const QChar chars[2] = {m_lastHighSurrogate, uch};
1139 QInputMethodEvent event;
1140 event.setCommitString(QString(chars, 2));
1141 QCoreApplication::sendEvent(focusObject, &event);
1142 }
1143 m_lastHighSurrogate = QChar();
1144 return true;
1145 } else {
1146 m_lastHighSurrogate = QChar();
1147 }
1148 if (msgType == WM_SYSKEYDOWN && uch.isLetter() && (msg.lParam & KF_ALTDOWN))
1149 uch = uch.toLower(); // (See doc of WM_SYSCHAR) Alt-letter
1150 if (!code && !uch.row())
1151 code = asciiToKeycode(char(uch.cell()), state);
1152 }
1153
1154 // Special handling for the WM_IME_KEYDOWN message. Microsoft IME (Korean) will not
1155 // generate a WM_IME_CHAR message corresponding to this message. We might get wrong
1156 // results, if we map this virtual key-code directly (for eg '?' US layouts). So try
1157 // to find the correct key using the current message parameters & keyboard state.
1158 if (uch.isNull() && msgType == WM_IME_KEYDOWN) {
1159 const auto *windowsInputContext =
1160 qobject_cast<const QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
1161 if (!(windowsInputContext && windowsInputContext->isComposing()))
1162 vk_key = ImmGetVirtualKey(reinterpret_cast<HWND>(window->winId()));
1163 BYTE keyState[256];
1164 wchar_t newKey[3] = {0};
1165 GetKeyboardState(keyState);
1166 int val = ToUnicode(vk_key, scancode, keyState, newKey, 2, 0);
1167 if (val == 1) {
1168 uch = QChar(newKey[0]);
1169 } else {
1170 // If we are still not able to find a unicode key, pass the WM_IME_KEYDOWN
1171 // message to DefWindowProc() for generating a proper WM_KEYDOWN.
1172 return false;
1173 }
1174 }
1175
1176 // If no ?_CHAR was found in the queue; deduct character from the ?_KEYDOWN parameters
1177 if (uch.isNull()) {
1178 if (msg.wParam == VK_DELETE) {
1179 uch = QChar(QLatin1Char(0x7f)); // Windows doesn't know this one.
1180 } else {
1181 if (msgType != WM_SYSKEYDOWN || !code) {
1182 UINT map = MapVirtualKey(UINT(msg.wParam), 2);
1183 // If the high bit of the return value is set, it's a deadkey
1184 if (!(map & 0x80000000))
1185 uch = QChar(ushort(map));
1186 }
1187 }
1188 if (!code && !uch.row())
1189 code = asciiToKeycode(char(uch.cell()), state);
1190 }
1191
1192 // Special handling of global Windows hotkeys
1193 if (state == Qt::AltModifier) {
1194 switch (code) {
1195 case Qt::Key_Escape:
1196 case Qt::Key_Tab:
1197 case Qt::Key_F4:
1198 return false; // Send the event on to Windows
1199 case Qt::Key_Space:
1200 // do not pass this key to windows, we will process it ourselves
1201 showSystemMenu(receiver);
1202 return true;
1203 default:
1204 break;
1205 }
1206 }
1207
1208 // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
1209 if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
1210 code = Qt::Key_Backtab;
1211
1212 // If we have a record, it means that the key is already pressed, the state is the same
1213 // so, we have an auto-repeating key
1214 if (rec) {
1215 if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) {
1216 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
1217 Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
1218 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
1219 Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
1220 result = true;
1221 }
1222 }
1223 // No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
1224 // and store the key data into our records.
1225 else {
1226 const QString text = uch.isNull() ? QString() : QString(uch);
1227 const char a = uch.row() ? char(0) : char(uch.cell());
1228 const Qt::KeyboardModifiers modifiers(state);
1229#ifndef QT_NO_SHORTCUT
1230 // Is Qt interested in the context menu key?
1231 if (modifiers == Qt::SHIFT && code == Qt::Key_F10
1232 && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(QKeySequence(Qt::SHIFT | Qt::Key_F10))) {
1233 return false;
1234 }
1235#endif // !QT_NO_SHORTCUT
1236 key_recorder.storeKey(int(msg.wParam), a, state, text);
1237
1238 // QTBUG-71210
1239 // VK_PACKET specifies multiple characters. The system only sends the first
1240 // character of this sequence for each.
1241 if (msg.wParam == VK_PACKET)
1242 code = asciiToKeycode(char(uch.cell()), state);
1243
1244 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
1245 modifiers, scancode, quint32(msg.wParam), nModifiers, text, false);
1246 result =true;
1247 bool store = true;
1248 // Alt+<alphanumerical> go to the Win32 menu system if unhandled by Qt
1249 if (msgType == WM_SYSKEYDOWN && !result && a) {
1250 HWND parent = GetParent(QWindowsWindow::handleOf(receiver));
1251 while (parent) {
1252 if (GetMenu(parent)) {
1253 SendMessage(parent, WM_SYSCOMMAND, SC_KEYMENU, a);
1254 store = false;
1255 result = true;
1256 break;
1257 }
1258 parent = GetParent(parent);
1259 }
1260 }
1261 if (!store)
1262 key_recorder.findKey(int(msg.wParam), true);
1263 }
1264 }
1265
1266 // KEYUP -----------------------------------------------------------------------------------
1267 else {
1268 // Try to locate the key in our records, and remove it if it exists.
1269 // The key may not be in our records if, for example, the down event was handled by
1270 // win32 natively, or our window gets focus while a key is already press, but now gets
1271 // the key release event.
1272 const KeyRecord *rec = key_recorder.findKey(int(msg.wParam), true);
1273 if (!rec && !(code == Qt::Key_Shift
1274 || code == Qt::Key_Control
1275 || code == Qt::Key_Meta
1276 || code == Qt::Key_Alt)) {
1277
1278 // Workaround for QTBUG-77153:
1279 // The Surface Pen eraser button generates Meta+F18/19/20 keystrokes,
1280 // but when it is not touching the screen the Fn Down is eaten and only
1281 // a Fn Up with the previous state as "not pressed" is generated, which
1282 // would be ignored. We detect this case and synthesize the expected events.
1283 if ((msg.lParam & 0x40000000) == 0 &&
1284 Qt::KeyboardModifier(state) == Qt::NoModifier &&
1285 ((code == Qt::Key_F18) || (code == Qt::Key_F19) || (code == Qt::Key_F20))) {
1286 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
1287 Qt::MetaModifier, scancode,
1288 quint32(msg.wParam), MetaLeft);
1289 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
1290 Qt::NoModifier, scancode,
1291 quint32(msg.wParam), 0);
1292 result = true;
1293 }
1294 } else {
1295 if (!code)
1296 code = asciiToKeycode(rec->ascii ? char(rec->ascii) : char(msg.wParam), state);
1297
1298 // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
1299 if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
1300 code = Qt::Key_Backtab;
1301 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
1302 Qt::KeyboardModifier(state), scancode, quint32(msg.wParam),
1303 nModifiers,
1304 (rec ? rec->text : QString()), false);
1305 result = true;
1306 // don't pass Alt to Windows unless we are embedded in a non-Qt window
1307 if (code == Qt::Key_Alt) {
1309 HWND parent = GetParent(QWindowsWindow::handleOf(receiver));
1310 while (parent) {
1311 if (!context->findPlatformWindow(parent) && GetMenu(parent)) {
1312 result = false;
1313 break;
1314 }
1315 parent = GetParent(parent);
1316 }
1317 }
1318 }
1319 }
1320 return result;
1321}
1322
1324{
1325 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
1326 if (GetKeyState(VK_SHIFT) < 0)
1327 modifiers |= Qt::ShiftModifier;
1328 if (GetKeyState(VK_CONTROL) < 0)
1329 modifiers |= Qt::ControlModifier;
1330 if (GetKeyState(VK_MENU) < 0)
1331 modifiers |= Qt::AltModifier;
1332 if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0)
1333 modifiers |= Qt::MetaModifier;
1334 return modifiers;
1335}
1336
1338{
1339 QList<QKeyCombination> result;
1340
1341
1342 const quint32 nativeVirtualKey = e->nativeVirtualKey();
1343 if (nativeVirtualKey > 255)
1344 return result;
1345
1346 const KeyboardLayoutItem &kbItem = keyLayout[nativeVirtualKey];
1347 if (!kbItem.exists)
1348 return result;
1349
1350 quint32 baseKey = kbItem.qtKey[0];
1351 Qt::KeyboardModifiers keyMods = e->modifiers();
1352 if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) {
1353 result << (Qt::Key_Enter | keyMods);
1354 return result;
1355 }
1356
1357 // If Key_Tab+Shift is pressed we add Key_Backtab without
1358 // shift modifier as a possible combination too
1359 if (baseKey == Qt::Key_Tab && (keyMods & Qt::ShiftModifier))
1360 result << (Qt::Key_Backtab | (keyMods & ~Qt::ShiftModifier));
1361
1362 // The base key is _always_ valid, of course
1363 result << QKeyCombination::fromCombined(int(baseKey) + int(keyMods));
1364
1365 for (size_t i = 1; i < NumMods; ++i) {
1366 Qt::KeyboardModifiers neededMods = ModsTbl[i];
1367 quint32 key = kbItem.qtKey[i];
1368 if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) {
1369 const Qt::KeyboardModifiers missingMods = keyMods & ~neededMods;
1370 const auto matchedKey = QKeyCombination::fromCombined(int(key) + int(missingMods));
1371 const auto it = std::find_if(result.begin(), result.end(),
1372 [key](auto keyCombination) {
1373 return keyCombination.key() == key;
1374 });
1375 // QTBUG-67200: Use the match with the least modifiers (prefer
1376 // Shift+9 over Alt + Shift + 9) resulting in more missing modifiers.
1377 if (it == result.end())
1378 result << matchedKey;
1379 else if (missingMods > it->keyboardModifiers())
1380 *it = matchedKey;
1381 }
1382 }
1383 qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey="
1384 << Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase
1385 << e->modifiers() << kbItem << "\n returns" << result;
1386 return result;
1387}
1388
1389QT_END_NAMESPACE
\inmodule QtCore\reentrant
Definition qpoint.h:29
Singleton container for all relevant information.
QWindowsWindow * findPlatformWindow(const QWindowsMenuBar *mb) const
static QWindowsContext * instance()
Windows Input context implementation.
static QWindowsIntegration * instance()
Translates Windows keys to QWindowSystemInterface events.
bool translateKeyEvent(QWindow *widget, HWND hwnd, const MSG &msg, LRESULT *result)
To be called from the window procedure.
Qt::KeyboardModifiers queryKeyboardModifiers() const override
QList< QKeyCombination > possibleKeyCombinations(const QKeyEvent *e) const override
Raster or OpenGL Window.
static const Qt::KeyboardModifiers ModsTbl[]
static bool isAltGr(MSG *msg)
static constexpr quint32 getScancode(const MSG &msg)
static int asciiToKeycode(char a, int state)
#define enabled
static const size_t NumMods
#define disabled
static const int QT_MAX_KEY_RECORDINGS
#define VK_OEM_PLUS
static int getTitleBarHeight(const HWND hwnd)
quint32 winceKeyBend(quint32 keyCode)
static void sendExtendedPressRelease(QWindow *w, unsigned long timestamp, int k, Qt::KeyboardModifiers mods, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString &text=QString(), bool autorep=false, ushort count=1)
static QString messageKeyText(const MSG &msg)
void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt)
static quint32 toKeyOrUnicode(quint32 vk, quint32 scancode, unsigned char *kbdBuffer, bool *isDeadkey=nullptr)
static const uint CmdTbl[]
static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state)
static bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags)
static void showSystemMenu(QWindow *w)
static KeyRecorder key_recorder
#define VK_OEM_3
static const uint KeyTbl[]
@ ExtendedKey
@ ShiftAny
@ ControlAny
KeyRecord(int c, int a, int s, const QString &t)
void storeKey(int code, int ascii, int state, const QString &text)
KeyRecord deleted_record
KeyRecord * findKey(int code, bool remove)
KeyRecord records[QT_MAX_KEY_RECORDINGS]