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 WCHAR buffer[KL_NAMELENGTH];
621 if (GetKeyboardLayoutName(buffer)) {
622 // https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values
623 m_isHebrewLayout = buffer[KL_NAMELENGTH - 4] == L'4' && buffer[KL_NAMELENGTH - 3] == L'0'
624 && buffer[KL_NAMELENGTH - 2] == L'D';
625 } else
626 m_isHebrewLayout = false;
627
628 keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight;
629 m_seenAltGr = false;
630}
631
632// Helper function that is used when obtaining the list of characters that can be produced by one key and
633// every possible combination of modifiers
634inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt)
635{
636 kbd[VK_LSHIFT ] = (shift ? 0x80 : 0);
637 kbd[VK_SHIFT ] = (shift ? 0x80 : 0);
638 kbd[VK_LCONTROL] = (ctrl ? 0x80 : 0);
639 kbd[VK_CONTROL ] = (ctrl ? 0x80 : 0);
640 kbd[VK_RMENU ] = (alt ? 0x80 : 0);
641 kbd[VK_MENU ] = (alt ? 0x80 : 0);
642}
643
644// Adds the msg's key to keyLayout if it is not yet present there
645void QWindowsKeyMapper::updateKeyMap(const MSG &msg)
646{
647 unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
648 GetKeyboardState(kbdBuffer);
649 const quint32 scancode = getScancode(msg);
650 updatePossibleKeyCodes(kbdBuffer, scancode, quint32(msg.wParam));
651}
652
653// Fills keyLayout for that vk_key. Values are all characters one can type using that key
654// (in connection with every combination of modifiers) and whether these "characters" are
655// dead keys.
656void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode,
657 quint32 vk_key)
658{
659 if (!vk_key || (keyLayout[vk_key].exists && !keyLayout[vk_key].dirty))
660 return;
661
662 // Copy keyboard state, so we can modify and query output for each possible permutation
663 unsigned char buffer[256];
664 memcpy(buffer, kbdBuffer, sizeof(buffer));
665 // Always 0, as Windows doesn't treat these as modifiers;
666 buffer[VK_LWIN ] = 0;
667 buffer[VK_RWIN ] = 0;
668 buffer[VK_CAPITAL ] = 0;
669 buffer[VK_NUMLOCK ] = 0;
670 buffer[VK_SCROLL ] = 0;
671 // Always 0, since we'll only change the other versions
672 buffer[VK_RSHIFT ] = 0;
673 buffer[VK_RCONTROL] = 0;
674 buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph
675
676 // keyLayout contains the actual characters which can be written using the vk_key together with the
677 // different modifiers. '2' together with shift will for example cause the character
678 // to be @ for a US key layout (thus keyLayout[vk_key].qtKey[1] will be @). In addition to that
679 // it stores whether the resulting key is a dead key as these keys have to be handled later.
680 bool isDeadKey = false;
681 keyLayout[vk_key].deadkeys = 0;
682 keyLayout[vk_key].dirty = false;
683 keyLayout[vk_key].exists = true;
684 setKbdState(buffer, false, false, false);
685 keyLayout[vk_key].qtKey[0] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
686 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x01 : 0;
687 setKbdState(buffer, true, false, false);
688 keyLayout[vk_key].qtKey[1] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
689 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x02 : 0;
690 setKbdState(buffer, false, true, false);
691 keyLayout[vk_key].qtKey[2] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
692 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x04 : 0;
693 setKbdState(buffer, true, true, false);
694 keyLayout[vk_key].qtKey[3] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
695 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x08 : 0;
696 setKbdState(buffer, false, false, true);
697 keyLayout[vk_key].qtKey[4] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
698 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x10 : 0;
699 setKbdState(buffer, true, false, true);
700 keyLayout[vk_key].qtKey[5] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
701 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x20 : 0;
702 setKbdState(buffer, false, true, true);
703 keyLayout[vk_key].qtKey[6] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
704 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x40 : 0;
705 setKbdState(buffer, true, true, true);
706 keyLayout[vk_key].qtKey[7] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
707 keyLayout[vk_key].deadkeys |= isDeadKey ? 0x80 : 0;
708 // Add a fall back key for layouts which don't do composition and show non-latin1 characters
709 quint32 fallbackKey = winceKeyBend(vk_key);
710 if (!fallbackKey || fallbackKey == Qt::Key_unknown) {
711 fallbackKey = 0;
712 if (vk_key != keyLayout[vk_key].qtKey[0]
713 && (vk_key != keyLayout[vk_key].qtKey[1] || m_isHebrewLayout)
714 && (vk_key < 0x5B && vk_key > 0x2F))
715 fallbackKey = vk_key;
716 }
717 keyLayout[vk_key].qtKey[8] = fallbackKey;
718
719 // If one of the values inserted into the keyLayout above, can be considered a dead key, we have
720 // to run the workaround below.
721 if (keyLayout[vk_key].deadkeys) {
722 // Push a Space, then the original key through the low-level ToAscii functions.
723 // We do this because these functions (ToAscii / ToUnicode) will alter the internal state of
724 // the keyboard driver By doing the following, we set the keyboard driver state back to what
725 // it was before we wrecked it with the code above.
726 // We need to push the space with an empty keystate map, since the driver checks the map for
727 // transitions in modifiers, so this helps us capture all possible deadkeys.
728 unsigned char emptyBuffer[256];
729 memset(emptyBuffer, 0, sizeof(emptyBuffer));
730 ::ToAscii(VK_SPACE, 0, emptyBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
731 ::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
732 }
733 qCDebug(lcQpaEvents) << __FUNCTION__ << "for virtual key="
734 << Qt::hex << Qt::showbase << vk_key << Qt::dec << Qt::noshowbase << keyLayout[vk_key];
735}
736
737static inline QString messageKeyText(const MSG &msg)
738{
739 const QChar ch = QChar(ushort(msg.wParam));
740 return ch.isNull() ? QString() : QString(ch);
741}
742
743[[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd)
744{
745 const UINT dpi = GetDpiForWindow(hwnd);
746 const int captionHeight = GetSystemMetricsForDpi(SM_CYCAPTION, dpi);
747 if (IsZoomed(hwnd))
748 return captionHeight;
749 // The frame height should also be taken into account if the window
750 // is not maximized.
751 const int frameHeight = GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi)
752 + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
753 return captionHeight + frameHeight;
754}
755
756[[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags)
757{
758 static constexpr const Qt::WindowFlags titleBarHints =
759 Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint;
760 return (flags & Qt::WindowSystemMenuHint) && (flags & Qt::WindowTitleHint) && !(flags & titleBarHints)
761 && (flags & (Qt::FramelessWindowHint | Qt::CustomizeWindowHint));
762}
763
764static void showSystemMenu(QWindow* w)
765{
766 QWindow *topLevel = QWindowsWindow::topLevelOf(w);
767 HWND topLevelHwnd = QWindowsWindow::handleOf(topLevel);
768 HMENU menu = GetSystemMenu(topLevelHwnd, FALSE);
769 if (!menu)
770 return; // no menu for this window
771
772#define enabled (MF_BYCOMMAND | MFS_ENABLED)
773#define disabled (MF_BYCOMMAND | MFS_GRAYED)
774
775 EnableMenuItem(menu, SC_MINIMIZE, (topLevel->flags() & Qt::WindowMinimizeButtonHint) ? enabled : disabled);
776 const bool maximized = IsZoomed(topLevelHwnd);
777
778 EnableMenuItem(menu, SC_MAXIMIZE, !(topLevel->flags() & Qt::WindowMaximizeButtonHint) || maximized ? disabled : enabled);
779
780 // We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check
781 // this and our menu here would be out-of-sync with the menu produced by mouse-click on the
782 // System Menu, or right-click on the title bar.
783 EnableMenuItem(menu, SC_SIZE, (topLevel->flags() & Qt::MSWindowsFixedSizeDialogHint) || maximized ? disabled : enabled);
784 EnableMenuItem(menu, SC_MOVE, maximized ? disabled : enabled);
785 EnableMenuItem(menu, SC_CLOSE, enabled);
786 EnableMenuItem(menu, SC_RESTORE, maximized ? enabled : disabled);
787
788 // Highlight the first entry in the menu, this is what native Win32 applications usually do.
789 HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_HILITE);
790
791 // Set bold on close menu item
792 SetMenuDefaultItem(menu, SC_CLOSE, FALSE);
793
794#undef enabled
795#undef disabled
796
797 const QPoint pos = QHighDpi::toNativePixels(topLevel->geometry().topLeft(), topLevel);
798 const int titleBarOffset = isSystemMenuOffsetNeeded(topLevel->flags()) ? getTitleBarHeight(topLevelHwnd) : 0;
799 const int ret = TrackPopupMenuEx(menu,
800 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
801 pos.x(), pos.y() + titleBarOffset,
802 topLevelHwnd,
803 nullptr);
804
805 // Remove the highlight of the restore menu item, otherwise when the user right-clicks
806 // on the title bar, the popuped system menu will also highlight the restore item, which
807 // is not appropriate, it should only be highlighted if the menu is brought up by keyboard.
808 HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_UNHILITE);
809
810 if (ret)
811 qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, WPARAM(ret), 0);
812}
813
814static inline void sendExtendedPressRelease(QWindow *w, unsigned long timestamp, int k,
815 Qt::KeyboardModifiers mods,
816 quint32 nativeScanCode,
817 quint32 nativeVirtualKey,
818 quint32 nativeModifiers,
819 const QString & text = QString(),
820 bool autorep = false,
821 ushort count = 1)
822{
823 QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
824 QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
825}
826
827/*!
828 \brief To be called from the window procedure.
829*/
830
831bool QWindowsKeyMapper::translateKeyEvent(QWindow *widget, HWND hwnd,
832 const MSG &msg, LRESULT *result)
833{
834 *result = 0;
835
836 // Reset layout map when system keyboard layout is changed
837 if (msg.message == WM_INPUTLANGCHANGE) {
839 return true;
840 }
841
842#if defined(WM_APPCOMMAND)
843 if (msg.message == WM_APPCOMMAND)
844 return translateMultimediaKeyEventInternal(widget, msg);
845#endif
846
847 // WM_(IME_)CHAR messages already contain the character in question so there is
848 // no need to fiddle with our key map. In any other case add this key to the
849 // keymap if it is not present yet.
850 if (msg.message != WM_CHAR && msg.message != WM_IME_CHAR)
851 updateKeyMap(msg);
852
853 MSG peekedMsg;
854 // consume dead chars?(for example, typing '`','a' resulting in a-accent).
855 if (PeekMessage(&peekedMsg, hwnd, 0, 0, PM_NOREMOVE) && peekedMsg.message == WM_DEADCHAR)
856 return true;
857
858 return translateKeyEventInternal(widget, msg, false, result);
859}
860
861bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, const MSG &msg)
862{
863#if defined(WM_APPCOMMAND)
864 const int cmd = GET_APPCOMMAND_LPARAM(msg.lParam);
865 // QTBUG-57198, do not send mouse-synthesized commands as key events in addition
866 bool skipPressRelease = false;
867 switch (GET_DEVICE_LPARAM(msg.lParam)) {
868 case FAPPCOMMAND_MOUSE:
869 return false;
870 case FAPPCOMMAND_KEY:
871 // QTBUG-62838, use WM_KEYDOWN/WM_KEYUP for commands that are reflected
872 // in VK(s) like VK_MEDIA_NEXT_TRACK, to get correct codes and autorepeat.
873 // Don't do that for APPCOMMAND_BROWSER_HOME as that one does not trigger two events.
874 if (cmd != APPCOMMAND_BROWSER_HOME)
875 skipPressRelease = true;
876 break;
877 }
878
879 const int dwKeys = GET_KEYSTATE_LPARAM(msg.lParam);
880 int state = 0;
881 state |= (dwKeys & MK_SHIFT ? int(Qt::ShiftModifier) : 0);
882 state |= (dwKeys & MK_CONTROL ? int(Qt::ControlModifier) : 0);
883
884 QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
885
886 if (cmd < 0 || cmd > 52)
887 return false;
888
889 const int qtKey = int(CmdTbl[cmd]);
890 if (!skipPressRelease)
891 sendExtendedPressRelease(receiver, msg.time, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
892 // QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise,
893 // the keys are not passed to the active media player.
894# if QT_CONFIG(shortcut)
895 const QKeySequence sequence(Qt::Modifier(state) | Qt::Key(qtKey));
896 return QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence);
897# else
898 return false;
899# endif
900#else
901 Q_UNREACHABLE();
902 return false;
903#endif
904}
905
906// QTBUG-69317: Check for AltGr found on some keyboards
907// which is a sequence of left Ctrl (SYSKEY) + right Menu (Alt).
908static bool isAltGr(MSG *msg)
909{
910 enum : LONG_PTR { RightFlag = 0x1000000 };
911 if (msg->wParam != VK_CONTROL || (msg->lParam & RightFlag) != 0
912 || (msg->message != WM_KEYDOWN && msg->message != WM_SYSKEYUP)) {
913 return false;
914 }
915 const UINT expectedMessage = msg->message == WM_SYSKEYUP
916 ? WM_KEYUP : msg->message;
917 MSG peekedMsg;
918 if (PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_NOREMOVE) == FALSE
919 || peekedMsg.message != expectedMessage || peekedMsg.wParam != VK_MENU
920 || (peekedMsg.lParam & RightFlag) == 0) {
921 return false;
922 }
923 *msg = peekedMsg;
924 PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_REMOVE);
925 return true;
926}
927
928bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
929 bool /* grab */, LRESULT *lResult)
930{
931 const bool altGr = m_detectAltGrModifier && isAltGr(&msg);
932 if (altGr)
933 m_seenAltGr = true;
934 const UINT msgType = msg.message;
935
936 const quint32 scancode = getScancode(msg);
937 auto vk_key = quint32(msg.wParam);
938 quint32 nModifiers = 0;
939
940 QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
941
942 // Map native modifiers to some bit representation
943 nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0);
944 nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0);
945 nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0);
946 nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0);
947 nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0);
948 nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0);
949 nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0);
950 nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0);
951 // Add Lock keys to the same bits
952 nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0);
953 nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0);
954 nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0);
955
956 if (msg.lParam & ExtendedKey)
957 nModifiers |= msg.lParam & ExtendedKey;
958
959 // Get the modifier states (may be altered later, depending on key code)
960 int state = 0;
961 state |= (nModifiers & ShiftAny ? int(Qt::ShiftModifier) : 0);
962 state |= (nModifiers & AltLeft ? int(Qt::AltModifier) : 0);
963 if ((nModifiers & AltRight) != 0)
964 state |= m_seenAltGr ? Qt::GroupSwitchModifier : Qt::AltModifier;
965 if ((nModifiers & ControlAny) != 0 && (state & Qt::GroupSwitchModifier) == 0)
966 state |= Qt::ControlModifier;
967 state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0);
968 // A multi-character key or a Input method character
969 // not found by our look-ahead
970 if (msgType == WM_CHAR || msgType == WM_IME_CHAR) {
971 sendExtendedPressRelease(receiver, msg.time, 0, Qt::KeyboardModifier(state), scancode, 0, nModifiers, messageKeyText(msg), false);
972 return true;
973 }
974
975 // Enable Alt accelerators ("&File") on menus
976 if (msgType == WM_SYSKEYDOWN && (nModifiers & AltAny) != 0 && GetMenu(msg.hwnd) != nullptr)
977 return false;
978 if (msgType == WM_SYSKEYUP && nModifiers == 0 && GetMenu(msg.hwnd) != nullptr)
979 return false;
980
981 bool result = false;
982 // handle Directionality changes (BiDi) with RTL extensions
983 if (m_useRTLExtensions) {
984 static int dirStatus = 0;
985 if (!dirStatus && state == Qt::ControlModifier
986 && msg.wParam == VK_CONTROL
987 && msgType == WM_KEYDOWN) {
988 if (GetKeyState(VK_LCONTROL) < 0)
989 dirStatus = VK_LCONTROL;
990 else if (GetKeyState(VK_RCONTROL) < 0)
991 dirStatus = VK_RCONTROL;
992 } else if (dirStatus) {
993 if (msgType == WM_KEYDOWN) {
994 if (msg.wParam == VK_SHIFT) {
995 if (dirStatus == VK_LCONTROL && GetKeyState(VK_LSHIFT) < 0)
996 dirStatus = VK_LSHIFT;
997 else if (dirStatus == VK_RCONTROL && GetKeyState(VK_RSHIFT) < 0)
998 dirStatus = VK_RSHIFT;
999 } else {
1000 dirStatus = 0;
1001 }
1002 } else if (msgType == WM_KEYUP) {
1003 if (dirStatus == VK_LSHIFT
1004 && ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL))
1005 || (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) {
1006 sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_L, {},
1007 scancode, vk_key, nModifiers, QString(), false);
1008 result = true;
1009 dirStatus = 0;
1010 } else if (dirStatus == VK_RSHIFT
1011 && ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL))
1012 || (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) {
1013 sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_R, {},
1014 scancode, vk_key, nModifiers, QString(), false);
1015 result = true;
1016 dirStatus = 0;
1017 } else {
1018 dirStatus = 0;
1019 }
1020 } else {
1021 dirStatus = 0;
1022 }
1023 }
1024 } // RTL
1025
1026 // IME will process these keys, so simply return
1027 if (msg.wParam == VK_PROCESSKEY)
1028 return true;
1029
1030 // Ignore invalid virtual keycodes (see bugs 127424, QTBUG-3630)
1031 if (msg.wParam == 0 || msg.wParam == 0xFF)
1032 return true;
1033
1034 // Translate VK_* (native) -> Key_* (Qt) keys
1035 int modifiersIndex = 0;
1036 modifiersIndex |= (nModifiers & ShiftAny ? 0x1 : 0);
1037 modifiersIndex |= (nModifiers & ControlAny ? 0x2 : 0);
1038 modifiersIndex |= (nModifiers & AltAny ? 0x4 : 0);
1039
1040 // Note: For the resulting key, AltGr is equivalent to Alt + Ctrl (as
1041 // opposed to Linux); hence no entry in KeyboardLayoutItem is required
1042 int code = keyLayout[vk_key].qtKey[modifiersIndex];
1043
1044 // If the bit 24 of lParm is set you received a enter,
1045 // otherwise a Return. (This is the extended key bit)
1046 if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000))
1047 code = Qt::Key_Enter;
1048 else if (altGr)
1049 code = Qt::Key_AltGr;
1050
1051 // Invert state logic:
1052 // If the key actually pressed is a modifier key, then we remove its modifier key from the
1053 // state, since a modifier-key can't have itself as a modifier
1054 if (code == Qt::Key_Control)
1055 state = state ^ Qt::ControlModifier;
1056 else if (code == Qt::Key_Shift)
1057 state = state ^ Qt::ShiftModifier;
1058 else if (code == Qt::Key_Alt)
1059 state = state ^ Qt::AltModifier;
1060 else if (code == Qt::Key_AltGr)
1061 state = state ^ Qt::GroupSwitchModifier;
1062
1063 // All cursor keys without extended bit
1064 if (!(msg.lParam & 0x1000000)) {
1065 switch (code) {
1066 case Qt::Key_Left:
1067 case Qt::Key_Right:
1068 case Qt::Key_Up:
1069 case Qt::Key_Down:
1070 case Qt::Key_PageUp:
1071 case Qt::Key_PageDown:
1072 case Qt::Key_Home:
1073 case Qt::Key_End:
1074 case Qt::Key_Insert:
1075 case Qt::Key_Delete:
1076 case Qt::Key_Asterisk:
1077 case Qt::Key_Plus:
1078 case Qt::Key_Minus:
1079 case Qt::Key_Period:
1080 case Qt::Key_Comma:
1081 case Qt::Key_0:
1082 case Qt::Key_1:
1083 case Qt::Key_2:
1084 case Qt::Key_3:
1085 case Qt::Key_4:
1086 case Qt::Key_5:
1087 case Qt::Key_6:
1088 case Qt::Key_7:
1089 case Qt::Key_8:
1090 case Qt::Key_9:
1091 state |= ((msg.wParam >= '0' && msg.wParam <= '9')
1092 || (msg.wParam >= VK_OEM_PLUS && msg.wParam <= VK_OEM_3))
1093 ? 0 : int(Qt::KeypadModifier);
1094 Q_FALLTHROUGH();
1095 default:
1096 if (uint(msg.lParam) == 0x004c0001 || uint(msg.lParam) == 0xc04c0001)
1097 state |= Qt::KeypadModifier;
1098 break;
1099 }
1100 }
1101 // Other keys with with extended bit
1102 else {
1103 switch (code) {
1104 case Qt::Key_Enter:
1105 case Qt::Key_Slash:
1106 case Qt::Key_NumLock:
1107 state |= Qt::KeypadModifier;
1108 break;
1109 default:
1110 break;
1111 }
1112 }
1113
1114 // KEYDOWN ---------------------------------------------------------------------------------
1115 if (msgType == WM_KEYDOWN || msgType == WM_IME_KEYDOWN || msgType == WM_SYSKEYDOWN) {
1116 // Get the last record of this key press, so we can validate the current state
1117 // The record is not removed from the list
1118 KeyRecord *rec = key_recorder.findKey(int(msg.wParam), false);
1119
1120 // If rec's state doesn't match the current state, something has changed behind our back
1121 // (Consumed by modal widget is one possibility) So, remove the record from the list
1122 // This will stop the auto-repeat of the key, should a modifier change, for example
1123 if (rec && rec->state != state) {
1124 key_recorder.findKey(int(msg.wParam), true);
1125 rec = nullptr;
1126 }
1127
1128 // Find unicode character from Windows Message Queue
1129 MSG wm_char;
1130 UINT charType = (msgType == WM_KEYDOWN
1131 ? WM_CHAR
1132 : msgType == WM_IME_KEYDOWN ? WM_IME_CHAR : WM_SYSCHAR);
1133
1134 QChar uch;
1135 if (PeekMessage(&wm_char, nullptr, charType, charType, PM_REMOVE)) {
1136 if (QWindowsContext::filterNativeEvent(&wm_char, lResult))
1137 return true;
1138 if (receiver && QWindowsContext::filterNativeEvent(receiver, &wm_char, lResult))
1139 return true;
1140 // Found a ?_CHAR
1141 uch = QChar(ushort(wm_char.wParam));
1142 if (uch.isHighSurrogate()) {
1143 m_lastHighSurrogate = uch;
1144 return true;
1145 }
1146 if (uch.isLowSurrogate() && !m_lastHighSurrogate.isNull()) {
1147 if (QObject *focusObject = QGuiApplication::focusObject()) {
1148 const QChar chars[2] = {m_lastHighSurrogate, uch};
1149 QInputMethodEvent event;
1150 event.setCommitString(QString(chars, 2));
1151 QCoreApplication::sendEvent(focusObject, &event);
1152 }
1153 m_lastHighSurrogate = QChar();
1154 return true;
1155 } else {
1156 m_lastHighSurrogate = QChar();
1157 }
1158 if (msgType == WM_SYSKEYDOWN && uch.isLetter() && (msg.lParam & KF_ALTDOWN))
1159 uch = uch.toLower(); // (See doc of WM_SYSCHAR) Alt-letter
1160 if (!code && !uch.row())
1161 code = asciiToKeycode(char(uch.cell()), state);
1162 }
1163
1164 // Special handling for the WM_IME_KEYDOWN message. Microsoft IME (Korean) will not
1165 // generate a WM_IME_CHAR message corresponding to this message. We might get wrong
1166 // results, if we map this virtual key-code directly (for eg '?' US layouts). So try
1167 // to find the correct key using the current message parameters & keyboard state.
1168 if (uch.isNull() && msgType == WM_IME_KEYDOWN) {
1169 const auto *windowsInputContext =
1170 qobject_cast<const QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
1171 if (!(windowsInputContext && windowsInputContext->isComposing()))
1172 vk_key = ImmGetVirtualKey(reinterpret_cast<HWND>(window->winId()));
1173 BYTE keyState[256];
1174 wchar_t newKey[3] = {0};
1175 GetKeyboardState(keyState);
1176 int val = ToUnicode(vk_key, scancode, keyState, newKey, 2, 0);
1177 if (val == 1) {
1178 uch = QChar(newKey[0]);
1179 } else {
1180 // If we are still not able to find a unicode key, pass the WM_IME_KEYDOWN
1181 // message to DefWindowProc() for generating a proper WM_KEYDOWN.
1182 return false;
1183 }
1184 }
1185
1186 // If no ?_CHAR was found in the queue; deduct character from the ?_KEYDOWN parameters
1187 if (uch.isNull()) {
1188 if (msg.wParam == VK_DELETE) {
1189 uch = QChar(QLatin1Char(0x7f)); // Windows doesn't know this one.
1190 } else {
1191 if (msgType != WM_SYSKEYDOWN || !code) {
1192 UINT map = MapVirtualKey(UINT(msg.wParam), 2);
1193 // If the high bit of the return value is set, it's a deadkey
1194 if (!(map & 0x80000000))
1195 uch = QChar(ushort(map));
1196 }
1197 }
1198 if (!code && !uch.row())
1199 code = asciiToKeycode(char(uch.cell()), state);
1200 }
1201
1202 // Special handling of global Windows hotkeys
1203 if (state == Qt::AltModifier) {
1204 switch (code) {
1205 case Qt::Key_Escape:
1206 case Qt::Key_Tab:
1207 case Qt::Key_F4:
1208 return false; // Send the event on to Windows
1209 case Qt::Key_Space:
1210 // do not pass this key to windows, we will process it ourselves
1211 showSystemMenu(receiver);
1212 return true;
1213 default:
1214 break;
1215 }
1216 }
1217
1218 // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
1219 if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
1220 code = Qt::Key_Backtab;
1221
1222 // If we have a record, it means that the key is already pressed, the state is the same
1223 // so, we have an auto-repeating key
1224 if (rec) {
1225 if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) {
1226 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
1227 Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
1228 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
1229 Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
1230 result = true;
1231 }
1232 }
1233 // No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
1234 // and store the key data into our records.
1235 else {
1236 const QString text = uch.isNull() ? QString() : QString(uch);
1237 const char a = uch.row() ? char(0) : char(uch.cell());
1238 const Qt::KeyboardModifiers modifiers(state);
1239#ifndef QT_NO_SHORTCUT
1240 // Is Qt interested in the context menu key?
1241 if (modifiers == Qt::SHIFT && code == Qt::Key_F10
1242 && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(QKeySequence(Qt::SHIFT | Qt::Key_F10))) {
1243 return false;
1244 }
1245#endif // !QT_NO_SHORTCUT
1246 key_recorder.storeKey(int(msg.wParam), a, state, text);
1247
1248 // QTBUG-71210
1249 // VK_PACKET specifies multiple characters. The system only sends the first
1250 // character of this sequence for each.
1251 if (msg.wParam == VK_PACKET)
1252 code = asciiToKeycode(char(uch.cell()), state);
1253
1254 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
1255 modifiers, scancode, quint32(msg.wParam), nModifiers, text, false);
1256 result =true;
1257 bool store = true;
1258 // Alt+<alphanumerical> go to the Win32 menu system if unhandled by Qt
1259 if (msgType == WM_SYSKEYDOWN && !result && a) {
1260 HWND parent = GetParent(QWindowsWindow::handleOf(receiver));
1261 while (parent) {
1262 if (GetMenu(parent)) {
1263 SendMessage(parent, WM_SYSCOMMAND, SC_KEYMENU, a);
1264 store = false;
1265 result = true;
1266 break;
1267 }
1268 parent = GetParent(parent);
1269 }
1270 }
1271 if (!store)
1272 key_recorder.findKey(int(msg.wParam), true);
1273 }
1274 }
1275
1276 // KEYUP -----------------------------------------------------------------------------------
1277 else {
1278 // Try to locate the key in our records, and remove it if it exists.
1279 // The key may not be in our records if, for example, the down event was handled by
1280 // win32 natively, or our window gets focus while a key is already press, but now gets
1281 // the key release event.
1282 const KeyRecord *rec = key_recorder.findKey(int(msg.wParam), true);
1283 if (!rec && !(code == Qt::Key_Shift
1284 || code == Qt::Key_Control
1285 || code == Qt::Key_Meta
1286 || code == Qt::Key_Alt)) {
1287
1288 // Workaround for QTBUG-77153:
1289 // The Surface Pen eraser button generates Meta+F18/19/20 keystrokes,
1290 // but when it is not touching the screen the Fn Down is eaten and only
1291 // a Fn Up with the previous state as "not pressed" is generated, which
1292 // would be ignored. We detect this case and synthesize the expected events.
1293 if ((msg.lParam & 0x40000000) == 0 &&
1294 Qt::KeyboardModifier(state) == Qt::NoModifier &&
1295 ((code == Qt::Key_F18) || (code == Qt::Key_F19) || (code == Qt::Key_F20))) {
1296 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
1297 Qt::MetaModifier, scancode,
1298 quint32(msg.wParam), MetaLeft);
1299 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
1300 Qt::NoModifier, scancode,
1301 quint32(msg.wParam), 0);
1302 result = true;
1303 }
1304 } else {
1305 if (!code)
1306 code = asciiToKeycode(rec->ascii ? char(rec->ascii) : char(msg.wParam), state);
1307
1308 // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
1309 if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
1310 code = Qt::Key_Backtab;
1311 QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
1312 Qt::KeyboardModifier(state), scancode, quint32(msg.wParam),
1313 nModifiers,
1314 (rec ? rec->text : QString()), false);
1315 result = true;
1316 // don't pass Alt to Windows unless we are embedded in a non-Qt window
1317 if (code == Qt::Key_Alt) {
1319 HWND parent = GetParent(QWindowsWindow::handleOf(receiver));
1320 while (parent) {
1321 if (!context->findPlatformWindow(parent) && GetMenu(parent)) {
1322 result = false;
1323 break;
1324 }
1325 parent = GetParent(parent);
1326 }
1327 }
1328 }
1329 }
1330 return result;
1331}
1332
1334{
1335 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
1336 if (GetKeyState(VK_SHIFT) < 0)
1337 modifiers |= Qt::ShiftModifier;
1338 if (GetKeyState(VK_CONTROL) < 0)
1339 modifiers |= Qt::ControlModifier;
1340 if (GetKeyState(VK_MENU) < 0)
1341 modifiers |= Qt::AltModifier;
1342 if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0)
1343 modifiers |= Qt::MetaModifier;
1344 return modifiers;
1345}
1346
1348{
1349 QList<QKeyCombination> result;
1350
1351
1352 const quint32 nativeVirtualKey = e->nativeVirtualKey();
1353 if (nativeVirtualKey > 255)
1354 return result;
1355
1356 const KeyboardLayoutItem &kbItem = keyLayout[nativeVirtualKey];
1357 if (!kbItem.exists)
1358 return result;
1359
1360 quint32 baseKey = kbItem.qtKey[0];
1361 Qt::KeyboardModifiers keyMods = e->modifiers();
1362 if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) {
1363 result << (Qt::Key_Enter | keyMods);
1364 return result;
1365 }
1366
1367 // If Key_Tab+Shift is pressed we add Key_Backtab without
1368 // shift modifier as a possible combination too
1369 if (baseKey == Qt::Key_Tab && (keyMods & Qt::ShiftModifier))
1370 result << (Qt::Key_Backtab | (keyMods & ~Qt::ShiftModifier));
1371
1372 // The base key is _always_ valid, of course
1373 result << QKeyCombination::fromCombined(int(baseKey) + int(keyMods));
1374
1375 for (size_t i = 1; i < NumMods; ++i) {
1376 Qt::KeyboardModifiers neededMods = ModsTbl[i];
1377 quint32 key = kbItem.qtKey[i];
1378 if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) {
1379 const Qt::KeyboardModifiers missingMods = keyMods & ~neededMods;
1380 const auto matchedKey = QKeyCombination::fromCombined(int(key) + int(missingMods));
1381 const auto it = std::find_if(result.begin(), result.end(),
1382 [key](auto keyCombination) {
1383 return keyCombination.key() == key;
1384 });
1385 // QTBUG-67200: Use the match with the least modifiers (prefer
1386 // Shift+9 over Alt + Shift + 9) resulting in more missing modifiers.
1387 if (it == result.end())
1388 result << matchedKey;
1389 else if (missingMods > it->keyboardModifiers())
1390 *it = matchedKey;
1391 }
1392 }
1393 qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey="
1394 << Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase
1395 << e->modifiers() << kbItem << "\n returns" << result;
1396 return result;
1397}
1398
1399QT_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]