diff options
author | Sven Gothel <[email protected]> | 2012-10-27 02:48:43 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-10-27 02:48:43 +0200 |
commit | 2f9c77a347b76bebdadd4bec1ac92aa7ab72365f (patch) | |
tree | d3280300b69ddb841eaf6f05aeaed1effabe8395 | |
parent | b8588b12e65ee47b5a74d75ea420a5252e0d93bb (diff) |
Fix Bug 631 and enhance 601: Handle multiple keys (pressed, released, types incl. auto-repeat)
- Using keyCode (bit) maps to isPressed and isAutoRepeat, allowing use of multiple keys
- Enhance unit test TestKeyEventOrderNEWT w/ injecting variations of 2 diff. keys
- Manual tested on X11, Windows and OSX w/ and w/o auto-repeat
8 files changed, 350 insertions, 108 deletions
diff --git a/make/scripts/tests-x64.bat b/make/scripts/tests-x64.bat index f67bda373..21e303dc2 100755 --- a/make/scripts/tests-x64.bat +++ b/make/scripts/tests-x64.bat @@ -34,6 +34,7 @@ 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 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 %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestKeyPressReleaseUnmaskRepeatNEWT %* 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 047c0ed63..734fa671f 100755 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -375,8 +375,9 @@ function testawtswt() { #testawt com.jogamp.opengl.test.junit.newt.TestEventSourceNotAWTBug #testawt com.jogamp.opengl.test.junit.newt.TestFocus01SwingAWTRobot $* #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.TestKeyEventOrderNEWT $* +#testawt com.jogamp.opengl.test.junit.newt.TestKeyEventAutoRepeatNEWT $* +#testawt com.jogamp.opengl.test.junit.newt.TestKeyPressReleaseUnmaskRepeatNEWT $* #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/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 8901411a3..311ed0bbb 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -37,6 +37,7 @@ package jogamp.newt; import java.util.ArrayList; import java.lang.reflect.Method; +import com.jogamp.common.util.IntBitfield; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Display; @@ -2169,6 +2170,30 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // // KeyListener/Event Support // + protected IntBitfield keyPressedState = new IntBitfield(KeyEvent.VK_CONTEXT_MENU+1); + protected IntBitfield keyRepeatState = new IntBitfield(KeyEvent.VK_CONTEXT_MENU+1); + + /** + * @param keyCode + * @return 1 if pressed, 0 if not pressed, -1 if not handled. + */ + protected final int isKeyPressed(int keyCode) { + if( 0 <= keyCode && keyCode < keyPressedState.capacity() ) { + return keyPressedState.get(keyCode) ? 1 : 0; + } + return -1; + } + /** + * @param keyCode + * @return 1 if pressed, 0 if not pressed, -1 if not handled. + */ + protected final int isKeyInAutoRepeat(int keyCode) { + if( 0 <= keyCode && keyCode < keyRepeatState.capacity() ) { + return keyRepeatState.get(keyCode) ? 1 : 0; + } + return -1; + } + public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { consumeKeyEvent(new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java index 4eeafb244..bcdd6b9df 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java @@ -306,74 +306,60 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } // else may need offscreen solution ? FIXME } - @Override - public void sendKeyEvent(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 - final int keyCode2 = MacKeyUtil.validateKeyCode(keyCode, keyChar); - final boolean valid = validateKeyEvent(eventType, modifiers, keyCode); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)+", valid "+valid); - if(valid) { - if(pressedKeyBalance > 1) { - // Auto-Repeat: OSX delivers only PRESSED - // inject auto-repeat RELEASE and TYPED keys _before_ - pressedKeyBalance--; - modifiers |= InputEvent.AUTOREPEAT_MASK; - super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); // RELEASED - super.sendKeyEvent(KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); // TYPED - } - // only deliver keyChar on key Typed events, harmonizing platform behavior - keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; - super.sendKeyEvent(eventType, modifiers, keyCode2, keyChar); + private final void emitKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { + if( send ) { + super.sendKeyEvent(eventType, modifiers, keyCode, keyChar); + } else { + super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keyChar); } } - - @Override - public void enqueueKeyEvent(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 - final int keyCode2 = MacKeyUtil.validateKeyCode(keyCode, keyChar); - final boolean valid = validateKeyEvent(eventType, modifiers, keyCode); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.enqueueKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)+", valid "+valid); - if(valid) { - if(pressedKeyBalance > 1) { - // Auto-Repeat: OSX delivers only PRESSED - // inject auto-repeat RELEASE and TYPED keys _before_ - pressedKeyBalance--; - modifiers |= InputEvent.AUTOREPEAT_MASK; - super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); // RELEASED - super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); // TYPED - } - // only deliver keyChar on key Typed events, harmonizing platform behavior - keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; - super.enqueueKeyEvent(wait, eventType, modifiers, keyCode2, keyChar); + 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)); } - } - - private int keyDownModifiers = 0; - private int keyDownCode = 0; - private int pressedKeyBalance = 0; - - private boolean validateKeyEvent(int eventType, int modifiers, int keyCode) { + // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE and TYPED keys _before_ PRESSED switch(eventType) { - case KeyEvent.EVENT_KEY_PRESSED: - pressedKeyBalance++; - keyDownModifiers = modifiers; - keyDownCode = keyCode; - return true; case KeyEvent.EVENT_KEY_RELEASED: - pressedKeyBalance--; - return keyDownModifiers == modifiers && keyDownCode == keyCode; + if( 1 == isKeyInAutoRepeat(keyCode) ) { + // AR out + keyRepeatState.put(keyCode, false); + } + keyPressedState.put(keyCode, false); + keyChar = (char)-1; + break; + case KeyEvent.EVENT_KEY_PRESSED: + if( 1 == isKeyPressed(keyCode) ) { + if( 0 == isKeyInAutoRepeat(keyCode) ) { + // AR in + keyRepeatState.put(keyCode, true); + } + modifiers |= InputEvent.AUTOREPEAT_MASK; + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); // RELEASED + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); // TYPED + } else { + keyPressedState.put(keyCode, true); + } + keyChar = (char)-1; + break; case KeyEvent.EVENT_KEY_TYPED: - final boolean matchKeyDown = keyDownModifiers == modifiers && keyDownCode == keyCode; - keyDownModifiers = 0; - keyDownCode = 0; - return matchKeyDown; - default: - throw new NativeWindowException("Unexpected key event type " + eventType); + break; } + emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); + } + + @Override + public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { + handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); } + @Override + public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { + handleKeyEvent(false, wait, eventType, modifiers, keyCode, keyChar); + } //---------------------------------------------------------------------- // Internals only diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index c211bac61..be9f6603e 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -46,6 +46,7 @@ import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; +import com.jogamp.common.util.IntIntHashMap; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.MouseAdapter; @@ -259,29 +260,10 @@ public class WindowDriver extends WindowImpl { // nop - using event driven insetsChange(..) } - private final int validateKeyCode(int eventType, int modifiers, int keyCode, char keyChar) { - switch(eventType) { - case KeyEvent.EVENT_KEY_RELEASED: - pressedKeyBalance--; - lastPressedKeyCode = keyCode; - break; - case KeyEvent.EVENT_KEY_PRESSED: - pressedKeyBalance++; - lastPressedKeyCode = keyCode; - break; - case KeyEvent.EVENT_KEY_TYPED: - if(-1==keyCode) { - keyCode = lastPressedKeyCode; - } - lastPressedKeyCode = -1; - break; - } - return keyCode; - } + /** We have to regenerate the keyCode for EVENT_KEY_TYPED on this platform. */ private int lastPressedKeyCode = 0; - private char lastTypedKeyChar = 0; - private int pressedKeyBalance = 0; - private int autoRepeat = 0; + /** We have to reorder the native key events to match NEWT's order */ + private IntIntHashMap typedKeyCode2KeyChar = new IntIntHashMap(KeyEvent.VK_CONTEXT_MENU+1); private final void emitKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { if( send ) { @@ -292,47 +274,50 @@ public class WindowDriver extends WindowImpl { } private final void handleKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - // System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", mods "+toHexString(modifiers)); - - // Note that we have to regenerate the keyCode for EVENT_KEY_TYPED on this platform - keyCode = validateKeyCode(eventType, modifiers, keyCode, keyChar); - + // final int kc = 0 <= keyCode ? keyCode : lastPressedKeyCode; + // System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", key "+toHexString(kc)+", mods "+toHexString(modifiers)+", was: pressed "+isKeyPressed(kc)+", repeat "+isKeyInAutoRepeat(kc)); + // Reorder: WINDOWS delivery order is PRESSED, TYPED and RELEASED -> NEWT order: PRESSED, RELEASED and TYPED - // Auto-Repeat: WINDOWS delivers only PRESSED and TYPED. + // Auto-Repeat: WINDOWS delivers only PRESSED and TYPED. switch(eventType) { case KeyEvent.EVENT_KEY_RELEASED: - if( 0 != autoRepeat ) { + if( 1 == isKeyInAutoRepeat(keyCode) ) { // AR out - send out missing PRESSED - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers | autoRepeat, keyCode, lastTypedKeyChar); + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers | InputEvent.AUTOREPEAT_MASK, keyCode, (char)-1); + keyRepeatState.put(keyCode, false); } - autoRepeat = 0; - emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); - if( 0 != lastTypedKeyChar ) { + keyPressedState.put(keyCode, false); + emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); + final char lastTypedKeyChar = (char) typedKeyCode2KeyChar.put(keyCode, 0); + if( 0 < lastTypedKeyChar ) { emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, lastTypedKeyChar); - lastTypedKeyChar = 0; } break; case KeyEvent.EVENT_KEY_PRESSED: - if( pressedKeyBalance > 1 ) { - pressedKeyBalance--; - if ( 0 == autoRepeat ) { + lastPressedKeyCode = keyCode; + if( 1 == isKeyPressed(keyCode) ) { + if( 0 == isKeyInAutoRepeat(keyCode) ) { // AR in - skip already send PRESSED - autoRepeat = InputEvent.AUTOREPEAT_MASK; + keyRepeatState.put(keyCode, true); } else { - emitKeyEvent(send, wait, eventType, modifiers | autoRepeat, keyCode, (char)-1); + emitKeyEvent(send, wait, eventType, modifiers | InputEvent.AUTOREPEAT_MASK, keyCode, (char)-1); } } else { - autoRepeat = 0; + keyPressedState.put(keyCode, true); emitKeyEvent(send, wait, eventType, modifiers, keyCode, (char)-1); } break; case KeyEvent.EVENT_KEY_TYPED: - if( 0 == autoRepeat ) { - lastTypedKeyChar = keyChar; - } else { - modifiers |= autoRepeat; + if(-1==keyCode) { + keyCode = lastPressedKeyCode; + } + lastPressedKeyCode = -1; + if( 1 == isKeyInAutoRepeat(keyCode) ) { + modifiers |= InputEvent.AUTOREPEAT_MASK; emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); - emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); + emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); + } else { + typedKeyCode2KeyChar.put(keyCode, keyChar); } break; } diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestKeyEventOrderNEWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestKeyEventOrderNEWT.java index e0c3005a2..31d9912d1 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestKeyEventOrderNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestKeyEventOrderNEWT.java @@ -141,17 +141,33 @@ public class TestKeyEventOrderNEWT extends UITestCase { System.err.println("KEY Event Order Test: "+loops); keyAdapter.reset(); for(int i=0; i<loops; i++) { + // 1 AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_A, 10); - robot.waitForIdle(); AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_A, 100); robot.waitForIdle(); + // 2 + AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_B, 10); + AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_B, 100); + robot.waitForIdle(); + // 3 + 4 + AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_A, 10); + AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_B, 10); + AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_A, 10); + AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_B, 10); + robot.waitForIdle(); + // 5 + 6 + AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_A, 10); + AWTRobotUtil.keyPress(0, robot, true, java.awt.event.KeyEvent.VK_B, 10); + AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_B, 10); + AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_A, 10); + robot.waitForIdle(); } robot.delay(250); // dumpKeyEvents(keyAdapter.getQueued()); NEWTKeyUtil.validateKeyEventOrder(keyAdapter.getQueued()); - NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, loops, 0); + NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, 6*loops, 0); } void testImpl(GLWindow glWindow) throws AWTException, InterruptedException, InvocationTargetException { @@ -182,7 +198,7 @@ public class TestKeyEventOrderNEWT extends UITestCase { // // Test the key event order w/o auto-repeat // - testKeyEventOrder(robot, glWindow1KA, 12); + testKeyEventOrder(robot, glWindow1KA, 6); // Remove listeners to avoid logging during dispose/destroy. glWindow.removeKeyListener(glWindow1KA); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestKeyPressReleaseUnmaskRepeatNEWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestKeyPressReleaseUnmaskRepeatNEWT.java new file mode 100644 index 000000000..6f3b97f0c --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestKeyPressReleaseUnmaskRepeatNEWT.java @@ -0,0 +1,222 @@ +/** + * 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 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.event.InputEvent; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +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.*; + +/** + * Testing key press and release events w/o AUTO-REPEAT + */ +public class TestKeyPressReleaseUnmaskRepeatNEWT 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(); + } + + 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); + + SimpleKeyPressRelease simpleKeyPressRelease = new SimpleKeyPressRelease(); + glWindow.addKeyListener(simpleKeyPressRelease); + + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow, true)); + AWTRobotUtil.clearAWTFocus(robot); + + // Continuous animation .. + Animator animator = new Animator(glWindow); + animator.start(); + + Thread.sleep(durationPerTest); // manual testing + + AWTRobotUtil.assertRequestFocusAndWait(null, glWindow, glWindow, null, null); // programmatic + // AWTRobotUtil.assertRequestFocusAndWait(robot, glWindow, glWindow, null, null); // by mouse click + + // Remove listeners to avoid logging during dispose/destroy. + glWindow.removeKeyListener(simpleKeyPressRelease); + + // 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; + } + + static class SimpleKeyPressRelease implements KeyListener { + int seq; + + SimpleKeyPressRelease() { + reset(); + } + + public void reset() { + seq=0; + } + + @Override + public void keyPressed(KeyEvent e) { + if( 0 == ( InputEvent.AUTOREPEAT_MASK & e.getModifiers() ) ) { + seq++; + System.err.println(seq+": "+e); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if( 0 == ( InputEvent.AUTOREPEAT_MASK & e.getModifiers() ) ) { + seq++; + System.err.println(seq+": "+e); + } + } + + @Override + public void keyTyped(KeyEvent e) { + } + + } + + 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 = TestKeyPressReleaseUnmaskRepeatNEWT.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 dfc96edcb..299ea3a6e 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java +++ b/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java @@ -32,6 +32,7 @@ import java.util.List; import org.junit.Assert; +import com.jogamp.common.util.IntIntHashMap; import com.jogamp.newt.event.KeyEvent; public class NEWTKeyUtil { @@ -56,12 +57,17 @@ public class NEWTKeyUtil { } public static void validateKeyEventOrder(List<EventObject> keyEvents) { - int eet = KeyEvent.EVENT_KEY_PRESSED; + IntIntHashMap keyCode2NextEvent = new IntIntHashMap(); for(int i=0; i<keyEvents.size(); i++) { final KeyEvent e = (KeyEvent) keyEvents.get(i); + int eet = keyCode2NextEvent.get(e.getKeyCode()); + if( 0 >= eet ) { + eet = KeyEvent.EVENT_KEY_PRESSED; + } final int et = e.getEventType(); Assert.assertEquals("Key event not in proper order", eet, et); eet = getNextKeyEventType(et); + keyCode2NextEvent.put(e.getKeyCode(), eet); } } |