7196547: [macosx] Implement dead key detection for KeyEvent
authoralexsch
Fri, 14 Sep 2012 15:30:46 +0400
changeset 13778 0ba9254a04ad
parent 13777 c94cf7e1bac2
child 13779 011727a60840
7196547: [macosx] Implement dead key detection for KeyEvent Reviewed-by: skovatch, kizune
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java
jdk/src/macosx/native/sun/awt/AWTEvent.m
jdk/test/java/awt/event/KeyEvent/DeadKey/deadKeyMacOSX.java
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java	Fri Sep 14 15:08:54 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java	Fri Sep 14 15:30:46 2012 +0400
@@ -134,7 +134,7 @@
         boolean postsTyped = false;
 
         char testChar = KeyEvent.CHAR_UNDEFINED;
-        char testDeadChar = 0;
+        boolean isDeadChar = (chars!= null && chars.length() == 0);
 
         if (isFlagsChangedEvent) {
             int[] in = new int[] {modifierFlags, keyCode};
@@ -150,14 +150,18 @@
                 testChar = chars.charAt(0);
             }
 
-            int[] in = new int[] {testChar, testDeadChar, modifierFlags, keyCode};
-            int[] out = new int[2]; // [jkeyCode, jkeyLocation]
+            int[] in = new int[] {testChar, isDeadChar ? 1 : 0, modifierFlags, keyCode};
+            int[] out = new int[3]; // [jkeyCode, jkeyLocation, deadChar]
 
             postsTyped = NSEvent.nsToJavaKeyInfo(in, out);
             if (!postsTyped) {
                 testChar = KeyEvent.CHAR_UNDEFINED;
             }
 
+            if(isDeadChar){
+                testChar = (char) out[2];
+            }
+
             jkeyCode = out[0];
             jkeyLocation = out[1];
             jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) :
--- a/jdk/src/macosx/native/sun/awt/AWTEvent.m	Fri Sep 14 15:08:54 2012 +0400
+++ b/jdk/src/macosx/native/sun/awt/AWTEvent.m	Fri Sep 14 15:30:46 2012 +0400
@@ -26,6 +26,7 @@
 #import <JavaNativeFoundation/JavaNativeFoundation.h>
 #import <JavaRuntimeSupport/JavaRuntimeSupport.h>
 #import <sys/time.h>
+#include <Carbon/Carbon.h>
 
 #import "LWCToolkit.h"
 #import "ThreadUtilities.h"
@@ -371,26 +372,67 @@
     return nsChar;
 }
 
+static unichar NsGetDeadKeyChar(unsigned short keyCode)
+{
+    TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+    CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+    const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
+    // Carbon modifiers should be used instead of NSEvent modifiers
+    UInt32 modifierKeyState = (GetCurrentEventKeyModifiers() >> 8) & 0xFF;
+
+    if (keyboardLayout) {
+        UInt32 deadKeyState = 0;
+        UniCharCount maxStringLength = 255;
+        UniCharCount actualStringLength = 0;
+        UniChar unicodeString[maxStringLength];
+
+        // get the deadKeyState
+        OSStatus status = UCKeyTranslate(keyboardLayout,
+                                         keyCode, kUCKeyActionDown, modifierKeyState,
+                                         LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
+                                         &deadKeyState,
+                                         maxStringLength,
+                                         &actualStringLength, unicodeString);
+
+        if (status == noErr && deadKeyState != 0) {
+            // Press SPACE to get the dead key char
+            status = UCKeyTranslate(keyboardLayout,
+                                    kVK_Space, kUCKeyActionDown, 0,
+                                    LMGetKbdType(), 0,
+                                    &deadKeyState,
+                                    maxStringLength,
+                                    &actualStringLength, unicodeString);
+
+            if (status == noErr && actualStringLength > 0) {
+                return unicodeString[0];
+            }
+        }
+    }
+    return 0;
+}
+
 /*
  * This is the function that uses the table above to take incoming
  * NSEvent keyCodes and translate to the Java virtual key code.
  */
 static void
-NsCharToJavaVirtualKeyCode(unichar ch, unichar deadChar,
+NsCharToJavaVirtualKeyCode(unichar ch, BOOL isDeadChar,
                            NSUInteger flags, unsigned short key,
-                           jint *keyCode, jint *keyLocation, BOOL *postsTyped)
+                           jint *keyCode, jint *keyLocation, BOOL *postsTyped, unichar *deadChar)
 {
     static size_t size = sizeof(keyTable) / sizeof(struct _key);
     NSInteger offset;
 
-    if (deadChar) {
+    if (isDeadChar) {
+        unichar testDeadChar = NsGetDeadKeyChar(key);
         const struct CharToVKEntry *map;
         for (map = charToDeadVKTable; map->c != 0; ++map) {
-            if (deadChar == map->c) {
+            if (testDeadChar == map->c) {
                 *keyCode = map->javaKey;
                 *postsTyped = NO;
                 // TODO: use UNKNOWN here?
                 *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
+                *deadChar = testDeadChar;
                 return;
             }
         }
@@ -615,20 +657,22 @@
 
     // in  = [testChar, testDeadChar, modifierFlags, keyCode]
     jchar testChar = (jchar)data[0];
-    jchar testDeadChar = (jchar)data[1];
+    BOOL isDeadChar = (data[1] != 0);
     jint modifierFlags = data[2];
     jshort keyCode = (jshort)data[3];
 
     jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
     jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
+    jchar testDeadChar = 0;
 
-    NsCharToJavaVirtualKeyCode((unichar)testChar, (unichar)testDeadChar,
+    NsCharToJavaVirtualKeyCode((unichar)testChar, isDeadChar,
                                (NSUInteger)modifierFlags, (unsigned short)keyCode,
-                               &jkeyCode, &jkeyLocation, &postsTyped);
+                               &jkeyCode, &jkeyLocation, &postsTyped, &testDeadChar);
 
     // out = [jkeyCode, jkeyLocation];
     (*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode);
     (*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation);
+    (*env)->SetIntArrayRegion(env, outData, 2, 1, (jint *)&testDeadChar);
 
     (*env)->ReleaseIntArrayElements(env, inData, data, 0);
 
@@ -685,12 +729,12 @@
 (JNIEnv *env, jclass cls, char nsChar, jint modifierFlags)
 {
     jchar javaChar = 0;
-    
+
 JNF_COCOA_ENTER(env);
-    
+
     javaChar = NsCharToJavaChar(nsChar, modifierFlags);
 
 JNF_COCOA_EXIT(env);
-    
+
     return javaChar;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/event/KeyEvent/DeadKey/deadKeyMacOSX.java	Fri Sep 14 15:30:46 2012 +0400
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7196547
+ * @summary Dead Key implementation for KeyEvent on Mac OS X
+ * @author alexandr.scherbatiy area=awt.event
+ * @run main deadKeyMacOSX
+ */
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.event.KeyEvent;
+import sun.awt.OSInfo;
+import sun.awt.SunToolkit;
+
+public class deadKeyMacOSX {
+
+    private static SunToolkit toolkit;
+    private static volatile int state = 0;
+
+    public static void main(String[] args) throws Exception {
+
+        if (OSInfo.getOSType() != OSInfo.OSType.MACOSX) {
+            return;
+        }
+
+        toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
+        Robot robot = new Robot();
+        robot.setAutoDelay(50);
+
+        createAndShowGUI();
+
+        // Pressed keys: Alt + E + A
+        // Results:  ALT + VK_DEAD_ACUTE + a with accute accent
+        robot.keyPress(KeyEvent.VK_ALT);
+        robot.keyPress(KeyEvent.VK_E);
+        robot.keyRelease(KeyEvent.VK_E);
+        robot.keyRelease(KeyEvent.VK_ALT);
+
+        robot.keyPress(KeyEvent.VK_A);
+        robot.keyRelease(KeyEvent.VK_A);
+
+        if (state != 3) {
+            throw new RuntimeException("Wrong number of key events.");
+        }
+    }
+
+    static void createAndShowGUI() {
+        Frame frame = new Frame();
+        frame.setSize(300, 300);
+        Panel panel = new Panel();
+        panel.addKeyListener(new DeadKeyListener());
+        frame.add(panel);
+        frame.setVisible(true);
+        toolkit.realSync();
+
+        panel.requestFocusInWindow();
+        toolkit.realSync();
+    }
+
+    static class DeadKeyListener extends KeyAdapter {
+
+        @Override
+        public void keyPressed(KeyEvent e) {
+            int keyCode = e.getKeyCode();
+            char keyChar = e.getKeyChar();
+
+            switch (state) {
+                case 0:
+                    if (keyCode != KeyEvent.VK_ALT) {
+                        throw new RuntimeException("Alt is not pressed.");
+                    }
+                    state++;
+                    break;
+                case 1:
+                    if (keyCode != KeyEvent.VK_DEAD_ACUTE) {
+                        throw new RuntimeException("Dead ACUTE is not pressed.");
+                    }
+                    if (keyChar != 0xB4) {
+                        throw new RuntimeException("Pressed char is not dead acute.");
+                    }
+
+                    state++;
+                    break;
+                case 2:
+                    if (keyCode != KeyEvent.VK_A) {
+                        throw new RuntimeException("A is not pressed.");
+                    }
+                    if (keyChar != 0xE1) {
+                        throw new RuntimeException("A char does not have ACCUTE accent");
+                    }
+                    state++;
+                    break;
+                default:
+                    throw new RuntimeException("Excessive keyPressed event.");
+            }
+        }
+
+        @Override
+        public void keyTyped(KeyEvent e) {
+            int keyCode = e.getKeyCode();
+            char keyChar = e.getKeyChar();
+
+            if (state == 3) {
+                if (keyCode != 0) {
+                    throw new RuntimeException("Key code should be undefined.");
+                }
+                if (keyChar != 0xE1) {
+                    throw new RuntimeException("A char does not have ACCUTE accent");
+                }
+            } else {
+                throw new RuntimeException("Wron number of keyTyped events.");
+            }
+        }
+    }
+}