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/newt/classes/com/jogamp | |
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/newt/classes/com/jogamp')
9 files changed, 122 insertions, 58 deletions
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); } |