aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-10-28 05:44:24 +0100
committerSven Gothel <[email protected]>2012-10-28 05:44:24 +0100
commitcf9a4e236891ce2f6d9469a017e880eed704dea0 (patch)
treedd4a4a9ba0709dfe00c99931eafaf93f90466966 /src
parent70d58b030bdbac98ba592a3a14a84cc0e4941c51 (diff)
Fix NEWT KeyCode: Basic KeyCode Validation on X11, Windows and OSX
- X11: Add VK_QUOTE mapping - OSX: Add single shift, ctrl alt key press; Fix mapping: Command -> Windows, Option -> ALT, add BACK_QUOTE and QUOTE.
Diffstat (limited to 'src')
-rw-r--r--src/newt/classes/com/jogamp/newt/event/KeyEvent.java16
-rw-r--r--src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java145
-rw-r--r--src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java6
-rw-r--r--src/newt/native/NewtMacWindow.h4
-rw-r--r--src/newt/native/NewtMacWindow.m74
-rw-r--r--src/newt/native/X11Display.c2
-rw-r--r--src/test/com/jogamp/opengl/test/junit/newt/TestKeyCodeNEWT.java256
-rw-r--r--src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java64
8 files changed, 468 insertions, 99 deletions
diff --git a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java
index a8ac70b4d..8d3d9e88f 100644
--- a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java
+++ b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java
@@ -156,10 +156,10 @@ public class KeyEvent extends InputEvent
/* Virtual key codes. */
- public static final int VK_ENTER = '\n';
- public static final int VK_BACK_SPACE = '\b';
- public static final int VK_TAB = '\t';
public static final int VK_CANCEL = 0x03;
+ public static final int VK_BACK_SPACE = 0x08; // '\b'
+ public static final int VK_TAB = 0x09; // '\t'
+ public static final int VK_ENTER = 0x0A; // '\n'
public static final int VK_CLEAR = 0x0C;
public static final int VK_SHIFT = 0x10;
public static final int VK_CONTROL = 0x11;
@@ -297,17 +297,9 @@ public class KeyEvent extends InputEvent
public static final int VK_ADD = 0x6B;
/**
- * This constant is obsolete, and is included only for backwards
- * compatibility.
- * @see #VK_SEPARATOR
- */
- public static final int VK_SEPARATER = 0x6C;
-
- /**
* Constant for the Numpad Separator key.
- * @since 1.4
*/
- public static final int VK_SEPARATOR = VK_SEPARATER;
+ public static final int VK_SEPARATOR = 0x6C;
public static final int VK_SUBTRACT = 0x6D;
public static final int VK_DECIMAL = 0x6E;
diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java b/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java
index d39e0027b..5966bd30f 100644
--- a/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java
+++ b/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java
@@ -162,13 +162,13 @@ public class MacKeyUtil {
case kVK_Space: return KeyEvent.VK_SPACE;
case kVK_Delete: return KeyEvent.VK_BACK_SPACE;
case kVK_Escape: return KeyEvent.VK_ESCAPE;
- case kVK_Command: return KeyEvent.VK_ALT;
+ case kVK_Command: return KeyEvent.VK_WINDOWS;
case kVK_Shift: return KeyEvent.VK_SHIFT;
case kVK_CapsLock: return KeyEvent.VK_CAPS_LOCK;
- case kVK_Option: return KeyEvent.VK_WINDOWS;
+ case kVK_Option: return KeyEvent.VK_ALT;
case kVK_Control: return KeyEvent.VK_CONTROL;
case kVK_RightShift: return KeyEvent.VK_SHIFT;
- case kVK_RightOption: return KeyEvent.VK_WINDOWS;
+ case kVK_RightOption: return KeyEvent.VK_ALT;
case kVK_RightControl: return KeyEvent.VK_CONTROL;
// case kVK_Function: return KeyEvent.VK_F;
case kVK_F17: return KeyEvent.VK_F17;
@@ -206,78 +206,73 @@ public class MacKeyUtil {
case kVK_UpArrow: return KeyEvent.VK_UP;
}
- if (keyChar == '\r') {
- // Turn these into \n
- return KeyEvent.VK_ENTER;
- }
-
- if (keyChar >= NSUpArrowFunctionKey && keyChar <= NSModeSwitchFunctionKey) {
- switch (keyChar) {
- case NSUpArrowFunctionKey: return KeyEvent.VK_UP;
- case NSDownArrowFunctionKey: return KeyEvent.VK_DOWN;
- case NSLeftArrowFunctionKey: return KeyEvent.VK_LEFT;
- case NSRightArrowFunctionKey: return KeyEvent.VK_RIGHT;
- case NSF1FunctionKey: return KeyEvent.VK_F1;
- case NSF2FunctionKey: return KeyEvent.VK_F2;
- case NSF3FunctionKey: return KeyEvent.VK_F3;
- case NSF4FunctionKey: return KeyEvent.VK_F4;
- case NSF5FunctionKey: return KeyEvent.VK_F5;
- case NSF6FunctionKey: return KeyEvent.VK_F6;
- case NSF7FunctionKey: return KeyEvent.VK_F7;
- case NSF8FunctionKey: return KeyEvent.VK_F8;
- case NSF9FunctionKey: return KeyEvent.VK_F9;
- case NSF10FunctionKey: return KeyEvent.VK_F10;
- case NSF11FunctionKey: return KeyEvent.VK_F11;
- case NSF12FunctionKey: return KeyEvent.VK_F12;
- case NSF13FunctionKey: return KeyEvent.VK_F13;
- case NSF14FunctionKey: return KeyEvent.VK_F14;
- case NSF15FunctionKey: return KeyEvent.VK_F15;
- case NSF16FunctionKey: return KeyEvent.VK_F16;
- case NSF17FunctionKey: return KeyEvent.VK_F17;
- case NSF18FunctionKey: return KeyEvent.VK_F18;
- case NSF19FunctionKey: return KeyEvent.VK_F19;
- case NSF20FunctionKey: return KeyEvent.VK_F20;
- case NSF21FunctionKey: return KeyEvent.VK_F21;
- case NSF22FunctionKey: return KeyEvent.VK_F22;
- case NSF23FunctionKey: return KeyEvent.VK_F23;
- case NSF24FunctionKey: return KeyEvent.VK_F24;
- case NSInsertFunctionKey: return KeyEvent.VK_INSERT;
- case NSDeleteFunctionKey: return KeyEvent.VK_DELETE;
- case NSHomeFunctionKey: return KeyEvent.VK_HOME;
- case NSBeginFunctionKey: return KeyEvent.VK_BEGIN;
- case NSEndFunctionKey: return KeyEvent.VK_END;
- case NSPageUpFunctionKey: return KeyEvent.VK_PAGE_UP;
- case NSPageDownFunctionKey: return KeyEvent.VK_PAGE_DOWN;
- case NSPrintScreenFunctionKey: return KeyEvent.VK_PRINTSCREEN;
- case NSScrollLockFunctionKey: return KeyEvent.VK_SCROLL_LOCK;
- case NSPauseFunctionKey: return KeyEvent.VK_PAUSE;
- // Not handled:
- // NSSysReqFunctionKey
- // NSBreakFunctionKey
- // NSResetFunctionKey
- case NSStopFunctionKey: return KeyEvent.VK_STOP;
- // Not handled:
- // NSMenuFunctionKey
- // NSUserFunctionKey
- // NSSystemFunctionKey
- // NSPrintFunctionKey
- // NSClearLineFunctionKey
- // NSClearDisplayFunctionKey
- // NSInsertLineFunctionKey
- // NSDeleteLineFunctionKey
- // NSInsertCharFunctionKey
- // NSDeleteCharFunctionKey
- // NSPrevFunctionKey
- // NSNextFunctionKey
- // NSSelectFunctionKey
- // NSExecuteFunctionKey
- // NSUndoFunctionKey
- // NSRedoFunctionKey
- // NSFindFunctionKey
- // NSHelpFunctionKey
- // NSModeSwitchFunctionKey
- default: break;
- }
+ switch (keyChar) {
+ case NSUpArrowFunctionKey: return KeyEvent.VK_UP;
+ case NSDownArrowFunctionKey: return KeyEvent.VK_DOWN;
+ case NSLeftArrowFunctionKey: return KeyEvent.VK_LEFT;
+ case NSRightArrowFunctionKey: return KeyEvent.VK_RIGHT;
+ case NSF1FunctionKey: return KeyEvent.VK_F1;
+ case NSF2FunctionKey: return KeyEvent.VK_F2;
+ case NSF3FunctionKey: return KeyEvent.VK_F3;
+ case NSF4FunctionKey: return KeyEvent.VK_F4;
+ case NSF5FunctionKey: return KeyEvent.VK_F5;
+ case NSF6FunctionKey: return KeyEvent.VK_F6;
+ case NSF7FunctionKey: return KeyEvent.VK_F7;
+ case NSF8FunctionKey: return KeyEvent.VK_F8;
+ case NSF9FunctionKey: return KeyEvent.VK_F9;
+ case NSF10FunctionKey: return KeyEvent.VK_F10;
+ case NSF11FunctionKey: return KeyEvent.VK_F11;
+ case NSF12FunctionKey: return KeyEvent.VK_F12;
+ case NSF13FunctionKey: return KeyEvent.VK_F13;
+ case NSF14FunctionKey: return KeyEvent.VK_F14;
+ case NSF15FunctionKey: return KeyEvent.VK_F15;
+ case NSF16FunctionKey: return KeyEvent.VK_F16;
+ case NSF17FunctionKey: return KeyEvent.VK_F17;
+ case NSF18FunctionKey: return KeyEvent.VK_F18;
+ case NSF19FunctionKey: return KeyEvent.VK_F19;
+ case NSF20FunctionKey: return KeyEvent.VK_F20;
+ case NSF21FunctionKey: return KeyEvent.VK_F21;
+ case NSF22FunctionKey: return KeyEvent.VK_F22;
+ case NSF23FunctionKey: return KeyEvent.VK_F23;
+ case NSF24FunctionKey: return KeyEvent.VK_F24;
+ case NSInsertFunctionKey: return KeyEvent.VK_INSERT;
+ case NSDeleteFunctionKey: return KeyEvent.VK_DELETE;
+ case NSHomeFunctionKey: return KeyEvent.VK_HOME;
+ case NSBeginFunctionKey: return KeyEvent.VK_BEGIN;
+ case NSEndFunctionKey: return KeyEvent.VK_END;
+ case NSPageUpFunctionKey: return KeyEvent.VK_PAGE_UP;
+ case NSPageDownFunctionKey: return KeyEvent.VK_PAGE_DOWN;
+ case NSPrintScreenFunctionKey: return KeyEvent.VK_PRINTSCREEN;
+ case NSScrollLockFunctionKey: return KeyEvent.VK_SCROLL_LOCK;
+ case NSPauseFunctionKey: return KeyEvent.VK_PAUSE;
+ // Not handled:
+ // NSSysReqFunctionKey
+ // NSBreakFunctionKey
+ // NSResetFunctionKey
+ case NSStopFunctionKey: return KeyEvent.VK_STOP;
+ // Not handled:
+ // NSMenuFunctionKey
+ // NSUserFunctionKey
+ // NSSystemFunctionKey
+ // NSPrintFunctionKey
+ // NSClearLineFunctionKey
+ // NSClearDisplayFunctionKey
+ // NSInsertLineFunctionKey
+ // NSDeleteLineFunctionKey
+ // NSInsertCharFunctionKey
+ // NSDeleteCharFunctionKey
+ // NSPrevFunctionKey
+ // NSNextFunctionKey
+ // NSSelectFunctionKey
+ // NSExecuteFunctionKey
+ // NSUndoFunctionKey
+ // NSRedoFunctionKey
+ // NSFindFunctionKey
+ // NSHelpFunctionKey
+ // NSModeSwitchFunctionKey
+ case 0x60: return KeyEvent.VK_BACK_QUOTE; // `
+ case 0x27: return KeyEvent.VK_QUOTE; // '
+ case '\r': return KeyEvent.VK_ENTER;
}
if ('a' <= keyChar && keyChar <= 'z') {
diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java
index e0ea8ea76..bc83f9540 100644
--- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java
+++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java
@@ -314,12 +314,12 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl
}
}
- private final void handleKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) {
+ private final void handleKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int _keyCode, char keyChar) {
// Note that we send the key char for the key code on this
// platform -- we do not get any useful key codes out of the system
- keyCode = MacKeyUtil.validateKeyCode(keyCode, keyChar);
+ final int keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar);
if(DEBUG_IMPLEMENTATION) {
- System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode));
+ System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(_keyCode)+" -> 0x"+Integer.toHexString(keyCode));
}
// Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE and TYPED keys _before_ PRESSED
switch(eventType) {
diff --git a/src/newt/native/NewtMacWindow.h b/src/newt/native/NewtMacWindow.h
index c0912ad3c..29b646fbf 100644
--- a/src/newt/native/NewtMacWindow.h
+++ b/src/newt/native/NewtMacWindow.h
@@ -111,6 +111,7 @@
BOOL mouseInside;
BOOL cursorIsHidden;
BOOL realized;
+ BOOL modsDown[4]; // shift, ctrl, alt/option, win/command
NSPoint lastInsideMousePosition;
@public
int cachedInsets[4]; // l, r, t, b
@@ -145,6 +146,7 @@
- (void) setMousePosition:(NSPoint)p;
- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType;
+- (void) sendKeyEvent: (jint) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jint) evType;
- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType;
- (void) focusChanged: (BOOL) gained;
@@ -157,6 +159,8 @@
- (void) windowDidResignKey: (NSNotification *) notification;
- (void) keyDown: (NSEvent*) theEvent;
- (void) keyUp: (NSEvent*) theEvent;
+- (void) handleFlagsChanged:(int) keyMask keyIndex: (int) keyIdx keyCode: (int) keyCode modifiers: (NSUInteger) mods;
+- (void) flagsChanged: (NSEvent *) theEvent;
- (void) mouseEntered: (NSEvent*) theEvent;
- (void) mouseExited: (NSEvent*) theEvent;
- (void) mouseMoved: (NSEvent*) theEvent;
diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m
index b58b99e38..de5f3773c 100644
--- a/src/newt/native/NewtMacWindow.m
+++ b/src/newt/native/NewtMacWindow.m
@@ -368,6 +368,10 @@ static jmethodID windowRepaintID = NULL;
cachedInsets[1] = 0; // r
cachedInsets[2] = 0; // t
cachedInsets[3] = 0; // b
+ modsDown[0] = NO; // shift
+ modsDown[1] = NO; // ctrl
+ modsDown[2] = NO; // alt
+ modsDown[3] = NO; // win
mouseConfined = NO;
mouseVisible = YES;
mouseInside = NO;
@@ -597,6 +601,14 @@ static jint mods2JavaMods(NSUInteger mods)
- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType
{
+ jint keyCode = (jint) [event keyCode];
+ NSString* chars = [event charactersIgnoringModifiers];
+ NSUInteger mods = [event modifierFlags];
+ [self sendKeyEvent: keyCode characters: chars modifiers: mods eventType: evType];
+}
+
+- (void) sendKeyEvent: (jint) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jint) evType
+{
NSView* nsview = [self contentView];
if( ! [nsview isMemberOfClass:[NewtView class]] ) {
return;
@@ -616,16 +628,30 @@ static jint mods2JavaMods(NSUInteger mods)
}
int i;
- jint keyCode = (jint) [event keyCode];
- NSString* chars = [event charactersIgnoringModifiers];
- int len = [chars length];
- jint javaMods = mods2JavaMods([event modifierFlags]);
-
- for (i = 0; i < len; i++) {
- // Note: the key code in the NSEvent does not map to anything we can use
- jchar keyChar = (jchar) [chars characterAtIndex: i];
+ int len = NULL != chars ? [chars length] : 0;
+ jint javaMods = mods2JavaMods(mods);
+
+ if(len > 0) {
+ // printable chars
+ for (i = 0; i < len; i++) {
+ // Note: the key code in the NSEvent does not map to anything we can use
+ jchar keyChar = (jchar) [chars characterAtIndex: i];
+
+ DBG_PRINT("sendKeyEvent: %d/%d char 0x%X, code 0x%X\n", i, len, (int)keyChar, (int)keyCode);
+
+ #ifdef USE_SENDIO_DIRECT
+ (*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID,
+ evType, javaMods, keyCode, keyChar);
+ #else
+ (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE,
+ evType, javaMods, keyCode, keyChar);
+ #endif
+ }
+ } else {
+ // non-printable chars
+ jchar keyChar = (jchar) -1;
- DBG_PRINT("sendKeyEvent: %d/%d char 0x%X, code 0x%X\n", i, len, (int)keyChar, (int)keyCode);
+ DBG_PRINT("sendKeyEvent: code 0x%X\n", (int)keyCode);
#ifdef USE_SENDIO_DIRECT
(*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID,
@@ -805,6 +831,36 @@ static jint mods2JavaMods(NSUInteger mods)
[self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED];
}
+#define kVK_Shift 0x38
+#define kVK_Option 0x3A
+#define kVK_Control 0x3B
+#define kVK_Command 0x37
+
+- (void) handleFlagsChanged:(int) keyMask keyIndex: (int) keyIdx keyCode: (int) keyCode modifiers: (NSUInteger) mods
+{
+ if ( NO == modsDown[keyIdx] && 0 != ( mods & keyMask ) ) {
+ modsDown[keyIdx] = YES;
+ mods &= ~keyMask;
+ [self sendKeyEvent: keyCode characters: NULL modifiers: mods eventType: EVENT_KEY_TYPED];
+ } else if ( YES == modsDown[keyIdx] && 0 == ( mods & keyMask ) ) {
+ modsDown[keyIdx] = NO;
+ [self sendKeyEvent: keyCode characters: NULL modifiers: mods eventType: EVENT_KEY_RELEASED];
+ [self sendKeyEvent: keyCode characters: NULL modifiers: mods eventType: EVENT_KEY_TYPED];
+ }
+}
+
+- (void) flagsChanged:(NSEvent *) theEvent
+{
+ NSUInteger mods = [theEvent modifierFlags];
+
+ // BOOL modsDown[4]; // shift, ctrl, alt/option, win/command
+
+ [self handleFlagsChanged: NSShiftKeyMask keyIndex: 0 keyCode: kVK_Shift modifiers: mods];
+ [self handleFlagsChanged: NSControlKeyMask keyIndex: 1 keyCode: kVK_Control modifiers: mods];
+ [self handleFlagsChanged: NSAlternateKeyMask keyIndex: 2 keyCode: kVK_Option modifiers: mods];
+ [self handleFlagsChanged: NSCommandKeyMask keyIndex: 3 keyCode: kVK_Command modifiers: mods];
+}
+
- (void) mouseEntered: (NSEvent*) theEvent
{
DBG_PRINT( "mouseEntered: confined %d, visible %d\n", mouseConfined, mouseVisible);
diff --git a/src/newt/native/X11Display.c b/src/newt/native/X11Display.c
index 9f29acc0c..341455f0f 100644
--- a/src/newt/native/X11Display.c
+++ b/src/newt/native/X11Display.c
@@ -148,6 +148,8 @@ static jint X11KeySym2NewtVKey(KeySym keySym) {
return J_VK_HELP;
case XK_grave:
return J_VK_BACK_QUOTE;
+ case XK_apostrophe:
+ return J_VK_QUOTE;
}
return keySym;
}
diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestKeyCodeNEWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestKeyCodeNEWT.java
new file mode 100644
index 000000000..8af0f0246
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/newt/TestKeyCodeNEWT.java
@@ -0,0 +1,256 @@
+/**
+ * Copyright 2012 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.newt;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.Before;
+
+import java.awt.AWTException;
+import java.awt.BorderLayout;
+import java.awt.Robot;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.List;
+
+import javax.media.opengl.GLCapabilities;
+import javax.media.opengl.GLEventListener;
+import javax.swing.JFrame;
+
+import java.io.IOException;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.jogamp.newt.awt.NewtCanvasAWT;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.test.junit.jogl.demos.es2.RedSquareES2;
+
+import com.jogamp.opengl.test.junit.util.*;
+import com.jogamp.opengl.test.junit.util.NEWTKeyUtil.CodeSeg;
+
+/**
+ * Testing key event order incl. auto-repeat (Bug 601)
+ *
+ * <p>
+ * Note Event order:
+ * <ol>
+ * <li>{@link #EVENT_KEY_PRESSED}</li>
+ * <li>{@link #EVENT_KEY_RELEASED}</li>
+ * <li>{@link #EVENT_KEY_TYPED}</li>
+ * </ol>
+ * </p>
+ */
+public class TestKeyCodeNEWT extends UITestCase {
+ static int width, height;
+ static long durationPerTest = 100;
+ static long awtWaitTimeout = 1000;
+
+ static GLCapabilities glCaps;
+
+ @BeforeClass
+ public static void initClass() {
+ width = 640;
+ height = 480;
+ glCaps = new GLCapabilities(null);
+ }
+
+ @AfterClass
+ public static void release() {
+ }
+
+ @Before
+ public void initTest() {
+ }
+
+ @After
+ public void releaseTest() {
+ }
+
+ @Test
+ public void test01NEWT() throws AWTException, InterruptedException, InvocationTargetException {
+ GLWindow glWindow = GLWindow.create(glCaps);
+ glWindow.setSize(width, height);
+ glWindow.setVisible(true);
+
+ testImpl(glWindow);
+
+ glWindow.destroy();
+ }
+
+ // @Test
+ public void test02NewtCanvasAWT() throws AWTException, InterruptedException, InvocationTargetException {
+ GLWindow glWindow = GLWindow.create(glCaps);
+
+ // Wrap the window in a canvas.
+ final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow);
+
+ // Add the canvas to a frame, and make it all visible.
+ final JFrame frame1 = new JFrame("Swing AWT Parent Frame: "+ glWindow.getTitle());
+ frame1.getContentPane().add(newtCanvasAWT, BorderLayout.CENTER);
+ frame1.setSize(width, height);
+ javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ frame1.setVisible(true);
+ } } );
+
+ Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame1, true));
+
+ testImpl(glWindow);
+
+ try {
+ javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ frame1.setVisible(false);
+ frame1.dispose();
+ }});
+ } catch( Throwable throwable ) {
+ throwable.printStackTrace();
+ Assume.assumeNoException( throwable );
+ }
+ glWindow.destroy();
+ }
+
+ static CodeSeg[] codeSegments = new CodeSeg[] {
+ new CodeSeg(0x008, 0x00a, "bs, tab, cr"),
+ new CodeSeg(0x010, 0x011, "shift, ctrl"), // single alt n/a on windows
+ new CodeSeg(0x01B, 0x01B, "esc"),
+ new CodeSeg(0x020, 0x024, "space, up, down, end, home"),
+ new CodeSeg(0x025, 0x028, "cursor"),
+ new CodeSeg(0x02C, 0x02F, ", - . /"),
+ new CodeSeg(0x030, 0x039, "0 - 9"),
+ new CodeSeg(0x03B, 0x03B, ";"),
+ new CodeSeg(0x03D, 0x03D, "="),
+ new CodeSeg(0x041, 0x05A, "a - z"),
+ new CodeSeg(0x05B, 0x05D, "[ \\ ]"),
+ // new CodeSeg(0x060, 0x06B, "numpad1"), // can be mapped to normal keycodes
+ // new CodeSeg(0x06D, 0x06F, "numpad2"), // can be mapped to normal keycodes
+ new CodeSeg(0x07F, 0x07F, "del"),
+ // new CodeSeg(0x090, 0x091, "num lock, scroll lock"),
+ // new CodeSeg(0x070, 0x07B, "F1 - F12"),
+ // new CodeSeg(0x09A, 0x09D, "prt ins hlp meta"),
+ new CodeSeg(0x0C0, 0x0C0, "back quote"),
+ new CodeSeg(0x0DE, 0x0DE, "quote"),
+ // new CodeSeg(0x0E0, 0x0E3, "cursor kp"),
+ // new CodeSeg(0x080, 0x08F, "dead-1"),
+ // new CodeSeg(0x096, 0x0A2, "& ^ \" < > { }"),
+ // new CodeSeg(0x200, 0x20D, "extra-2"), // @ ; ..
+ };
+
+ static void testKeyCode(Robot robot, NEWTKeyAdapter keyAdapter) {
+ List<List<EventObject>> cse = new ArrayList<List<EventObject>>();
+
+ for(int i=0; i<codeSegments.length; i++) {
+ keyAdapter.reset();
+ final CodeSeg codeSeg = codeSegments[i];
+ // System.err.println("*** Segment "+codeSeg.description);
+ for(int c=codeSeg.min; c<=codeSeg.max; c++) {
+ // System.err.println("*** KeyCode 0x"+Integer.toHexString(c));
+ AWTRobotUtil.keyPress(0, robot, true, c, 10);
+ AWTRobotUtil.keyPress(0, robot, false, c, 10);
+ robot.waitForIdle();
+ }
+ final int codeCount = codeSeg.max - codeSeg.min + 1;
+ final List<EventObject> queue = keyAdapter.getQueued();
+ for(int j=0; j < 10 && queue.size() < 3 * codeCount; j++) { // wait until events are collected
+ robot.delay(100);
+ }
+ final ArrayList<EventObject> events = new ArrayList<EventObject>(queue);
+ cse.add(events);
+ }
+ Assert.assertEquals("KeyCode impl. incomplete", true, NEWTKeyUtil.validateKeyCode(codeSegments, cse, true));
+ }
+
+ void testImpl(GLWindow glWindow) throws AWTException, InterruptedException, InvocationTargetException {
+ final Robot robot = new Robot();
+ robot.setAutoWaitForIdle(true);
+
+ GLEventListener demo1 = new RedSquareES2();
+ TestListenerCom01AWT.setDemoFields(demo1, glWindow, false);
+ glWindow.addGLEventListener(demo1);
+
+ NEWTKeyAdapter glWindow1KA = new NEWTKeyAdapter("GLWindow1");
+ glWindow1KA.setVerbose(false);
+ glWindow.addKeyListener(glWindow1KA);
+
+ Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow, true));
+ AWTRobotUtil.clearAWTFocus(robot);
+
+ // Continuous animation ..
+ Animator animator = new Animator(glWindow);
+ animator.start();
+
+ Thread.sleep(durationPerTest); // manual testing
+
+ glWindow1KA.reset();
+ AWTRobotUtil.assertRequestFocusAndWait(null, glWindow, glWindow, null, null); // programmatic
+ // AWTRobotUtil.assertRequestFocusAndWait(robot, glWindow, glWindow, null, null); // by mouse click
+
+ //
+ // Test the key event order w/o auto-repeat
+ //
+ testKeyCode(robot, glWindow1KA);
+
+ // Remove listeners to avoid logging during dispose/destroy.
+ glWindow.removeKeyListener(glWindow1KA);
+
+ // Shutdown the test.
+ animator.stop();
+ }
+
+ static int atoi(String a) {
+ int i=0;
+ try {
+ i = Integer.parseInt(a);
+ } catch (Exception ex) { ex.printStackTrace(); }
+ return i;
+ }
+
+ public static void main(String args[]) throws IOException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ durationPerTest = atoi(args[++i]);
+ }
+ }
+ /**
+ BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
+ System.err.println("Press enter to continue");
+ System.err.println(stdin.readLine());
+ */
+ System.out.println("durationPerTest: "+durationPerTest);
+ String tstname = TestKeyCodeNEWT.class.getName();
+ org.junit.runner.JUnitCore.main(tstname);
+ }
+
+
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java b/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java
index 299ea3a6e..95818845e 100644
--- a/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java
+++ b/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java
@@ -27,6 +27,7 @@
*/
package com.jogamp.opengl.test.junit.util;
+import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
@@ -36,12 +37,75 @@ import com.jogamp.common.util.IntIntHashMap;
import com.jogamp.newt.event.KeyEvent;
public class NEWTKeyUtil {
+ public static class CodeSeg {
+ public final int min;
+ public final int max;
+ public final String description;
+
+ public CodeSeg(int min, int max, String description) {
+ this.min = min;
+ this.max = max;
+ this.description = description;
+ }
+ }
+ public static class CodeEvent {
+ public final int code;
+ public final String description;
+ public final KeyEvent event;
+
+ public CodeEvent(int code, String description, KeyEvent event) {
+ this.code = code;
+ this.description = description;
+ this.event = event;
+ }
+ public String toString() {
+ return "Code 0x"+Integer.toHexString(code)+" != "+event+" // "+description;
+ }
+ }
+
public static void dumpKeyEvents(List<EventObject> keyEvents) {
for(int i=0; i<keyEvents.size(); i++) {
System.err.println(i+": "+keyEvents.get(i));
}
}
+ public static boolean validateKeyCode(CodeSeg[] codeSegments, List<List<EventObject>> keyEventsList, boolean verbose) {
+ final List<CodeEvent> missCodes = new ArrayList<CodeEvent>();
+ int totalCodeCount = 0;
+ boolean res = true;
+ for(int i=0; i<codeSegments.length; i++) {
+ final CodeSeg codeSeg = codeSegments[i];
+ totalCodeCount += codeSeg.max - codeSeg.min + 1;
+ final List<EventObject> keyEvents = keyEventsList.get(i);
+ res &= validateKeyCode(missCodes, codeSeg, keyEvents, verbose);
+ }
+ if(verbose) {
+ System.err.println("*** Total KeyCode Misses "+missCodes.size()+" / "+totalCodeCount+", valid "+res);
+ for(int i=0; i<missCodes.size(); i++) {
+ System.err.println("Miss["+i+"]: "+missCodes.get(i));
+ }
+ }
+ return res;
+ }
+ public static boolean validateKeyCode(List<CodeEvent> missCodes, CodeSeg codeSeg, List<EventObject> keyEvents, boolean verbose) {
+ final int codeCount = codeSeg.max - codeSeg.min + 1;
+ int misses = 0;
+ for(int i=0; i<codeCount; i++) {
+ final int c = codeSeg.min + i;
+ final int j = i*3+0; // KEY_PRESSED
+ final KeyEvent e = (KeyEvent) ( j < keyEvents.size() ? keyEvents.get(j) : null );
+ if( null == e || c != e.getKeyCode() ) {
+ missCodes.add(new CodeEvent(c, codeSeg.description, e));
+ misses++;
+ }
+ }
+ final boolean res = 3*codeCount == keyEvents.size() && 0 == missCodes.size();
+ if(verbose) {
+ System.err.println("+++ Code Segment "+codeSeg.description+", Misses: "+misses+" / "+codeCount+", events "+keyEvents.size()+", valid "+res);
+ }
+ return res;
+ }
+
public static int getNextKeyEventType(int et) {
switch( et ) {
case KeyEvent.EVENT_KEY_PRESSED: