Bug 1293505 part.2 KeyboardLayout::SynthesizeNativeKeyEvent() should emulate WM_SYEKEYDOWN, WM_SYSCHAR, WM_SYSDEADCHAR and WM_SYSKEYUP correctly r?m_kato
The new test failure is caused by a bug of the test API, KeyboardLayout::SysnthesizeNativeKeyEvent(). It doesn't generate WM_SYSKEY* messages nor WM_SYS*CHAR messages when Alt key is pressed. Therefore, the new path in the previous code works unexpectedly with automated tests.
This patch makes KeyboardLayout::SysnthesizeNativeKeyEvent() WM_SYS* message aware. When Alt key is pressed (but Ctrl key is not pressed) and the Alt key + something doesn't cause text input, the API generates WM_SYS* messages as expected.
MozReview-Commit-ID: FLbe4SYEZLf
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -2550,16 +2550,43 @@ KeyboardLayout::IsDeadKey(uint8_t aVirtu
if (virtualKeyIndex < 0) {
return false;
}
return mVirtualKeys[virtualKeyIndex].IsDeadKey(
VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
}
+bool
+KeyboardLayout::IsSysKey(uint8_t aVirtualKey,
+ const ModifierKeyState& aModKeyState) const
+{
+ // If Alt key is not pressed, it's never a system key combination.
+ // Additionally, if Ctrl key is pressed, it's never a system key combination
+ // too.
+ if (!aModKeyState.IsAlt() || aModKeyState.IsControl()) {
+ return false;
+ }
+
+ int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
+ if (virtualKeyIndex < 0) {
+ return true;
+ }
+
+ UniCharsAndModifiers inputCharsAndModifiers =
+ GetUniCharsAndModifiers(aVirtualKey, aModKeyState);
+ if (inputCharsAndModifiers.IsEmpty()) {
+ return true;
+ }
+
+ // If the Alt key state isn't consumed, that means that the key with Alt
+ // doesn't cause text input. So, the combination is a system key.
+ return inputCharsAndModifiers.mModifiers[0] != MODIFIER_ALT;
+}
+
void
KeyboardLayout::InitNativeKey(NativeKey& aNativeKey,
const ModifierKeyState& aModKeyState)
{
if (mIsPendingToRestoreKeyboardLayout) {
LoadLayout(::GetKeyboardLayout(0));
}
@@ -3458,18 +3485,20 @@ KeyboardLayout::SynthesizeNativeKeyEvent
UINT scanCode =
ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
// Add extended key flag to the lParam for right control key and right alt
// key.
if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
lParam |= 0x1000000;
}
- MSG keyDownMsg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam,
- aWidget->GetWindowHandle());
+ bool makeSysKeyMsg = IsSysKey(key, modKeyState);
+ MSG keyDownMsg =
+ WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYDOWN : WM_KEYDOWN,
+ key, lParam, aWidget->GetWindowHandle());
if (i == keySequence.Length() - 1) {
bool makeDeadCharMsg =
(IsDeadKey(key, modKeyState) && aCharacters.IsEmpty());
nsAutoString chars(aCharacters);
if (makeDeadCharMsg) {
UniCharsAndModifiers deadChars =
GetUniCharsAndModifiers(key, modKeyState);
chars = deadChars.ToString();
@@ -3480,16 +3509,17 @@ KeyboardLayout::SynthesizeNativeKeyEvent
NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
nativeKey.HandleKeyDownMessage();
} else {
AutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs;
for (uint32_t j = 0; j < chars.Length(); j++) {
NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement();
fakeCharMsg->mCharCode = chars.CharAt(j);
fakeCharMsg->mScanCode = scanCode;
+ fakeCharMsg->mIsSysKey = makeSysKeyMsg;
fakeCharMsg->mIsDeadKey = makeDeadCharMsg;
}
NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, 0, &fakeCharMsgs);
bool dispatched;
nativeKey.HandleKeyDownMessage(&dispatched);
// If some char messages are not consumed, let's emulate the widget
// receiving the message directly.
for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) {
@@ -3518,17 +3548,20 @@ KeyboardLayout::SynthesizeNativeKeyEvent
UINT scanCode =
ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
// Add extended key flag to the lParam for right control key and right alt
// key.
if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
lParam |= 0x1000000;
}
- MSG keyUpMsg = WinUtils::InitMSG(WM_KEYUP, key, lParam,
+ // Don't use WM_SYSKEYUP for Alt keyup.
+ bool makeSysKeyMsg = IsSysKey(key, modKeyState) && key != VK_MENU;
+ MSG keyUpMsg = WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYUP : WM_KEYUP,
+ key, lParam,
aWidget->GetWindowHandle());
NativeKey nativeKey(aWidget, keyUpMsg, modKeyState);
nativeKey.HandleKeyUpMessage();
}
// Restore old key state and layout
::SetKeyboardState(originalKbdState);
RestoreLayout();
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -175,29 +175,37 @@ class MOZ_STACK_CLASS NativeKey final
{
friend class KeyboardLayout;
public:
struct FakeCharMsg
{
UINT mCharCode;
UINT mScanCode;
+ bool mIsSysKey;
bool mIsDeadKey;
bool mConsumed;
- FakeCharMsg() :
- mCharCode(0), mScanCode(0), mIsDeadKey(false), mConsumed(false)
+ FakeCharMsg()
+ : mCharCode(0)
+ , mScanCode(0)
+ , mIsSysKey(false)
+ , mIsDeadKey(false)
+ , mConsumed(false)
{
}
MSG GetCharMsg(HWND aWnd) const
{
MSG msg;
msg.hwnd = aWnd;
- msg.message = mIsDeadKey ? WM_DEADCHAR : WM_CHAR;
+ msg.message = mIsDeadKey && mIsSysKey ? WM_SYSDEADCHAR :
+ mIsDeadKey ? WM_DEADCHAR :
+ mIsSysKey ? WM_SYSCHAR :
+ WM_CHAR;
msg.wParam = static_cast<WPARAM>(mCharCode);
msg.lParam = static_cast<LPARAM>(mScanCode << 16);
msg.time = 0;
msg.pt.x = msg.pt.y = 0;
return msg;
}
};
@@ -585,16 +593,23 @@ public:
/**
* IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState.
* This method isn't stateful.
*/
bool IsDeadKey(uint8_t aVirtualKey,
const ModifierKeyState& aModKeyState) const;
/**
+ * IsSysKey() returns true if aVirtualKey with aModKeyState causes WM_SYSKEY*
+ * or WM_SYS*CHAR messages.
+ */
+ bool IsSysKey(uint8_t aVirtualKey,
+ const ModifierKeyState& aModKeyState) const;
+
+ /**
* GetUniCharsAndModifiers() returns characters which is inputted by the
* aVirtualKey with aModKeyState. This method isn't stateful.
*/
UniCharsAndModifiers GetUniCharsAndModifiers(
uint8_t aVirtualKey,
const ModifierKeyState& aModKeyState) const;
/**