Bug 900750 - part 1: Make KeyboardLayout store the information if current keyboard layout has AltGr key r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 29 May 2018 20:36:38 +0900
changeset 806853 ca9456ac0ec037482b7c98adbc3da6532e224cd1
parent 806530 8ab6afabc78cd909ff90ba74c1eab098985f83ef
child 806854 d6e496308d305b68b6123462c2f38fba728fea7d
push id112973
push usermasayuki@d-toybox.com
push dateTue, 12 Jun 2018 10:30:22 +0000
reviewersm_kato
bugs900750
milestone62.0a1
Bug 900750 - part 1: Make KeyboardLayout store the information if current keyboard layout has AltGr key r?m_kato For setting AltRight key's key value to "AltGraph" if it should work as so, we need to know if current keyboard layout has AltGr key. Unfortunately, Windows doesn't provide such information but we retrieve all input characters from each key when a keyboard layout is loaded. So, when we load a keyboard layout, we can mark if current keyboard layout has AltGr key with checking at least one key inputs different character(s) when AltGr key is pressed. MozReview-Commit-ID: 8GI3phSVTUS
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -261,34 +261,53 @@ GetKeyLocationName(uint32_t aLocation)
     case eKeyLocationNumpad:
       return NS_LITERAL_CSTRING("KEY_LOCATION_NUMPAD");
     default:
       return nsPrintfCString("Unknown (0x%04X)", aLocation);
   }
 }
 
 static const nsCString
-GetCharacterCodeName(char16_t* aChars, uint32_t aLength)
+GetCharacterCodeName(const char16_t* aChars, uint32_t aLength)
 {
   if (!aLength) {
-    return NS_LITERAL_CSTRING("");
+    return EmptyCString();
   }
   nsAutoCString result;
   for (uint32_t i = 0; i < aLength; ++i) {
     if (!result.IsEmpty()) {
       result.AppendLiteral(", ");
     } else {
       result.AssignLiteral("\"");
     }
     result.Append(GetCharacterCodeName(aChars[i]));
   }
   result.AppendLiteral("\"");
   return result;
 }
 
+static const nsCString
+GetCharacterCodeName(const UniCharsAndModifiers& aUniCharsAndModifiers)
+{
+  if (aUniCharsAndModifiers.IsEmpty()) {
+    return EmptyCString();
+  }
+  nsAutoCString result;
+  for (uint32_t i = 0; i < aUniCharsAndModifiers.Length(); i++) {
+    if (!result.IsEmpty()) {
+      result.AppendLiteral(", ");
+    } else {
+      result.AssignLiteral("\"");
+    }
+    result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(i)));
+  }
+  result.AppendLiteral("\"");
+  return result;
+}
+
 class MOZ_STACK_CLASS GetShiftStateName final : public nsAutoCString
 {
 public:
   explicit GetShiftStateName(VirtualKey::ShiftState aShiftState)
   {
     if (!aShiftState) {
       AssignLiteral("none");
       return;
@@ -1032,41 +1051,40 @@ VirtualKey::SetDeadChar(ShiftState aShif
   mShiftStates[aShiftState].DeadKey.Table = nullptr;
 }
 
 UniCharsAndModifiers
 VirtualKey::GetUniChars(ShiftState aShiftState) const
 {
   UniCharsAndModifiers result = GetNativeUniChars(aShiftState);
 
-  const ShiftState STATE_ALT_CONTROL = (STATE_ALT | STATE_CONTROL);
-  if (!(aShiftState & STATE_ALT_CONTROL)) {
+  if (!(aShiftState & STATE_CONTROL_ALT)) {
     return result;
   }
 
   if (result.IsEmpty()) {
-    result = GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
+    result = GetNativeUniChars(aShiftState & ~STATE_CONTROL_ALT);
     result.FillModifiers(ShiftStateToModifiers(aShiftState));
     return result;
   }
 
-  if ((aShiftState & STATE_ALT_CONTROL) == STATE_ALT_CONTROL) {
+  if (IsAltGrIndex(aShiftState)) {
     // Even if the shifted chars and the unshifted chars are same, we
     // should consume the Alt key state and the Ctrl key state when
     // AltGr key is pressed. Because if we don't consume them, the input
     // events are ignored on EditorBase. (I.e., Users cannot input the
     // characters with this key combination.)
     Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
     finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
     result.FillModifiers(finalModifiers);
     return result;
   }
 
   UniCharsAndModifiers unmodifiedReslt =
-    GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
+    GetNativeUniChars(aShiftState & ~STATE_CONTROL_ALT);
   if (!result.UniCharsEqual(unmodifiedReslt)) {
     // Otherwise, we should consume the Alt key state and the Ctrl key state
     // only when the shifted chars and unshifted chars are different.
     Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
     finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
     result.FillModifiers(finalModifiers);
   }
   return result;
@@ -3751,16 +3769,17 @@ KeyboardLayout::NotifyIdleServiceOfUserA
 {
   sIdleService->ResetIdleTimeOut(0);
 }
 
 KeyboardLayout::KeyboardLayout()
   : mKeyboardLayout(0)
   , mIsOverridden(false)
   , mIsPendingToRestoreKeyboardLayout(false)
+  , mHasAltGr(false)
 {
   mDeadKeyTableListHead = nullptr;
   // A dead key sequence should be made from up to 5 keys.  Therefore, 4 is
   // enough and makes sense because the item is uint8_t.
   // (Although, even if it's possible to be 6 keys or more in a sequence,
   // this array will be re-allocated).
   mActiveDeadKeys.SetCapacity(4);
   mDeadKeyShiftStates.SetCapacity(4);
@@ -4256,16 +4275,17 @@ KeyboardLayout::LoadLayout(HKL aLayout)
 {
   mIsPendingToRestoreKeyboardLayout = false;
 
   if (mKeyboardLayout == aLayout) {
     return;
   }
 
   mKeyboardLayout = aLayout;
+  mHasAltGr = false;
 
   MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Info,
     ("KeyboardLayout::LoadLayout(aLayout=0x%08X (%s))",
      aLayout, GetLayoutName(aLayout).get()));
 
   BYTE kbdState[256];
   memset(kbdState, 0, sizeof(kbdState));
 
@@ -4283,16 +4303,17 @@ KeyboardLayout::LoadLayout(HKL aLayout)
 
   ::GetKeyboardState(originalKbdState);
 
   // For each shift state gather all printable characters that are produced
   // for normal case when no any dead-key is active.
 
   for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
     VirtualKey::FillKbdState(kbdState, shiftState);
+    bool isAltGr = VirtualKey::IsAltGrIndex(shiftState);
     for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
       int32_t vki = GetKeyIndex(virtualKey);
       if (vki < 0) {
         continue;
       }
       NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index");
       char16_t uniChars[5];
       int32_t ret =
@@ -4321,16 +4342,33 @@ KeyboardLayout::LoadLayout(HKL aLayout)
         }
         mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret);
         MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Verbose,
           ("  %s (%d): NormalChar(%s, %s) (ret=%d)",
            kVirtualKeyName[virtualKey], vki,
            GetShiftStateName(shiftState).get(),
            GetCharacterCodeName(uniChars, ret).get(), ret));
       }
+
+      // If the key inputs at least one character with AltGr modifier,
+      // check if AltGr changes inputting character.  If it does, mark
+      // this keyboard layout has AltGr modifier actually.
+      if (!mHasAltGr && ret > 0 && isAltGr &&
+          mVirtualKeys[vki].IsChangedByAltGr(shiftState)) {
+        mHasAltGr = true;
+        MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Info,
+          ("  Found a key (%s) changed by AltGr: %s -> %s (%s) (ret=%d)",
+           kVirtualKeyName[virtualKey],
+           GetCharacterCodeName(
+             mVirtualKeys[vki].GetNativeUniChars(
+               shiftState - VirtualKey::ShiftStateIndex::eAltGr)).get(),
+           GetCharacterCodeName(
+             mVirtualKeys[vki].GetNativeUniChars(shiftState)).get(),
+           GetShiftStateName(shiftState).get(), ret));
+      }
     }
   }
 
   // Now process each dead-key to find all its base characters and resulting
   // composite characters.
   for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
     if (!(shiftStatesWithDeadKeys & (1 << shiftState))) {
       continue;
@@ -4369,16 +4407,20 @@ KeyboardLayout::LoadLayout(HKL aLayout)
         UINT scanCode = kExtendedScanCode[i] + j;
         UINT virtualKeyCode =
           ::MapVirtualKeyEx(scanCode, kMapType, mKeyboardLayout);
         MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Verbose,
           ("0x%04X, %s", scanCode, kVirtualKeyName[virtualKeyCode]));
       }
     }
   }
+
+  MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Info,
+    ("  AltGr key is %s in %s",
+     mHasAltGr ? "found" : "not found", GetLayoutName(aLayout).get()));
 }
 
 inline int32_t
 KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey)
 {
 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
 // to produce visible representation:
 // 0x20 - VK_SPACE          ' '
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -156,50 +156,74 @@ public:
   }
 
   char16_t GetCompositeChar(char16_t aBaseChar) const;
 };
 
 class VirtualKey
 {
 public:
-  //  0 - Normal
-  //  1 - Shift
-  //  2 - Control
-  //  3 - Control + Shift
-  //  4 - Alt
-  //  5 - Alt + Shift
-  //  6 - Alt + Control (AltGr)
-  //  7 - Alt + Control + Shift (AltGr + Shift)
-  //  8 - CapsLock
-  //  9 - CapsLock + Shift
-  // 10 - CapsLock + Control
-  // 11 - CapsLock + Control + Shift
-  // 12 - CapsLock + Alt
-  // 13 - CapsLock + Alt + Shift
-  // 14 - CapsLock + Alt + Control (CapsLock + AltGr)
-  // 15 - CapsLock + Alt + Control + Shift (CapsLock + AltGr + Shift)
+  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
+    STATE_CAPSLOCK = 0x08,
+    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;
@@ -225,16 +249,51 @@ private:
 public:
   static void FillKbdState(PBYTE aKbdState, const ShiftState aShiftState);
 
   bool IsDeadKey(ShiftState aShiftState) const
   {
     return (mIsDeadKey & (1 << 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(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)
   {
     mShiftStates[aShiftState].DeadKey.Table = aDeadKeyTable;
   }
 
   void SetNormalChars(ShiftState aShiftState, const char16_t* aChars,
                       uint32_t aNumOfChars);
@@ -722,16 +781,22 @@ public:
   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;
 
   /**
    * IsInDeadKeySequence() returns true when it's in a dead key sequence.
@@ -861,16 +926,17 @@ private:
   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);