forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1120 lines
		
	
	
	
		
			39 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1120 lines
		
	
	
	
		
			39 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #ifndef KeyboardLayout_h__
 | |
| #define KeyboardLayout_h__
 | |
| 
 | |
| #include "mozilla/RefPtr.h"
 | |
| #include "nscore.h"
 | |
| #include "nsString.h"
 | |
| #include "nsWindowBase.h"
 | |
| #include "nsWindowDefs.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/EventForwards.h"
 | |
| #include "mozilla/TextEventDispatcher.h"
 | |
| #include "mozilla/widget/WinMessages.h"
 | |
| #include "mozilla/widget/WinModifierKeyState.h"
 | |
| #include <windows.h>
 | |
| 
 | |
| #define NS_NUM_OF_KEYS 70
 | |
| 
 | |
| #define VK_OEM_1 0xBA     // ';:' for US
 | |
| #define VK_OEM_PLUS 0xBB  // '+' any country
 | |
| #define VK_OEM_COMMA 0xBC
 | |
| #define VK_OEM_MINUS 0xBD  // '-' any country
 | |
| #define VK_OEM_PERIOD 0xBE
 | |
| #define VK_OEM_2 0xBF
 | |
| #define VK_OEM_3 0xC0
 | |
| // '/?' for Brazilian (ABNT)
 | |
| #define VK_ABNT_C1 0xC1
 | |
| // Separator in Numpad for Brazilian (ABNT) or JIS keyboard for Mac.
 | |
| #define VK_ABNT_C2 0xC2
 | |
| #define VK_OEM_4 0xDB
 | |
| #define VK_OEM_5 0xDC
 | |
| #define VK_OEM_6 0xDD
 | |
| #define VK_OEM_7 0xDE
 | |
| #define VK_OEM_8 0xDF
 | |
| #define VK_OEM_102 0xE2
 | |
| #define VK_OEM_CLEAR 0xFE
 | |
| 
 | |
| class nsIUserIdleServiceInternal;
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace widget {
 | |
| 
 | |
| enum ScanCode : uint16_t {
 | |
|   eCapsLock = 0x003A,
 | |
|   eNumLock = 0xE045,
 | |
|   eShiftLeft = 0x002A,
 | |
|   eShiftRight = 0x0036,
 | |
|   eControlLeft = 0x001D,
 | |
|   eControlRight = 0xE01D,
 | |
|   eAltLeft = 0x0038,
 | |
|   eAltRight = 0xE038,
 | |
| };
 | |
| 
 | |
| // 0: nsIWidget's native modifier flag
 | |
| // 1: Virtual keycode which does not distinguish whether left or right location.
 | |
| // 2: Virtual keycode which distinguishes whether left or right location.
 | |
| // 3: Scan code.
 | |
| static const uint32_t sModifierKeyMap[][4] = {
 | |
|     {nsIWidget::CAPS_LOCK, VK_CAPITAL, 0, ScanCode::eCapsLock},
 | |
|     {nsIWidget::NUM_LOCK, VK_NUMLOCK, 0, ScanCode::eNumLock},
 | |
|     {nsIWidget::SHIFT_L, VK_SHIFT, VK_LSHIFT, ScanCode::eShiftLeft},
 | |
|     {nsIWidget::SHIFT_R, VK_SHIFT, VK_RSHIFT, ScanCode::eShiftRight},
 | |
|     {nsIWidget::CTRL_L, VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft},
 | |
|     {nsIWidget::CTRL_R, VK_CONTROL, VK_RCONTROL, ScanCode::eControlRight},
 | |
|     {nsIWidget::ALT_L, VK_MENU, VK_LMENU, ScanCode::eAltLeft},
 | |
|     {nsIWidget::ALT_R, VK_MENU, VK_RMENU, ScanCode::eAltRight}};
 | |
| 
 | |
| class KeyboardLayout;
 | |
| 
 | |
| class MOZ_STACK_CLASS UniCharsAndModifiers final {
 | |
|  public:
 | |
|   UniCharsAndModifiers() {}
 | |
|   UniCharsAndModifiers operator+(const UniCharsAndModifiers& aOther) const;
 | |
|   UniCharsAndModifiers& operator+=(const UniCharsAndModifiers& aOther);
 | |
| 
 | |
|   /**
 | |
|    * Append a pair of unicode character and the final modifier.
 | |
|    */
 | |
|   void Append(char16_t aUniChar, Modifiers aModifiers);
 | |
|   void Clear() {
 | |
|     mChars.Truncate();
 | |
|     mModifiers.Clear();
 | |
|   }
 | |
|   bool IsEmpty() const {
 | |
|     MOZ_ASSERT(mChars.Length() == mModifiers.Length());
 | |
|     return mChars.IsEmpty();
 | |
|   }
 | |
| 
 | |
|   char16_t CharAt(size_t aIndex) const {
 | |
|     MOZ_ASSERT(aIndex < Length());
 | |
|     return mChars[aIndex];
 | |
|   }
 | |
|   Modifiers ModifiersAt(size_t aIndex) const {
 | |
|     MOZ_ASSERT(aIndex < Length());
 | |
|     return mModifiers[aIndex];
 | |
|   }
 | |
|   size_t Length() const {
 | |
|     MOZ_ASSERT(mChars.Length() == mModifiers.Length());
 | |
|     return mChars.Length();
 | |
|   }
 | |
| 
 | |
|   bool IsProducingCharsWithAltGr() const {
 | |
|     return !IsEmpty() && (ModifiersAt(0) & MODIFIER_ALTGRAPH) != 0;
 | |
|   }
 | |
| 
 | |
|   void FillModifiers(Modifiers aModifiers);
 | |
|   /**
 | |
|    * OverwriteModifiersIfBeginsWith() assigns mModifiers with aOther between
 | |
|    * [0] and [aOther.mLength - 1] only when mChars begins with aOther.mChars.
 | |
|    */
 | |
|   void OverwriteModifiersIfBeginsWith(const UniCharsAndModifiers& aOther);
 | |
| 
 | |
|   bool UniCharsEqual(const UniCharsAndModifiers& aOther) const;
 | |
|   bool UniCharsCaseInsensitiveEqual(const UniCharsAndModifiers& aOther) const;
 | |
|   bool BeginsWith(const UniCharsAndModifiers& aOther) const;
 | |
| 
 | |
|   const nsString& ToString() const { return mChars; }
 | |
| 
 | |
|  private:
 | |
|   nsAutoString mChars;
 | |
|   // 5 is enough number for normal keyboard layout handling.  On Windows,
 | |
|   // a dead key sequence may cause inputting up to 5 characters per key press.
 | |
|   CopyableAutoTArray<Modifiers, 5> mModifiers;
 | |
| };
 | |
| 
 | |
| struct DeadKeyEntry {
 | |
|   char16_t BaseChar;
 | |
|   char16_t CompositeChar;
 | |
| };
 | |
| 
 | |
| class DeadKeyTable {
 | |
|   friend class KeyboardLayout;
 | |
| 
 | |
|   uint16_t mEntries;
 | |
|   // KeyboardLayout::AddDeadKeyTable() will allocate as many entries as
 | |
|   // required.  It is the only way to create new DeadKeyTable instances.
 | |
|   DeadKeyEntry mTable[1];
 | |
| 
 | |
|   void Init(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) {
 | |
|     mEntries = aEntries;
 | |
|     memcpy(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry));
 | |
|   }
 | |
| 
 | |
|   static uint32_t SizeInBytes(uint32_t aEntries) {
 | |
|     return offsetof(DeadKeyTable, mTable) + aEntries * sizeof(DeadKeyEntry);
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   uint32_t Entries() const { return mEntries; }
 | |
| 
 | |
|   bool IsEqual(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const {
 | |
|     return (mEntries == aEntries &&
 | |
|             !memcmp(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry)));
 | |
|   }
 | |
| 
 | |
|   char16_t GetCompositeChar(char16_t aBaseChar) const;
 | |
| };
 | |
| 
 | |
| class VirtualKey {
 | |
|  public:
 | |
|   enum ShiftStateIndex : uint8_t {
 | |
|     //  0 - Normal
 | |
|     eNormal = 0,
 | |
|     //  1 - Shift
 | |
|     eShift,
 | |
|     //  2 - Control
 | |
|     eControl,
 | |
|     //  3 - Control + Shift
 | |
|     eControlShift,
 | |
|     //  4 - Alt
 | |
|     eAlt,
 | |
|     //  5 - Alt + Shift
 | |
|     eAltShift,
 | |
|     //  6 - Alt + Control (AltGr)
 | |
|     eAltGr,
 | |
|     //  7 - Alt + Control + Shift (AltGr + Shift)
 | |
|     eAltGrShift,
 | |
|     //  8 - CapsLock
 | |
|     eWithCapsLock,
 | |
|     //  9 - CapsLock + Shift
 | |
|     eShiftWithCapsLock,
 | |
|     // 10 - CapsLock + Control
 | |
|     eControlWithCapsLock,
 | |
|     // 11 - CapsLock + Control + Shift
 | |
|     eControlShiftWithCapsLock,
 | |
|     // 12 - CapsLock + Alt
 | |
|     eAltWithCapsLock,
 | |
|     // 13 - CapsLock + Alt + Shift
 | |
|     eAltShiftWithCapsLock,
 | |
|     // 14 - CapsLock + Alt + Control (CapsLock + AltGr)
 | |
|     eAltGrWithCapsLock,
 | |
|     // 15 - CapsLock + Alt + Control + Shift (CapsLock + AltGr + Shift)
 | |
|     eAltGrShiftWithCapsLock,
 | |
|   };
 | |
| 
 | |
|   enum ShiftStateFlag {
 | |
|     STATE_SHIFT = 0x01,
 | |
|     STATE_CONTROL = 0x02,
 | |
|     STATE_ALT = 0x04,
 | |
|     STATE_CAPSLOCK = 0x08,
 | |
|     // ShiftState needs to have AltGr state separately since this is necessary
 | |
|     // for lossless conversion with Modifiers.
 | |
|     STATE_ALTGRAPH = 0x80,
 | |
|     // Useful to remove or check Ctrl and Alt flags.
 | |
|     STATE_CONTROL_ALT = STATE_CONTROL | STATE_ALT,
 | |
|   };
 | |
| 
 | |
|   typedef uint8_t ShiftState;
 | |
| 
 | |
|   static ShiftState ModifiersToShiftState(Modifiers aModifiers);
 | |
|   static ShiftState ModifierKeyStateToShiftState(
 | |
|       const ModifierKeyState& aModKeyState) {
 | |
|     return ModifiersToShiftState(aModKeyState.GetModifiers());
 | |
|   }
 | |
|   static Modifiers ShiftStateToModifiers(ShiftState aShiftState);
 | |
|   static bool IsAltGrIndex(uint8_t aIndex) {
 | |
|     return (aIndex & STATE_CONTROL_ALT) == STATE_CONTROL_ALT;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   union KeyShiftState {
 | |
|     struct {
 | |
|       char16_t Chars[4];
 | |
|     } Normal;
 | |
|     struct {
 | |
|       const DeadKeyTable* Table;
 | |
|       char16_t DeadChar;
 | |
|     } DeadKey;
 | |
|   };
 | |
| 
 | |
|   KeyShiftState mShiftStates[16];
 | |
|   uint16_t mIsDeadKey;
 | |
| 
 | |
|   static uint8_t ToShiftStateIndex(ShiftState aShiftState) {
 | |
|     if (!(aShiftState & STATE_ALTGRAPH)) {
 | |
|       MOZ_ASSERT(aShiftState <= eAltGrShiftWithCapsLock);
 | |
|       return static_cast<uint8_t>(aShiftState);
 | |
|     }
 | |
|     uint8_t index = aShiftState & ~STATE_ALTGRAPH;
 | |
|     index |= (STATE_ALT | STATE_CONTROL);
 | |
|     MOZ_ASSERT(index <= eAltGrShiftWithCapsLock);
 | |
|     return index;
 | |
|   }
 | |
| 
 | |
|   void SetDeadKey(ShiftState aShiftState, bool aIsDeadKey) {
 | |
|     if (aIsDeadKey) {
 | |
|       mIsDeadKey |= 1 << ToShiftStateIndex(aShiftState);
 | |
|     } else {
 | |
|       mIsDeadKey &= ~(1 << ToShiftStateIndex(aShiftState));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   static void FillKbdState(PBYTE aKbdState, const ShiftState aShiftState);
 | |
| 
 | |
|   bool IsDeadKey(ShiftState aShiftState) const {
 | |
|     return (mIsDeadKey & (1 << ToShiftStateIndex(aShiftState))) != 0;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * IsChangedByAltGr() is useful to check if a key with AltGr produces
 | |
|    * different character(s) from the key without AltGr.
 | |
|    * Note that this is designed for checking if a keyboard layout has AltGr
 | |
|    * key.  So, this result may not exactly correct for the key since it's
 | |
|    * okay to fails in some edge cases when we check all keys which produce
 | |
|    * character(s) in a layout.
 | |
|    */
 | |
|   bool IsChangedByAltGr(ShiftState aShiftState) const {
 | |
|     MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));
 | |
|     MOZ_ASSERT(IsAltGrIndex(aShiftState));
 | |
|     MOZ_ASSERT(IsDeadKey(aShiftState) ||
 | |
|                mShiftStates[aShiftState].Normal.Chars[0]);
 | |
|     const ShiftState kShiftStateWithoutAltGr =
 | |
|         aShiftState - ShiftStateIndex::eAltGr;
 | |
|     if (IsDeadKey(aShiftState) != IsDeadKey(kShiftStateWithoutAltGr)) {
 | |
|       return false;
 | |
|     }
 | |
|     if (IsDeadKey(aShiftState)) {
 | |
|       return mShiftStates[aShiftState].DeadKey.DeadChar !=
 | |
|              mShiftStates[kShiftStateWithoutAltGr].DeadKey.DeadChar;
 | |
|     }
 | |
|     for (size_t i = 0; i < 4; i++) {
 | |
|       if (mShiftStates[aShiftState].Normal.Chars[i] !=
 | |
|           mShiftStates[kShiftStateWithoutAltGr].Normal.Chars[i]) {
 | |
|         return true;
 | |
|       }
 | |
|       if (!mShiftStates[aShiftState].Normal.Chars[i] &&
 | |
|           !mShiftStates[kShiftStateWithoutAltGr].Normal.Chars[i]) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   void AttachDeadKeyTable(ShiftState aShiftState,
 | |
|                           const DeadKeyTable* aDeadKeyTable) {
 | |
|     MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));
 | |
|     mShiftStates[aShiftState].DeadKey.Table = aDeadKeyTable;
 | |
|   }
 | |
| 
 | |
|   void SetNormalChars(ShiftState aShiftState, const char16_t* aChars,
 | |
|                       uint32_t aNumOfChars);
 | |
|   void SetDeadChar(ShiftState aShiftState, char16_t aDeadChar);
 | |
|   const DeadKeyTable* MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
 | |
|                                            uint32_t aEntries) const;
 | |
|   inline char16_t GetCompositeChar(ShiftState aShiftState,
 | |
|                                    char16_t aBaseChar) const {
 | |
|     return mShiftStates[ToShiftStateIndex(aShiftState)]
 | |
|         .DeadKey.Table->GetCompositeChar(aBaseChar);
 | |
|   }
 | |
| 
 | |
|   char16_t GetCompositeChar(const ModifierKeyState& aModKeyState,
 | |
|                             char16_t aBaseChar) const {
 | |
|     return GetCompositeChar(ModifierKeyStateToShiftState(aModKeyState),
 | |
|                             aBaseChar);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * GetNativeUniChars() returns character(s) which is produced by the
 | |
|    * key with given modifiers.  This does NOT return proper MODIFIER_ALTGRAPH
 | |
|    * state because this is raw accessor of the database of this key.
 | |
|    */
 | |
|   UniCharsAndModifiers GetNativeUniChars(ShiftState aShiftState) const;
 | |
|   UniCharsAndModifiers GetNativeUniChars(
 | |
|       const ModifierKeyState& aModKeyState) const {
 | |
|     return GetNativeUniChars(ModifierKeyStateToShiftState(aModKeyState));
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * GetUniChars() returns characters and modifiers which are not consumed
 | |
|    * to input the character.
 | |
|    * For example, if you specify Ctrl key but the key produces no character
 | |
|    * with Ctrl, this returns character(s) which is produced by the key
 | |
|    * without Ctrl.  So, the result is useful to decide KeyboardEvent.key
 | |
|    * value.
 | |
|    * Another example is, if you specify Ctrl key and the key produces
 | |
|    * different character(s) from the case without Ctrl key, this returns
 | |
|    * the character(s) *without* MODIFIER_CONTROL.  This modifier information
 | |
|    * is useful for eKeyPress since TextEditor does not treat eKeyPress events
 | |
|    * whose modifier includes MODIFIER_ALT and/or MODIFIER_CONTROL.
 | |
|    *
 | |
|    * @param aShiftState         Modifiers which you want to retrieve
 | |
|    *                            KeyboardEvent.key value for the key with.
 | |
|    *                            If AltGr key is pressed, this should include
 | |
|    *                            STATE_ALTGRAPH and should NOT include
 | |
|    *                            STATE_ALT nor STATE_CONTROL.
 | |
|    *                            If both Alt and Ctrl are pressed to emulate
 | |
|    *                            AltGr, this should include both STATE_ALT and
 | |
|    *                            STATE_CONTROL but should NOT include
 | |
|    *                            MODIFIER_ALTGRAPH.
 | |
|    *                            Then, this returns proper modifiers when
 | |
|    *                            this key produces no character with AltGr.
 | |
|    */
 | |
|   UniCharsAndModifiers GetUniChars(ShiftState aShiftState) const;
 | |
|   UniCharsAndModifiers GetUniChars(const ModifierKeyState& aModKeyState) const {
 | |
|     return GetUniChars(ModifierKeyStateToShiftState(aModKeyState));
 | |
|   }
 | |
| };
 | |
| 
 | |
| class MOZ_STACK_CLASS NativeKey final {
 | |
|   friend class KeyboardLayout;
 | |
| 
 | |
|  public:
 | |
|   struct FakeCharMsg {
 | |
|     UINT mCharCode;
 | |
|     UINT mScanCode;
 | |
|     bool mIsSysKey;
 | |
|     bool mIsDeadKey;
 | |
|     bool mConsumed;
 | |
| 
 | |
|     FakeCharMsg()
 | |
|         : mCharCode(0),
 | |
|           mScanCode(0),
 | |
|           mIsSysKey(false),
 | |
|           mIsDeadKey(false),
 | |
|           mConsumed(false) {}
 | |
| 
 | |
|     MSG GetCharMsg(HWND aWnd) const {
 | |
|       MSG msg;
 | |
|       msg.hwnd = aWnd;
 | |
|       msg.message = mIsDeadKey && mIsSysKey ? WM_SYSDEADCHAR
 | |
|                     : mIsDeadKey            ? WM_DEADCHAR
 | |
|                     : mIsSysKey             ? WM_SYSCHAR
 | |
|                                             : WM_CHAR;
 | |
|       msg.wParam = static_cast<WPARAM>(mCharCode);
 | |
|       msg.lParam = static_cast<LPARAM>(mScanCode << 16);
 | |
|       msg.time = 0;
 | |
|       msg.pt.x = msg.pt.y = 0;
 | |
|       return msg;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   NativeKey(nsWindowBase* aWidget, const MSG& aMessage,
 | |
|             const ModifierKeyState& aModKeyState,
 | |
|             HKL aOverrideKeyboardLayout = 0,
 | |
|             nsTArray<FakeCharMsg>* aFakeCharMsgs = nullptr);
 | |
| 
 | |
|   ~NativeKey();
 | |
| 
 | |
|   /**
 | |
|    * Handle WM_KEYDOWN message or WM_SYSKEYDOWN message.  The instance must be
 | |
|    * initialized with WM_KEYDOWN or WM_SYSKEYDOWN.
 | |
|    * Returns true if dispatched keydown event or keypress event is consumed.
 | |
|    * Otherwise, false.
 | |
|    */
 | |
|   bool HandleKeyDownMessage(bool* aEventDispatched = nullptr) const;
 | |
| 
 | |
|   /**
 | |
|    * Handles WM_CHAR message or WM_SYSCHAR message.  The instance must be
 | |
|    * initialized with them.
 | |
|    * Returns true if dispatched keypress event is consumed.  Otherwise, false.
 | |
|    */
 | |
|   bool HandleCharMessage(bool* aEventDispatched = nullptr) const;
 | |
| 
 | |
|   /**
 | |
|    * Handles keyup message.  Returns true if the event is consumed.
 | |
|    * Otherwise, false.
 | |
|    */
 | |
|   bool HandleKeyUpMessage(bool* aEventDispatched = nullptr) const;
 | |
| 
 | |
|   /**
 | |
|    * Handles WM_APPCOMMAND message.  Returns true if the event is consumed.
 | |
|    * Otherwise, false.
 | |
|    */
 | |
|   bool HandleAppCommandMessage() const;
 | |
| 
 | |
|   /**
 | |
|    * Callback of TextEventDispatcherListener::WillDispatchKeyboardEvent().
 | |
|    * This method sets alternative char codes of aKeyboardEvent.
 | |
|    */
 | |
|   void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
 | |
|                                  uint32_t aIndex);
 | |
| 
 | |
|   /**
 | |
|    * Returns true if aChar is a control character which shouldn't be inputted
 | |
|    * into focused text editor.
 | |
|    */
 | |
|   static bool IsControlChar(char16_t aChar);
 | |
| 
 | |
|   bool IsShift() const { return mModKeyState.IsShift(); }
 | |
|   bool IsControl() const { return mModKeyState.IsControl(); }
 | |
|   bool IsAlt() const { return mModKeyState.IsAlt(); }
 | |
|   bool MaybeEmulatingAltGraph() const;
 | |
|   Modifiers GetModifiers() const { return mModKeyState.GetModifiers(); }
 | |
|   const ModifierKeyState& ModifierKeyStateRef() const { return mModKeyState; }
 | |
|   VirtualKey::ShiftState GetShiftState() const {
 | |
|     return VirtualKey::ModifierKeyStateToShiftState(mModKeyState);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * GenericVirtualKeyCode() returns virtual keycode which cannot distinguish
 | |
|    * position of modifier keys.  E.g., VK_CONTROL for both ControlLeft and
 | |
|    * ControlRight.
 | |
|    */
 | |
|   uint8_t GenericVirtualKeyCode() const { return mOriginalVirtualKeyCode; }
 | |
| 
 | |
|   /**
 | |
|    * SpecificVirtualKeyCode() returns virtual keycode which can distinguish
 | |
|    * position of modifier keys.  E.g., returns VK_LCONTROL or VK_RCONTROL
 | |
|    * instead of VK_CONTROL.  If the key message is synthesized with not
 | |
|    * enough information, this prefers left position's keycode.
 | |
|    */
 | |
|   uint8_t SpecificVirtualKeyCode() const { return mVirtualKeyCode; }
 | |
| 
 | |
|  private:
 | |
|   NativeKey* mLastInstance;
 | |
|   // mRemovingMsg is set at removing a char message from
 | |
|   // GetFollowingCharMessage().
 | |
|   MSG mRemovingMsg;
 | |
|   // mReceivedMsg is set when another instance starts to handle the message
 | |
|   // unexpectedly.
 | |
|   MSG mReceivedMsg;
 | |
|   RefPtr<nsWindowBase> mWidget;
 | |
|   RefPtr<TextEventDispatcher> mDispatcher;
 | |
|   HKL mKeyboardLayout;
 | |
|   MSG mMsg;
 | |
|   // mFollowingCharMsgs stores WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or
 | |
|   // WM_SYSDEADCHAR message which follows WM_KEYDOWN.
 | |
|   // Note that the stored messaged are already removed from the queue.
 | |
|   // FYI: 5 is enough number for usual keyboard layout handling.  On Windows,
 | |
|   // a dead key sequence may cause inputting up to 5 characters per key press.
 | |
|   AutoTArray<MSG, 5> mFollowingCharMsgs;
 | |
|   // mRemovedOddCharMsgs stores WM_CHAR messages which are caused by ATOK or
 | |
|   // WXG (they are Japanese IME) when the user tries to do "Kakutei-undo"
 | |
|   // (it means "undo the last commit").
 | |
|   nsTArray<MSG> mRemovedOddCharMsgs;
 | |
|   // If dispatching eKeyDown or eKeyPress event causes focus change,
 | |
|   // the instance shouldn't handle remaning char messages.  For checking it,
 | |
|   // this should store first focused window.
 | |
|   HWND mFocusedWndBeforeDispatch;
 | |
| 
 | |
|   uint32_t mDOMKeyCode;
 | |
|   KeyNameIndex mKeyNameIndex;
 | |
|   CodeNameIndex mCodeNameIndex;
 | |
| 
 | |
|   ModifierKeyState mModKeyState;
 | |
| 
 | |
|   // mVirtualKeyCode distinguishes left key or right key of modifier key.
 | |
|   uint8_t mVirtualKeyCode;
 | |
|   // mOriginalVirtualKeyCode doesn't distinguish left key or right key of
 | |
|   // modifier key.  However, if the given keycode is VK_PROCESS, it's resolved
 | |
|   // to a keycode before it's handled by IME.
 | |
|   uint8_t mOriginalVirtualKeyCode;
 | |
| 
 | |
|   // mCommittedChars indicates the inputted characters which is committed by
 | |
|   // the key.  If dead key fail to composite a character, mCommittedChars
 | |
|   // indicates both the dead characters and the base characters.
 | |
|   UniCharsAndModifiers mCommittedCharsAndModifiers;
 | |
| 
 | |
|   // Following strings are computed by
 | |
|   // ComputeInputtingStringWithKeyboardLayout() which is typically called
 | |
|   // before dispatching keydown event.
 | |
|   // mInputtingStringAndModifiers's string is the string to be
 | |
|   // inputted into the focused editor and its modifier state is proper
 | |
|   // modifier state for inputting the string into the editor.
 | |
|   UniCharsAndModifiers mInputtingStringAndModifiers;
 | |
|   // mShiftedString is the string to be inputted into the editor with
 | |
|   // current modifier state with active shift state.
 | |
|   UniCharsAndModifiers mShiftedString;
 | |
|   // mUnshiftedString is the string to be inputted into the editor with
 | |
|   // current modifier state without shift state.
 | |
|   UniCharsAndModifiers mUnshiftedString;
 | |
|   // Following integers are computed by
 | |
|   // ComputeInputtingStringWithKeyboardLayout() which is typically called
 | |
|   // before dispatching keydown event.  The meaning of these values is same
 | |
|   // as charCode.
 | |
|   uint32_t mShiftedLatinChar;
 | |
|   uint32_t mUnshiftedLatinChar;
 | |
| 
 | |
|   WORD mScanCode;
 | |
|   bool mIsExtended;
 | |
|   // mIsRepeat is true if the key message is caused by the auto-repeat
 | |
|   // feature.
 | |
|   bool mIsRepeat;
 | |
|   bool mIsDeadKey;
 | |
|   // mIsPrintableKey is true if the key may be a printable key without
 | |
|   // any modifier keys.  Otherwise, false.
 | |
|   // Please note that the event may not cause any text input even if this
 | |
|   // is true.  E.g., it might be dead key state or Ctrl key may be pressed.
 | |
|   bool mIsPrintableKey;
 | |
|   // mIsSkippableInRemoteProcess is false if the key event shouldn't be
 | |
|   // skipped in the remote process even if it's too old event.
 | |
|   bool mIsSkippableInRemoteProcess;
 | |
|   // mCharMessageHasGone is true if the message is a keydown message and
 | |
|   // it's followed by at least one char message but it's gone at removing
 | |
|   // from the queue.  This could occur if PeekMessage() or something is
 | |
|   // hooked by odd tool.
 | |
|   bool mCharMessageHasGone;
 | |
|   // mIsOverridingKeyboardLayout is true if the instance temporarily overriding
 | |
|   // keyboard layout with specified by the constructor.
 | |
|   bool mIsOverridingKeyboardLayout;
 | |
|   // mCanIgnoreModifierStateAtKeyPress is true if it's allowed to remove
 | |
|   // Ctrl or Alt modifier state at dispatching eKeyPress.
 | |
|   bool mCanIgnoreModifierStateAtKeyPress;
 | |
| 
 | |
|   nsTArray<FakeCharMsg>* mFakeCharMsgs;
 | |
| 
 | |
|   // When a keydown event is dispatched at handling WM_APPCOMMAND, the computed
 | |
|   // virtual keycode is set to this.  Even if we consume WM_APPCOMMAND message,
 | |
|   // Windows may send WM_KEYDOWN and WM_KEYUP message for them.
 | |
|   // At that time, we should not dispatch key events for them.
 | |
|   static uint8_t sDispatchedKeyOfAppCommand;
 | |
| 
 | |
|   NativeKey() {
 | |
|     MOZ_CRASH("The default constructor of NativeKey isn't available");
 | |
|   }
 | |
| 
 | |
|   void InitWithAppCommand();
 | |
|   void InitWithKeyOrChar();
 | |
| 
 | |
|   /**
 | |
|    * InitIsSkippableForKeyOrChar() initializes mIsSkippableInRemoteProcess with
 | |
|    * mIsRepeat and previous key message information.  So, this must be called
 | |
|    * after mIsRepeat is initialized.
 | |
|    */
 | |
|   void InitIsSkippableForKeyOrChar(const MSG& aLastKeyMSG);
 | |
| 
 | |
|   /**
 | |
|    * InitCommittedCharsAndModifiersWithFollowingCharMessages() initializes
 | |
|    * mCommittedCharsAndModifiers with mFollowingCharMsgs and mModKeyState.
 | |
|    * If mFollowingCharMsgs includes non-printable char messages, they are
 | |
|    * ignored (skipped).
 | |
|    */
 | |
|   void InitCommittedCharsAndModifiersWithFollowingCharMessages();
 | |
| 
 | |
|   UINT GetScanCodeWithExtendedFlag() const;
 | |
| 
 | |
|   // The result is one of eKeyLocation*.
 | |
|   uint32_t GetKeyLocation() const;
 | |
| 
 | |
|   /**
 | |
|    * RemoveFollowingOddCharMessages() removes odd WM_CHAR messages from the
 | |
|    * queue when IsIMEDoingKakuteiUndo() returns true.
 | |
|    */
 | |
|   void RemoveFollowingOddCharMessages();
 | |
| 
 | |
|   /**
 | |
|    * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes
 | |
|    * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern.  So, when this
 | |
|    * returns true, the caller needs to be careful for processing the messages.
 | |
|    */
 | |
|   bool IsIMEDoingKakuteiUndo() const;
 | |
| 
 | |
|   /**
 | |
|    * This returns true if user types a number key in numpad with Alt key
 | |
|    * to input a Unicode character from its scalar value.
 | |
|    * Note that inputting Unicode scalar value is available without NumLock.
 | |
|    * Therefore, this returns true even if user presses a function key on
 | |
|    * numpad without NumLock, but that may be intended to perform a shortcut
 | |
|    * key like Alt + Home.
 | |
|    */
 | |
|   bool MaybeTypingUnicodeScalarValue() const {
 | |
|     return !mIsExtended && IsSysKeyDownOrKeyUpMessage() && IsAlt() &&
 | |
|            !IsControl() && !IsShift() &&
 | |
|            ((mScanCode >= 0x004F && mScanCode <= 0x0052) ||  // Numpad0-3
 | |
|             (mScanCode >= 0x004B && mScanCode <= 0x004D) ||  // Numpad4-6
 | |
|             (mScanCode >= 0x0047 && mScanCode <= 0x0049));   // Numpad7-9
 | |
|   }
 | |
| 
 | |
|   bool IsKeyDownMessage() const {
 | |
|     return mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN;
 | |
|   }
 | |
|   bool IsKeyUpMessage() const {
 | |
|     return mMsg.message == WM_KEYUP || mMsg.message == WM_SYSKEYUP;
 | |
|   }
 | |
|   bool IsSysKeyDownOrKeyUpMessage() const {
 | |
|     return mMsg.message == WM_SYSKEYDOWN || mMsg.message == WM_SYSKEYUP;
 | |
|   }
 | |
|   bool IsCharOrSysCharMessage(const MSG& aMSG) const {
 | |
|     return IsCharOrSysCharMessage(aMSG.message);
 | |
|   }
 | |
|   bool IsCharOrSysCharMessage(UINT aMessage) const {
 | |
|     return (aMessage == WM_CHAR || aMessage == WM_SYSCHAR);
 | |
|   }
 | |
|   bool IsCharMessage(const MSG& aMSG) const {
 | |
|     return IsCharMessage(aMSG.message);
 | |
|   }
 | |
|   bool IsCharMessage(UINT aMessage) const {
 | |
|     return (IsCharOrSysCharMessage(aMessage) || IsDeadCharMessage(aMessage));
 | |
|   }
 | |
|   bool IsDeadCharMessage(const MSG& aMSG) const {
 | |
|     return IsDeadCharMessage(aMSG.message);
 | |
|   }
 | |
|   bool IsDeadCharMessage(UINT aMessage) const {
 | |
|     return (aMessage == WM_DEADCHAR || aMessage == WM_SYSDEADCHAR);
 | |
|   }
 | |
|   bool IsSysCharMessage(const MSG& aMSG) const {
 | |
|     return IsSysCharMessage(aMSG.message);
 | |
|   }
 | |
|   bool IsSysCharMessage(UINT aMessage) const {
 | |
|     return (aMessage == WM_SYSCHAR || aMessage == WM_SYSDEADCHAR);
 | |
|   }
 | |
|   bool MayBeSameCharMessage(const MSG& aCharMsg1, const MSG& aCharMsg2) const;
 | |
|   bool IsSamePhysicalKeyMessage(const MSG& aKeyOrCharMsg1,
 | |
|                                 const MSG& aKeyOrCharMsg2) const;
 | |
|   bool IsFollowedByPrintableCharMessage() const;
 | |
|   bool IsFollowedByPrintableCharOrSysCharMessage() const;
 | |
|   bool IsFollowedByDeadCharMessage() const;
 | |
|   bool IsPrintableCharMessage(const MSG& aMSG) const {
 | |
|     return aMSG.message == WM_CHAR &&
 | |
|            !IsControlChar(static_cast<char16_t>(aMSG.wParam));
 | |
|   }
 | |
|   bool IsEnterKeyPressCharMessage(const MSG& aMSG) const {
 | |
|     return aMSG.message == WM_CHAR && aMSG.wParam == '\r';
 | |
|   }
 | |
|   bool IsPrintableCharOrSysCharMessage(const MSG& aMSG) const {
 | |
|     return IsCharOrSysCharMessage(aMSG) &&
 | |
|            !IsControlChar(static_cast<char16_t>(aMSG.wParam));
 | |
|   }
 | |
|   bool IsControlCharMessage(const MSG& aMSG) const {
 | |
|     return IsCharMessage(aMSG.message) &&
 | |
|            IsControlChar(static_cast<char16_t>(aMSG.wParam));
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * IsReservedBySystem() returns true if the key combination is reserved by
 | |
|    * the system.  Even if it's consumed by web apps, the message should be
 | |
|    * sent to next wndproc.
 | |
|    */
 | |
|   bool IsReservedBySystem() const;
 | |
| 
 | |
|   /**
 | |
|    * GetFollowingCharMessage() returns following char message of handling
 | |
|    * keydown event.  If the message is found, this method returns true.
 | |
|    * Otherwise, returns false.
 | |
|    *
 | |
|    * WARNING: Even if this returns true, aCharMsg may be WM_NULL or its
 | |
|    *          hwnd may be different window.
 | |
|    */
 | |
|   bool GetFollowingCharMessage(MSG& aCharMsg);
 | |
| 
 | |
|   /**
 | |
|    * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK.
 | |
|    */
 | |
|   uint8_t ComputeVirtualKeyCodeFromScanCode() const;
 | |
| 
 | |
|   /**
 | |
|    * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK_EX.
 | |
|    */
 | |
|   uint8_t ComputeVirtualKeyCodeFromScanCodeEx() const;
 | |
| 
 | |
|   /**
 | |
|    * Wraps MapVirtualKeyEx() with MAPVK_VK_TO_VSC_EX or MAPVK_VK_TO_VSC.
 | |
|    */
 | |
|   uint16_t ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode) const;
 | |
| 
 | |
|   /**
 | |
|    * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK and MAPVK_VK_TO_CHAR.
 | |
|    */
 | |
|   char16_t ComputeUnicharFromScanCode() const;
 | |
| 
 | |
|   /**
 | |
|    * Initializes the aKeyEvent with the information stored in the instance.
 | |
|    */
 | |
|   nsEventStatus InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
 | |
|                              const ModifierKeyState& aModKeyState) const;
 | |
|   nsEventStatus InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const;
 | |
| 
 | |
|   /**
 | |
|    * Dispatches a command event for aEventCommand.
 | |
|    * Returns true if the event is consumed.  Otherwise, false.
 | |
|    */
 | |
|   bool DispatchCommandEvent(uint32_t aEventCommand) const;
 | |
| 
 | |
|   /**
 | |
|    * DispatchKeyPressEventsWithRetrievedCharMessages() dispatches keypress
 | |
|    * event(s) with retrieved char messages.
 | |
|    */
 | |
|   bool DispatchKeyPressEventsWithRetrievedCharMessages() const;
 | |
| 
 | |
|   /**
 | |
|    * DispatchKeyPressEventsWithoutCharMessage() dispatches keypress event(s)
 | |
|    * without char messages.  So, this should be used only when there are no
 | |
|    * following char messages.
 | |
|    */
 | |
|   bool DispatchKeyPressEventsWithoutCharMessage() const;
 | |
| 
 | |
|   /**
 | |
|    * Checkes whether the key event down message is handled without following
 | |
|    * WM_CHAR messages.  For example, if following WM_CHAR message indicates
 | |
|    * control character input, the WM_CHAR message is unclear whether it's
 | |
|    * caused by a printable key with Ctrl or just a function key such as Enter
 | |
|    * or Backspace.
 | |
|    */
 | |
|   bool NeedsToHandleWithoutFollowingCharMessages() const;
 | |
| 
 | |
|   /**
 | |
|    * ComputeInputtingStringWithKeyboardLayout() computes string to be inputted
 | |
|    * with the key and the modifier state, without shift state and with shift
 | |
|    * state.
 | |
|    */
 | |
|   void ComputeInputtingStringWithKeyboardLayout();
 | |
| 
 | |
|   /**
 | |
|    * IsFocusedWindowChanged() returns true if focused window is changed
 | |
|    * after the instance is created.
 | |
|    */
 | |
|   bool IsFocusedWindowChanged() const {
 | |
|     return mFocusedWndBeforeDispatch != ::GetFocus();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handles WM_CHAR message or WM_SYSCHAR message.  The instance must be
 | |
|    * initialized with WM_KEYDOWN, WM_SYSKEYDOWN or them.
 | |
|    * Returns true if dispatched keypress event is consumed.  Otherwise, false.
 | |
|    */
 | |
|   bool HandleCharMessage(const MSG& aCharMsg,
 | |
|                          bool* aEventDispatched = nullptr) const;
 | |
| 
 | |
|   // Calls of PeekMessage() from NativeKey might cause nested message handling
 | |
|   // due to (perhaps) odd API hook.  NativeKey should do nothing if given
 | |
|   // message is tried to be retrieved by another instance.
 | |
| 
 | |
|   /**
 | |
|    * sLatestInstacne is a pointer to the newest instance of NativeKey which is
 | |
|    * handling a key or char message(s).
 | |
|    */
 | |
|   static NativeKey* sLatestInstance;
 | |
| 
 | |
|   static const MSG sEmptyMSG;
 | |
| 
 | |
|   static MSG sLastKeyOrCharMSG;
 | |
| 
 | |
|   static MSG sLastKeyMSG;
 | |
| 
 | |
|   static bool IsEmptyMSG(const MSG& aMSG) {
 | |
|     return !memcmp(&aMSG, &sEmptyMSG, sizeof(MSG));
 | |
|   }
 | |
| 
 | |
|   bool IsAnotherInstanceRemovingCharMessage() const {
 | |
|     return mLastInstance && !IsEmptyMSG(mLastInstance->mRemovingMsg);
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   /**
 | |
|    * Returns last key or char MSG.  If no MSG has been received yet, the result
 | |
|    * is empty MSG (i.e., .message is WM_NULL).
 | |
|    */
 | |
|   static const MSG& LastKeyOrCharMSG() { return sLastKeyOrCharMSG; }
 | |
| };
 | |
| 
 | |
| class KeyboardLayout {
 | |
|  public:
 | |
|   static KeyboardLayout* GetInstance();
 | |
|   static void Shutdown();
 | |
|   static HKL GetActiveLayout();
 | |
|   static nsCString GetActiveLayoutName();
 | |
|   static void NotifyIdleServiceOfUserActivity();
 | |
| 
 | |
|   static bool IsPrintableCharKey(uint8_t aVirtualKey);
 | |
| 
 | |
|   /**
 | |
|    * HasAltGr() returns true if the keyboard layout's AltRight key is AltGr
 | |
|    * key.
 | |
|    */
 | |
|   bool HasAltGr() const { return mHasAltGr; }
 | |
| 
 | |
|   /**
 | |
|    * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState.
 | |
|    * This method isn't stateful.
 | |
|    */
 | |
|   bool IsDeadKey(uint8_t aVirtualKey,
 | |
|                  const ModifierKeyState& aModKeyState) const;
 | |
|   bool IsDeadKey(const NativeKey& aNativeKey) const {
 | |
|     return IsDeadKey(aNativeKey.GenericVirtualKeyCode(),
 | |
|                      aNativeKey.ModifierKeyStateRef());
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * IsInDeadKeySequence() returns true when it's in a dead key sequence.
 | |
|    * It starts when a dead key is down and ends when another key down causes
 | |
|    * inactivating the dead key state.
 | |
|    */
 | |
|   bool IsInDeadKeySequence() const { return !mActiveDeadKeys.IsEmpty(); }
 | |
| 
 | |
|   /**
 | |
|    * IsSysKey() returns true if aVirtualKey with aModKeyState causes WM_SYSKEY*
 | |
|    * or WM_SYS*CHAR messages.
 | |
|    */
 | |
|   bool IsSysKey(uint8_t aVirtualKey,
 | |
|                 const ModifierKeyState& aModKeyState) const;
 | |
|   bool IsSysKey(const NativeKey& aNativeKey) const {
 | |
|     return IsSysKey(aNativeKey.GenericVirtualKeyCode(),
 | |
|                     aNativeKey.ModifierKeyStateRef());
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * GetUniCharsAndModifiers() returns characters which are inputted by
 | |
|    * aVirtualKey with aModKeyState.  This method isn't stateful.
 | |
|    * Note that if the combination causes text input, the result's Ctrl and
 | |
|    * Alt key state are never active.
 | |
|    */
 | |
|   UniCharsAndModifiers GetUniCharsAndModifiers(
 | |
|       uint8_t aVirtualKey, const ModifierKeyState& aModKeyState) const {
 | |
|     VirtualKey::ShiftState shiftState =
 | |
|         VirtualKey::ModifierKeyStateToShiftState(aModKeyState);
 | |
|     return GetUniCharsAndModifiers(aVirtualKey, shiftState);
 | |
|   }
 | |
|   UniCharsAndModifiers GetUniCharsAndModifiers(
 | |
|       const NativeKey& aNativeKey) const {
 | |
|     return GetUniCharsAndModifiers(aNativeKey.GenericVirtualKeyCode(),
 | |
|                                    aNativeKey.GetShiftState());
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * OnLayoutChange() must be called before the first keydown message is
 | |
|    * received.  LoadLayout() changes the keyboard state, that causes breaking
 | |
|    * dead key state.  Therefore, we need to load the layout before the first
 | |
|    * keydown message.
 | |
|    */
 | |
|   void OnLayoutChange(HKL aKeyboardLayout) {
 | |
|     MOZ_ASSERT(!mIsOverridden);
 | |
|     LoadLayout(aKeyboardLayout);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * OverrideLayout() loads the specified keyboard layout.
 | |
|    */
 | |
|   void OverrideLayout(HKL aLayout) {
 | |
|     mIsOverridden = true;
 | |
|     LoadLayout(aLayout);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * RestoreLayout() loads the current keyboard layout of the thread.
 | |
|    */
 | |
|   void RestoreLayout() {
 | |
|     mIsOverridden = false;
 | |
|     mIsPendingToRestoreKeyboardLayout = true;
 | |
|   }
 | |
| 
 | |
|   uint32_t ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const;
 | |
| 
 | |
|   /**
 | |
|    * ConvertNativeKeyCodeToKeyNameIndex() returns KeyNameIndex value for
 | |
|    * non-printable keys (except some special keys like space key).
 | |
|    */
 | |
|   KeyNameIndex ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const;
 | |
| 
 | |
|   /**
 | |
|    * ConvertScanCodeToCodeNameIndex() returns CodeNameIndex value for
 | |
|    * the given scan code.  aScanCode can be over 0xE000 since this method
 | |
|    * doesn't use Windows API.
 | |
|    */
 | |
|   static CodeNameIndex ConvertScanCodeToCodeNameIndex(UINT aScanCode);
 | |
| 
 | |
|   HKL GetLayout() const {
 | |
|     return mIsPendingToRestoreKeyboardLayout ? ::GetKeyboardLayout(0)
 | |
|                                              : mKeyboardLayout;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * This wraps MapVirtualKeyEx() API with MAPVK_VK_TO_VSC.
 | |
|    */
 | |
|   WORD ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const;
 | |
| 
 | |
|   /**
 | |
|    * Implementation of nsIWidget::SynthesizeNativeKeyEvent().
 | |
|    */
 | |
|   nsresult SynthesizeNativeKeyEvent(nsWindowBase* aWidget,
 | |
|                                     int32_t aNativeKeyboardLayout,
 | |
|                                     int32_t aNativeKeyCode,
 | |
|                                     uint32_t aModifierFlags,
 | |
|                                     const nsAString& aCharacters,
 | |
|                                     const nsAString& aUnmodifiedCharacters);
 | |
| 
 | |
|  private:
 | |
|   KeyboardLayout();
 | |
|   ~KeyboardLayout();
 | |
| 
 | |
|   static KeyboardLayout* sInstance;
 | |
|   static nsIUserIdleServiceInternal* sIdleService;
 | |
| 
 | |
|   struct DeadKeyTableListEntry {
 | |
|     DeadKeyTableListEntry* next;
 | |
|     uint8_t data[1];
 | |
|   };
 | |
| 
 | |
|   HKL mKeyboardLayout;
 | |
| 
 | |
|   VirtualKey mVirtualKeys[NS_NUM_OF_KEYS];
 | |
|   DeadKeyTableListEntry* mDeadKeyTableListHead;
 | |
|   // When mActiveDeadKeys is empty, it's not in dead key sequence.
 | |
|   // Otherwise, it contains virtual keycodes which are pressed in current
 | |
|   // dead key sequence.
 | |
|   nsTArray<uint8_t> mActiveDeadKeys;
 | |
|   // mDeadKeyShiftStates is always same length as mActiveDeadKeys.
 | |
|   // This stores shift states at pressing each dead key stored in
 | |
|   // mActiveDeadKeys.
 | |
|   nsTArray<VirtualKey::ShiftState> mDeadKeyShiftStates;
 | |
| 
 | |
|   bool mIsOverridden;
 | |
|   bool mIsPendingToRestoreKeyboardLayout;
 | |
|   bool mHasAltGr;
 | |
| 
 | |
|   static inline int32_t GetKeyIndex(uint8_t aVirtualKey);
 | |
|   static int CompareDeadKeyEntries(const void* aArg1, const void* aArg2,
 | |
|                                    void* aData);
 | |
|   static bool AddDeadKeyEntry(char16_t aBaseChar, char16_t aCompositeChar,
 | |
|                               DeadKeyEntry* aDeadKeyArray, uint32_t aEntries);
 | |
|   bool EnsureDeadKeyActive(bool aIsActive, uint8_t aDeadKey,
 | |
|                            const PBYTE aDeadKeyKbdState);
 | |
|   uint32_t GetDeadKeyCombinations(uint8_t aDeadKey,
 | |
|                                   const PBYTE aDeadKeyKbdState,
 | |
|                                   uint16_t aShiftStatesWithBaseChars,
 | |
|                                   DeadKeyEntry* aDeadKeyArray,
 | |
|                                   uint32_t aMaxEntries);
 | |
|   /**
 | |
|    * Activates or deactivates dead key state.
 | |
|    */
 | |
|   void ActivateDeadKeyState(const NativeKey& aNativeKey);
 | |
|   void DeactivateDeadKeyState();
 | |
| 
 | |
|   const DeadKeyTable* AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
 | |
|                                       uint32_t aEntries);
 | |
|   void ReleaseDeadKeyTables();
 | |
| 
 | |
|   /**
 | |
|    * Loads the specified keyboard layout. This method always clear the dead key
 | |
|    * state.
 | |
|    */
 | |
|   void LoadLayout(HKL aLayout);
 | |
| 
 | |
|   /**
 | |
|    * Gets the keyboard layout name of aLayout.  Be careful, this may be too
 | |
|    * slow to call at handling user input.
 | |
|    */
 | |
|   nsCString GetLayoutName(HKL aLayout) const;
 | |
| 
 | |
|   /**
 | |
|    * InitNativeKey() must be called when actually widget receives WM_KEYDOWN or
 | |
|    * WM_KEYUP.  This method is stateful.  This saves current dead key state at
 | |
|    * WM_KEYDOWN.  Additionally, computes current inputted character(s) and set
 | |
|    * them to the aNativeKey.
 | |
|    */
 | |
|   void InitNativeKey(NativeKey& aNativeKey);
 | |
| 
 | |
|   /**
 | |
|    * MaybeInitNativeKeyAsDeadKey() initializes aNativeKey only when aNativeKey
 | |
|    * is a dead key's event.
 | |
|    * When it's not in a dead key sequence, this activates the dead key state.
 | |
|    * When it's in a dead key sequence, this initializes aNativeKey with a
 | |
|    * composite character or a preceding dead char and a dead char which should
 | |
|    * be caused by aNativeKey.
 | |
|    * Returns true when this initializes aNativeKey.  Otherwise, false.
 | |
|    */
 | |
|   bool MaybeInitNativeKeyAsDeadKey(NativeKey& aNativeKey);
 | |
| 
 | |
|   /**
 | |
|    * MaybeInitNativeKeyWithCompositeChar() may initialize aNativeKey with
 | |
|    * proper composite character when dead key produces a composite character.
 | |
|    * Otherwise, just returns false.
 | |
|    */
 | |
|   bool MaybeInitNativeKeyWithCompositeChar(NativeKey& aNativeKey);
 | |
| 
 | |
|   /**
 | |
|    * See the comment of GetUniCharsAndModifiers() below.
 | |
|    */
 | |
|   UniCharsAndModifiers GetUniCharsAndModifiers(
 | |
|       uint8_t aVirtualKey, VirtualKey::ShiftState aShiftState) const;
 | |
| 
 | |
|   /**
 | |
|    * GetDeadUniCharsAndModifiers() returns dead chars which are stored in
 | |
|    * current dead key sequence.  So, this is stateful.
 | |
|    */
 | |
|   UniCharsAndModifiers GetDeadUniCharsAndModifiers() const;
 | |
| 
 | |
|   /**
 | |
|    * GetCompositeChar() returns a composite character with dead character
 | |
|    * caused by mActiveDeadKeys, mDeadKeyShiftStates and a base character
 | |
|    * (aBaseChar).
 | |
|    * If the combination of the dead character and the base character doesn't
 | |
|    * cause a composite character, this returns 0.
 | |
|    */
 | |
|   char16_t GetCompositeChar(char16_t aBaseChar) const;
 | |
| 
 | |
|   // NativeKey class should access InitNativeKey() directly, but it shouldn't
 | |
|   // be available outside of NativeKey.  So, let's make NativeKey a friend
 | |
|   // class of this.
 | |
|   friend class NativeKey;
 | |
| };
 | |
| 
 | |
| class RedirectedKeyDownMessageManager {
 | |
|  public:
 | |
|   /*
 | |
|    * If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is
 | |
|    * a redirected message, NativeKey::DispatchKeyDownAndKeyPressEvent()
 | |
|    * prevents to dispatch eKeyDown event because it has been dispatched
 | |
|    * before the message was redirected.  However, in some cases, WM_*KEYDOWN
 | |
|    * message handler may not handle actually.  Then, the message handler needs
 | |
|    * to forget the redirected message and remove WM_CHAR message or WM_SYSCHAR
 | |
|    * message for the redirected keydown message.  AutoFlusher class is a helper
 | |
|    * class for doing it.  This must be created in the stack.
 | |
|    */
 | |
|   class MOZ_STACK_CLASS AutoFlusher final {
 | |
|    public:
 | |
|     AutoFlusher(nsWindowBase* aWidget, const MSG& aMsg)
 | |
|         : mCancel(!RedirectedKeyDownMessageManager::IsRedirectedMessage(aMsg)),
 | |
|           mWidget(aWidget),
 | |
|           mMsg(aMsg) {}
 | |
| 
 | |
|     ~AutoFlusher() {
 | |
|       if (mCancel) {
 | |
|         return;
 | |
|       }
 | |
|       // Prevent unnecessary keypress event
 | |
|       if (!mWidget->Destroyed()) {
 | |
|         RedirectedKeyDownMessageManager::RemoveNextCharMessage(mMsg.hwnd);
 | |
|       }
 | |
|       // Foreget the redirected message
 | |
|       RedirectedKeyDownMessageManager::Forget();
 | |
|     }
 | |
| 
 | |
|     void Cancel() { mCancel = true; }
 | |
| 
 | |
|    private:
 | |
|     bool mCancel;
 | |
|     RefPtr<nsWindowBase> mWidget;
 | |
|     const MSG& mMsg;
 | |
|   };
 | |
| 
 | |
|   static void WillRedirect(const MSG& aMsg, bool aDefualtPrevented) {
 | |
|     sRedirectedKeyDownMsg = aMsg;
 | |
|     sDefaultPreventedOfRedirectedMsg = aDefualtPrevented;
 | |
|   }
 | |
| 
 | |
|   static void Forget() { sRedirectedKeyDownMsg.message = WM_NULL; }
 | |
| 
 | |
|   static void PreventDefault() { sDefaultPreventedOfRedirectedMsg = true; }
 | |
|   static bool DefaultPrevented() { return sDefaultPreventedOfRedirectedMsg; }
 | |
| 
 | |
|   static bool IsRedirectedMessage(const MSG& aMsg);
 | |
| 
 | |
|   /**
 | |
|    * RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM
 | |
|    * message handler.  If there is no WM_(SYS)CHAR message for it, this
 | |
|    * method does nothing.
 | |
|    * NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is
 | |
|    * called in message loop.  So, WM_(SYS)KEYDOWN message should have
 | |
|    * WM_(SYS)CHAR message in the queue if the keydown event causes character
 | |
|    * input.
 | |
|    */
 | |
|   static void RemoveNextCharMessage(HWND aWnd);
 | |
| 
 | |
|  private:
 | |
|   // sRedirectedKeyDownMsg is WM_KEYDOWN message or WM_SYSKEYDOWN message which
 | |
|   // is reirected with SendInput() API by
 | |
|   // widget::NativeKey::DispatchKeyDownAndKeyPressEvent()
 | |
|   static MSG sRedirectedKeyDownMsg;
 | |
|   static bool sDefaultPreventedOfRedirectedMsg;
 | |
| };
 | |
| 
 | |
| }  // namespace widget
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif
 | 
