Fix virtual key mapping on MS Windows
In order to map MS Windows virtual keys to Qt keys without messing with dead keys now I use the built-in keyMap structure of QWindowsKeyMapper and assert every cell in the keymap is properly updated. In order to guarantee this even when the user changes the keyboard layout, WndProc now manages the WM_INPUTLANGCHANGE message, which is handled by QWindowsKeyMapper, resetting the layout structure. I don't fully understand yet some things about QWindowsKeyMapper, i.e. how QWindowsKeyMapper::updatePossibleKeyCodes workarounds the dead key issue with ToAscii; but it seems to work fine in all the tests I've done. Any further testing is highly appreciated, though. [ChangeLog][[QtGui][Platform Specific Changes][Windows] Fixed virtual key mapping on Windows. Task-number: QTBUG-33409 Task-number: QTBUG-8764 Task-number: QTBUG-10032 Change-Id: I4f7709a90906b03f4504deea1ff5c361e9f94b3f Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
parent
c3f6a5d93b
commit
fcebbaeba3
@ -831,6 +831,11 @@ QString decodeMSG(const MSG& msg)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WM_INPUTLANGCHANGE
|
||||||
|
case WM_INPUTLANGCHANGE:
|
||||||
|
parameters = QStringLiteral("Keyboard layout changed");
|
||||||
|
break;
|
||||||
|
#endif // WM_INPUTLANGCHANGE
|
||||||
#ifdef WM_NCACTIVATE
|
#ifdef WM_NCACTIVATE
|
||||||
case WM_NCACTIVATE:
|
case WM_NCACTIVATE:
|
||||||
{
|
{
|
||||||
|
@ -93,6 +93,7 @@ enum WindowsEventType // Simplify event types
|
|||||||
NonClientHitTest = NonClientEventFlag + 2,
|
NonClientHitTest = NonClientEventFlag + 2,
|
||||||
KeyEvent = KeyEventFlag + 1,
|
KeyEvent = KeyEventFlag + 1,
|
||||||
KeyDownEvent = KeyEventFlag + KeyDownEventFlag + 1,
|
KeyDownEvent = KeyEventFlag + KeyDownEventFlag + 1,
|
||||||
|
KeyboardLayoutChangeEvent = KeyEventFlag + 2,
|
||||||
InputMethodKeyEvent = InputMethodEventFlag + KeyEventFlag + 1,
|
InputMethodKeyEvent = InputMethodEventFlag + KeyEventFlag + 1,
|
||||||
InputMethodKeyDownEvent = InputMethodEventFlag + KeyEventFlag + KeyDownEventFlag + 1,
|
InputMethodKeyDownEvent = InputMethodEventFlag + KeyEventFlag + KeyDownEventFlag + 1,
|
||||||
ClipboardEvent = ClipboardEventFlag + 1,
|
ClipboardEvent = ClipboardEventFlag + 1,
|
||||||
@ -165,6 +166,10 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
|
|||||||
return QtWindows::InputMethodKeyEvent;
|
return QtWindows::InputMethodKeyEvent;
|
||||||
case WM_IME_KEYDOWN:
|
case WM_IME_KEYDOWN:
|
||||||
return QtWindows::InputMethodKeyDownEvent;
|
return QtWindows::InputMethodKeyDownEvent;
|
||||||
|
#ifdef WM_INPUTLANGCHANGE
|
||||||
|
case WM_INPUTLANGCHANGE:
|
||||||
|
return QtWindows::KeyboardLayoutChangeEvent;
|
||||||
|
#endif // WM_INPUTLANGCHANGE
|
||||||
case WM_TOUCH:
|
case WM_TOUCH:
|
||||||
return QtWindows::TouchEvent;
|
return QtWindows::TouchEvent;
|
||||||
case WM_CHANGECBCHAIN:
|
case WM_CHANGECBCHAIN:
|
||||||
|
@ -842,6 +842,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
|||||||
case QtWindows::KeyEvent:
|
case QtWindows::KeyEvent:
|
||||||
case QtWindows::InputMethodKeyEvent:
|
case QtWindows::InputMethodKeyEvent:
|
||||||
case QtWindows::InputMethodKeyDownEvent:
|
case QtWindows::InputMethodKeyDownEvent:
|
||||||
|
case QtWindows::KeyboardLayoutChangeEvent:
|
||||||
#if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER)
|
#if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER)
|
||||||
return platformSessionManager()->isInterractionBlocked() ? true : d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
|
return platformSessionManager()->isInterractionBlocked() ? true : d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
|
||||||
#else
|
#else
|
||||||
|
@ -574,7 +574,7 @@ void QWindowsKeyMapper::updateKeyMap(const MSG &msg)
|
|||||||
{
|
{
|
||||||
unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
|
unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
|
||||||
GetKeyboardState(kbdBuffer);
|
GetKeyboardState(kbdBuffer);
|
||||||
quint32 scancode = (msg.lParam >> 16) & 0xfff;
|
const quint32 scancode = (msg.lParam >> 16) & 0xff;
|
||||||
updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam);
|
updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -742,12 +742,21 @@ bool QWindowsKeyMapper::translateKeyEvent(QWindow *widget, HWND hwnd,
|
|||||||
const MSG &msg, LRESULT *result)
|
const MSG &msg, LRESULT *result)
|
||||||
{
|
{
|
||||||
*result = 0;
|
*result = 0;
|
||||||
|
|
||||||
|
// Reset layout map when system keyboard layout is changed
|
||||||
|
if (msg.message == WM_INPUTLANGCHANGE) {
|
||||||
|
deleteLayouts();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this key to the keymap if it is not present yet.
|
||||||
|
updateKeyMap(msg);
|
||||||
|
|
||||||
MSG peekedMsg;
|
MSG peekedMsg;
|
||||||
// consume dead chars?(for example, typing '`','a' resulting in a-accent).
|
// consume dead chars?(for example, typing '`','a' resulting in a-accent).
|
||||||
if (PeekMessage(&peekedMsg, hwnd, 0, 0, PM_NOREMOVE) && peekedMsg.message == WM_DEADCHAR)
|
if (PeekMessage(&peekedMsg, hwnd, 0, 0, PM_NOREMOVE) && peekedMsg.message == WM_DEADCHAR)
|
||||||
return true;
|
return true;
|
||||||
if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
|
|
||||||
updateKeyMap(msg);
|
|
||||||
return translateKeyEventInternal(widget, msg, false);
|
return translateKeyEventInternal(widget, msg, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,9 +764,8 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms
|
|||||||
{
|
{
|
||||||
const int msgType = msg.message;
|
const int msgType = msg.message;
|
||||||
|
|
||||||
const quint32 scancode = (msg.lParam >> 16) & 0xfff;
|
const quint32 scancode = (msg.lParam >> 16) & 0xff;
|
||||||
const quint32 vk_key = MapVirtualKey(scancode, 1);
|
const quint32 vk_key = msg.wParam;
|
||||||
const bool isNumpad = (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9);
|
|
||||||
quint32 nModifiers = 0;
|
quint32 nModifiers = 0;
|
||||||
|
|
||||||
QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
|
QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
|
||||||
@ -786,10 +794,6 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms
|
|||||||
state |= (nModifiers & AltAny ? int(Qt::AltModifier) : 0);
|
state |= (nModifiers & AltAny ? int(Qt::AltModifier) : 0);
|
||||||
state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0);
|
state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0);
|
||||||
|
|
||||||
// Now we know enough to either have MapVirtualKey or our own keymap tell us if it's a deadkey
|
|
||||||
const bool isDeadKey = isADeadKey(msg.wParam, state)
|
|
||||||
|| MapVirtualKey(msg.wParam, 2) & 0x80000000;
|
|
||||||
|
|
||||||
// A multi-character key or a Input method character
|
// A multi-character key or a Input method character
|
||||||
// not found by our look-ahead
|
// not found by our look-ahead
|
||||||
if (msgType == WM_CHAR || msgType == WM_IME_CHAR) {
|
if (msgType == WM_CHAR || msgType == WM_IME_CHAR) {
|
||||||
@ -849,23 +853,12 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Translate VK_* (native) -> Key_* (Qt) keys
|
// Translate VK_* (native) -> Key_* (Qt) keys
|
||||||
// If it's a dead key, we cannot use the toKeyOrUnicode() function, since that will change
|
int modifiersIndex = 0;
|
||||||
// the internal state of the keyboard driver, resulting in that dead keys no longer works.
|
modifiersIndex |= (nModifiers & ShiftAny ? 0x1 : 0);
|
||||||
// ..also if we're typing numbers on the keypad, while holding down the Alt modifier.
|
modifiersIndex |= (nModifiers & ControlAny ? 0x2 : 0);
|
||||||
int code = 0;
|
modifiersIndex |= (nModifiers & AltAny ? 0x4 : 0);
|
||||||
if (isNumpad && (nModifiers & AltAny)) {
|
|
||||||
code = winceKeyBend(msg.wParam);
|
|
||||||
} else if (!isDeadKey) {
|
|
||||||
// QTBUG-8764, QTBUG-10032
|
|
||||||
// Can't call toKeyOrUnicode because that would call ToUnicode, and, if a dead key
|
|
||||||
// is pressed at the moment, Windows would NOT use it to compose a character for the next
|
|
||||||
// WM_CHAR event.
|
|
||||||
|
|
||||||
// Instead, use MapVirtualKey, which will provide adequate values.
|
int code = keyLayout[vk_key].qtKey[modifiersIndex];
|
||||||
code = MapVirtualKey(msg.wParam, MAPVK_VK_TO_CHAR);
|
|
||||||
if (code < 0x20 || code == 0x7f) // The same logic as in toKeyOrUnicode()
|
|
||||||
code = winceKeyBend(msg.wParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invert state logic:
|
// Invert state logic:
|
||||||
// If the key actually pressed is a modifier key, then we remove its modifier key from the
|
// If the key actually pressed is a modifier key, then we remove its modifier key from the
|
||||||
|
Loading…
Reference in New Issue
Block a user