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