michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: michael@0: #include "KeyboardLayout.h" michael@0: #include "nsIMM32Handler.h" michael@0: michael@0: #include "nsMemory.h" michael@0: #include "nsToolkit.h" michael@0: #include "nsQuickSort.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "WidgetUtils.h" michael@0: #include "WinUtils.h" michael@0: #include "nsWindowDbg.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsPrintfCString.h" michael@0: michael@0: #include "nsIDOMKeyEvent.h" michael@0: #include "nsIIdleServiceInternal.h" michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: #include "nsExceptionHandler.h" michael@0: #endif michael@0: michael@0: #include "npapi.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifndef WINABLEAPI michael@0: #include michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: // Unique id counter associated with a keydown / keypress events. Used in michael@0: // identifing keypress events for removal from async event dispatch queue michael@0: // in metrofx after preventDefault is called on keydown events. michael@0: static uint32_t sUniqueKeyEventId = 0; michael@0: michael@0: struct DeadKeyEntry michael@0: { michael@0: char16_t BaseChar; michael@0: char16_t CompositeChar; michael@0: }; michael@0: michael@0: michael@0: class DeadKeyTable michael@0: { michael@0: friend class KeyboardLayout; michael@0: michael@0: uint16_t mEntries; michael@0: // KeyboardLayout::AddDeadKeyTable() will allocate as many entries as michael@0: // required. It is the only way to create new DeadKeyTable instances. michael@0: DeadKeyEntry mTable[1]; michael@0: michael@0: void Init(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) michael@0: { michael@0: mEntries = aEntries; michael@0: memcpy(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry)); michael@0: } michael@0: michael@0: static uint32_t SizeInBytes(uint32_t aEntries) michael@0: { michael@0: return offsetof(DeadKeyTable, mTable) + aEntries * sizeof(DeadKeyEntry); michael@0: } michael@0: michael@0: public: michael@0: uint32_t Entries() const michael@0: { michael@0: return mEntries; michael@0: } michael@0: michael@0: bool IsEqual(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const michael@0: { michael@0: return (mEntries == aEntries && michael@0: !memcmp(mTable, aDeadKeyArray, michael@0: aEntries * sizeof(DeadKeyEntry))); michael@0: } michael@0: michael@0: char16_t GetCompositeChar(char16_t aBaseChar) const; michael@0: }; michael@0: michael@0: michael@0: /***************************************************************************** michael@0: * mozilla::widget::ModifierKeyState michael@0: *****************************************************************************/ michael@0: michael@0: ModifierKeyState::ModifierKeyState() michael@0: { michael@0: Update(); michael@0: } michael@0: michael@0: ModifierKeyState::ModifierKeyState(bool aIsShiftDown, michael@0: bool aIsControlDown, michael@0: bool aIsAltDown) michael@0: { michael@0: Update(); michael@0: Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH); michael@0: Modifiers modifiers = 0; michael@0: if (aIsShiftDown) { michael@0: modifiers |= MODIFIER_SHIFT; michael@0: } michael@0: if (aIsControlDown) { michael@0: modifiers |= MODIFIER_CONTROL; michael@0: } michael@0: if (aIsAltDown) { michael@0: modifiers |= MODIFIER_ALT; michael@0: } michael@0: if (modifiers) { michael@0: Set(modifiers); michael@0: } michael@0: } michael@0: michael@0: ModifierKeyState::ModifierKeyState(Modifiers aModifiers) : michael@0: mModifiers(aModifiers) michael@0: { michael@0: EnsureAltGr(); michael@0: } michael@0: michael@0: void michael@0: ModifierKeyState::Update() michael@0: { michael@0: mModifiers = 0; michael@0: if (IS_VK_DOWN(VK_SHIFT)) { michael@0: mModifiers |= MODIFIER_SHIFT; michael@0: } michael@0: if (IS_VK_DOWN(VK_CONTROL)) { michael@0: mModifiers |= MODIFIER_CONTROL; michael@0: } michael@0: if (IS_VK_DOWN(VK_MENU)) { michael@0: mModifiers |= MODIFIER_ALT; michael@0: } michael@0: if (IS_VK_DOWN(VK_LWIN) || IS_VK_DOWN(VK_RWIN)) { michael@0: mModifiers |= MODIFIER_OS; michael@0: } michael@0: if (::GetKeyState(VK_CAPITAL) & 1) { michael@0: mModifiers |= MODIFIER_CAPSLOCK; michael@0: } michael@0: if (::GetKeyState(VK_NUMLOCK) & 1) { michael@0: mModifiers |= MODIFIER_NUMLOCK; michael@0: } michael@0: if (::GetKeyState(VK_SCROLL) & 1) { michael@0: mModifiers |= MODIFIER_SCROLLLOCK; michael@0: } michael@0: michael@0: EnsureAltGr(); michael@0: } michael@0: michael@0: void michael@0: ModifierKeyState::Unset(Modifiers aRemovingModifiers) michael@0: { michael@0: mModifiers &= ~aRemovingModifiers; michael@0: // Note that we don't need to unset AltGr flag here automatically. michael@0: // For nsEditor, we need to remove Alt and Control flags but AltGr isn't michael@0: // checked in nsEditor, so, it can be kept. michael@0: } michael@0: michael@0: void michael@0: ModifierKeyState::Set(Modifiers aAddingModifiers) michael@0: { michael@0: mModifiers |= aAddingModifiers; michael@0: EnsureAltGr(); michael@0: } michael@0: michael@0: void michael@0: ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const michael@0: { michael@0: aInputEvent.modifiers = mModifiers; michael@0: michael@0: switch(aInputEvent.eventStructType) { michael@0: case NS_MOUSE_EVENT: michael@0: case NS_MOUSE_SCROLL_EVENT: michael@0: case NS_WHEEL_EVENT: michael@0: case NS_DRAG_EVENT: michael@0: case NS_SIMPLE_GESTURE_EVENT: michael@0: InitMouseEvent(aInputEvent); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const michael@0: { michael@0: NS_ASSERTION(aMouseEvent.eventStructType == NS_MOUSE_EVENT || michael@0: aMouseEvent.eventStructType == NS_WHEEL_EVENT || michael@0: aMouseEvent.eventStructType == NS_DRAG_EVENT || michael@0: aMouseEvent.eventStructType == NS_SIMPLE_GESTURE_EVENT, michael@0: "called with non-mouse event"); michael@0: michael@0: if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { michael@0: // Buttons for immersive mode are handled in MetroInput. michael@0: return; michael@0: } michael@0: michael@0: WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase(); michael@0: mouseEvent.buttons = 0; michael@0: if (::GetKeyState(VK_LBUTTON) < 0) { michael@0: mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag; michael@0: } michael@0: if (::GetKeyState(VK_RBUTTON) < 0) { michael@0: mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag; michael@0: } michael@0: if (::GetKeyState(VK_MBUTTON) < 0) { michael@0: mouseEvent.buttons |= WidgetMouseEvent::eMiddleButtonFlag; michael@0: } michael@0: if (::GetKeyState(VK_XBUTTON1) < 0) { michael@0: mouseEvent.buttons |= WidgetMouseEvent::e4thButtonFlag; michael@0: } michael@0: if (::GetKeyState(VK_XBUTTON2) < 0) { michael@0: mouseEvent.buttons |= WidgetMouseEvent::e5thButtonFlag; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsShift() const michael@0: { michael@0: return (mModifiers & MODIFIER_SHIFT) != 0; michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsControl() const michael@0: { michael@0: return (mModifiers & MODIFIER_CONTROL) != 0; michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsAlt() const michael@0: { michael@0: return (mModifiers & MODIFIER_ALT) != 0; michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsAltGr() const michael@0: { michael@0: return IsControl() && IsAlt(); michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsWin() const michael@0: { michael@0: return (mModifiers & MODIFIER_OS) != 0; michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsCapsLocked() const michael@0: { michael@0: return (mModifiers & MODIFIER_CAPSLOCK) != 0; michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsNumLocked() const michael@0: { michael@0: return (mModifiers & MODIFIER_NUMLOCK) != 0; michael@0: } michael@0: michael@0: bool michael@0: ModifierKeyState::IsScrollLocked() const michael@0: { michael@0: return (mModifiers & MODIFIER_SCROLLLOCK) != 0; michael@0: } michael@0: michael@0: Modifiers michael@0: ModifierKeyState::GetModifiers() const michael@0: { michael@0: return mModifiers; michael@0: } michael@0: michael@0: void michael@0: ModifierKeyState::EnsureAltGr() michael@0: { michael@0: // If both Control key and Alt key are pressed, it means AltGr is pressed. michael@0: // Ideally, we should check whether the current keyboard layout has AltGr michael@0: // or not. However, setting AltGr flags for keyboard which doesn't have michael@0: // AltGr must not be serious bug. So, it should be OK for now. michael@0: if (IsAltGr()) { michael@0: mModifiers |= MODIFIER_ALTGRAPH; michael@0: } michael@0: } michael@0: michael@0: /***************************************************************************** michael@0: * mozilla::widget::UniCharsAndModifiers michael@0: *****************************************************************************/ michael@0: michael@0: void michael@0: UniCharsAndModifiers::Append(char16_t aUniChar, Modifiers aModifiers) michael@0: { michael@0: MOZ_ASSERT(mLength < 5); michael@0: mChars[mLength] = aUniChar; michael@0: mModifiers[mLength] = aModifiers; michael@0: mLength++; michael@0: } michael@0: michael@0: void michael@0: UniCharsAndModifiers::FillModifiers(Modifiers aModifiers) michael@0: { michael@0: for (uint32_t i = 0; i < mLength; i++) { michael@0: mModifiers[i] = aModifiers; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: UniCharsAndModifiers::UniCharsEqual(const UniCharsAndModifiers& aOther) const michael@0: { michael@0: if (mLength != aOther.mLength) { michael@0: return false; michael@0: } michael@0: return !memcmp(mChars, aOther.mChars, mLength * sizeof(char16_t)); michael@0: } michael@0: michael@0: bool michael@0: UniCharsAndModifiers::UniCharsCaseInsensitiveEqual( michael@0: const UniCharsAndModifiers& aOther) const michael@0: { michael@0: if (mLength != aOther.mLength) { michael@0: return false; michael@0: } michael@0: michael@0: nsCaseInsensitiveStringComparator comp; michael@0: return !comp(mChars, aOther.mChars, mLength, aOther.mLength); michael@0: } michael@0: michael@0: UniCharsAndModifiers& michael@0: UniCharsAndModifiers::operator+=(const UniCharsAndModifiers& aOther) michael@0: { michael@0: uint32_t copyCount = std::min(aOther.mLength, 5 - mLength); michael@0: NS_ENSURE_TRUE(copyCount > 0, *this); michael@0: memcpy(&mChars[mLength], aOther.mChars, copyCount * sizeof(char16_t)); michael@0: memcpy(&mModifiers[mLength], aOther.mModifiers, michael@0: copyCount * sizeof(Modifiers)); michael@0: mLength += copyCount; michael@0: return *this; michael@0: } michael@0: michael@0: UniCharsAndModifiers michael@0: UniCharsAndModifiers::operator+(const UniCharsAndModifiers& aOther) const michael@0: { michael@0: UniCharsAndModifiers result(*this); michael@0: result += aOther; michael@0: return result; michael@0: } michael@0: michael@0: /***************************************************************************** michael@0: * mozilla::widget::VirtualKey michael@0: *****************************************************************************/ michael@0: michael@0: // static michael@0: VirtualKey::ShiftState michael@0: VirtualKey::ModifiersToShiftState(Modifiers aModifiers) michael@0: { michael@0: ShiftState state = 0; michael@0: if (aModifiers & MODIFIER_SHIFT) { michael@0: state |= STATE_SHIFT; michael@0: } michael@0: if (aModifiers & MODIFIER_CONTROL) { michael@0: state |= STATE_CONTROL; michael@0: } michael@0: if (aModifiers & MODIFIER_ALT) { michael@0: state |= STATE_ALT; michael@0: } michael@0: if (aModifiers & MODIFIER_CAPSLOCK) { michael@0: state |= STATE_CAPSLOCK; michael@0: } michael@0: return state; michael@0: } michael@0: michael@0: // static michael@0: Modifiers michael@0: VirtualKey::ShiftStateToModifiers(ShiftState aShiftState) michael@0: { michael@0: Modifiers modifiers = 0; michael@0: if (aShiftState & STATE_SHIFT) { michael@0: modifiers |= MODIFIER_SHIFT; michael@0: } michael@0: if (aShiftState & STATE_CONTROL) { michael@0: modifiers |= MODIFIER_CONTROL; michael@0: } michael@0: if (aShiftState & STATE_ALT) { michael@0: modifiers |= MODIFIER_ALT; michael@0: } michael@0: if (aShiftState & STATE_CAPSLOCK) { michael@0: modifiers |= MODIFIER_CAPSLOCK; michael@0: } michael@0: if ((modifiers & (MODIFIER_ALT | MODIFIER_CONTROL)) == michael@0: (MODIFIER_ALT | MODIFIER_CONTROL)) { michael@0: modifiers |= MODIFIER_ALTGRAPH; michael@0: } michael@0: return modifiers; michael@0: } michael@0: michael@0: inline char16_t michael@0: VirtualKey::GetCompositeChar(ShiftState aShiftState, char16_t aBaseChar) const michael@0: { michael@0: return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar); michael@0: } michael@0: michael@0: const DeadKeyTable* michael@0: VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, michael@0: uint32_t aEntries) const michael@0: { michael@0: if (!mIsDeadKey) { michael@0: return nullptr; michael@0: } michael@0: michael@0: for (ShiftState shiftState = 0; shiftState < 16; shiftState++) { michael@0: if (!IsDeadKey(shiftState)) { michael@0: continue; michael@0: } michael@0: const DeadKeyTable* dkt = mShiftStates[shiftState].DeadKey.Table; michael@0: if (dkt && dkt->IsEqual(aDeadKeyArray, aEntries)) { michael@0: return dkt; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: VirtualKey::SetNormalChars(ShiftState aShiftState, michael@0: const char16_t* aChars, michael@0: uint32_t aNumOfChars) michael@0: { michael@0: NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index"); michael@0: michael@0: SetDeadKey(aShiftState, false); michael@0: michael@0: for (uint32_t index = 0; index < aNumOfChars; index++) { michael@0: // Ignore legacy non-printable control characters michael@0: mShiftStates[aShiftState].Normal.Chars[index] = michael@0: (aChars[index] >= 0x20) ? aChars[index] : 0; michael@0: } michael@0: michael@0: uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars); michael@0: for (uint32_t index = aNumOfChars; index < len; index++) { michael@0: mShiftStates[aShiftState].Normal.Chars[index] = 0; michael@0: } michael@0: } michael@0: michael@0: void michael@0: VirtualKey::SetDeadChar(ShiftState aShiftState, char16_t aDeadChar) michael@0: { michael@0: NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index"); michael@0: michael@0: SetDeadKey(aShiftState, true); michael@0: michael@0: mShiftStates[aShiftState].DeadKey.DeadChar = aDeadChar; michael@0: mShiftStates[aShiftState].DeadKey.Table = nullptr; michael@0: } michael@0: michael@0: UniCharsAndModifiers michael@0: VirtualKey::GetUniChars(ShiftState aShiftState) const michael@0: { michael@0: UniCharsAndModifiers result = GetNativeUniChars(aShiftState); michael@0: michael@0: const ShiftState STATE_ALT_CONTROL = (STATE_ALT | STATE_CONTROL); michael@0: if (!(aShiftState & STATE_ALT_CONTROL)) { michael@0: return result; michael@0: } michael@0: michael@0: if (!result.mLength) { michael@0: result = GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL); michael@0: result.FillModifiers(ShiftStateToModifiers(aShiftState)); michael@0: return result; michael@0: } michael@0: michael@0: if ((aShiftState & STATE_ALT_CONTROL) == STATE_ALT_CONTROL) { michael@0: // Even if the shifted chars and the unshifted chars are same, we michael@0: // should consume the Alt key state and the Ctrl key state when michael@0: // AltGr key is pressed. Because if we don't consume them, the input michael@0: // events are ignored on nsEditor. (I.e., Users cannot input the michael@0: // characters with this key combination.) michael@0: Modifiers finalModifiers = ShiftStateToModifiers(aShiftState); michael@0: finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL); michael@0: result.FillModifiers(finalModifiers); michael@0: return result; michael@0: } michael@0: michael@0: UniCharsAndModifiers unmodifiedReslt = michael@0: GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL); michael@0: if (!result.UniCharsEqual(unmodifiedReslt)) { michael@0: // Otherwise, we should consume the Alt key state and the Ctrl key state michael@0: // only when the shifted chars and unshifted chars are different. michael@0: Modifiers finalModifiers = ShiftStateToModifiers(aShiftState); michael@0: finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL); michael@0: result.FillModifiers(finalModifiers); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: michael@0: UniCharsAndModifiers michael@0: VirtualKey::GetNativeUniChars(ShiftState aShiftState) const michael@0: { michael@0: #ifdef DEBUG michael@0: if (aShiftState < 0 || aShiftState >= ArrayLength(mShiftStates)) { michael@0: nsPrintfCString warning("Shift state is out of range: " michael@0: "aShiftState=%d, ArrayLength(mShiftState)=%d", michael@0: aShiftState, ArrayLength(mShiftStates)); michael@0: NS_WARNING(warning.get()); michael@0: } michael@0: #endif michael@0: michael@0: UniCharsAndModifiers result; michael@0: Modifiers modifiers = ShiftStateToModifiers(aShiftState); michael@0: if (IsDeadKey(aShiftState)) { michael@0: result.Append(mShiftStates[aShiftState].DeadKey.DeadChar, modifiers); michael@0: return result; michael@0: } michael@0: michael@0: uint32_t index; michael@0: uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars); michael@0: for (index = 0; michael@0: index < len && mShiftStates[aShiftState].Normal.Chars[index]; index++) { michael@0: result.Append(mShiftStates[aShiftState].Normal.Chars[index], modifiers); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: VirtualKey::FillKbdState(PBYTE aKbdState, michael@0: const ShiftState aShiftState) michael@0: { michael@0: NS_ASSERTION(aShiftState < 16, "aShiftState out of range"); michael@0: michael@0: if (aShiftState & STATE_SHIFT) { michael@0: aKbdState[VK_SHIFT] |= 0x80; michael@0: } else { michael@0: aKbdState[VK_SHIFT] &= ~0x80; michael@0: aKbdState[VK_LSHIFT] &= ~0x80; michael@0: aKbdState[VK_RSHIFT] &= ~0x80; michael@0: } michael@0: michael@0: if (aShiftState & STATE_CONTROL) { michael@0: aKbdState[VK_CONTROL] |= 0x80; michael@0: } else { michael@0: aKbdState[VK_CONTROL] &= ~0x80; michael@0: aKbdState[VK_LCONTROL] &= ~0x80; michael@0: aKbdState[VK_RCONTROL] &= ~0x80; michael@0: } michael@0: michael@0: if (aShiftState & STATE_ALT) { michael@0: aKbdState[VK_MENU] |= 0x80; michael@0: } else { michael@0: aKbdState[VK_MENU] &= ~0x80; michael@0: aKbdState[VK_LMENU] &= ~0x80; michael@0: aKbdState[VK_RMENU] &= ~0x80; michael@0: } michael@0: michael@0: if (aShiftState & STATE_CAPSLOCK) { michael@0: aKbdState[VK_CAPITAL] |= 0x01; michael@0: } else { michael@0: aKbdState[VK_CAPITAL] &= ~0x01; michael@0: } michael@0: } michael@0: michael@0: /***************************************************************************** michael@0: * mozilla::widget::NativeKey michael@0: *****************************************************************************/ michael@0: michael@0: NativeKey::NativeKey(nsWindowBase* aWidget, michael@0: const MSG& aKeyOrCharMessage, michael@0: const ModifierKeyState& aModKeyState, michael@0: nsTArray* aFakeCharMsgs) : michael@0: mWidget(aWidget), mMsg(aKeyOrCharMessage), mDOMKeyCode(0), michael@0: mModKeyState(aModKeyState), mVirtualKeyCode(0), mOriginalVirtualKeyCode(0), michael@0: mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ? michael@0: aFakeCharMsgs : nullptr) michael@0: { michael@0: MOZ_ASSERT(aWidget); michael@0: KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance(); michael@0: mKeyboardLayout = keyboardLayout->GetLayout(); michael@0: mScanCode = WinUtils::GetScanCode(mMsg.lParam); michael@0: mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam); michael@0: // On WinXP and WinServer2003, we cannot compute the virtual keycode for michael@0: // extended keys due to the API limitation. michael@0: bool canComputeVirtualKeyCodeFromScanCode = michael@0: (!mIsExtended || IsVistaOrLater()); michael@0: switch (mMsg.message) { michael@0: case WM_KEYDOWN: michael@0: case WM_SYSKEYDOWN: michael@0: case WM_KEYUP: michael@0: case WM_SYSKEYUP: { michael@0: // First, resolve the IME converted virtual keycode to its original michael@0: // keycode. michael@0: if (mMsg.wParam == VK_PROCESSKEY) { michael@0: mOriginalVirtualKeyCode = michael@0: static_cast(::ImmGetVirtualKey(mMsg.hwnd)); michael@0: } else { michael@0: mOriginalVirtualKeyCode = static_cast(mMsg.wParam); michael@0: } michael@0: michael@0: // Most keys are not distinguished as left or right keys. michael@0: bool isLeftRightDistinguishedKey = false; michael@0: michael@0: // mOriginalVirtualKeyCode must not distinguish left or right of michael@0: // Shift, Control or Alt. michael@0: switch (mOriginalVirtualKeyCode) { michael@0: case VK_SHIFT: michael@0: case VK_CONTROL: michael@0: case VK_MENU: michael@0: isLeftRightDistinguishedKey = true; michael@0: break; michael@0: case VK_LSHIFT: michael@0: case VK_RSHIFT: michael@0: mVirtualKeyCode = mOriginalVirtualKeyCode; michael@0: mOriginalVirtualKeyCode = VK_SHIFT; michael@0: isLeftRightDistinguishedKey = true; michael@0: break; michael@0: case VK_LCONTROL: michael@0: case VK_RCONTROL: michael@0: mVirtualKeyCode = mOriginalVirtualKeyCode; michael@0: mOriginalVirtualKeyCode = VK_CONTROL; michael@0: isLeftRightDistinguishedKey = true; michael@0: break; michael@0: case VK_LMENU: michael@0: case VK_RMENU: michael@0: mVirtualKeyCode = mOriginalVirtualKeyCode; michael@0: mOriginalVirtualKeyCode = VK_MENU; michael@0: isLeftRightDistinguishedKey = true; michael@0: break; michael@0: } michael@0: michael@0: // If virtual keycode (left-right distinguished keycode) is already michael@0: // computed, we don't need to do anymore. michael@0: if (mVirtualKeyCode) { michael@0: break; michael@0: } michael@0: michael@0: // If the keycode doesn't have LR distinguished keycode, we just set michael@0: // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute michael@0: // it from MapVirtualKeyEx() because the scan code might be wrong if michael@0: // the message is sent/posted by other application. Then, we will compute michael@0: // unexpected keycode from the scan code. michael@0: if (!isLeftRightDistinguishedKey) { michael@0: break; michael@0: } michael@0: michael@0: if (!canComputeVirtualKeyCodeFromScanCode) { michael@0: // The right control key and the right alt key are extended keys. michael@0: // Therefore, we never get VK_RCONTRL and VK_RMENU for the result of michael@0: // MapVirtualKeyEx() on WinXP or WinServer2003. michael@0: // michael@0: // If VK_CONTROL or VK_MENU key message is caused by an extended key, michael@0: // we should assume that the right key of them is pressed. michael@0: switch (mOriginalVirtualKeyCode) { michael@0: case VK_CONTROL: michael@0: mVirtualKeyCode = VK_RCONTROL; michael@0: break; michael@0: case VK_MENU: michael@0: mVirtualKeyCode = VK_RMENU; michael@0: break; michael@0: case VK_SHIFT: michael@0: // Neither left shift nor right shift is not an extended key, michael@0: // let's use VK_LSHIFT for invalid scan code. michael@0: mVirtualKeyCode = VK_LSHIFT; michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Unsupported mOriginalVirtualKeyCode"); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: NS_ASSERTION(!mVirtualKeyCode, michael@0: "mVirtualKeyCode has been computed already"); michael@0: michael@0: // Otherwise, compute the virtual keycode with MapVirtualKeyEx(). michael@0: mVirtualKeyCode = ComputeVirtualKeyCodeFromScanCodeEx(); michael@0: michael@0: // The result might be unexpected value due to the scan code is michael@0: // wrong. For example, any key messages can be generated by michael@0: // SendMessage() or PostMessage() from applications. So, it's possible michael@0: // failure. Then, let's respect the extended flag even if it might be michael@0: // set intentionally. michael@0: switch (mOriginalVirtualKeyCode) { michael@0: case VK_CONTROL: michael@0: if (mVirtualKeyCode != VK_LCONTROL && michael@0: mVirtualKeyCode != VK_RCONTROL) { michael@0: mVirtualKeyCode = mIsExtended ? VK_RCONTROL : VK_LCONTROL; michael@0: } michael@0: break; michael@0: case VK_MENU: michael@0: if (mVirtualKeyCode != VK_LMENU && mVirtualKeyCode != VK_RMENU) { michael@0: mVirtualKeyCode = mIsExtended ? VK_RMENU : VK_LMENU; michael@0: } michael@0: break; michael@0: case VK_SHIFT: michael@0: if (mVirtualKeyCode != VK_LSHIFT && mVirtualKeyCode != VK_RSHIFT) { michael@0: // Neither left shift nor right shift is not an extended key, michael@0: // let's use VK_LSHIFT for invalid scan code. michael@0: mVirtualKeyCode = VK_LSHIFT; michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Unsupported mOriginalVirtualKeyCode"); michael@0: } michael@0: break; michael@0: } michael@0: case WM_CHAR: michael@0: case WM_UNICHAR: michael@0: case WM_SYSCHAR: michael@0: // We cannot compute the virtual key code from WM_CHAR message on WinXP michael@0: // if it's caused by an extended key. michael@0: if (!canComputeVirtualKeyCodeFromScanCode) { michael@0: break; michael@0: } michael@0: mVirtualKeyCode = mOriginalVirtualKeyCode = michael@0: ComputeVirtualKeyCodeFromScanCodeEx(); michael@0: NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode"); michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Unsupported message"); michael@0: } michael@0: michael@0: if (!mVirtualKeyCode) { michael@0: mVirtualKeyCode = mOriginalVirtualKeyCode; michael@0: } michael@0: michael@0: mDOMKeyCode = michael@0: keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode); michael@0: mKeyNameIndex = michael@0: keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode); michael@0: michael@0: keyboardLayout->InitNativeKey(*this, mModKeyState); michael@0: michael@0: mIsDeadKey = michael@0: (IsFollowedByDeadCharMessage() || michael@0: keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState)); michael@0: mIsPrintableKey = KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::IsFollowedByDeadCharMessage() const michael@0: { michael@0: MSG nextMsg; michael@0: if (mFakeCharMsgs) { michael@0: nextMsg = mFakeCharMsgs->ElementAt(0).GetCharMsg(mMsg.hwnd); michael@0: } else { michael@0: if (!WinUtils::PeekMessage(&nextMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST, michael@0: PM_NOREMOVE | PM_NOYIELD)) { michael@0: return false; michael@0: } michael@0: } michael@0: return IsDeadCharMessage(nextMsg); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::IsIMEDoingKakuteiUndo() const michael@0: { michael@0: // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG: michael@0: // --------------------------------------------------------------------------- michael@0: // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1) michael@0: // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK michael@0: // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0) michael@0: // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF) michael@0: // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1) michael@0: // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001) michael@0: // --------------------------------------------------------------------------- michael@0: // This doesn't match usual key message pattern such as: michael@0: // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP michael@0: // See following bugs for the detail. michael@0: // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese) michael@0: // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English) michael@0: MSG startCompositionMsg, compositionMsg, charMsg; michael@0: return WinUtils::PeekMessage(&startCompositionMsg, mMsg.hwnd, michael@0: WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION, michael@0: PM_NOREMOVE | PM_NOYIELD) && michael@0: WinUtils::PeekMessage(&compositionMsg, mMsg.hwnd, WM_IME_COMPOSITION, michael@0: WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) && michael@0: WinUtils::PeekMessage(&charMsg, mMsg.hwnd, WM_CHAR, WM_CHAR, michael@0: PM_NOREMOVE | PM_NOYIELD) && michael@0: startCompositionMsg.wParam == 0x0 && michael@0: startCompositionMsg.lParam == 0x0 && michael@0: compositionMsg.wParam == 0x0 && michael@0: compositionMsg.lParam == 0x1BF && michael@0: charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 && michael@0: startCompositionMsg.time <= compositionMsg.time && michael@0: compositionMsg.time <= charMsg.time; michael@0: } michael@0: michael@0: UINT michael@0: NativeKey::GetScanCodeWithExtendedFlag() const michael@0: { michael@0: // MapVirtualKeyEx() has been improved for supporting extended keys since michael@0: // Vista. When we call it for mapping a scancode of an extended key and michael@0: // a virtual keycode, we need to add 0xE000 to the scancode. michael@0: // On Win XP and Win Server 2003, this doesn't support. On them, we have michael@0: // no way to get virtual keycodes from scancode of extended keys. michael@0: if (!mIsExtended || !IsVistaOrLater()) { michael@0: return mScanCode; michael@0: } michael@0: return (0xE000 | mScanCode); michael@0: } michael@0: michael@0: uint32_t michael@0: NativeKey::GetKeyLocation() const michael@0: { michael@0: switch (mVirtualKeyCode) { michael@0: case VK_LSHIFT: michael@0: case VK_LCONTROL: michael@0: case VK_LMENU: michael@0: case VK_LWIN: michael@0: return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT; michael@0: michael@0: case VK_RSHIFT: michael@0: case VK_RCONTROL: michael@0: case VK_RMENU: michael@0: case VK_RWIN: michael@0: return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT; michael@0: michael@0: case VK_RETURN: michael@0: // XXX This code assumes that all keyboard drivers use same mapping. michael@0: return !mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD : michael@0: nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; michael@0: michael@0: case VK_INSERT: michael@0: case VK_DELETE: michael@0: case VK_END: michael@0: case VK_DOWN: michael@0: case VK_NEXT: michael@0: case VK_LEFT: michael@0: case VK_CLEAR: michael@0: case VK_RIGHT: michael@0: case VK_HOME: michael@0: case VK_UP: michael@0: case VK_PRIOR: michael@0: // XXX This code assumes that all keyboard drivers use same mapping. michael@0: return mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD : michael@0: nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; michael@0: michael@0: // NumLock key isn't included due to IE9's behavior. michael@0: case VK_NUMPAD0: michael@0: case VK_NUMPAD1: michael@0: case VK_NUMPAD2: michael@0: case VK_NUMPAD3: michael@0: case VK_NUMPAD4: michael@0: case VK_NUMPAD5: michael@0: case VK_NUMPAD6: michael@0: case VK_NUMPAD7: michael@0: case VK_NUMPAD8: michael@0: case VK_NUMPAD9: michael@0: case VK_DECIMAL: michael@0: case VK_DIVIDE: michael@0: case VK_MULTIPLY: michael@0: case VK_SUBTRACT: michael@0: case VK_ADD: michael@0: // Separator key of Brazilian keyboard or JIS keyboard for Mac michael@0: case VK_ABNT_C2: michael@0: return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; michael@0: michael@0: case VK_SHIFT: michael@0: case VK_CONTROL: michael@0: case VK_MENU: michael@0: NS_WARNING("Failed to decide the key location?"); michael@0: michael@0: default: michael@0: return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD; michael@0: } michael@0: } michael@0: michael@0: uint8_t michael@0: NativeKey::ComputeVirtualKeyCodeFromScanCode() const michael@0: { michael@0: return static_cast( michael@0: ::MapVirtualKeyEx(mScanCode, MAPVK_VSC_TO_VK, mKeyboardLayout)); michael@0: } michael@0: michael@0: uint8_t michael@0: NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const michael@0: { michael@0: // NOTE: WinXP doesn't support mapping scan code to virtual keycode of michael@0: // extended keys. michael@0: NS_ENSURE_TRUE(!mIsExtended || IsVistaOrLater(), 0); michael@0: return static_cast( michael@0: ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX, michael@0: mKeyboardLayout)); michael@0: } michael@0: michael@0: char16_t michael@0: NativeKey::ComputeUnicharFromScanCode() const michael@0: { michael@0: return static_cast( michael@0: ::MapVirtualKeyEx(ComputeVirtualKeyCodeFromScanCode(), michael@0: MAPVK_VK_TO_CHAR, mKeyboardLayout)); michael@0: } michael@0: michael@0: void michael@0: NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const michael@0: { michael@0: InitKeyEvent(aKeyEvent, mModKeyState); michael@0: } michael@0: michael@0: void michael@0: NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, michael@0: const ModifierKeyState& aModKeyState) const michael@0: { michael@0: nsIntPoint point(0, 0); michael@0: mWidget->InitEvent(aKeyEvent, &point); michael@0: michael@0: switch (aKeyEvent.message) { michael@0: case NS_KEY_DOWN: michael@0: aKeyEvent.keyCode = mDOMKeyCode; michael@0: // Unique id for this keydown event and its associated keypress. michael@0: sUniqueKeyEventId++; michael@0: aKeyEvent.mUniqueId = sUniqueKeyEventId; michael@0: break; michael@0: case NS_KEY_UP: michael@0: aKeyEvent.keyCode = mDOMKeyCode; michael@0: // Set defaultPrevented of the key event if the VK_MENU is not a system michael@0: // key release, so that the menu bar does not trigger. This helps avoid michael@0: // triggering the menu bar for ALT key accelerators used in assistive michael@0: // technologies such as Window-Eyes and ZoomText or for switching open michael@0: // state of IME. michael@0: aKeyEvent.mFlags.mDefaultPrevented = michael@0: (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP); michael@0: break; michael@0: case NS_KEY_PRESS: michael@0: aKeyEvent.mUniqueId = sUniqueKeyEventId; michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Invalid event message"); michael@0: } michael@0: michael@0: aKeyEvent.mIsRepeat = IsRepeat(); michael@0: aKeyEvent.mKeyNameIndex = mKeyNameIndex; michael@0: if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) { michael@0: aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString(); michael@0: } michael@0: aKeyEvent.location = GetKeyLocation(); michael@0: aModKeyState.InitInputEvent(aKeyEvent); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::DispatchKeyEvent(WidgetKeyboardEvent& aKeyEvent, michael@0: const MSG* aMsgSentToPlugin) const michael@0: { michael@0: if (mWidget->Destroyed()) { michael@0: MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget"); michael@0: } michael@0: michael@0: KeyboardLayout::NotifyIdleServiceOfUserActivity(); michael@0: michael@0: NPEvent pluginEvent; michael@0: if (aMsgSentToPlugin && michael@0: mWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) { michael@0: pluginEvent.event = aMsgSentToPlugin->message; michael@0: pluginEvent.wParam = aMsgSentToPlugin->wParam; michael@0: pluginEvent.lParam = aMsgSentToPlugin->lParam; michael@0: aKeyEvent.pluginEvent = static_cast(&pluginEvent); michael@0: } michael@0: michael@0: return (mWidget->DispatchKeyboardEvent(&aKeyEvent) || mWidget->Destroyed()); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::HandleKeyDownMessage(bool* aEventDispatched) const michael@0: { michael@0: MOZ_ASSERT(IsKeyDownMessage()); michael@0: michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = false; michael@0: } michael@0: michael@0: bool defaultPrevented = false; michael@0: if (mFakeCharMsgs || michael@0: !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) { michael@0: // Ignore [shift+]alt+space so the OS can handle it. michael@0: if (mModKeyState.IsAlt() && !mModKeyState.IsControl() && michael@0: mVirtualKeyCode == VK_SPACE) { michael@0: return false; michael@0: } michael@0: michael@0: bool isIMEEnabled = WinUtils::IsIMEEnabled(mWidget->GetInputContext()); michael@0: WidgetKeyboardEvent keydownEvent(true, NS_KEY_DOWN, mWidget); michael@0: InitKeyEvent(keydownEvent, mModKeyState); michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = true; michael@0: } michael@0: defaultPrevented = DispatchKeyEvent(keydownEvent, &mMsg); michael@0: michael@0: if (mWidget->Destroyed()) { michael@0: return true; michael@0: } michael@0: michael@0: // If IMC wasn't associated to the window but is associated it now (i.e., michael@0: // focus is moved from a non-editable editor to an editor by keydown michael@0: // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character michael@0: // inputting if IME is opened. But then, we should redirect the native michael@0: // keydown message to IME. michael@0: // However, note that if focus has been already moved to another michael@0: // application, we shouldn't redirect the message to it because the keydown michael@0: // message is processed by us, so, nobody shouldn't process it. michael@0: HWND focusedWnd = ::GetFocus(); michael@0: if (!defaultPrevented && !mFakeCharMsgs && focusedWnd && michael@0: !mWidget->PluginHasFocus() && !isIMEEnabled && michael@0: WinUtils::IsIMEEnabled(mWidget->GetInputContext())) { michael@0: RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd); michael@0: michael@0: INPUT keyinput; michael@0: keyinput.type = INPUT_KEYBOARD; michael@0: keyinput.ki.wVk = mOriginalVirtualKeyCode; michael@0: keyinput.ki.wScan = mScanCode; michael@0: keyinput.ki.dwFlags = KEYEVENTF_SCANCODE; michael@0: if (mIsExtended) { michael@0: keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; michael@0: } michael@0: keyinput.ki.time = 0; michael@0: keyinput.ki.dwExtraInfo = 0; michael@0: michael@0: RedirectedKeyDownMessageManager::WillRedirect(mMsg, defaultPrevented); michael@0: michael@0: ::SendInput(1, &keyinput, sizeof(keyinput)); michael@0: michael@0: // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN. michael@0: // If it's needed, it will be dispatched after next (redirected) michael@0: // WM_KEYDOWN. michael@0: return true; michael@0: } michael@0: } else { michael@0: defaultPrevented = RedirectedKeyDownMessageManager::DefaultPrevented(); michael@0: // If this is redirected keydown message, we have dispatched the keydown michael@0: // event already. michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = true; michael@0: } michael@0: } michael@0: michael@0: RedirectedKeyDownMessageManager::Forget(); michael@0: michael@0: // If the key was processed by IME, we shouldn't dispatch keypress event. michael@0: if (mOriginalVirtualKeyCode == VK_PROCESSKEY) { michael@0: return defaultPrevented; michael@0: } michael@0: michael@0: // Don't dispatch keypress event for modifier keys. michael@0: switch (mDOMKeyCode) { michael@0: case NS_VK_SHIFT: michael@0: case NS_VK_CONTROL: michael@0: case NS_VK_ALT: michael@0: case NS_VK_CAPS_LOCK: michael@0: case NS_VK_NUM_LOCK: michael@0: case NS_VK_SCROLL_LOCK: michael@0: case NS_VK_WIN: michael@0: return defaultPrevented; michael@0: } michael@0: michael@0: if (defaultPrevented) { michael@0: DispatchPluginEventsAndDiscardsCharMessages(); michael@0: return true; michael@0: } michael@0: michael@0: // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a michael@0: // keypress for almost all keys michael@0: if (NeedsToHandleWithoutFollowingCharMessages()) { michael@0: return (DispatchPluginEventsAndDiscardsCharMessages() || michael@0: DispatchKeyPressEventsWithKeyboardLayout()); michael@0: } michael@0: michael@0: MSG followingCharMsg; michael@0: if (GetFollowingCharMessage(followingCharMsg)) { michael@0: // Even if there was char message, it might be redirected by different michael@0: // window (perhaps, focus move?). Then, we shouldn't continue to handle michael@0: // the message since no input should occur on the window. michael@0: if (followingCharMsg.message == WM_NULL || michael@0: followingCharMsg.hwnd != mMsg.hwnd) { michael@0: return false; michael@0: } michael@0: return DispatchKeyPressEventForFollowingCharMessage(followingCharMsg); michael@0: } michael@0: michael@0: if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() && michael@0: !mModKeyState.IsWin() && mIsPrintableKey) { michael@0: // If this is simple KeyDown event but next message is not WM_CHAR, michael@0: // this event may not input text, so we should ignore this event. michael@0: // See bug 314130. michael@0: return false; michael@0: } michael@0: michael@0: if (mIsDeadKey) { michael@0: return false; michael@0: } michael@0: michael@0: return DispatchKeyPressEventsWithKeyboardLayout(); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::HandleCharMessage(const MSG& aCharMsg, michael@0: bool* aEventDispatched) const michael@0: { michael@0: MOZ_ASSERT(IsKeyDownMessage() || IsPrintableCharMessage(mMsg)); michael@0: MOZ_ASSERT(IsPrintableCharMessage(aCharMsg.message)); michael@0: michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = false; michael@0: } michael@0: michael@0: // Alt+Space key is handled by OS, we shouldn't touch it. michael@0: if (mModKeyState.IsAlt() && !mModKeyState.IsControl() && michael@0: mVirtualKeyCode == VK_SPACE) { michael@0: return false; michael@0: } michael@0: michael@0: // Bug 818235: Ignore Ctrl+Enter. michael@0: if (!mModKeyState.IsAlt() && mModKeyState.IsControl() && michael@0: mVirtualKeyCode == VK_RETURN) { michael@0: return false; michael@0: } michael@0: michael@0: // XXXmnakao I think that if aNativeKeyDown is null, such lonely WM_CHAR michael@0: // should cause composition events because they are not caused michael@0: // by actual keyboard operation. michael@0: michael@0: static const char16_t U_SPACE = 0x20; michael@0: static const char16_t U_EQUAL = 0x3D; michael@0: michael@0: // First, handle normal text input or non-printable key case here. michael@0: if ((!mModKeyState.IsAlt() && !mModKeyState.IsControl()) || michael@0: mModKeyState.IsAltGr() || michael@0: (mOriginalVirtualKeyCode && michael@0: !KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode))) { michael@0: WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); michael@0: if (aCharMsg.wParam >= U_SPACE) { michael@0: keypressEvent.charCode = static_cast(aCharMsg.wParam); michael@0: } else { michael@0: keypressEvent.keyCode = mDOMKeyCode; michael@0: } michael@0: // When AltGr (Alt+Ctrl) is pressed, that causes normal text input. michael@0: // At this time, if either alt or ctrl flag is set, nsEditor ignores the michael@0: // keypress event. For avoiding this issue, we should remove ctrl and alt michael@0: // flags. michael@0: ModifierKeyState modKeyState(mModKeyState); michael@0: modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL); michael@0: InitKeyEvent(keypressEvent, modKeyState); michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = true; michael@0: } michael@0: return DispatchKeyEvent(keypressEvent, &aCharMsg); michael@0: } michael@0: michael@0: // XXX It seems that following code was implemented for shortcut key michael@0: // handling. However, it's now handled in WM_KEYDOWN message handler. michael@0: // So, this actually runs only when WM_CHAR is sent/posted without michael@0: // WM_KEYDOWN. I think that we don't need to keypress event in such michael@0: // case especially for shortcut keys. michael@0: michael@0: char16_t uniChar; michael@0: // Ctrl+A Ctrl+Z, see Programming Windows 3.1 page 110 for details michael@0: if (mModKeyState.IsControl() && aCharMsg.wParam <= 0x1A) { michael@0: // Bug 16486: Need to account for shift here. michael@0: uniChar = aCharMsg.wParam - 1 + (mModKeyState.IsShift() ? 'A' : 'a'); michael@0: } else if (mModKeyState.IsControl() && aCharMsg.wParam <= 0x1F) { michael@0: // Bug 50255: <[> and <]> are not being processed. michael@0: // also fixes ctrl+\ (x1c), ctrl+^ (x1e) and ctrl+_ (x1f) michael@0: // for some reason the keypress handler need to have the uniChar code set michael@0: // with the addition of a upper case A not the lower case. michael@0: uniChar = aCharMsg.wParam - 1 + 'A'; michael@0: } else if (aCharMsg.wParam < U_SPACE || michael@0: (aCharMsg.wParam == U_EQUAL && mModKeyState.IsControl())) { michael@0: uniChar = 0; michael@0: } else { michael@0: uniChar = aCharMsg.wParam; michael@0: } michael@0: michael@0: // Bug 50255 and Bug 351310: Keep the characters unshifted for shortcuts and michael@0: // accesskeys and make sure that numbers are always passed as such. michael@0: if (uniChar && (mModKeyState.IsControl() || mModKeyState.IsAlt())) { michael@0: KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance(); michael@0: char16_t unshiftedCharCode = michael@0: (mVirtualKeyCode >= '0' && mVirtualKeyCode <= '9') ? michael@0: mVirtualKeyCode : mModKeyState.IsShift() ? michael@0: ComputeUnicharFromScanCode() : 0; michael@0: // Ignore diacritics (top bit set) and key mapping errors (char code 0) michael@0: if (static_cast(unshiftedCharCode) > 0) { michael@0: uniChar = unshiftedCharCode; michael@0: } michael@0: } michael@0: michael@0: // Bug 285161 and Bug 295095: They were caused by the initial fix for michael@0: // bug 178110. When pressing (alt|ctrl)+char, the char must be lowercase michael@0: // unless shift is pressed too. michael@0: if (!mModKeyState.IsShift() && michael@0: (mModKeyState.IsAlt() || mModKeyState.IsControl())) { michael@0: uniChar = towlower(uniChar); michael@0: } michael@0: michael@0: WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); michael@0: keypressEvent.charCode = uniChar; michael@0: if (!keypressEvent.charCode) { michael@0: keypressEvent.keyCode = mDOMKeyCode; michael@0: } michael@0: InitKeyEvent(keypressEvent, mModKeyState); michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = true; michael@0: } michael@0: return DispatchKeyEvent(keypressEvent, &aCharMsg); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::HandleKeyUpMessage(bool* aEventDispatched) const michael@0: { michael@0: MOZ_ASSERT(IsKeyUpMessage()); michael@0: michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = false; michael@0: } michael@0: michael@0: // Ignore [shift+]alt+space so the OS can handle it. michael@0: if (mModKeyState.IsAlt() && !mModKeyState.IsControl() && michael@0: mVirtualKeyCode == VK_SPACE) { michael@0: return false; michael@0: } michael@0: michael@0: WidgetKeyboardEvent keyupEvent(true, NS_KEY_UP, mWidget); michael@0: InitKeyEvent(keyupEvent, mModKeyState); michael@0: if (aEventDispatched) { michael@0: *aEventDispatched = true; michael@0: } michael@0: return DispatchKeyEvent(keyupEvent, &mMsg); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::NeedsToHandleWithoutFollowingCharMessages() const michael@0: { michael@0: MOZ_ASSERT(IsKeyDownMessage()); michael@0: michael@0: // Enter and backspace are always handled here to avoid for example the michael@0: // confusion between ctrl-enter and ctrl-J. michael@0: if (mDOMKeyCode == NS_VK_RETURN || mDOMKeyCode == NS_VK_BACK) { michael@0: return true; michael@0: } michael@0: michael@0: // If any modifier keys which may cause printable keys becoming non-printable michael@0: // are not pressed, we don't need special handling for the key. michael@0: if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() && michael@0: !mModKeyState.IsWin()) { michael@0: return false; michael@0: } michael@0: michael@0: // If the key event causes dead key event, we don't need to dispatch keypress michael@0: // event. michael@0: if (mIsDeadKey && mCommittedCharsAndModifiers.IsEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: // Even if the key is a printable key, it might cause non-printable character michael@0: // input with modifier key(s). michael@0: return mIsPrintableKey; michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: michael@0: static nsCString michael@0: GetResultOfInSendMessageEx() michael@0: { michael@0: DWORD ret = ::InSendMessageEx(nullptr); michael@0: if (!ret) { michael@0: return NS_LITERAL_CSTRING("ISMEX_NOSEND"); michael@0: } michael@0: nsAutoCString result; michael@0: if (ret & ISMEX_CALLBACK) { michael@0: result = "ISMEX_CALLBACK"; michael@0: } michael@0: if (ret & ISMEX_NOTIFY) { michael@0: if (!result.IsEmpty()) { michael@0: result += " | "; michael@0: } michael@0: result += "ISMEX_NOTIFY"; michael@0: } michael@0: if (ret & ISMEX_REPLIED) { michael@0: if (!result.IsEmpty()) { michael@0: result += " | "; michael@0: } michael@0: result += "ISMEX_REPLIED"; michael@0: } michael@0: if (ret & ISMEX_SEND) { michael@0: if (!result.IsEmpty()) { michael@0: result += " | "; michael@0: } michael@0: result += "ISMEX_SEND"; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static const char* michael@0: GetMessageName(UINT aMessage) michael@0: { michael@0: switch (aMessage) { michael@0: case WM_KEYDOWN: return "WM_KEYDOWN"; michael@0: case WM_SYSKEYDOWN: return "WM_SYSKEYDOWN"; michael@0: case WM_KEYUP: return "WM_KEYUP"; michael@0: case WM_SYSKEYUP: return "WM_SYSKEYUP"; michael@0: case WM_CHAR: return "WM_CHAR"; michael@0: case WM_DEADCHAR: return "WM_DEADCHAR"; michael@0: case WM_SYSCHAR: return "WM_SYSCHAR"; michael@0: case WM_SYSDEADCHAR: return "WM_SYSDEADCHAR"; michael@0: case WM_UNICHAR: return "WM_UNICHAR"; michael@0: case WM_QUIT: return "WM_QUIT"; michael@0: case WM_NULL: return "WM_NULL"; michael@0: default: return "Unknown"; michael@0: } michael@0: } michael@0: michael@0: #endif // #ifdef MOZ_CRASHREPORTER michael@0: michael@0: bool michael@0: NativeKey::MayBeSameCharMessage(const MSG& aCharMsg1, michael@0: const MSG& aCharMsg2) const michael@0: { michael@0: // NOTE: Although, we don't know when this case occurs, the scan code value michael@0: // in lParam may be changed from 0 to something. The changed value michael@0: // is different from the scan code of handling keydown message. michael@0: static const LPARAM kScanCodeMask = 0x00FF0000; michael@0: return michael@0: aCharMsg1.message == aCharMsg2.message && michael@0: aCharMsg1.wParam == aCharMsg2.wParam && michael@0: (aCharMsg1.lParam & ~kScanCodeMask) == (aCharMsg2.lParam & ~kScanCodeMask); michael@0: } michael@0: michael@0: bool michael@0: NativeKey::GetFollowingCharMessage(MSG& aCharMsg) const michael@0: { michael@0: MOZ_ASSERT(IsKeyDownMessage()); michael@0: michael@0: aCharMsg.message = WM_NULL; michael@0: michael@0: if (mFakeCharMsgs) { michael@0: FakeCharMsg& fakeCharMsg = mFakeCharMsgs->ElementAt(0); michael@0: if (fakeCharMsg.mConsumed) { michael@0: return false; michael@0: } michael@0: MSG charMsg = fakeCharMsg.GetCharMsg(mMsg.hwnd); michael@0: fakeCharMsg.mConsumed = true; michael@0: if (!IsCharMessage(charMsg)) { michael@0: return false; michael@0: } michael@0: aCharMsg = charMsg; michael@0: return true; michael@0: } michael@0: michael@0: // If next key message is not char message, we should give up to find a michael@0: // related char message for the handling keydown event for now. michael@0: // Note that it's possible other applications may send other key message michael@0: // after we call TranslateMessage(). That may cause PeekMessage() failing michael@0: // to get char message for the handling keydown message. michael@0: MSG nextKeyMsg; michael@0: if (!WinUtils::PeekMessage(&nextKeyMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST, michael@0: PM_NOREMOVE | PM_NOYIELD) || michael@0: !IsCharMessage(nextKeyMsg)) { michael@0: return false; michael@0: } michael@0: michael@0: // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify michael@0: // the message range. So, if it returns WM_NULL, we should retry to get michael@0: // the following char message it was found above. michael@0: for (uint32_t i = 0; i < 5; i++) { michael@0: MSG removedMsg, nextKeyMsgInAllWindows; michael@0: bool doCrash = false; michael@0: if (!WinUtils::PeekMessage(&removedMsg, mMsg.hwnd, michael@0: nextKeyMsg.message, nextKeyMsg.message, michael@0: PM_REMOVE | PM_NOYIELD)) { michael@0: // We meets unexpected case. We should collect the message queue state michael@0: // and crash for reporting the bug. michael@0: doCrash = true; michael@0: // The char message is redirected to different thread's window by focus michael@0: // move or something or just cancelled by external application. michael@0: if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, michael@0: WM_KEYFIRST, WM_KEYLAST, michael@0: PM_NOREMOVE | PM_NOYIELD)) { michael@0: return true; michael@0: } michael@0: if (MayBeSameCharMessage(nextKeyMsgInAllWindows, nextKeyMsg)) { michael@0: // The char message is redirected to different window created by our michael@0: // thread. michael@0: if (nextKeyMsgInAllWindows.hwnd != mMsg.hwnd) { michael@0: aCharMsg = nextKeyMsgInAllWindows; michael@0: return true; michael@0: } michael@0: // The found char message still in the queue, but PeekMessage() failed michael@0: // to remove it only with PM_REMOVE. Although, we don't know why this michael@0: // occurs. However, this occurs acctually. michael@0: // Try to remove the char message with GetMessage() again. michael@0: if (WinUtils::GetMessage(&removedMsg, mMsg.hwnd, michael@0: nextKeyMsg.message, nextKeyMsg.message)) { michael@0: // Cancel to crash, but we need to check the removed message value. michael@0: doCrash = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (doCrash) { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: nsPrintfCString info("\nPeekMessage() failed to remove char message! " michael@0: "\nHandling message: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, \n" michael@0: "Found message: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, hwnd=0x%p, " michael@0: "\nWM_NULL has been removed: %d, " michael@0: "\nNext key message in all windows: %s (0x%08X), " michael@0: "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, " michael@0: "time=%d, ", michael@0: GetMessageName(mMsg.message), michael@0: mMsg.message, mMsg.wParam, mMsg.lParam, michael@0: nextKeyMsg.hwnd, michael@0: GetResultOfInSendMessageEx().get(), michael@0: GetMessageName(nextKeyMsg.message), michael@0: nextKeyMsg.message, nextKeyMsg.wParam, michael@0: nextKeyMsg.lParam, nextKeyMsg.hwnd, i, michael@0: GetMessageName(nextKeyMsgInAllWindows.message), michael@0: nextKeyMsgInAllWindows.message, michael@0: nextKeyMsgInAllWindows.wParam, michael@0: nextKeyMsgInAllWindows.lParam, michael@0: nextKeyMsgInAllWindows.hwnd, michael@0: nextKeyMsgInAllWindows.time); michael@0: CrashReporter::AppendAppNotesToCrashReport(info); michael@0: MSG nextMsg; michael@0: if (WinUtils::PeekMessage(&nextMsg, 0, 0, 0, michael@0: PM_NOREMOVE | PM_NOYIELD)) { michael@0: nsPrintfCString info("\nNext message in all windows: %s (0x%08X), " michael@0: "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, " michael@0: "time=%d", michael@0: GetMessageName(nextMsg.message), michael@0: nextMsg.message, nextMsg.wParam, nextMsg.lParam, michael@0: nextMsg.hwnd, nextMsg.time); michael@0: CrashReporter::AppendAppNotesToCrashReport(info); michael@0: } else { michael@0: CrashReporter::AppendAppNotesToCrashReport( michael@0: NS_LITERAL_CSTRING("\nThere is no message in any window")); michael@0: } michael@0: #endif // #ifdef MOZ_CRASHREPORTER michael@0: MOZ_CRASH("We lost the following char message"); michael@0: } michael@0: michael@0: // Retry for the strange case. michael@0: if (removedMsg.message == WM_NULL) { michael@0: continue; michael@0: } michael@0: michael@0: // Typically, this case occurs with WM_DEADCHAR. If the removed message's michael@0: // wParam becomes 0, that means that the key event shouldn't cause text michael@0: // input. So, let's ignore the strange char message. michael@0: if (removedMsg.message == nextKeyMsg.message && !removedMsg.wParam) { michael@0: return false; michael@0: } michael@0: michael@0: // NOTE: Although, we don't know when this case occurs, the scan code value michael@0: // in lParam may be changed from 0 to something. The changed value michael@0: // is different from the scan code of handling keydown message. michael@0: if (!MayBeSameCharMessage(removedMsg, nextKeyMsg)) { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: nsPrintfCString info("\nPeekMessage() removed unexpcted char message! " michael@0: "\nHandling message: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, " michael@0: "\nFound message: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, hwnd=0x%p, " michael@0: "\nRemoved message: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, hwnd=0x%p, ", michael@0: GetMessageName(mMsg.message), michael@0: mMsg.message, mMsg.wParam, mMsg.lParam, mMsg.hwnd, michael@0: GetResultOfInSendMessageEx().get(), michael@0: GetMessageName(nextKeyMsg.message), michael@0: nextKeyMsg.message, nextKeyMsg.wParam, michael@0: nextKeyMsg.lParam, nextKeyMsg.hwnd, michael@0: GetMessageName(removedMsg.message), michael@0: removedMsg.message, removedMsg.wParam, michael@0: removedMsg.lParam, removedMsg.hwnd); michael@0: CrashReporter::AppendAppNotesToCrashReport(info); michael@0: // What's the next key message? michael@0: MSG nextKeyMsgAfter; michael@0: if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd, michael@0: WM_KEYFIRST, WM_KEYLAST, michael@0: PM_NOREMOVE | PM_NOYIELD)) { michael@0: nsPrintfCString info("\nNext key message after unexpected char message " michael@0: "removed: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, hwnd=0x%p, ", michael@0: GetMessageName(nextKeyMsgAfter.message), michael@0: nextKeyMsgAfter.message, nextKeyMsgAfter.wParam, michael@0: nextKeyMsgAfter.lParam, nextKeyMsgAfter.hwnd); michael@0: CrashReporter::AppendAppNotesToCrashReport(info); michael@0: } else { michael@0: CrashReporter::AppendAppNotesToCrashReport( michael@0: NS_LITERAL_CSTRING("\nThere is no key message after unexpected char " michael@0: "message removed, ")); michael@0: } michael@0: // Another window has a key message? michael@0: MSG nextKeyMsgInAllWindows; michael@0: if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, michael@0: WM_KEYFIRST, WM_KEYLAST, michael@0: PM_NOREMOVE | PM_NOYIELD)) { michael@0: nsPrintfCString info("\nNext key message in all windows: %s (0x%08X), " michael@0: "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p.", michael@0: GetMessageName(nextKeyMsgInAllWindows.message), michael@0: nextKeyMsgInAllWindows.message, michael@0: nextKeyMsgInAllWindows.wParam, michael@0: nextKeyMsgInAllWindows.lParam, michael@0: nextKeyMsgInAllWindows.hwnd); michael@0: CrashReporter::AppendAppNotesToCrashReport(info); michael@0: } else { michael@0: CrashReporter::AppendAppNotesToCrashReport( michael@0: NS_LITERAL_CSTRING("\nThere is no key message in any windows.")); michael@0: } michael@0: #endif // #ifdef MOZ_CRASHREPORTER michael@0: MOZ_CRASH("PeekMessage() removed unexpected message"); michael@0: } michael@0: michael@0: aCharMsg = removedMsg; michael@0: return true; michael@0: } michael@0: #ifdef MOZ_CRASHREPORTER michael@0: nsPrintfCString info("\nWe lost following char message! " michael@0: "\nHandling message: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, InSendMessageEx()=%s, \n" michael@0: "Found message: %s (0x%08X), wParam: 0x%08X, " michael@0: "lParam: 0x%08X, removed a lot of WM_NULL", michael@0: GetMessageName(mMsg.message), michael@0: mMsg.message, mMsg.wParam, mMsg.lParam, michael@0: GetResultOfInSendMessageEx().get(), michael@0: GetMessageName(nextKeyMsg.message), michael@0: nextKeyMsg.message, nextKeyMsg.wParam, michael@0: nextKeyMsg.lParam); michael@0: CrashReporter::AppendAppNotesToCrashReport(info); michael@0: #endif // #ifdef MOZ_CRASHREPORTER michael@0: MOZ_CRASH("We lost the following char message"); michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: NativeKey::DispatchPluginEventsAndDiscardsCharMessages() const michael@0: { michael@0: MOZ_ASSERT(IsKeyDownMessage()); michael@0: michael@0: // Remove a possible WM_CHAR or WM_SYSCHAR messages from the message queue. michael@0: // They can be more than one because of: michael@0: // * Dead-keys not pairing with base character michael@0: // * Some keyboard layouts may map up to 4 characters to the single key michael@0: bool anyCharMessagesRemoved = false; michael@0: MSG msg; michael@0: while (GetFollowingCharMessage(msg)) { michael@0: if (msg.message == WM_NULL) { michael@0: continue; michael@0: } michael@0: anyCharMessagesRemoved = true; michael@0: // If the window handle is changed, focused window must be changed. michael@0: // So, plugin shouldn't handle it anymore. michael@0: if (msg.hwnd != mMsg.hwnd) { michael@0: break; michael@0: } michael@0: MOZ_RELEASE_ASSERT(!mWidget->Destroyed(), michael@0: "NativeKey tries to dispatch a plugin event on destroyed widget"); michael@0: mWidget->DispatchPluginEvent(msg); michael@0: if (mWidget->Destroyed()) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: if (!mFakeCharMsgs && !anyCharMessagesRemoved && michael@0: mDOMKeyCode == NS_VK_BACK && IsIMEDoingKakuteiUndo()) { michael@0: // This is for a hack for ATOK and WXG. So, PeekMessage() must scceed! michael@0: while (WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_CHAR, WM_CHAR, michael@0: PM_REMOVE | PM_NOYIELD)) { michael@0: if (msg.message != WM_CHAR) { michael@0: MOZ_RELEASE_ASSERT(msg.message == WM_NULL, michael@0: "Unexpected message was removed"); michael@0: continue; michael@0: } michael@0: MOZ_RELEASE_ASSERT(!mWidget->Destroyed(), michael@0: "NativeKey tries to dispatch a plugin event on destroyed widget"); michael@0: mWidget->DispatchPluginEvent(msg); michael@0: return mWidget->Destroyed(); michael@0: } michael@0: MOZ_CRASH("NativeKey failed to get WM_CHAR for ATOK or WXG"); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: NativeKey::DispatchKeyPressEventsWithKeyboardLayout() const michael@0: { michael@0: MOZ_ASSERT(IsKeyDownMessage()); michael@0: MOZ_ASSERT(!mIsDeadKey); michael@0: michael@0: KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance(); michael@0: michael@0: UniCharsAndModifiers inputtingChars(mCommittedCharsAndModifiers); michael@0: UniCharsAndModifiers shiftedChars; michael@0: UniCharsAndModifiers unshiftedChars; michael@0: uint32_t shiftedLatinChar = 0; michael@0: uint32_t unshiftedLatinChar = 0; michael@0: michael@0: if (!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode)) { michael@0: inputtingChars.Clear(); michael@0: } michael@0: michael@0: if (mModKeyState.IsControl() ^ mModKeyState.IsAlt()) { michael@0: ModifierKeyState capsLockState( michael@0: mModKeyState.GetModifiers() & MODIFIER_CAPSLOCK); michael@0: michael@0: unshiftedChars = michael@0: keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState); michael@0: capsLockState.Set(MODIFIER_SHIFT); michael@0: shiftedChars = michael@0: keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState); michael@0: michael@0: // The current keyboard cannot input alphabets or numerics, michael@0: // we should append them for Shortcut/Access keys. michael@0: // E.g., for Cyrillic keyboard layout. michael@0: capsLockState.Unset(MODIFIER_SHIFT); michael@0: WidgetUtils::GetLatinCharCodeForKeyCode(mDOMKeyCode, michael@0: capsLockState.GetModifiers(), michael@0: &unshiftedLatinChar, michael@0: &shiftedLatinChar); michael@0: michael@0: // If the shiftedLatinChar isn't 0, the key code is NS_VK_[A-Z]. michael@0: if (shiftedLatinChar) { michael@0: // If the produced characters of the key on current keyboard layout michael@0: // are same as computed Latin characters, we shouldn't append the michael@0: // Latin characters to alternativeCharCode. michael@0: if (unshiftedLatinChar == unshiftedChars.mChars[0] && michael@0: shiftedLatinChar == shiftedChars.mChars[0]) { michael@0: shiftedLatinChar = unshiftedLatinChar = 0; michael@0: } michael@0: } else if (unshiftedLatinChar) { michael@0: // If the shiftedLatinChar is 0, the keyCode doesn't produce michael@0: // alphabet character. At that time, the character may be produced michael@0: // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT michael@0: // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without michael@0: // Shift key but with Shift key, it produces '%'. michael@0: // If the unshiftedLatinChar is produced by the key on current michael@0: // keyboard layout, we shouldn't append it to alternativeCharCode. michael@0: if (unshiftedLatinChar == unshiftedChars.mChars[0] || michael@0: unshiftedLatinChar == shiftedChars.mChars[0]) { michael@0: unshiftedLatinChar = 0; michael@0: } michael@0: } michael@0: michael@0: // If the charCode is not ASCII character, we should replace the michael@0: // charCode with ASCII character only when Ctrl is pressed. michael@0: // But don't replace the charCode when the charCode is not same as michael@0: // unmodified characters. In such case, Ctrl is sometimes used for a michael@0: // part of character inputting key combination like Shift. michael@0: if (mModKeyState.IsControl()) { michael@0: uint32_t ch = michael@0: mModKeyState.IsShift() ? shiftedLatinChar : unshiftedLatinChar; michael@0: if (ch && michael@0: (!inputtingChars.mLength || michael@0: inputtingChars.UniCharsCaseInsensitiveEqual( michael@0: mModKeyState.IsShift() ? shiftedChars : unshiftedChars))) { michael@0: inputtingChars.Clear(); michael@0: inputtingChars.Append(ch, mModKeyState.GetModifiers()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (inputtingChars.IsEmpty() && michael@0: shiftedChars.IsEmpty() && unshiftedChars.IsEmpty()) { michael@0: WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); michael@0: keypressEvent.keyCode = mDOMKeyCode; michael@0: InitKeyEvent(keypressEvent, mModKeyState); michael@0: return DispatchKeyEvent(keypressEvent); michael@0: } michael@0: michael@0: uint32_t longestLength = michael@0: std::max(inputtingChars.mLength, michael@0: std::max(shiftedChars.mLength, unshiftedChars.mLength)); michael@0: uint32_t skipUniChars = longestLength - inputtingChars.mLength; michael@0: uint32_t skipShiftedChars = longestLength - shiftedChars.mLength; michael@0: uint32_t skipUnshiftedChars = longestLength - unshiftedChars.mLength; michael@0: UINT keyCode = !inputtingChars.mLength ? mDOMKeyCode : 0; michael@0: bool defaultPrevented = false; michael@0: for (uint32_t cnt = 0; cnt < longestLength; cnt++) { michael@0: uint16_t uniChar, shiftedChar, unshiftedChar; michael@0: uniChar = shiftedChar = unshiftedChar = 0; michael@0: ModifierKeyState modKeyState(mModKeyState); michael@0: if (skipUniChars <= cnt) { michael@0: if (cnt - skipUniChars < inputtingChars.mLength) { michael@0: // If key in combination with Alt and/or Ctrl produces a different michael@0: // character than without them then do not report these flags michael@0: // because it is separate keyboard layout shift state. If dead-key michael@0: // and base character does not produce a valid composite character michael@0: // then both produced dead-key character and following base michael@0: // character may have different modifier flags, too. michael@0: modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | michael@0: MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK); michael@0: modKeyState.Set(inputtingChars.mModifiers[cnt - skipUniChars]); michael@0: } michael@0: uniChar = inputtingChars.mChars[cnt - skipUniChars]; michael@0: } michael@0: if (skipShiftedChars <= cnt) michael@0: shiftedChar = shiftedChars.mChars[cnt - skipShiftedChars]; michael@0: if (skipUnshiftedChars <= cnt) michael@0: unshiftedChar = unshiftedChars.mChars[cnt - skipUnshiftedChars]; michael@0: nsAutoTArray altArray; michael@0: michael@0: if (shiftedChar || unshiftedChar) { michael@0: AlternativeCharCode chars(unshiftedChar, shiftedChar); michael@0: altArray.AppendElement(chars); michael@0: } michael@0: if (cnt == longestLength - 1) { michael@0: if (unshiftedLatinChar || shiftedLatinChar) { michael@0: AlternativeCharCode chars(unshiftedLatinChar, shiftedLatinChar); michael@0: altArray.AppendElement(chars); michael@0: } michael@0: michael@0: // Typically, following virtual keycodes are used for a key which can michael@0: // input the character. However, these keycodes are also used for michael@0: // other keys on some keyboard layout. E.g., in spite of Shift+'1' michael@0: // inputs '+' on Thai keyboard layout, a key which is at '=/+' michael@0: // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications michael@0: // handle it as '+' key if Ctrl key is pressed. michael@0: char16_t charForOEMKeyCode = 0; michael@0: switch (mVirtualKeyCode) { michael@0: case VK_OEM_PLUS: charForOEMKeyCode = '+'; break; michael@0: case VK_OEM_COMMA: charForOEMKeyCode = ','; break; michael@0: case VK_OEM_MINUS: charForOEMKeyCode = '-'; break; michael@0: case VK_OEM_PERIOD: charForOEMKeyCode = '.'; break; michael@0: } michael@0: if (charForOEMKeyCode && michael@0: charForOEMKeyCode != unshiftedChars.mChars[0] && michael@0: charForOEMKeyCode != shiftedChars.mChars[0] && michael@0: charForOEMKeyCode != unshiftedLatinChar && michael@0: charForOEMKeyCode != shiftedLatinChar) { michael@0: AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode); michael@0: altArray.AppendElement(OEMChars); michael@0: } michael@0: } michael@0: michael@0: WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); michael@0: keypressEvent.charCode = uniChar; michael@0: keypressEvent.alternativeCharCodes.AppendElements(altArray); michael@0: InitKeyEvent(keypressEvent, modKeyState); michael@0: defaultPrevented = (DispatchKeyEvent(keypressEvent) || defaultPrevented); michael@0: if (mWidget->Destroyed()) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return defaultPrevented; michael@0: } michael@0: michael@0: bool michael@0: NativeKey::DispatchKeyPressEventForFollowingCharMessage( michael@0: const MSG& aCharMsg) const michael@0: { michael@0: MOZ_ASSERT(IsKeyDownMessage()); michael@0: michael@0: if (mFakeCharMsgs) { michael@0: if (IsDeadCharMessage(aCharMsg)) { michael@0: return false; michael@0: } michael@0: #ifdef DEBUG michael@0: if (mIsPrintableKey) { michael@0: nsPrintfCString log( michael@0: "mOriginalVirtualKeyCode=0x%02X, mCommittedCharsAndModifiers={ " michael@0: "mChars=[ 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X ], mLength=%d }, " michael@0: "wParam=0x%04X", michael@0: mOriginalVirtualKeyCode, mCommittedCharsAndModifiers.mChars[0], michael@0: mCommittedCharsAndModifiers.mChars[1], michael@0: mCommittedCharsAndModifiers.mChars[2], michael@0: mCommittedCharsAndModifiers.mChars[3], michael@0: mCommittedCharsAndModifiers.mChars[4], michael@0: mCommittedCharsAndModifiers.mLength, aCharMsg.wParam); michael@0: if (mCommittedCharsAndModifiers.IsEmpty()) { michael@0: log.Insert("length is zero: ", 0); michael@0: NS_ERROR(log.get()); michael@0: NS_ABORT(); michael@0: } else if (mCommittedCharsAndModifiers.mChars[0] != aCharMsg.wParam) { michael@0: log.Insert("character mismatch: ", 0); michael@0: NS_ERROR(log.get()); michael@0: NS_ABORT(); michael@0: } michael@0: } michael@0: #endif // #ifdef DEBUG michael@0: return HandleCharMessage(aCharMsg); michael@0: } michael@0: michael@0: if (IsDeadCharMessage(aCharMsg)) { michael@0: if (!mWidget->PluginHasFocus()) { michael@0: return false; michael@0: } michael@0: return (mWidget->DispatchPluginEvent(aCharMsg) || mWidget->Destroyed()); michael@0: } michael@0: michael@0: bool defaultPrevented = HandleCharMessage(aCharMsg); michael@0: // If a syschar keypress wasn't processed, Windows may want to michael@0: // handle it to activate a native menu. michael@0: if (!defaultPrevented && IsSysCharMessage(aCharMsg)) { michael@0: ::DefWindowProcW(aCharMsg.hwnd, aCharMsg.message, michael@0: aCharMsg.wParam, aCharMsg.lParam); michael@0: } michael@0: return defaultPrevented; michael@0: } michael@0: michael@0: /***************************************************************************** michael@0: * mozilla::widget::KeyboardLayout michael@0: *****************************************************************************/ michael@0: michael@0: KeyboardLayout* KeyboardLayout::sInstance = nullptr; michael@0: nsIIdleServiceInternal* KeyboardLayout::sIdleService = nullptr; michael@0: michael@0: // static michael@0: KeyboardLayout* michael@0: KeyboardLayout::GetInstance() michael@0: { michael@0: if (!sInstance) { michael@0: sInstance = new KeyboardLayout(); michael@0: nsCOMPtr idleService = michael@0: do_GetService("@mozilla.org/widget/idleservice;1"); michael@0: // The refcount will be decreased at shut down. michael@0: sIdleService = idleService.forget().take(); michael@0: } michael@0: return sInstance; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: KeyboardLayout::Shutdown() michael@0: { michael@0: delete sInstance; michael@0: sInstance = nullptr; michael@0: NS_IF_RELEASE(sIdleService); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: KeyboardLayout::NotifyIdleServiceOfUserActivity() michael@0: { michael@0: sIdleService->ResetIdleTimeOut(0); michael@0: } michael@0: michael@0: KeyboardLayout::KeyboardLayout() : michael@0: mKeyboardLayout(0), mIsOverridden(false), michael@0: mIsPendingToRestoreKeyboardLayout(false) michael@0: { michael@0: mDeadKeyTableListHead = nullptr; michael@0: michael@0: // NOTE: LoadLayout() should be called via OnLayoutChange(). michael@0: } michael@0: michael@0: KeyboardLayout::~KeyboardLayout() michael@0: { michael@0: ReleaseDeadKeyTables(); michael@0: } michael@0: michael@0: bool michael@0: KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey) michael@0: { michael@0: return GetKeyIndex(aVirtualKey) >= 0; michael@0: } michael@0: michael@0: WORD michael@0: KeyboardLayout::ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const michael@0: { michael@0: return static_cast( michael@0: ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC, GetLayout())); michael@0: } michael@0: michael@0: bool michael@0: KeyboardLayout::IsDeadKey(uint8_t aVirtualKey, michael@0: const ModifierKeyState& aModKeyState) const michael@0: { michael@0: int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey); michael@0: if (virtualKeyIndex < 0) { michael@0: return false; michael@0: } michael@0: michael@0: return mVirtualKeys[virtualKeyIndex].IsDeadKey( michael@0: VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers())); michael@0: } michael@0: michael@0: void michael@0: KeyboardLayout::InitNativeKey(NativeKey& aNativeKey, michael@0: const ModifierKeyState& aModKeyState) michael@0: { michael@0: if (mIsPendingToRestoreKeyboardLayout) { michael@0: LoadLayout(::GetKeyboardLayout(0)); michael@0: } michael@0: michael@0: uint8_t virtualKey = aNativeKey.mOriginalVirtualKeyCode; michael@0: int32_t virtualKeyIndex = GetKeyIndex(virtualKey); michael@0: michael@0: if (virtualKeyIndex < 0) { michael@0: // Does not produce any printable characters, but still preserves the michael@0: // dead-key state. michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(aNativeKey.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING, michael@0: "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING"); michael@0: michael@0: bool isKeyDown = aNativeKey.IsKeyDownMessage(); michael@0: uint8_t shiftState = michael@0: VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()); michael@0: michael@0: if (mVirtualKeys[virtualKeyIndex].IsDeadKey(shiftState)) { michael@0: if ((isKeyDown && mActiveDeadKey < 0) || michael@0: (!isKeyDown && mActiveDeadKey == virtualKey)) { michael@0: // First dead key event doesn't generate characters. michael@0: if (isKeyDown) { michael@0: // Dead-key state activated at keydown. michael@0: mActiveDeadKey = virtualKey; michael@0: mDeadKeyShiftState = shiftState; michael@0: } michael@0: UniCharsAndModifiers deadChars = michael@0: mVirtualKeys[virtualKeyIndex].GetNativeUniChars(shiftState); michael@0: NS_ASSERTION(deadChars.mLength == 1, michael@0: "dead key must generate only one character"); michael@0: aNativeKey.mKeyNameIndex = michael@0: WidgetUtils::GetDeadKeyNameIndex(deadChars.mChars[0]); michael@0: return; michael@0: } michael@0: michael@0: // Dead key followed by another dead key causes inputting both character. michael@0: // However, at keydown message handling, we need to forget the first michael@0: // dead key because there is no guarantee coming WM_KEYUP for the second michael@0: // dead key before next WM_KEYDOWN. E.g., due to auto key repeat or michael@0: // pressing another dead key before releasing current key. Therefore, michael@0: // we can set only a character for current key for keyup event. michael@0: if (mActiveDeadKey < 0) { michael@0: aNativeKey.mCommittedCharsAndModifiers = michael@0: mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState); michael@0: return; michael@0: } michael@0: michael@0: int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey); michael@0: if (activeDeadKeyIndex < 0 || activeDeadKeyIndex >= NS_NUM_OF_KEYS) { michael@0: #if defined(DEBUG) || defined(MOZ_CRASHREPORTER) michael@0: nsPrintfCString warning("The virtual key index (%d) of mActiveDeadKey " michael@0: "(0x%02X) is not a printable key (virtualKey=" michael@0: "0x%02X)", michael@0: activeDeadKeyIndex, mActiveDeadKey, virtualKey); michael@0: NS_WARNING(warning.get()); michael@0: #ifdef MOZ_CRASHREPORTER michael@0: CrashReporter::AppendAppNotesToCrashReport( michael@0: NS_LITERAL_CSTRING("\n") + warning); michael@0: #endif // #ifdef MOZ_CRASHREPORTER michael@0: #endif // #if defined(DEBUG) || defined(MOZ_CRASHREPORTER) michael@0: MOZ_CRASH("Trying to reference out of range of mVirtualKeys"); michael@0: } michael@0: UniCharsAndModifiers prevDeadChars = michael@0: mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState); michael@0: UniCharsAndModifiers newChars = michael@0: mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState); michael@0: // But keypress events should be fired for each committed character. michael@0: aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars; michael@0: if (isKeyDown) { michael@0: DeactivateDeadKeyState(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: UniCharsAndModifiers baseChars = michael@0: mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState); michael@0: if (mActiveDeadKey < 0) { michael@0: // No dead-keys are active. Just return the produced characters. michael@0: aNativeKey.mCommittedCharsAndModifiers = baseChars; michael@0: return; michael@0: } michael@0: michael@0: // Dead-key was active. See if pressed base character does produce michael@0: // valid composite character. michael@0: int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey); michael@0: char16_t compositeChar = (baseChars.mLength == 1 && baseChars.mChars[0]) ? michael@0: mVirtualKeys[activeDeadKeyIndex].GetCompositeChar(mDeadKeyShiftState, michael@0: baseChars.mChars[0]) : 0; michael@0: if (compositeChar) { michael@0: // Active dead-key and base character does produce exactly one michael@0: // composite character. michael@0: aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar, michael@0: baseChars.mModifiers[0]); michael@0: if (isKeyDown) { michael@0: DeactivateDeadKeyState(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // There is no valid dead-key and base character combination. michael@0: // Return dead-key character followed by base character. michael@0: UniCharsAndModifiers deadChars = michael@0: mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState); michael@0: // But keypress events should be fired for each committed character. michael@0: aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars; michael@0: if (isKeyDown) { michael@0: DeactivateDeadKeyState(); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: UniCharsAndModifiers michael@0: KeyboardLayout::GetUniCharsAndModifiers( michael@0: uint8_t aVirtualKey, michael@0: const ModifierKeyState& aModKeyState) const michael@0: { michael@0: UniCharsAndModifiers result; michael@0: int32_t key = GetKeyIndex(aVirtualKey); michael@0: if (key < 0) { michael@0: return result; michael@0: } michael@0: return mVirtualKeys[key]. michael@0: GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers())); michael@0: } michael@0: michael@0: void michael@0: KeyboardLayout::LoadLayout(HKL aLayout) michael@0: { michael@0: mIsPendingToRestoreKeyboardLayout = false; michael@0: michael@0: if (mKeyboardLayout == aLayout) { michael@0: return; michael@0: } michael@0: michael@0: mKeyboardLayout = aLayout; michael@0: michael@0: BYTE kbdState[256]; michael@0: memset(kbdState, 0, sizeof(kbdState)); michael@0: michael@0: BYTE originalKbdState[256]; michael@0: // Bitfield with all shift states that have at least one dead-key. michael@0: uint16_t shiftStatesWithDeadKeys = 0; michael@0: // Bitfield with all shift states that produce any possible dead-key base michael@0: // characters. michael@0: uint16_t shiftStatesWithBaseChars = 0; michael@0: michael@0: mActiveDeadKey = -1; michael@0: michael@0: ReleaseDeadKeyTables(); michael@0: michael@0: ::GetKeyboardState(originalKbdState); michael@0: michael@0: // For each shift state gather all printable characters that are produced michael@0: // for normal case when no any dead-key is active. michael@0: michael@0: for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) { michael@0: VirtualKey::FillKbdState(kbdState, shiftState); michael@0: for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) { michael@0: int32_t vki = GetKeyIndex(virtualKey); michael@0: if (vki < 0) { michael@0: continue; michael@0: } michael@0: NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index"); michael@0: char16_t uniChars[5]; michael@0: int32_t ret = michael@0: ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars, michael@0: ArrayLength(uniChars), 0, mKeyboardLayout); michael@0: // dead-key michael@0: if (ret < 0) { michael@0: shiftStatesWithDeadKeys |= (1 << shiftState); michael@0: // Repeat dead-key to deactivate it and get its character michael@0: // representation. michael@0: char16_t deadChar[2]; michael@0: ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)deadChar, michael@0: ArrayLength(deadChar), 0, mKeyboardLayout); michael@0: NS_ASSERTION(ret == 2, "Expecting twice repeated dead-key character"); michael@0: mVirtualKeys[vki].SetDeadChar(shiftState, deadChar[0]); michael@0: } else { michael@0: if (ret == 1) { michael@0: // dead-key can pair only with exactly one base character. michael@0: shiftStatesWithBaseChars |= (1 << shiftState); michael@0: } michael@0: mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Now process each dead-key to find all its base characters and resulting michael@0: // composite characters. michael@0: for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) { michael@0: if (!(shiftStatesWithDeadKeys & (1 << shiftState))) { michael@0: continue; michael@0: } michael@0: michael@0: VirtualKey::FillKbdState(kbdState, shiftState); michael@0: michael@0: for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) { michael@0: int32_t vki = GetKeyIndex(virtualKey); michael@0: if (vki >= 0 && mVirtualKeys[vki].IsDeadKey(shiftState)) { michael@0: DeadKeyEntry deadKeyArray[256]; michael@0: int32_t n = GetDeadKeyCombinations(virtualKey, kbdState, michael@0: shiftStatesWithBaseChars, michael@0: deadKeyArray, michael@0: ArrayLength(deadKeyArray)); michael@0: const DeadKeyTable* dkt = michael@0: mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray, n); michael@0: if (!dkt) { michael@0: dkt = AddDeadKeyTable(deadKeyArray, n); michael@0: } michael@0: mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt); michael@0: } michael@0: } michael@0: } michael@0: michael@0: ::SetKeyboardState(originalKbdState); michael@0: } michael@0: michael@0: inline int32_t michael@0: KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey) michael@0: { michael@0: // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed michael@0: // to produce visible representation: michael@0: // 0x20 - VK_SPACE ' ' michael@0: // 0x30..0x39 '0'..'9' michael@0: // 0x41..0x5A 'A'..'Z' michael@0: // 0x60..0x69 '0'..'9' on numpad michael@0: // 0x6A - VK_MULTIPLY '*' on numpad michael@0: // 0x6B - VK_ADD '+' on numpad michael@0: // 0x6D - VK_SUBTRACT '-' on numpad michael@0: // 0x6E - VK_DECIMAL '.' on numpad michael@0: // 0x6F - VK_DIVIDE '/' on numpad michael@0: // 0x6E - VK_DECIMAL '.' michael@0: // 0xBA - VK_OEM_1 ';:' for US michael@0: // 0xBB - VK_OEM_PLUS '+' any country michael@0: // 0xBC - VK_OEM_COMMA ',' any country michael@0: // 0xBD - VK_OEM_MINUS '-' any country michael@0: // 0xBE - VK_OEM_PERIOD '.' any country michael@0: // 0xBF - VK_OEM_2 '/?' for US michael@0: // 0xC0 - VK_OEM_3 '`~' for US michael@0: // 0xC1 - VK_ABNT_C1 '/?' for Brazilian michael@0: // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac) michael@0: // 0xDB - VK_OEM_4 '[{' for US michael@0: // 0xDC - VK_OEM_5 '\|' for US michael@0: // 0xDD - VK_OEM_6 ']}' for US michael@0: // 0xDE - VK_OEM_7 ''"' for US michael@0: // 0xDF - VK_OEM_8 michael@0: // 0xE1 - no name michael@0: // 0xE2 - VK_OEM_102 '\_' for JIS michael@0: // 0xE3 - no name michael@0: // 0xE4 - no name michael@0: michael@0: static const int8_t xlat[256] = michael@0: { michael@0: // 0 1 2 3 4 5 6 7 8 9 A B C D E F michael@0: //----------------------------------------------------------------------- michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10 michael@0: 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20 michael@0: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30 michael@0: -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40 michael@0: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50 michael@0: 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0 michael@0: 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0 michael@0: -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0 michael@0: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0 michael@0: }; michael@0: michael@0: return xlat[aVirtualKey]; michael@0: } michael@0: michael@0: int michael@0: KeyboardLayout::CompareDeadKeyEntries(const void* aArg1, michael@0: const void* aArg2, michael@0: void*) michael@0: { michael@0: const DeadKeyEntry* arg1 = static_cast(aArg1); michael@0: const DeadKeyEntry* arg2 = static_cast(aArg2); michael@0: michael@0: return arg1->BaseChar - arg2->BaseChar; michael@0: } michael@0: michael@0: const DeadKeyTable* michael@0: KeyboardLayout::AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, michael@0: uint32_t aEntries) michael@0: { michael@0: DeadKeyTableListEntry* next = mDeadKeyTableListHead; michael@0: michael@0: const size_t bytes = offsetof(DeadKeyTableListEntry, data) + michael@0: DeadKeyTable::SizeInBytes(aEntries); michael@0: uint8_t* p = new uint8_t[bytes]; michael@0: michael@0: mDeadKeyTableListHead = reinterpret_cast(p); michael@0: mDeadKeyTableListHead->next = next; michael@0: michael@0: DeadKeyTable* dkt = michael@0: reinterpret_cast(mDeadKeyTableListHead->data); michael@0: michael@0: dkt->Init(aDeadKeyArray, aEntries); michael@0: michael@0: return dkt; michael@0: } michael@0: michael@0: void michael@0: KeyboardLayout::ReleaseDeadKeyTables() michael@0: { michael@0: while (mDeadKeyTableListHead) { michael@0: uint8_t* p = reinterpret_cast(mDeadKeyTableListHead); michael@0: mDeadKeyTableListHead = mDeadKeyTableListHead->next; michael@0: michael@0: delete [] p; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: KeyboardLayout::EnsureDeadKeyActive(bool aIsActive, michael@0: uint8_t aDeadKey, michael@0: const PBYTE aDeadKeyKbdState) michael@0: { michael@0: int32_t ret; michael@0: do { michael@0: char16_t dummyChars[5]; michael@0: ret = ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState, michael@0: (LPWSTR)dummyChars, ArrayLength(dummyChars), 0, michael@0: mKeyboardLayout); michael@0: // returned values: michael@0: // <0 - Dead key state is active. The keyboard driver will wait for next michael@0: // character. michael@0: // 1 - Previous pressed key was a valid base character that produced michael@0: // exactly one composite character. michael@0: // >1 - Previous pressed key does not produce any composite characters. michael@0: // Return dead-key character followed by base character(s). michael@0: } while ((ret < 0) != aIsActive); michael@0: michael@0: return (ret < 0); michael@0: } michael@0: michael@0: void michael@0: KeyboardLayout::DeactivateDeadKeyState() michael@0: { michael@0: if (mActiveDeadKey < 0) { michael@0: return; michael@0: } michael@0: michael@0: BYTE kbdState[256]; michael@0: memset(kbdState, 0, sizeof(kbdState)); michael@0: michael@0: VirtualKey::FillKbdState(kbdState, mDeadKeyShiftState); michael@0: michael@0: EnsureDeadKeyActive(false, mActiveDeadKey, kbdState); michael@0: mActiveDeadKey = -1; michael@0: } michael@0: michael@0: bool michael@0: KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar, michael@0: char16_t aCompositeChar, michael@0: DeadKeyEntry* aDeadKeyArray, michael@0: uint32_t aEntries) michael@0: { michael@0: for (uint32_t index = 0; index < aEntries; index++) { michael@0: if (aDeadKeyArray[index].BaseChar == aBaseChar) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: aDeadKeyArray[aEntries].BaseChar = aBaseChar; michael@0: aDeadKeyArray[aEntries].CompositeChar = aCompositeChar; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: uint32_t michael@0: KeyboardLayout::GetDeadKeyCombinations(uint8_t aDeadKey, michael@0: const PBYTE aDeadKeyKbdState, michael@0: uint16_t aShiftStatesWithBaseChars, michael@0: DeadKeyEntry* aDeadKeyArray, michael@0: uint32_t aMaxEntries) michael@0: { michael@0: bool deadKeyActive = false; michael@0: uint32_t entries = 0; michael@0: BYTE kbdState[256]; michael@0: memset(kbdState, 0, sizeof(kbdState)); michael@0: michael@0: for (uint32_t shiftState = 0; shiftState < 16; shiftState++) { michael@0: if (!(aShiftStatesWithBaseChars & (1 << shiftState))) { michael@0: continue; michael@0: } michael@0: michael@0: VirtualKey::FillKbdState(kbdState, shiftState); michael@0: michael@0: for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) { michael@0: int32_t vki = GetKeyIndex(virtualKey); michael@0: // Dead-key can pair only with such key that produces exactly one base michael@0: // character. michael@0: if (vki >= 0 && michael@0: mVirtualKeys[vki].GetNativeUniChars(shiftState).mLength == 1) { michael@0: // Ensure dead-key is in active state, when it swallows entered michael@0: // character and waits for the next pressed key. michael@0: if (!deadKeyActive) { michael@0: deadKeyActive = EnsureDeadKeyActive(true, aDeadKey, michael@0: aDeadKeyKbdState); michael@0: } michael@0: michael@0: // Depending on the character the followed the dead-key, the keyboard michael@0: // driver can produce one composite character, or a dead-key character michael@0: // followed by a second character. michael@0: char16_t compositeChars[5]; michael@0: int32_t ret = michael@0: ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars, michael@0: ArrayLength(compositeChars), 0, mKeyboardLayout); michael@0: switch (ret) { michael@0: case 0: michael@0: // This key combination does not produce any characters. The michael@0: // dead-key is still in active state. michael@0: break; michael@0: case 1: { michael@0: // Exactly one composite character produced. Now, when dead-key michael@0: // is not active, repeat the last character one more time to michael@0: // determine the base character. michael@0: char16_t baseChars[5]; michael@0: ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)baseChars, michael@0: ArrayLength(baseChars), 0, mKeyboardLayout); michael@0: NS_ASSERTION(ret == 1, "One base character expected"); michael@0: if (ret == 1 && entries < aMaxEntries && michael@0: AddDeadKeyEntry(baseChars[0], compositeChars[0], michael@0: aDeadKeyArray, entries)) { michael@0: entries++; michael@0: } michael@0: deadKeyActive = false; michael@0: break; michael@0: } michael@0: default: michael@0: // 1. Unexpected dead-key. Dead-key chaining is not supported. michael@0: // 2. More than one character generated. This is not a valid michael@0: // dead-key and base character combination. michael@0: deadKeyActive = false; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (deadKeyActive) { michael@0: deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState); michael@0: } michael@0: michael@0: NS_QuickSort(aDeadKeyArray, entries, sizeof(DeadKeyEntry), michael@0: CompareDeadKeyEntries, nullptr); michael@0: return entries; michael@0: } michael@0: michael@0: uint32_t michael@0: KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const michael@0: { michael@0: // Alphabet or Numeric or Numpad or Function keys michael@0: if ((aNativeKeyCode >= 0x30 && aNativeKeyCode <= 0x39) || michael@0: (aNativeKeyCode >= 0x41 && aNativeKeyCode <= 0x5A) || michael@0: (aNativeKeyCode >= 0x60 && aNativeKeyCode <= 0x87)) { michael@0: return static_cast(aNativeKeyCode); michael@0: } michael@0: switch (aNativeKeyCode) { michael@0: // Following keycodes are same as our DOM keycodes michael@0: case VK_CANCEL: michael@0: case VK_BACK: michael@0: case VK_TAB: michael@0: case VK_CLEAR: michael@0: case VK_RETURN: michael@0: case VK_SHIFT: michael@0: case VK_CONTROL: michael@0: case VK_MENU: // Alt michael@0: case VK_PAUSE: michael@0: case VK_CAPITAL: // CAPS LOCK michael@0: case VK_KANA: // same as VK_HANGUL michael@0: case VK_JUNJA: michael@0: case VK_FINAL: michael@0: case VK_HANJA: // same as VK_KANJI michael@0: case VK_ESCAPE: michael@0: case VK_CONVERT: michael@0: case VK_NONCONVERT: michael@0: case VK_ACCEPT: michael@0: case VK_MODECHANGE: michael@0: case VK_SPACE: michael@0: case VK_PRIOR: // PAGE UP michael@0: case VK_NEXT: // PAGE DOWN michael@0: case VK_END: michael@0: case VK_HOME: michael@0: case VK_LEFT: michael@0: case VK_UP: michael@0: case VK_RIGHT: michael@0: case VK_DOWN: michael@0: case VK_SELECT: michael@0: case VK_PRINT: michael@0: case VK_EXECUTE: michael@0: case VK_SNAPSHOT: michael@0: case VK_INSERT: michael@0: case VK_DELETE: michael@0: case VK_APPS: // Context Menu michael@0: case VK_SLEEP: michael@0: case VK_NUMLOCK: michael@0: case VK_SCROLL: // SCROLL LOCK michael@0: case VK_ATTN: // Attension key of IBM midrange computers, e.g., AS/400 michael@0: case VK_CRSEL: // Cursor Selection michael@0: case VK_EXSEL: // Extend Selection michael@0: case VK_EREOF: // Erase EOF key of IBM 3270 keyboard layout michael@0: case VK_PLAY: michael@0: case VK_ZOOM: michael@0: case VK_PA1: // PA1 key of IBM 3270 keyboard layout michael@0: return uint32_t(aNativeKeyCode); michael@0: michael@0: case VK_HELP: michael@0: return NS_VK_HELP; michael@0: michael@0: // Windows key should be mapped to a Win keycode michael@0: // They should be able to be distinguished by DOM3 KeyboardEvent.location michael@0: case VK_LWIN: michael@0: case VK_RWIN: michael@0: return NS_VK_WIN; michael@0: michael@0: case VK_VOLUME_MUTE: michael@0: return NS_VK_VOLUME_MUTE; michael@0: case VK_VOLUME_DOWN: michael@0: return NS_VK_VOLUME_DOWN; michael@0: case VK_VOLUME_UP: michael@0: return NS_VK_VOLUME_UP; michael@0: michael@0: // Following keycodes are not defined in our DOM keycodes. michael@0: case VK_BROWSER_BACK: michael@0: case VK_BROWSER_FORWARD: michael@0: case VK_BROWSER_REFRESH: michael@0: case VK_BROWSER_STOP: michael@0: case VK_BROWSER_SEARCH: michael@0: case VK_BROWSER_FAVORITES: michael@0: case VK_BROWSER_HOME: michael@0: case VK_MEDIA_NEXT_TRACK: michael@0: case VK_MEDIA_STOP: michael@0: case VK_MEDIA_PLAY_PAUSE: michael@0: case VK_LAUNCH_MAIL: michael@0: case VK_LAUNCH_MEDIA_SELECT: michael@0: case VK_LAUNCH_APP1: michael@0: case VK_LAUNCH_APP2: michael@0: return 0; michael@0: michael@0: // Following OEM specific virtual keycodes should pass through DOM keyCode michael@0: // for compatibility with the other browsers on Windows. michael@0: michael@0: // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS. michael@0: case VK_OEM_FJ_JISHO: michael@0: case VK_OEM_FJ_MASSHOU: michael@0: case VK_OEM_FJ_TOUROKU: michael@0: case VK_OEM_FJ_LOYA: michael@0: case VK_OEM_FJ_ROYA: michael@0: // Not sure what means "ICO". michael@0: case VK_ICO_HELP: michael@0: case VK_ICO_00: michael@0: case VK_ICO_CLEAR: michael@0: // Following OEM specific virtual keycodes are defined for Nokia/Ericsson. michael@0: case VK_OEM_RESET: michael@0: case VK_OEM_JUMP: michael@0: case VK_OEM_PA1: michael@0: case VK_OEM_PA2: michael@0: case VK_OEM_PA3: michael@0: case VK_OEM_WSCTRL: michael@0: case VK_OEM_CUSEL: michael@0: case VK_OEM_ATTN: michael@0: case VK_OEM_FINISH: michael@0: case VK_OEM_COPY: michael@0: case VK_OEM_AUTO: michael@0: case VK_OEM_ENLW: michael@0: case VK_OEM_BACKTAB: michael@0: // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though michael@0: // DOM keyCode like other OEM specific virtual keycodes. michael@0: case VK_OEM_CLEAR: michael@0: return uint32_t(aNativeKeyCode); michael@0: michael@0: // 0xE1 is an OEM specific virtual keycode. However, the value is already michael@0: // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode michael@0: // cannot pass through DOM keyCode. michael@0: case 0xE1: michael@0: return 0; michael@0: michael@0: // Following keycodes are OEM keys which are keycodes for non-alphabet and michael@0: // non-numeric keys, we should compute each keycode of them from unshifted michael@0: // character which is inputted by each key. But if the unshifted character michael@0: // is not an ASCII character but shifted character is an ASCII character, michael@0: // we should refer it. michael@0: case VK_OEM_1: michael@0: case VK_OEM_PLUS: michael@0: case VK_OEM_COMMA: michael@0: case VK_OEM_MINUS: michael@0: case VK_OEM_PERIOD: michael@0: case VK_OEM_2: michael@0: case VK_OEM_3: michael@0: case VK_OEM_4: michael@0: case VK_OEM_5: michael@0: case VK_OEM_6: michael@0: case VK_OEM_7: michael@0: case VK_OEM_8: michael@0: case VK_OEM_102: michael@0: case VK_ABNT_C1: michael@0: { michael@0: NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode), michael@0: "The key must be printable"); michael@0: ModifierKeyState modKeyState(0); michael@0: UniCharsAndModifiers uniChars = michael@0: GetUniCharsAndModifiers(aNativeKeyCode, modKeyState); michael@0: if (uniChars.mLength != 1 || michael@0: uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) { michael@0: modKeyState.Set(MODIFIER_SHIFT); michael@0: uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState); michael@0: if (uniChars.mLength != 1 || michael@0: uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) { michael@0: return 0; michael@0: } michael@0: } michael@0: return WidgetUtils::ComputeKeyCodeFromChar(uniChars.mChars[0]); michael@0: } michael@0: michael@0: // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already michael@0: // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore, michael@0: // We should keep consistency between Gecko on all platforms rather than michael@0: // with other browsers since a lot of keyCode values are already different michael@0: // between browsers. michael@0: case VK_ABNT_C2: michael@0: return NS_VK_SEPARATOR; michael@0: michael@0: // VK_PROCESSKEY means IME already consumed the key event. michael@0: case VK_PROCESSKEY: michael@0: return 0; michael@0: // VK_PACKET is generated by SendInput() API, we don't need to michael@0: // care this message as key event. michael@0: case VK_PACKET: michael@0: return 0; michael@0: // If a key is not mapped to a virtual keycode, 0xFF is used. michael@0: case 0xFF: michael@0: NS_WARNING("The key is failed to be converted to a virtual keycode"); michael@0: return 0; michael@0: } michael@0: #ifdef DEBUG michael@0: nsPrintfCString warning("Unknown virtual keycode (0x%08X), please check the " michael@0: "latest MSDN document, there may be some new " michael@0: "keycodes we've never known.", michael@0: aNativeKeyCode); michael@0: NS_WARNING(warning.get()); michael@0: #endif michael@0: return 0; michael@0: } michael@0: michael@0: KeyNameIndex michael@0: KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const michael@0: { michael@0: if (IsPrintableCharKey(aVirtualKey)) { michael@0: return KEY_NAME_INDEX_USE_STRING; michael@0: } michael@0: michael@0: #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) michael@0: #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) michael@0: #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) michael@0: #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) michael@0: michael@0: switch (aVirtualKey) { michael@0: michael@0: #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \ michael@0: case aNativeKey: return aKeyNameIndex; michael@0: michael@0: #include "NativeKeyToDOMKeyName.h" michael@0: michael@0: #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: HKL layout = GetLayout(); michael@0: WORD langID = LOWORD(static_cast(layout)); michael@0: WORD primaryLangID = PRIMARYLANGID(langID); michael@0: michael@0: if (primaryLangID == LANG_JAPANESE) { michael@0: switch (aVirtualKey) { michael@0: michael@0: #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\ michael@0: case aNativeKey: return aKeyNameIndex; michael@0: michael@0: #include "NativeKeyToDOMKeyName.h" michael@0: michael@0: #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: } else if (primaryLangID == LANG_KOREAN) { michael@0: switch (aVirtualKey) { michael@0: michael@0: #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\ michael@0: case aNativeKey: return aKeyNameIndex; michael@0: michael@0: #include "NativeKeyToDOMKeyName.h" michael@0: michael@0: #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) michael@0: michael@0: default: michael@0: return KEY_NAME_INDEX_Unidentified; michael@0: } michael@0: } michael@0: michael@0: switch (aVirtualKey) { michael@0: michael@0: #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\ michael@0: case aNativeKey: return aKeyNameIndex; michael@0: michael@0: #include "NativeKeyToDOMKeyName.h" michael@0: michael@0: #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX michael@0: michael@0: default: michael@0: return KEY_NAME_INDEX_Unidentified; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: KeyboardLayout::SynthesizeNativeKeyEvent(nsWindowBase* aWidget, michael@0: int32_t aNativeKeyboardLayout, michael@0: int32_t aNativeKeyCode, michael@0: uint32_t aModifierFlags, michael@0: const nsAString& aCharacters, michael@0: const nsAString& aUnmodifiedCharacters) michael@0: { michael@0: UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, nullptr); michael@0: NS_ASSERTION(keyboardLayoutListCount > 0, michael@0: "One keyboard layout must be installed at least"); michael@0: HKL keyboardLayoutListBuff[50]; michael@0: HKL* keyboardLayoutList = michael@0: keyboardLayoutListCount < 50 ? keyboardLayoutListBuff : michael@0: new HKL[keyboardLayoutListCount]; michael@0: keyboardLayoutListCount = michael@0: ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList); michael@0: NS_ASSERTION(keyboardLayoutListCount > 0, michael@0: "Failed to get all keyboard layouts installed on the system"); michael@0: michael@0: nsPrintfCString layoutName("%08x", aNativeKeyboardLayout); michael@0: HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL); michael@0: if (loadedLayout == nullptr) { michael@0: if (keyboardLayoutListBuff != keyboardLayoutList) { michael@0: delete [] keyboardLayoutList; michael@0: } michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // Setup clean key state and load desired layout michael@0: BYTE originalKbdState[256]; michael@0: ::GetKeyboardState(originalKbdState); michael@0: BYTE kbdState[256]; michael@0: memset(kbdState, 0, sizeof(kbdState)); michael@0: // This changes the state of the keyboard for the current thread only, michael@0: // and we'll restore it soon, so this should be OK. michael@0: ::SetKeyboardState(kbdState); michael@0: michael@0: OverrideLayout(loadedLayout); michael@0: michael@0: uint8_t argumentKeySpecific = 0; michael@0: switch (aNativeKeyCode) { michael@0: case VK_SHIFT: michael@0: aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R); michael@0: argumentKeySpecific = VK_LSHIFT; michael@0: break; michael@0: case VK_LSHIFT: michael@0: aModifierFlags &= ~nsIWidget::SHIFT_L; michael@0: argumentKeySpecific = aNativeKeyCode; michael@0: aNativeKeyCode = VK_SHIFT; michael@0: break; michael@0: case VK_RSHIFT: michael@0: aModifierFlags &= ~nsIWidget::SHIFT_R; michael@0: argumentKeySpecific = aNativeKeyCode; michael@0: aNativeKeyCode = VK_SHIFT; michael@0: break; michael@0: case VK_CONTROL: michael@0: aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R); michael@0: argumentKeySpecific = VK_LCONTROL; michael@0: break; michael@0: case VK_LCONTROL: michael@0: aModifierFlags &= ~nsIWidget::CTRL_L; michael@0: argumentKeySpecific = aNativeKeyCode; michael@0: aNativeKeyCode = VK_CONTROL; michael@0: break; michael@0: case VK_RCONTROL: michael@0: aModifierFlags &= ~nsIWidget::CTRL_R; michael@0: argumentKeySpecific = aNativeKeyCode; michael@0: aNativeKeyCode = VK_CONTROL; michael@0: break; michael@0: case VK_MENU: michael@0: aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R); michael@0: argumentKeySpecific = VK_LMENU; michael@0: break; michael@0: case VK_LMENU: michael@0: aModifierFlags &= ~nsIWidget::ALT_L; michael@0: argumentKeySpecific = aNativeKeyCode; michael@0: aNativeKeyCode = VK_MENU; michael@0: break; michael@0: case VK_RMENU: michael@0: aModifierFlags &= ~nsIWidget::ALT_R; michael@0: argumentKeySpecific = aNativeKeyCode; michael@0: aNativeKeyCode = VK_MENU; michael@0: break; michael@0: case VK_CAPITAL: michael@0: aModifierFlags &= ~nsIWidget::CAPS_LOCK; michael@0: argumentKeySpecific = VK_CAPITAL; michael@0: break; michael@0: case VK_NUMLOCK: michael@0: aModifierFlags &= ~nsIWidget::NUM_LOCK; michael@0: argumentKeySpecific = VK_NUMLOCK; michael@0: break; michael@0: } michael@0: michael@0: nsAutoTArray keySequence; michael@0: WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags); michael@0: NS_ASSERTION(aNativeKeyCode >= 0 && aNativeKeyCode < 256, michael@0: "Native VK key code out of range"); michael@0: keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific)); michael@0: michael@0: // Simulate the pressing of each modifier key and then the real key michael@0: for (uint32_t i = 0; i < keySequence.Length(); ++i) { michael@0: uint8_t key = keySequence[i].mGeneral; michael@0: uint8_t keySpecific = keySequence[i].mSpecific; michael@0: kbdState[key] = 0x81; // key is down and toggled on if appropriate michael@0: if (keySpecific) { michael@0: kbdState[keySpecific] = 0x81; michael@0: } michael@0: ::SetKeyboardState(kbdState); michael@0: ModifierKeyState modKeyState; michael@0: UINT scanCode = michael@0: ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key); michael@0: LPARAM lParam = static_cast(scanCode << 16); michael@0: // Add extended key flag to the lParam for right control key and right alt michael@0: // key. michael@0: if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) { michael@0: lParam |= 0x1000000; michael@0: } michael@0: MSG keyDownMsg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam, michael@0: aWidget->GetWindowHandle()); michael@0: if (i == keySequence.Length() - 1) { michael@0: bool makeDeadCharMsg = michael@0: (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty()); michael@0: nsAutoString chars(aCharacters); michael@0: if (makeDeadCharMsg) { michael@0: UniCharsAndModifiers deadChars = michael@0: GetUniCharsAndModifiers(key, modKeyState); michael@0: chars = deadChars.ToString(); michael@0: NS_ASSERTION(chars.Length() == 1, michael@0: "Dead char must be only one character"); michael@0: } michael@0: if (chars.IsEmpty()) { michael@0: NativeKey nativeKey(aWidget, keyDownMsg, modKeyState); michael@0: nativeKey.HandleKeyDownMessage(); michael@0: } else { michael@0: nsAutoTArray fakeCharMsgs; michael@0: for (uint32_t j = 0; j < chars.Length(); j++) { michael@0: NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement(); michael@0: fakeCharMsg->mCharCode = chars.CharAt(j); michael@0: fakeCharMsg->mScanCode = scanCode; michael@0: fakeCharMsg->mIsDeadKey = makeDeadCharMsg; michael@0: } michael@0: NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, &fakeCharMsgs); michael@0: bool dispatched; michael@0: nativeKey.HandleKeyDownMessage(&dispatched); michael@0: // If some char messages are not consumed, let's emulate the widget michael@0: // receiving the message directly. michael@0: for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) { michael@0: if (fakeCharMsgs[j].mConsumed) { michael@0: continue; michael@0: } michael@0: MSG charMsg = fakeCharMsgs[j].GetCharMsg(aWidget->GetWindowHandle()); michael@0: NativeKey nativeKey(aWidget, charMsg, modKeyState); michael@0: nativeKey.HandleCharMessage(charMsg); michael@0: } michael@0: } michael@0: } else { michael@0: NativeKey nativeKey(aWidget, keyDownMsg, modKeyState); michael@0: nativeKey.HandleKeyDownMessage(); michael@0: } michael@0: } michael@0: for (uint32_t i = keySequence.Length(); i > 0; --i) { michael@0: uint8_t key = keySequence[i - 1].mGeneral; michael@0: uint8_t keySpecific = keySequence[i - 1].mSpecific; michael@0: kbdState[key] = 0; // key is up and toggled off if appropriate michael@0: if (keySpecific) { michael@0: kbdState[keySpecific] = 0; michael@0: } michael@0: ::SetKeyboardState(kbdState); michael@0: ModifierKeyState modKeyState; michael@0: UINT scanCode = michael@0: ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key); michael@0: LPARAM lParam = static_cast(scanCode << 16); michael@0: // Add extended key flag to the lParam for right control key and right alt michael@0: // key. michael@0: if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) { michael@0: lParam |= 0x1000000; michael@0: } michael@0: MSG keyUpMsg = WinUtils::InitMSG(WM_KEYUP, key, lParam, michael@0: aWidget->GetWindowHandle()); michael@0: NativeKey nativeKey(aWidget, keyUpMsg, modKeyState); michael@0: nativeKey.HandleKeyUpMessage(); michael@0: } michael@0: michael@0: // Restore old key state and layout michael@0: ::SetKeyboardState(originalKbdState); michael@0: RestoreLayout(); michael@0: michael@0: // Don't unload the layout if it's installed actually. michael@0: for (uint32_t i = 0; i < keyboardLayoutListCount; i++) { michael@0: if (keyboardLayoutList[i] == loadedLayout) { michael@0: loadedLayout = 0; michael@0: break; michael@0: } michael@0: } michael@0: if (keyboardLayoutListBuff != keyboardLayoutList) { michael@0: delete [] keyboardLayoutList; michael@0: } michael@0: if (loadedLayout) { michael@0: ::UnloadKeyboardLayout(loadedLayout); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /***************************************************************************** michael@0: * mozilla::widget::DeadKeyTable michael@0: *****************************************************************************/ michael@0: michael@0: char16_t michael@0: DeadKeyTable::GetCompositeChar(char16_t aBaseChar) const michael@0: { michael@0: // Dead-key table is sorted by BaseChar in ascending order. michael@0: // Usually they are too small to use binary search. michael@0: michael@0: for (uint32_t index = 0; index < mEntries; index++) { michael@0: if (mTable[index].BaseChar == aBaseChar) { michael@0: return mTable[index].CompositeChar; michael@0: } michael@0: if (mTable[index].BaseChar > aBaseChar) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /***************************************************************************** michael@0: * mozilla::widget::RedirectedKeyDownMessage michael@0: *****************************************************************************/ michael@0: michael@0: MSG RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg; michael@0: bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg = false; michael@0: michael@0: // static michael@0: bool michael@0: RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG& aMsg) michael@0: { michael@0: return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) && michael@0: (sRedirectedKeyDownMsg.message == aMsg.message && michael@0: WinUtils::GetScanCode(sRedirectedKeyDownMsg.lParam) == michael@0: WinUtils::GetScanCode(aMsg.lParam)); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd) michael@0: { michael@0: MSG msg; michael@0: if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST, michael@0: PM_NOREMOVE | PM_NOYIELD) && michael@0: (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) { michael@0: WinUtils::PeekMessage(&msg, aWnd, msg.message, msg.message, michael@0: PM_REMOVE | PM_NOYIELD); michael@0: } michael@0: } michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: