From 808a9a27a8c1c9e0a6701a8dd81d51f8daa8129d Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 28 Feb 2013 00:39:28 +0100 Subject: Fix NEWT/AWT WindowClosing Unit Tests ; Review/Cleanup NEWT WindowClosing mechanism Due to a NEWT WindowClosing event regression cause by NewtCanvasAWT changes a review of our WindowClosing event mechanism was required. Important cleanups are marked w/ '(*)' below. I would have preferred to change the 'WindowListener.windowDestroyNotify(WindowEvent)' method to pass a WindowCloseEvent object exposing more information like toolkit or programmatic destruction and passing whether a 'closing' or 'nop' action will be performed based on the WindowClosingMode. For now I postponed this idea .. since it would change the API again, but may reconsider it after merging the Android 'closing' patch. - InputEvent.consumedTag -> NEWTEvent.consumedTag - Window - (*) Promote setWindowDestroyNotifyAction(Runnable) to public, former WindowImpl.setHandleDestroyNotify(boolean). Using a Runnable action for WindowImpl.windowDestroyNotify(boolean) allows a setting defined alternative for destroy() and gets rid of [ab]using WindowListener.windowDestroyNotify(WindowEvent) for lifecycle actions. Used in: - GLWindow - GLAutoDrawableDelegate impl. - WindowImpl - Respect NEWTEvent.consumedTag for WindowEvents as well - (*) Impl. setHandleDestroyNotify(boolean) (see above) - (*) destroy() simply sends out pre- and post- destruction Window events, where windowDestroyNotify(boolean) sends out the pre-destruction event if NOP. - (*) windowDestroyNotify(boolean) is public now, allowing other impl. details to follow proper destruction using handleDestroyNotify Runnable (-> NewtCanvasAWT). - AWTWindowClosingProtocol: - addClosingListenerOneShot() -> addClosingListener() - calling addClosingListener() at addNotify() - calling removeClosingListener() at removeNotify() - AWTWindowClosingProtocol ctor taking NOP runnable, allowing to send WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY at WindowClosingMode.DO_NOTHING_ON_CLOSE - add/remove listener on AWT-EDT - AWTWindowAdapter - Add 'removeWindowClosingFrom(..)', allowing to remove window closing event fwd. - Also fwd windowClosed in window closing fwd'ing. - NewtCanvasAWT - (*) Utilize AWTWindowClosingProtocol NOP runnable (see above) to fwd closing-NOP event to NEWT - (*) Unify remove/destroy code in destroyImpl(..) - !removeNotify -> destroy NEWT child programatic or as toolkit event - removeNotify || windowClosing -> destroy jawtWindow - (*) Remove AWTWindowAdapter/AWTParentWindowAdapter's windowClosingListener, since we utilize AWTWindowClosingProtocol - DisplayImpl - Adding 'final void dispatchMessage(final NEWTEvent event)' allowing to remove the NEWTEventTask wrapping for no reason in enqueueEvent(..) if on EDT and waiting. --- .../jogl/acore/TestGLAutoDrawableDelegateNEWT.java | 160 +++++++++++++++++++++ ...estGLAutoDrawableDelegateOnOffscrnCapsNEWT.java | 11 +- .../acore/TestGLContextDrawableSwitch01NEWT.java | 9 +- .../acore/TestGLContextDrawableSwitch11NEWT.java | 9 +- .../jogl/acore/TestSharedContextNewtAWTBug523.java | 5 +- .../opengl/test/junit/newt/TestCloseNewtAWT.java | 7 +- .../junit/newt/TestWindowClosingProtocol01AWT.java | 24 ++-- .../newt/TestWindowClosingProtocol02NEWT.java | 12 +- .../newt/TestWindowClosingProtocol03NewtAWT.java | 41 ++++-- .../opengl/test/junit/util/AWTRobotUtil.java | 83 +++++++---- 10 files changed, 291 insertions(+), 70 deletions(-) create mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateNEWT.java (limited to 'src/test/com/jogamp/opengl') diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateNEWT.java new file mode 100644 index 000000000..2729d6a5d --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateNEWT.java @@ -0,0 +1,160 @@ +/** + * Copyright 2013 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.jogl.acore; + +import java.io.IOException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; + +import org.junit.Assert; +import org.junit.Test; + +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowUpdateEvent; +import com.jogamp.opengl.GLAutoDrawableDelegate; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.QuitAdapter; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.Animator; + +/** + * Test using a NEWT {@link Window} for onscreen case. + *

+ * Creates a {@link GLDrawable} using the + * {@link GLDrawableFactory#createGLDrawable(javax.media.nativewindow.NativeSurface) factory model}. + * The {@link GLContext} is derived {@link GLDrawable#createContext(GLContext) from the drawable}. + *

+ *

+ * Finally a {@link GLAutoDrawableDelegate} is created with the just created {@link GLDrawable} and {@link GLContext}. + * It is being used to run the {@link GLEventListener}. + *

+ */ +public class TestGLAutoDrawableDelegateNEWT extends UITestCase { + static long duration = 500; // ms + + void doTest(GLCapabilitiesImmutable reqGLCaps, GLEventListener demo) throws InterruptedException { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(reqGLCaps.getGLProfile()); + + // + // Create native windowing resources .. X11/Win/OSX + // + final Window window = NewtFactory.createWindow(reqGLCaps); + Assert.assertNotNull(window); + window.setSize(640, 400); + window.setVisible(true); + Assert.assertTrue(AWTRobotUtil.waitForVisible(window, true)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(window, true)); + System.out.println("Window: "+window.getClass().getName()); + + final GLDrawable drawable = factory.createGLDrawable(window); + Assert.assertNotNull(drawable); + drawable.setRealized(true); + + final GLAutoDrawableDelegate glad = new GLAutoDrawableDelegate(drawable, drawable.createContext(null), window, false, null) { + @Override + protected void destroyImplInLock() { + super.destroyImplInLock(); // destroys drawable/context + window.destroy(); // destroys the actual window, incl. the device + } + }; + + window.setWindowDestroyNotifyAction( new Runnable() { + public void run() { + glad.windowDestroyNotifyOp(); + } } ); + + window.addWindowListener(new WindowAdapter() { + @Override + public void windowRepaint(WindowUpdateEvent e) { + glad.windowRepaintOp(); + } + + @Override + public void windowResized(WindowEvent e) { + glad.windowResizedOp(window.getWidth(), window.getHeight()); + } + }); + + glad.addGLEventListener(demo); + + QuitAdapter quitAdapter = new QuitAdapter(); + //glWindow.addKeyListener(new TraceKeyAdapter(quitAdapter)); + //glWindow.addWindowListener(new TraceWindowAdapter(quitAdapter)); + window.addKeyListener(quitAdapter); + window.addWindowListener(quitAdapter); + + Animator animator = new Animator(); + animator.setUpdateFPSFrames(60, System.err); + animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD); + animator.add(glad); + animator.start(); + Assert.assertTrue(animator.isStarted()); + Assert.assertTrue(animator.isAnimating()); + + while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration() dispose @@ -90,10 +93,12 @@ public class TestWindowClosingProtocol01AWT extends UITestCase { Thread.sleep(300); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false)); // no frame close, but GLCanvas's GL resources will be destroyed + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false, closingListener)); // no frame close, but GLCanvas's GL resources will be destroyed Thread.sleep(100); Assert.assertEquals(true, frame.isDisplayable()); Assert.assertEquals(true, frame.isVisible()); + Assert.assertEquals(true, closingListener.isWindowClosing()); + Assert.assertEquals(false, closingListener.isWindowClosed()); for (int wait=0; wait dispose @@ -84,9 +84,9 @@ public class TestWindowClosingProtocol02NEWT extends UITestCase { op = glWindow.getDefaultCloseOperation(); Assert.assertEquals(WindowClosingMode.DISPOSE_ON_CLOSE, op); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(glWindow, true)); + Assert.assertEquals(true, AWTRobotUtil.closeWindow(glWindow, true, closingListener)); Assert.assertEquals(false, glWindow.isNativeValid()); - Assert.assertEquals(true, windowClosingListener.isWindowClosing()); + Assert.assertEquals(true, closingListener.isWindowClosing()); } public static void main(String[] args) { diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol03NewtAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol03NewtAWT.java index be3c48fb6..b0a222a5a 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol03NewtAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol03NewtAWT.java @@ -46,17 +46,19 @@ import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.util.AWTRobotUtil; import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil.WindowClosingListener; public class TestWindowClosingProtocol03NewtAWT extends UITestCase { @Test public void testCloseJFrameNewtCanvasAWT() throws InterruptedException, InvocationTargetException { final JFrame frame = new JFrame("testCloseJFrameNewtCanvasAWT"); - + final WindowClosingListener awtClosingListener = AWTRobotUtil.addClosingListener(frame); + GLProfile glp = GLProfile.getGL2ES2(); GLCapabilities caps = new GLCapabilities(glp); final GLWindow glWindow = GLWindow.create(caps); - final AWTRobotUtil.WindowClosingListener windowClosingListener = AWTRobotUtil.addClosingListener(glWindow); + final AWTRobotUtil.WindowClosingListener newtClosingListener = AWTRobotUtil.addClosingListener(glWindow); glWindow.addGLEventListener(new GearsES2()); @@ -81,20 +83,26 @@ public class TestWindowClosingProtocol03NewtAWT extends UITestCase { // // close with op: DO_NOTHING_ON_CLOSE -> NOP / HIDE (default) // - Assert.assertEquals(JFrame.HIDE_ON_CLOSE, frame.getDefaultCloseOperation()); - WindowClosingMode op = newtCanvas.getDefaultCloseOperation(); - Assert.assertEquals(WindowClosingMode.DO_NOTHING_ON_CLOSE, op); + { + Assert.assertEquals(JFrame.HIDE_ON_CLOSE, frame.getDefaultCloseOperation()); + WindowClosingMode op = newtCanvas.getDefaultCloseOperation(); + Assert.assertEquals(WindowClosingMode.DO_NOTHING_ON_CLOSE, op); + } Thread.sleep(300); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false)); + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false, awtClosingListener)); Assert.assertEquals(true, frame.isDisplayable()); Assert.assertEquals(false, frame.isVisible()); Assert.assertEquals(true, newtCanvas.isValid()); Assert.assertEquals(true, newtCanvas.isDisplayable()); Assert.assertEquals(true, glWindow.isNativeValid()); - Assert.assertEquals(true, windowClosingListener.isWindowClosing()); - windowClosingListener.reset(); + Assert.assertEquals(true, awtClosingListener.isWindowClosing()); + Assert.assertEquals(false, awtClosingListener.isWindowClosed()); + Assert.assertEquals(true, newtClosingListener.isWindowClosing()); + Assert.assertEquals(false, newtClosingListener.isWindowClosed()); + awtClosingListener.reset(); + newtClosingListener.reset(); SwingUtilities.invokeAndWait(new Runnable() { public void run() { @@ -107,20 +115,25 @@ public class TestWindowClosingProtocol03NewtAWT extends UITestCase { // // close with op (JFrame): DISPOSE_ON_CLOSE -- newtCanvas -- glWindow --> dispose // - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - Assert.assertEquals(JFrame.DISPOSE_ON_CLOSE, frame.getDefaultCloseOperation()); - op = newtCanvas.getDefaultCloseOperation(); - Assert.assertEquals(WindowClosingMode.DISPOSE_ON_CLOSE, op); + { + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + Assert.assertEquals(JFrame.DISPOSE_ON_CLOSE, frame.getDefaultCloseOperation()); + WindowClosingMode op = newtCanvas.getDefaultCloseOperation(); + Assert.assertEquals(WindowClosingMode.DISPOSE_ON_CLOSE, op); + } Thread.sleep(300); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, true, awtClosingListener)); Assert.assertEquals(false, frame.isDisplayable()); Assert.assertEquals(false, frame.isVisible()); Assert.assertEquals(false, newtCanvas.isValid()); Assert.assertEquals(false, newtCanvas.isDisplayable()); Assert.assertEquals(false, glWindow.isNativeValid()); - Assert.assertEquals(true, windowClosingListener.isWindowClosing()); + Assert.assertEquals(true, awtClosingListener.isWindowClosing()); + Assert.assertEquals(true, awtClosingListener.isWindowClosed()); + Assert.assertEquals(true, newtClosingListener.isWindowClosing()); + Assert.assertEquals(true, newtClosingListener.isWindowClosed()); } public static void main(String[] args) { 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 ffc42e318..8b46760e1 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java +++ b/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java @@ -28,6 +28,7 @@ package com.jogamp.opengl.test.junit.util; +import jogamp.common.awt.AWTEDTExecutor; import jogamp.newt.WindowImplAccess; import java.lang.reflect.InvocationTargetException; @@ -41,6 +42,8 @@ import javax.media.opengl.awt.GLCanvas; import org.junit.Assert; +import com.jogamp.newt.event.WindowEvent; + public class AWTRobotUtil { static final boolean DEBUG = false; @@ -646,16 +649,19 @@ public class AWTRobotUtil { * * @param obj either an AWT Window (Frame, JFrame) or NEWT Window * @param willClose indicating that the window will close, hence this method waits for the window to be closed + * @param wcl the WindowClosingListener to determine whether the AWT or NEWT widget has been closed. It should be attached + * to the widget ASAP before any other listener, e.g. via {@link #addClosingListener(Object)}. + * The WindowClosingListener will be reset before attempting to close the widget. * @return True if the Window is closing and closed (if willClose is true), each within TIME_OUT * @throws InterruptedException */ - public static boolean closeWindow(Object obj, boolean willClose) throws InterruptedException, InvocationTargetException { - WindowClosingListener closingListener = addClosingListener(obj); + public static boolean closeWindow(Object obj, boolean willClose, WindowClosingListener closingListener) throws InterruptedException { + closingListener.reset(); if(obj instanceof java.awt.Window) { final java.awt.Window win = (java.awt.Window) obj; java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit(); final java.awt.EventQueue evtQ = tk.getSystemEventQueue(); - java.awt.EventQueue.invokeAndWait(new Runnable() { + AWTEDTExecutor.singleton.invoke(true, new Runnable() { public void run() { evtQ.postEvent(new java.awt.event.WindowEvent(win, java.awt.event.WindowEvent.WINDOW_CLOSING)); } }); @@ -675,12 +681,15 @@ public class AWTRobotUtil { return wait