diff options
author | Sven Gothel <[email protected]> | 2013-02-28 00:39:28 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-02-28 00:39:28 +0100 |
commit | 808a9a27a8c1c9e0a6701a8dd81d51f8daa8129d (patch) | |
tree | 35bf5791a829b59f1abe436abb176df7fffb045f /src | |
parent | af384debfdf354d98e3d0d0c6e0c5cf5a967904e (diff) |
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.
Diffstat (limited to 'src')
29 files changed, 536 insertions, 221 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java index 206331ac0..0f0f03ac4 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java @@ -51,12 +51,12 @@ import jogamp.opengl.GLDrawableImpl; * utilizing already created {@link GLDrawable} and {@link GLContext} instances. * <p> * Since no native windowing system events are being processed, it is recommended - * to handle at least: + * to handle at least the {@link com.jogamp.newt.event.WindowEvent window events}: * <ul> * <li>{@link com.jogamp.newt.event.WindowListener#windowRepaint(com.jogamp.newt.event.WindowUpdateEvent) repaint} using {@link #windowRepaintOp()}</li> * <li>{@link com.jogamp.newt.event.WindowListener#windowResized(com.jogamp.newt.event.WindowEvent) resize} using {@link #windowResizedOp()}</li> - * <li>{@link com.jogamp.newt.event.WindowListener#windowDestroyNotify(com.jogamp.newt.event.WindowEvent) destroy-notify} using {@link #windowDestroyNotifyOp()}</li> - * </ul> + * </ul> + * and setup a {@link com.jogamp.newt.Window#setWindowDestroyNotifyAction(Runnable) custom toolkit destruction} issuing {@link #windowDestroyNotifyOp()}. * </p> * <p> * See example {@link com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateNEWT TestGLAutoDrawableDelegateNEWT}. diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index dc4fe955c..94e123b3e 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -176,7 +176,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing public void run() { GLCanvas.this.destroyImpl( true ); } - }); + }, null); /** Creates a new GLCanvas component with a default set of OpenGL capabilities, using the default OpenGL capabilities selection @@ -460,7 +460,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing if( isVisible() ) { Threading.invoke(true, displayOnEDTAction, getTreeLock()); } - awtWindowClosingProtocol.addClosingListenerOneShot(); } /** @@ -568,7 +567,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // for all launch flavors (applet/javaws/..) // validateGLDrawable(); } - + awtWindowClosingProtocol.addClosingListener(); + if(DEBUG) { System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer()); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 2b99bb570..6c28c75ab 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -174,7 +174,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing public void run() { GLJPanel.this.destroy(); } - }); + }, null); static { // Force eager initialization of part of the Java2D class since @@ -369,6 +369,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public void addNotify() { super.addNotify(); + awtWindowClosingProtocol.addClosingListener(); if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.addNotify()"); } @@ -677,8 +678,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if (!isInitialized) { backend.initialize(); } - - awtWindowClosingProtocol.addClosingListenerOneShot(); } @Override diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTWindowClosingProtocol.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTWindowClosingProtocol.java index d78b4ac15..e3f85b948 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTWindowClosingProtocol.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTWindowClosingProtocol.java @@ -33,22 +33,31 @@ import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; + import javax.media.nativewindow.WindowClosingProtocol; +import jogamp.common.awt.AWTEDTExecutor; import jogamp.nativewindow.awt.AWTMisc; public class AWTWindowClosingProtocol implements WindowClosingProtocol { private Component comp; - private Runnable closingOperation; - private volatile boolean closingListenerSet = false; + private Runnable closingOperationClose; + private Runnable closingOperationNOP; + private boolean closingListenerSet = false; private Object closingListenerLock = new Object(); private WindowClosingMode defaultCloseOperation = WindowClosingMode.DISPOSE_ON_CLOSE; private boolean defaultCloseOperationSetByUser = false; - public AWTWindowClosingProtocol(Component comp, Runnable closingOperation) { + /** + * @param comp mandatory AWT component which AWT Window is being queried by parent traversal + * @param closingOperationClose mandatory closing operation, triggered if windowClosing and {@link WindowClosingMode#DISPOSE_ON_CLOSE} + * @param closingOperationNOP optional closing operation, triggered if windowClosing and {@link WindowClosingMode#DO_NOTHING_ON_CLOSE} + */ + public AWTWindowClosingProtocol(Component comp, Runnable closingOperationClose, Runnable closingOperationNOP) { this.comp = comp; - this.closingOperation = closingOperation; + this.closingOperationClose = closingOperationClose; + this.closingOperationNOP = closingOperationNOP; } class WindowClosingAdapter extends WindowAdapter { @@ -59,54 +68,56 @@ public class AWTWindowClosingProtocol implements WindowClosingProtocol { if( WindowClosingMode.DISPOSE_ON_CLOSE == op ) { // we have to issue this call right away, // otherwise the window gets destroyed - closingOperation.run(); + closingOperationClose.run(); + } else if( null != closingOperationNOP ){ + closingOperationNOP.run(); } } } WindowListener windowClosingAdapter = new WindowClosingAdapter(); - final boolean addClosingListenerImpl() { - Window w = AWTMisc.getWindow(comp); - if(null!=w) { - w.addWindowListener(windowClosingAdapter); - return true; - } - return false; - } - /** - * Adds this closing listener to the components Window if exist and only one time.<br> - * Hence you may call this method every time to ensure it has been set, - * ie in case the Window parent is not available yet. + * Adds this closing listener to the components Window if exist and only one time. + * <p> + * If the closing listener is already added, and {@link IllegalStateException} is thrown. + * </p> * - * @return + * @return true if added, otherwise false. + * @throws IllegalStateException */ - public final boolean addClosingListenerOneShot() { - if(!closingListenerSet) { // volatile: ok + public final boolean addClosingListener() throws IllegalStateException { synchronized(closingListenerLock) { - if(!closingListenerSet) { - closingListenerSet=addClosingListenerImpl(); - return closingListenerSet; - } + if(closingListenerSet) { + throw new IllegalStateException("WindowClosingListener already set"); + } + final Window w = AWTMisc.getWindow(comp); + if(null!=w) { + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + w.addWindowListener(windowClosingAdapter); + } } ); + closingListenerSet = true; + return true; + } } - } - return false; + return false; } public final boolean removeClosingListener() { - if(closingListenerSet) { // volatile: ok synchronized(closingListenerLock) { - if(closingListenerSet) { - Window w = AWTMisc.getWindow(comp); - if(null!=w) { - w.removeWindowListener(windowClosingAdapter); - closingListenerSet = false; - return true; - } - } + if(closingListenerSet) { + final Window w = AWTMisc.getWindow(comp); + if(null!=w) { + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + w.removeWindowListener(windowClosingAdapter); + } } ); + closingListenerSet = false; + return true; + } + } } - } - return false; + return false; } /** diff --git a/src/nativewindow/classes/javax/media/nativewindow/NativeWindow.java b/src/nativewindow/classes/javax/media/nativewindow/NativeWindow.java index 12e202975..a740ebbe0 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/NativeWindow.java +++ b/src/nativewindow/classes/javax/media/nativewindow/NativeWindow.java @@ -54,8 +54,7 @@ import javax.media.nativewindow.util.Point; public interface NativeWindow extends NativeSurface { /** - * destroys the window and releases - * windowing related resources. + * Destroys this window incl. releasing all related resources. */ public void destroy(); diff --git a/src/nativewindow/classes/javax/media/nativewindow/WindowClosingProtocol.java b/src/nativewindow/classes/javax/media/nativewindow/WindowClosingProtocol.java index 884c916e4..02f68f442 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/WindowClosingProtocol.java +++ b/src/nativewindow/classes/javax/media/nativewindow/WindowClosingProtocol.java @@ -37,6 +37,10 @@ package javax.media.nativewindow; * this protocol default behavior {@link WindowClosingMode#DISPOSE_ON_CLOSE DISPOSE_ON_CLOSE} shall be used.</p> */ public interface WindowClosingProtocol { + + /** + * Window closing mode if triggered by toolkit close operation. + */ public enum WindowClosingMode { /** * Do nothing on native window close operation.<br> diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index cc42465f1..ab1eef308 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -28,12 +28,15 @@ package com.jogamp.newt; +import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.MouseListener; import jogamp.newt.Debug; +import jogamp.newt.WindowImpl; + import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeWindow; @@ -104,14 +107,26 @@ public interface Window extends NativeWindow, WindowClosingProtocol { CapabilitiesImmutable getChosenCapabilities(); /** - * Destroy the Window and it's children, incl. native destruction.<br> - * The Window can be recreate via {@link #setVisible(boolean) setVisible(true)}. - * <p>Visibility is set to false.</p> + * {@inheritDoc} + * <p> + * Also iterates through this window's children and destroys them. + * </p> + * <p> + * Visibility is set to false. + * </p> + * <p> + * Method sends out {@link WindowEvent#EVENT_WINDOW_DESTROY_NOTIFY pre-} and + * {@link WindowEvent#EVENT_WINDOW_DESTROYED post-} destruction events + * to all of it's {@link WindowListener}. + * </p> * <p> * This method invokes {@link Screen#removeReference()} after it's own destruction,<br> * which will issue {@link Screen#destroy()} if the reference count becomes 0.<br> * This destruction sequence shall end up in {@link Display#destroy()}, if all reference counts become 0. * </p> + * <p> + * The Window can be recreate via {@link #setVisible(boolean) setVisible(true)}. + * </p> * @see #destroy() * @see #setVisible(boolean) */ @@ -119,6 +134,16 @@ public interface Window extends NativeWindow, WindowClosingProtocol { void destroy(); /** + * Set a custom action handling destruction issued by a {@link WindowImpl#windowDestroyNotify(boolean) toolkit triggered window destroy} + * replacing the default {@link #destroy()} action. + * <p> + * The custom action shall call {@link #destroy()} + * but may perform further tasks before and after. + * </p> + */ + void setWindowDestroyNotifyAction(Runnable r); + + /** * <code>setVisible</code> makes the window and children visible if <code>visible</code> is true, * otherwise the window and children becomes invisible. * <p> @@ -383,10 +408,13 @@ public interface Window extends NativeWindow, WindowClosingProtocol { // WindowListener // + /** + * Send a {@link WindowEvent} to all {@link WindowListener}. + * @param eventType a {@link WindowEvent} type, e.g. {@link WindowEvent#EVENT_WINDOW_REPAINT}. + */ public void sendWindowEvent(int eventType); /** - * * Appends the given {@link com.jogamp.newt.event.WindowListener} to the end of * the list. */ diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index 3503dabd5..3c10859bf 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -51,6 +51,7 @@ import javax.swing.MenuSelectionManager; import jogamp.nativewindow.awt.AWTMisc; import jogamp.newt.Debug; +import jogamp.newt.WindowImpl; import jogamp.newt.awt.NewtFactoryAWT; import jogamp.newt.awt.event.AWTParentWindowAdapter; import jogamp.newt.driver.DriverClearFocus; @@ -59,9 +60,9 @@ import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.nativewindow.awt.JAWTWindow; import com.jogamp.newt.Display; import com.jogamp.newt.Window; -import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.NEWTEvent; import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; @@ -88,16 +89,22 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto private boolean newtChildAttached = false; private boolean isOnscreen = true; private WindowClosingMode newtChildCloseOp; - private AWTAdapter awtAdapter = null; + private AWTParentWindowAdapter awtAdapter = null; private AWTAdapter awtMouseAdapter = null; private AWTAdapter awtKeyAdapter = null; private AWTWindowClosingProtocol awtWindowClosingProtocol = new AWTWindowClosingProtocol(this, new Runnable() { public void run() { - NewtCanvasAWT.this.destroy(); + NewtCanvasAWT.this.destroyImpl(false /* removeNotify */, true /* windowClosing */); } - }); + }, new Runnable() { + public void run() { + if( newtChild != null ) { + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + } + } + } ); /** * Instantiates a NewtCanvas without a NEWT child.<br> @@ -209,7 +216,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } public void keyTyped(KeyEvent e) { if(suppress) { - e.setAttachment(InputEvent.consumedTag); + e.setAttachment(NEWTEvent.consumedTag); suppress = false; // reset } } @@ -239,7 +246,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } if(suppress) { - evt.setAttachment(InputEvent.consumedTag); + evt.setAttachment(NEWTEvent.consumedTag); } if(DEBUG) { System.err.println("NewtCanvasAWT.focusKey: XXX: "+ks); @@ -361,27 +368,17 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto ", displayable "+isDisplayable()+", cont "+AWTMisc.getContainer(this)); } } + awtWindowClosingProtocol.addClosingListener(); } @Override public void removeNotify() { + awtWindowClosingProtocol.removeClosingListener(); + if( Beans.isDesignTime() ) { super.removeNotify(); } else { - java.awt.Container cont = AWTMisc.getContainer(this); - if(DEBUG) { - System.err.println("NewtCanvasAWT.removeNotify: "+newtChild+", from "+cont); - } - // Detach OLS early.. - final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(newtChild, true); - if(null != ols && ols.isSurfaceLayerAttached()) { - ols.detachSurfaceLayer(); - } - detachNewtChild(cont); // will destroy context (offscreen -> onscreen) and implicit detachSurfaceLayer (if still attached) - - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - jawtWindow=null; - + destroyImpl(true /* removeNotify */, false /* windowClosing */); super.removeNotify(); } } @@ -397,20 +394,32 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto * @see Window#destroy() */ public final void destroy() { + destroyImpl(false /* removeNotify */, false /* windowClosing */); + } + + private final void destroyImpl(boolean removeNotify, boolean windowClosing) { if( null !=newtChild ) { java.awt.Container cont = AWTMisc.getContainer(this); if(DEBUG) { - System.err.println("NewtCanvasAWT.destroy(): "+newtChild+", from "+cont); + System.err.println("NewtCanvasAWT.destroy(removeNotify "+removeNotify+", windowClosing "+windowClosing+"): nw "+newtWinHandleToHexString(newtChild)+", from "+cont); } - // Detach OLS early.. - final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(newtChild, true); - if(null != ols && ols.isSurfaceLayerAttached()) { - ols.detachSurfaceLayer(); - } - detachNewtChild(cont); // will destroy context (offscreen -> onscreen) and implicit detachSurfaceLayer (if still attached) - newtChild.destroy(); - newtChild=null; + detachNewtChild(cont); + + if( !removeNotify ) { + final Window cWin = newtChild; + final Window dWin = cWin.getDelegatedWindow(); + newtChild=null; + if( windowClosing && dWin instanceof WindowImpl ) { + ((WindowImpl)dWin).windowDestroyNotify(true); + } else { + cWin.destroy(); + } + } } + if( ( removeNotify || windowClosing ) && null!=jawtWindow) { + NewtFactoryAWT.destroyNativeWindow(jawtWindow); + jawtWindow=null; + } } @Override @@ -501,8 +510,6 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto return false; } - awtWindowClosingProtocol.addClosingListenerOneShot(); - if( attachNewtChild && !newtChildAttached && null != newtChild ) { attachNewtChild(cont); } @@ -535,11 +542,11 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto throw new InternalError("XXX"); } isOnscreen = jawtWindow.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); - awtAdapter = new AWTParentWindowAdapter(jawtWindow, newtChild).addTo(this); + awtAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter(jawtWindow, newtChild).addTo(this); + awtAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! newtChild.addWindowListener(clearAWTMenusOnNewtFocus); newtChild.setFocusAction(focusAction); // enable AWT focus traversal newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); - awtWindowClosingProtocol.addClosingListenerOneShot(); keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); keyboardFocusManager.addPropertyChangeListener("focusOwner", focusPropertyChangeListener); if(isOnscreen) { @@ -554,7 +561,6 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto newtChild.removeWindowListener(clearAWTMenusOnNewtFocus); newtChild.setFocusAction(null); newtChild.setDefaultCloseOperation(newtChildCloseOp); - awtWindowClosingProtocol.removeClosingListener(); } } } @@ -611,7 +617,14 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto newtChild.setFocusAction(null); // no AWT focus traversal .. configureNewtChild(false); newtChild.setVisible(false); - newtChild.reparentWindow(null); + + // Detach OLS early.. + final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(newtChild, true); + if(null != ols && ols.isSurfaceLayerAttached()) { + ols.detachSurfaceLayer(); + } + newtChild.reparentWindow(null); // will destroy context (offscreen -> onscreen) and implicit detachSurfaceLayer (if still attached) + if(DEBUG) { System.err.println("NewtCanvasAWT.detachNewtChild.X: win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+", comp "+this); } diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java index 07004503e..c3ad51c96 100755 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java +++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java @@ -300,9 +300,7 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { glWindow.reparentWindow(awtParent); } else { glWindow.reparentWindow(null); - if(glClosable) { - glWindow.setDefaultCloseOperation(WindowClosingMode.DISPOSE_ON_CLOSE); - } + glWindow.setDefaultCloseOperation( glClosable ? WindowClosingMode.DISPOSE_ON_CLOSE : WindowClosingMode.DO_NOTHING_ON_CLOSE ); } } } diff --git a/src/newt/classes/com/jogamp/newt/event/InputEvent.java b/src/newt/classes/com/jogamp/newt/event/InputEvent.java index 9ef4de2b6..4920b59ea 100644 --- a/src/newt/classes/com/jogamp/newt/event/InputEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/InputEvent.java @@ -81,11 +81,6 @@ public abstract class InputEvent extends NEWTEvent return 0; } - /** Object when attached via {@link #setAttachment(Object)} marks the event consumed, - * ie. stops propagating the event any further to the event listener. - */ - public static final Object consumedTag = new Object(); - protected InputEvent(short eventType, Object source, long when, int modifiers) { super(eventType, source, when); this.modifiers=modifiers; diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java index b8de6eb18..6f4561ce6 100644 --- a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java @@ -48,6 +48,12 @@ package com.jogamp.newt.event; */ @SuppressWarnings("serial") public class NEWTEvent extends java.util.EventObject { + /** + * Object when attached via {@link #setAttachment(Object)} marks the event consumed, + * ie. stops propagating the event any further to the <i>other</i> event listener. + */ + public static final Object consumedTag = new Object(); + private final short eventType; private final long when; private Object attachment; diff --git a/src/newt/classes/com/jogamp/newt/event/WindowListener.java b/src/newt/classes/com/jogamp/newt/event/WindowListener.java index 011e1f654..dde182510 100644 --- a/src/newt/classes/com/jogamp/newt/event/WindowListener.java +++ b/src/newt/classes/com/jogamp/newt/event/WindowListener.java @@ -36,6 +36,7 @@ package com.jogamp.newt.event; import javax.media.nativewindow.WindowClosingProtocol; +/** NEWT {@link WindowEvent} listener. */ public interface WindowListener extends NEWTEventListener { /** Window is resized, your application shall respect the new window dimension. A repaint is recommended. */ public void windowResized(WindowEvent e); @@ -53,7 +54,9 @@ public interface WindowListener extends NEWTEventListener { **/ public void windowDestroyNotify(WindowEvent e); - /** Window has been destroyed.*/ + /** + * Window has been destroyed. + */ public void windowDestroyed(WindowEvent e); /** Window gained focus. */ diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java index 8991203d5..6de2eee45 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java @@ -181,8 +181,13 @@ public abstract class AWTAdapter implements java.util.EventListener /** @see #addTo(java.awt.Component) */ public abstract AWTAdapter removeFrom(java.awt.Component awtComponent); + /** + * Enqueues the event to the {@link #getNewtWindow()} is not null. + */ void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { - newtWindow.enqueueEvent(wait, event); + if( null != newtWindow ) { + newtWindow.enqueueEvent(wait, event); + } } } diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java index 2d63ca455..e91bb2f82 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java @@ -66,13 +66,18 @@ public class AWTWindowAdapter return this; } - public AWTAdapter removeFrom(java.awt.Component awtComponent) { - awtComponent.removeFocusListener(this); - awtComponent.removeComponentListener(this); + public AWTAdapter removeWindowClosingFrom(java.awt.Component awtComponent) { java.awt.Window win = getWindow(awtComponent); if( null != win && null != windowClosingListener ) { win.removeWindowListener(windowClosingListener); } + return this; + } + + public AWTAdapter removeFrom(java.awt.Component awtComponent) { + awtComponent.removeFocusListener(this); + awtComponent.removeComponentListener(this); + removeWindowClosingFrom(awtComponent); if(awtComponent instanceof java.awt.Window) { ((java.awt.Window)awtComponent).removeWindowListener(this); } @@ -220,9 +225,16 @@ public class AWTWindowAdapter enqueueEvent(true, event); } } + public void windowClosed(java.awt.event.WindowEvent e) { + com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + if(null!=newtListener) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowDestroyed(event); + } else { + enqueueEvent(true, event); + } + } public void windowActivated(java.awt.event.WindowEvent e) { } - public void windowClosed(java.awt.event.WindowEvent e) { } public void windowDeactivated(java.awt.event.WindowEvent e) { } public void windowDeiconified(java.awt.event.WindowEvent e) { } public void windowIconified(java.awt.event.WindowEvent e) { } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index 7fccb6622..ce50d95dd 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -100,7 +100,10 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind protected GLWindow(Window window) { super(null, null, false /* always handle device lifecycle ourselves */); this.window = (WindowImpl) window; - this.window.setHandleDestroyNotify(false); + this.window.setWindowDestroyNotifyAction( new Runnable() { + public void run() { + defaultWindowDestroyNotifyOp(); + } } ); window.addWindowListener(new WindowAdapter() { @Override public void windowRepaint(WindowUpdateEvent e) { @@ -112,10 +115,6 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind defaultWindowResizedOp(getWidth(), getHeight()); } - @Override - public void windowDestroyNotify(WindowEvent e) { - defaultWindowDestroyNotifyOp(); - } }); this.window.setLifecycleHook(new GLLifecycleHook()); } @@ -390,6 +389,11 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override + public void setWindowDestroyNotifyAction(Runnable r) { + window.setWindowDestroyNotifyAction(r); + } + + @Override public final void setVisible(boolean visible) { window.setVisible(visible); } diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index 20b915cae..d4842ba2f 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -370,15 +370,8 @@ public abstract class DisplayImpl extends Display { DisplayImpl.this.dispatchMessages(); } }; - final void dispatchMessage(final NEWTEventTask eventTask) { - final NEWTEvent event = eventTask.get(); + final void dispatchMessage(final NEWTEvent event) { try { - if(null == event) { - // Ooops ? - System.err.println("Warning: event of eventTask is NULL"); - Thread.dumpStack(); - return; - } final Object source = event.getSource(); if(source instanceof NEWTEventConsumer) { final NEWTEventConsumer consumer = (NEWTEventConsumer) source ; @@ -396,6 +389,21 @@ public abstract class DisplayImpl extends Display { } else { re = new RuntimeException(t); } + throw re; + } + } + + final void dispatchMessage(final NEWTEventTask eventTask) { + final NEWTEvent event = eventTask.get(); + try { + if(null == event) { + // Ooops ? + System.err.println("Warning: event of eventTask is NULL"); + Thread.dumpStack(); + return; + } + dispatchMessage(event); + } catch (RuntimeException re) { if( eventTask.isCallerWaiting() ) { // propagate exception to caller eventTask.setException(re); @@ -451,7 +459,7 @@ public abstract class DisplayImpl extends Display { // can't wait if we are on EDT or NEDT -> consume right away if(wait && edtUtil.isCurrentThreadEDTorNEDT() ) { - dispatchMessage(new NEWTEventTask(e, null)); + dispatchMessage(e); return; } diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 222a1173c..66ca3e07d 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -110,7 +110,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private boolean pointerConfined = false; private LifecycleHook lifecycleHook = null; - private boolean handleDestroyNotify = true; + private Runnable windowDestroyNotifyAction = null; private FocusRunnable focusAction = null; private KeyListener keyboardFocusHandler = null; @@ -864,8 +864,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer _lock.lock(); try { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window DestroyAction() "+getThreadName()); + System.err.println("Window DestroyAction() hasScreen "+(null != screen)+", isNativeValid "+isNativeValid()+" - "+getThreadName()); } + + // send synced destroy-notify notification + sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + // Childs first .. synchronized(childWindowsLock) { if(childWindows.size()>0) { @@ -875,8 +879,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer while( clonedChildWindows.size() > 0 ) { NativeWindow nw = clonedChildWindows.remove(0); if(nw instanceof WindowImpl) { - ((WindowImpl)nw).sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); - ((WindowImpl)nw).destroy(); + ((WindowImpl)nw).windowDestroyNotify(true); } else { nw.destroy(); } @@ -1549,14 +1552,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public Object getWrappedWindow() { return null; } - - /** - * If set to true, the default value, this NEWT Window implementation will - * handle the destruction (ie {@link #destroy()} call) within {@link #windowDestroyNotify(boolean)} implementation.<br> - * If set to false, it's up to the caller/owner to handle destruction within {@link #windowDestroyNotify(boolean)}. - */ - public void setHandleDestroyNotify(boolean b) { - handleDestroyNotify = b; + + @Override + public void setWindowDestroyNotifyAction(Runnable r) { + windowDestroyNotifyAction = r; } /** @@ -2211,7 +2210,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer default: throw new NativeWindowException("Unexpected mouse event type " + e.getEventType()); } - consumed = InputEvent.consumedTag == e.getAttachment(); + consumed = NEWTEvent.consumedTag == e.getAttachment(); } } @@ -2340,7 +2339,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer default: throw new NativeWindowException("Unexpected key event type " + e.getEventType()); } - return InputEvent.consumedTag == e.getAttachment(); + return NEWTEvent.consumedTag == e.getAttachment(); } @SuppressWarnings("deprecation") @@ -2446,7 +2445,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(DEBUG_IMPLEMENTATION) { System.err.println("consumeWindowEvent: "+e+", visible "+isVisible()+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } - for(int i = 0; i < windowListeners.size(); i++ ) { + boolean consumed = false; + for(int i = 0; !consumed && i < windowListeners.size(); i++ ) { WindowListener l = windowListeners.get(i); switch(e.getEventType()) { case WindowEvent.EVENT_WINDOW_RESIZED: @@ -2475,6 +2475,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer new NativeWindowException("Unexpected window event type " + e.getEventType()); } + consumed = NEWTEvent.consumedTag == e.getAttachment(); } } @@ -2615,32 +2616,45 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } /** - * Triggered by implementation's WM events or programmatically + * Triggered by implementation's WM events or programmatic while respecting {@link #getDefaultCloseOperation()}. * * @param force if true, overrides {@link #setDefaultCloseOperation(WindowClosingMode)} with {@link WindowClosingProtocol#DISPOSE_ON_CLOSE} * and hence force destruction. Otherwise is follows the user settings. * @return true if this window is no more valid and hence has been destroyed, otherwise false. */ - protected boolean windowDestroyNotify(boolean force) { + public boolean windowDestroyNotify(boolean force) { + final WindowClosingMode defMode = getDefaultCloseOperation(); + final WindowClosingMode mode = force ? WindowClosingMode.DISPOSE_ON_CLOSE : defMode; if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.windowDestroyNotify(force: "+force+") START "+getThreadName()+": "+this); - } - if(force) { - setDefaultCloseOperation(WindowClosingMode.DISPOSE_ON_CLOSE); + System.err.println("Window.windowDestroyNotify(force: "+force+", mode "+defMode+" -> "+mode+") "+getThreadName()+": "+this); } - - // send synced destroy notifications - enqueueWindowEvent(true, WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); - - if(handleDestroyNotify && WindowClosingMode.DISPOSE_ON_CLOSE == getDefaultCloseOperation()) { - destroy(); + + if( WindowClosingMode.DISPOSE_ON_CLOSE == mode ) { + if(force) { + setDefaultCloseOperation(mode); + } + try { + if( null == windowDestroyNotifyAction ) { + destroy(); + } else { + windowDestroyNotifyAction.run(); + } + } finally { + if(force) { + setDefaultCloseOperation(defMode); + } + } + } else { + // send synced destroy notifications + sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); } final boolean destroyed = !isNativeValid(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.windowDestroyNotify(force: "+force+") END "+getThreadName()+": destroyed "+destroyed+", "+this); - } + System.err.println("Window.windowDestroyNotify(force: "+force+", mode "+mode+") END "+getThreadName()+": destroyed "+destroyed+", "+this); + } + return destroyed; } diff --git a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java index 701d9d60a..b348220d6 100644 --- a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java +++ b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java @@ -42,9 +42,7 @@ import com.jogamp.newt.event.awt.AWTWindowAdapter; * Specialized parent/client adapter, * where the NEWT child window really gets resized, * and the parent move window event gets discarded. */ -public class AWTParentWindowAdapter - extends AWTWindowAdapter - implements java.awt.event.HierarchyListener +public class AWTParentWindowAdapter extends AWTWindowAdapter implements java.awt.event.HierarchyListener { NativeWindow downstreamParent; diff --git a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java index bee43a95e..0172309fb 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java @@ -256,10 +256,7 @@ public class WindowDriver extends WindowImpl { } @Override public void windowDestroyed(WindowEvent e) { - if(isNativeValid()) { - WindowDriver.this.windowDestroyNotify(true); - } - + // Not fwd by AWTWindowAdapter, synthesized by NEWT } @Override public void windowGainedFocus(WindowEvent e) { 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. + * <p> + * 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}. + * </p> + * <p> + * Finally a {@link GLAutoDrawableDelegate} is created with the just created {@link GLDrawable} and {@link GLContext}. + * It is being used to run the {@link GLEventListener}. + * </p> + */ +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()<duration) { + Thread.sleep(100); + } + System.out.println("Fin start ..."); + + animator.stop(); + Assert.assertFalse(animator.isAnimating()); + Assert.assertFalse(animator.isStarted()); + glad.destroy(); + System.out.println("Fin Drawable: "+drawable); + System.out.println("Fin Window: "+window); + } + + @Test + public void testOnScreenDblBuf() throws InterruptedException { + final GLCapabilities reqGLCaps = new GLCapabilities( GLProfile.getGL2ES2() ); + doTest(reqGLCaps, new GearsES2(1)); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + duration = MiscUtils.atol(args[i], duration); + } + } + org.junit.runner.JUnitCore.main(TestGLAutoDrawableDelegateNEWT.class.getName()); + } + +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateOnOffscrnCapsNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateOnOffscrnCapsNEWT.java index c5b4227c2..050e596cf 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateOnOffscrnCapsNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateOnOffscrnCapsNEWT.java @@ -55,6 +55,7 @@ import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.jogl.demos.gl2.Gears; import com.jogamp.opengl.test.junit.util.AWTRobotUtil; import com.jogamp.opengl.test.junit.util.UITestCase; +import jogamp.newt.WindowImpl; /** * Tests using a NEWT {@link Window} for on- and offscreen cases. @@ -169,6 +170,11 @@ public class TestGLAutoDrawableDelegateOnOffscrnCapsNEWT extends UITestCase { } }; + window.setWindowDestroyNotifyAction( new Runnable() { + public void run() { + glad.windowDestroyNotifyOp(); + } } ); + window.addWindowListener(new WindowAdapter() { @Override public void windowRepaint(WindowUpdateEvent e) { @@ -179,11 +185,6 @@ public class TestGLAutoDrawableDelegateOnOffscrnCapsNEWT extends UITestCase { public void windowResized(WindowEvent e) { glad.windowResizedOp(window.getWidth(), window.getHeight()); } - - @Override - public void windowDestroyNotify(WindowEvent e) { - glad.windowDestroyNotifyOp(); - } }); glad.addGLEventListener(demo); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch01NEWT.java index 8b1449493..cce4149ba 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch01NEWT.java @@ -107,6 +107,11 @@ public class TestGLContextDrawableSwitch01NEWT extends UITestCase { } }; + window.setWindowDestroyNotifyAction( new Runnable() { + public void run() { + glad.windowDestroyNotifyOp(); + } } ); + // add basic window interaction window.addWindowListener(new WindowAdapter() { @Override @@ -117,10 +122,6 @@ public class TestGLContextDrawableSwitch01NEWT extends UITestCase { public void windowResized(WindowEvent e) { glad.windowResizedOp(window.getWidth(), window.getHeight()); } - @Override - public void windowDestroyNotify(WindowEvent e) { - glad.windowDestroyNotifyOp(); - } }); window.addWindowListener(wl); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch11NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch11NEWT.java index 4af9a3932..2ef797fba 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch11NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitch11NEWT.java @@ -109,6 +109,11 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { } }; + window.setWindowDestroyNotifyAction( new Runnable() { + public void run() { + glad.windowDestroyNotifyOp(); + } } ); + // add basic window interaction window.addWindowListener(new WindowAdapter() { @Override @@ -119,10 +124,6 @@ public class TestGLContextDrawableSwitch11NEWT extends UITestCase { public void windowResized(WindowEvent e) { glad.windowResizedOp(window.getWidth(), window.getHeight()); } - @Override - public void windowDestroyNotify(WindowEvent e) { - glad.windowDestroyNotifyOp(); - } }); window.addWindowListener(wl); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java index 7894f3e86..fbea81a54 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java @@ -68,6 +68,7 @@ import com.jogamp.newt.awt.NewtCanvasAWT; import com.jogamp.newt.opengl.GLWindow; 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; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLBuffers; @@ -507,6 +508,8 @@ public class TestSharedContextNewtAWTBug523 extends UITestCase { */ public void testContextSharingCreateVisibleDestroy(final boolean useNewt, final boolean shareContext) throws InterruptedException, InvocationTargetException { final JFrame frame = new JFrame("Simple JOGL App for testing context sharing"); + final WindowClosingListener awtClosingListener = AWTRobotUtil.addClosingListener(frame); + // // GLDrawableFactory factory = GLDrawableFactory.getFactory(GLProfile.get(GLProfile.GL2)); // GLContext sharedContext = factory.getOrCreateSharedContext(factory.getDefaultDevice()); @@ -699,7 +702,7 @@ public class TestSharedContextNewtAWTBug523 extends UITestCase { while(animator.isAnimating() && animator.getTotalFPSDuration() < durationPerTest) { Thread.sleep(100); } - AWTRobotUtil.closeWindow(frame, true); + AWTRobotUtil.closeWindow(frame, true, awtClosingListener); boolean windowClosed = closingSemaphore.tryAcquire(5000, TimeUnit.MILLISECONDS); Assert.assertEquals(true, windowClosed); } catch (InterruptedException e) { 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 4ebb7dddd..d63193445 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestCloseNewtAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestCloseNewtAWT.java @@ -45,6 +45,7 @@ import com.jogamp.newt.awt.NewtCanvasAWT; import com.jogamp.newt.opengl.GLWindow; 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 TestCloseNewtAWT extends UITestCase { @@ -107,9 +108,11 @@ public class TestCloseNewtAWT extends UITestCase { frame.setVisible(true); } }); - Thread.sleep(500); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(newtWindow, true)); + final WindowClosingListener closingListener = AWTRobotUtil.addClosingListener(frame); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, true, closingListener)); } public static void main(String[] args) { diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol01AWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol01AWT.java index 6aedb8433..5b7986f4d 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol01AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol01AWT.java @@ -46,13 +46,14 @@ 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 TestWindowClosingProtocol01AWT extends UITestCase { @Test public void testCloseFrameGLCanvas() throws InterruptedException, InvocationTargetException { final Frame frame = new Frame("testCloseFrameGLCanvas AWT"); - + final WindowClosingListener closingListener = AWTRobotUtil.addClosingListener(frame); GLProfile glp = GLProfile.getGL2ES2(); GLCapabilities caps = new GLCapabilities(glp); final GLCanvas glCanvas = new GLCanvas(caps); @@ -74,12 +75,14 @@ public class TestWindowClosingProtocol01AWT extends UITestCase { WindowClosingMode op = glCanvas.getDefaultCloseOperation(); Assert.assertEquals(WindowClosingMode.DO_NOTHING_ON_CLOSE, op); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false)); // nop + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false, closingListener)); // nop Thread.sleep(100); - Assert.assertEquals(true, frame.isDisplayable()); + Assert.assertEquals(true, frame.isDisplayable()); Assert.assertEquals(true, frame.isVisible()); - Assert.assertEquals(true, glCanvas.isValid()); - Assert.assertEquals(true, glCanvas.isDisplayable()); + Assert.assertEquals(true, glCanvas.isValid()); + Assert.assertEquals(true, glCanvas.isDisplayable()); + Assert.assertEquals(true, closingListener.isWindowClosing()); + Assert.assertEquals(false, closingListener.isWindowClosed()); // // close with op (GLCanvas): DISPOSE_ON_CLOSE -> 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<AWTRobotUtil.POLL_DIVIDER && glCanvas.isRealized(); wait++) { Thread.sleep(AWTRobotUtil.TIME_SLICE); } @@ -108,7 +113,8 @@ public class TestWindowClosingProtocol01AWT extends UITestCase { @Test public void testCloseJFrameGLCanvas() throws InterruptedException, InvocationTargetException { final JFrame frame = new JFrame("testCloseJFrameGLCanvas AWT"); - + final WindowClosingListener closingListener = AWTRobotUtil.addClosingListener(frame); + GLProfile glp = GLProfile.getGL2ES2(); GLCapabilities caps = new GLCapabilities(glp); GLCanvas glCanvas = new GLCanvas(caps); @@ -133,7 +139,7 @@ public class TestWindowClosingProtocol01AWT extends UITestCase { Thread.sleep(300); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false)); // nop + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false, closingListener)); // nop Thread.sleep(100); Assert.assertEquals(true, frame.isDisplayable()); Assert.assertEquals(true, glCanvas.isValid()); @@ -154,7 +160,7 @@ public class TestWindowClosingProtocol01AWT extends UITestCase { op = glCanvas.getDefaultCloseOperation(); Assert.assertEquals(WindowClosingMode.DISPOSE_ON_CLOSE, op); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, true, closingListener)); Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glCanvas, false)); Assert.assertEquals(false, frame.isDisplayable()); Assert.assertEquals(false, glCanvas.isValid()); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol02NEWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol02NEWT.java index 8cd44b82c..8d32beea3 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol02NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestWindowClosingProtocol02NEWT.java @@ -52,7 +52,7 @@ public class TestWindowClosingProtocol02NEWT extends UITestCase { 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 closingListener = AWTRobotUtil.addClosingListener(glWindow); glWindow.addGLEventListener(new GearsES2()); glWindow.setSize(512, 512); @@ -72,10 +72,10 @@ public class TestWindowClosingProtocol02NEWT extends UITestCase { Thread.sleep(300); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(glWindow, false)); // nop + Assert.assertEquals(true, AWTRobotUtil.closeWindow(glWindow, false, closingListener)); // nop Assert.assertEquals(true, glWindow.isNativeValid()); - Assert.assertEquals(true, windowClosingListener.isWindowClosing()); - windowClosingListener.reset(); + Assert.assertEquals(true, closingListener.isWindowClosing()); + closingListener.reset(); // // close with op (GLCanvas): DISPOSE_ON_CLOSE -> 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<POLL_DIVIDER; } - public static WindowClosingListener addClosingListener(Object obj) throws InterruptedException { + public static WindowClosingListener addClosingListener(Object obj) { WindowClosingListener cl = null; if(obj instanceof java.awt.Window) { - java.awt.Window win = (java.awt.Window) obj; - AWTWindowClosingAdapter acl = new AWTWindowClosingAdapter(); - win.addWindowListener(acl); + final java.awt.Window win = (java.awt.Window) obj; + final AWTWindowClosingAdapter acl = new AWTWindowClosingAdapter(); + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + win.addWindowListener(acl); + } } ); cl = acl; } else if(obj instanceof com.jogamp.newt.Window) { com.jogamp.newt.Window win = (com.jogamp.newt.Window) obj; @@ -694,53 +703,77 @@ public class AWTRobotUtil { } public static interface WindowClosingListener { void reset(); + public int getWindowClosingCount(); + public int getWindowClosedCount(); public boolean isWindowClosing(); public boolean isWindowClosed(); } static class AWTWindowClosingAdapter extends java.awt.event.WindowAdapter implements WindowClosingListener { - volatile boolean closing = false; - volatile boolean closed = false; + volatile int closing = 0; + volatile int closed = 0; public void reset() { - closing = false; - closed = false; + closing = 0; + closed = 0; } - public boolean isWindowClosing() { + public int getWindowClosingCount() { return closing; } - public boolean isWindowClosed() { + public int getWindowClosedCount() { return closed; } + public boolean isWindowClosing() { + return 0 < closing; + } + public boolean isWindowClosed() { + return 0 < closed; + } public void windowClosing(java.awt.event.WindowEvent e) { - closing = true; + closing++; + System.err.println("AWTWindowClosingAdapter.windowClosing: "+this); } public void windowClosed(java.awt.event.WindowEvent e) { - closed = true; + closed++; + System.err.println("AWTWindowClosingAdapter.windowClosed: "+this); + } + public String toString() { + return "AWTWindowClosingAdapter[closing "+closing+", closed "+closed+"]"; } } static class NEWTWindowClosingAdapter extends com.jogamp.newt.event.WindowAdapter implements WindowClosingListener { - volatile boolean closing = false; - volatile boolean closed = false; + volatile int closing = 0; + volatile int closed = 0; public void reset() { - closing = false; - closed = false; + closing = 0; + closed = 0; } - public boolean isWindowClosing() { + public int getWindowClosingCount() { return closing; } - public boolean isWindowClosed() { + public int getWindowClosedCount() { return closed; } - public void windowDestroyNotify(com.jogamp.newt.event.WindowEvent e) { - closing = true; + public boolean isWindowClosing() { + return 0 < closing; + } + public boolean isWindowClosed() { + return 0 < closed; + } + public void windowDestroyNotify(WindowEvent e) { + closing++; + System.err.println("NEWTWindowClosingAdapter.windowDestroyNotify: "+this); + } + public void windowDestroyed(WindowEvent e) { + closed++; + System.err.println("NEWTWindowClosingAdapter.windowDestroyed: "+this); } - public void windowDestroyed(com.jogamp.newt.event.WindowEvent e) { - closed = true; + public String toString() { + return "NEWTWindowClosingAdapter[closing "+closing+", closed "+closed+"]"; } } |