From cf9a4e236891ce2f6d9469a017e880eed704dea0 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sun, 28 Oct 2012 05:44:24 +0100 Subject: 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. --- make/scripts/tests-x64.bat | 3 +- make/scripts/tests.sh | 3 +- .../classes/com/jogamp/newt/event/KeyEvent.java | 16 +- .../jogamp/newt/driver/macosx/MacKeyUtil.java | 145 ++++++------ .../jogamp/newt/driver/macosx/WindowDriver.java | 6 +- src/newt/native/NewtMacWindow.h | 4 + src/newt/native/NewtMacWindow.m | 74 +++++- src/newt/native/X11Display.c | 2 + .../opengl/test/junit/newt/TestKeyCodeNEWT.java | 256 +++++++++++++++++++++ .../jogamp/opengl/test/junit/util/NEWTKeyUtil.java | 64 ++++++ 10 files changed, 472 insertions(+), 101 deletions(-) create mode 100644 src/test/com/jogamp/opengl/test/junit/newt/TestKeyCodeNEWT.java diff --git a/make/scripts/tests-x64.bat b/make/scripts/tests-x64.bat index 72ee5f666..6c9dae729 100755 --- a/make/scripts/tests-x64.bat +++ b/make/scripts/tests-x64.bat @@ -34,7 +34,8 @@ REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestParenting01 REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestListenerCom01AWT REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestKeyEventOrderNEWT %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestKeyEventAutoRepeatNEWT %* -scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestKeyPressReleaseUnmaskRepeatNEWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestKeyPressReleaseUnmaskRepeatNEWT %* +scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestKeyCodeNEWT %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01NEWT %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting02NEWT %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01bAWT %* diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 8594bfe2e..0db48b289 100755 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -377,7 +377,8 @@ function testawtswt() { #testawt com.jogamp.opengl.test.junit.newt.TestFocus02SwingAWTRobot $* #testawt com.jogamp.opengl.test.junit.newt.TestKeyEventOrderNEWT $* #testawt com.jogamp.opengl.test.junit.newt.TestKeyEventAutoRepeatNEWT $* -testawt com.jogamp.opengl.test.junit.newt.TestKeyPressReleaseUnmaskRepeatNEWT $* +#testawt com.jogamp.opengl.test.junit.newt.TestKeyPressReleaseUnmaskRepeatNEWT $* +testawt com.jogamp.opengl.test.junit.newt.TestKeyCodeNEWT $* #testawt com.jogamp.opengl.test.junit.newt.TestListenerCom01AWT #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting01aAWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting01bAWT $* 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; @@ -296,18 +296,10 @@ public class KeyEvent extends InputEvent public static final int VK_MULTIPLY = 0x6A; 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; @@ -596,6 +600,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]] ) { @@ -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) + * + *

+ * Note Event order: + *

    + *
  1. {@link #EVENT_KEY_PRESSED}
  2. + *
  3. {@link #EVENT_KEY_RELEASED}
  4. + *
  5. {@link #EVENT_KEY_TYPED}
  6. + *
+ *

+ */ +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> cse = new ArrayList>(); + + for(int i=0; i queue = keyAdapter.getQueued(); + for(int j=0; j < 10 && queue.size() < 3 * codeCount; j++) { // wait until events are collected + robot.delay(100); + } + final ArrayList events = new ArrayList(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 keyEvents) { for(int i=0; i> keyEventsList, boolean verbose) { + final List missCodes = new ArrayList(); + int totalCodeCount = 0; + boolean res = true; + for(int i=0; i 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, CodeSeg codeSeg, List keyEvents, boolean verbose) { + final int codeCount = codeSeg.max - codeSeg.min + 1; + int misses = 0; + for(int i=0; i