Bug 432389 CapsLock and NumLock state should be preferred at KeyTranslate code: r=josh+karl, sr=roc, a=dsicore tests: r+sr=roc
authormasayuki@d-toybox.com
Thu, 08 May 2008 17:01:29 -0700
changeset 15072 3ffdc6735cd01bb37668309bbad0e6500e991bc3
parent 15071 37b5cf2562cf724cd786f43fcedcb307686649f8
child 15073 88cbf650b48a74067d8b8d371e5167b1eabc213f
push idunknown
push userunknown
push dateunknown
reviewersjosh, roc, dsicore
bugs432389
milestone1.9pre
Bug 432389 CapsLock and NumLock state should be preferred at KeyTranslate code: r=josh+karl, sr=roc, a=dsicore tests: r+sr=roc
widget/src/cocoa/nsChildView.mm
widget/tests/test_keycodes.xul
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -4142,16 +4142,29 @@ GetScriptFromKeyboardLayout(SInt32 aLayo
     case 3:                      // German
     case -2:     return smRoman; // US-Extended
     case -18944: return smGreek; // Greek
     default: NS_NOTREACHED("unknown keyboard layout");
   }
   return smRoman;
 }
 
+static PRUint32
+GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
+{
+  KeyTranslateData kt;
+  Handle handle = ::GetResource('uchr', kKLUSKeyboard); // US keyboard layout
+  if (!handle || !(*handle)) {
+    NS_ERROR("US keyboard layout doesn't have uchr resource");
+    return 0;
+  }
+  UInt32 kbType = 40; // ANSI, don't use actual layout
+  return UCKeyTranslateToUnicode((UCKeyboardLayout*)(*handle), aKeyCode,
+                                 aModifiers, kbType);
+}
 
 - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NS_ASSERTION(aKeyEvent && outGeckoEvent, "convertCocoaKeyEvent:toGeckoEvent: requires non-null arguments");
   if (!aKeyEvent || !outGeckoEvent)
     return;
@@ -4218,58 +4231,107 @@ GetScriptFromKeyboardLayout(SInt32 aLayo
           OSStatus err =
             ::GetTextEncodingFromScriptInfo(kt.mScript, kTextLanguageDontCare,
                                             kTextRegionDontCare,
                                             &kt.mKchr.mEncoding);
           if (err != noErr)
             kt.mKchr.mHandle = nsnull;
         }
       }
-      PRUint32 unshiftedChar = 0;
-      PRUint32 shiftedChar = 0;
-      PRUint32 unshiftedCmdChar = 0;
-      PRUint32 shiftedCmdChar = 0;
 
       UInt32 key = [aKeyEvent keyCode];
       UInt32 mod;
 
+      UInt32 baseMod = 0;
+      if ([aKeyEvent modifierFlags] & NSAlphaShiftKeyMask)
+        baseMod |= alphaLock;
+      if ([aKeyEvent modifierFlags] & NSNumericPadKeyMask)
+        baseMod |= kEventKeyModifierNumLockMask;
+
       // normal chars
-      mod = 0;
-      unshiftedChar = GetUniCharFromKeyTranslate(kt, key, mod);
+      mod = baseMod;
+      PRUint32 unshiftedChar = GetUniCharFromKeyTranslate(kt, key, mod);
       mod |= shiftKey;
-      shiftedChar = GetUniCharFromKeyTranslate(kt, key, mod);
-
-      // with cmd chars
-      mod = cmdKey;
-      unshiftedCmdChar = GetUniCharFromKeyTranslate(kt, key, mod);
+      PRUint32 shiftedChar = GetUniCharFromKeyTranslate(kt, key, mod);
+
+      // with Cmd key
+      // XXX we should remove CapsLock state, that changes from Latin to
+      //     Cyrillic characters with Russian layout of 10.4 only when Cmd key
+      //     is pressed.
+      mod = (baseMod & ~alphaLock);
+      PRUint32 uncmdedChar = GetUniCharFromKeyTranslate(kt, key, mod);
+      PRUint32 uncmdedUSChar = GetUSLayoutCharFromKeyTranslate(key, mod);
+      mod |= cmdKey;
+      PRUint32 cmdedChar = GetUniCharFromKeyTranslate(kt, key, mod);
       mod |= shiftKey;
-      shiftedCmdChar = GetUniCharFromKeyTranslate(kt, key, mod);
+      PRUint32 cmdedShiftChar = GetUniCharFromKeyTranslate(kt, key, mod);
 
       // Is the keyboard layout changed by Cmd key?
       // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
-      PRBool isCmdSwitchLayout = unshiftedChar != unshiftedCmdChar;
+      PRBool isCmdSwitchLayout = uncmdedChar != cmdedChar;
       // Is the keyboard layout for Latin, but Cmd key switches the layout?
       // I.e., Dvorak-QWERTY
-      PRBool isDvorakQWERTY = isCmdSwitchLayout &&
-               unshiftedChar && unshiftedChar < 0x7F;
+      PRBool isDvorakQWERTY = isCmdSwitchLayout && kt.mScript == smRoman;
 
       // If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed,
       // we should append unshiftedChar and shiftedChar for handling the
       // normal characters.
       if ((unshiftedChar || shiftedChar) &&
           (!outGeckoEvent->isMeta || !isDvorakQWERTY)) {
         nsAlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
         outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
       }
+
+      // Cleaning up cmdedShiftChar with CapsLocked characters.
+      if (!isCmdSwitchLayout) {
+        // Don't replace if cmdedChar and cmdedShiftChar are not same.
+        // E.g., Cmd+Shift+SS(eszett) is '/'. But Shift+SS is '?'. Then,
+        // we should send '/' directly and '?' should be sent as alternative.
+        if (shiftedChar && cmdedChar == cmdedShiftChar)
+          cmdedShiftChar = shiftedChar;
+        if (unshiftedChar)
+          cmdedChar = unshiftedChar;
+      } else if (uncmdedUSChar == cmdedChar) {
+        mod = baseMod | shiftKey;
+        PRUint32 ch = GetUSLayoutCharFromKeyTranslate(key, mod);
+        if (ch && cmdedChar == cmdedShiftChar)
+          cmdedShiftChar = ch;
+        ch = GetUSLayoutCharFromKeyTranslate(key, mod & ~shiftKey);
+        if (ch)
+          cmdedChar = ch;
+      }
+
+      // XXX We should do something similar when Control is down (bug 429510).
+      if (outGeckoEvent->isMeta &&
+           !(outGeckoEvent->isControl || outGeckoEvent->isAlt)) {
+
+        // The character to use for charCode.
+        PRUint32 preferredCharCode = 0;
+        preferredCharCode = outGeckoEvent->isShift ? cmdedShiftChar : cmdedChar;
+
+        if (preferredCharCode) {
+#ifdef DEBUG_KB
+          if (outGeckoEvent->charCode != preferredCharCode) {
+            NSLog(@"      charCode replaced: %X(%C) to %X(%C)",
+                  outGeckoEvent->charCode,
+                  outGeckoEvent->charCode > ' ' ? outGeckoEvent->charCode : ' ',
+                  preferredCharCode,
+                  preferredCharCode > ' ' ? preferredCharCode : ' ');
+          }
+#endif
+          outGeckoEvent->charCode = preferredCharCode;
+        }
+      }
+
       // If the current keyboard layout is switched by the Cmd key,
-      // we should append unshiftedCmdChar and shiftedCmdChar that are
+      // we should append cmdedChar and shiftedCmdChar that are
       // Latin char for the key. But don't append at Dvorak-QWERTY.
-      if ((unshiftedCmdChar || shiftedCmdChar) &&
+      if ((cmdedChar || cmdedShiftChar) &&
           isCmdSwitchLayout && !isDvorakQWERTY) {
-        nsAlternativeCharCode altCharCodes(unshiftedCmdChar, shiftedCmdChar);
+        nsAlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
         outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
       }
     }
   }
   else {
     NSString* characters = nil;
     if ([aKeyEvent type] != NSFlagsChanged)
       characters = [aKeyEvent charactersIgnoringModifiers];
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -187,44 +187,44 @@ function runPressTests()
     testKey({layout:"US", keyCode:0, alt:1, chars:"\u00e5", unmodifiedChars:"a"},
             "\u00e5");
     testKey({layout:"US", keyCode:0, alt:1, shift:1, chars:"\u00c5", unmodifiedChars:"A"},
             "\u00c5");
     
     // Command keys
     testKey({layout:"US", keyCode:0, command:1, chars:"a", unmodifiedChars:"a"},
             "a");
-    // Shift-cmd gives us the unshifted character
+    // Shift-cmd gives us the shifted character
     testKey({layout:"US", keyCode:0, command:1, shift:1, chars:"a", unmodifiedChars:"A"},
-            "a");
+            "A");
     // Ctrl-cmd gives us the unshifted character
     testKey({layout:"US", keyCode:0, command:1, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
             "a");
-    // Alt-cmd gives us the *shifted* character
+    // Alt-cmd gives us the shifted character
     testKey({layout:"US", keyCode:0, command:1, alt:1, chars:"\u00e5", unmodifiedChars:"a"},
             "\u00e5");
     testKey({layout:"US", keyCode:0, command:1, alt:1, shift:1, chars:"\u00c5", unmodifiedChars:"a"},
             "\u00c5");
 
     // Greek ctrl keys produce Latin charcodes
     testKey({layout:"Greek", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
             "a");
     testKey({layout:"Greek", keyCode:0, ctrl:1, shift:1, chars:"\u0001", unmodifiedChars:"\u0391"},
             "A");
 
     // Greek command keys
     testKey({layout:"Greek", keyCode:0, command:1, chars:"a", unmodifiedChars:"\u03b1"},
             "a");
-    // Shift-cmd gives us the unshifted character
+    // Shift-cmd gives us the shifted character
     testKey({layout:"Greek", keyCode:0, command:1, shift:1, chars:"a", unmodifiedChars:"\u391"},
-            "a");
+            "A");
     // Ctrl-cmd gives us the unshifted character
     testKey({layout:"Greek", keyCode:0, command:1, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
             "a");
-    // Alt-cmd gives us the *shifted* character
+    // Alt-cmd gives us the shifted character
     testKey({layout:"Greek", keyCode:0, command:1, alt:1, chars:"\u00a8", unmodifiedChars:"\u03b1"},
             "\u00a8");
     testKey({layout:"Greek", keyCode:0, command:1, alt:1, shift:1, chars:"\u00b9", unmodifiedChars:"\u0391"},
             "\u00b9");
 
     // German (KCHR/KeyTranslate case)
     testKey({layout:"German", keyCode:0, chars:"a", unmodifiedChars:"a"},
             "a");