diff options
author | Sven Gothel <[email protected]> | 2011-11-18 09:14:08 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2011-11-18 09:14:08 +0100 |
commit | 3db4e89cb2c36f63c6d0a8f3450705d1ef3694b0 (patch) | |
tree | 193deaa032784afb368c3e097d3ec2031760dd38 /src/test/com/jogamp/opengl | |
parent | 3b38957f36d4f89b85730755a41c00892ac70591 (diff) |
NEWT/AWT Focus Traversal / Deadlock Fix (Windows) ; Harmonized NEWT KeyListener handling (Bug 526)
NativeWindow:
- expose 'hasFocus()'
Window:
- 'protected enqueueRequestFocus(..)' -> 'public requestFocus(boolean wait)'
- New: 'setKeyboardFocusHandler(KeyListener)' allowing focus traversal co-op w/ covered TK (AWT)
WindowImpl:
- Impl Window changes (see above)
- Impl 'consumedTag' see commit 3b38957f36d4f89b85730755a41c00892ac70591
NewtCanvasAWT:
- FocusAction only removes the global AWT focus owner.
This fixes a deadlock on the Windows platform of AWT's native peer requestFocus impl,
since it's no more called at this point.
- NEW FocusTraversalKeyListener is set as the newtChild's KeyboardFocusHandler,
allowing traversal to the next/previous AWT component.
AWTParentWindowAdapter:
- focusGained(..) clears AWT focus and propagates focus to Newt child,
non blocking w/ 'requestFocus(false)' (see above)
KeyEvent:
- Document limitations of getKeyChar() (Bug 526)
MacWindow:
- only deliver keyChar on key Typed events, harmonizing platform behavior (Bug 526)
WindowsWindow:
- regenerate the keyCode for EVENT_KEY_TYPED (Bug 526)
X11Windows:
- complete keyCode mapping X11 -> Newt - X11KeySym2NewtVKey()
- only deliver keyChar on key Typed events, harmonizing platform behavior (Bug 526)
Tests:
- GearsES2: Make focus visible
- TestParentingFocusTraversal01AWT: unit test for keyboard focus traversal w/ NewtCanvasAWT
Diffstat (limited to 'src/test/com/jogamp/opengl')
6 files changed, 298 insertions, 18 deletions
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java index 3fa61bf1d..63bbab66e 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java @@ -34,6 +34,8 @@ import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.glsl.ShaderState; import java.nio.FloatBuffer; + +import javax.media.nativewindow.NativeWindow; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLAutoDrawable; @@ -244,8 +246,16 @@ public class GearsES2 implements GLEventListener { // Get the GL corresponding to the drawable we are animating GL2ES2 gl = drawable.getGL().getGL2ES2(); - gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - + boolean hasFocus = false; + if(drawable.getNativeSurface() instanceof NativeWindow) { + hasFocus = ((NativeWindow)drawable.getNativeSurface()).hasFocus(); + } + if(hasFocus) { + gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + } else { + gl.glClearColor(0.2f, 0.2f, 0.2f, 0.0f); + } + // Special handling for the case where the GLJPanel is translucent // and wants to be composited with other Java 2D content if (GLProfile.isAWTAvailable() && diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestCloseNewtAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestCloseNewtAWT.java index adc885191..664cab03b 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestCloseNewtAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestCloseNewtAWT.java @@ -39,10 +39,9 @@ import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.util.Point; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLProfile; + import com.jogamp.newt.Window; import com.jogamp.newt.awt.NewtCanvasAWT; -import com.jogamp.newt.event.WindowAdapter; -import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.util.AWTRobotUtil; import com.jogamp.opengl.test.junit.util.UITestCase; @@ -53,6 +52,7 @@ public class TestCloseNewtAWT extends UITestCase { NewtCanvasAWT newtCanvas = null; JFrame frame = null; + @SuppressWarnings("serial") class MyCanvas extends NewtCanvasAWT { public MyCanvas(Window window) { super(window); @@ -78,7 +78,7 @@ public class TestCloseNewtAWT extends UITestCase { NativeWindow nw = MyCanvas.this.getNativeWindow(); if(null != nw) { Point p = nw.getLocationOnScreen(null); - System.err.println("MyCanvas On NEWT-EDT: position: "+p); + System.err.println("MyCanvas On NEWT-EDT: position: "+p); } else { System.err.println("MyCanvas On NEWT-EDT: position n/a, null NativeWindow"); } diff --git a/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java b/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java index 46748cb52..473f2f584 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java @@ -51,7 +51,11 @@ class NewtAWTReparentingKeyAdapter extends KeyAdapter { if(e.getKeyChar()=='d') { glWindow.setUndecorated(!glWindow.isUndecorated()); } else if(e.getKeyChar()=='f') { - glWindow.setFullscreen(!glWindow.isFullscreen()); + glWindow.setFullscreen(!glWindow.isFullscreen()); + } else if(e.getKeyChar()=='l') { + javax.media.nativewindow.util.Point p0 = newtCanvasAWT.getNativeWindow().getLocationOnScreen(null); + javax.media.nativewindow.util.Point p1 = glWindow.getLocationOnScreen(null); + System.err.println("NewtCanvasAWT position: "+p0+", "+p1); } else if(e.getKeyChar()=='p') { new Thread() { public void run() { diff --git a/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting03AWT.java b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting03AWT.java index 387d8a9a2..4a75d1343 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting03AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting03AWT.java @@ -66,11 +66,16 @@ public class TestParenting03AWT extends UITestCase { } @Test - public void testWindowParenting1AWTTwoNewtChilds01() throws InterruptedException, InvocationTargetException { - testWindowParenting1AWTTwoNewtChilds(); + public void testWindowParenting1AWTOneNewtChilds01() throws InterruptedException, InvocationTargetException { + testWindowParenting1AWT(false); } - public void testWindowParenting1AWTTwoNewtChilds() throws InterruptedException, InvocationTargetException { + @Test + public void testWindowParenting1AWTTwoNewtChilds01() throws InterruptedException, InvocationTargetException { + testWindowParenting1AWT(true); + } + + public void testWindowParenting1AWT(boolean use2nd) throws InterruptedException, InvocationTargetException { final Frame frame1 = new Frame("AWT Parent Frame"); GLWindow glWindow1 = GLWindow.create(glCaps); glWindow1.setUpdateFPSFrames(1, null); @@ -84,7 +89,6 @@ public class TestParenting03AWT extends UITestCase { GLAnimatorControl animator1 = new Animator(glWindow1); animator1.start(); - final boolean use2nd = true; GLWindow glWindow2 = null; NewtCanvasAWT newtCanvasAWT2 = null; GLAnimatorControl animator2 = null; diff --git a/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParentingFocusTraversal01AWT.java b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParentingFocusTraversal01AWT.java new file mode 100644 index 000000000..cb94d8f02 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParentingFocusTraversal01AWT.java @@ -0,0 +1,262 @@ +/** + * Copyright 2010 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.parenting; + +import java.lang.reflect.*; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.AWTException; +import java.awt.AWTKeyStroke; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Robot; + +import javax.media.opengl.*; + +import com.jogamp.opengl.util.Animator; +import com.jogamp.newt.*; +import com.jogamp.newt.opengl.*; +import com.jogamp.newt.awt.NewtCanvasAWT; + +import java.io.IOException; + +import com.jogamp.opengl.test.junit.util.*; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; + +public class TestParentingFocusTraversal01AWT extends UITestCase { + static Dimension glSize, fSize; + static int numFocus = 5; + static long durationPerTest = numFocus * 100; + static GLCapabilities glCaps; + static boolean manual = false; + + @BeforeClass + public static void initClass() { + glSize = new Dimension(200,200); + fSize = new Dimension(300,300); + glCaps = new GLCapabilities(null); + } + + @Test + public void testWindowParentingAWTFocusTraversal01() throws InterruptedException, InvocationTargetException, AWTException { + testWindowParentingAWTFocusTraversal(); + } + + public void testWindowParentingAWTFocusTraversal() throws InterruptedException, InvocationTargetException, AWTException { + Robot robot = new Robot(); + + // Bug 4908075 - http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4908075 + // Bug 6463168 - http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6463168 + { + final KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + final Set<AWTKeyStroke> bwdKeys = kfm.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + final AWTKeyStroke newBack = AWTKeyStroke.getAWTKeyStroke(java.awt.event.KeyEvent.VK_BACK_SPACE, 0, false); + Assert.assertNotNull(newBack); + final Set<AWTKeyStroke> bwdKeys2 = new HashSet<AWTKeyStroke>(bwdKeys); + bwdKeys2.add(newBack); + kfm.setDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, bwdKeys2); + } + { + final KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + final Set<AWTKeyStroke> fwdKeys = kfm.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + final Set<AWTKeyStroke> bwdKeys = kfm.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + Iterator<AWTKeyStroke> iter; + for(iter = fwdKeys.iterator(); iter.hasNext(); ) { + System.err.println("FTKL.fwd-keys: "+iter.next()); + } + for(iter = bwdKeys.iterator(); iter.hasNext(); ) { + System.err.println("FTKL.bwd-keys: "+iter.next()); + } + } + + final Frame frame1 = new Frame("AWT Parent Frame"); + final Button bWest = new Button("WEST"); + final Button bEast = new Button("EAST"); + GLWindow glWindow1 = GLWindow.create(glCaps); + glWindow1.setUpdateFPSFrames(1, null); + final NewtCanvasAWT newtCanvasAWT1 = new NewtCanvasAWT(glWindow1); + newtCanvasAWT1.setPreferredSize(glSize); + + // Test FocusAdapter + NEWTFocusAdapter glWindow1FA = new NEWTFocusAdapter("GLWindow1"); + glWindow1.addWindowListener(glWindow1FA); + AWTFocusAdapter bWestFA = new AWTFocusAdapter("WEST"); + bWest.addFocusListener(bWestFA); + AWTFocusAdapter bEastFA = new AWTFocusAdapter("EAST"); + bEast.addFocusListener(bEastFA); + + // Test KeyAdapter + NEWTKeyAdapter glWindow1KA = new NEWTKeyAdapter("GLWindow1"); + glWindow1.addKeyListener(glWindow1KA); + AWTKeyAdapter bWestKA = new AWTKeyAdapter("bWest"); + bWest.addKeyListener(bWestKA); + AWTKeyAdapter bEastKA = new AWTKeyAdapter("bEast"); + bEast.addKeyListener(bEastKA); + + // demo .. + GLEventListener demo1 = new GearsES2(1); + setDemoFields(demo1, glWindow1, false); + glWindow1.addGLEventListener(demo1); + glWindow1.addKeyListener(new NewtAWTReparentingKeyAdapter(frame1, newtCanvasAWT1, glWindow1)); + GLAnimatorControl animator1 = new Animator(glWindow1); + animator1.start(); + + // make frame + frame1.setLayout(new BorderLayout()); + frame1.setLayout(new BorderLayout()); + frame1.add(bWest, BorderLayout.WEST); + frame1.add(newtCanvasAWT1, BorderLayout.CENTER); + frame1.add(bEast, BorderLayout.EAST); + + frame1.setLocation(0, 0); + frame1.setSize(fSize); + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame1.validate(); + frame1.setVisible(true); + }}); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(glWindow1, true)); + Assert.assertEquals(newtCanvasAWT1.getNativeWindow(),glWindow1.getParent()); + + Assert.assertEquals(true, animator1.isAnimating()); + Assert.assertEquals(false, animator1.isPaused()); + Assert.assertNotNull(animator1.getThread()); + + if(manual) { + Thread.sleep(durationPerTest); + } else { + // + // initial focus on bWest + // + AWTRobotUtil.assertRequestFocusAndWait(robot, bWest, bWest, bWestFA, null); + Assert.assertEquals(true, bWestFA.focusGained()); + Thread.sleep(durationPerTest/numFocus); + + // + // forth + // + + // bWest -> glWin + AWTRobotUtil.keyType(0, robot, java.awt.event.KeyEvent.VK_TAB, bWest, null); + Assert.assertTrue("Did not gain focus", AWTRobotUtil.waitForFocus(glWindow1, glWindow1FA, bWestFA)); + Assert.assertEquals(true, glWindow1FA.focusGained()); + Assert.assertEquals(true, bWestFA.focusLost()); + Thread.sleep(durationPerTest/numFocus); + + // glWin -> bEast + AWTRobotUtil.keyType(0, robot, java.awt.event.KeyEvent.VK_TAB, glWindow1, null); + Assert.assertTrue("Did not gain focus", AWTRobotUtil.waitForFocus(bEast, bEastFA, glWindow1FA)); + Assert.assertEquals(true, bEastFA.focusGained()); + Assert.assertEquals(true, glWindow1FA.focusLost()); + Thread.sleep(durationPerTest/numFocus); + + // + // back (using custom back traversal key 'backspace') + // + // bEast -> glWin + AWTRobotUtil.keyType(0, robot, java.awt.event.KeyEvent.VK_BACK_SPACE, bEast, null); + Assert.assertTrue("Did not gain focus", AWTRobotUtil.waitForFocus(glWindow1, glWindow1FA, bEastFA)); + Assert.assertEquals(true, glWindow1FA.focusGained()); + Assert.assertEquals(true, bEastFA.focusLost()); + Thread.sleep(durationPerTest/numFocus); + + AWTRobotUtil.keyType(0, robot, java.awt.event.KeyEvent.VK_BACK_SPACE, glWindow1, null); + Assert.assertTrue("Did not gain focus", AWTRobotUtil.waitForFocus(bWest, bWestFA, glWindow1FA)); + Assert.assertEquals(true, bWestFA.focusGained()); + Assert.assertEquals(true, glWindow1FA.focusLost()); + Thread.sleep(durationPerTest/numFocus); + } + + animator1.stop(); + Assert.assertEquals(false, animator1.isAnimating()); + Assert.assertEquals(false, animator1.isPaused()); + Assert.assertEquals(null, animator1.getThread()); + + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame1.dispose(); + } } ); + glWindow1.destroy(); + } + + public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { + Assert.assertNotNull(demo); + Assert.assertNotNull(glWindow); + Window window = glWindow.getWindow(); + if(debug) { + MiscUtils.setFieldIfExists(demo, "glDebug", true); + MiscUtils.setFieldIfExists(demo, "glTrace", true); + } + if(!MiscUtils.setFieldIfExists(demo, "window", window)) { + MiscUtils.setFieldIfExists(demo, "glWindow", glWindow); + } + } + + 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]); + } else if(args[i].equals("-manual")) { + manual = true; + } + } + String tstname = TestParentingFocusTraversal01AWT.class.getName(); + /* + org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(new String[] { + tstname, + "filtertrace=true", + "haltOnError=false", + "haltOnFailure=false", + "showoutput=true", + "outputtoformatters=true", + "logfailedtests=true", + "logtestlistenerevents=true", + "formatter=org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter", + "formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,TEST-"+tstname+".xml" } ); */ + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java b/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java index 7df8645de..131e3a714 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java +++ b/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java @@ -67,7 +67,7 @@ public class AWTRobotUtil { int x0, y0; if(null!=comp) { - java.awt.Point p0 = comp.getLocationOnScreen(); + java.awt.Point p0 = comp.getLocationOnScreen(); java.awt.Rectangle r0 = comp.getBounds(); if( onTitleBarIfWindow && comp instanceof java.awt.Window) { java.awt.Window window = (java.awt.Window) comp; @@ -78,7 +78,7 @@ public class AWTRobotUtil { } x0 = (int) ( p0.getX() + r0.getWidth() / 2.0 + .5 ) ; } else { - javax.media.nativewindow.util.Point p0 = win.getLocationOnScreen(null); + javax.media.nativewindow.util.Point p0 = win.getLocationOnScreen(null); if( onTitleBarIfWindow ) { javax.media.nativewindow.util.InsetsImmutable insets = win.getInsets(); p0.translate(win.getWidth()/2, insets.getTopHeight()/2); @@ -280,8 +280,8 @@ public class AWTRobotUtil { Assert.assertTrue("Did not gain focus", hasFocus); } - static int keyType(int i, Robot robot, int keyCode, - Object obj, InputEventCountAdapter counter) throws InterruptedException, AWTException, InvocationTargetException + public static int keyType(int i, Robot robot, int keyCode, + Object obj, InputEventCountAdapter counter) throws InterruptedException, AWTException, InvocationTargetException { int tc = 0; int j; @@ -293,13 +293,13 @@ public class AWTRobotUtil { if(DEBUG) { System.err.println(i+":"+j+" KC1.0: "+counter+" - regain focus"); } requestFocus(null, obj); } - final int c0 = counter.getCount(); + final int c0 = null!=counter ? counter.getCount() : 0; if(DEBUG) { System.err.println(i+":"+j+" KC1.1: "+counter); } robot.waitForIdle(); robot.keyPress(keyCode); robot.keyRelease(keyCode); if(DEBUG) { System.err.println(i+":"+j+" KC1.2: "+counter); } - tc = counter.getCount() - c0; + tc = ( null!=counter ? counter.getCount() : 1 ) - c0; for (int wait=0; wait<POLL_DIVIDER && 1 > tc; wait++) { robot.delay(TIME_SLICE); tc = counter.getCount() - c0; @@ -355,13 +355,13 @@ public class AWTRobotUtil { if(DEBUG) { System.err.println(i+":"+j+" MC1.0: "+counter+" - regain focus"); } requestFocus(null, obj); } - final int c0 = counter.getCount(); + final int c0 = null != counter ? counter.getCount() : 0; if(DEBUG) { System.err.println(i+":"+j+" MC1.1: "+counter); } robot.waitForIdle(); robot.mousePress(mouseButton); robot.mouseRelease(mouseButton); if(DEBUG) { System.err.println(i+":"+j+" MC1.2: "+counter); } - tc = counter.getCount() - c0; + tc = ( null != counter ? counter.getCount() : 1 ) - c0; for (int wait=0; wait<POLL_DIVIDER && 1 > tc; wait++) { robot.delay(TIME_SLICE); tc = counter.getCount() - c0; |