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: #ifndef KeyboardLayout_h__ michael@0: #define KeyboardLayout_h__ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsWindowBase.h" michael@0: #include "nsWindowDefs.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/EventForwards.h" michael@0: #include michael@0: michael@0: #define NS_NUM_OF_KEYS 70 michael@0: michael@0: #define VK_OEM_1 0xBA // ';:' for US michael@0: #define VK_OEM_PLUS 0xBB // '+' any country michael@0: #define VK_OEM_COMMA 0xBC michael@0: #define VK_OEM_MINUS 0xBD // '-' any country michael@0: #define VK_OEM_PERIOD 0xBE michael@0: #define VK_OEM_2 0xBF michael@0: #define VK_OEM_3 0xC0 michael@0: // '/?' for Brazilian (ABNT) michael@0: #define VK_ABNT_C1 0xC1 michael@0: // Separator in Numpad for Brazilian (ABNT) or JIS keyboard for Mac. michael@0: #define VK_ABNT_C2 0xC2 michael@0: #define VK_OEM_4 0xDB michael@0: #define VK_OEM_5 0xDC michael@0: #define VK_OEM_6 0xDD michael@0: #define VK_OEM_7 0xDE michael@0: #define VK_OEM_8 0xDF michael@0: #define VK_OEM_102 0xE2 michael@0: #define VK_OEM_CLEAR 0xFE michael@0: michael@0: class nsIIdleServiceInternal; michael@0: struct nsModifierKeyState; michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: static const uint32_t sModifierKeyMap[][3] = { michael@0: { nsIWidget::CAPS_LOCK, VK_CAPITAL, 0 }, michael@0: { nsIWidget::NUM_LOCK, VK_NUMLOCK, 0 }, michael@0: { nsIWidget::SHIFT_L, VK_SHIFT, VK_LSHIFT }, michael@0: { nsIWidget::SHIFT_R, VK_SHIFT, VK_RSHIFT }, michael@0: { nsIWidget::CTRL_L, VK_CONTROL, VK_LCONTROL }, michael@0: { nsIWidget::CTRL_R, VK_CONTROL, VK_RCONTROL }, michael@0: { nsIWidget::ALT_L, VK_MENU, VK_LMENU }, michael@0: { nsIWidget::ALT_R, VK_MENU, VK_RMENU } michael@0: }; michael@0: michael@0: class KeyboardLayout; michael@0: michael@0: class ModifierKeyState michael@0: { michael@0: public: michael@0: ModifierKeyState(); michael@0: ModifierKeyState(bool aIsShiftDown, bool aIsControlDown, bool aIsAltDown); michael@0: ModifierKeyState(Modifiers aModifiers); michael@0: michael@0: MOZ_ALWAYS_INLINE void Update(); michael@0: michael@0: MOZ_ALWAYS_INLINE void Unset(Modifiers aRemovingModifiers); michael@0: void Set(Modifiers aAddingModifiers); michael@0: michael@0: void InitInputEvent(WidgetInputEvent& aInputEvent) const; michael@0: michael@0: bool IsShift() const; michael@0: bool IsControl() const; michael@0: MOZ_ALWAYS_INLINE bool IsAlt() const; michael@0: MOZ_ALWAYS_INLINE bool IsAltGr() const; michael@0: MOZ_ALWAYS_INLINE bool IsWin() const; michael@0: michael@0: MOZ_ALWAYS_INLINE bool IsCapsLocked() const; michael@0: MOZ_ALWAYS_INLINE bool IsNumLocked() const; michael@0: MOZ_ALWAYS_INLINE bool IsScrollLocked() const; michael@0: michael@0: MOZ_ALWAYS_INLINE Modifiers GetModifiers() const; michael@0: michael@0: private: michael@0: Modifiers mModifiers; michael@0: michael@0: MOZ_ALWAYS_INLINE void EnsureAltGr(); michael@0: michael@0: void InitMouseEvent(WidgetInputEvent& aMouseEvent) const; michael@0: }; michael@0: michael@0: struct UniCharsAndModifiers michael@0: { michael@0: // Dead-key + up to 4 characters michael@0: char16_t mChars[5]; michael@0: Modifiers mModifiers[5]; michael@0: uint32_t mLength; michael@0: michael@0: UniCharsAndModifiers() : mLength(0) {} michael@0: UniCharsAndModifiers operator+(const UniCharsAndModifiers& aOther) const; michael@0: UniCharsAndModifiers& operator+=(const UniCharsAndModifiers& aOther); michael@0: michael@0: /** michael@0: * Append a pair of unicode character and the final modifier. michael@0: */ michael@0: void Append(char16_t aUniChar, Modifiers aModifiers); michael@0: void Clear() { mLength = 0; } michael@0: bool IsEmpty() const { return !mLength; } michael@0: michael@0: void FillModifiers(Modifiers aModifiers); michael@0: michael@0: bool UniCharsEqual(const UniCharsAndModifiers& aOther) const; michael@0: bool UniCharsCaseInsensitiveEqual(const UniCharsAndModifiers& aOther) const; michael@0: michael@0: nsString ToString() const { return nsString(mChars, mLength); } michael@0: }; michael@0: michael@0: struct DeadKeyEntry; michael@0: class DeadKeyTable; michael@0: michael@0: michael@0: class VirtualKey michael@0: { michael@0: public: michael@0: // 0 - Normal michael@0: // 1 - Shift michael@0: // 2 - Control michael@0: // 3 - Control + Shift michael@0: // 4 - Alt michael@0: // 5 - Alt + Shift michael@0: // 6 - Alt + Control (AltGr) michael@0: // 7 - Alt + Control + Shift (AltGr + Shift) michael@0: // 8 - CapsLock michael@0: // 9 - CapsLock + Shift michael@0: // 10 - CapsLock + Control michael@0: // 11 - CapsLock + Control + Shift michael@0: // 12 - CapsLock + Alt michael@0: // 13 - CapsLock + Alt + Shift michael@0: // 14 - CapsLock + Alt + Control (CapsLock + AltGr) michael@0: // 15 - CapsLock + Alt + Control + Shift (CapsLock + AltGr + Shift) michael@0: michael@0: enum ShiftStateFlag michael@0: { michael@0: STATE_SHIFT = 0x01, michael@0: STATE_CONTROL = 0x02, michael@0: STATE_ALT = 0x04, michael@0: STATE_CAPSLOCK = 0x08 michael@0: }; michael@0: michael@0: typedef uint8_t ShiftState; michael@0: michael@0: static ShiftState ModifiersToShiftState(Modifiers aModifiers); michael@0: static Modifiers ShiftStateToModifiers(ShiftState aShiftState); michael@0: michael@0: private: michael@0: union KeyShiftState michael@0: { michael@0: struct michael@0: { michael@0: char16_t Chars[4]; michael@0: } Normal; michael@0: struct michael@0: { michael@0: const DeadKeyTable* Table; michael@0: char16_t DeadChar; michael@0: } DeadKey; michael@0: }; michael@0: michael@0: KeyShiftState mShiftStates[16]; michael@0: uint16_t mIsDeadKey; michael@0: michael@0: void SetDeadKey(ShiftState aShiftState, bool aIsDeadKey) michael@0: { michael@0: if (aIsDeadKey) { michael@0: mIsDeadKey |= 1 << aShiftState; michael@0: } else { michael@0: mIsDeadKey &= ~(1 << aShiftState); michael@0: } michael@0: } michael@0: michael@0: public: michael@0: static void FillKbdState(PBYTE aKbdState, const ShiftState aShiftState); michael@0: michael@0: bool IsDeadKey(ShiftState aShiftState) const michael@0: { michael@0: return (mIsDeadKey & (1 << aShiftState)) != 0; michael@0: } michael@0: michael@0: void AttachDeadKeyTable(ShiftState aShiftState, michael@0: const DeadKeyTable* aDeadKeyTable) michael@0: { michael@0: mShiftStates[aShiftState].DeadKey.Table = aDeadKeyTable; michael@0: } michael@0: michael@0: void SetNormalChars(ShiftState aShiftState, const char16_t* aChars, michael@0: uint32_t aNumOfChars); michael@0: void SetDeadChar(ShiftState aShiftState, char16_t aDeadChar); michael@0: const DeadKeyTable* MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, michael@0: uint32_t aEntries) const; michael@0: inline char16_t GetCompositeChar(ShiftState aShiftState, michael@0: char16_t aBaseChar) const; michael@0: UniCharsAndModifiers GetNativeUniChars(ShiftState aShiftState) const; michael@0: UniCharsAndModifiers GetUniChars(ShiftState aShiftState) const; michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS NativeKey michael@0: { michael@0: friend class KeyboardLayout; michael@0: michael@0: public: michael@0: struct FakeCharMsg michael@0: { michael@0: UINT mCharCode; michael@0: UINT mScanCode; michael@0: bool mIsDeadKey; michael@0: bool mConsumed; michael@0: michael@0: FakeCharMsg() : michael@0: mCharCode(0), mScanCode(0), mIsDeadKey(false), mConsumed(false) michael@0: { michael@0: } michael@0: michael@0: MSG GetCharMsg(HWND aWnd) const michael@0: { michael@0: MSG msg; michael@0: msg.hwnd = aWnd; michael@0: msg.message = mIsDeadKey ? WM_DEADCHAR : WM_CHAR; michael@0: msg.wParam = static_cast(mCharCode); michael@0: msg.lParam = static_cast(mScanCode << 16); michael@0: msg.time = 0; michael@0: msg.pt.x = msg.pt.y = 0; michael@0: return msg; michael@0: } michael@0: }; michael@0: michael@0: NativeKey(nsWindowBase* aWidget, michael@0: const MSG& aKeyOrCharMessage, michael@0: const ModifierKeyState& aModKeyState, michael@0: nsTArray* aFakeCharMsgs = nullptr); michael@0: michael@0: /** michael@0: * Handle WM_KEYDOWN message or WM_SYSKEYDOWN message. The instance must be michael@0: * initialized with WM_KEYDOWN or WM_SYSKEYDOWN. michael@0: * Returns true if dispatched keydown event or keypress event is consumed. michael@0: * Otherwise, false. michael@0: */ michael@0: bool HandleKeyDownMessage(bool* aEventDispatched = nullptr) const; michael@0: michael@0: /** michael@0: * Handles WM_CHAR message or WM_SYSCHAR message. The instance must be michael@0: * initialized with WM_KEYDOWN, WM_SYSKEYDOWN or them. michael@0: * Returns true if dispatched keypress event is consumed. Otherwise, false. michael@0: */ michael@0: bool HandleCharMessage(const MSG& aCharMsg, michael@0: bool* aEventDispatched = nullptr) const; michael@0: michael@0: /** michael@0: * Handles keyup message. Returns true if the event is consumed. michael@0: * Otherwise, false. michael@0: */ michael@0: bool HandleKeyUpMessage(bool* aEventDispatched = nullptr) const; michael@0: michael@0: private: michael@0: nsRefPtr mWidget; michael@0: HKL mKeyboardLayout; michael@0: MSG mMsg; michael@0: michael@0: uint32_t mDOMKeyCode; michael@0: KeyNameIndex mKeyNameIndex; michael@0: michael@0: ModifierKeyState mModKeyState; michael@0: michael@0: // mVirtualKeyCode distinguishes left key or right key of modifier key. michael@0: uint8_t mVirtualKeyCode; michael@0: // mOriginalVirtualKeyCode doesn't distinguish left key or right key of michael@0: // modifier key. However, if the given keycode is VK_PROCESS, it's resolved michael@0: // to a keycode before it's handled by IME. michael@0: uint8_t mOriginalVirtualKeyCode; michael@0: michael@0: // mCommittedChars indicates the inputted characters which is committed by michael@0: // the key. If dead key fail to composite a character, mCommittedChars michael@0: // indicates both the dead characters and the base characters. michael@0: UniCharsAndModifiers mCommittedCharsAndModifiers; michael@0: michael@0: WORD mScanCode; michael@0: bool mIsExtended; michael@0: bool mIsDeadKey; michael@0: // mIsPrintableKey is true if the key may be a printable key without michael@0: // any modifier keys. Otherwise, false. michael@0: // Please note that the event may not cause any text input even if this michael@0: // is true. E.g., it might be dead key state or Ctrl key may be pressed. michael@0: bool mIsPrintableKey; michael@0: michael@0: nsTArray* mFakeCharMsgs; michael@0: michael@0: NativeKey() michael@0: { michael@0: MOZ_CRASH("The default constructor of NativeKey isn't available"); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the key event is caused by auto repeat. michael@0: */ michael@0: bool IsRepeat() const michael@0: { michael@0: switch (mMsg.message) { michael@0: case WM_KEYDOWN: michael@0: case WM_SYSKEYDOWN: michael@0: case WM_CHAR: michael@0: case WM_SYSCHAR: michael@0: case WM_DEADCHAR: michael@0: case WM_SYSDEADCHAR: michael@0: return ((mMsg.lParam & (1 << 30)) != 0); michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: UINT GetScanCodeWithExtendedFlag() const; michael@0: michael@0: // The result is one of nsIDOMKeyEvent::DOM_KEY_LOCATION_*. michael@0: uint32_t GetKeyLocation() const; michael@0: michael@0: /** michael@0: * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes michael@0: * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this michael@0: * returns true, the caller needs to be careful for processing the messages. michael@0: */ michael@0: bool IsIMEDoingKakuteiUndo() const; michael@0: michael@0: bool IsKeyDownMessage() const michael@0: { michael@0: return (mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN); michael@0: } michael@0: bool IsKeyUpMessage() const michael@0: { michael@0: return (mMsg.message == WM_KEYUP || mMsg.message == WM_SYSKEYUP); michael@0: } michael@0: bool IsPrintableCharMessage(const MSG& aMSG) const michael@0: { michael@0: return IsPrintableCharMessage(aMSG.message); michael@0: } michael@0: bool IsPrintableCharMessage(UINT aMessage) const michael@0: { michael@0: return (aMessage == WM_CHAR || aMessage == WM_SYSCHAR); michael@0: } michael@0: bool IsCharMessage(const MSG& aMSG) const michael@0: { michael@0: return IsCharMessage(aMSG.message); michael@0: } michael@0: bool IsCharMessage(UINT aMessage) const michael@0: { michael@0: return (IsPrintableCharMessage(aMessage) || IsDeadCharMessage(aMessage)); michael@0: } michael@0: bool IsDeadCharMessage(const MSG& aMSG) const michael@0: { michael@0: return IsDeadCharMessage(aMSG.message); michael@0: } michael@0: bool IsDeadCharMessage(UINT aMessage) const michael@0: { michael@0: return (aMessage == WM_DEADCHAR || aMessage == WM_SYSDEADCHAR); michael@0: } michael@0: bool IsSysCharMessage(const MSG& aMSG) const michael@0: { michael@0: return IsSysCharMessage(aMSG.message); michael@0: } michael@0: bool IsSysCharMessage(UINT aMessage) const michael@0: { michael@0: return (aMessage == WM_SYSCHAR || aMessage == WM_SYSDEADCHAR); michael@0: } michael@0: bool MayBeSameCharMessage(const MSG& aCharMsg1, const MSG& aCharMsg2) const; michael@0: bool IsFollowedByDeadCharMessage() const; michael@0: michael@0: /** michael@0: * GetFollowingCharMessage() returns following char message of handling michael@0: * keydown event. If the message is found, this method returns true. michael@0: * Otherwise, returns false. michael@0: * michael@0: * WARNING: Even if this returns true, aCharMsg may be WM_NULL or its michael@0: * hwnd may be different window. michael@0: */ michael@0: bool GetFollowingCharMessage(MSG& aCharMsg) const; michael@0: michael@0: /** michael@0: * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK. michael@0: */ michael@0: uint8_t ComputeVirtualKeyCodeFromScanCode() const; michael@0: michael@0: /** michael@0: * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK_EX. michael@0: */ michael@0: uint8_t ComputeVirtualKeyCodeFromScanCodeEx() const; michael@0: michael@0: /** michael@0: * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK and MAPVK_VK_TO_CHAR. michael@0: */ michael@0: char16_t ComputeUnicharFromScanCode() const; michael@0: michael@0: /** michael@0: * Initializes the aKeyEvent with the information stored in the instance. michael@0: */ michael@0: void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, michael@0: const ModifierKeyState& aModKeyState) const; michael@0: void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const; michael@0: michael@0: /** michael@0: * Dispatches the key event. Returns true if the event is consumed. michael@0: * Otherwise, false. michael@0: */ michael@0: bool DispatchKeyEvent(WidgetKeyboardEvent& aKeyEvent, michael@0: const MSG* aMsgSentToPlugin = nullptr) const; michael@0: michael@0: /** michael@0: * DispatchKeyPressEventsWithKeyboardLayout() dispatches keypress event(s) michael@0: * with the information provided by KeyboardLayout class. michael@0: */ michael@0: bool DispatchKeyPressEventsWithKeyboardLayout() const; michael@0: michael@0: /** michael@0: * Remove all following WM_CHAR, WM_SYSCHAR and WM_DEADCHAR messages for the michael@0: * WM_KEYDOWN or WM_SYSKEYDOWN message. Additionally, dispatches plugin michael@0: * events if it's necessary. michael@0: * Returns true if the widget is destroyed. Otherwise, false. michael@0: */ michael@0: bool DispatchPluginEventsAndDiscardsCharMessages() const; michael@0: michael@0: /** michael@0: * DispatchKeyPressEventForFollowingCharMessage() dispatches keypress event michael@0: * for following WM_*CHAR message which is removed and set to aCharMsg. michael@0: * Returns true if the event is consumed. Otherwise, false. michael@0: */ michael@0: bool DispatchKeyPressEventForFollowingCharMessage(const MSG& aCharMsg) const; michael@0: michael@0: /** michael@0: * Checkes whether the key event down message is handled without following michael@0: * WM_CHAR messages. For example, if following WM_CHAR message indicates michael@0: * control character input, the WM_CHAR message is unclear whether it's michael@0: * caused by a printable key with Ctrl or just a function key such as Enter michael@0: * or Backspace. michael@0: */ michael@0: bool NeedsToHandleWithoutFollowingCharMessages() const; michael@0: }; michael@0: michael@0: class KeyboardLayout michael@0: { michael@0: friend class NativeKey; michael@0: michael@0: private: michael@0: KeyboardLayout(); michael@0: ~KeyboardLayout(); michael@0: michael@0: static KeyboardLayout* sInstance; michael@0: static nsIIdleServiceInternal* sIdleService; michael@0: michael@0: struct DeadKeyTableListEntry michael@0: { michael@0: DeadKeyTableListEntry* next; michael@0: uint8_t data[1]; michael@0: }; michael@0: michael@0: HKL mKeyboardLayout; michael@0: michael@0: VirtualKey mVirtualKeys[NS_NUM_OF_KEYS]; michael@0: DeadKeyTableListEntry* mDeadKeyTableListHead; michael@0: int32_t mActiveDeadKey; // -1 = no active dead-key michael@0: VirtualKey::ShiftState mDeadKeyShiftState; michael@0: michael@0: bool mIsOverridden : 1; michael@0: bool mIsPendingToRestoreKeyboardLayout : 1; michael@0: michael@0: static inline int32_t GetKeyIndex(uint8_t aVirtualKey); michael@0: static int CompareDeadKeyEntries(const void* aArg1, const void* aArg2, michael@0: void* aData); michael@0: static bool AddDeadKeyEntry(char16_t aBaseChar, char16_t aCompositeChar, michael@0: DeadKeyEntry* aDeadKeyArray, uint32_t aEntries); michael@0: bool EnsureDeadKeyActive(bool aIsActive, uint8_t aDeadKey, michael@0: const PBYTE aDeadKeyKbdState); michael@0: uint32_t 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: void DeactivateDeadKeyState(); michael@0: const DeadKeyTable* AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, michael@0: uint32_t aEntries); michael@0: void ReleaseDeadKeyTables(); michael@0: michael@0: /** michael@0: * Loads the specified keyboard layout. This method always clear the dead key michael@0: * state. michael@0: */ michael@0: void LoadLayout(HKL aLayout); michael@0: michael@0: /** michael@0: * InitNativeKey() must be called when actually widget receives WM_KEYDOWN or michael@0: * WM_KEYUP. This method is stateful. This saves current dead key state at michael@0: * WM_KEYDOWN. Additionally, computes current inputted character(s) and set michael@0: * them to the aNativeKey. michael@0: */ michael@0: void InitNativeKey(NativeKey& aNativeKey, michael@0: const ModifierKeyState& aModKeyState); michael@0: michael@0: public: michael@0: static KeyboardLayout* GetInstance(); michael@0: static void Shutdown(); michael@0: static void NotifyIdleServiceOfUserActivity(); michael@0: michael@0: static bool IsPrintableCharKey(uint8_t aVirtualKey); michael@0: michael@0: /** michael@0: * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState. michael@0: * This method isn't stateful. michael@0: */ michael@0: bool IsDeadKey(uint8_t aVirtualKey, michael@0: const ModifierKeyState& aModKeyState) const; michael@0: michael@0: /** michael@0: * GetUniCharsAndModifiers() returns characters which is inputted by the michael@0: * aVirtualKey with aModKeyState. This method isn't stateful. michael@0: */ michael@0: UniCharsAndModifiers GetUniCharsAndModifiers( michael@0: uint8_t aVirtualKey, michael@0: const ModifierKeyState& aModKeyState) const; michael@0: michael@0: /** michael@0: * OnLayoutChange() must be called before the first keydown message is michael@0: * received. LoadLayout() changes the keyboard state, that causes breaking michael@0: * dead key state. Therefore, we need to load the layout before the first michael@0: * keydown message. michael@0: */ michael@0: void OnLayoutChange(HKL aKeyboardLayout) michael@0: { michael@0: MOZ_ASSERT(!mIsOverridden); michael@0: LoadLayout(aKeyboardLayout); michael@0: } michael@0: michael@0: /** michael@0: * OverrideLayout() loads the specified keyboard layout. michael@0: */ michael@0: void OverrideLayout(HKL aLayout) michael@0: { michael@0: mIsOverridden = true; michael@0: LoadLayout(aLayout); michael@0: } michael@0: michael@0: /** michael@0: * RestoreLayout() loads the current keyboard layout of the thread. michael@0: */ michael@0: void RestoreLayout() michael@0: { michael@0: mIsOverridden = false; michael@0: mIsPendingToRestoreKeyboardLayout = true; michael@0: } michael@0: michael@0: uint32_t ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const; michael@0: michael@0: /** michael@0: * ConvertNativeKeyCodeToKeyNameIndex() returns KeyNameIndex value for michael@0: * non-printable keys (except some special keys like space key). michael@0: */ michael@0: KeyNameIndex ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const; michael@0: michael@0: HKL GetLayout() const michael@0: { michael@0: return mIsPendingToRestoreKeyboardLayout ? ::GetKeyboardLayout(0) : michael@0: mKeyboardLayout; michael@0: } michael@0: michael@0: /** michael@0: * This wraps MapVirtualKeyEx() API with MAPVK_VK_TO_VSC. michael@0: */ michael@0: WORD ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const; michael@0: michael@0: /** michael@0: * Implementation of nsIWidget::SynthesizeNativeKeyEvent(). michael@0: */ michael@0: nsresult 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: michael@0: class RedirectedKeyDownMessageManager michael@0: { michael@0: public: michael@0: /* michael@0: * If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is michael@0: * a redirected message, NativeKey::DispatchKeyDownAndKeyPressEvent() michael@0: * prevents to dispatch NS_KEY_DOWN event because it has been dispatched michael@0: * before the message was redirected. However, in some cases, WM_*KEYDOWN michael@0: * message handler may not handle actually. Then, the message handler needs michael@0: * to forget the redirected message and remove WM_CHAR message or WM_SYSCHAR michael@0: * message for the redirected keydown message. AutoFlusher class is a helper michael@0: * class for doing it. This must be created in the stack. michael@0: */ michael@0: class MOZ_STACK_CLASS AutoFlusher MOZ_FINAL michael@0: { michael@0: public: michael@0: AutoFlusher(nsWindowBase* aWidget, const MSG &aMsg) : michael@0: mCancel(!RedirectedKeyDownMessageManager::IsRedirectedMessage(aMsg)), michael@0: mWidget(aWidget), mMsg(aMsg) michael@0: { michael@0: } michael@0: michael@0: ~AutoFlusher() michael@0: { michael@0: if (mCancel) { michael@0: return; michael@0: } michael@0: // Prevent unnecessary keypress event michael@0: if (!mWidget->Destroyed()) { michael@0: RedirectedKeyDownMessageManager::RemoveNextCharMessage(mMsg.hwnd); michael@0: } michael@0: // Foreget the redirected message michael@0: RedirectedKeyDownMessageManager::Forget(); michael@0: } michael@0: michael@0: void Cancel() { mCancel = true; } michael@0: michael@0: private: michael@0: bool mCancel; michael@0: nsRefPtr mWidget; michael@0: const MSG &mMsg; michael@0: }; michael@0: michael@0: static void WillRedirect(const MSG& aMsg, bool aDefualtPrevented) michael@0: { michael@0: sRedirectedKeyDownMsg = aMsg; michael@0: sDefaultPreventedOfRedirectedMsg = aDefualtPrevented; michael@0: } michael@0: michael@0: static void Forget() michael@0: { michael@0: sRedirectedKeyDownMsg.message = WM_NULL; michael@0: } michael@0: michael@0: static void PreventDefault() { sDefaultPreventedOfRedirectedMsg = true; } michael@0: static bool DefaultPrevented() { return sDefaultPreventedOfRedirectedMsg; } michael@0: michael@0: static bool IsRedirectedMessage(const MSG& aMsg); michael@0: michael@0: /** michael@0: * RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM michael@0: * message handler. If there is no WM_(SYS)CHAR message for it, this michael@0: * method does nothing. michael@0: * NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is michael@0: * called in message loop. So, WM_(SYS)KEYDOWN message should have michael@0: * WM_(SYS)CHAR message in the queue if the keydown event causes character michael@0: * input. michael@0: */ michael@0: static void RemoveNextCharMessage(HWND aWnd); michael@0: michael@0: private: michael@0: // sRedirectedKeyDownMsg is WM_KEYDOWN message or WM_SYSKEYDOWN message which michael@0: // is reirected with SendInput() API by michael@0: // widget::NativeKey::DispatchKeyDownAndKeyPressEvent() michael@0: static MSG sRedirectedKeyDownMsg; michael@0: static bool sDefaultPreventedOfRedirectedMsg; michael@0: }; michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: michael@0: #endif