2022-05-10 10:06:48 +00:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
#include "qwindowskeymapper.h"
|
|
|
|
#include "qwindowscontext.h"
|
2015-09-22 08:57:51 +00:00
|
|
|
#include "qwindowsintegration.h"
|
2011-08-17 13:58:39 +00:00
|
|
|
#include "qwindowswindow.h"
|
2015-03-10 21:47:52 +00:00
|
|
|
#include "qwindowsinputcontext.h"
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2018-05-22 07:31:13 +00:00
|
|
|
#include <QtGui/qguiapplication.h>
|
|
|
|
#include <QtGui/qwindow.h>
|
2012-07-26 11:23:28 +00:00
|
|
|
#include <qpa/qwindowsysteminterface.h>
|
2014-11-03 10:44:42 +00:00
|
|
|
#include <private/qguiapplication_p.h>
|
2015-06-29 08:25:24 +00:00
|
|
|
#include <private/qhighdpiscaling_p.h>
|
2018-05-22 07:31:13 +00:00
|
|
|
#include <QtGui/qevent.h>
|
2020-05-28 05:12:25 +00:00
|
|
|
#include <QtGui/private/qwindowsguieventdispatcher_p.h>
|
2018-03-27 10:54:30 +00:00
|
|
|
#include <QtCore/private/qdebug_p.h>
|
2022-12-20 09:07:00 +00:00
|
|
|
#include <QtCore/private/qtools_p.h>
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2015-01-23 12:10:26 +00:00
|
|
|
#if defined(WM_APPCOMMAND)
|
|
|
|
# ifndef FAPPCOMMAND_MOUSE
|
|
|
|
# define FAPPCOMMAND_MOUSE 0x8000
|
|
|
|
# endif
|
|
|
|
# ifndef FAPPCOMMAND_KEY
|
|
|
|
# define FAPPCOMMAND_KEY 0
|
|
|
|
# endif
|
|
|
|
# ifndef FAPPCOMMAND_OEM
|
|
|
|
# define FAPPCOMMAND_OEM 0x1000
|
|
|
|
# endif
|
|
|
|
# ifndef FAPPCOMMAND_MASK
|
|
|
|
# define FAPPCOMMAND_MASK 0xF000
|
|
|
|
# endif
|
|
|
|
# ifndef GET_APPCOMMAND_LPARAM
|
|
|
|
# define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
|
|
|
|
# endif
|
|
|
|
# ifndef GET_DEVICE_LPARAM
|
|
|
|
# define GET_DEVICE_LPARAM(lParam) ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK))
|
|
|
|
# endif
|
|
|
|
# ifndef GET_MOUSEORKEY_LPARAM
|
|
|
|
# define GET_MOUSEORKEY_LPARAM GET_DEVICE_LPARAM
|
|
|
|
# endif
|
|
|
|
# ifndef GET_FLAGS_LPARAM
|
|
|
|
# define GET_FLAGS_LPARAM(lParam) (LOWORD(lParam))
|
|
|
|
# endif
|
|
|
|
# ifndef GET_KEYSTATE_LPARAM
|
|
|
|
# define GET_KEYSTATE_LPARAM(lParam) GET_FLAGS_LPARAM(lParam)
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\class QWindowsKeyMapper
|
|
|
|
\brief Translates Windows keys to QWindowSystemInterface events.
|
2012-05-29 10:46:26 +00:00
|
|
|
\internal
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
In addition, handles some special keys to display system menus, etc.
|
|
|
|
The code originates from \c qkeymapper_win.cpp.
|
|
|
|
*/
|
|
|
|
|
2015-12-14 11:02:29 +00:00
|
|
|
static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state);
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
QWindowsKeyMapper::QWindowsKeyMapper()
|
2019-01-24 07:54:38 +00:00
|
|
|
: m_useRTLExtensions(false), m_keyGrabber(nullptr)
|
2011-08-17 13:58:39 +00:00
|
|
|
{
|
|
|
|
memset(keyLayout, 0, sizeof(keyLayout));
|
2019-05-24 14:08:18 +00:00
|
|
|
auto *app = static_cast<QGuiApplication *>(QGuiApplication::instance());
|
2015-12-14 11:02:29 +00:00
|
|
|
QObject::connect(app, &QGuiApplication::applicationStateChanged,
|
|
|
|
app, clearKeyRecorderOnApplicationInActive);
|
2018-04-25 06:46:28 +00:00
|
|
|
changeKeyboard();
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-06 08:45:53 +00:00
|
|
|
QWindowsKeyMapper::~QWindowsKeyMapper()= default;
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
#ifndef LANG_PASHTO
|
|
|
|
#define LANG_PASHTO 0x63
|
|
|
|
#endif
|
|
|
|
#ifndef LANG_SYRIAC
|
|
|
|
#define LANG_SYRIAC 0x5a
|
|
|
|
#endif
|
|
|
|
#ifndef LANG_DIVEHI
|
|
|
|
#define LANG_DIVEHI 0x65
|
|
|
|
#endif
|
|
|
|
#ifndef VK_OEM_PLUS
|
|
|
|
#define VK_OEM_PLUS 0xBB
|
|
|
|
#endif
|
|
|
|
#ifndef VK_OEM_3
|
|
|
|
#define VK_OEM_3 0xC0
|
|
|
|
#endif
|
|
|
|
|
2023-07-04 14:50:19 +00:00
|
|
|
// Get scancode from the given message
|
|
|
|
static constexpr quint32 getScancode(const MSG &msg)
|
|
|
|
{
|
|
|
|
const auto keyFlags = HIWORD(msg.lParam);
|
|
|
|
quint32 scancode = LOBYTE(keyFlags);
|
|
|
|
// if extended-key flag is on, the scan code consists of a sequence of two bytes,
|
|
|
|
// where the first byte has a value of 0xe0.
|
|
|
|
if ((keyFlags & KF_EXTENDED) != 0)
|
|
|
|
scancode |= 0xE000;
|
|
|
|
return scancode;
|
|
|
|
}
|
2013-11-19 11:47:03 +00:00
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
// Key recorder ------------------------------------------------------------------------[ start ] --
|
|
|
|
struct KeyRecord {
|
|
|
|
KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {}
|
|
|
|
KeyRecord() {}
|
|
|
|
|
|
|
|
int code;
|
|
|
|
int ascii;
|
|
|
|
int state;
|
|
|
|
QString text;
|
|
|
|
};
|
|
|
|
|
2013-10-25 13:43:42 +00:00
|
|
|
// We need to record the pressed keys in order to decide, whether the key event is an autorepeat
|
|
|
|
// event. As soon as its state changes, the chain of autorepeat events will be broken.
|
2011-08-17 13:58:39 +00:00
|
|
|
static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers...
|
|
|
|
struct KeyRecorder
|
|
|
|
{
|
|
|
|
inline KeyRecord *findKey(int code, bool remove);
|
|
|
|
inline void storeKey(int code, int ascii, int state, const QString& text);
|
|
|
|
inline void clearKeys();
|
|
|
|
|
2016-11-24 14:48:34 +00:00
|
|
|
int nrecs = 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
KeyRecord deleted_record; // A copy of last entry removed from records[]
|
|
|
|
KeyRecord records[QT_MAX_KEY_RECORDINGS];
|
|
|
|
};
|
|
|
|
static KeyRecorder key_recorder;
|
|
|
|
|
2015-12-14 11:02:29 +00:00
|
|
|
static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state)
|
|
|
|
{
|
|
|
|
if (state == Qt::ApplicationInactive)
|
|
|
|
key_recorder.clearKeys();
|
|
|
|
}
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
KeyRecord *KeyRecorder::findKey(int code, bool remove)
|
|
|
|
{
|
2019-01-24 07:54:38 +00:00
|
|
|
KeyRecord *result = nullptr;
|
2011-08-17 13:58:39 +00:00
|
|
|
for (int i = 0; i < nrecs; ++i) {
|
|
|
|
if (records[i].code == code) {
|
|
|
|
if (remove) {
|
|
|
|
deleted_record = records[i];
|
|
|
|
// Move rest down, and decrease count
|
|
|
|
while (i + 1 < nrecs) {
|
|
|
|
records[i] = records[i + 1];
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
--nrecs;
|
|
|
|
result = &deleted_record;
|
|
|
|
} else {
|
|
|
|
result = &records[i];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KeyRecorder::storeKey(int code, int ascii, int state, const QString& text)
|
|
|
|
{
|
|
|
|
Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS,
|
|
|
|
"Internal KeyRecorder",
|
|
|
|
"Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS");
|
|
|
|
|
|
|
|
if (nrecs == QT_MAX_KEY_RECORDINGS) {
|
|
|
|
qWarning("Qt: Internal keyboard buffer overflow");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
records[nrecs++] = KeyRecord(code,ascii,state,text);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KeyRecorder::clearKeys()
|
|
|
|
{
|
|
|
|
nrecs = 0;
|
|
|
|
}
|
|
|
|
// Key recorder --------------------------------------------------------------------------[ end ] --
|
|
|
|
|
|
|
|
|
|
|
|
// Key translation ---------------------------------------------------------------------[ start ] --
|
|
|
|
// Meaning of values:
|
|
|
|
// 0 = Character output key, needs keyboard driver mapping
|
|
|
|
// Key_unknown = Unknown Virtual Key, no translation possible, ignore
|
|
|
|
static const uint KeyTbl[] = { // Keyboard mapping table
|
|
|
|
// Dec | Hex | Windows Virtual key
|
|
|
|
Qt::Key_unknown, // 0 0x00
|
|
|
|
Qt::Key_unknown, // 1 0x01 VK_LBUTTON | Left mouse button
|
|
|
|
Qt::Key_unknown, // 2 0x02 VK_RBUTTON | Right mouse button
|
|
|
|
Qt::Key_Cancel, // 3 0x03 VK_CANCEL | Control-Break processing
|
|
|
|
Qt::Key_unknown, // 4 0x04 VK_MBUTTON | Middle mouse button
|
|
|
|
Qt::Key_unknown, // 5 0x05 VK_XBUTTON1 | X1 mouse button
|
|
|
|
Qt::Key_unknown, // 6 0x06 VK_XBUTTON2 | X2 mouse button
|
|
|
|
Qt::Key_unknown, // 7 0x07 -- unassigned --
|
|
|
|
Qt::Key_Backspace, // 8 0x08 VK_BACK | BackSpace key
|
|
|
|
Qt::Key_Tab, // 9 0x09 VK_TAB | Tab key
|
|
|
|
Qt::Key_unknown, // 10 0x0A -- reserved --
|
|
|
|
Qt::Key_unknown, // 11 0x0B -- reserved --
|
|
|
|
Qt::Key_Clear, // 12 0x0C VK_CLEAR | Clear key
|
|
|
|
Qt::Key_Return, // 13 0x0D VK_RETURN | Enter key
|
|
|
|
Qt::Key_unknown, // 14 0x0E -- unassigned --
|
|
|
|
Qt::Key_unknown, // 15 0x0F -- unassigned --
|
|
|
|
Qt::Key_Shift, // 16 0x10 VK_SHIFT | Shift key
|
|
|
|
Qt::Key_Control, // 17 0x11 VK_CONTROL | Ctrl key
|
|
|
|
Qt::Key_Alt, // 18 0x12 VK_MENU | Alt key
|
|
|
|
Qt::Key_Pause, // 19 0x13 VK_PAUSE | Pause key
|
|
|
|
Qt::Key_CapsLock, // 20 0x14 VK_CAPITAL | Caps-Lock
|
|
|
|
Qt::Key_unknown, // 21 0x15 VK_KANA / VK_HANGUL | IME Kana or Hangul mode
|
|
|
|
Qt::Key_unknown, // 22 0x16 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 23 0x17 VK_JUNJA | IME Junja mode
|
|
|
|
Qt::Key_unknown, // 24 0x18 VK_FINAL | IME final mode
|
|
|
|
Qt::Key_unknown, // 25 0x19 VK_HANJA / VK_KANJI | IME Hanja or Kanji mode
|
|
|
|
Qt::Key_unknown, // 26 0x1A -- unassigned --
|
|
|
|
Qt::Key_Escape, // 27 0x1B VK_ESCAPE | Esc key
|
|
|
|
Qt::Key_unknown, // 28 0x1C VK_CONVERT | IME convert
|
|
|
|
Qt::Key_unknown, // 29 0x1D VK_NONCONVERT | IME non-convert
|
|
|
|
Qt::Key_unknown, // 30 0x1E VK_ACCEPT | IME accept
|
|
|
|
Qt::Key_Mode_switch,// 31 0x1F VK_MODECHANGE | IME mode change request
|
|
|
|
Qt::Key_Space, // 32 0x20 VK_SPACE | Spacebar
|
|
|
|
Qt::Key_PageUp, // 33 0x21 VK_PRIOR | Page Up key
|
|
|
|
Qt::Key_PageDown, // 34 0x22 VK_NEXT | Page Down key
|
|
|
|
Qt::Key_End, // 35 0x23 VK_END | End key
|
|
|
|
Qt::Key_Home, // 36 0x24 VK_HOME | Home key
|
|
|
|
Qt::Key_Left, // 37 0x25 VK_LEFT | Left arrow key
|
|
|
|
Qt::Key_Up, // 38 0x26 VK_UP | Up arrow key
|
|
|
|
Qt::Key_Right, // 39 0x27 VK_RIGHT | Right arrow key
|
|
|
|
Qt::Key_Down, // 40 0x28 VK_DOWN | Down arrow key
|
|
|
|
Qt::Key_Select, // 41 0x29 VK_SELECT | Select key
|
|
|
|
Qt::Key_Printer, // 42 0x2A VK_PRINT | Print key
|
|
|
|
Qt::Key_Execute, // 43 0x2B VK_EXECUTE | Execute key
|
|
|
|
Qt::Key_Print, // 44 0x2C VK_SNAPSHOT | Print Screen key
|
|
|
|
Qt::Key_Insert, // 45 0x2D VK_INSERT | Ins key
|
|
|
|
Qt::Key_Delete, // 46 0x2E VK_DELETE | Del key
|
|
|
|
Qt::Key_Help, // 47 0x2F VK_HELP | Help key
|
|
|
|
0, // 48 0x30 (VK_0) | 0 key
|
|
|
|
0, // 49 0x31 (VK_1) | 1 key
|
|
|
|
0, // 50 0x32 (VK_2) | 2 key
|
|
|
|
0, // 51 0x33 (VK_3) | 3 key
|
|
|
|
0, // 52 0x34 (VK_4) | 4 key
|
|
|
|
0, // 53 0x35 (VK_5) | 5 key
|
|
|
|
0, // 54 0x36 (VK_6) | 6 key
|
|
|
|
0, // 55 0x37 (VK_7) | 7 key
|
|
|
|
0, // 56 0x38 (VK_8) | 8 key
|
|
|
|
0, // 57 0x39 (VK_9) | 9 key
|
|
|
|
Qt::Key_unknown, // 58 0x3A -- unassigned --
|
|
|
|
Qt::Key_unknown, // 59 0x3B -- unassigned --
|
|
|
|
Qt::Key_unknown, // 60 0x3C -- unassigned --
|
|
|
|
Qt::Key_unknown, // 61 0x3D -- unassigned --
|
|
|
|
Qt::Key_unknown, // 62 0x3E -- unassigned --
|
|
|
|
Qt::Key_unknown, // 63 0x3F -- unassigned --
|
|
|
|
Qt::Key_unknown, // 64 0x40 -- unassigned --
|
|
|
|
0, // 65 0x41 (VK_A) | A key
|
|
|
|
0, // 66 0x42 (VK_B) | B key
|
|
|
|
0, // 67 0x43 (VK_C) | C key
|
|
|
|
0, // 68 0x44 (VK_D) | D key
|
|
|
|
0, // 69 0x45 (VK_E) | E key
|
|
|
|
0, // 70 0x46 (VK_F) | F key
|
|
|
|
0, // 71 0x47 (VK_G) | G key
|
|
|
|
0, // 72 0x48 (VK_H) | H key
|
|
|
|
0, // 73 0x49 (VK_I) | I key
|
|
|
|
0, // 74 0x4A (VK_J) | J key
|
|
|
|
0, // 75 0x4B (VK_K) | K key
|
|
|
|
0, // 76 0x4C (VK_L) | L key
|
|
|
|
0, // 77 0x4D (VK_M) | M key
|
|
|
|
0, // 78 0x4E (VK_N) | N key
|
|
|
|
0, // 79 0x4F (VK_O) | O key
|
|
|
|
0, // 80 0x50 (VK_P) | P key
|
|
|
|
0, // 81 0x51 (VK_Q) | Q key
|
|
|
|
0, // 82 0x52 (VK_R) | R key
|
|
|
|
0, // 83 0x53 (VK_S) | S key
|
|
|
|
0, // 84 0x54 (VK_T) | T key
|
|
|
|
0, // 85 0x55 (VK_U) | U key
|
|
|
|
0, // 86 0x56 (VK_V) | V key
|
|
|
|
0, // 87 0x57 (VK_W) | W key
|
|
|
|
0, // 88 0x58 (VK_X) | X key
|
|
|
|
0, // 89 0x59 (VK_Y) | Y key
|
|
|
|
0, // 90 0x5A (VK_Z) | Z key
|
|
|
|
Qt::Key_Meta, // 91 0x5B VK_LWIN | Left Windows - MS Natural kbd
|
|
|
|
Qt::Key_Meta, // 92 0x5C VK_RWIN | Right Windows - MS Natural kbd
|
|
|
|
Qt::Key_Menu, // 93 0x5D VK_APPS | Application key-MS Natural kbd
|
|
|
|
Qt::Key_unknown, // 94 0x5E -- reserved --
|
|
|
|
Qt::Key_Sleep, // 95 0x5F VK_SLEEP
|
|
|
|
Qt::Key_0, // 96 0x60 VK_NUMPAD0 | Numeric keypad 0 key
|
|
|
|
Qt::Key_1, // 97 0x61 VK_NUMPAD1 | Numeric keypad 1 key
|
|
|
|
Qt::Key_2, // 98 0x62 VK_NUMPAD2 | Numeric keypad 2 key
|
|
|
|
Qt::Key_3, // 99 0x63 VK_NUMPAD3 | Numeric keypad 3 key
|
|
|
|
Qt::Key_4, // 100 0x64 VK_NUMPAD4 | Numeric keypad 4 key
|
|
|
|
Qt::Key_5, // 101 0x65 VK_NUMPAD5 | Numeric keypad 5 key
|
|
|
|
Qt::Key_6, // 102 0x66 VK_NUMPAD6 | Numeric keypad 6 key
|
|
|
|
Qt::Key_7, // 103 0x67 VK_NUMPAD7 | Numeric keypad 7 key
|
|
|
|
Qt::Key_8, // 104 0x68 VK_NUMPAD8 | Numeric keypad 8 key
|
|
|
|
Qt::Key_9, // 105 0x69 VK_NUMPAD9 | Numeric keypad 9 key
|
|
|
|
Qt::Key_Asterisk, // 106 0x6A VK_MULTIPLY | Multiply key
|
|
|
|
Qt::Key_Plus, // 107 0x6B VK_ADD | Add key
|
2018-04-25 06:38:01 +00:00
|
|
|
Qt::Key_unknown, // 108 0x6C VK_SEPARATOR | Separator key (locale-dependent)
|
2011-08-17 13:58:39 +00:00
|
|
|
Qt::Key_Minus, // 109 0x6D VK_SUBTRACT | Subtract key
|
2018-04-25 06:38:01 +00:00
|
|
|
Qt::Key_unknown, // 110 0x6E VK_DECIMAL | Decimal key (locale-dependent)
|
2011-08-17 13:58:39 +00:00
|
|
|
Qt::Key_Slash, // 111 0x6F VK_DIVIDE | Divide key
|
|
|
|
Qt::Key_F1, // 112 0x70 VK_F1 | F1 key
|
|
|
|
Qt::Key_F2, // 113 0x71 VK_F2 | F2 key
|
|
|
|
Qt::Key_F3, // 114 0x72 VK_F3 | F3 key
|
|
|
|
Qt::Key_F4, // 115 0x73 VK_F4 | F4 key
|
|
|
|
Qt::Key_F5, // 116 0x74 VK_F5 | F5 key
|
|
|
|
Qt::Key_F6, // 117 0x75 VK_F6 | F6 key
|
|
|
|
Qt::Key_F7, // 118 0x76 VK_F7 | F7 key
|
|
|
|
Qt::Key_F8, // 119 0x77 VK_F8 | F8 key
|
|
|
|
Qt::Key_F9, // 120 0x78 VK_F9 | F9 key
|
|
|
|
Qt::Key_F10, // 121 0x79 VK_F10 | F10 key
|
|
|
|
Qt::Key_F11, // 122 0x7A VK_F11 | F11 key
|
|
|
|
Qt::Key_F12, // 123 0x7B VK_F12 | F12 key
|
|
|
|
Qt::Key_F13, // 124 0x7C VK_F13 | F13 key
|
|
|
|
Qt::Key_F14, // 125 0x7D VK_F14 | F14 key
|
|
|
|
Qt::Key_F15, // 126 0x7E VK_F15 | F15 key
|
|
|
|
Qt::Key_F16, // 127 0x7F VK_F16 | F16 key
|
|
|
|
Qt::Key_F17, // 128 0x80 VK_F17 | F17 key
|
|
|
|
Qt::Key_F18, // 129 0x81 VK_F18 | F18 key
|
|
|
|
Qt::Key_F19, // 130 0x82 VK_F19 | F19 key
|
|
|
|
Qt::Key_F20, // 131 0x83 VK_F20 | F20 key
|
|
|
|
Qt::Key_F21, // 132 0x84 VK_F21 | F21 key
|
|
|
|
Qt::Key_F22, // 133 0x85 VK_F22 | F22 key
|
|
|
|
Qt::Key_F23, // 134 0x86 VK_F23 | F23 key
|
|
|
|
Qt::Key_F24, // 135 0x87 VK_F24 | F24 key
|
|
|
|
Qt::Key_unknown, // 136 0x88 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 137 0x89 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 138 0x8A -- unassigned --
|
|
|
|
Qt::Key_unknown, // 139 0x8B -- unassigned --
|
|
|
|
Qt::Key_unknown, // 140 0x8C -- unassigned --
|
|
|
|
Qt::Key_unknown, // 141 0x8D -- unassigned --
|
|
|
|
Qt::Key_unknown, // 142 0x8E -- unassigned --
|
|
|
|
Qt::Key_unknown, // 143 0x8F -- unassigned --
|
|
|
|
Qt::Key_NumLock, // 144 0x90 VK_NUMLOCK | Num Lock key
|
|
|
|
Qt::Key_ScrollLock, // 145 0x91 VK_SCROLL | Scroll Lock key
|
|
|
|
// Fujitsu/OASYS kbd --------------------
|
|
|
|
0, //Qt::Key_Jisho, // 146 0x92 VK_OEM_FJ_JISHO | 'Dictionary' key /
|
|
|
|
// VK_OEM_NEC_EQUAL = key on numpad on NEC PC-9800 kbd
|
|
|
|
Qt::Key_Massyo, // 147 0x93 VK_OEM_FJ_MASSHOU | 'Unregister word' key
|
|
|
|
Qt::Key_Touroku, // 148 0x94 VK_OEM_FJ_TOUROKU | 'Register word' key
|
|
|
|
0, //Qt::Key_Oyayubi_Left,//149 0x95 VK_OEM_FJ_LOYA | 'Left OYAYUBI' key
|
|
|
|
0, //Qt::Key_Oyayubi_Right,//150 0x96 VK_OEM_FJ_ROYA | 'Right OYAYUBI' key
|
|
|
|
Qt::Key_unknown, // 151 0x97 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 152 0x98 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 153 0x99 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 154 0x9A -- unassigned --
|
|
|
|
Qt::Key_unknown, // 155 0x9B -- unassigned --
|
|
|
|
Qt::Key_unknown, // 156 0x9C -- unassigned --
|
|
|
|
Qt::Key_unknown, // 157 0x9D -- unassigned --
|
|
|
|
Qt::Key_unknown, // 158 0x9E -- unassigned --
|
|
|
|
Qt::Key_unknown, // 159 0x9F -- unassigned --
|
|
|
|
Qt::Key_Shift, // 160 0xA0 VK_LSHIFT | Left Shift key
|
|
|
|
Qt::Key_Shift, // 161 0xA1 VK_RSHIFT | Right Shift key
|
|
|
|
Qt::Key_Control, // 162 0xA2 VK_LCONTROL | Left Ctrl key
|
|
|
|
Qt::Key_Control, // 163 0xA3 VK_RCONTROL | Right Ctrl key
|
|
|
|
Qt::Key_Alt, // 164 0xA4 VK_LMENU | Left Menu key
|
|
|
|
Qt::Key_Alt, // 165 0xA5 VK_RMENU | Right Menu key
|
|
|
|
Qt::Key_Back, // 166 0xA6 VK_BROWSER_BACK | Browser Back key
|
|
|
|
Qt::Key_Forward, // 167 0xA7 VK_BROWSER_FORWARD | Browser Forward key
|
|
|
|
Qt::Key_Refresh, // 168 0xA8 VK_BROWSER_REFRESH | Browser Refresh key
|
|
|
|
Qt::Key_Stop, // 169 0xA9 VK_BROWSER_STOP | Browser Stop key
|
|
|
|
Qt::Key_Search, // 170 0xAA VK_BROWSER_SEARCH | Browser Search key
|
|
|
|
Qt::Key_Favorites, // 171 0xAB VK_BROWSER_FAVORITES| Browser Favorites key
|
|
|
|
Qt::Key_HomePage, // 172 0xAC VK_BROWSER_HOME | Browser Start and Home key
|
|
|
|
Qt::Key_VolumeMute, // 173 0xAD VK_VOLUME_MUTE | Volume Mute key
|
|
|
|
Qt::Key_VolumeDown, // 174 0xAE VK_VOLUME_DOWN | Volume Down key
|
|
|
|
Qt::Key_VolumeUp, // 175 0xAF VK_VOLUME_UP | Volume Up key
|
|
|
|
Qt::Key_MediaNext, // 176 0xB0 VK_MEDIA_NEXT_TRACK | Next Track key
|
|
|
|
Qt::Key_MediaPrevious, //177 0xB1 VK_MEDIA_PREV_TRACK | Previous Track key
|
|
|
|
Qt::Key_MediaStop, // 178 0xB2 VK_MEDIA_STOP | Stop Media key
|
2021-01-08 10:54:53 +00:00
|
|
|
Qt::Key_MediaTogglePlayPause,
|
|
|
|
// 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key
|
2011-08-17 13:58:39 +00:00
|
|
|
Qt::Key_LaunchMail, // 180 0xB4 VK_LAUNCH_MAIL | Start Mail key
|
|
|
|
Qt::Key_LaunchMedia,// 181 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
|
|
|
|
Qt::Key_Launch0, // 182 0xB6 VK_LAUNCH_APP1 | Start Application 1 key
|
|
|
|
Qt::Key_Launch1, // 183 0xB7 VK_LAUNCH_APP2 | Start Application 2 key
|
|
|
|
Qt::Key_unknown, // 184 0xB8 -- reserved --
|
|
|
|
Qt::Key_unknown, // 185 0xB9 -- reserved --
|
|
|
|
0, // 186 0xBA VK_OEM_1 | ';:' for US
|
|
|
|
0, // 187 0xBB VK_OEM_PLUS | '+' any country
|
|
|
|
0, // 188 0xBC VK_OEM_COMMA | ',' any country
|
|
|
|
0, // 189 0xBD VK_OEM_MINUS | '-' any country
|
|
|
|
0, // 190 0xBE VK_OEM_PERIOD | '.' any country
|
|
|
|
0, // 191 0xBF VK_OEM_2 | '/?' for US
|
|
|
|
0, // 192 0xC0 VK_OEM_3 | '`~' for US
|
|
|
|
Qt::Key_unknown, // 193 0xC1 -- reserved --
|
|
|
|
Qt::Key_unknown, // 194 0xC2 -- reserved --
|
|
|
|
Qt::Key_unknown, // 195 0xC3 -- reserved --
|
|
|
|
Qt::Key_unknown, // 196 0xC4 -- reserved --
|
|
|
|
Qt::Key_unknown, // 197 0xC5 -- reserved --
|
|
|
|
Qt::Key_unknown, // 198 0xC6 -- reserved --
|
|
|
|
Qt::Key_unknown, // 199 0xC7 -- reserved --
|
|
|
|
Qt::Key_unknown, // 200 0xC8 -- reserved --
|
|
|
|
Qt::Key_unknown, // 201 0xC9 -- reserved --
|
|
|
|
Qt::Key_unknown, // 202 0xCA -- reserved --
|
|
|
|
Qt::Key_unknown, // 203 0xCB -- reserved --
|
|
|
|
Qt::Key_unknown, // 204 0xCC -- reserved --
|
|
|
|
Qt::Key_unknown, // 205 0xCD -- reserved --
|
|
|
|
Qt::Key_unknown, // 206 0xCE -- reserved --
|
|
|
|
Qt::Key_unknown, // 207 0xCF -- reserved --
|
|
|
|
Qt::Key_unknown, // 208 0xD0 -- reserved --
|
|
|
|
Qt::Key_unknown, // 209 0xD1 -- reserved --
|
|
|
|
Qt::Key_unknown, // 210 0xD2 -- reserved --
|
|
|
|
Qt::Key_unknown, // 211 0xD3 -- reserved --
|
|
|
|
Qt::Key_unknown, // 212 0xD4 -- reserved --
|
|
|
|
Qt::Key_unknown, // 213 0xD5 -- reserved --
|
|
|
|
Qt::Key_unknown, // 214 0xD6 -- reserved --
|
|
|
|
Qt::Key_unknown, // 215 0xD7 -- reserved --
|
|
|
|
Qt::Key_unknown, // 216 0xD8 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 217 0xD9 -- unassigned --
|
|
|
|
Qt::Key_unknown, // 218 0xDA -- unassigned --
|
|
|
|
0, // 219 0xDB VK_OEM_4 | '[{' for US
|
|
|
|
0, // 220 0xDC VK_OEM_5 | '\|' for US
|
|
|
|
0, // 221 0xDD VK_OEM_6 | ']}' for US
|
|
|
|
0, // 222 0xDE VK_OEM_7 | ''"' for US
|
|
|
|
0, // 223 0xDF VK_OEM_8
|
|
|
|
Qt::Key_unknown, // 224 0xE0 -- reserved --
|
|
|
|
Qt::Key_unknown, // 225 0xE1 VK_OEM_AX | 'AX' key on Japanese AX kbd
|
|
|
|
Qt::Key_unknown, // 226 0xE2 VK_OEM_102 | "<>" or "\|" on RT 102-key kbd
|
|
|
|
Qt::Key_unknown, // 227 0xE3 VK_ICO_HELP | Help key on ICO
|
|
|
|
Qt::Key_unknown, // 228 0xE4 VK_ICO_00 | 00 key on ICO
|
|
|
|
Qt::Key_unknown, // 229 0xE5 VK_PROCESSKEY | IME Process key
|
|
|
|
Qt::Key_unknown, // 230 0xE6 VK_ICO_CLEAR |
|
|
|
|
Qt::Key_unknown, // 231 0xE7 VK_PACKET | Unicode char as keystrokes
|
|
|
|
Qt::Key_unknown, // 232 0xE8 -- unassigned --
|
|
|
|
// Nokia/Ericsson definitions ---------------
|
|
|
|
Qt::Key_unknown, // 233 0xE9 VK_OEM_RESET
|
|
|
|
Qt::Key_unknown, // 234 0xEA VK_OEM_JUMP
|
|
|
|
Qt::Key_unknown, // 235 0xEB VK_OEM_PA1
|
|
|
|
Qt::Key_unknown, // 236 0xEC VK_OEM_PA2
|
|
|
|
Qt::Key_unknown, // 237 0xED VK_OEM_PA3
|
|
|
|
Qt::Key_unknown, // 238 0xEE VK_OEM_WSCTRL
|
|
|
|
Qt::Key_unknown, // 239 0xEF VK_OEM_CUSEL
|
|
|
|
Qt::Key_unknown, // 240 0xF0 VK_OEM_ATTN
|
|
|
|
Qt::Key_unknown, // 241 0xF1 VK_OEM_FINISH
|
|
|
|
Qt::Key_unknown, // 242 0xF2 VK_OEM_COPY
|
|
|
|
Qt::Key_unknown, // 243 0xF3 VK_OEM_AUTO
|
|
|
|
Qt::Key_unknown, // 244 0xF4 VK_OEM_ENLW
|
|
|
|
Qt::Key_unknown, // 245 0xF5 VK_OEM_BACKTAB
|
|
|
|
Qt::Key_unknown, // 246 0xF6 VK_ATTN | Attn key
|
|
|
|
Qt::Key_unknown, // 247 0xF7 VK_CRSEL | CrSel key
|
|
|
|
Qt::Key_unknown, // 248 0xF8 VK_EXSEL | ExSel key
|
|
|
|
Qt::Key_unknown, // 249 0xF9 VK_EREOF | Erase EOF key
|
|
|
|
Qt::Key_Play, // 250 0xFA VK_PLAY | Play key
|
|
|
|
Qt::Key_Zoom, // 251 0xFB VK_ZOOM | Zoom key
|
|
|
|
Qt::Key_unknown, // 252 0xFC VK_NONAME | Reserved
|
|
|
|
Qt::Key_unknown, // 253 0xFD VK_PA1 | PA1 key
|
|
|
|
Qt::Key_Clear, // 254 0xFE VK_OEM_CLEAR | Clear key
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2014-07-02 13:44:49 +00:00
|
|
|
static const uint CmdTbl[] = { // Multimedia keys mapping table
|
|
|
|
// Dec | Hex | AppCommand
|
|
|
|
Qt::Key_unknown, // 0 0x00
|
|
|
|
Qt::Key_Back, // 1 0x01 APPCOMMAND_BROWSER_BACKWARD
|
|
|
|
Qt::Key_Forward, // 2 0x02 APPCOMMAND_BROWSER_FORWARD
|
|
|
|
Qt::Key_Refresh, // 3 0x03 APPCOMMAND_BROWSER_REFRESH
|
|
|
|
Qt::Key_Stop, // 4 0x04 APPCOMMAND_BROWSER_STOP
|
|
|
|
Qt::Key_Search, // 5 0x05 APPCOMMAND_BROWSER_SEARCH
|
|
|
|
Qt::Key_Favorites, // 6 0x06 APPCOMMAND_BROWSER_FAVORITES
|
|
|
|
Qt::Key_Home, // 7 0x07 APPCOMMAND_BROWSER_HOME
|
|
|
|
Qt::Key_VolumeMute, // 8 0x08 APPCOMMAND_VOLUME_MUTE
|
|
|
|
Qt::Key_VolumeDown, // 9 0x09 APPCOMMAND_VOLUME_DOWN
|
|
|
|
Qt::Key_VolumeUp, // 10 0x0a APPCOMMAND_VOLUME_UP
|
|
|
|
Qt::Key_MediaNext, // 11 0x0b APPCOMMAND_MEDIA_NEXTTRACK
|
|
|
|
Qt::Key_MediaPrevious, // 12 0x0c APPCOMMAND_MEDIA_PREVIOUSTRACK
|
|
|
|
Qt::Key_MediaStop, // 13 0x0d APPCOMMAND_MEDIA_STOP
|
|
|
|
Qt::Key_MediaTogglePlayPause, // 14 0x0e APPCOMMAND_MEDIA_PLAYPAUSE
|
|
|
|
Qt::Key_LaunchMail, // 15 0x0f APPCOMMAND_LAUNCH_MAIL
|
|
|
|
Qt::Key_LaunchMedia, // 16 0x10 APPCOMMAND_LAUNCH_MEDIA_SELECT
|
|
|
|
Qt::Key_Launch0, // 17 0x11 APPCOMMAND_LAUNCH_APP1
|
|
|
|
Qt::Key_Launch1, // 18 0x12 APPCOMMAND_LAUNCH_APP2
|
|
|
|
Qt::Key_BassDown, // 19 0x13 APPCOMMAND_BASS_DOWN
|
|
|
|
Qt::Key_BassBoost, // 20 0x14 APPCOMMAND_BASS_BOOST
|
|
|
|
Qt::Key_BassUp, // 21 0x15 APPCOMMAND_BASS_UP
|
|
|
|
Qt::Key_TrebleDown, // 22 0x16 APPCOMMAND_TREBLE_DOWN
|
|
|
|
Qt::Key_TrebleUp, // 23 0x17 APPCOMMAND_TREBLE_UP
|
|
|
|
Qt::Key_MicMute, // 24 0x18 APPCOMMAND_MICROPHONE_VOLUME_MUTE
|
|
|
|
Qt::Key_MicVolumeDown, // 25 0x19 APPCOMMAND_MICROPHONE_VOLUME_DOWN
|
|
|
|
Qt::Key_MicVolumeUp, // 26 0x1a APPCOMMAND_MICROPHONE_VOLUME_UP
|
|
|
|
Qt::Key_Help, // 27 0x1b APPCOMMAND_HELP
|
|
|
|
Qt::Key_Find, // 28 0x1c APPCOMMAND_FIND
|
|
|
|
Qt::Key_New, // 29 0x1d APPCOMMAND_NEW
|
|
|
|
Qt::Key_Open, // 30 0x1e APPCOMMAND_OPEN
|
|
|
|
Qt::Key_Close, // 31 0x1f APPCOMMAND_CLOSE
|
|
|
|
Qt::Key_Save, // 32 0x20 APPCOMMAND_SAVE
|
2016-10-04 10:44:12 +00:00
|
|
|
Qt::Key_Printer, // 33 0x21 APPCOMMAND_PRINT
|
2014-07-02 13:44:49 +00:00
|
|
|
Qt::Key_Undo, // 34 0x22 APPCOMMAND_UNDO
|
|
|
|
Qt::Key_Redo, // 35 0x23 APPCOMMAND_REDO
|
|
|
|
Qt::Key_Copy, // 36 0x24 APPCOMMAND_COPY
|
|
|
|
Qt::Key_Cut, // 37 0x25 APPCOMMAND_CUT
|
|
|
|
Qt::Key_Paste, // 38 0x26 APPCOMMAND_PASTE
|
|
|
|
Qt::Key_Reply, // 39 0x27 APPCOMMAND_REPLY_TO_MAIL
|
|
|
|
Qt::Key_MailForward, // 40 0x28 APPCOMMAND_FORWARD_MAIL
|
|
|
|
Qt::Key_Send, // 41 0x29 APPCOMMAND_SEND_MAIL
|
|
|
|
Qt::Key_Spell, // 42 0x2a APPCOMMAND_SPELL_CHECK
|
|
|
|
Qt::Key_unknown, // 43 0x2b APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE
|
|
|
|
Qt::Key_unknown, // 44 0x2c APPCOMMAND_MIC_ON_OFF_TOGGLE
|
|
|
|
Qt::Key_unknown, // 45 0x2d APPCOMMAND_CORRECTION_LIST
|
|
|
|
Qt::Key_MediaPlay, // 46 0x2e APPCOMMAND_MEDIA_PLAY
|
|
|
|
Qt::Key_MediaPause, // 47 0x2f APPCOMMAND_MEDIA_PAUSE
|
|
|
|
Qt::Key_MediaRecord, // 48 0x30 APPCOMMAND_MEDIA_RECORD
|
|
|
|
Qt::Key_AudioForward, // 49 0x31 APPCOMMAND_MEDIA_FAST_FORWARD
|
|
|
|
Qt::Key_AudioRewind, // 50 0x32 APPCOMMAND_MEDIA_REWIND
|
|
|
|
Qt::Key_ChannelDown, // 51 0x33 APPCOMMAND_MEDIA_CHANNEL_DOWN
|
|
|
|
Qt::Key_ChannelUp // 52 0x34 APPCOMMAND_MEDIA_CHANNEL_UP
|
|
|
|
};
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
// Possible modifier states.
|
|
|
|
// NOTE: The order of these states match the order in QWindowsKeyMapper::updatePossibleKeyCodes()!
|
|
|
|
static const Qt::KeyboardModifiers ModsTbl[] = {
|
|
|
|
Qt::NoModifier, // 0
|
|
|
|
Qt::ShiftModifier, // 1
|
|
|
|
Qt::ControlModifier, // 2
|
|
|
|
Qt::ControlModifier | Qt::ShiftModifier, // 3
|
|
|
|
Qt::AltModifier, // 4
|
|
|
|
Qt::AltModifier | Qt::ShiftModifier, // 5
|
|
|
|
Qt::AltModifier | Qt::ControlModifier, // 6
|
|
|
|
Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
|
|
|
|
Qt::NoModifier, // Fall-back to raw Key_*
|
|
|
|
};
|
2012-10-10 15:52:21 +00:00
|
|
|
static const size_t NumMods = sizeof ModsTbl / sizeof *ModsTbl;
|
2020-06-11 09:35:19 +00:00
|
|
|
static_assert((NumMods == KeyboardLayoutItem::NumQtKeys));
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2018-03-27 10:54:30 +00:00
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
|
|
QDebug operator<<(QDebug d, const KeyboardLayoutItem &k)
|
|
|
|
{
|
|
|
|
QDebugStateSaver saver(d);
|
|
|
|
d.nospace();
|
|
|
|
d << "KeyboardLayoutItem(";
|
|
|
|
if (k.exists) {
|
|
|
|
for (size_t i = 0; i < NumMods; ++i) {
|
|
|
|
if (const quint32 qtKey = k.qtKey[i]) {
|
|
|
|
d << '[' << i << ' ';
|
|
|
|
QtDebugUtils::formatQFlags(d, ModsTbl[i]);
|
2019-04-30 10:51:36 +00:00
|
|
|
d << ' ' << Qt::hex << Qt::showbase << qtKey << Qt::dec << Qt::noshowbase << ' ';
|
2018-03-27 10:54:30 +00:00
|
|
|
QtDebugUtils::formatQEnum(d, Qt::Key(qtKey));
|
|
|
|
if (qtKey >= 32 && qtKey < 128)
|
|
|
|
d << " '" << char(qtKey) << '\'';
|
|
|
|
if (k.deadkeys & (1<<i))
|
|
|
|
d << " deadkey";
|
|
|
|
d << "] ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d << ')';
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
#endif // QT_NO_DEBUG_STREAM
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
/**
|
|
|
|
Remap return or action key to select key for windows mobile.
|
|
|
|
*/
|
2016-02-18 12:13:49 +00:00
|
|
|
inline quint32 winceKeyBend(quint32 keyCode)
|
2011-08-17 13:58:39 +00:00
|
|
|
{
|
|
|
|
return KeyTbl[keyCode];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Translate a VK into a Qt key code, or unicode character
|
2019-01-24 07:54:38 +00:00
|
|
|
static inline quint32 toKeyOrUnicode(quint32 vk, quint32 scancode, unsigned char *kbdBuffer, bool *isDeadkey = nullptr)
|
2011-08-17 13:58:39 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(vk > 0 && vk < 256);
|
2016-02-18 12:13:49 +00:00
|
|
|
quint32 code = 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
QChar unicodeBuffer[5];
|
2014-06-05 07:43:04 +00:00
|
|
|
int res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
|
2015-01-27 15:58:53 +00:00
|
|
|
// When Ctrl modifier is used ToUnicode does not return correct values. In order to assign the
|
|
|
|
// right key the control modifier is removed for just that function if the previous call failed.
|
|
|
|
if (res == 0 && kbdBuffer[VK_CONTROL]) {
|
|
|
|
const unsigned char controlState = kbdBuffer[VK_CONTROL];
|
|
|
|
kbdBuffer[VK_CONTROL] = 0;
|
|
|
|
res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
|
2012-11-13 06:50:43 +00:00
|
|
|
kbdBuffer[VK_CONTROL] = controlState;
|
2015-01-27 15:58:53 +00:00
|
|
|
}
|
2011-08-17 13:58:39 +00:00
|
|
|
if (res)
|
|
|
|
code = unicodeBuffer[0].toUpper().unicode();
|
|
|
|
|
|
|
|
// Qt::Key_*'s are not encoded below 0x20, so try again, and DEL keys (0x7f) is encoded with a
|
|
|
|
// proper Qt::Key_ code
|
|
|
|
if (code < 0x20 || code == 0x7f) // Handles res==0 too
|
|
|
|
code = winceKeyBend(vk);
|
|
|
|
|
|
|
|
if (isDeadkey)
|
|
|
|
*isDeadkey = (res == -1);
|
|
|
|
|
|
|
|
return code == Qt::Key_unknown ? 0 : code;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int asciiToKeycode(char a, int state)
|
|
|
|
{
|
2022-12-20 09:07:00 +00:00
|
|
|
a = QtMiscUtils::toAsciiUpper(a);
|
2011-08-17 13:58:39 +00:00
|
|
|
if ((state & Qt::ControlModifier) != 0) {
|
|
|
|
if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_
|
|
|
|
a += '@'; // to @..A..Z.._
|
|
|
|
}
|
|
|
|
return a & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Key translation -----------------------------------------------------------------------[ end ]---
|
|
|
|
|
|
|
|
|
|
|
|
// Keyboard map private ----------------------------------------------------------------[ start ]---
|
|
|
|
|
|
|
|
void QWindowsKeyMapper::deleteLayouts()
|
|
|
|
{
|
2018-08-03 12:34:17 +00:00
|
|
|
for (KeyboardLayoutItem &k : keyLayout)
|
|
|
|
k.exists = false;
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QWindowsKeyMapper::changeKeyboard()
|
|
|
|
{
|
|
|
|
deleteLayouts();
|
|
|
|
|
|
|
|
/* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout()
|
|
|
|
* returns a DWORD. */
|
|
|
|
|
2016-02-18 12:13:49 +00:00
|
|
|
LCID newLCID = MAKELCID(quintptr(GetKeyboardLayout(0)), SORT_DEFAULT);
|
2011-08-17 13:58:39 +00:00
|
|
|
// keyboardInputLocale = qt_localeFromLCID(newLCID);
|
|
|
|
|
|
|
|
bool bidi = false;
|
|
|
|
wchar_t LCIDFontSig[16];
|
|
|
|
if (GetLocaleInfo(newLCID, LOCALE_FONTSIGNATURE, LCIDFontSig, sizeof(LCIDFontSig) / sizeof(wchar_t))
|
2016-02-18 12:13:49 +00:00
|
|
|
&& (LCIDFontSig[7] & wchar_t(0x0800)))
|
2011-08-17 13:58:39 +00:00
|
|
|
bidi = true;
|
|
|
|
|
|
|
|
keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight;
|
2018-07-13 06:21:31 +00:00
|
|
|
m_seenAltGr = false;
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
2013-10-25 13:43:42 +00:00
|
|
|
// Helper function that is used when obtaining the list of characters that can be produced by one key and
|
|
|
|
// every possible combination of modifiers
|
2011-08-17 13:58:39 +00:00
|
|
|
inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt)
|
|
|
|
{
|
|
|
|
kbd[VK_LSHIFT ] = (shift ? 0x80 : 0);
|
|
|
|
kbd[VK_SHIFT ] = (shift ? 0x80 : 0);
|
|
|
|
kbd[VK_LCONTROL] = (ctrl ? 0x80 : 0);
|
|
|
|
kbd[VK_CONTROL ] = (ctrl ? 0x80 : 0);
|
|
|
|
kbd[VK_RMENU ] = (alt ? 0x80 : 0);
|
|
|
|
kbd[VK_MENU ] = (alt ? 0x80 : 0);
|
|
|
|
}
|
|
|
|
|
2013-10-25 13:43:42 +00:00
|
|
|
// Adds the msg's key to keyLayout if it is not yet present there
|
2011-08-17 13:58:39 +00:00
|
|
|
void QWindowsKeyMapper::updateKeyMap(const MSG &msg)
|
|
|
|
{
|
|
|
|
unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
|
|
|
|
GetKeyboardState(kbdBuffer);
|
2023-07-04 14:50:19 +00:00
|
|
|
const quint32 scancode = getScancode(msg);
|
2016-02-18 12:13:49 +00:00
|
|
|
updatePossibleKeyCodes(kbdBuffer, scancode, quint32(msg.wParam));
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
2013-10-25 13:43:42 +00:00
|
|
|
// Fills keyLayout for that vk_key. Values are all characters one can type using that key
|
|
|
|
// (in connection with every combination of modifiers) and whether these "characters" are
|
|
|
|
// dead keys.
|
2011-08-17 13:58:39 +00:00
|
|
|
void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode,
|
|
|
|
quint32 vk_key)
|
|
|
|
{
|
2012-10-10 16:27:04 +00:00
|
|
|
if (!vk_key || (keyLayout[vk_key].exists && !keyLayout[vk_key].dirty))
|
2011-08-17 13:58:39 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Copy keyboard state, so we can modify and query output for each possible permutation
|
|
|
|
unsigned char buffer[256];
|
|
|
|
memcpy(buffer, kbdBuffer, sizeof(buffer));
|
|
|
|
// Always 0, as Windows doesn't treat these as modifiers;
|
|
|
|
buffer[VK_LWIN ] = 0;
|
|
|
|
buffer[VK_RWIN ] = 0;
|
|
|
|
buffer[VK_CAPITAL ] = 0;
|
|
|
|
buffer[VK_NUMLOCK ] = 0;
|
|
|
|
buffer[VK_SCROLL ] = 0;
|
|
|
|
// Always 0, since we'll only change the other versions
|
|
|
|
buffer[VK_RSHIFT ] = 0;
|
|
|
|
buffer[VK_RCONTROL] = 0;
|
|
|
|
buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph
|
|
|
|
|
2013-10-25 13:43:42 +00:00
|
|
|
// keyLayout contains the actual characters which can be written using the vk_key together with the
|
|
|
|
// different modifiers. '2' together with shift will for example cause the character
|
|
|
|
// to be @ for a US key layout (thus keyLayout[vk_key].qtKey[1] will be @). In addition to that
|
|
|
|
// it stores whether the resulting key is a dead key as these keys have to be handled later.
|
2011-08-17 13:58:39 +00:00
|
|
|
bool isDeadKey = false;
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].deadkeys = 0;
|
|
|
|
keyLayout[vk_key].dirty = false;
|
|
|
|
keyLayout[vk_key].exists = true;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, false, false, false);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[0] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x01 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, true, false, false);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[1] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x02 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, false, true, false);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[2] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x04 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, true, true, false);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[3] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x08 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, false, false, true);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[4] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x10 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, true, false, true);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[5] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x20 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, false, true, true);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[6] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x40 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
setKbdState(buffer, true, true, true);
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[7] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
|
|
|
|
keyLayout[vk_key].deadkeys |= isDeadKey ? 0x80 : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
// Add a fall back key for layouts which don't do composition and show non-latin1 characters
|
2016-02-18 12:13:49 +00:00
|
|
|
quint32 fallbackKey = winceKeyBend(vk_key);
|
2011-08-17 13:58:39 +00:00
|
|
|
if (!fallbackKey || fallbackKey == Qt::Key_unknown) {
|
|
|
|
fallbackKey = 0;
|
2017-01-10 07:50:57 +00:00
|
|
|
if (vk_key != keyLayout[vk_key].qtKey[0] && vk_key != keyLayout[vk_key].qtKey[1]
|
|
|
|
&& vk_key < 0x5B && vk_key > 0x2F)
|
2011-08-17 13:58:39 +00:00
|
|
|
fallbackKey = vk_key;
|
|
|
|
}
|
2012-10-10 16:27:04 +00:00
|
|
|
keyLayout[vk_key].qtKey[8] = fallbackKey;
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2013-10-25 13:43:42 +00:00
|
|
|
// If one of the values inserted into the keyLayout above, can be considered a dead key, we have
|
|
|
|
// to run the workaround below.
|
2013-10-23 10:34:02 +00:00
|
|
|
if (keyLayout[vk_key].deadkeys) {
|
2011-08-17 13:58:39 +00:00
|
|
|
// Push a Space, then the original key through the low-level ToAscii functions.
|
|
|
|
// We do this because these functions (ToAscii / ToUnicode) will alter the internal state of
|
|
|
|
// the keyboard driver By doing the following, we set the keyboard driver state back to what
|
|
|
|
// it was before we wrecked it with the code above.
|
|
|
|
// We need to push the space with an empty keystate map, since the driver checks the map for
|
|
|
|
// transitions in modifiers, so this helps us capture all possible deadkeys.
|
|
|
|
unsigned char emptyBuffer[256];
|
|
|
|
memset(emptyBuffer, 0, sizeof(emptyBuffer));
|
|
|
|
::ToAscii(VK_SPACE, 0, emptyBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
|
|
|
|
::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
|
|
|
|
}
|
2018-03-27 10:54:30 +00:00
|
|
|
qCDebug(lcQpaEvents) << __FUNCTION__ << "for virtual key="
|
2019-04-30 10:51:36 +00:00
|
|
|
<< Qt::hex << Qt::showbase << vk_key << Qt::dec << Qt::noshowbase << keyLayout[vk_key];
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline QString messageKeyText(const MSG &msg)
|
|
|
|
{
|
2016-02-18 12:13:49 +00:00
|
|
|
const QChar ch = QChar(ushort(msg.wParam));
|
2011-08-17 13:58:39 +00:00
|
|
|
return ch.isNull() ? QString() : QString(ch);
|
|
|
|
}
|
|
|
|
|
2022-02-12 08:46:26 +00:00
|
|
|
[[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd)
|
|
|
|
{
|
|
|
|
const UINT dpi = GetDpiForWindow(hwnd);
|
|
|
|
const int captionHeight = GetSystemMetricsForDpi(SM_CYCAPTION, dpi);
|
|
|
|
if (IsZoomed(hwnd))
|
|
|
|
return captionHeight;
|
|
|
|
// The frame height should also be taken into account if the window
|
|
|
|
// is not maximized.
|
|
|
|
const int frameHeight = GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi)
|
|
|
|
+ GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
|
|
|
|
return captionHeight + frameHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags)
|
|
|
|
{
|
|
|
|
static constexpr const Qt::WindowFlags titleBarHints =
|
|
|
|
Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint;
|
|
|
|
return (flags & Qt::WindowSystemMenuHint) && (flags & Qt::WindowTitleHint) && !(flags & titleBarHints)
|
|
|
|
&& (flags & (Qt::FramelessWindowHint | Qt::CustomizeWindowHint));
|
|
|
|
}
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
static void showSystemMenu(QWindow* w)
|
|
|
|
{
|
|
|
|
QWindow *topLevel = QWindowsWindow::topLevelOf(w);
|
|
|
|
HWND topLevelHwnd = QWindowsWindow::handleOf(topLevel);
|
2012-09-06 08:54:50 +00:00
|
|
|
HMENU menu = GetSystemMenu(topLevelHwnd, FALSE);
|
2011-08-17 13:58:39 +00:00
|
|
|
if (!menu)
|
|
|
|
return; // no menu for this window
|
|
|
|
|
2022-09-02 14:01:16 +00:00
|
|
|
#define enabled (MF_BYCOMMAND | MFS_ENABLED)
|
|
|
|
#define disabled (MF_BYCOMMAND | MFS_GRAYED)
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2022-09-02 14:31:02 +00:00
|
|
|
EnableMenuItem(menu, SC_MINIMIZE, (topLevel->flags() & Qt::WindowMinimizeButtonHint) ? enabled : disabled);
|
2022-09-02 14:07:17 +00:00
|
|
|
const bool maximized = IsZoomed(topLevelHwnd);
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2022-09-02 14:31:02 +00:00
|
|
|
EnableMenuItem(menu, SC_MAXIMIZE, !(topLevel->flags() & Qt::WindowMaximizeButtonHint) || maximized ? disabled : enabled);
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
// We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check
|
|
|
|
// this and our menu here would be out-of-sync with the menu produced by mouse-click on the
|
|
|
|
// System Menu, or right-click on the title bar.
|
2022-09-02 14:31:02 +00:00
|
|
|
EnableMenuItem(menu, SC_SIZE, (topLevel->flags() & Qt::MSWindowsFixedSizeDialogHint) || maximized ? disabled : enabled);
|
|
|
|
EnableMenuItem(menu, SC_MOVE, maximized ? disabled : enabled);
|
2011-08-17 13:58:39 +00:00
|
|
|
EnableMenuItem(menu, SC_CLOSE, enabled);
|
2022-09-02 14:12:48 +00:00
|
|
|
EnableMenuItem(menu, SC_RESTORE, maximized ? enabled : disabled);
|
2022-02-07 09:48:00 +00:00
|
|
|
|
|
|
|
// Highlight the first entry in the menu, this is what native Win32 applications usually do.
|
2022-09-02 14:12:48 +00:00
|
|
|
HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_HILITE);
|
2022-02-07 09:48:00 +00:00
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
// Set bold on close menu item
|
2022-09-02 14:23:55 +00:00
|
|
|
SetMenuDefaultItem(menu, SC_CLOSE, FALSE);
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
#undef enabled
|
|
|
|
#undef disabled
|
2022-09-02 14:31:02 +00:00
|
|
|
|
2015-06-29 08:25:24 +00:00
|
|
|
const QPoint pos = QHighDpi::toNativePixels(topLevel->geometry().topLeft(), topLevel);
|
2022-02-12 08:46:26 +00:00
|
|
|
const int titleBarOffset = isSystemMenuOffsetNeeded(topLevel->flags()) ? getTitleBarHeight(topLevelHwnd) : 0;
|
2011-08-17 13:58:39 +00:00
|
|
|
const int ret = TrackPopupMenuEx(menu,
|
2022-09-02 14:31:02 +00:00
|
|
|
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
|
2022-02-12 08:46:26 +00:00
|
|
|
pos.x(), pos.y() + titleBarOffset,
|
2015-04-30 12:54:53 +00:00
|
|
|
topLevelHwnd,
|
2019-01-24 07:54:38 +00:00
|
|
|
nullptr);
|
2022-08-29 02:43:45 +00:00
|
|
|
|
|
|
|
// Remove the highlight of the restore menu item, otherwise when the user right-clicks
|
|
|
|
// on the title bar, the popuped system menu will also highlight the restore item, which
|
|
|
|
// is not appropriate, it should only be highlighted if the menu is brought up by keyboard.
|
|
|
|
HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_UNHILITE);
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
if (ret)
|
2016-02-18 12:13:49 +00:00
|
|
|
qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, WPARAM(ret), 0);
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
static inline void sendExtendedPressRelease(QWindow *w, unsigned long timestamp, int k,
|
2011-08-17 13:58:39 +00:00
|
|
|
Qt::KeyboardModifiers mods,
|
|
|
|
quint32 nativeScanCode,
|
|
|
|
quint32 nativeVirtualKey,
|
|
|
|
quint32 nativeModifiers,
|
|
|
|
const QString & text = QString(),
|
|
|
|
bool autorep = false,
|
|
|
|
ushort count = 1)
|
|
|
|
{
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
|
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\brief To be called from the window procedure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool QWindowsKeyMapper::translateKeyEvent(QWindow *widget, HWND hwnd,
|
|
|
|
const MSG &msg, LRESULT *result)
|
|
|
|
{
|
|
|
|
*result = 0;
|
2013-10-16 15:34:52 +00:00
|
|
|
|
|
|
|
// Reset layout map when system keyboard layout is changed
|
|
|
|
if (msg.message == WM_INPUTLANGCHANGE) {
|
2018-04-25 06:46:28 +00:00
|
|
|
changeKeyboard();
|
2013-10-16 15:34:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-02 13:44:49 +00:00
|
|
|
#if defined(WM_APPCOMMAND)
|
|
|
|
if (msg.message == WM_APPCOMMAND)
|
|
|
|
return translateMultimediaKeyEventInternal(widget, msg);
|
|
|
|
#endif
|
|
|
|
|
2014-01-13 10:12:34 +00:00
|
|
|
// WM_(IME_)CHAR messages already contain the character in question so there is
|
2013-12-16 13:56:59 +00:00
|
|
|
// no need to fiddle with our key map. In any other case add this key to the
|
|
|
|
// keymap if it is not present yet.
|
2014-01-13 10:12:34 +00:00
|
|
|
if (msg.message != WM_CHAR && msg.message != WM_IME_CHAR)
|
2013-12-16 13:56:59 +00:00
|
|
|
updateKeyMap(msg);
|
2013-10-16 15:34:52 +00:00
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
MSG peekedMsg;
|
|
|
|
// consume dead chars?(for example, typing '`','a' resulting in a-accent).
|
|
|
|
if (PeekMessage(&peekedMsg, hwnd, 0, 0, PM_NOREMOVE) && peekedMsg.message == WM_DEADCHAR)
|
|
|
|
return true;
|
2013-10-16 15:34:52 +00:00
|
|
|
|
2018-03-19 14:09:19 +00:00
|
|
|
return translateKeyEventInternal(widget, msg, false, result);
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
2014-07-02 13:44:49 +00:00
|
|
|
bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, const MSG &msg)
|
|
|
|
{
|
|
|
|
#if defined(WM_APPCOMMAND)
|
2018-04-30 12:36:56 +00:00
|
|
|
const int cmd = GET_APPCOMMAND_LPARAM(msg.lParam);
|
2016-11-22 14:45:36 +00:00
|
|
|
// QTBUG-57198, do not send mouse-synthesized commands as key events in addition
|
2019-04-17 15:19:03 +00:00
|
|
|
bool skipPressRelease = false;
|
2018-04-30 12:36:56 +00:00
|
|
|
switch (GET_DEVICE_LPARAM(msg.lParam)) {
|
|
|
|
case FAPPCOMMAND_MOUSE:
|
2016-11-22 14:45:36 +00:00
|
|
|
return false;
|
2018-04-30 12:36:56 +00:00
|
|
|
case FAPPCOMMAND_KEY:
|
2019-04-17 15:19:03 +00:00
|
|
|
// QTBUG-62838, use WM_KEYDOWN/WM_KEYUP for commands that are reflected
|
|
|
|
// in VK(s) like VK_MEDIA_NEXT_TRACK, to get correct codes and autorepeat.
|
|
|
|
// Don't do that for APPCOMMAND_BROWSER_HOME as that one does not trigger two events.
|
|
|
|
if (cmd != APPCOMMAND_BROWSER_HOME)
|
|
|
|
skipPressRelease = true;
|
2018-04-30 12:36:56 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-07-02 13:44:49 +00:00
|
|
|
const int dwKeys = GET_KEYSTATE_LPARAM(msg.lParam);
|
|
|
|
int state = 0;
|
|
|
|
state |= (dwKeys & MK_SHIFT ? int(Qt::ShiftModifier) : 0);
|
|
|
|
state |= (dwKeys & MK_CONTROL ? int(Qt::ControlModifier) : 0);
|
|
|
|
|
|
|
|
QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
|
|
|
|
|
|
|
|
if (cmd < 0 || cmd > 52)
|
|
|
|
return false;
|
|
|
|
|
2016-02-18 12:13:49 +00:00
|
|
|
const int qtKey = int(CmdTbl[cmd]);
|
2019-04-17 15:19:03 +00:00
|
|
|
if (!skipPressRelease)
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
sendExtendedPressRelease(receiver, msg.time, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
|
2014-12-15 13:18:13 +00:00
|
|
|
// QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise,
|
|
|
|
// the keys are not passed to the active media player.
|
2018-05-03 16:31:53 +00:00
|
|
|
# if QT_CONFIG(shortcut)
|
2020-09-04 08:43:21 +00:00
|
|
|
const QKeySequence sequence(Qt::Modifier(state) | Qt::Key(qtKey));
|
2014-12-15 13:18:13 +00:00
|
|
|
return QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence);
|
2018-05-03 16:31:53 +00:00
|
|
|
# else
|
|
|
|
return false;
|
|
|
|
# endif
|
2014-07-02 13:44:49 +00:00
|
|
|
#else
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-07-13 06:21:31 +00:00
|
|
|
// QTBUG-69317: Check for AltGr found on some keyboards
|
|
|
|
// which is a sequence of left Ctrl (SYSKEY) + right Menu (Alt).
|
|
|
|
static bool isAltGr(MSG *msg)
|
2011-08-17 13:58:39 +00:00
|
|
|
{
|
2018-07-13 06:21:31 +00:00
|
|
|
enum : LONG_PTR { RightFlag = 0x1000000 };
|
|
|
|
if (msg->wParam != VK_CONTROL || (msg->lParam & RightFlag) != 0
|
|
|
|
|| (msg->message != WM_KEYDOWN && msg->message != WM_SYSKEYUP)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const UINT expectedMessage = msg->message == WM_SYSKEYUP
|
|
|
|
? WM_KEYUP : msg->message;
|
|
|
|
MSG peekedMsg;
|
|
|
|
if (PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_NOREMOVE) == FALSE
|
|
|
|
|| peekedMsg.message != expectedMessage || peekedMsg.wParam != VK_MENU
|
|
|
|
|| (peekedMsg.lParam & RightFlag) == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*msg = peekedMsg;
|
|
|
|
PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_REMOVE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
|
|
|
|
bool /* grab */, LRESULT *lResult)
|
|
|
|
{
|
|
|
|
const bool altGr = m_detectAltGrModifier && isAltGr(&msg);
|
|
|
|
if (altGr)
|
|
|
|
m_seenAltGr = true;
|
2016-02-18 12:13:49 +00:00
|
|
|
const UINT msgType = msg.message;
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2023-07-04 14:50:19 +00:00
|
|
|
const quint32 scancode = getScancode(msg);
|
2019-05-24 14:08:18 +00:00
|
|
|
auto vk_key = quint32(msg.wParam);
|
2011-08-17 13:58:39 +00:00
|
|
|
quint32 nModifiers = 0;
|
|
|
|
|
|
|
|
QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
|
|
|
|
|
|
|
|
// Map native modifiers to some bit representation
|
|
|
|
nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0);
|
|
|
|
// Add Lock keys to the same bits
|
|
|
|
nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0);
|
|
|
|
nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0);
|
|
|
|
|
|
|
|
if (msg.lParam & ExtendedKey)
|
|
|
|
nModifiers |= msg.lParam & ExtendedKey;
|
|
|
|
|
|
|
|
// Get the modifier states (may be altered later, depending on key code)
|
|
|
|
int state = 0;
|
2012-06-13 14:14:40 +00:00
|
|
|
state |= (nModifiers & ShiftAny ? int(Qt::ShiftModifier) : 0);
|
2018-07-13 06:21:31 +00:00
|
|
|
state |= (nModifiers & AltLeft ? int(Qt::AltModifier) : 0);
|
|
|
|
if ((nModifiers & AltRight) != 0)
|
|
|
|
state |= m_seenAltGr ? Qt::GroupSwitchModifier : Qt::AltModifier;
|
|
|
|
if ((nModifiers & ControlAny) != 0 && (state & Qt::GroupSwitchModifier) == 0)
|
|
|
|
state |= Qt::ControlModifier;
|
2012-06-13 14:14:40 +00:00
|
|
|
state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0);
|
2011-08-17 13:58:39 +00:00
|
|
|
// A multi-character key or a Input method character
|
|
|
|
// not found by our look-ahead
|
|
|
|
if (msgType == WM_CHAR || msgType == WM_IME_CHAR) {
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
sendExtendedPressRelease(receiver, msg.time, 0, Qt::KeyboardModifier(state), scancode, 0, nModifiers, messageKeyText(msg), false);
|
2011-08-17 13:58:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-28 13:03:50 +00:00
|
|
|
// Enable Alt accelerators ("&File") on menus
|
|
|
|
if (msgType == WM_SYSKEYDOWN && (nModifiers & AltAny) != 0 && GetMenu(msg.hwnd) != nullptr)
|
|
|
|
return false;
|
|
|
|
if (msgType == WM_SYSKEYUP && nModifiers == 0 && GetMenu(msg.hwnd) != nullptr)
|
|
|
|
return false;
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
bool result = false;
|
|
|
|
// handle Directionality changes (BiDi) with RTL extensions
|
|
|
|
if (m_useRTLExtensions) {
|
|
|
|
static int dirStatus = 0;
|
|
|
|
if (!dirStatus && state == Qt::ControlModifier
|
|
|
|
&& msg.wParam == VK_CONTROL
|
|
|
|
&& msgType == WM_KEYDOWN) {
|
|
|
|
if (GetKeyState(VK_LCONTROL) < 0)
|
|
|
|
dirStatus = VK_LCONTROL;
|
|
|
|
else if (GetKeyState(VK_RCONTROL) < 0)
|
|
|
|
dirStatus = VK_RCONTROL;
|
|
|
|
} else if (dirStatus) {
|
|
|
|
if (msgType == WM_KEYDOWN) {
|
|
|
|
if (msg.wParam == VK_SHIFT) {
|
|
|
|
if (dirStatus == VK_LCONTROL && GetKeyState(VK_LSHIFT) < 0)
|
|
|
|
dirStatus = VK_LSHIFT;
|
|
|
|
else if (dirStatus == VK_RCONTROL && GetKeyState(VK_RSHIFT) < 0)
|
|
|
|
dirStatus = VK_RSHIFT;
|
|
|
|
} else {
|
|
|
|
dirStatus = 0;
|
|
|
|
}
|
|
|
|
} else if (msgType == WM_KEYUP) {
|
|
|
|
if (dirStatus == VK_LSHIFT
|
|
|
|
&& ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL))
|
|
|
|
|| (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) {
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_L, {},
|
2019-01-24 07:54:38 +00:00
|
|
|
scancode, vk_key, nModifiers, QString(), false);
|
2011-08-17 13:58:39 +00:00
|
|
|
result = true;
|
|
|
|
dirStatus = 0;
|
|
|
|
} else if (dirStatus == VK_RSHIFT
|
|
|
|
&& ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL))
|
|
|
|
|| (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) {
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_R, {},
|
2019-01-24 07:54:38 +00:00
|
|
|
scancode, vk_key, nModifiers, QString(), false);
|
2011-08-17 13:58:39 +00:00
|
|
|
result = true;
|
|
|
|
dirStatus = 0;
|
|
|
|
} else {
|
|
|
|
dirStatus = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dirStatus = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // RTL
|
|
|
|
|
|
|
|
// IME will process these keys, so simply return
|
|
|
|
if (msg.wParam == VK_PROCESSKEY)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Ignore invalid virtual keycodes (see bugs 127424, QTBUG-3630)
|
|
|
|
if (msg.wParam == 0 || msg.wParam == 0xFF)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Translate VK_* (native) -> Key_* (Qt) keys
|
2013-10-16 15:34:52 +00:00
|
|
|
int modifiersIndex = 0;
|
|
|
|
modifiersIndex |= (nModifiers & ShiftAny ? 0x1 : 0);
|
|
|
|
modifiersIndex |= (nModifiers & ControlAny ? 0x2 : 0);
|
|
|
|
modifiersIndex |= (nModifiers & AltAny ? 0x4 : 0);
|
|
|
|
|
2018-07-13 06:21:31 +00:00
|
|
|
// Note: For the resulting key, AltGr is equivalent to Alt + Ctrl (as
|
|
|
|
// opposed to Linux); hence no entry in KeyboardLayoutItem is required
|
2013-10-16 15:34:52 +00:00
|
|
|
int code = keyLayout[vk_key].qtKey[modifiersIndex];
|
2011-08-17 13:58:39 +00:00
|
|
|
|
2018-07-13 06:21:31 +00:00
|
|
|
// If the bit 24 of lParm is set you received a enter,
|
|
|
|
// otherwise a Return. (This is the extended key bit)
|
|
|
|
if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000))
|
|
|
|
code = Qt::Key_Enter;
|
|
|
|
else if (altGr)
|
|
|
|
code = Qt::Key_AltGr;
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
// Invert state logic:
|
|
|
|
// If the key actually pressed is a modifier key, then we remove its modifier key from the
|
|
|
|
// state, since a modifier-key can't have itself as a modifier
|
|
|
|
if (code == Qt::Key_Control)
|
|
|
|
state = state ^ Qt::ControlModifier;
|
|
|
|
else if (code == Qt::Key_Shift)
|
|
|
|
state = state ^ Qt::ShiftModifier;
|
|
|
|
else if (code == Qt::Key_Alt)
|
|
|
|
state = state ^ Qt::AltModifier;
|
2018-07-13 06:21:31 +00:00
|
|
|
else if (code == Qt::Key_AltGr)
|
|
|
|
state = state ^ Qt::GroupSwitchModifier;
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
// All cursor keys without extended bit
|
|
|
|
if (!(msg.lParam & 0x1000000)) {
|
|
|
|
switch (code) {
|
|
|
|
case Qt::Key_Left:
|
|
|
|
case Qt::Key_Right:
|
|
|
|
case Qt::Key_Up:
|
|
|
|
case Qt::Key_Down:
|
|
|
|
case Qt::Key_PageUp:
|
|
|
|
case Qt::Key_PageDown:
|
|
|
|
case Qt::Key_Home:
|
|
|
|
case Qt::Key_End:
|
|
|
|
case Qt::Key_Insert:
|
|
|
|
case Qt::Key_Delete:
|
|
|
|
case Qt::Key_Asterisk:
|
|
|
|
case Qt::Key_Plus:
|
|
|
|
case Qt::Key_Minus:
|
|
|
|
case Qt::Key_Period:
|
2014-06-05 09:56:01 +00:00
|
|
|
case Qt::Key_Comma:
|
2011-08-17 13:58:39 +00:00
|
|
|
case Qt::Key_0:
|
|
|
|
case Qt::Key_1:
|
|
|
|
case Qt::Key_2:
|
|
|
|
case Qt::Key_3:
|
|
|
|
case Qt::Key_4:
|
|
|
|
case Qt::Key_5:
|
|
|
|
case Qt::Key_6:
|
|
|
|
case Qt::Key_7:
|
|
|
|
case Qt::Key_8:
|
|
|
|
case Qt::Key_9:
|
|
|
|
state |= ((msg.wParam >= '0' && msg.wParam <= '9')
|
|
|
|
|| (msg.wParam >= VK_OEM_PLUS && msg.wParam <= VK_OEM_3))
|
2012-06-13 14:14:40 +00:00
|
|
|
? 0 : int(Qt::KeypadModifier);
|
2017-06-21 11:45:53 +00:00
|
|
|
Q_FALLTHROUGH();
|
2011-08-17 13:58:39 +00:00
|
|
|
default:
|
2016-02-18 12:13:49 +00:00
|
|
|
if (uint(msg.lParam) == 0x004c0001 || uint(msg.lParam) == 0xc04c0001)
|
2011-08-17 13:58:39 +00:00
|
|
|
state |= Qt::KeypadModifier;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Other keys with with extended bit
|
|
|
|
else {
|
|
|
|
switch (code) {
|
|
|
|
case Qt::Key_Enter:
|
|
|
|
case Qt::Key_Slash:
|
|
|
|
case Qt::Key_NumLock:
|
|
|
|
state |= Qt::KeypadModifier;
|
2016-02-18 12:13:49 +00:00
|
|
|
break;
|
2011-08-17 13:58:39 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// KEYDOWN ---------------------------------------------------------------------------------
|
|
|
|
if (msgType == WM_KEYDOWN || msgType == WM_IME_KEYDOWN || msgType == WM_SYSKEYDOWN) {
|
|
|
|
// Get the last record of this key press, so we can validate the current state
|
|
|
|
// The record is not removed from the list
|
2016-02-18 12:13:49 +00:00
|
|
|
KeyRecord *rec = key_recorder.findKey(int(msg.wParam), false);
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
// If rec's state doesn't match the current state, something has changed behind our back
|
|
|
|
// (Consumed by modal widget is one possibility) So, remove the record from the list
|
|
|
|
// This will stop the auto-repeat of the key, should a modifier change, for example
|
|
|
|
if (rec && rec->state != state) {
|
2016-02-18 12:13:49 +00:00
|
|
|
key_recorder.findKey(int(msg.wParam), true);
|
2019-01-24 07:54:38 +00:00
|
|
|
rec = nullptr;
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find unicode character from Windows Message Queue
|
|
|
|
MSG wm_char;
|
|
|
|
UINT charType = (msgType == WM_KEYDOWN
|
|
|
|
? WM_CHAR
|
|
|
|
: msgType == WM_IME_KEYDOWN ? WM_IME_CHAR : WM_SYSCHAR);
|
|
|
|
|
|
|
|
QChar uch;
|
2019-01-24 07:54:38 +00:00
|
|
|
if (PeekMessage(&wm_char, nullptr, charType, charType, PM_REMOVE)) {
|
2018-03-19 14:09:19 +00:00
|
|
|
if (QWindowsContext::filterNativeEvent(&wm_char, lResult))
|
|
|
|
return true;
|
|
|
|
if (receiver && QWindowsContext::filterNativeEvent(receiver, &wm_char, lResult))
|
|
|
|
return true;
|
2011-08-17 13:58:39 +00:00
|
|
|
// Found a ?_CHAR
|
2016-02-18 12:13:49 +00:00
|
|
|
uch = QChar(ushort(wm_char.wParam));
|
2016-08-01 14:15:13 +00:00
|
|
|
if (uch.isHighSurrogate()) {
|
|
|
|
m_lastHighSurrogate = uch;
|
|
|
|
return true;
|
2018-08-03 12:34:17 +00:00
|
|
|
}
|
|
|
|
if (uch.isLowSurrogate() && !m_lastHighSurrogate.isNull()) {
|
2016-08-01 14:15:13 +00:00
|
|
|
if (QObject *focusObject = QGuiApplication::focusObject()) {
|
|
|
|
const QChar chars[2] = {m_lastHighSurrogate, uch};
|
|
|
|
QInputMethodEvent event;
|
|
|
|
event.setCommitString(QString(chars, 2));
|
|
|
|
QCoreApplication::sendEvent(focusObject, &event);
|
|
|
|
}
|
|
|
|
m_lastHighSurrogate = QChar();
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
m_lastHighSurrogate = QChar();
|
|
|
|
}
|
2011-08-17 13:58:39 +00:00
|
|
|
if (msgType == WM_SYSKEYDOWN && uch.isLetter() && (msg.lParam & KF_ALTDOWN))
|
|
|
|
uch = uch.toLower(); // (See doc of WM_SYSCHAR) Alt-letter
|
|
|
|
if (!code && !uch.row())
|
2016-02-18 12:13:49 +00:00
|
|
|
code = asciiToKeycode(char(uch.cell()), state);
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Special handling for the WM_IME_KEYDOWN message. Microsoft IME (Korean) will not
|
|
|
|
// generate a WM_IME_CHAR message corresponding to this message. We might get wrong
|
|
|
|
// results, if we map this virtual key-code directly (for eg '?' US layouts). So try
|
|
|
|
// to find the correct key using the current message parameters & keyboard state.
|
|
|
|
if (uch.isNull() && msgType == WM_IME_KEYDOWN) {
|
2019-05-24 14:08:18 +00:00
|
|
|
const auto *windowsInputContext =
|
2015-09-22 08:57:51 +00:00
|
|
|
qobject_cast<const QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
|
|
|
|
if (!(windowsInputContext && windowsInputContext->isComposing()))
|
2016-02-18 12:13:49 +00:00
|
|
|
vk_key = ImmGetVirtualKey(reinterpret_cast<HWND>(window->winId()));
|
2011-08-17 13:58:39 +00:00
|
|
|
BYTE keyState[256];
|
|
|
|
wchar_t newKey[3] = {0};
|
|
|
|
GetKeyboardState(keyState);
|
|
|
|
int val = ToUnicode(vk_key, scancode, keyState, newKey, 2, 0);
|
|
|
|
if (val == 1) {
|
|
|
|
uch = QChar(newKey[0]);
|
|
|
|
} else {
|
|
|
|
// If we are still not able to find a unicode key, pass the WM_IME_KEYDOWN
|
|
|
|
// message to DefWindowProc() for generating a proper WM_KEYDOWN.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no ?_CHAR was found in the queue; deduct character from the ?_KEYDOWN parameters
|
|
|
|
if (uch.isNull()) {
|
|
|
|
if (msg.wParam == VK_DELETE) {
|
|
|
|
uch = QChar(QLatin1Char(0x7f)); // Windows doesn't know this one.
|
|
|
|
} else {
|
|
|
|
if (msgType != WM_SYSKEYDOWN || !code) {
|
2016-02-18 12:13:49 +00:00
|
|
|
UINT map = MapVirtualKey(UINT(msg.wParam), 2);
|
2011-08-17 13:58:39 +00:00
|
|
|
// If the high bit of the return value is set, it's a deadkey
|
|
|
|
if (!(map & 0x80000000))
|
2016-02-18 12:13:49 +00:00
|
|
|
uch = QChar(ushort(map));
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!code && !uch.row())
|
2016-02-18 12:13:49 +00:00
|
|
|
code = asciiToKeycode(char(uch.cell()), state);
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Special handling of global Windows hotkeys
|
|
|
|
if (state == Qt::AltModifier) {
|
|
|
|
switch (code) {
|
|
|
|
case Qt::Key_Escape:
|
|
|
|
case Qt::Key_Tab:
|
|
|
|
case Qt::Key_F4:
|
|
|
|
return false; // Send the event on to Windows
|
|
|
|
case Qt::Key_Space:
|
|
|
|
// do not pass this key to windows, we will process it ourselves
|
|
|
|
showSystemMenu(receiver);
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
|
|
|
|
if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
|
|
|
|
code = Qt::Key_Backtab;
|
|
|
|
|
|
|
|
// If we have a record, it means that the key is already pressed, the state is the same
|
|
|
|
// so, we have an auto-repeating key
|
|
|
|
if (rec) {
|
|
|
|
if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) {
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
|
2016-02-18 12:13:49 +00:00
|
|
|
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
|
2016-02-18 12:13:49 +00:00
|
|
|
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
|
2011-08-17 13:58:39 +00:00
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
|
|
|
|
// and store the key data into our records.
|
|
|
|
else {
|
|
|
|
const QString text = uch.isNull() ? QString() : QString(uch);
|
2016-02-18 12:13:49 +00:00
|
|
|
const char a = uch.row() ? char(0) : char(uch.cell());
|
2014-11-03 10:44:42 +00:00
|
|
|
const Qt::KeyboardModifiers modifiers(state);
|
|
|
|
#ifndef QT_NO_SHORTCUT
|
|
|
|
// Is Qt interested in the context menu key?
|
|
|
|
if (modifiers == Qt::SHIFT && code == Qt::Key_F10
|
2020-09-21 12:20:34 +00:00
|
|
|
&& !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(QKeySequence(Qt::SHIFT | Qt::Key_F10))) {
|
2014-11-03 10:44:42 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif // !QT_NO_SHORTCUT
|
2016-02-18 12:13:49 +00:00
|
|
|
key_recorder.storeKey(int(msg.wParam), a, state, text);
|
2018-11-06 10:03:42 +00:00
|
|
|
|
|
|
|
// QTBUG-71210
|
|
|
|
// VK_PACKET specifies multiple characters. The system only sends the first
|
|
|
|
// character of this sequence for each.
|
|
|
|
if (msg.wParam == VK_PACKET)
|
|
|
|
code = asciiToKeycode(char(uch.cell()), state);
|
|
|
|
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
|
2016-02-18 12:13:49 +00:00
|
|
|
modifiers, scancode, quint32(msg.wParam), nModifiers, text, false);
|
2011-08-17 13:58:39 +00:00
|
|
|
result =true;
|
|
|
|
bool store = true;
|
|
|
|
// Alt+<alphanumerical> go to the Win32 menu system if unhandled by Qt
|
|
|
|
if (msgType == WM_SYSKEYDOWN && !result && a) {
|
|
|
|
HWND parent = GetParent(QWindowsWindow::handleOf(receiver));
|
|
|
|
while (parent) {
|
|
|
|
if (GetMenu(parent)) {
|
|
|
|
SendMessage(parent, WM_SYSCOMMAND, SC_KEYMENU, a);
|
|
|
|
store = false;
|
|
|
|
result = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
parent = GetParent(parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!store)
|
2016-02-18 12:13:49 +00:00
|
|
|
key_recorder.findKey(int(msg.wParam), true);
|
2011-08-17 13:58:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// KEYUP -----------------------------------------------------------------------------------
|
|
|
|
else {
|
|
|
|
// Try to locate the key in our records, and remove it if it exists.
|
|
|
|
// The key may not be in our records if, for example, the down event was handled by
|
|
|
|
// win32 natively, or our window gets focus while a key is already press, but now gets
|
|
|
|
// the key release event.
|
2016-02-18 12:13:49 +00:00
|
|
|
const KeyRecord *rec = key_recorder.findKey(int(msg.wParam), true);
|
2011-08-17 13:58:39 +00:00
|
|
|
if (!rec && !(code == Qt::Key_Shift
|
|
|
|
|| code == Qt::Key_Control
|
|
|
|
|| code == Qt::Key_Meta
|
|
|
|
|| code == Qt::Key_Alt)) {
|
2019-07-26 16:16:33 +00:00
|
|
|
|
|
|
|
// Workaround for QTBUG-77153:
|
|
|
|
// The Surface Pen eraser button generates Meta+F18/19/20 keystrokes,
|
|
|
|
// but when it is not touching the screen the Fn Down is eaten and only
|
|
|
|
// a Fn Up with the previous state as "not pressed" is generated, which
|
|
|
|
// would be ignored. We detect this case and synthesize the expected events.
|
|
|
|
if ((msg.lParam & 0x40000000) == 0 &&
|
|
|
|
Qt::KeyboardModifier(state) == Qt::NoModifier &&
|
|
|
|
((code == Qt::Key_F18) || (code == Qt::Key_F19) || (code == Qt::Key_F20))) {
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
|
2019-07-26 16:16:33 +00:00
|
|
|
Qt::MetaModifier, scancode,
|
|
|
|
quint32(msg.wParam), MetaLeft);
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
|
2019-07-26 16:16:33 +00:00
|
|
|
Qt::NoModifier, scancode,
|
|
|
|
quint32(msg.wParam), 0);
|
|
|
|
result = true;
|
|
|
|
}
|
2011-08-17 13:58:39 +00:00
|
|
|
} else {
|
|
|
|
if (!code)
|
2016-02-18 12:13:49 +00:00
|
|
|
code = asciiToKeycode(rec->ascii ? char(rec->ascii) : char(msg.wParam), state);
|
2011-08-17 13:58:39 +00:00
|
|
|
|
|
|
|
// Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
|
|
|
|
if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
|
|
|
|
code = Qt::Key_Backtab;
|
Windows: use MSG timestamps for input events
Input events have a timestamp. When dispatching an event through QPA, a
platform plugin can either provide it, or QPA will use an internal
QElapsedTimer to provide a timestamp.
Windows input messages do come with a timestamp already, so we can use
that instead of the QPA.
The two methods are not equivalent.
For instance: for various reasons, Qt does not honor Windows' "double
clicked" message, but uses the delta between two mouse events to
establish if the second click is actually a double click.
Now suppose that the user double clicks on a widget. On the first click,
the application does something that freezes it for a bit (e.g. some
heavy repainting or whatever). Does the second click register as a
double click or not?
* If we're using Qt's own timer, the answer is NO; the event is pulled
from the WM queue after the freeze, given a timestamp far away from
the last click, and so it will be deemed another single click
* If we use the OS' timestamps, then the second click will be seen as
"close" to the first click, and correctly registered as second click.
This reasoning can be extended to many other QPA events, but looks like
the APIs for some are missing (e.g. enter events), so I'm not tackling
them here.
This commit reverts ade96ff6446d4b0977d4e5f03b96b77ff8223b8b.
Task-number: QTBUG-109833
Task-number: QTBUG-122226
Change-Id: I5f3fba029b44c618ef622bacdc4bcc3928e04867
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
2023-01-05 02:46:45 +00:00
|
|
|
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
|
2016-02-18 12:13:49 +00:00
|
|
|
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam),
|
|
|
|
nModifiers,
|
2013-05-28 11:31:55 +00:00
|
|
|
(rec ? rec->text : QString()), false);
|
2011-08-17 13:58:39 +00:00
|
|
|
result = true;
|
|
|
|
// don't pass Alt to Windows unless we are embedded in a non-Qt window
|
|
|
|
if (code == Qt::Key_Alt) {
|
|
|
|
const QWindowsContext *context = QWindowsContext::instance();
|
|
|
|
HWND parent = GetParent(QWindowsWindow::handleOf(receiver));
|
|
|
|
while (parent) {
|
|
|
|
if (!context->findPlatformWindow(parent) && GetMenu(parent)) {
|
|
|
|
result = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
parent = GetParent(parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-09-20 23:26:00 +00:00
|
|
|
Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() const
|
2012-03-12 16:16:35 +00:00
|
|
|
{
|
|
|
|
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
|
|
|
|
if (GetKeyState(VK_SHIFT) < 0)
|
|
|
|
modifiers |= Qt::ShiftModifier;
|
|
|
|
if (GetKeyState(VK_CONTROL) < 0)
|
|
|
|
modifiers |= Qt::ControlModifier;
|
|
|
|
if (GetKeyState(VK_MENU) < 0)
|
|
|
|
modifiers |= Qt::AltModifier;
|
2014-12-08 08:18:39 +00:00
|
|
|
if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0)
|
|
|
|
modifiers |= Qt::MetaModifier;
|
2012-03-12 16:16:35 +00:00
|
|
|
return modifiers;
|
|
|
|
}
|
|
|
|
|
2023-09-20 23:26:00 +00:00
|
|
|
QList<QKeyCombination> QWindowsKeyMapper::possibleKeyCombinations(const QKeyEvent *e) const
|
2012-10-11 05:42:08 +00:00
|
|
|
{
|
2023-09-20 23:26:00 +00:00
|
|
|
QList<QKeyCombination> result;
|
2012-10-11 05:42:08 +00:00
|
|
|
|
2016-03-09 22:16:29 +00:00
|
|
|
|
|
|
|
const quint32 nativeVirtualKey = e->nativeVirtualKey();
|
|
|
|
if (nativeVirtualKey > 255)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
const KeyboardLayoutItem &kbItem = keyLayout[nativeVirtualKey];
|
2012-10-10 16:27:04 +00:00
|
|
|
if (!kbItem.exists)
|
2012-10-11 05:42:08 +00:00
|
|
|
return result;
|
|
|
|
|
2012-10-10 16:27:04 +00:00
|
|
|
quint32 baseKey = kbItem.qtKey[0];
|
2012-10-11 05:42:08 +00:00
|
|
|
Qt::KeyboardModifiers keyMods = e->modifiers();
|
|
|
|
if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) {
|
2023-09-20 23:26:00 +00:00
|
|
|
result << (Qt::Key_Enter | keyMods);
|
2012-10-11 05:42:08 +00:00
|
|
|
return result;
|
|
|
|
}
|
2023-09-20 23:26:00 +00:00
|
|
|
|
2024-09-30 13:06:31 +00:00
|
|
|
// If Key_Tab+Shift is pressed we add Key_Backtab without
|
|
|
|
// shift modifier as a possible combination too
|
|
|
|
if (baseKey == Qt::Key_Tab && (keyMods & Qt::ShiftModifier))
|
|
|
|
result << (Qt::Key_Backtab | (keyMods & ~Qt::ShiftModifier));
|
|
|
|
|
2023-09-20 23:26:00 +00:00
|
|
|
// The base key is _always_ valid, of course
|
|
|
|
result << QKeyCombination::fromCombined(int(baseKey) + int(keyMods));
|
2012-10-11 05:42:08 +00:00
|
|
|
|
2013-01-18 11:15:00 +00:00
|
|
|
for (size_t i = 1; i < NumMods; ++i) {
|
2012-10-11 05:42:08 +00:00
|
|
|
Qt::KeyboardModifiers neededMods = ModsTbl[i];
|
2012-10-10 16:27:04 +00:00
|
|
|
quint32 key = kbItem.qtKey[i];
|
2018-03-27 15:03:18 +00:00
|
|
|
if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) {
|
|
|
|
const Qt::KeyboardModifiers missingMods = keyMods & ~neededMods;
|
2023-09-20 23:26:00 +00:00
|
|
|
const auto matchedKey = QKeyCombination::fromCombined(int(key) + int(missingMods));
|
|
|
|
const auto it = std::find_if(result.begin(), result.end(),
|
|
|
|
[key](auto keyCombination) {
|
|
|
|
return keyCombination.key() == key;
|
|
|
|
});
|
2018-03-27 15:03:18 +00:00
|
|
|
// QTBUG-67200: Use the match with the least modifiers (prefer
|
|
|
|
// Shift+9 over Alt + Shift + 9) resulting in more missing modifiers.
|
|
|
|
if (it == result.end())
|
|
|
|
result << matchedKey;
|
2023-09-20 23:26:00 +00:00
|
|
|
else if (missingMods > it->keyboardModifiers())
|
2018-03-27 15:03:18 +00:00
|
|
|
*it = matchedKey;
|
|
|
|
}
|
2012-10-11 05:42:08 +00:00
|
|
|
}
|
2018-03-27 10:54:30 +00:00
|
|
|
qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey="
|
2019-04-30 10:51:36 +00:00
|
|
|
<< Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase
|
2023-09-20 23:26:00 +00:00
|
|
|
<< e->modifiers() << kbItem << "\n returns" << result;
|
2012-10-11 05:42:08 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-08-17 13:58:39 +00:00
|
|
|
QT_END_NAMESPACE
|