7196547: [macosx] Implement dead key detection for KeyEvent
Reviewed-by: skovatch, kizune
--- 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.");
+ }
+ }
+ }
+}