diff options
Diffstat (limited to 'src')
100 files changed, 5879 insertions, 2543 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/com/jogamp/opengl/GLRendererQuirks.java b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java index 9a13ff904..715511d1b 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java +++ b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java @@ -73,12 +73,20 @@ public class GLRendererQuirks { */ public static final int GLNonCompliant = 6; + /** + * The OpenGL Context needs a <code>glFlush()</code> before releasing it, otherwise driver may freeze: + * <ul> + * <li>OSX < 10.7.3 - NVidia Driver. Bug 533 and Bug 548 @ https://jogamp.org/bugzilla/.</li> + * </ul> + */ + public static final int GLFlushBeforeRelease = 7; + /** Number of quirks known. */ - public static final int COUNT = 7; + public static final int COUNT = 8; private static final String[] _names = new String[] { "NoDoubleBufferedPBuffer", "NoDoubleBufferedBitmap", "NoSetSwapInterval", "NoOffscreenBitmap", "NoSetSwapIntervalPostRetarget", "GLSLBuggyDiscard", - "GLNonCompliant" + "GLNonCompliant", "GLFlushBeforeRelease" }; private final int _bitmask; diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java index 53a99b640..aa0e70132 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -537,7 +537,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { remaining -= System.currentTimeMillis() - t1 ; nok = waitCondition.eval(); } - if(DEBUG || nok) { + if(DEBUG || blocking && nok) { // Info only if DEBUG or ( blocking && not-ok ) ; !blocking possible if AWT if( remaining<=0 && nok ) { System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): ++++++ timeout reached ++++++ " + Thread.currentThread().getName()); } diff --git a/src/jogl/classes/javax/media/opengl/GLContext.java b/src/jogl/classes/javax/media/opengl/GLContext.java index 4817add4d..c31b76401 100644 --- a/src/jogl/classes/javax/media/opengl/GLContext.java +++ b/src/jogl/classes/javax/media/opengl/GLContext.java @@ -442,7 +442,7 @@ public abstract class GLContext { * new GLContext implementations; not for use by end users. */ protected static void setCurrent(GLContext cur) { - if(TRACE_SWITCH) { + if( TRACE_SWITCH ) { if(null == cur) { System.err.println(getThreadName()+": GLContext.ContextSwitch: - setCurrent() - NULL"); } else { diff --git a/src/jogl/classes/javax/media/opengl/GLProfile.java b/src/jogl/classes/javax/media/opengl/GLProfile.java index f916ab8b1..25bba1725 100644 --- a/src/jogl/classes/javax/media/opengl/GLProfile.java +++ b/src/jogl/classes/javax/media/opengl/GLProfile.java @@ -543,8 +543,20 @@ public class GLProfile { * </ul> * */ - public static final String[] GL_PROFILE_LIST_MAX_PROGSHADER = new String[] { GL4bc, GL4, GL3bc, GL3, GL2, GLES2 }; + public static final String[] GL_PROFILE_LIST_MAX_PROGSHADER = new String[] { GL4bc, GL4, GL3bc, GL3, GL2, GLES2 }; + /** + * Order of maximum programmable shader <i>core only</i> profiles + * + * <ul> + * <li> GL4 + * <li> GL3 + * <li> GLES2 + * </ul> + * + */ + public static final String[] GL_PROFILE_LIST_MAX_PROGSHADER_CORE = new String[] { GL4, GL3, GLES2 }; + /** Returns a default GLProfile object, reflecting the best for the running platform. * It selects the first of the set {@link GLProfile#GL_PROFILE_LIST_ALL} * and favors hardware acceleration. @@ -659,6 +671,29 @@ public class GLProfile { return get(GL_PROFILE_LIST_MAX_PROGSHADER, favorHardwareRasterizer); } + /** + * Returns the highest profile, implementing the programmable shader <i>core</i> pipeline <i>only</i>. + * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MAX_PROGSHADER_CORE} + * + * @throws GLException if no programmable core profile is available for the device. + * @see #GL_PROFILE_LIST_MAX_PROGSHADER_CORE + */ + public static GLProfile getMaxProgrammableCore(AbstractGraphicsDevice device, boolean favorHardwareRasterizer) + throws GLException + { + return get(device, GL_PROFILE_LIST_MAX_PROGSHADER_CORE, favorHardwareRasterizer); + } + + /** Uses the default device + * @throws GLException if no programmable core profile is available for the default device. + * @see #GL_PROFILE_LIST_MAX_PROGSHADER_CORE + */ + public static GLProfile getMaxProgrammableCore(boolean favorHardwareRasterizer) + throws GLException + { + return get(GL_PROFILE_LIST_MAX_PROGSHADER_CORE, favorHardwareRasterizer); + } + /** * Returns the GL2ES1 profile implementation, hence compatible w/ GL2ES1.<br/> * It returns: diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index 2de86b545..94e123b3e 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -174,9 +174,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing new AWTWindowClosingProtocol(this, new Runnable() { @Override public void run() { - GLCanvas.this.destroy(); + GLCanvas.this.destroyImpl( true ); } - }); + }, null); /** Creates a new GLCanvas component with a default set of OpenGL capabilities, using the default OpenGL capabilities selection @@ -312,6 +312,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * all platforms since the peer hasn't been created. */ final GraphicsConfiguration gc = super.getGraphicsConfiguration(); + + if( Beans.isDesignTime() ) { + return gc; + } + /* * chosen is only non-null on platforms where the GLDrawableFactory * returns a non-null GraphicsConfiguration (in the GLCanvas @@ -370,10 +375,15 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing */ chosen = compatible; - awtConfig = config; - if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) { - dispose(true); + // complete destruction! + destroyImpl( true ); + // recreation! + awtConfig = config; + createDrawableAndContext( true ); + validateGLDrawable(); + } else { + awtConfig = config; } } } @@ -447,26 +457,33 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } return; // not yet available .. } - Threading.invoke(true, displayOnEDTAction, getTreeLock()); - awtWindowClosingProtocol.addClosingListenerOneShot(); - } - - private void dispose(boolean regenerate) { - disposeRegenerate=regenerate; - Threading.invoke(true, disposeOnEDTAction, getTreeLock()); + if( isVisible() ) { + Threading.invoke(true, displayOnEDTAction, getTreeLock()); + } } /** * {@inheritDoc} * * <p> - * This impl. calls this class's {@link #removeNotify()} AWT override, - * where the actual implementation resides. + * This impl. only destroys all GL related resources. + * </p> + * <p> + * This impl. does not remove the GLCanvas from it's parent AWT container + * so this class's {@link #removeNotify()} AWT override won't get called. + * To do so, remove this component from it's parent AWT container. * </p> */ @Override public void destroy() { - removeNotify(); + destroyImpl( false ); + } + + protected void destroyImpl(boolean destroyJAWTWindowAndAWTDevice) { + Threading.invoke(true, destroyOnEDTAction, getTreeLock()); + if( destroyJAWTWindowAndAWTDevice ) { + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, disposeJAWTWindowAndAWTDeviceOnEDT); + } } /** Overridden to cause OpenGL rendering to be performed during @@ -476,7 +493,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing */ @Override public void paint(Graphics g) { - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { // Make GLCanvas behave better in NetBeans GUI builder g.setColor(Color.BLACK); g.fillRect(0, 0, getWidth(), getHeight()); @@ -494,9 +511,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing g.drawString(name, (int) ((getWidth() - bounds.getWidth()) / 2), (int) ((getHeight() + bounds.getHeight()) / 2)); - return; - } - if( ! this.helper.isAnimatorAnimatingOnOtherThread() ) { + } else if( !this.helper.isAnimatorAnimatingOnOtherThread() ) { display(); } } @@ -513,42 +528,47 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing public void addNotify() { final RecursiveLock _lock = lock; _lock.lock(); - try { + try { + final boolean isBeansDesignTime = Beans.isDesignTime(); + if(DEBUG) { - System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()); + System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()+", isBeansDesignTime "+isBeansDesignTime); Thread.dumpStack(); } - /** - * 'super.addNotify()' determines the GraphicsConfiguration, - * while calling this class's overriden 'getGraphicsConfiguration()' method - * after which it creates the native peer. - * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration - * is being used in getGraphicsConfiguration(). - * This code order also allows recreation, ie re-adding the GLCanvas. - */ - awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device); - if(null==awtConfig) { - throw new GLException("Error: NULL AWTGraphicsConfiguration"); - } - - // before native peer is valid: X11 - disableBackgroundErase(); - - // issues getGraphicsConfiguration() and creates the native peer - super.addNotify(); - - // after native peer is valid: Windows - disableBackgroundErase(); - - if (!Beans.isDesignTime()) { - createDrawableAndContext(); + if( isBeansDesignTime ) { + super.addNotify(); + } else { + /** + * 'super.addNotify()' determines the GraphicsConfiguration, + * while calling this class's overriden 'getGraphicsConfiguration()' method + * after which it creates the native peer. + * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration + * is being used in getGraphicsConfiguration(). + * This code order also allows recreation, ie re-adding the GLCanvas. + */ + awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device); + if(null==awtConfig) { + throw new GLException("Error: NULL AWTGraphicsConfiguration"); + } + + // before native peer is valid: X11 + disableBackgroundErase(); + + // issues getGraphicsConfiguration() and creates the native peer + super.addNotify(); + + // after native peer is valid: Windows + disableBackgroundErase(); + + createDrawableAndContext( true ); + + // init drawable by paint/display makes the init sequence more equal + // for all launch flavors (applet/javaws/..) + // validateGLDrawable(); } - - // init drawable by paint/display makes the init sequence more equal - // for all launch flavors (applet/javaws/..) - // validateGLDrawable(); - + awtWindowClosingProtocol.addClosingListener(); + if(DEBUG) { System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer()); } @@ -557,27 +577,33 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } } - private void createDrawableAndContext() { - // no lock required, since this resource ain't available yet - jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); - jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); - jawtWindow.lockSurface(); - try { - drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); - context = (GLContextImpl) drawable.createContext(shareWith); - context.setContextCreationFlags(additionalCtxCreationFlags); - } finally { - jawtWindow.unlockSurface(); + private void createDrawableAndContext(boolean createJAWTWindow) { + if ( !Beans.isDesignTime() ) { + if( createJAWTWindow ) { + jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); + jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); + } + jawtWindow.lockSurface(); + try { + drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); + context = (GLContextImpl) drawable.createContext(shareWith); + context.setContextCreationFlags(additionalCtxCreationFlags); + } finally { + jawtWindow.unlockSurface(); + } } - } + } private boolean validateGLDrawable() { + if( Beans.isDesignTime() || !isDisplayable() ) { + return false; // early out! + } final GLDrawable _drawable = drawable; if ( null != _drawable ) { if( _drawable.isRealized() ) { return true; } - if( Beans.isDesignTime() || !isDisplayable() || 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) { + if( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) { return false; // early out! } // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock! @@ -627,11 +653,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing awtWindowClosingProtocol.removeClosingListener(); - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { super.removeNotify(); } else { try { - dispose(false); + destroyImpl( true ); } finally { super.removeNotify(); } @@ -676,6 +702,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } } sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock + if(null != jawtWindow && jawtWindow.isOffscreenLayerSurfaceEnabled() ) { + jawtWindow.layoutSurfaceLayer(); + } } } } @@ -790,7 +819,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public GL getGL() { - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { return null; } final GLContext _context = context; @@ -844,18 +873,18 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { - if (awtConfig == null) { + if( Beans.isDesignTime() ) { + return capsReqUser; + } else if( null == awtConfig ) { throw new GLException("No AWTGraphicsConfiguration: "+this); } - return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(); } public GLCapabilitiesImmutable getRequestedGLCapabilities() { - if (awtConfig == null) { + if( null == awtConfig ) { return capsReqUser; } - return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(); } @@ -897,8 +926,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // Internals only below this point // - private boolean disposeRegenerate; - private final Runnable disposeOnEDTAction = new Runnable() { + private final Runnable destroyOnEDTAction = new Runnable() { @Override public void run() { final RecursiveLock _lock = lock; @@ -907,11 +935,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing final GLAnimatorControl animator = getAnimator(); if(DEBUG) { - System.err.println(getThreadName()+": Info: dispose("+disposeRegenerate+") - START, hasContext " + + System.err.println(getThreadName()+": Info: destroyOnEDTAction() - START, hasContext " + (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator); Thread.dumpStack(); } - + final boolean animatorPaused; if(null!=animator) { // can't remove us from animator for recreational addNotify() @@ -926,50 +954,29 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // so we can continue with the destruction. try { helper.disposeGL(GLCanvas.this, context, true); + if(DEBUG) { + System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context); + } } catch (GLException gle) { gle.printStackTrace(); } - } - context=null; + } + context = null; } if( null != drawable ) { drawable.setRealized(false); if(DEBUG) { - System.err.println(getThreadName()+": dispose("+disposeRegenerate+") - 1: "+drawable); + System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); } - drawable=null; + drawable = null; } - if( null != jawtWindow ) { - jawtWindow.destroy(); - if(DEBUG) { - System.err.println(getThreadName()+": dispose("+disposeRegenerate+") - 2: "+jawtWindow); - } - jawtWindow=null; - } - - if(disposeRegenerate) { - // Similar process as in addNotify()! - - // Recreate GLDrawable/GLContext to reflect it's new graphics configuration - createDrawableAndContext(); - if(DEBUG) { - System.err.println(getThreadName()+": GLCanvas.dispose(true): new drawable: "+drawable); - } - validateGLDrawable(); // immediate attempt to recreate the drawable - } else { - if(null != awtConfig) { - AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, disposeAbstractGraphicsDeviceActionOnEDT); - } - awtConfig=null; - } - if(animatorPaused) { animator.resume(); } if(DEBUG) { - System.err.println(getThreadName()+": dispose("+disposeRegenerate+") - END, animator "+animator); + System.err.println(getThreadName()+": dispose() - END, animator "+animator); } } finally { @@ -979,28 +986,43 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing }; /** - * Disposes the AbstractGraphicsDevice within EDT, + * Disposes the JAWTWindow and AbstractGraphicsDevice within EDT, * since resources created (X11: Display), must be destroyed in the same thread, where they have been created. + * <p> + * The drawable and context handle are null'ed as well, assuming {@link #destroy()} has been called already. + * </p> * * @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice) */ - private final Runnable disposeAbstractGraphicsDeviceActionOnEDT = new Runnable() { + private final Runnable disposeJAWTWindowAndAWTDeviceOnEDT = new Runnable() { @Override public void run() { - if(null != awtConfig) { - final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); - final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); - final String adeviceMsg; - if(DEBUG) { - adeviceMsg = adevice.toString(); - } else { - adeviceMsg = null; - } - boolean closed = adevice.close(); - if(DEBUG) { - System.err.println(getThreadName()+": GLCanvas.dispose(false): closed GraphicsDevice: "+adeviceMsg+", result: "+closed); - } - } + context=null; + drawable=null; + + if( null != jawtWindow ) { + jawtWindow.destroy(); + if(DEBUG) { + System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow); + } + jawtWindow=null; + } + + if(null != awtConfig) { + final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); + final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); + final String adeviceMsg; + if(DEBUG) { + adeviceMsg = adevice.toString(); + } else { + adeviceMsg = null; + } + boolean closed = adevice.close(); + if(DEBUG) { + System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed); + } + } + awtConfig=null; } }; @@ -1139,14 +1161,14 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * @param device * @return the chosen AWTGraphicsConfiguration * - * @see #disposeAbstractGraphicsDeviceActionOnEDT + * @see #disposeJAWTWindowAndAWTDeviceOnEDT */ private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final GraphicsDevice device) { // Make GLCanvas behave better in NetBeans GUI builder - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { return null; } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 664edb996..6c28c75ab 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -152,9 +152,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // Used by all backends either directly or indirectly to hook up callbacks private Updater updater = new Updater(); - // Indicates whether the Java 2D OpenGL pipeline is enabled - private boolean oglPipelineEnabled = - Java2D.isOGLPipelineActive() && + // Indicates whether the Java 2D OpenGL pipeline is enabled and resource-compatible + private boolean oglPipelineUsable = + Java2D.isOGLPipelineResourceCompatible() && !Debug.isPropertyDefined("jogl.gljpanel.noogl", true); // For handling reshape events lazily @@ -174,13 +174,13 @@ 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 // otherwise it's likely it will try to be initialized while on // the Queue Flusher Thread, which is not allowed - if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) { + if (Java2D.isOGLPipelineResourceCompatible() && Java2D.isFBOEnabled()) { Java2D.getShareContext(GraphicsEnvironment. getLocalGraphicsEnvironment(). getDefaultScreenDevice()); @@ -250,17 +250,19 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public void display() { - if (EventQueue.isDispatchThread()) { - // Want display() to be synchronous, so call paintImmediately() - paintImmediately(0, 0, getWidth(), getHeight()); - } else { - // Multithreaded redrawing of Swing components is not allowed, - // so do everything on the event dispatch thread - try { - EventQueue.invokeAndWait(paintImmediatelyAction); - } catch (Exception e) { - throw new GLException(e); - } + if( isVisible() ) { + if (EventQueue.isDispatchThread()) { + // Want display() to be synchronous, so call paintImmediately() + paintImmediately(0, 0, getWidth(), getHeight()); + } else { + // Multithreaded redrawing of Swing components is not allowed, + // so do everything on the event dispatch thread + try { + EventQueue.invokeAndWait(paintImmediatelyAction); + } catch (Exception e) { + throw new GLException(e); + } + } } } @@ -350,9 +352,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing sendReshape = handleReshape(); } - updater.setGraphics(g); - - backend.doPaintComponent(g); + if( isVisible() ) { + updater.setGraphics(g); + + backend.doPaintComponent(g); + } } @@ -365,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()"); } @@ -608,7 +613,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing to perform OpenGL rendering using the GLJPanel into the same OpenGL drawable as the Swing implementation uses. */ public boolean shouldPreserveColorBufferIfTranslucent() { - return oglPipelineEnabled; + return oglPipelineUsable; } @Override @@ -662,7 +667,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } if ( null == backend ) { - if (oglPipelineEnabled) { + if (oglPipelineUsable) { backend = new J2DOGLBackend(); } else { backend = new OffscreenBackend(); @@ -673,8 +678,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if (!isInitialized) { backend.initialize(); } - - awtWindowClosingProtocol.addClosingListenerOneShot(); } @Override @@ -1560,7 +1563,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } isInitialized = false; backend = null; - oglPipelineEnabled = false; + oglPipelineUsable = false; handleReshape = true; j2dContext.destroy(); j2dContext = null; diff --git a/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java b/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java index 27569d210..b74ac9f41 100644 --- a/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java +++ b/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java @@ -103,6 +103,7 @@ public class FPSCounterImpl implements FPSCounter { fpsLastPeriod = 0; fpsTotalFrames = 0; fpsLast = 0f; fpsTotal = 0f; + fpsLastPeriod = 0; fpsTotalDuration=0; } public final synchronized int getUpdateFPSFrames() { diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index b17fce569..42364dbfd 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -278,14 +278,14 @@ public abstract class GLContextImpl extends GLContext { release(false); } private void release(boolean inDestruction) throws GLException { - if(TRACE_SWITCH) { + if( TRACE_SWITCH ) { System.err.println(getThreadName() +": GLContext.ContextSwitch[release.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock); } if ( !lock.isOwner(Thread.currentThread()) ) { final String msg = getThreadName() +": Context not current on current thread, obj " + toHexString(hashCode())+", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock; if( DEBUG_TRACE_SWITCH ) { System.err.println(msg); - if( null != lastCtxReleaseStack) { + if( null != lastCtxReleaseStack ) { System.err.print("Last release call: "); lastCtxReleaseStack.printStackTrace(); } else { @@ -318,7 +318,7 @@ public abstract class GLContextImpl extends GLContext { if( DEBUG_TRACE_SWITCH ) { final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+(actualRelease?"switch":"keep ")+" - "+lock; lastCtxReleaseStack = new Throwable(msg); - if(TRACE_SWITCH) { + if( TRACE_SWITCH ) { System.err.println(msg); // Thread.dumpStack(); } @@ -334,7 +334,7 @@ public abstract class GLContextImpl extends GLContext { @Override public final void destroy() { - if (DEBUG_TRACE_SWITCH) { + if ( DEBUG_TRACE_SWITCH ) { System.err.println(getThreadName() + ": GLContextImpl.destroy.0: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", surf "+toHexString(drawable.getHandle())+", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } @@ -351,7 +351,7 @@ public abstract class GLContextImpl extends GLContext { lock.lock(); // holdCount++ -> 1 - n (1: not locked, 2-n: destroy while rendering) if ( lock.getHoldCount() > 2 ) { final String msg = getThreadName() + ": GLContextImpl.destroy: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle); - if (DEBUG_TRACE_SWITCH) { + if ( DEBUG_TRACE_SWITCH ) { System.err.println(msg+" - Lock was hold more than once - makeCurrent/release imbalance: "+lock); Thread.dumpStack(); } @@ -388,7 +388,7 @@ public abstract class GLContextImpl extends GLContext { } } finally { lock.unlock(); - if (TRACE_SWITCH) { + if ( DEBUG_TRACE_SWITCH ) { System.err.println(getThreadName() + ": GLContextImpl.destroy.X: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } @@ -468,14 +468,14 @@ public abstract class GLContextImpl extends GLContext { */ @Override public int makeCurrent() throws GLException { - if(TRACE_SWITCH) { + if( TRACE_SWITCH ) { System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+lock); } // Note: the surface is locked within [makeCurrent .. swap .. release] final int lockRes = drawable.lockSurface(); if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { - if(DEBUG_TRACE_SWITCH) { + if( DEBUG_TRACE_SWITCH ) { System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X1]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - Surface Not Ready - CONTEXT_NOT_CURRENT - "+lock); } return CONTEXT_NOT_CURRENT; @@ -503,7 +503,7 @@ public abstract class GLContextImpl extends GLContext { // For Mac OS X, however, we need to update the context to track resizes drawableUpdatedNotify(); unlockContextAndSurface = false; // success - if(TRACE_SWITCH) { + if( TRACE_SWITCH ) { System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X2]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - keep - CONTEXT_CURRENT - "+lock); } return CONTEXT_CURRENT; @@ -526,7 +526,7 @@ public abstract class GLContextImpl extends GLContext { throw e; } finally { if (unlockContextAndSurface) { - if(DEBUG_TRACE_SWITCH) { + if( DEBUG_TRACE_SWITCH ) { System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.1]: Context lock.unlock() due to error, res "+makeCurrentResultToString(res)+", "+lock); } lock.unlock(); @@ -575,7 +575,7 @@ public abstract class GLContextImpl extends GLContext { } */ } - if(TRACE_SWITCH) { + if( TRACE_SWITCH ) { System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X3]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - switch - "+makeCurrentResultToString(res)+" - "+lock); } return res; @@ -611,7 +611,7 @@ public abstract class GLContextImpl extends GLContext { shareWith.getDrawableImpl().unlockSurface(); } } - if (DEBUG_TRACE_SWITCH) { + if ( DEBUG_TRACE_SWITCH ) { if(created) { System.err.println(getThreadName() + ": Create GL context OK: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()+" - "+getGLVersion()); // Thread.dumpStack(); @@ -811,7 +811,7 @@ public abstract class GLContextImpl extends GLContext { if(PROFILE_ALIASING) { hasGL3 = true; } - resetStates(); // clean this context states, since creation was temporary + resetStates(); // clean context states, since creation was temporary } } if(!hasGL3) { @@ -1301,7 +1301,7 @@ public abstract class GLContextImpl extends GLContext { // Only validate if a valid int version was fetched, otherwise cont. w/ version-string method -> 3.0 > Version || Version > MAX! if ( GLContext.isValidGLVersion(glIntMajor[0], glIntMinor[0]) ) { if( glIntMajor[0]<major || ( glIntMajor[0]==major && glIntMinor[0]<minor ) || 0 == major ) { - if( strictMatch && 0 < major ) { + if( strictMatch && 2 < major ) { // relaxed match for versions major < 3 requests, last resort! if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (Int): "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+glIntMajor[0]+"."+glIntMinor[0]); } @@ -1326,7 +1326,7 @@ public abstract class GLContextImpl extends GLContext { // Only validate if a valid string version was fetched -> MIN > Version || Version > MAX! if( null != strGLVersionNumber ) { if( strGLVersionNumber.compareTo(setGLVersionNumber) < 0 || 0 == major ) { - if( strictMatch && 0 < major ) { + if( strictMatch && 2 < major ) { // relaxed match for versions major < 3 requests, last resort! if(DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (String): "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+strGLVersionNumber); } @@ -1462,29 +1462,55 @@ public abstract class GLContextImpl extends GLContext { final boolean hwAccel = 0 == ( ctp & GLContext.CTX_IMPL_ACCEL_SOFT ); final boolean compatCtx = 0 != ( ctp & GLContext.CTX_PROFILE_COMPAT ); + // // OS related quirks + // if( Platform.getOSType() == Platform.OSType.MACOS ) { - final int quirk1 = GLRendererQuirks.NoOffscreenBitmap; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk1)+": cause: OS "+Platform.getOSType()); + // + // OSX + // + { + final int quirk = GLRendererQuirks.NoOffscreenBitmap; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); + } + quirks[i++] = quirk; + } + + final VersionNumber OSXVersion173 = new VersionNumber(1,7,3); + if( Platform.getOSVersionNumber().compareTo(OSXVersion173) < 0 && glRendererLowerCase.contains("nvidia") ) { + final int quirk = GLRendererQuirks.GLFlushBeforeRelease; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); + } + quirks[i++] = quirk; } - quirks[i++] = quirk1; } else if( Platform.getOSType() == Platform.OSType.WINDOWS ) { + // + // WINDOWS + // final int quirk = GLRendererQuirks.NoDoubleBufferedBitmap; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); } quirks[i++] = quirk; - } - - // Renderer related quirks, may also involve OS - if( Platform.OSType.ANDROID == Platform.getOSType() && glRendererLowerCase.contains("powervr") ) { - final int quirk = GLRendererQuirks.NoSetSwapInterval; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + " / Renderer " + glRenderer); + } else if( Platform.OSType.ANDROID == Platform.getOSType() ) { + // + // ANDROID + // + // Renderer related quirks, may also involve OS + if( glRendererLowerCase.contains("powervr") ) { + final int quirk = GLRendererQuirks.NoSetSwapInterval; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + " / Renderer " + glRenderer); + } + quirks[i++] = quirk; } - quirks[i++] = quirk; } + + // + // RENDERER related quirks + // if( glRendererLowerCase.contains("mesa") || glRendererLowerCase.contains("gallium") ) { { final int quirk = GLRendererQuirks.NoSetSwapIntervalPostRetarget; diff --git a/src/jogl/classes/jogamp/opengl/GLVersionNumber.java b/src/jogl/classes/jogamp/opengl/GLVersionNumber.java index 1004f04c6..83815f7a4 100644 --- a/src/jogl/classes/jogamp/opengl/GLVersionNumber.java +++ b/src/jogl/classes/jogamp/opengl/GLVersionNumber.java @@ -29,6 +29,9 @@ package jogamp.opengl; import java.util.StringTokenizer; + +import javax.media.opengl.GLContext; + import com.jogamp.common.util.VersionNumber; /** @@ -90,9 +93,11 @@ class GLVersionNumber extends VersionNumber { // Avoid possibly confusing situations by putting some // constraints on the upgrades we do to the major and // minor versions - if ((altMajor == val[0] && altMinor > val[1]) || altMajor == val[0] + 1) { - val[0] = altMajor; - val[1] = altMinor; + if ( (altMajor == val[0] && altMinor > val[1]) || altMajor == val[0] + 1 ) { + if( GLContext.isValidGLVersion(altMajor, altMinor) ) { + val[0] = altMajor; + val[1] = altMinor; + } } } } diff --git a/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java b/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java index 983f11133..ae8969d07 100644 --- a/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java +++ b/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java @@ -94,11 +94,7 @@ public class AWTThreadingPlugin implements ToolkitThreadingPlugin { // QFT which is not allowed. For now, on X11 platforms, // continue to perform this work on the EDT. if (wait && Java2D.isOGLPipelineActive() && !ThreadingImpl.isX11()) { - if(wait) { - Java2D.invokeWithOGLContextCurrent(null, r); - } else { - - } + Java2D.invokeWithOGLContextCurrent(null, r); } else { AWTEDTExecutor.singleton.invoke(wait, r); } diff --git a/src/jogl/classes/jogamp/opengl/awt/Java2D.java b/src/jogl/classes/jogamp/opengl/awt/Java2D.java index 3dbfefb19..edf9e89f8 100644 --- a/src/jogl/classes/jogamp/opengl/awt/Java2D.java +++ b/src/jogl/classes/jogamp/opengl/awt/Java2D.java @@ -69,6 +69,7 @@ public class Java2D { private static boolean DEBUG = Debug.debug("Java2D"); private static boolean isHeadless; private static boolean isOGLPipelineActive; + private static boolean isOGLPipelineResourceCompatible; private static Method invokeWithOGLContextCurrentMethod; private static Method isQueueFlusherThreadMethod; private static Method getOGLViewportMethod; @@ -124,19 +125,37 @@ public class Java2D { isHeadless = true; // Figure out whether the default graphics configuration is an // OpenGL graphics configuration - GraphicsConfiguration cfg = - GraphicsEnvironment.getLocalGraphicsEnvironment(). - getDefaultScreenDevice(). - getDefaultConfiguration(); + final GraphicsConfiguration cfg; + final String cfgName; + final boolean java2dOGLDisabledByOS = Platform.OS_TYPE == Platform.OSType.MACOS; + final boolean java2dOGLDisabledByProp; + { + boolean enabled = true; + final String sVal = System.getProperty("sun.java2d.opengl"); + if( null != sVal ) { + enabled = Boolean.valueOf(sVal); + } + java2dOGLDisabledByProp = !enabled; + } + if( !java2dOGLDisabledByProp && !java2dOGLDisabledByOS ) { + cfg = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); + cfgName = cfg.getClass().getName(); + } else { + if (DEBUG) { + System.err.println("Java2D support disabled: by Property "+java2dOGLDisabledByProp+", by OS "+java2dOGLDisabledByOS); + } + cfg = null; + cfgName = "nil"; + } // If we get here, we aren't running in headless mode isHeadless = false; - String name = cfg.getClass().getName(); if (DEBUG) { - System.err.println("Java2D support: default GraphicsConfiguration = " + name); + System.err.println("Java2D support: default GraphicsConfiguration = " + cfgName); } - isOGLPipelineActive = Platform.OS_TYPE != Platform.OSType.MACOS && - (name.startsWith("sun.java2d.opengl")); - + isOGLPipelineActive = cfgName.startsWith("sun.java2d.opengl"); + isOGLPipelineResourceCompatible = isOGLPipelineActive; + if (isOGLPipelineActive) { try { // Try to get methods we need to integrate @@ -152,99 +171,101 @@ public class Java2D { new Class[] {}); isQueueFlusherThreadMethod.setAccessible(true); - getOGLViewportMethod = utils.getDeclaredMethod("getOGLViewport", - new Class[] { - Graphics.class, - Integer.TYPE, - Integer.TYPE - }); - getOGLViewportMethod.setAccessible(true); - - getOGLScissorBoxMethod = utils.getDeclaredMethod("getOGLScissorBox", - new Class[] { - Graphics.class - }); - getOGLScissorBoxMethod.setAccessible(true); - - getOGLSurfaceIdentifierMethod = utils.getDeclaredMethod("getOGLSurfaceIdentifier", + if( isOGLPipelineResourceCompatible ) { + getOGLViewportMethod = utils.getDeclaredMethod("getOGLViewport", + new Class[] { + Graphics.class, + Integer.TYPE, + Integer.TYPE + }); + getOGLViewportMethod.setAccessible(true); + + getOGLScissorBoxMethod = utils.getDeclaredMethod("getOGLScissorBox", + new Class[] { + Graphics.class + }); + getOGLScissorBoxMethod.setAccessible(true); + + getOGLSurfaceIdentifierMethod = utils.getDeclaredMethod("getOGLSurfaceIdentifier", + new Class[] { + Graphics.class + }); + getOGLSurfaceIdentifierMethod.setAccessible(true); + + // Try to get additional methods required for proper FBO support + fbObjectSupportInitialized = true; + try { + invokeWithOGLSharedContextCurrentMethod = utils.getDeclaredMethod("invokeWithOGLSharedContextCurrent", + new Class[] { + GraphicsConfiguration.class, + Runnable.class + }); + invokeWithOGLSharedContextCurrentMethod.setAccessible(true); + + getOGLSurfaceTypeMethod = utils.getDeclaredMethod("getOGLSurfaceType", new Class[] { Graphics.class }); - getOGLSurfaceIdentifierMethod.setAccessible(true); - - // Try to get additional methods required for proper FBO support - fbObjectSupportInitialized = true; - try { - invokeWithOGLSharedContextCurrentMethod = utils.getDeclaredMethod("invokeWithOGLSharedContextCurrent", - new Class[] { - GraphicsConfiguration.class, - Runnable.class - }); - invokeWithOGLSharedContextCurrentMethod.setAccessible(true); - - getOGLSurfaceTypeMethod = utils.getDeclaredMethod("getOGLSurfaceType", - new Class[] { - Graphics.class - }); - getOGLSurfaceTypeMethod.setAccessible(true); - } catch (Exception e) { - fbObjectSupportInitialized = false; - if (DEBUG) { - e.printStackTrace(); - System.err.println("Info: Disabling Java2D/JOGL FBO support"); - } - } - - // Try to get an additional method for FBO support in recent Mustang builds - try { - getOGLTextureTypeMethod = utils.getDeclaredMethod("getOGLTextureType", - new Class[] { - Graphics.class - }); - getOGLTextureTypeMethod.setAccessible(true); - } catch (Exception e) { - if (DEBUG) { - e.printStackTrace(); - System.err.println("Info: GL_ARB_texture_rectangle FBO support disabled"); - } - } - - // Try to set up APIs for enabling the bridge on OS X, - // where it isn't possible to create generalized - // external GLDrawables - Class<?> cglSurfaceData = null; - try { - cglSurfaceData = Class.forName("sun.java2d.opengl.CGLSurfaceData"); - } catch (Exception e) { - if (DEBUG) { - e.printStackTrace(); - System.err.println("Info: Unable to find class sun.java2d.opengl.CGLSurfaceData for OS X"); - } - } - if (cglSurfaceData != null) { - // FIXME: for now, assume that FBO support is not enabled on OS X - fbObjectSupportInitialized = false; - - // We need to find these methods in order to make the bridge work on OS X - createOGLContextOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("createOGLContextOnSurface", - new Class[] { - Graphics.class, - Long.TYPE - }); - createOGLContextOnSurfaceMethod.setAccessible(true); - - makeOGLContextCurrentOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("makeOGLContextCurrentOnSurface", - new Class[] { - Graphics.class, - Long.TYPE - }); - makeOGLContextCurrentOnSurfaceMethod.setAccessible(true); - - destroyOGLContextMethod = cglSurfaceData.getDeclaredMethod("destroyOGLContext", - new Class[] { - Long.TYPE - }); - destroyOGLContextMethod.setAccessible(true); + getOGLSurfaceTypeMethod.setAccessible(true); + } catch (Exception e) { + fbObjectSupportInitialized = false; + if (DEBUG) { + e.printStackTrace(); + System.err.println("Info: Disabling Java2D/JOGL FBO support"); + } + } + + // Try to get an additional method for FBO support in recent Mustang builds + try { + getOGLTextureTypeMethod = utils.getDeclaredMethod("getOGLTextureType", + new Class[] { + Graphics.class + }); + getOGLTextureTypeMethod.setAccessible(true); + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + System.err.println("Info: GL_ARB_texture_rectangle FBO support disabled"); + } + } + + // Try to set up APIs for enabling the bridge on OS X, + // where it isn't possible to create generalized + // external GLDrawables + Class<?> cglSurfaceData = null; + try { + cglSurfaceData = Class.forName("sun.java2d.opengl.CGLSurfaceData"); + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + System.err.println("Info: Unable to find class sun.java2d.opengl.CGLSurfaceData for OS X"); + } + } + if (cglSurfaceData != null) { + // FIXME: for now, assume that FBO support is not enabled on OS X + fbObjectSupportInitialized = false; + + // We need to find these methods in order to make the bridge work on OS X + createOGLContextOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("createOGLContextOnSurface", + new Class[] { + Graphics.class, + Long.TYPE + }); + createOGLContextOnSurfaceMethod.setAccessible(true); + + makeOGLContextCurrentOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("makeOGLContextCurrentOnSurface", + new Class[] { + Graphics.class, + Long.TYPE + }); + makeOGLContextCurrentOnSurfaceMethod.setAccessible(true); + + destroyOGLContextMethod = cglSurfaceData.getDeclaredMethod("destroyOGLContext", + new Class[] { + Long.TYPE + }); + destroyOGLContextMethod.setAccessible(true); + } } } catch (Exception e) { catched = e; @@ -252,6 +273,7 @@ public class Java2D { System.err.println("Info: Disabling Java2D/JOGL integration"); } isOGLPipelineActive = false; + isOGLPipelineResourceCompatible = false; } } } catch (HeadlessException e) { @@ -265,7 +287,7 @@ public class Java2D { if(null != catched) { catched.printStackTrace(); } - System.err.println("JOGL/Java2D integration " + (isOGLPipelineActive ? "enabled" : "disabled")); + System.err.println("JOGL/Java2D OGL Pipeline active " + isOGLPipelineActive + ", resourceCompatible "+isOGLPipelineResourceCompatible); } return null; } @@ -275,6 +297,10 @@ public class Java2D { public static boolean isOGLPipelineActive() { return isOGLPipelineActive; } + + public static boolean isOGLPipelineResourceCompatible() { + return isOGLPipelineResourceCompatible; + } public static boolean isFBOEnabled() { return fbObjectSupportInitialized; @@ -330,7 +356,7 @@ public class Java2D { false if the passed GraphicsConfiguration was not an OpenGL GraphicsConfiguration. */ public static boolean invokeWithOGLSharedContextCurrent(GraphicsConfiguration g, Runnable r) throws GLException { - checkActive(); + checkCompatible(); try { AWTUtil.lockToolkit(); @@ -355,7 +381,7 @@ public class Java2D { public static Rectangle getOGLViewport(Graphics g, int componentWidth, int componentHeight) { - checkActive(); + checkCompatible(); try { return (Rectangle) getOGLViewportMethod.invoke(null, new Object[] {g, @@ -375,7 +401,7 @@ public class Java2D { passed to a call to glScissor(). Should only be called from the Queue Flusher Thread. */ public static Rectangle getOGLScissorBox(Graphics g) { - checkActive(); + checkCompatible(); try { return (Rectangle) getOGLScissorBoxMethod.invoke(null, new Object[] {g}); @@ -393,7 +419,7 @@ public class Java2D { created (and the old ones destroyed). Should only be called from the Queue Flusher Thread.*/ public static Object getOGLSurfaceIdentifier(Graphics g) { - checkActive(); + checkCompatible(); try { return getOGLSurfaceIdentifierMethod.invoke(null, new Object[] {g}); @@ -408,7 +434,7 @@ public class Java2D { object. This indicates, in particular, whether Java2D is currently rendering into a pbuffer or FBO. */ public static int getOGLSurfaceType(Graphics g) { - checkActive(); + checkCompatible(); try { // FIXME: fallback path for pre-b73 (?) Mustang builds -- remove @@ -429,7 +455,7 @@ public class Java2D { object assuming it is rendering to an FBO. Returns either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB. */ public static int getOGLTextureType(Graphics g) { - checkActive(); + checkCompatible(); if (getOGLTextureTypeMethod == null) { return GL.GL_TEXTURE_2D; @@ -483,7 +509,7 @@ public class Java2D { associated with the given Graphics object, sharing textures and display lists with the specified (CGLContextObj) share context. */ public static long createOGLContextOnSurface(Graphics g, long shareCtx) { - checkActive(); + checkCompatible(); try { return ((Long) createOGLContextOnSurfaceMethod.invoke(null, new Object[] { g, new Long(shareCtx) })).longValue(); @@ -497,7 +523,7 @@ public class Java2D { /** (Mac OS X-specific) Makes the given OpenGL context current on the surface associated with the given Graphics object. */ public static boolean makeOGLContextCurrentOnSurface(Graphics g, long ctx) { - checkActive(); + checkCompatible(); try { return ((Boolean) makeOGLContextCurrentOnSurfaceMethod.invoke(null, new Object[] { g, new Long(ctx) })).booleanValue(); @@ -510,7 +536,7 @@ public class Java2D { /** (Mac OS X-specific) Destroys the given OpenGL context. */ public static void destroyOGLContext(long ctx) { - checkActive(); + checkCompatible(); try { destroyOGLContextMethod.invoke(null, new Object[] { new Long(ctx) }); @@ -527,7 +553,13 @@ public class Java2D { private static void checkActive() { if (!isOGLPipelineActive()) { - throw new GLException("Java2D OpenGL pipeline not active (or necessary support not present)"); + throw new GLException("Java2D OpenGL pipeline not active"); + } + } + + private static void checkCompatible() { + if ( !isOGLPipelineResourceCompatible() ) { + throw new GLException("Java2D OpenGL pipeline not resource compatible"); } } @@ -562,7 +594,7 @@ public class Java2D { // Note 2: the first execution of this method must not be from the // Java2D Queue Flusher Thread. - if (isOGLPipelineActive() && + if (isOGLPipelineResourceCompatible() && isFBOEnabled() && !initializedJ2DFBOShareContext) { diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index 838a0387d..3825f855c 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -74,6 +74,7 @@ import com.jogamp.common.util.VersionNumber; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.util.PMVMatrix; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; @@ -687,13 +688,33 @@ public abstract class MacOSXCGLContext extends GLContextImpl } else { gl3ShaderProgramName = 0; } - nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, gl3ShaderProgramName, nsOpenGLLayerPFmt, pbufferHandle, texID, chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight); - nsOpenGLLayerPFmt = 0; // NSOpenGLLayer will release pfmt - if (DEBUG) { - System.err.println("NS create nsOpenGLLayer "+toHexString(nsOpenGLLayer)+" w/ pbuffer "+toHexString(pbufferHandle)+", texID "+texID+", texSize "+lastWidth+"x"+lastHeight+", "+drawable); + + /** + * Perform NSOpenGLLayer creation and attaching on main-thread, + * hence release the lock on our context - which will be used to + * create a shared context within NSOpenGLLayer. + */ + final long cglCtx = CGL.getCGLContext(ctx); + if(0 == cglCtx) { + throw new InternalError("Null CGLContext for: "+this); + } + final boolean ctxUnlocked = CGL.kCGLNoError == CGL.CGLUnlockContext(cglCtx); + try { + nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, gl3ShaderProgramName, nsOpenGLLayerPFmt, pbufferHandle, texID, chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight); + nsOpenGLLayerPFmt = 0; // NSOpenGLLayer will release pfmt + if (DEBUG) { + System.err.println("NS create nsOpenGLLayer "+toHexString(nsOpenGLLayer)+" w/ pbuffer "+toHexString(pbufferHandle)+", texID "+texID+", texSize "+lastWidth+"x"+lastHeight+", "+drawable); + } + backingLayerHost.attachSurfaceLayer(nsOpenGLLayer); + setSwapInterval(1); // enabled per default in layered surface + } finally { + if( ctxUnlocked ) { + if( CGL.kCGLNoError != CGL.CGLLockContext(cglCtx) ) { + throw new InternalError("Could not re-lock CGLContext for: "+this); + } + } } - backingLayerHost.attachSurfaceLayer(nsOpenGLLayer); - setSwapInterval(1); // enabled per default in layered surface + backingLayerHost.layoutSurfaceLayer(); } else { lastWidth = drawable.getWidth(); lastHeight = drawable.getHeight(); @@ -773,7 +794,9 @@ public abstract class MacOSXCGLContext extends GLContextImpl @Override public boolean release(long ctx) { try { - gl.glFlush(); // w/o glFlush()/glFinish() OSX < 10.7 (NVidia driver) may freeze + if( hasRendererQuirk(GLRendererQuirks.GLFlushBeforeRelease) && null != MacOSXCGLContext.this.getGLProcAddressTable() ) { + gl.glFlush(); + } } catch (GLException gle) { if(DEBUG) { System.err.println("MacOSXCGLContext.NSOpenGLImpl.release: INFO: glFlush() catched exception:"); @@ -953,7 +976,9 @@ public abstract class MacOSXCGLContext extends GLContextImpl @Override public boolean release(long ctx) { try { - gl.glFlush(); // w/o glFlush()/glFinish() OSX < 10.7 (NVidia driver) may freeze + if( hasRendererQuirk(GLRendererQuirks.GLFlushBeforeRelease) && null != MacOSXCGLContext.this.getGLProcAddressTable() ) { + gl.glFlush(); + } } catch (GLException gle) { if(DEBUG) { System.err.println("MacOSXCGLContext.CGLImpl.release: INFO: glFlush() catched exception:"); diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java index e76c39805..9f034adb1 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java @@ -346,7 +346,7 @@ public class WindowsWGLContext extends GLContextImpl { if(glCaps.getGLProfile().isGL3()) { WGL.wglMakeCurrent(0, 0); WGL.wglDeleteContext(temp_ctx); - throw new GLException("WindowsWGLContext.createContext ctx !ARB, context > GL2 requested "+getGLVersion()); + throw new GLException(getThreadName()+": WindowsWGLContex.createContextImpl ctx !ARB, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glCaps.getGLProfile()+", current: "+getGLVersion()); } if(DEBUG) { System.err.println("WindowsWGLContext.createContext failed, fall back to !ARB context "+getGLVersion()); diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java index feda64f30..575ff51b8 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java @@ -166,7 +166,7 @@ public abstract class X11GLXContext extends GLContextImpl { throw new InternalError("Given readDrawable but no driver support"); } } catch (RuntimeException re) { - if(DEBUG_TRACE_SWITCH) { + if( DEBUG_TRACE_SWITCH ) { System.err.println(getThreadName()+": Warning: X11GLXContext.glXMakeContextCurrent failed: "+re+", with "+ "dpy "+toHexString(dpy)+ ", write "+toHexString(writeDrawable)+ @@ -374,7 +374,7 @@ public abstract class X11GLXContext extends GLContextImpl { if(glp.isGL3()) { glXMakeContextCurrent(display, 0, 0, 0); GLX.glXDestroyContext(display, temp_ctx); - throw new GLException(getThreadName()+": X11GLXContext.createContextImpl ctx !ARB, context > GL2 requested - requested: "+glp+", current: "+getGLVersion()+", "); + throw new GLException(getThreadName()+": X11GLXContext.createContextImpl ctx !ARB, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glp+", current: "+getGLVersion()); } if(DEBUG) { System.err.println(getThreadName()+": X11GLXContext.createContextImpl failed, fall back to !ARB context "+getGLVersion()); diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m index b37930587..2cf74380c 100644 --- a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m +++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m @@ -1,6 +1,7 @@ #import "MacOSXWindowSystemInterface.h" #import <QuartzCore/QuartzCore.h> #import <pthread.h> +#import "NativeWindowProtocols.h" #include "timespec.h" #import <OpenGL/glext.h> @@ -41,6 +42,8 @@ extern GLboolean glIsVertexArray (GLuint array); // // #define DBG_PERF 1 +// #define DBG_LIFECYCLE 1 + /** * Capture setView(NULL), which produces a 'invalid drawable' message * @@ -52,6 +55,10 @@ extern GLboolean glIsVertexArray (GLuint array); - (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share; - (void)setView:(NSView *)view; - (void)update; +#ifdef DBG_LIFECYCLE +- (id)retain; +- (oneway void)release; +#endif - (void)dealloc; @end @@ -69,8 +76,11 @@ extern GLboolean glIsVertexArray (GLuint array); - (void)setView:(NSView *)view { DBG_PRINT("MyNSOpenGLContext.setView: this.0 %p, view %p\n", self, view); + // NSLog(@"MyNSOpenGLContext::setView: %@",[NSThread callStackSymbols]); if(NULL != view) { [super setView:view]; + } else { + [self clearDrawable]; } DBG_PRINT("MyNSOpenGLContext.setView.X\n"); } @@ -82,19 +92,47 @@ extern GLboolean glIsVertexArray (GLuint array); DBG_PRINT("MyNSOpenGLContext.update.X\n"); } +#ifdef DBG_LIFECYCLE + +- (id)retain +{ + DBG_PRINT("MyNSOpenGLContext::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyNSOpenGLContext::retain: %@",[NSThread callStackSymbols]); + id o = [super retain]; + DBG_PRINT("MyNSOpenGLContext::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]); + return o; +} + +- (oneway void)release +{ + DBG_PRINT("MyNSOpenGLContext::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + [super release]; + // DBG_PRINT("MyNSOpenGLContext::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]); +} + +#endif + - (void)dealloc { - DBG_PRINT("MyNSOpenGLContext.dealloc: this.0 %p\n", self); + CGLContextObj cglCtx = [self CGLContextObj]; + + DBG_PRINT("MyNSOpenGLContext::dealloc.0 %p (refcnt %d) - CGL-Ctx %p\n", self, (int)[self retainCount], cglCtx); + [self clearDrawable]; + if( NULL != cglCtx ) { + CGLDestroyContext( cglCtx ); + } [super dealloc]; - DBG_PRINT("MyNSOpenGLContext.dealloc.X: %p\n", self); + // DBG_PRINT("MyNSOpenGLContext.dealloc.X: %p\n", self); } @end -@interface MyNSOpenGLLayer: NSOpenGLLayer +@interface MyNSOpenGLLayer: NSOpenGLLayer <NWDedicatedSize> { @private GLfloat gl_texCoords[8]; + NSOpenGLContext* myCtx; + Bool isGLEnabled; @protected NSOpenGLContext* parentCtx; @@ -106,8 +144,8 @@ extern GLboolean glIsVertexArray (GLuint array); NSOpenGLPixelFormat* parentPixelFmt; int texWidth; int texHeight; - int newTexWidth; - int newTexHeight; + int dedicatedWidth; + int dedicatedHeight; volatile NSOpenGLPixelBuffer* pbuffer; volatile GLuint textureID; volatile NSOpenGLPixelBuffer* newPBuffer; @@ -136,7 +174,17 @@ extern GLboolean glIsVertexArray (GLuint array); texWidth: (int) texWidth texHeight: (int) texHeight; -- (Bool) validateTexSizeWithNewSize; +- (void)releaseLayer; +- (void)deallocPBuffer; +- (void)disableAnimation; +- (void)pauseAnimation:(Bool)pause; +- (void)setSwapInterval:(int)interval; +- (void)tick; +- (void)waitUntilRenderSignal: (long) to_micros; +- (Bool)isGLSourceValid; + +- (void) setGLEnabled: (Bool) enable; +- (Bool) validateTexSizeWithDedicatedSize; - (Bool) validateTexSize: (int) _texWidth texHeight: (int) _texHeight; - (void) setTextureID: (int) _texID; @@ -144,17 +192,22 @@ extern GLboolean glIsVertexArray (GLuint array); - (void) setNewPBuffer: (NSOpenGLPixelBuffer*)p; - (void) applyNewPBuffer; +- (void)setDedicatedSize:(CGSize)size; // @NWDedicatedSize +- (CGRect)fixMyFrame; +- (CGRect)fixSuperPosition; +- (id<CAAction>)actionForKey:(NSString *)key ; - (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask; - (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat; -- (void)disableAnimation; -- (void)pauseAnimation:(Bool)pause; -- (void)deallocPBuffer; -- (void)releaseLayer; +- (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat + forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp; +- (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat + forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp; + +#ifdef DBG_LIFECYCLE +- (id)retain; +- (oneway void)release; +#endif - (void)dealloc; -- (void)setSwapInterval:(int)interval; -- (void)tick; -- (void)waitUntilRenderSignal: (long) to_micros; -- (Bool)isGLSourceValid; @end @@ -209,6 +262,7 @@ static const GLfloat gl_verts[] = { pthread_mutex_init(&renderLock, &renderLockAttr); // recursive pthread_cond_init(&renderSignal, NULL); // no attribute + myCtx = NULL; { int i; for(i=0; i<8; i++) { @@ -226,9 +280,10 @@ static const GLfloat gl_verts[] = { swapIntervalCounter = 0; timespec_now(&lastWaitTime); shallDraw = NO; - newTexWidth = _texWidth; - newTexHeight = _texHeight; - [self validateTexSizeWithNewSize]; + isGLEnabled = YES; + dedicatedWidth = _texWidth; + dedicatedHeight = _texHeight; + [self validateTexSizeWithDedicatedSize]; [self setTextureID: texID]; newPBuffer = NULL; @@ -265,13 +320,6 @@ static const GLfloat gl_verts[] = { } } if(NULL != displayLink) { - cvres = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [parentCtx CGLContextObj], [parentPixelFmt CGLPixelFormatObj]); - if(kCVReturnSuccess != cvres) { - DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext failed: %d\n", self, cvres); - displayLink = NULL; - } - } - if(NULL != displayLink) { cvres = CVDisplayLinkSetOutputCallback(displayLink, renderMyNSOpenGLLayer, self); if(kCVReturnSuccess != cvres) { DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetOutputCallback failed: %d\n", self, cvres); @@ -302,9 +350,15 @@ static const GLfloat gl_verts[] = { return self; } -- (Bool) validateTexSizeWithNewSize +- (void) setGLEnabled: (Bool) enable { - return [self validateTexSize: newTexWidth texHeight: newTexHeight]; + DBG_PRINT("MyNSOpenGLLayer::setGLEnabled: %p, %d -> %d\n", self, (int)isGLEnabled, (int)enable); + isGLEnabled = enable; +} + +- (Bool) validateTexSizeWithDedicatedSize +{ + return [self validateTexSize: dedicatedWidth texHeight: dedicatedHeight]; } - (Bool) validateTexSize: (int) _texWidth texHeight: (int) _texHeight @@ -312,12 +366,14 @@ static const GLfloat gl_verts[] = { if(_texHeight != texHeight || _texWidth != texWidth) { texWidth = _texWidth; texHeight = _texHeight; + /** CGRect lRect = [self bounds]; lRect.origin.x = 0; lRect.origin.y = 0; lRect.size.width = texWidth; lRect.size.height = texHeight; - [self setFrame: lRect]; + [self setFrame: lRect]; */ + CGRect lRect = [self fixMyFrame]; GLfloat texCoordWidth, texCoordHeight; if(NULL != pbuffer) { @@ -339,8 +395,14 @@ static const GLfloat gl_verts[] = { gl_texCoords[5] = texCoordHeight; gl_texCoords[4] = texCoordWidth; gl_texCoords[6] = texCoordWidth; +#ifdef VERBOSE_ON + DBG_PRINT("MyNSOpenGLLayer::validateTexSize %p -> tex %dx%d, bounds: %lf/%lf %lfx%lf\n", + self, texWidth, texHeight, + lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); +#endif return YES; } else { + [self fixMyFrame]; return NO; } } @@ -407,26 +469,9 @@ static const GLfloat gl_verts[] = { } } -- (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask -{ - DBG_PRINT("MyNSOpenGLLayer::openGLPixelFormatForDisplayMask: %p (refcnt %d) - parent-pfmt %p -> new-pfmt %p\n", - self, (int)[self retainCount], parentPixelFmt, parentPixelFmt); - return parentPixelFmt; -} - -- (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat -{ - DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.0: %p (refcnt %d) - pfmt %p, parent %p\n", - self, (int)[self retainCount], pixelFormat, parentCtx); - NSOpenGLContext * nctx = [[MyNSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:parentCtx]; - DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.X: new-ctx %p\n", nctx); - return nctx; -} - - (void)disableAnimation { - DBG_PRINT("MyNSOpenGLLayer::disableAnimation: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink); - pthread_mutex_lock(&renderLock); + DBG_PRINT("MyNSOpenGLLayer::disableAnimation.0: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink); [self setAsynchronous: NO]; if(NULL != displayLink) { #ifdef HAS_CADisplayLink @@ -438,35 +483,60 @@ static const GLfloat gl_verts[] = { #endif displayLink = NULL; } - pthread_mutex_unlock(&renderLock); + DBG_PRINT("MyNSOpenGLLayer::disableAnimation.X: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink); } - (void)releaseLayer { DBG_PRINT("MyNSOpenGLLayer::releaseLayer.0: %p (refcnt %d)\n", self, (int)[self retainCount]); - pthread_mutex_lock(&renderLock); [self disableAnimation]; + pthread_mutex_lock(&renderLock); [self deallocPBuffer]; - [[self openGLContext] release]; + // [[self openGLContext] release]; + if( NULL != myCtx ) { + [myCtx release]; + myCtx = NULL; + } + parentCtx = NULL; [parentPixelFmt release]; - [self release]; - DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p (refcnt %d)\n", self, (int)[self retainCount]); pthread_mutex_unlock(&renderLock); + [self release]; + DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p\n", self); +} + +#ifdef DBG_LIFECYCLE + +- (id)retain +{ + DBG_PRINT("MyNSOpenGLLayer::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyNSOpenGLLayer::retain: %@",[NSThread callStackSymbols]); + id o = [super retain]; + DBG_PRINT("MyNSOpenGLLayer::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]); + return o; } +- (oneway void)release +{ + DBG_PRINT("MyNSOpenGLLayer::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyNSOpenGLLayer::release: %@",[NSThread callStackSymbols]); + [super release]; + // DBG_PRINT("MyNSOpenGLLayer::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]); +} + +#endif + - (void)dealloc { DBG_PRINT("MyNSOpenGLLayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]); // NSLog(@"MyNSOpenGLLayer::dealloc: %@",[NSThread callStackSymbols]); - - pthread_mutex_lock(&renderLock); [self disableAnimation]; + pthread_mutex_lock(&renderLock); [self deallocPBuffer]; pthread_mutex_unlock(&renderLock); pthread_cond_destroy(&renderSignal); pthread_mutex_destroy(&renderLock); [super dealloc]; - DBG_PRINT("MyNSOpenGLLayer::dealloc.X %p\n", self); + // DBG_PRINT("MyNSOpenGLLayer::dealloc.X %p\n", self); } - (Bool)isGLSourceValid @@ -474,26 +544,114 @@ static const GLfloat gl_verts[] = { return NULL != pbuffer || NULL != newPBuffer || 0 != textureID ; } -- (void)resizeWithOldSuperlayerSize:(CGSize)size - { - CGRect lRectS = [[self superlayer] bounds]; +// @NWDedicatedSize +- (void)setDedicatedSize:(CGSize)size { + DBG_PRINT("MyNSOpenGLLayer::setDedicatedSize: %p, texSize %dx%d <- %lfx%lf\n", + self, texWidth, texHeight, size.width, size.height); + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + + dedicatedWidth = size.width; + dedicatedHeight = size.height; - DBG_PRINT("MyNSOpenGLLayer::resizeWithOldSuperlayerSize: %p, texSize %dx%d, bounds: %lfx%lf -> %lfx%lf (refcnt %d)\n", - self, texWidth, texHeight, size.width, size.height, lRectS.size.width, lRectS.size.height, (int)[self retainCount]); + CGRect rect = CGRectMake(0, 0, dedicatedWidth, dedicatedHeight); + [self setFrame: rect]; - newTexWidth = lRectS.size.width; - newTexHeight = lRectS.size.height; - shallDraw = [self isGLSourceValid]; - SYNC_PRINT("<SZ %dx%d>", newTexWidth, newTexHeight); + [CATransaction commit]; +} - [super resizeWithOldSuperlayerSize: size]; +- (void) setFrame:(CGRect) frame { + CGRect rect = CGRectMake(0, 0, dedicatedWidth, dedicatedHeight); + [super setFrame: rect]; +} + +- (CGRect)fixMyFrame +{ + CGRect lRect = [self frame]; + + // With Java7 our root CALayer's frame gets off-limit -> force 0/0 origin! + if( lRect.origin.x!=0 || lRect.origin.y!=0 || lRect.size.width!=texWidth || lRect.size.height!=texHeight) { + DBG_PRINT("MyNSOpenGLLayer::fixMyFrame: %p, 0/0 texSize %dx%d -> Frame[%lf/%lf %lfx%lf]\n", + self, texWidth, texHeight, + lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + + lRect.origin.x = 0; + lRect.origin.y = 0; + lRect.size.width=texWidth; + lRect.size.height=texHeight; + [self setFrame: lRect]; + + [CATransaction commit]; + } + return lRect; +} + +- (CGRect)fixSuperPosition +{ + CALayer * superL = [self superlayer]; + CGRect lRect = [superL frame]; + + // With Java7 our root CALayer's frame gets off-limit -> force 0/0 origin! + if( lRect.origin.x!=0 || lRect.origin.y!=0 ) { + DBG_PRINT("MyNSOpenGLLayer::fixSuperPosition: %p, 0/0 -> Super Frame[%lf/%lf %lfx%lf]\n", + self, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + + lRect.origin.x = 0; + lRect.origin.y = 0; + [superL setPosition: lRect.origin]; + + [CATransaction commit]; + } + return lRect; +} + +- (id<CAAction>)actionForKey:(NSString *)key +{ + DBG_PRINT("MyNSOpenGLLayer::actionForKey.0 %p key %s -> NIL\n", self, [key UTF8String]); + return nil; + // return [super actionForKey: key]; +} + +- (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask +{ + DBG_PRINT("MyNSOpenGLLayer::openGLPixelFormatForDisplayMask: %p (refcnt %d) - parent-pfmt %p -> new-pfmt %p\n", + self, (int)[self retainCount], parentPixelFmt, parentPixelFmt); + // We simply take over ownership of parent PixelFormat .. + return parentPixelFmt; +} + +- (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat +{ + DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.0: %p (refcnt %d) - pfmt %p, parent %p, DisplayLink %p\n", + self, (int)[self retainCount], pixelFormat, parentCtx, displayLink); + // NSLog(@"MyNSOpenGLLayer::openGLContextForPixelFormat: %@",[NSThread callStackSymbols]); + myCtx = [[MyNSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:parentCtx]; +#ifndef HAS_CADisplayLink + if(NULL != displayLink) { + CVReturn cvres; + DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.1: setup DisplayLink %p\n", displayLink); + cvres = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [myCtx CGLContextObj], [pixelFormat CGLPixelFormatObj]); + if(kCVReturnSuccess != cvres) { + DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext failed: %d\n", self, cvres); + } + } +#endif + DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.X: new-ctx %p\n", myCtx); + return myCtx; } - (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp { - SYNC_PRINT("<? %d>", (int)shallDraw); - return shallDraw; + SYNC_PRINT("<? %d, %d>", (int)shallDraw, (int)isGLEnabled); + return shallDraw && isGLEnabled; } - (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat @@ -503,7 +661,7 @@ static const GLfloat gl_verts[] = { SYNC_PRINT("<* "); // NSLog(@"MyNSOpenGLLayer::DRAW: %@",[NSThread callStackSymbols]); - if( shallDraw && ( NULL != pbuffer || NULL != newPBuffer || 0 != textureID ) ) { + if( isGLEnabled && shallDraw && ( NULL != pbuffer || NULL != newPBuffer || 0 != textureID ) ) { [context makeCurrentContext]; if( NULL != newPBuffer ) { // volatile OK @@ -512,7 +670,7 @@ static const GLfloat gl_verts[] = { GLenum textureTarget; - Bool texSizeChanged = [self validateTexSizeWithNewSize]; + Bool texSizeChanged = [self validateTexSizeWithDedicatedSize]; if( NULL != pbuffer ) { if( texSizeChanged && 0 != textureID ) { @@ -752,32 +910,34 @@ static const GLfloat gl_verts[] = { @end NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, int gl3ShaderProgramName, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* p, uint32_t texID, Bool opaque, int texWidth, int texHeight) { - // This simply crashes after dealloc() has been called .. ie. ref-count -> 0 too early ? - // However using alloc/init, actual dealloc happens at JAWT destruction, hence too later IMHO. - // return [[MyNSOpenGLLayer layer] setupWithContext:ctx pixelFormat: fmt pbuffer: p texIDArg: (GLuint)texID - // opaque: opaque texWidth: texWidth texHeight: texHeight]; - return [[[MyNSOpenGLLayer alloc] init] setupWithContext:ctx gl3ShaderProgramName: (GLuint)gl3ShaderProgramName pixelFormat: fmt pbuffer: p texIDArg: (GLuint)texID opaque: opaque texWidth: texWidth texHeight: texHeight]; } + +void setNSOpenGLLayerEnabled(NSOpenGLLayer* layer, Bool enable) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; + [l setGLEnabled: enable]; + [pool release]; +} void setNSOpenGLLayerSwapInterval(NSOpenGLLayer* layer, int interval) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; [l setSwapInterval: interval]; [pool release]; } void waitUntilNSOpenGLLayerIsReady(NSOpenGLLayer* layer, long to_micros) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; [l waitUntilRenderSignal: to_micros]; [pool release]; } void setNSOpenGLLayerNeedsDisplayFBO(NSOpenGLLayer* layer, uint32_t texID) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; Bool shallDraw; // volatile OK @@ -799,8 +959,8 @@ void setNSOpenGLLayerNeedsDisplayFBO(NSOpenGLLayer* layer, uint32_t texID) { } void setNSOpenGLLayerNeedsDisplayPBuffer(NSOpenGLLayer* layer, NSOpenGLPixelBuffer* p) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; Bool shallDraw; if( NO == [l isSamePBuffer: p] ) { @@ -825,16 +985,10 @@ void setNSOpenGLLayerNeedsDisplayPBuffer(NSOpenGLLayer* layer, NSOpenGLPixelBuff } void releaseNSOpenGLLayer(NSOpenGLLayer* layer) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.0: %p\n", l); - - if ( [NSThread isMainThread] == YES ) { - [l releaseLayer]; - } else { - [l performSelectorOnMainThread:@selector(releaseLayer) withObject:nil waitUntilDone:NO]; - } - + [l releaseLayer]; DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.X: %p\n", l); [pool release]; } diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface.m index e8925f8e8..f8faeb8d0 100644 --- a/src/jogl/native/macosx/MacOSXWindowSystemInterface.m +++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface.m @@ -575,7 +575,6 @@ void setContextView(NSOpenGLContext* ctx, NSView* view) { [ctx setView:view]; } [pool release]; - return ctx; } Bool makeCurrentContext(NSOpenGLContext* ctx) { 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/com/jogamp/nativewindow/awt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java index 8527a0200..d65f8c231 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java @@ -130,7 +130,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, if(changed) { if(DEBUG) { System.err.println("JAWTWindow.updateBounds: "+bounds+" -> "+jb); - Thread.dumpStack(); + // Thread.dumpStack(); } bounds.setX(jawtBounds.getX()); bounds.setY(jawtBounds.getY()); @@ -205,7 +205,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } try { if(DEBUG) { - System.err.println("JAWTWindow.attachSurfaceHandle(): 0x"+Long.toHexString(layerHandle) + ", bounds "+bounds); + System.err.println("JAWTWindow.attachSurfaceHandle: "+toHexString(layerHandle) + ", bounds "+bounds); } attachSurfaceLayerImpl(layerHandle); offscreenSurfaceLayer = layerHandle; @@ -216,6 +216,17 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, protected abstract void attachSurfaceLayerImpl(final long layerHandle); @Override + public void layoutSurfaceLayer() throws NativeWindowException { + if( !isOffscreenLayerSurfaceEnabled() ) { + throw new NativeWindowException("Not an offscreen layer surface"); + } + if( 0 != offscreenSurfaceLayer) { + layoutSurfaceLayerImpl(); + } + } + protected void layoutSurfaceLayerImpl() {} + + @Override public final void detachSurfaceLayer() throws NativeWindowException { if( !isOffscreenLayerSurfaceEnabled() ) { throw new java.lang.UnsupportedOperationException("Not an offscreen layer surface"); @@ -229,7 +240,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } try { if(DEBUG) { - System.err.println("JAWTWindow.detachSurfaceHandle(): 0x"+Long.toHexString(offscreenSurfaceLayer)); + System.err.println("JAWTWindow.detachSurfaceHandle(): osh "+toHexString(offscreenSurfaceLayer)); } detachSurfaceLayerImpl(offscreenSurfaceLayer); offscreenSurfaceLayer = 0; @@ -341,7 +352,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, if(LOCK_SUCCESS == res && drawable_old != drawable) { res = LOCK_SURFACE_CHANGED; if(DEBUG) { - System.err.println("JAWTWindow: surface change 0x"+Long.toHexString(drawable_old)+" -> 0x"+Long.toHexString(drawable)); + System.err.println("JAWTWindow: surface change "+toHexString(drawable_old)+" -> "+toHexString(drawable)); // Thread.dumpStack(); } } @@ -549,8 +560,8 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, StringBuilder sb = new StringBuilder(); sb.append("JAWT-Window["+ - "windowHandle 0x"+Long.toHexString(getWindowHandle())+ - ", surfaceHandle 0x"+Long.toHexString(getSurfaceHandle())+ + "windowHandle "+toHexString(getWindowHandle())+ + ", surfaceHandle "+toHexString(getSurfaceHandle())+ ", bounds "+bounds+", insets "+insets+ ", shallUseOffscreenLayer "+shallUseOffscreenLayer+", isOffscreenLayerSurface "+isOffscreenLayerSurface); if(null!=component) { @@ -566,4 +577,8 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, return sb.toString(); } + + protected final String toHexString(long l) { + return "0x"+Long.toHexString(l); + } } diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java index 2b49f6745..5c4fd82d2 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java @@ -38,7 +38,6 @@ import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import javax.media.nativewindow.AbstractGraphicsScreen; -import javax.media.nativewindow.DefaultGraphicsScreen; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowFactory; @@ -49,7 +48,6 @@ import com.jogamp.common.util.VersionNumber; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsDevice; -import com.jogamp.nativewindow.x11.X11GraphicsScreen; import jogamp.nativewindow.macosx.OSXUtil; import jogamp.nativewindow.x11.X11Lib; @@ -484,6 +482,23 @@ public class SWTAccessor { } } + /** + * Runs the specified action on the SWT UI thread. + * <p> + * If <code>display</code> is disposed or the current thread is the SWT UI thread + * {@link #invoke(boolean, Runnable)} is being used. + * @see #invoke(boolean, Runnable) + */ + public static void invoke(org.eclipse.swt.widgets.Display display, boolean wait, Runnable runnable) { + if( display.isDisposed() || Thread.currentThread() == display.getThread() ) { + invoke(wait, runnable); + } else if( wait ) { + display.syncExec(runnable); + } else { + display.asyncExec(runnable); + } + } + // // Specific X11 GTK ChildWindow - Using plain X11 native parenting (works well) // 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/OffscreenLayerSurface.java b/src/nativewindow/classes/javax/media/nativewindow/OffscreenLayerSurface.java index f6bc5822b..4885d5a4c 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/OffscreenLayerSurface.java +++ b/src/nativewindow/classes/javax/media/nativewindow/OffscreenLayerSurface.java @@ -33,11 +33,34 @@ package javax.media.nativewindow; public interface OffscreenLayerSurface { /** * Attach the offscreen layer to this offscreen layer surface. + * <p> + * Implementation may realize all required resources at this point. + * </p> + * <p> + * It is mandatory that any related resources, e.g. a shared context, + * are not locked while calling this method. + * </p> + * * @see #isOffscreenLayerSurfaceEnabled() * @throws NativeWindowException if {@link #isOffscreenLayerSurfaceEnabled()} == false */ public void attachSurfaceLayer(final long layerHandle) throws NativeWindowException; + /** + * Layout the offscreen layer according to the implementing class's constraints. + * <p> + * This method allows triggering a re-layout of the offscreen surface + * in case the implementation requires it. + * </p> + * <p> + * Call this method if any parent or ancestor's layout has been changed, + * which could affects the layout of this surface. + * </p> + * @see #isOffscreenLayerSurfaceEnabled() + * @throws NativeWindowException if {@link #isOffscreenLayerSurfaceEnabled()} == false + */ + public void layoutSurfaceLayer() throws NativeWindowException; + /** * Detaches a previously attached offscreen layer from this offscreen layer surface. * @see #attachSurfaceLayer(long) 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/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java index 9e270d403..3ec54ca78 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java @@ -51,6 +51,7 @@ import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.MutableSurface; import javax.media.nativewindow.util.Point; +import com.jogamp.common.util.Function; import com.jogamp.nativewindow.awt.JAWTWindow; import jogamp.nativewindow.jawt.JAWT; @@ -70,11 +71,40 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { } protected void invalidateNative() { + if(DEBUG) { + System.err.println("MacOSXJAWTWindow.invalidateNative(): osh-enabled "+isOffscreenLayerSurfaceEnabled()+ + ", osh-set "+offscreenSurfaceHandleSet+ + ", osh "+toHexString(offscreenSurfaceHandle)+ + ", rsh "+toHexString(rootSurfaceLayerHandle)+ + ", wh "+toHexString(windowHandle)); + } offscreenSurfaceHandle=0; offscreenSurfaceHandleSet=false; - if(isOffscreenLayerSurfaceEnabled()) { + if( isOffscreenLayerSurfaceEnabled() ) { if(0 != rootSurfaceLayerHandle) { + final JAWT jawt = getJAWT(); + if( null != jawt ) { + final JAWT_DrawingSurface ds = jawt.GetDrawingSurface(component); + if (ds != null) { + if ( 0 == ( ds.Lock() & JAWTFactory.JAWT_LOCK_ERROR ) ) { + JAWT_DrawingSurfaceInfo dsi = null; + try { + dsi = ds.GetDrawingSurfaceInfo(); + if(! UnsetJAWTRootSurfaceLayer(dsi.getBuffer(), rootSurfaceLayerHandle)) { + System.err.println("Error clearing JAWT rootSurfaceLayerHandle "+toHexString(rootSurfaceLayerHandle)); + } + } finally { + if ( null != dsi ) { + ds.FreeDrawingSurfaceInfo(dsi); + } + ds.Unlock(); + } + } + jawt.FreeDrawingSurface(ds); + } + } OSXUtil.DestroyCALayer(rootSurfaceLayerHandle); + rootSurfaceLayerHandle = 0; } if(0 != windowHandle) { @@ -88,10 +118,20 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { OSXUtil.AddCASublayer(rootSurfaceLayerHandle, layerHandle); } + protected void layoutSurfaceLayerImpl() { + final long osl = getAttachedSurfaceLayer(); + final int w = getWidth(); + final int h = getHeight(); + if(DEBUG) { + System.err.println("JAWTWindow.fixSurfaceLayerLayout: "+toHexString(osl) + ", bounds "+bounds+", "+w+"x"+h); + } + OSXUtil.FixCALayerLayout(rootSurfaceLayerHandle, osl, w, h); + } + protected void detachSurfaceLayerImpl(final long layerHandle) { OSXUtil.RemoveCASublayer(rootSurfaceLayerHandle, layerHandle); } - + @Override public final long getWindowHandle() { return windowHandle; @@ -107,7 +147,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { throw new java.lang.UnsupportedOperationException("Not using CALAYER"); } if(DEBUG) { - System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); + System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): "+toHexString(surfaceHandle)); } this.offscreenSurfaceHandle = surfaceHandle; this.offscreenSurfaceHandleSet = true; @@ -117,6 +157,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { // use offscreen if supported and [ applet or requested ] return JAWTUtil.getJAWT(getShallUseOffscreenLayer() || isApplet()); } + protected int lockSurfaceImpl() throws NativeWindowException { int ret = NativeWindow.LOCK_SURFACE_NOT_READY; ds = getJAWT().GetDrawingSurface(component); @@ -189,7 +230,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { } else { drawable = OSXUtil.GetNSView(windowHandle); if(0 == drawable) { - errMsg = "Null NSView of NSWindow 0x"+Long.toHexString(windowHandle); + errMsg = "Null NSView of NSWindow "+toHexString(windowHandle); } } if(null == errMsg) { @@ -204,8 +245,8 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { rootSurfaceLayerHandle = OSXUtil.CreateCALayer(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); if(0 == rootSurfaceLayerHandle) { errMsg = "Could not create root CALayer"; - } else if(!SetJAWTRootSurfaceLayer0(dsi.getBuffer(), rootSurfaceLayerHandle)) { - errMsg = "Could not set JAWT rootSurfaceLayerHandle 0x"+Long.toHexString(rootSurfaceLayerHandle); + } else if(!SetJAWTRootSurfaceLayer(dsi.getBuffer(), rootSurfaceLayerHandle)) { + errMsg = "Could not set JAWT rootSurfaceLayerHandle "+toHexString(rootSurfaceLayerHandle); } } } @@ -265,9 +306,23 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { return getLocationOnScreenNonBlocking(storage, component); } protected Point getLocationOnScreenNativeImpl(final int x0, final int y0) { return null; } + + private static boolean SetJAWTRootSurfaceLayer(final Buffer jawtDrawingSurfaceInfoBuffer, final long caLayer) { + return OSXUtil.RunOnMainThread(true, new Function<Boolean, Object>() { + public Boolean eval(Object... args) { + return Boolean.valueOf( SetJAWTRootSurfaceLayer0(jawtDrawingSurfaceInfoBuffer, caLayer) ); + } } ).booleanValue(); + } + + private static boolean UnsetJAWTRootSurfaceLayer(final Buffer jawtDrawingSurfaceInfoBuffer, final long caLayer) { + return OSXUtil.RunOnMainThread(true, new Function<Boolean, Object>() { + public Boolean eval(Object... args) { + return Boolean.valueOf( UnsetJAWTRootSurfaceLayer0(jawtDrawingSurfaceInfoBuffer, caLayer) ); + } } ).booleanValue(); + } private static native boolean SetJAWTRootSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); - // private static native boolean UnsetJAWTRootSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); + private static native boolean UnsetJAWTRootSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); // Variables for lockSurface/unlockSurface private JAWT_DrawingSurface ds; diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index f06f97064..aa44e2d64 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -32,6 +32,8 @@ import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; +import com.jogamp.common.util.Function; +import com.jogamp.common.util.FunctionTask; import com.jogamp.common.util.RunnableTask; import jogamp.nativewindow.Debug; @@ -134,26 +136,94 @@ public class OSXUtil implements ToolkitProperties { return GetNSWindow0(nsView); } - public static long CreateCALayer(int x, int y, int width, int height) { - return CreateCALayer0(x, y, width, height); + /** + * Create a CALayer suitable to act as a root CALayer on the main-thread. + * @see #DestroyCALayer(long) + * @see #AddCASublayer(long, long) + */ + public static long CreateCALayer(final int x, final int y, final int width, final int height) { + return OSXUtil.RunOnMainThread(true, new Function<Long, Object>() { + public Long eval(Object... args) { + return Long.valueOf( CreateCALayer0(x, y, width, height) ); + } } ).longValue(); } - public static void AddCASublayer(long rootCALayer, long subCALayer) { + + /** + * Attach a sub CALayer to the root CALayer on the main-thread w/ blocking. + * <p> + * Method will trigger a <code>display</code> + * call to the CALayer hierarchy to enforce resource creation if required, e.g. an NSOpenGLContext. + * </p> + * <p> + * It is mandatory that any related resources, e.g. a shared NSOpenGLContext, + * are not locked while calling this method. + * </p> + * @see #CreateCALayer(int, int, int, int) + * @see #RemoveCASublayer(long, long) + */ + public static void AddCASublayer(final long rootCALayer, final long subCALayer) { if(0==rootCALayer || 0==subCALayer) { throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer)); } - AddCASublayer0(rootCALayer, subCALayer); + RunOnMainThread(true, new Runnable() { + public void run() { + AddCASublayer0(rootCALayer, subCALayer); + } + }); + } + + /** + * Fix root and sub CALayer position to 0/0 and size on the main-thread w/o blocking. + * <p> + * If the sub CALayer implements the Objective-C NativeWindow protocol NWDedicatedSize (e.g. JOGL's MyNSOpenGLLayer), + * the dedicated size is passed to the layer, which propagates it appropriately. + * </p> + * <p> + * On OSX/Java7 our root CALayer's frame position and size gets corrupted by its NSView, + * hence we have created the NWDedicatedSize protocol. + * </p> + * + * @param rootCALayer the root surface layer, maybe null. + * @param subCALayer the client surface layer, maybe null. + * @param width the expected width + * @param height the expected height + */ + public static void FixCALayerLayout(final long rootCALayer, final long subCALayer, final int width, final int height) { + if( 0==rootCALayer && 0==subCALayer ) { + return; + } + RunOnMainThread(false, new Runnable() { + public void run() { + FixCALayerLayout0(rootCALayer, subCALayer, width, height); + } + }); } - public static void RemoveCASublayer(long rootCALayer, long subCALayer) { + + /** + * Detach a sub CALayer from the root CALayer on the main-thread w/ blocking. + */ + public static void RemoveCASublayer(final long rootCALayer, final long subCALayer) { if(0==rootCALayer || 0==subCALayer) { throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer)); } - RemoveCASublayer0(rootCALayer, subCALayer); + RunOnMainThread(true, new Runnable() { + public void run() { + RemoveCASublayer0(rootCALayer, subCALayer); + } } ); } - public static void DestroyCALayer(long caLayer) { + + /** + * Destroy a CALayer on the main-thread w/ blocking. + * @see #CreateCALayer(int, int, int, int) + */ + public static void DestroyCALayer(final long caLayer) { if(0==caLayer) { throw new IllegalArgumentException("caLayer 0x"+Long.toHexString(caLayer)); } - DestroyCALayer0(caLayer); + RunOnMainThread(true, new Runnable() { + public void run() { + DestroyCALayer0(caLayer); + } } ); } /** @@ -169,14 +239,14 @@ public class OSXUtil implements ToolkitProperties { if( IsMainThread0() ) { runnable.run(); // don't leave the JVM } else { - if( waitUntilDone ) { - // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, - // otherwise we may freeze the OSX main thread. - Throwable throwable = null; - final Object sync = new Object(); - final RunnableTask rt = new RunnableTask( runnable, sync, true ); - synchronized(sync) { - RunOnMainThread0(rt); + // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, + // otherwise we may freeze the OSX main thread. + Throwable throwable = null; + final Object sync = new Object(); + final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true ); + synchronized(sync) { + RunOnMainThread0(rt); + if( waitUntilDone ) { try { sync.wait(); } catch (InterruptedException ie) { @@ -188,10 +258,54 @@ public class OSXUtil implements ToolkitProperties { if(null!=throwable) { throw new RuntimeException(throwable); } - } - } else { - RunOnMainThread0(runnable); + } + } + } + } + + private static Runnable _nop = new Runnable() { public void run() {}; }; + + /** Issues a {@link #RunOnMainThread(boolean, Runnable)} w/ an <i>NOP</i> runnable, while waiting until done. */ + public static void WaitUntilFinish() { + RunOnMainThread(true, _nop); + } + + /** + * Run on OSX UI main thread. + * <p> + * 'waitUntilDone' is implemented on Java site via lock/wait on {@link FunctionTask} to not freeze OSX main thread. + * </p> + * + * @param waitUntilDone + * @param func + */ + public static <R,A> R RunOnMainThread(boolean waitUntilDone, Function<R,A> func, A... args) { + if( IsMainThread0() ) { + return func.eval(args); // don't leave the JVM + } else { + // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, + // otherwise we may freeze the OSX main thread. + Throwable throwable = null; + final Object sync = new Object(); + final FunctionTask<R,A> rt = new FunctionTask<R,A>( func, waitUntilDone ? sync : null, true ); + synchronized(sync) { + rt.setArgs(args); + RunOnMainThread0(rt); + if( waitUntilDone ) { + try { + sync.wait(); + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rt.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } } + return rt.getResult(); } } @@ -236,6 +350,7 @@ public class OSXUtil implements ToolkitProperties { private static native long GetNSWindow0(long nsView); private static native long CreateCALayer0(int x, int y, int width, int height); private static native void AddCASublayer0(long rootCALayer, long subCALayer); + private static native void FixCALayerLayout0(long rootCALayer, long subCALayer, int width, int height); private static native void RemoveCASublayer0(long rootCALayer, long subCALayer); private static native void DestroyCALayer0(long caLayer); private static native void RunOnMainThread0(Runnable runnable); diff --git a/src/nativewindow/native/macosx/NativeWindowProtocols.h b/src/nativewindow/native/macosx/NativeWindowProtocols.h new file mode 100644 index 000000000..b91a50dfd --- /dev/null +++ b/src/nativewindow/native/macosx/NativeWindowProtocols.h @@ -0,0 +1,34 @@ +/** + * 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. + */ + +#import <Foundation/NSGeometry.h> + +@protocol NWDedicatedSize +- (void)setDedicatedSize:(CGSize)size; +@end + diff --git a/src/nativewindow/native/macosx/OSXmisc.m b/src/nativewindow/native/macosx/OSXmisc.m index 72bb2338c..c74d6cc58 100644 --- a/src/nativewindow/native/macosx/OSXmisc.m +++ b/src/nativewindow/native/macosx/OSXmisc.m @@ -32,13 +32,14 @@ #include <stdarg.h> #include <unistd.h> #include <AppKit/AppKit.h> +#import <QuartzCore/QuartzCore.h> +#import "NativeWindowProtocols.h" #include "NativewindowCommon.h" #include "jogamp_nativewindow_macosx_OSXUtil.h" #include "jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow.h" #include <jawt_md.h> -#import <JavaNativeFoundation.h> // #define VERBOSE 1 // @@ -49,6 +50,16 @@ #define DBG_PRINT(...) #endif +// #define VERBOSE2 1 +// +#ifdef VERBOSE2 + #define DBG_PRINT2(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +#else + #define DBG_PRINT2(...) +#endif + +// #define DBG_LIFECYCLE 1 + static const char * const ClazzNameRunnable = "java/lang/Runnable"; static jmethodID runnableRunID = NULL; @@ -313,6 +324,71 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetNSWindow0 return res; } +/** + * Track lifecycle via DBG_PRINT messages, if VERBOSE is enabled! + */ +@interface MyCALayer: CALayer +{ +} +- (id)init; +#ifdef DBG_LIFECYCLE +- (id)retain; +- (oneway void)release; +- (void)dealloc; +#endif +- (id<CAAction>)actionForKey:(NSString *)key ; + +@end + +@implementation MyCALayer + +- (id)init +{ + DBG_PRINT("MyCALayer.0\n"); + MyCALayer * o = [super init]; + DBG_PRINT("MyNSOpenGLContext.init.X: new %p\n", o); + DBG_PRINT("MyCALayer.0\n"); + return o; +} + +#ifdef DBG_LIFECYCLE + +- (id)retain +{ + DBG_PRINT("MyCALayer::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyCALayer::retain: %@",[NSThread callStackSymbols]); + id o = [super retain]; + DBG_PRINT("MyCALayer::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]); + return o; +} + +- (oneway void)release +{ + DBG_PRINT("MyCALayer::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyCALayer::release: %@",[NSThread callStackSymbols]); + [super release]; + // DBG_PRINT("MyCALayer::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]); +} + +- (void)dealloc +{ + DBG_PRINT("MyCALayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyCALayer::dealloc: %@",[NSThread callStackSymbols]); + [super dealloc]; + // DBG_PRINT("MyCALayer.dealloc.X: %p\n", self); +} + +#endif + +- (id<CAAction>)actionForKey:(NSString *)key +{ + DBG_PRINT("MyCALayer::actionForKey.0 %p key %s -> NIL\n", self, [key UTF8String]); + return nil; + // return [super actionForKey: key]; +} + +@end + /* * Class: Java_jogamp_nativewindow_macosx_OSXUtil * Method: CreateCALayer0 @@ -323,8 +399,8 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0 { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - CALayer* layer = [[CALayer alloc] init]; - DBG_PRINT("CALayer::CreateCALayer.0: %p %d/%d %dx%d (refcnt %d)\n", layer, (int)x, (int)y, (int)width, (int)height, (int)[layer retainCount]); + MyCALayer* layer = [[MyCALayer alloc] init]; + DBG_PRINT("CALayer::CreateCALayer.0: root %p %d/%d %dx%d (refcnt %d)\n", layer, (int)x, (int)y, (int)width, (int)height, (int)[layer retainCount]); // avoid zero size if(0 == width) { width = 32; } if(0 == height) { height = 32; } @@ -339,12 +415,12 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0 // no animations for add/remove/swap sublayers etc // doesn't work: [layer removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition] [layer removeAllAnimations]; + // [layer addAnimation:nil forKey:kCATransition]; [layer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; [layer setNeedsDisplayOnBoundsChange: YES]; - DBG_PRINT("CALayer::CreateCALayer.1: %p %lf/%lf %lfx%lf\n", layer, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); - DBG_PRINT("CALayer::CreateCALayer.X: %p (refcnt %d)\n", layer, (int)[layer retainCount]); - + DBG_PRINT("CALayer::CreateCALayer.1: root %p %lf/%lf %lfx%lf\n", layer, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); [pool release]; + DBG_PRINT("CALayer::CreateCALayer.X: root %p (refcnt %d)\n", layer, (int)[layer retainCount]); return (jlong) ((intptr_t) layer); } @@ -357,41 +433,105 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_AddCASublayer0 (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer) { - JNF_COCOA_ENTER(env); - CALayer* rootLayer = (CALayer*) ((intptr_t) rootCALayer); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer); CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer); + [subLayer retain]; // Pairs w/ RemoveCASublayer + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + CGRect lRectRoot = [rootLayer frame]; - DBG_PRINT("CALayer::AddCASublayer0.0: Origin %p frame0: %lf/%lf %lfx%lf\n", + DBG_PRINT("CALayer::AddCASublayer0.0: Origin %p frame0: %lf/%lf %lfx%lf\n", rootLayer, lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height); if(lRectRoot.origin.x!=0 || lRectRoot.origin.y!=0) { lRectRoot.origin.x = 0; lRectRoot.origin.y = 0; - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - [rootLayer setFrame: lRectRoot]; - }]; + [rootLayer setFrame: lRectRoot]; DBG_PRINT("CALayer::AddCASublayer0.1: Origin %p frame*: %lf/%lf %lfx%lf\n", rootLayer, lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height); } - DBG_PRINT("CALayer::AddCASublayer0.2: %p . %p %lf/%lf %lfx%lf (refcnt %d)\n", - rootLayer, subLayer, lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height, (int)[subLayer retainCount]); - - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - // simple 1:1 layout ! - [subLayer setFrame:lRectRoot]; - [rootLayer addSublayer:subLayer]; - - // no animations for add/remove/swap sublayers etc - // doesn't work: [layer removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition] - [rootLayer removeAllAnimations]; - [rootLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; - [rootLayer setNeedsDisplayOnBoundsChange: YES]; - [subLayer removeAllAnimations]; - [subLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; - [subLayer setNeedsDisplayOnBoundsChange: YES]; - }]; - DBG_PRINT("CALayer::AddCASublayer0.X: %p . %p (refcnt %d)\n", rootLayer, subLayer, (int)[subLayer retainCount]); - JNF_COCOA_EXIT(env); + DBG_PRINT("CALayer::AddCASublayer0.2: root %p (refcnt %d) .sub %p %lf/%lf %lfx%lf (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], + subLayer, lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height, (int)[subLayer retainCount]); + + // simple 1:1 layout ! + [subLayer setFrame:lRectRoot]; + [rootLayer addSublayer:subLayer]; + + // no animations for add/remove/swap sublayers etc + // doesn't work: [layer removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition] + [rootLayer removeAllAnimations]; + // [rootLayer addAnimation:nil forKey:kCATransition]; // JAU + [rootLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; + [rootLayer setNeedsDisplayOnBoundsChange: YES]; + [subLayer removeAllAnimations]; + // [subLayer addAnimation:nil forKey:kCATransition]; // JAU + [subLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; + [subLayer setNeedsDisplayOnBoundsChange: YES]; + + // Trigger display and hence ctx creation. + // The latter is essential since since the parent-context lock is cleared + // only for this window of time (method call). + [rootLayer setNeedsDisplay]; + [rootLayer displayIfNeeded]; + + [CATransaction commit]; + + [pool release]; + DBG_PRINT("CALayer::AddCASublayer0.X: root %p (refcnt %d) .sub %p (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]); +} + +/* + * Class: Java_jogamp_nativewindow_macosx_OSXUtil + * Method: FixCALayerLayout0 + * Signature: (JJII)V + */ +JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_FixCALayerLayout0 + (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer, jint width, jint height) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer); + CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer); + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + + if( NULL != rootLayer ) { + CGRect lRect = [rootLayer frame]; + if(lRect.origin.x!=0 || lRect.origin.y!=0 || lRect.size.width!=width || lRect.size.height!=height) { + DBG_PRINT("CALayer::FixCALayerLayout0.0: Root %p exp 0/0 %dx%d -> frame0: %lf/%lf %lfx%lf\n", + rootLayer, (int)width, (int)height, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); + lRect.origin.x = 0; + lRect.origin.y = 0; + lRect.size.width = width; + lRect.size.height = height; + [rootLayer setFrame: lRect]; + } + } + if( NULL != subLayer ) { + CGRect lRect = [subLayer frame]; + if(lRect.origin.x!=0 || lRect.origin.y!=0 || lRect.size.width!=width || lRect.size.height!=height) { + DBG_PRINT("CALayer::FixCALayerLayout0.0: SubL %p exp 0/0 %dx%d -> frame0: %lf/%lf %lfx%lf\n", + subLayer, (int)width, (int)height, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); + lRect.origin.x = 0; + lRect.origin.y = 0; + lRect.size.width = width; + lRect.size.height = height; + if( [subLayer conformsToProtocol:@protocol(NWDedicatedSize)] ) { + CALayer <NWDedicatedSize> * subLayerDS = (CALayer <NWDedicatedSize> *) subLayer; + [subLayerDS setDedicatedSize: lRect.size]; + } else { + [subLayer setFrame: lRect]; + } + } + } + + [CATransaction commit]; + + [pool release]; } /* @@ -402,18 +542,26 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_AddCASublayer0 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RemoveCASublayer0 (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer) { - JNF_COCOA_ENTER(env); - CALayer* rootLayer = (CALayer*) ((intptr_t) rootCALayer); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer); CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer); (void)rootLayer; // no warnings - DBG_PRINT("CALayer::RemoveCASublayer0.0: %p . %p (refcnt %d)\n", rootLayer, subLayer, (int)[subLayer retainCount]); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - [subLayer removeFromSuperlayer]; - }]; - DBG_PRINT("CALayer::RemoveCASublayer0.X: %p . %p\n", rootLayer, subLayer); - JNF_COCOA_EXIT(env); + DBG_PRINT("CALayer::RemoveCASublayer0.0: root %p (refcnt %d) .sub %p (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]); + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + + [subLayer removeFromSuperlayer]; + [subLayer release]; // Pairs w/ AddCASublayer + + [CATransaction commit]; + + [pool release]; + DBG_PRINT("CALayer::RemoveCASublayer0.X: root %p (refcnt %d) .sub %p (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]); } /* @@ -424,15 +572,13 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RemoveCASublayer0 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_DestroyCALayer0 (JNIEnv *env, jclass unused, jlong caLayer) { - JNF_COCOA_ENTER(env); - CALayer* layer = (CALayer*) ((intptr_t) caLayer); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyCALayer* layer = (MyCALayer*) ((intptr_t) caLayer); - DBG_PRINT("CALayer::DestroyCALayer0.0: %p (refcnt %d)\n", layer, (int)[layer retainCount]); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - [layer release]; // performs release! - }]; - DBG_PRINT("CALayer::DestroyCALayer0.X: %p\n", layer); - JNF_COCOA_EXIT(env); + DBG_PRINT("CALayer::DestroyCALayer0.0: root %p (refcnt %d)\n", layer, (int)[layer retainCount]); + [layer release]; // Trigger release of root CALayer + [pool release]; + DBG_PRINT("CALayer::DestroyCALayer0.X: root %p\n", layer); } /* @@ -443,20 +589,18 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_DestroyCALayer0 JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow_SetJAWTRootSurfaceLayer0 (JNIEnv *env, jclass unused, jobject jawtDrawingSurfaceInfoBuffer, jlong caLayer) { - JNF_COCOA_ENTER(env); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; JAWT_DrawingSurfaceInfo* dsi = (JAWT_DrawingSurfaceInfo*) (*env)->GetDirectBufferAddress(env, jawtDrawingSurfaceInfoBuffer); if (NULL == dsi) { NativewindowCommon_throwNewRuntimeException(env, "Argument \"jawtDrawingSurfaceInfoBuffer\" was not a direct buffer"); return JNI_FALSE; } - CALayer* layer = (CALayer*) (intptr_t) caLayer; - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)dsi->platformInfo; - DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.0: %p -> %p (refcnt %d)\n", surfaceLayers.layer, layer, (int)[layer retainCount]); - surfaceLayers.layer = layer; // already incr. retain count - DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.X: %p (refcnt %d)\n", layer, (int)[layer retainCount]); - }]; - JNF_COCOA_EXIT(env); + MyCALayer* layer = (MyCALayer*) (intptr_t) caLayer; + id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)dsi->platformInfo; + DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.0: pre %p -> root %p (refcnt %d)\n", surfaceLayers.layer, layer, (int)[layer retainCount]); + surfaceLayers.layer = [layer retain]; // Pairs w/ Unset + [pool release]; + DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.X: root %p (refcnt %d)\n", layer, (int)[layer retainCount]); return JNI_TRUE; } @@ -464,33 +608,29 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow * Class: Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow * Method: UnsetJAWTRootSurfaceLayer0 * Signature: (JJ)Z + */ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow_UnsetJAWTRootSurfaceLayer0 (JNIEnv *env, jclass unused, jobject jawtDrawingSurfaceInfoBuffer, jlong caLayer) { - JNF_COCOA_ENTER(env); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; JAWT_DrawingSurfaceInfo* dsi = (JAWT_DrawingSurfaceInfo*) (*env)->GetDirectBufferAddress(env, jawtDrawingSurfaceInfoBuffer); if (NULL == dsi) { NativewindowCommon_throwNewRuntimeException(env, "Argument \"jawtDrawingSurfaceInfoBuffer\" was not a direct buffer"); return JNI_FALSE; } - CALayer* layer = (CALayer*) (intptr_t) caLayer; - { - id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)dsi->platformInfo; - if(layer != surfaceLayers.layer) { - NativewindowCommon_throwNewRuntimeException(env, "Attached layer %p doesn't match given layer %p\n", surfaceLayers.layer, layer); - return JNI_FALSE; - } + MyCALayer* layer = (MyCALayer*) (intptr_t) caLayer; + id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)dsi->platformInfo; + if(layer != surfaceLayers.layer) { + NativewindowCommon_throwNewRuntimeException(env, "Attached layer %p doesn't match given layer %p\n", surfaceLayers.layer, layer); + return JNI_FALSE; } - // [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)dsi->platformInfo; - DBG_PRINT("CALayer::detachJAWTSurfaceLayer: (%p) %p -> NULL\n", layer, surfaceLayers.layer); - surfaceLayers.layer = NULL; - [layer release]; - // }]; - JNF_COCOA_EXIT(env); + DBG_PRINT("CALayer::UnsetJAWTRootSurfaceLayer.0: root %p (refcnt %d) -> nil\n", layer, (int)[layer retainCount]); + [layer release]; // Pairs w/ Set + surfaceLayers.layer = NULL; + [pool release]; + DBG_PRINT("CALayer::UnsetJAWTRootSurfaceLayer.X: root %p (refcnt %d) -> nil\n", layer, (int)[layer retainCount]); return JNI_TRUE; } - */ @interface MainRunnable : NSObject @@ -503,6 +643,13 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow - (id) initWithRunnable: (jobject)runnable jvmHandle: (JavaVM*)jvm jvmVersion: (int)jvmVers; - (void) jRun; +#ifdef DBG_LIFECYCLE +- (id)retain; +- (oneway void)release; +- (void)dealloc; +#endif + + @end @implementation MainRunnable @@ -519,20 +666,47 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow { int shallBeDetached = 0; JNIEnv* env = NativewindowCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); - DBG_PRINT("MainRunnable.0 env: %d\n", (int)(NULL!=env)); + DBG_PRINT2("MainRunnable.1 env: %d\n", (int)(NULL!=env)); if(NULL!=env) { - DBG_PRINT("MainRunnable.1.0\n"); + DBG_PRINT2("MainRunnable.1.0\n"); (*env)->CallVoidMethod(env, runnableObj, runnableRunID); - DBG_PRINT("MainRunnable.1.1\n"); + DBG_PRINT2("MainRunnable.1.1\n"); + (*env)->DeleteGlobalRef(env, runnableObj); if (shallBeDetached) { - DBG_PRINT("MainRunnable.1.2\n"); + DBG_PRINT2("MainRunnable.1.3\n"); (*jvmHandle)->DetachCurrentThread(jvmHandle); } } - DBG_PRINT("MainRunnable.X\n"); + DBG_PRINT2("MainRunnable.X\n"); } +#ifdef DBG_LIFECYCLE + +- (id)retain +{ + DBG_PRINT2("MainRunnable::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + id o = [super retain]; + DBG_PRINT2("MainRunnable::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]); + return o; +} + +- (oneway void)release +{ + DBG_PRINT2("MainRunnable::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + [super release]; + // DBG_PRINT2("MainRunnable::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]); +} + +- (void)dealloc +{ + DBG_PRINT2("MainRunnable::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]); + [super dealloc]; + // DBG_PRINT2("MainRunnable.dealloc.X: %p\n", self); +} + +#endif + @end @@ -546,11 +720,11 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunOnMainThread0 { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - DBG_PRINT( "RunOnMainThread0: isMainThread %d, NSApp %d, NSApp-isRunning %d\n", + DBG_PRINT2( "RunOnMainThread0: isMainThread %d, NSApp %d, NSApp-isRunning %d\n", (int)([NSThread isMainThread]), (int)(NULL!=NSApp), (int)([NSApp isRunning])); if ( NO == [NSThread isMainThread] ) { - jobject runnableGlob = (*env)->NewGlobalRef(env, runnable); + jobject runnableObj = (*env)->NewGlobalRef(env, runnable); JavaVM *jvmHandle = NULL; int jvmVersion = 0; @@ -561,18 +735,18 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunOnMainThread0 jvmVersion = (*env)->GetVersion(env); } - DBG_PRINT( "RunOnMainThread0.1.0\n"); - MainRunnable * mr = [[MainRunnable alloc] initWithRunnable: runnableGlob jvmHandle: jvmHandle jvmVersion: jvmVersion]; + DBG_PRINT2( "RunOnMainThread0.1.0\n"); + MainRunnable * mr = [[MainRunnable alloc] initWithRunnable: runnableObj jvmHandle: jvmHandle jvmVersion: jvmVersion]; [mr performSelectorOnMainThread:@selector(jRun) withObject:nil waitUntilDone:NO]; + DBG_PRINT2( "RunOnMainThread0.1.1\n"); [mr release]; - DBG_PRINT( "RunOnMainThread0.1.1\n"); + DBG_PRINT2( "RunOnMainThread0.1.2\n"); - (*env)->DeleteGlobalRef(env, runnableGlob); } else { - DBG_PRINT( "RunOnMainThread0.2\n"); + DBG_PRINT2( "RunOnMainThread0.2\n"); (*env)->CallVoidMethod(env, runnable, runnableRunID); } - DBG_PRINT( "RunOnMainThread0.X\n"); + DBG_PRINT2( "RunOnMainThread0.X\n"); [pool release]; } @@ -625,8 +799,7 @@ JNIEXPORT jint JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetScreenRefreshR (JNIEnv *env, jclass unused, jint scrn_idx) { int res = 0; - JNF_COCOA_ENTER(env); - // NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSScreen *screen = NewtScreen_getNSScreenByIndex((int)scrn_idx); DBG_PRINT("GetScreenRefreshRate.0: screen %p\n", (void *)screen); if(NULL != screen) { @@ -644,9 +817,8 @@ JNIEXPORT jint JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetScreenRefreshR if(0 == res) { res = 60; // default .. (experienced on OSX 10.6.8) } - DBG_PRINT(stderr, "GetScreenRefreshRate.X: %d\n", (int)res); - // [pool release]; - JNF_COCOA_EXIT(env); + DBG_PRINT("GetScreenRefreshRate.X: %d\n", (int)res); + [pool release]; return res; } 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 89a749c51..3c10859bf 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -34,6 +34,7 @@ import java.awt.Canvas; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.KeyboardFocusManager; +import java.beans.Beans; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.Method; @@ -50,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; @@ -58,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; @@ -84,18 +86,25 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto private JAWTWindow jawtWindow = null; private boolean shallUseOffscreenLayer = false; private Window newtChild = null; + 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> @@ -181,6 +190,10 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto WindowListener clearAWTMenusOnNewtFocus = new WindowAdapter() { @Override + public void windowResized(WindowEvent e) { + updateLayoutSize(); + } + @Override public void windowGainedFocus(WindowEvent arg0) { if( isParent() && !isFullscreen() ) { MenuSelectionManager.defaultManager().clearSelectedPath(); @@ -203,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 } } @@ -233,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); @@ -292,16 +305,26 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto final java.awt.Container cont = AWTMisc.getContainer(this); // remove old one if(null != newtChild) { - reparentWindow( false, cont ); + detachNewtChild( cont ); newtChild = null; } // add new one, reparent only if ready newtChild = newChild; - if( isDisplayable() && null != newChild) { - reparentWindow( true, cont ); - } + + updateLayoutSize(); + // will be done later at paint/display/..: attachNewtChild(cont); + return prevChild; } + + private final void updateLayoutSize() { + if( null != newtChild ) { + // use NEWT child's size for min/pref size! + java.awt.Dimension minSize = new java.awt.Dimension(newtChild.getWidth(), newtChild.getHeight()); + setMinimumSize(minSize); + setPreferredSize(minSize); + } + } /** @return the current NEWT child */ public Window getNEWTChild() { @@ -320,149 +343,44 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto return awtWindowClosingProtocol.setDefaultCloseOperation(op); } - /* package */ void configureNewtChild(boolean attach) { - if(null!=awtAdapter) { - awtAdapter.removeFrom(this); - awtAdapter=null; - } - if(null!=awtMouseAdapter) { - awtMouseAdapter.removeFrom(this); - awtMouseAdapter = null; - } - if(null!=awtKeyAdapter) { - awtKeyAdapter.removeFrom(this); - awtKeyAdapter = null; - } - if(null != keyboardFocusManager) { - keyboardFocusManager.removePropertyChangeListener("focusOwner", focusPropertyChangeListener); - keyboardFocusManager = null; - } - - if( null != newtChild ) { - newtChild.setKeyboardFocusHandler(null); - if(attach) { - if(null == jawtWindow.getGraphicsConfiguration()) { - throw new InternalError("XXX"); - } - isOnscreen = jawtWindow.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); - awtAdapter = new AWTParentWindowAdapter(jawtWindow, newtChild).addTo(this); - 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) { - // onscreen newt child needs to fwd AWT focus - newtChild.setKeyboardFocusHandler(newtFocusTraversalKeyListener); - } else { - // offscreen newt child requires AWT to fwd AWT key/mouse event - awtMouseAdapter = new AWTMouseAdapter(newtChild).addTo(this); - awtKeyAdapter = new AWTKeyAdapter(newtChild).addTo(this); - } - } else { - newtChild.removeWindowListener(clearAWTMenusOnNewtFocus); - newtChild.setFocusAction(null); - newtChild.setDefaultCloseOperation(newtChildCloseOp); - awtWindowClosingProtocol.removeClosingListener(); - } - } - } - @Override public void addNotify() { - - // before native peer is valid: X11 - disableBackgroundErase(); - - // creates the native peer - super.addNotify(); - - // after native peer is valid: Windows - disableBackgroundErase(); - - java.awt.Container cont = AWTMisc.getContainer(this); - if(DEBUG) { - // if ( isShowing() == false ) -> Container was not visible yet. - // if ( isShowing() == true ) -> Container is already visible. - System.err.println("NewtCanvasAWT.addNotify: "+newtChild+", "+this+", visible "+isVisible()+", showing "+isShowing()+ - ", displayable "+isDisplayable()+" -> "+cont); + if( Beans.isDesignTime() ) { + super.addNotify(); + } else { + // before native peer is valid: X11 + disableBackgroundErase(); + + // creates the native peer + super.addNotify(); + + // after native peer is valid: Windows + disableBackgroundErase(); + + jawtWindow = NewtFactoryAWT.getNativeWindow(this, newtChild.getRequestedCapabilities()); + jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); + + if(DEBUG) { + // if ( isShowing() == false ) -> Container was not visible yet. + // if ( isShowing() == true ) -> Container is already visible. + System.err.println("NewtCanvasAWT.addNotify: win "+newtWinHandleToHexString(newtChild)+ + ", comp "+this+", visible "+isVisible()+", showing "+isShowing()+ + ", displayable "+isDisplayable()+", cont "+AWTMisc.getContainer(this)); + } } - reparentWindow(true, cont); + awtWindowClosingProtocol.addClosingListener(); } @Override public void removeNotify() { - java.awt.Container cont = AWTMisc.getContainer(this); - if(DEBUG) { - System.err.println("NewtCanvasAWT.removeNotify: "+newtChild+", from "+cont); - } - final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(newtChild, true); - if(null != ols && ols.isSurfaceLayerAttached()) { - ols.detachSurfaceLayer(); - } - reparentWindow(false, cont); + awtWindowClosingProtocol.removeClosingListener(); - if(null != jawtWindow) { - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - jawtWindow=null; + if( Beans.isDesignTime() ) { + super.removeNotify(); + } else { + destroyImpl(true /* removeNotify */, false /* windowClosing */); + super.removeNotify(); } - - super.removeNotify(); - } - - void reparentWindow(boolean add, java.awt.Container cont) { - if(null==newtChild) { - return; // nop - } - if(DEBUG) { - System.err.println("NewtCanvasAWT.reparentWindow.0: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); - } - - newtChild.setFocusAction(null); // no AWT focus traversal .. - if(add) { - if(DEBUG) { - System.err.println("NewtCanvasAWT.reparentWindow: newtChild: "+newtChild); - } - if(null == jawtWindow) { - jawtWindow = NewtFactoryAWT.getNativeWindow(this, newtChild.getRequestedCapabilities()); - jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); - } - final int w; - final int h; - if(isPreferredSizeSet()) { - java.awt.Dimension d = getPreferredSize(); - w = d.width; - h = d.height; - } else { - final java.awt.Dimension min; - if(this.isMinimumSizeSet()) { - min = getMinimumSize(); - } else { - min = new java.awt.Dimension(0, 0); - } - java.awt.Insets ins = cont.getInsets(); - w = Math.max(min.width, cont.getWidth() - ins.left - ins.right); - h = Math.max(min.height, cont.getHeight() - ins.top - ins.bottom); - } - setSize(w, h); - newtChild.setSize(w, h); - newtChild.reparentWindow(jawtWindow); - newtChild.setVisible(true); - configureNewtChild(true); - newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener - - // force this AWT Canvas to be focus-able, - // since this it is completely covered by the newtChild (z-order). - setFocusable(true); - } else { - configureNewtChild(false); - newtChild.setVisible(false); - newtChild.reparentWindow(null); - } - if(DEBUG) { - System.err.println("NewtCanvasAWT.reparentWindow.X: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); - } } /** @@ -472,46 +390,68 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto * <li> Disconnects the NEWT Child from this Canvas NativeWindow, reparent to NULL </li> * <li> Issues <code>destroy()</code> on the NEWT Child</li> * <li> Remove reference to the NEWT Child</li> - * <li> Remove this Canvas from it's parent.</li> * </ul> * @see Window#destroy() */ public final void destroy() { - if(null!=newtChild) { + 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); - } - configureNewtChild(false); - if(null!=jawtWindow) { - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - jawtWindow=null; - } - newtChild.setVisible(false); - newtChild.reparentWindow(null); - newtChild.destroy(); - newtChild=null; - if(null!=cont) { - cont.remove(this); + System.err.println("NewtCanvasAWT.destroy(removeNotify "+removeNotify+", windowClosing "+windowClosing+"): nw "+newtWinHandleToHexString(newtChild)+", from "+cont); } + 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 public void paint(Graphics g) { - awtWindowClosingProtocol.addClosingListenerOneShot(); - if(null!=newtChild) { + if( validateComponent(true, null) ) { newtChild.windowRepaint(0, 0, getWidth(), getHeight()); } } @Override public void update(Graphics g) { - awtWindowClosingProtocol.addClosingListenerOneShot(); - if(null!=newtChild) { + if( validateComponent(true, null) ) { newtChild.windowRepaint(0, 0, getWidth(), getHeight()); } } + @SuppressWarnings("deprecation") + @Override + public void reshape(int x, int y, int width, int height) { + synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape + super.reshape(x, y, width, height); + if(DEBUG) { + System.err.println("NewtCanvasAWT.reshape: "+x+"/"+y+" "+width+"x"+height); + } + if( validateComponent(true, null) ) { + // newtChild.setSize(width, height); + if(null != jawtWindow && jawtWindow.isOffscreenLayerSurfaceEnabled() ) { + jawtWindow.layoutSurfaceLayer(); + } + } + } + } + private final void requestFocusNEWTChild() { if(null!=newtChild) { newtChild.setFocusAction(null); @@ -556,6 +496,140 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto return res; } + private final boolean validateComponent(boolean attachNewtChild, java.awt.Container cont) { + if( Beans.isDesignTime() || !isDisplayable() ) { + return false; + } + if ( null == newtChild || null == jawtWindow ) { + return false; + } + if( null == cont ) { + cont = AWTMisc.getContainer(this); + } + if( 0 >= getWidth() || 0 >= getHeight() ) { + return false; + } + + if( attachNewtChild && !newtChildAttached && null != newtChild ) { + attachNewtChild(cont); + } + + return true; + } + + private final void configureNewtChild(boolean attach) { + if(null!=awtAdapter) { + awtAdapter.removeFrom(this); + awtAdapter=null; + } + if(null!=awtMouseAdapter) { + awtMouseAdapter.removeFrom(this); + awtMouseAdapter = null; + } + if(null!=awtKeyAdapter) { + awtKeyAdapter.removeFrom(this); + awtKeyAdapter = null; + } + if(null != keyboardFocusManager) { + keyboardFocusManager.removePropertyChangeListener("focusOwner", focusPropertyChangeListener); + keyboardFocusManager = null; + } + + if( null != newtChild ) { + newtChild.setKeyboardFocusHandler(null); + if(attach) { + if(null == jawtWindow.getGraphicsConfiguration()) { + throw new InternalError("XXX"); + } + isOnscreen = jawtWindow.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + 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); + keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + keyboardFocusManager.addPropertyChangeListener("focusOwner", focusPropertyChangeListener); + if(isOnscreen) { + // onscreen newt child needs to fwd AWT focus + newtChild.setKeyboardFocusHandler(newtFocusTraversalKeyListener); + } else { + // offscreen newt child requires AWT to fwd AWT key/mouse event + awtMouseAdapter = new AWTMouseAdapter(newtChild).addTo(this); + awtKeyAdapter = new AWTKeyAdapter(newtChild).addTo(this); + } + } else { + newtChild.removeWindowListener(clearAWTMenusOnNewtFocus); + newtChild.setFocusAction(null); + newtChild.setDefaultCloseOperation(newtChildCloseOp); + } + } + } + + private final void attachNewtChild(java.awt.Container cont) { + if( null == newtChild || null == jawtWindow || newtChildAttached ) { + return; // nop + } + if(DEBUG) { + // if ( isShowing() == false ) -> Container was not visible yet. + // if ( isShowing() == true ) -> Container is already visible. + System.err.println("NewtCanvasAWT.attachNewtChild.0: win "+newtWinHandleToHexString(newtChild)+ + ", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+ + ", comp "+this+", visible "+isVisible()+", showing "+isShowing()+", displayable "+isDisplayable()+ + ", cont "+cont); + } + + newtChildAttached = true; + newtChild.setFocusAction(null); // no AWT focus traversal .. + if(DEBUG) { + System.err.println("NewtCanvasAWT.attachNewtChild.1: newtChild: "+newtChild); + } + final int w = getWidth(); + final int h = getHeight(); + System.err.println("NewtCanvasAWT.attachNewtChild.2: size "+w+"x"+h); + newtChild.setSize(w, h); + newtChild.reparentWindow(jawtWindow); + newtChild.setVisible(true); + configureNewtChild(true); + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener + + // force this AWT Canvas to be focus-able, + // since this it is completely covered by the newtChild (z-order). + setFocusable(true); + if(DEBUG) { + System.err.println("NewtCanvasAWT.attachNewtChild.X: win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+", comp "+this); + } + } + + private final void detachNewtChild(java.awt.Container cont) { + if( null == newtChild || null == jawtWindow || !newtChildAttached ) { + return; // nop + } + if(DEBUG) { + // if ( isShowing() == false ) -> Container was not visible yet. + // if ( isShowing() == true ) -> Container is already visible. + System.err.println("NewtCanvasAWT.detachNewtChild.0: win "+newtWinHandleToHexString(newtChild)+ + ", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+ + ", comp "+this+", visible "+isVisible()+", showing "+isShowing()+", displayable "+isDisplayable()+ + ", cont "+cont); + } + + newtChildAttached = false; + newtChild.setFocusAction(null); // no AWT focus traversal .. + configureNewtChild(false); + newtChild.setVisible(false); + + // 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); + } + } + // Disables the AWT's erasing of this Canvas's background on Windows // in Java SE 6. This internal API is not available in previous // releases, but the system property 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 ad77ec79f..4920b59ea 100644 --- a/src/newt/classes/com/jogamp/newt/event/InputEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/InputEvent.java @@ -81,12 +81,7 @@ 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(int eventType, Object source, long when, int modifiers) { + protected InputEvent(short eventType, Object source, long when, int modifiers) { super(eventType, source, when); this.modifiers=modifiers; } @@ -154,16 +149,16 @@ public abstract class InputEvent extends NEWTEvent * @return Array of pressed mouse buttons [{@link MouseEvent#BUTTON1} .. {@link MouseEvent#BUTTON6}]. * If none is down, the resulting array is of length 0. */ - public final int[] getButtonsDown() { + public final short[] getButtonsDown() { int len = 0; for(int i=1; i<=MouseEvent.BUTTON_NUMBER; i++) { - if(isButtonDown(i)) { len++; } + if( isButtonDown(i) ) { len++; } } - int[] res = new int[len]; + short[] res = new short[len]; int j = 0; for(int i=1; i<=MouseEvent.BUTTON_NUMBER; i++) { - if(isButtonDown(i)) { res[j++] = ( MouseEvent.BUTTON1 - 1 ) + i; } + if( isButtonDown(i) ) { res[j++] = (short) ( ( MouseEvent.BUTTON1 - 1 ) + i ); } } return res; } diff --git a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java index ff67b7f57..f626fec38 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java @@ -36,68 +36,123 @@ package com.jogamp.newt.event; /** * Key events are delivered in the following order: - * <ol> - * <li>{@link #EVENT_KEY_PRESSED}</li> - * <li>{@link #EVENT_KEY_RELEASED}</li> - * <li>{@link #EVENT_KEY_TYPED}</li> - * </ol> + * <p> + * <table border="0"> + * <tr><th>#</th><th>Event Type</th> <th>Constraints</th> <th>Notes</th></tr> + * <tr><td>1</td><td>{@link #EVENT_KEY_PRESSED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys</i></td><td></td></tr> + * <tr><td>2</td><td>{@link #EVENT_KEY_RELEASED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys</i></td><td></td></tr> + * <tr><td>3</td><td>{@link #EVENT_KEY_TYPED} </td><td> <i>only for {@link #isPrintableKey() printable} and non {@link #isAutoRepeat() auto-repeat} keys</i></td><td><b>Deprecated</b>: Use {@link #EVENT_KEY_RELEASED} and apply constraints.</td></tr> + * </table> + * </p> * In case the native platform does not * deliver keyboard events in the above order or skip events, * the NEWT driver will reorder and inject synthetic events if required. * <p> * Besides regular modifiers like {@link InputEvent#SHIFT_MASK} etc., - * the {@link InputEvent#AUTOREPEAT_MASK} bit is added if repetition is detected. + * the {@link InputEvent#AUTOREPEAT_MASK} bit is added if repetition is detected, following above constraints. * </p> * <p> * Auto-Repeat shall behave as follow: * <pre> - D = pressed, U = released, T = typed + P = pressed, R = released, T = typed 0 = normal, 1 = auto-repeat - D(0), [ U(1), T(1), D(1), U(1) T(1) ..], U(0) T(0) + P(0), [ R(1), P(1), R(1), ..], R(0) T(0) * </pre> * The idea is if you mask out auto-repeat in your event listener - * you just get one long pressed key D/U/T triple. + * or catch {@link #EVENT_KEY_TYPED typed} events only, + * you just get one long pressed P/R/T triple for {@link #isPrintableKey() printable} keys. + * {@link #isActionKey() Action} keys would produce one long pressed P/R tuple in case you mask out auto-repeat . + * </p> + * <p> + * {@link #isActionKey() Action} keys will produce {@link #EVENT_KEY_PRESSED pressed} + * and {@link #EVENT_KEY_RELEASED released} events including {@link #isAutoRepeat() auto-repeat}. + * </p> + * <p> + * {@link #isPrintableKey() Printable} keys will produce {@link #EVENT_KEY_PRESSED pressed}, + * {@link #EVENT_KEY_RELEASED released} and {@link #EVENT_KEY_TYPED typed} events, the latter is excluded for {@link #isAutoRepeat() auto-repeat} events. * </p> * <p> - * {@link #isModifierKey() Modifiers keys} will produce regular events (pressed, released and typed), - * however they will not produce Auto-Repeat events itself. + * {@link #isModifierKey() Modifier} keys will produce {@link #EVENT_KEY_PRESSED pressed} + * and {@link #EVENT_KEY_RELEASED released} events excluding {@link #isAutoRepeat() auto-repeat}. + * They will also influence subsequent event's {@link #getModifiers() modifier} bits while pressed. * </p> */ @SuppressWarnings("serial") public class KeyEvent extends InputEvent { - public KeyEvent(int eventType, Object source, long when, int modifiers, int keyCode, char keyChar) { + public KeyEvent(short eventType, Object source, long when, int modifiers, short keyCode, short keySym, char keyChar) { super(eventType, source, when, modifiers); this.keyCode=keyCode; + this.keySym=keySym; this.keyChar=keyChar; + { // cache modifier and action flags + byte _flags = 0; + if( isModifierKey(keySym) ) { + _flags |= F_MODIFIER_MASK; + } + if( isActionKey(keySym) ) { + _flags |= F_ACTION_MASK; + } + flags = _flags; + } } /** - * Returns the character matching the {@link #getKeyCode() virtual key code}. + * Returns the <i>UTF-16</i> character reflecting the {@link #getKeySymbol() key symbol}. + * @see #getKeySymbol() + * @see #getKeyCode() */ - public char getKeyChar() { + public final char getKeyChar() { return keyChar; } - /** Returns the virtual key code. */ - public int getKeyCode() { + /** + * Returns the virtual <i>key symbol</i> reflecting the current <i>keyboard layout</i>. + * @see #getKeyChar() + * @see #getKeyCode() + */ + public final short getKeySymbol() { + return keySym; + } + + /** + * Returns the virtual <i>key code</i> using a fixed mapping to the <i>US keyboard layout</i>. + * <p> + * In contrast to {@link #getKeySymbol() key symbol}, <i>key code</i> + * uses a fixed <i>US keyboard layout</i> and therefore is keyboard layout independent. + * </p> + * <p> + * E.g. <i>virtual key code</i> {@link #VK_Y} denotes the same physical key + * regardless whether <i>keyboard layout</i> <code>QWERTY</code> or + * <code>QWERTZ</code> is active. The {@link #getKeySymbol() key symbol} of the former is + * {@link #VK_Y}, where the latter produces {@link #VK_Y}. + * </p> + * <p> + * <b>Disclaimer</b>: In case <i>key code</i> is not implemented on your platform (OSX, ..) + * the {@link #getKeySymbol() key symbol} is returned. + * </p> + * @see #getKeyChar() + * @see #getKeySymbol() + */ + public final short getKeyCode() { return keyCode; } - public String toString() { + public final String toString() { return toString(null).toString(); } - public StringBuilder toString(StringBuilder sb) { + public final StringBuilder toString(StringBuilder sb) { if(null == sb) { sb = new StringBuilder(); } - sb.append("KeyEvent[").append(getEventTypeString(getEventType())).append(", code ").append(keyCode).append("(").append(toHexString(keyCode)).append("), char '").append(keyChar).append("' (").append(toHexString((int)keyChar)).append("), isActionKey ").append(isActionKey()).append(", "); + sb.append("KeyEvent[").append(getEventTypeString(getEventType())).append(", code ").append(toHexString(keyCode)).append(", sym ").append(toHexString(keySym)).append(", char '").append(keyChar).append("' (").append(toHexString((short)keyChar)) + .append("), isModifierKey ").append(isModifierKey()).append(", isActionKey ").append(isActionKey()).append(", "); return super.toString(sb).append("]"); } - public static String getEventTypeString(int type) { + public static String getEventTypeString(short type) { switch(type) { case EVENT_KEY_PRESSED: return "EVENT_KEY_PRESSED"; case EVENT_KEY_RELEASED: return "EVENT_KEY_RELEASED"; @@ -107,13 +162,13 @@ public class KeyEvent extends InputEvent } /** - * Returns true if the given <code>keyCode</code> represents a non-printable modifier key. + * Returns <code>true</code> if the given <code>virtualKey</code> represents a modifier key, otherwise <code>false</code>. * <p> * A modifier key is one of {@link #VK_SHIFT}, {@link #VK_CONTROL}, {@link #VK_ALT}, {@link #VK_ALT_GRAPH}, {@link #VK_META}. * </p> */ - public static boolean isModifierKey(int keyCode) { - switch (keyCode) { + public static boolean isModifierKey(short vKey) { + switch (vKey) { case VK_SHIFT: case VK_CONTROL: case VK_ALT: @@ -126,31 +181,36 @@ public class KeyEvent extends InputEvent } /** - * Returns true if {@link #getKeyCode()} represents a non-printable modifier key. + * Returns <code>true</code> if {@link #getKeySymbol() key symbol} represents a modifier key, + * otherwise <code>false</code>. + * <p> + * See {@link #isModifierKey(short)} for details. + * </p> * <p> - * See {@link #isModifierKey(int)} for details. + * Note: Implementation uses a cached value. * </p> */ - public boolean isModifierKey() { - return isModifierKey(keyCode); + public final boolean isModifierKey() { + return 0 != ( F_MODIFIER_MASK & flags ) ; } /** - * Returns true if the given <code>keyCode</code> represents a non-printable action key, which is not a {@link #isModifierKey(int) modifier key}. + * Returns <code>true</code> if the given <code>virtualKey</code> represents a non-printable and + * non-{@link #isModifierKey(short) modifier} action key, otherwise <code>false</code>. * <p> * An action key is one of {@link #VK_HOME}, {@link #VK_END}, {@link #VK_PAGE_UP}, {@link #VK_PAGE_DOWN}, {@link #VK_UP}, {@link #VK_PAGE_DOWN}, * {@link #VK_LEFT}, {@link #VK_RIGHT}, {@link #VK_F1}-{@link #VK_F24}, {@link #VK_PRINTSCREEN}, {@link #VK_CAPS_LOCK}, {@link #VK_PAUSE}, * {@link #VK_INSERT}, {@link #VK_HELP}, {@link #VK_WINDOWS}, etc ... * </p> */ - public static boolean isActionKey(int keyCode) { - if( ( VK_F1 <= keyCode && keyCode <= VK_F24 ) || - ( VK_ALL_CANDIDATES <= keyCode && keyCode <= VK_INPUT_METHOD_ON_OFF ) || - ( VK_CUT <= keyCode && keyCode <= VK_STOP ) ) { + public static boolean isActionKey(short vKey) { + if( ( VK_F1 <= vKey && vKey <= VK_F24 ) || + ( VK_ALL_CANDIDATES <= vKey && vKey <= VK_INPUT_METHOD_ON_OFF ) || + ( VK_CUT <= vKey && vKey <= VK_STOP ) ) { return true; } - switch (keyCode) { + switch (vKey) { case VK_CANCEL: case VK_CLEAR: case VK_PAUSE: @@ -208,600 +268,614 @@ public class KeyEvent extends InputEvent } /** - * Returns true if {@link #getKeyCode() keyCode} represents a non-printable action key, which is not a {@link #isModifierKey(int) modifier key}. + * Returns <code>true</code> if {@link #getKeySymbol() key symbol} represents a non-printable and + * non-{@link #isModifierKey(short) modifier} action key, otherwise <code>false</code>. * <p> - * See {@link #isActionKey(int)} for details. + * See {@link #isActionKey(short)} for details. * </p> */ - public boolean isActionKey() { - return isActionKey(keyCode); + public final boolean isActionKey() { + return 0 != ( F_ACTION_MASK & flags ) ; } /** - * Returns true if given <code>keyKode</code> represents a printable character, which is neither a {@link #isModifierKey(int) modifier key} - * nor an {@link #isActionKey(int) action key}. + * Returns <code>true</code> if given <code>virtualKey</code> represents a printable character, + * i.e. neither a {@link #isModifierKey(short) modifier key} + * nor an {@link #isActionKey(short) action key}. * Otherwise returns <code>false</code>. */ - public static boolean isPrintableKey(int keyCode) { - return !isModifierKey(keyCode) && !isActionKey(keyCode); + public static boolean isPrintableKey(short vKey) { + return !isModifierKey(vKey) && !isActionKey(vKey); } /** - * Returns true if {@link #getKeyCode() keyCode} represents a printable character, which is neither a {@link #isModifierKey(int) modifier key} - * nor an {@link #isActionKey(int) action key}. + * Returns <code>true</code> if {@link #getKeySymbol() key symbol} represents a printable character, + * i.e. neither a {@link #isModifierKey(short) modifier key} + * nor an {@link #isActionKey(short) action key}. * Otherwise returns <code>false</code>. */ - public boolean isPrintableKey() { - return isPrintableKey(keyCode); + public final boolean isPrintableKey() { + return 0 == ( F_NON_PRINT_MASK & flags ) ; } - private final int keyCode; + private final short keyCode; + private final short keySym; private final char keyChar; - - public static final int EVENT_KEY_PRESSED = 300; - public static final int EVENT_KEY_RELEASED= 301; - public static final int EVENT_KEY_TYPED = 302; + private final byte flags; + private static final byte F_MODIFIER_MASK = 1 << 0; + private static final byte F_ACTION_MASK = 1 << 1; + private static final byte F_NON_PRINT_MASK = F_MODIFIER_MASK | F_ACTION_MASK ; + + /** A key has been pressed, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. */ + public static final short EVENT_KEY_PRESSED = 300; + /** A key has been released, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. */ + public static final short EVENT_KEY_RELEASED= 301; + /** + * A {@link #isPrintableKey() printable} key has been typed (pressed and released), excluding {@link #isAutoRepeat() auto-repeat}. + * @deprecated Redundant, will be removed soon. Use {@link #EVENT_KEY_RELEASED} and exclude non {@link #isPrintableKey() printable} keys and {@link #isAutoRepeat() auto-repeat}. + */ + public static final short EVENT_KEY_TYPED = 302; /* Virtual key codes. */ - public static final int VK_CANCEL = 0x03; - public static final int VK_BACK_SPACE = 0x08; // '\b' - public static final int VK_TAB = 0x09; // '\t' - public static final int VK_ENTER = 0x0A; // '\n' - public static final int VK_CLEAR = 0x0C; - public static final int VK_SHIFT = 0x10; - public static final int VK_CONTROL = 0x11; - public static final int VK_ALT = 0x12; - public static final int VK_PAUSE = 0x13; - public static final int VK_CAPS_LOCK = 0x14; - public static final int VK_ESCAPE = 0x1B; - public static final int VK_SPACE = 0x20; - public static final int VK_PAGE_UP = 0x21; - public static final int VK_PAGE_DOWN = 0x22; - public static final int VK_END = 0x23; - public static final int VK_HOME = 0x24; + public static final short VK_CANCEL = (short) 0x03; + public static final short VK_BACK_SPACE = (short) 0x08; // '\b' + public static final short VK_TAB = (short) 0x09; // '\t' + public static final short VK_ENTER = (short) 0x0A; // '\n' + public static final short VK_CLEAR = (short) 0x0C; + public static final short VK_SHIFT = (short) 0x10; + public static final short VK_CONTROL = (short) 0x11; + public static final short VK_ALT = (short) 0x12; + public static final short VK_PAUSE = (short) 0x13; + public static final short VK_CAPS_LOCK = (short) 0x14; + public static final short VK_ESCAPE = (short) 0x1B; + public static final short VK_SPACE = (short) 0x20; + public static final short VK_PAGE_UP = (short) 0x21; + public static final short VK_PAGE_DOWN = (short) 0x22; + public static final short VK_END = (short) 0x23; + public static final short VK_HOME = (short) 0x24; /** * Constant for the non-numpad <b>left</b> arrow key. * @see #VK_KP_LEFT */ - public static final int VK_LEFT = 0x25; + public static final short VK_LEFT = (short) 0x25; /** * Constant for the non-numpad <b>up</b> arrow key. * @see #VK_KP_UP */ - public static final int VK_UP = 0x26; + public static final short VK_UP = (short) 0x26; /** * Constant for the non-numpad <b>right</b> arrow key. * @see #VK_KP_RIGHT */ - public static final int VK_RIGHT = 0x27; + public static final short VK_RIGHT = (short) 0x27; /** * Constant for the non-numpad <b>down</b> arrow key. * @see #VK_KP_DOWN */ - public static final int VK_DOWN = 0x28; + public static final short VK_DOWN = (short) 0x28; /** * Constant for the comma key, "," */ - public static final int VK_COMMA = 0x2C; + public static final short VK_COMMA = (short) 0x2C; /** * Constant for the minus key, "-" * @since 1.2 */ - public static final int VK_MINUS = 0x2D; + public static final short VK_MINUS = (short) 0x2D; /** * Constant for the period key, "." */ - public static final int VK_PERIOD = 0x2E; + public static final short VK_PERIOD = (short) 0x2E; /** * Constant for the forward slash key, "/" */ - public static final int VK_SLASH = 0x2F; + public static final short VK_SLASH = (short) 0x2F; /** VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ - public static final int VK_0 = 0x30; - public static final int VK_1 = 0x31; - public static final int VK_2 = 0x32; - public static final int VK_3 = 0x33; - public static final int VK_4 = 0x34; - public static final int VK_5 = 0x35; - public static final int VK_6 = 0x36; - public static final int VK_7 = 0x37; - public static final int VK_8 = 0x38; - public static final int VK_9 = 0x39; + public static final short VK_0 = (short) 0x30; + public static final short VK_1 = (short) 0x31; + public static final short VK_2 = (short) 0x32; + public static final short VK_3 = (short) 0x33; + public static final short VK_4 = (short) 0x34; + public static final short VK_5 = (short) 0x35; + public static final short VK_6 = (short) 0x36; + public static final short VK_7 = (short) 0x37; + public static final short VK_8 = (short) 0x38; + public static final short VK_9 = (short) 0x39; /** * Constant for the semicolon key, ";" */ - public static final int VK_SEMICOLON = 0x3B; + public static final short VK_SEMICOLON = (short) 0x3B; /** * Constant for the equals key, "=" */ - public static final int VK_EQUALS = 0x3D; + public static final short VK_EQUALS = (short) 0x3D; /** VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ - public static final int VK_A = 0x41; - public static final int VK_B = 0x42; - public static final int VK_C = 0x43; - public static final int VK_D = 0x44; - public static final int VK_E = 0x45; - public static final int VK_F = 0x46; - public static final int VK_G = 0x47; - public static final int VK_H = 0x48; - public static final int VK_I = 0x49; - public static final int VK_J = 0x4A; - public static final int VK_K = 0x4B; - public static final int VK_L = 0x4C; - public static final int VK_M = 0x4D; - public static final int VK_N = 0x4E; - public static final int VK_O = 0x4F; - public static final int VK_P = 0x50; - public static final int VK_Q = 0x51; - public static final int VK_R = 0x52; - public static final int VK_S = 0x53; - public static final int VK_T = 0x54; - public static final int VK_U = 0x55; - public static final int VK_V = 0x56; - public static final int VK_W = 0x57; - public static final int VK_X = 0x58; - public static final int VK_Y = 0x59; - public static final int VK_Z = 0x5A; + public static final short VK_A = (short) 0x41; + public static final short VK_B = (short) 0x42; + public static final short VK_C = (short) 0x43; + public static final short VK_D = (short) 0x44; + public static final short VK_E = (short) 0x45; + public static final short VK_F = (short) 0x46; + public static final short VK_G = (short) 0x47; + public static final short VK_H = (short) 0x48; + public static final short VK_I = (short) 0x49; + public static final short VK_J = (short) 0x4A; + public static final short VK_K = (short) 0x4B; + public static final short VK_L = (short) 0x4C; + public static final short VK_M = (short) 0x4D; + public static final short VK_N = (short) 0x4E; + public static final short VK_O = (short) 0x4F; + public static final short VK_P = (short) 0x50; + public static final short VK_Q = (short) 0x51; + public static final short VK_R = (short) 0x52; + public static final short VK_S = (short) 0x53; + public static final short VK_T = (short) 0x54; + public static final short VK_U = (short) 0x55; + public static final short VK_V = (short) 0x56; + public static final short VK_W = (short) 0x57; + public static final short VK_X = (short) 0x58; + public static final short VK_Y = (short) 0x59; + public static final short VK_Z = (short) 0x5A; /** * Constant for the open bracket key, "[" */ - public static final int VK_OPEN_BRACKET = 0x5B; + public static final short VK_OPEN_BRACKET = (short) 0x5B; /** * Constant for the back slash key, "\" */ - public static final int VK_BACK_SLASH = 0x5C; + public static final short VK_BACK_SLASH = (short) 0x5C; /** * Constant for the close bracket key, "]" */ - public static final int VK_CLOSE_BRACKET = 0x5D; - - public static final int VK_NUMPAD0 = 0x60; - public static final int VK_NUMPAD1 = 0x61; - public static final int VK_NUMPAD2 = 0x62; - public static final int VK_NUMPAD3 = 0x63; - public static final int VK_NUMPAD4 = 0x64; - public static final int VK_NUMPAD5 = 0x65; - public static final int VK_NUMPAD6 = 0x66; - public static final int VK_NUMPAD7 = 0x67; - public static final int VK_NUMPAD8 = 0x68; - public static final int VK_NUMPAD9 = 0x69; - public static final int VK_MULTIPLY = 0x6A; - public static final int VK_ADD = 0x6B; + public static final short VK_CLOSE_BRACKET = (short) 0x5D; + + public static final short VK_NUMPAD0 = (short) 0x60; + public static final short VK_NUMPAD1 = (short) 0x61; + public static final short VK_NUMPAD2 = (short) 0x62; + public static final short VK_NUMPAD3 = (short) 0x63; + public static final short VK_NUMPAD4 = (short) 0x64; + public static final short VK_NUMPAD5 = (short) 0x65; + public static final short VK_NUMPAD6 = (short) 0x66; + public static final short VK_NUMPAD7 = (short) 0x67; + public static final short VK_NUMPAD8 = (short) 0x68; + public static final short VK_NUMPAD9 = (short) 0x69; + public static final short VK_MULTIPLY = (short) 0x6A; + public static final short VK_ADD = (short) 0x6B; /** * Constant for the Numpad Separator key. */ - public static final int VK_SEPARATOR = 0x6C; + public static final short VK_SEPARATOR = (short) 0x6C; - public static final int VK_SUBTRACT = 0x6D; - public static final int VK_DECIMAL = 0x6E; - public static final int VK_DIVIDE = 0x6F; - public static final int VK_DELETE = 0x7F; /* ASCII DEL */ - public static final int VK_NUM_LOCK = 0x90; - public static final int VK_SCROLL_LOCK = 0x91; + public static final short VK_SUBTRACT = (short) 0x6D; + public static final short VK_DECIMAL = (short) 0x6E; + public static final short VK_DIVIDE = (short) 0x6F; + public static final short VK_DELETE = (short) 0x7F; /* ASCII DEL */ + public static final short VK_NUM_LOCK = (short) 0x90; + public static final short VK_SCROLL_LOCK = (short) 0x91; /** Constant for the F1 function key. */ - public static final int VK_F1 = 0x70; + public static final short VK_F1 = (short) 0x70; /** Constant for the F2 function key. */ - public static final int VK_F2 = 0x71; + public static final short VK_F2 = (short) 0x71; /** Constant for the F3 function key. */ - public static final int VK_F3 = 0x72; + public static final short VK_F3 = (short) 0x72; /** Constant for the F4 function key. */ - public static final int VK_F4 = 0x73; + public static final short VK_F4 = (short) 0x73; /** Constant for the F5 function key. */ - public static final int VK_F5 = 0x74; + public static final short VK_F5 = (short) 0x74; /** Constant for the F6 function key. */ - public static final int VK_F6 = 0x75; + public static final short VK_F6 = (short) 0x75; /** Constant for the F7 function key. */ - public static final int VK_F7 = 0x76; + public static final short VK_F7 = (short) 0x76; /** Constant for the F8 function key. */ - public static final int VK_F8 = 0x77; + public static final short VK_F8 = (short) 0x77; /** Constant for the F9 function key. */ - public static final int VK_F9 = 0x78; + public static final short VK_F9 = (short) 0x78; /** Constant for the F10 function key. */ - public static final int VK_F10 = 0x79; + public static final short VK_F10 = (short) 0x79; /** Constant for the F11 function key. */ - public static final int VK_F11 = 0x7A; + public static final short VK_F11 = (short) 0x7A; /** Constant for the F12 function key. */ - public static final int VK_F12 = 0x7B; + public static final short VK_F12 = (short) 0x7B; /** * Constant for the F13 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F13 = 0xF000; + public static final short VK_F13 = (short) 0xF000; /** * Constant for the F14 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F14 = 0xF001; + public static final short VK_F14 = (short) 0xF001; /** * Constant for the F15 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F15 = 0xF002; + public static final short VK_F15 = (short) 0xF002; /** * Constant for the F16 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F16 = 0xF003; + public static final short VK_F16 = (short) 0xF003; /** * Constant for the F17 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F17 = 0xF004; + public static final short VK_F17 = (short) 0xF004; /** * Constant for the F18 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F18 = 0xF005; + public static final short VK_F18 = (short) 0xF005; /** * Constant for the F19 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F19 = 0xF006; + public static final short VK_F19 = (short) 0xF006; /** * Constant for the F20 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F20 = 0xF007; + public static final short VK_F20 = (short) 0xF007; /** * Constant for the F21 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F21 = 0xF008; + public static final short VK_F21 = (short) 0xF008; /** * Constant for the F22 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F22 = 0xF009; + public static final short VK_F22 = (short) 0xF009; /** * Constant for the F23 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F23 = 0xF00A; + public static final short VK_F23 = (short) 0xF00A; /** * Constant for the F24 function key. * <p>F13 - F24 are used on IBM 3270 keyboard; use random range for constants.</p> */ - public static final int VK_F24 = 0xF00B; + public static final short VK_F24 = (short) 0xF00B; - public static final int VK_PRINTSCREEN = 0x9A; - public static final int VK_INSERT = 0x9B; - public static final int VK_HELP = 0x9C; - public static final int VK_META = 0x9D; + public static final short VK_PRINTSCREEN = (short) 0x9A; + public static final short VK_INSERT = (short) 0x9B; + public static final short VK_HELP = (short) 0x9C; + public static final short VK_META = (short) 0x9D; - public static final int VK_BACK_QUOTE = 0xC0; - public static final int VK_QUOTE = 0xDE; + public static final short VK_BACK_QUOTE = (short) 0xC0; + public static final short VK_QUOTE = (short) 0xDE; /** * Constant for the numeric keypad <b>up</b> arrow key. * @see #VK_UP */ - public static final int VK_KP_UP = 0xE0; + public static final short VK_KP_UP = (short) 0xE0; /** * Constant for the numeric keypad <b>down</b> arrow key. * @see #VK_DOWN */ - public static final int VK_KP_DOWN = 0xE1; + public static final short VK_KP_DOWN = (short) 0xE1; /** * Constant for the numeric keypad <b>left</b> arrow key. * @see #VK_LEFT */ - public static final int VK_KP_LEFT = 0xE2; + public static final short VK_KP_LEFT = (short) 0xE2; /** * Constant for the numeric keypad <b>right</b> arrow key. * @see #VK_RIGHT */ - public static final int VK_KP_RIGHT = 0xE3; + public static final short VK_KP_RIGHT = (short) 0xE3; /** For European keyboards */ - public static final int VK_DEAD_GRAVE = 0x80; + public static final short VK_DEAD_GRAVE = (short) 0x80; /** For European keyboards */ - public static final int VK_DEAD_ACUTE = 0x81; + public static final short VK_DEAD_ACUTE = (short) 0x81; /** For European keyboards */ - public static final int VK_DEAD_CIRCUMFLEX = 0x82; + public static final short VK_DEAD_CIRCUMFLEX = (short) 0x82; /** For European keyboards */ - public static final int VK_DEAD_TILDE = 0x83; + public static final short VK_DEAD_TILDE = (short) 0x83; /** For European keyboards */ - public static final int VK_DEAD_MACRON = 0x84; + public static final short VK_DEAD_MACRON = (short) 0x84; /** For European keyboards */ - public static final int VK_DEAD_BREVE = 0x85; + public static final short VK_DEAD_BREVE = (short) 0x85; /** For European keyboards */ - public static final int VK_DEAD_ABOVEDOT = 0x86; + public static final short VK_DEAD_ABOVEDOT = (short) 0x86; /** For European keyboards */ - public static final int VK_DEAD_DIAERESIS = 0x87; + public static final short VK_DEAD_DIAERESIS = (short) 0x87; /** For European keyboards */ - public static final int VK_DEAD_ABOVERING = 0x88; + public static final short VK_DEAD_ABOVERING = (short) 0x88; /** For European keyboards */ - public static final int VK_DEAD_DOUBLEACUTE = 0x89; + public static final short VK_DEAD_DOUBLEACUTE = (short) 0x89; /** For European keyboards */ - public static final int VK_DEAD_CARON = 0x8a; + public static final short VK_DEAD_CARON = (short) 0x8a; /** For European keyboards */ - public static final int VK_DEAD_CEDILLA = 0x8b; + public static final short VK_DEAD_CEDILLA = (short) 0x8b; /** For European keyboards */ - public static final int VK_DEAD_OGONEK = 0x8c; + public static final short VK_DEAD_OGONEK = (short) 0x8c; /** For European keyboards */ - public static final int VK_DEAD_IOTA = 0x8d; + public static final short VK_DEAD_IOTA = (short) 0x8d; /** For European keyboards */ - public static final int VK_DEAD_VOICED_SOUND = 0x8e; + public static final short VK_DEAD_VOICED_SOUND = (short) 0x8e; /** For European keyboards */ - public static final int VK_DEAD_SEMIVOICED_SOUND = 0x8f; + public static final short VK_DEAD_SEMIVOICED_SOUND = (short) 0x8f; /** For European keyboards */ - public static final int VK_AMPERSAND = 0x96; + public static final short VK_AMPERSAND = (short) 0x96; /** For European keyboards */ - public static final int VK_ASTERISK = 0x97; + public static final short VK_ASTERISK = (short) 0x97; /** For European keyboards */ - public static final int VK_QUOTEDBL = 0x98; + public static final short VK_QUOTEDBL = (short) 0x98; /** For European keyboards */ - public static final int VK_LESS = 0x99; + public static final short VK_LESS = (short) 0x99; /** For European keyboards */ - public static final int VK_GREATER = 0xa0; + public static final short VK_GREATER = (short) 0xa0; /** For European keyboards */ - public static final int VK_BRACELEFT = 0xa1; + public static final short VK_BRACELEFT = (short) 0xa1; /** For European keyboards */ - public static final int VK_BRACERIGHT = 0xa2; + public static final short VK_BRACERIGHT = (short) 0xa2; /** * Constant for the "@" key. */ - public static final int VK_AT = 0x0200; + public static final short VK_AT = (short) 0x0200; /** * Constant for the ":" key. */ - public static final int VK_COLON = 0x0201; + public static final short VK_COLON = (short) 0x0201; /** * Constant for the "^" key. */ - public static final int VK_CIRCUMFLEX = 0x0202; + public static final short VK_CIRCUMFLEX = (short) 0x0202; /** * Constant for the "$" key. */ - public static final int VK_DOLLAR = 0x0203; + public static final short VK_DOLLAR = (short) 0x0203; /** * Constant for the Euro currency sign key. */ - public static final int VK_EURO_SIGN = 0x0204; + public static final short VK_EURO_SIGN = (short) 0x0204; /** * Constant for the "!" key. */ - public static final int VK_EXCLAMATION_MARK = 0x0205; + public static final short VK_EXCLAMATION_MARK = (short) 0x0205; /** * Constant for the inverted exclamation mark key. */ - public static final int VK_INVERTED_EXCLAMATION_MARK = 0x0206; + public static final short VK_INVERTED_EXCLAMATION_MARK = (short) 0x0206; /** * Constant for the "(" key. */ - public static final int VK_LEFT_PARENTHESIS = 0x0207; + public static final short VK_LEFT_PARENTHESIS = (short) 0x0207; /** * Constant for the "#" key. */ - public static final int VK_NUMBER_SIGN = 0x0208; + public static final short VK_NUMBER_SIGN = (short) 0x0208; /** * Constant for the "+" key. */ - public static final int VK_PLUS = 0x0209; + public static final short VK_PLUS = (short) 0x0209; /** * Constant for the ")" key. */ - public static final int VK_RIGHT_PARENTHESIS = 0x020A; + public static final short VK_RIGHT_PARENTHESIS = (short) 0x020A; /** * Constant for the "_" key. */ - public static final int VK_UNDERSCORE = 0x020B; + public static final short VK_UNDERSCORE = (short) 0x020B; /** * Constant for the Microsoft Windows "Windows" key. * It is used for both the left and right version of the key. */ - public static final int VK_WINDOWS = 0x020C; + public static final short VK_WINDOWS = (short) 0x020C; /** * Constant for the Microsoft Windows Context Menu key. */ - public static final int VK_CONTEXT_MENU = 0x020D; + public static final short VK_CONTEXT_MENU = (short) 0x020D; /* for input method support on Asian Keyboards */ /* not clear what this means - listed in Microsoft Windows API */ - public static final int VK_FINAL = 0x0018; + public static final short VK_FINAL = (short) 0x0018; /** Constant for the Convert function key. */ /* Japanese PC 106 keyboard, Japanese Solaris keyboard: henkan */ - public static final int VK_CONVERT = 0x001C; + public static final short VK_CONVERT = (short) 0x001C; /** Constant for the Don't Convert function key. */ /* Japanese PC 106 keyboard: muhenkan */ - public static final int VK_NONCONVERT = 0x001D; + public static final short VK_NONCONVERT = (short) 0x001D; /** Constant for the Accept or Commit function key. */ /* Japanese Solaris keyboard: kakutei */ - public static final int VK_ACCEPT = 0x001E; + public static final short VK_ACCEPT = (short) 0x001E; /* not clear what this means - listed in Microsoft Windows API */ - public static final int VK_MODECHANGE = 0x001F; + public static final short VK_MODECHANGE = (short) 0x001F; /* replaced by VK_KANA_LOCK for Microsoft Windows and Solaris; might still be used on other platforms */ - public static final int VK_KANA = 0x0015; + public static final short VK_KANA = (short) 0x0015; /* replaced by VK_INPUT_METHOD_ON_OFF for Microsoft Windows and Solaris; might still be used for other platforms */ - public static final int VK_KANJI = 0x0019; + public static final short VK_KANJI = (short) 0x0019; /** * Constant for the Alphanumeric function key. */ /* Japanese PC 106 keyboard: eisuu */ - public static final int VK_ALPHANUMERIC = 0x00F0; + public static final short VK_ALPHANUMERIC = (short) 0x00F0; /** * Constant for the Katakana function key. */ /* Japanese PC 106 keyboard: katakana */ - public static final int VK_KATAKANA = 0x00F1; + public static final short VK_KATAKANA = (short) 0x00F1; /** * Constant for the Hiragana function key. */ /* Japanese PC 106 keyboard: hiragana */ - public static final int VK_HIRAGANA = 0x00F2; + public static final short VK_HIRAGANA = (short) 0x00F2; /** * Constant for the Full-Width Characters function key. */ /* Japanese PC 106 keyboard: zenkaku */ - public static final int VK_FULL_WIDTH = 0x00F3; + public static final short VK_FULL_WIDTH = (short) 0x00F3; /** * Constant for the Half-Width Characters function key. */ /* Japanese PC 106 keyboard: hankaku */ - public static final int VK_HALF_WIDTH = 0x00F4; + public static final short VK_HALF_WIDTH = (short) 0x00F4; /** * Constant for the Roman Characters function key. */ /* Japanese PC 106 keyboard: roumaji */ - public static final int VK_ROMAN_CHARACTERS = 0x00F5; + public static final short VK_ROMAN_CHARACTERS = (short) 0x00F5; /** * Constant for the All Candidates function key. */ /* Japanese PC 106 keyboard - VK_CONVERT + ALT: zenkouho */ - public static final int VK_ALL_CANDIDATES = 0x0100; + public static final short VK_ALL_CANDIDATES = (short) 0x0100; /** * Constant for the Previous Candidate function key. */ /* Japanese PC 106 keyboard - VK_CONVERT + SHIFT: maekouho */ - public static final int VK_PREVIOUS_CANDIDATE = 0x0101; + public static final short VK_PREVIOUS_CANDIDATE = (short) 0x0101; /** * Constant for the Code Input function key. */ /* Japanese PC 106 keyboard - VK_ALPHANUMERIC + ALT: kanji bangou */ - public static final int VK_CODE_INPUT = 0x0102; + public static final short VK_CODE_INPUT = (short) 0x0102; /** * Constant for the Japanese-Katakana function key. * This key switches to a Japanese input method and selects its Katakana input mode. */ /* Japanese Macintosh keyboard - VK_JAPANESE_HIRAGANA + SHIFT */ - public static final int VK_JAPANESE_KATAKANA = 0x0103; + public static final short VK_JAPANESE_KATAKANA = (short) 0x0103; /** * Constant for the Japanese-Hiragana function key. * This key switches to a Japanese input method and selects its Hiragana input mode. */ /* Japanese Macintosh keyboard */ - public static final int VK_JAPANESE_HIRAGANA = 0x0104; + public static final short VK_JAPANESE_HIRAGANA = (short) 0x0104; /** * Constant for the Japanese-Roman function key. * This key switches to a Japanese input method and selects its Roman-Direct input mode. */ /* Japanese Macintosh keyboard */ - public static final int VK_JAPANESE_ROMAN = 0x0105; + public static final short VK_JAPANESE_ROMAN = (short) 0x0105; /** * Constant for the locking Kana function key. * This key locks the keyboard into a Kana layout. */ /* Japanese PC 106 keyboard with special Windows driver - eisuu + Control; Japanese Solaris keyboard: kana */ - public static final int VK_KANA_LOCK = 0x0106; + public static final short VK_KANA_LOCK = (short) 0x0106; /** * Constant for the input method on/off key. */ /* Japanese PC 106 keyboard: kanji. Japanese Solaris keyboard: nihongo */ - public static final int VK_INPUT_METHOD_ON_OFF = 0x0107; + public static final short VK_INPUT_METHOD_ON_OFF = (short) 0x0107; /* for Sun keyboards */ - public static final int VK_CUT = 0xFFD1; - public static final int VK_COPY = 0xFFCD; - public static final int VK_PASTE = 0xFFCF; - public static final int VK_UNDO = 0xFFCB; - public static final int VK_AGAIN = 0xFFC9; - public static final int VK_FIND = 0xFFD0; - public static final int VK_PROPS = 0xFFCA; - public static final int VK_STOP = 0xFFC8; + public static final short VK_CUT = (short) 0xFFD1; + public static final short VK_COPY = (short) 0xFFCD; + public static final short VK_PASTE = (short) 0xFFCF; + public static final short VK_UNDO = (short) 0xFFCB; + public static final short VK_AGAIN = (short) 0xFFC9; + public static final short VK_FIND = (short) 0xFFD0; + public static final short VK_PROPS = (short) 0xFFCA; + public static final short VK_STOP = (short) 0xFFC8; /** * Constant for the Compose function key. */ - public static final int VK_COMPOSE = 0xFF20; + public static final short VK_COMPOSE = (short) 0xFF20; /** * Constant for the AltGraph function key. */ - public static final int VK_ALT_GRAPH = 0xFF7E; + public static final short VK_ALT_GRAPH = (short) 0xFF7E; /** * Constant for the Begin key. */ - public static final int VK_BEGIN = 0xFF58; + public static final short VK_BEGIN = (short) 0xFF58; /** * This value is used to indicate that the keyCode is unknown. * KEY_TYPED events do not have a keyCode value; this value * is used instead. */ - public static final int VK_UNDEFINED = 0x0; + public static final short VK_UNDEFINED = (short) 0x0; } diff --git a/src/newt/classes/com/jogamp/newt/event/KeyListener.java b/src/newt/classes/com/jogamp/newt/event/KeyListener.java index dae343d80..5bca733d3 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyListener.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyListener.java @@ -34,10 +34,22 @@ package com.jogamp.newt.event; +/** + * Listener for {@link KeyEvent}s. + * + * @see KeyEvent + */ public interface KeyListener extends NEWTEventListener { - public void keyPressed(KeyEvent e); - public void keyReleased(KeyEvent e); - public void keyTyped(KeyEvent e) ; + /** A key has been {@link KeyEvent#EVENT_KEY_PRESSED pressed}, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. See {@link KeyEvent}. */ + public void keyPressed(KeyEvent e); + /** A key has been {@link KeyEvent#EVENT_KEY_RELEASED released}, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. See {@link KeyEvent}. */ + public void keyReleased(KeyEvent e); + + /** + * A {@link #isPrintableKey() printable} key has been {@link KeyEvent#EVENT_KEY_TYPED typed} (pressed and released), excluding {@link #isAutoRepeat() auto-repeat}. See {@link KeyEvent}. + * @deprecated Redundant, will be removed soon. Use {@link #keyReleased(KeyEvent)} and exclude non {@link #isPrintableKey() printable} keys and {@link #isAutoRepeat() auto-repeat}. + */ + public void keyTyped(KeyEvent e) ; } diff --git a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java index e6b3d8a24..23549533e 100644 --- a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java @@ -38,47 +38,47 @@ package com.jogamp.newt.event; public class MouseEvent extends InputEvent { /** ID for button 1, value <code>1</code> */ - public static final int BUTTON1 = 1; + public static final short BUTTON1 = 1; /** ID for button 2, value <code>2</code> */ - public static final int BUTTON2 = 2; + public static final short BUTTON2 = 2; /** ID for button 3, value <code>3</code> */ - public static final int BUTTON3 = 3; + public static final short BUTTON3 = 3; /** ID for button 4, value <code>4</code> */ - public static final int BUTTON4 = 4; + public static final short BUTTON4 = 4; /** ID for button 5, value <code>5</code> */ - public static final int BUTTON5 = 5; + public static final short BUTTON5 = 5; /** ID for button 6, value <code>6</code> */ - public static final int BUTTON6 = 6; + public static final short BUTTON6 = 6; /** ID for button 6, value <code>7</code> */ - public static final int BUTTON7 = 7; + public static final short BUTTON7 = 7; /** ID for button 6, value <code>8</code> */ - public static final int BUTTON8 = 8; + public static final short BUTTON8 = 8; /** ID for button 6, value <code>9</code> */ - public static final int BUTTON9 = 9; + public static final short BUTTON9 = 9; /** Maximum number of buttons, value <code>16</code> */ - public static final int BUTTON_NUMBER = 16; + public static final short BUTTON_NUMBER = 16; - public static final int getClickTimeout() { + public static final short getClickTimeout() { return 300; } - public MouseEvent(int eventType, Object source, long when, - int modifiers, int x, int y, int clickCount, int button, + public MouseEvent(short eventType, Object source, long when, + int modifiers, int x, int y, short clickCount, short button, float rotation) { super(eventType, source, when, modifiers); this.x = new int[]{x}; this.y = new int[]{y}; this.pressure = new float[]{0}; - this.pointerids = new int[]{-1}; + this.pointerids = new short[]{-1}; this.clickCount=clickCount; this.button=button; this.wheelRotation = rotation; } - public MouseEvent(int eventType, Object source, long when, - int modifiers, int[] x, int[] y, float[] pressure, int[] pointerids, int clickCount, int button, + public MouseEvent(short eventType, Object source, long when, + int modifiers, int[] x, int[] y, float[] pressure, short[] pointerids, short clickCount, short button, float rotation) { super(eventType, source, when, modifiers); @@ -107,17 +107,17 @@ public class MouseEvent extends InputEvent * @return the pointer id for the data at index. * return -1 if index not available. */ - public int getPointerId(int index) { + public short getPointerId(int index) { if(index >= pointerids.length) return -1; return pointerids[index]; } - public int getButton() { + public short getButton() { return button; } - public int getClickCount() { + public short getClickCount() { return clickCount; } public int getX() { @@ -208,7 +208,7 @@ public class MouseEvent extends InputEvent return super.toString(sb).append("]"); } - public static String getEventTypeString(int type) { + public static String getEventTypeString(short type) { switch(type) { case EVENT_MOUSE_CLICKED: return "EVENT_MOUSE_CLICKED"; case EVENT_MOUSE_ENTERED: return "EVENT_MOUSE_ENTERED"; @@ -221,17 +221,18 @@ public class MouseEvent extends InputEvent default: return "unknown (" + type + ")"; } } - private final int x[], y[], clickCount, button; + private final int x[], y[];; + private final short clickCount, button; private final float wheelRotation; private final float pressure[]; - private final int pointerids[]; + private final short pointerids[]; - public static final int EVENT_MOUSE_CLICKED = 200; - public static final int EVENT_MOUSE_ENTERED = 201; - public static final int EVENT_MOUSE_EXITED = 202; - public static final int EVENT_MOUSE_PRESSED = 203; - public static final int EVENT_MOUSE_RELEASED = 204; - public static final int EVENT_MOUSE_MOVED = 205; - public static final int EVENT_MOUSE_DRAGGED = 206; - public static final int EVENT_MOUSE_WHEEL_MOVED = 207; + public static final short EVENT_MOUSE_CLICKED = 200; + public static final short EVENT_MOUSE_ENTERED = 201; + public static final short EVENT_MOUSE_EXITED = 202; + public static final short EVENT_MOUSE_PRESSED = 203; + public static final short EVENT_MOUSE_RELEASED = 204; + public static final short EVENT_MOUSE_MOVED = 205; + public static final short EVENT_MOUSE_DRAGGED = 206; + public static final short EVENT_MOUSE_WHEEL_MOVED = 207; } diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java index 9d8d92ff6..6f4561ce6 100644 --- a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java @@ -48,81 +48,27 @@ package com.jogamp.newt.event; */ @SuppressWarnings("serial") public class NEWTEvent extends java.util.EventObject { - private final boolean isSystemEvent; - private final int eventType; + /** + * 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; static final boolean DEBUG = false; - // 0: NEWTEvent.java - // 1: InputEvent.java - // 2: KeyEvent.java - // 3: com.jogamp.newt.Window - // 3: com.jogamp.newt.event.awt.AWTNewtEventFactory - // 2: MouseEvent.java - // 3: com.jogamp.newt.Window - // 3: com.jogamp.newt.event.awt.AWTNewtEventFactory - // 1: WindowEvent.java - // 2: com.jogamp.newt.Window - // 2: com.jogamp.newt.event.awt.AWTNewtEventFactory - // - // FIXME: verify the isSystemEvent evaluation - // - static final String WindowClazzName = "com.jogamp.newt.Window" ; - static final String AWTNewtEventFactoryClazzName = "com.jogamp.newt.event.awt.AWTNewtEventFactory" ; - - /** - static final boolean evaluateIsSystemEvent(NEWTEvent event, Throwable t) { - StackTraceElement[] stack = t.getStackTrace(); - if(stack.length==0 || null==stack[0]) { - return false; - } - if(DEBUG) { - for (int i = 0; i < stack.length && i<5; i++) { - System.err.println(i+": " + stack[i].getClassName()+ "." + stack[i].getMethodName()); - } - } - - String clazzName = null; - - if( event instanceof com.jogamp.newt.event.WindowEvent ) { - if ( stack.length > 2 ) { - clazzName = stack[2].getClassName(); - } - } else if( (event instanceof com.jogamp.newt.event.MouseEvent) || - (event instanceof com.jogamp.newt.event.KeyEvent) ) { - if ( stack.length > 3 ) { - clazzName = stack[3].getClassName(); - } - } - - boolean res = null!=clazzName && ( - clazzName.equals(WindowClazzName) || - clazzName.equals(AWTNewtEventFactoryClazzName) ) ; - if(DEBUG) { - System.err.println("system: "+res); - } - return res; - } */ - - protected NEWTEvent(int eventType, Object source, long when) { + protected NEWTEvent(short eventType, Object source, long when) { super(source); - // this.isSystemEvent = evaluateIsSystemEvent(this, new Throwable()); - this.isSystemEvent = false; // FIXME: Need a more efficient way to determine system events this.eventType = eventType; this.when = when; this.attachment=null; } - /** Indicates whether this event was produced by the system or - generated by user code. */ - public final boolean isSystemEvent() { - return isSystemEvent; - } - /** Returns the event type of this event. */ - public final int getEventType() { + public final short getEventType() { return eventType; } @@ -158,10 +104,10 @@ public class NEWTEvent extends java.util.EventObject { if(null == sb) { sb = new StringBuilder(); } - return sb.append("NEWTEvent[sys:").append(isSystemEvent()).append(", source:").append(getSource().getClass().getName()).append(", when:").append(getWhen()).append(" d ").append((System.currentTimeMillis()-getWhen())).append("ms]"); + return sb.append("NEWTEvent[source:").append(getSource().getClass().getName()).append(", when:").append(getWhen()).append(" d ").append((System.currentTimeMillis()-getWhen())).append("ms]"); } - static String toHexString(int hex) { - return "0x" + Integer.toHexString(hex); + static String toHexString(short hex) { + return "0x" + Integer.toHexString( (int)hex & 0x0000FFFF ); } } diff --git a/src/newt/classes/com/jogamp/newt/event/WindowEvent.java b/src/newt/classes/com/jogamp/newt/event/WindowEvent.java index 163b51439..24b3b380a 100644 --- a/src/newt/classes/com/jogamp/newt/event/WindowEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/WindowEvent.java @@ -41,19 +41,19 @@ package com.jogamp.newt.event; */ @SuppressWarnings("serial") public class WindowEvent extends NEWTEvent { - public static final int EVENT_WINDOW_RESIZED = 100; - public static final int EVENT_WINDOW_MOVED = 101; - public static final int EVENT_WINDOW_DESTROY_NOTIFY = 102; - public static final int EVENT_WINDOW_GAINED_FOCUS = 103; - public static final int EVENT_WINDOW_LOST_FOCUS = 104; - public static final int EVENT_WINDOW_REPAINT = 105; - public static final int EVENT_WINDOW_DESTROYED = 106; + public static final short EVENT_WINDOW_RESIZED = 100; + public static final short EVENT_WINDOW_MOVED = 101; + public static final short EVENT_WINDOW_DESTROY_NOTIFY = 102; + public static final short EVENT_WINDOW_GAINED_FOCUS = 103; + public static final short EVENT_WINDOW_LOST_FOCUS = 104; + public static final short EVENT_WINDOW_REPAINT = 105; + public static final short EVENT_WINDOW_DESTROYED = 106; - public WindowEvent(int eventType, Object source, long when) { + public WindowEvent(short eventType, Object source, long when) { super(eventType, source, when); } - public static String getEventTypeString(int type) { + public static String getEventTypeString(short type) { switch(type) { case EVENT_WINDOW_RESIZED: return "WINDOW_RESIZED"; case EVENT_WINDOW_MOVED: return "WINDOW_MOVED"; 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/WindowUpdateEvent.java b/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java index e3f0373ec..a0f6e2cb4 100644 --- a/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java @@ -34,7 +34,7 @@ import javax.media.nativewindow.util.Rectangle; public class WindowUpdateEvent extends WindowEvent { final Rectangle bounds; - public WindowUpdateEvent(int eventType, Object source, long when, Rectangle bounds) + public WindowUpdateEvent(short eventType, Object source, long when, Rectangle bounds) { super(eventType, source, when); this.bounds = bounds; 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/AWTKeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java index 64071eed6..1edef347b 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java @@ -72,14 +72,11 @@ public class AWTKeyAdapter extends AWTAdapter implements java.awt.event.KeyListe @Override public void keyReleased(java.awt.event.KeyEvent e) { com.jogamp.newt.event.KeyEvent keyReleaseEvt = AWTNewtEventFactory.createKeyEvent(com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED, e, newtWindow); - com.jogamp.newt.event.KeyEvent keyTypedEvt = AWTNewtEventFactory.createKeyEvent(com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED, e, newtWindow); if(null!=newtListener) { final com.jogamp.newt.event.KeyListener newtKeyListener = (com.jogamp.newt.event.KeyListener)newtListener; newtKeyListener.keyReleased(keyReleaseEvt); - newtKeyListener.keyTyped(keyTypedEvt); } else { enqueueEvent(false, keyReleaseEvt); - enqueueEvent(false, keyTypedEvt); } } 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 cb43fae32..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; @@ -121,10 +121,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private ArrayList<NativeWindow> childWindows = new ArrayList<NativeWindow>(); private ArrayList<MouseListener> mouseListeners = new ArrayList<MouseListener>(); - private int mouseButtonPressed = 0; // current pressed mouse button number - private int mouseButtonModMask = 0; // current pressed mouse button modifier mask + private short mouseButtonPressed = (short)0; // current pressed mouse button number + private int mouseButtonModMask = 0; // current pressed mouse button modifier mask private long lastMousePressed = 0; // last time when a mouse button was pressed - private int lastMouseClickCount = 0; // last mouse button click count + private short lastMouseClickCount = (short)0; // last mouse button click count private boolean mouseInWindow = false;// mouse entered window - is inside the window (may be synthetic) private Point lastMousePosition = new Point(); @@ -527,9 +527,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected void setTitleImpl(String title) {} /** - * Return screen coordinates of the given coordinates - * or null, in which case a NativeWindow traversal shall being used + * Translates the given window client-area coordinates with top-left origin + * to screen coordinates. + * <p> + * Since the position reflects the client area, it does not include the insets. + * </p> + * <p> + * May return <code>null</code>, in which case the caller shall traverse through the NativeWindow tree * as demonstrated in {@link #getLocationOnScreen(javax.media.nativewindow.util.Point)}. + * </p> * * @return if not null, the screen location of the given coordinates */ @@ -644,14 +650,17 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // public final void destroy() - see below + @Override public final NativeWindow getParent() { return parentWindow; } + @Override public final long getWindowHandle() { return windowHandle; } + @Override public Point getLocationOnScreen(Point storage) { if(isNativeValid()) { Point d; @@ -688,14 +697,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Window // + @Override public final boolean isNativeValid() { return 0 != windowHandle ; } + @Override public final Screen getScreen() { return screen; } - + protected final void setVisibleImpl(boolean visible, int x, int y, int width, int height) { reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible)); } @@ -778,7 +789,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } runOnEDTIfAvail(wait, new VisibleAction(visible)); } - + + @Override public void setVisible(boolean visible) { setVisible(true, visible); } @@ -830,9 +842,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } + @Override public void setSize(int width, int height) { runOnEDTIfAvail(true, new SetSizeAction(width, height)); } + @Override public void setTopLevelSize(int width, int height) { setSize(width - getInsets().getTotalWidth(), height - getInsets().getTotalHeight()); } @@ -850,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) { @@ -861,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(); } @@ -932,6 +949,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } private final DestroyAction destroyAction = new DestroyAction(); + @Override public void destroy() { visible = false; // Immediately mark synchronized visibility flag, avoiding possible recreation runOnEDTIfAvail(true, destroyAction); @@ -1253,6 +1271,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } private final ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate(); + @Override public final ReparentOperation reparentWindow(NativeWindow newParent) { return reparentWindow(newParent, false); } @@ -1263,16 +1282,19 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return reparentAction.getOp(); } + @Override public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { CapabilitiesChooser old = this.capabilitiesChooser; this.capabilitiesChooser = chooser; return old; } + @Override public final CapabilitiesImmutable getChosenCapabilities() { return getGraphicsConfiguration().getChosenCapabilities(); } + @Override public final CapabilitiesImmutable getRequestedCapabilities() { return capsRequested; } @@ -1312,10 +1334,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } + @Override public void setUndecorated(boolean value) { runOnEDTIfAvail(true, new DecorationAction(value)); } + @Override public final boolean isUndecorated() { return 0 != parentWindowHandle || undecorated || fullscreen ; } @@ -1355,17 +1379,21 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } + @Override public final void setAlwaysOnTop(boolean value) { runOnEDTIfAvail(true, new AlwaysOnTopAction(value)); } + @Override public final boolean isAlwaysOnTop() { return alwaysOnTop; } + @Override public String getTitle() { return title; } + @Override public void setTitle(String title) { if (title == null) { title = ""; @@ -1376,9 +1404,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } + @Override public boolean isPointerVisible() { return pointerVisible; } + @Override public void setPointerVisible(boolean pointerVisible) { if(this.pointerVisible != pointerVisible) { boolean setVal = 0 == getWindowHandle(); @@ -1390,10 +1420,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } + @Override public boolean isPointerConfined() { return pointerConfined; } + @Override public void confinePointer(boolean confine) { if(this.pointerConfined != confine) { boolean setVal = 0 == getWindowHandle(); @@ -1417,12 +1449,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } + @Override public void warpPointer(int x, int y) { if(0 != getWindowHandle()) { warpPointerImpl(x, y); } } + @Override public final InsetsImmutable getInsets() { if(isUndecorated()) { return Insets.getZero(); @@ -1431,18 +1465,22 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return insets; } + @Override public final int getWidth() { return width; } + @Override public final int getHeight() { return height; } + @Override public final int getX() { return x; } + @Override public final int getY() { return y; } @@ -1468,10 +1506,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer this.width = width; this.height = height; } + @Override public final boolean isVisible() { return visible; } + @Override public final boolean isFullscreen() { return fullscreen; } @@ -1480,6 +1520,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Window // + @Override + public final Window getDelegatedWindow() { + return this; + } + + //---------------------------------------------------------------------- + // WindowImpl + // + /** * If the implementation is capable of detecting a device change * return true and clear the status/reason of the change. @@ -1503,23 +1552,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public Object getWrappedWindow() { return null; } - - public final Window getDelegatedWindow() { - return this; - } - - /** - * 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; - } - //---------------------------------------------------------------------- - // WindowImpl - // + @Override + public void setWindowDestroyNotifyAction(Runnable r) { + windowDestroyNotifyAction = r; + } /** * Returns the non delegated {@link AbstractGraphicsConfiguration}, @@ -1976,17 +2013,17 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // // MouseListener/Event Support // - public void sendMouseEvent(int eventType, int modifiers, - int x, int y, int button, float rotation) { + public void sendMouseEvent(short eventType, int modifiers, + int x, int y, short button, float rotation) { doMouseEvent(false, false, eventType, modifiers, x, y, button, rotation); } - public void enqueueMouseEvent(boolean wait, int eventType, int modifiers, - int x, int y, int button, float rotation) { + public void enqueueMouseEvent(boolean wait, short eventType, int modifiers, + int x, int y, short button, float rotation) { doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotation); } - protected void doMouseEvent(boolean enqueue, boolean wait, int eventType, int modifiers, - int x, int y, int button, float rotation) { + protected void doMouseEvent(boolean enqueue, boolean wait, short eventType, int modifiers, + int x, int y, short button, float rotation) { if( eventType == MouseEvent.EVENT_MOUSE_ENTERED || eventType == MouseEvent.EVENT_MOUSE_EXITED ) { if( eventType == MouseEvent.EVENT_MOUSE_EXITED && x==-1 && y==-1 ) { x = lastMousePosition.getX(); @@ -1996,9 +2033,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer x = Math.min(Math.max(x, 0), getWidth()-1); y = Math.min(Math.max(y, 0), getHeight()-1); mouseInWindow = eventType == MouseEvent.EVENT_MOUSE_ENTERED; - lastMousePressed = 0; // clear state - mouseButtonPressed = 0; // clear state - mouseButtonModMask = 0; // clear state + // clear states + lastMousePressed = 0; + lastMouseClickCount = (short)0; + mouseButtonPressed = 0; + mouseButtonModMask = 0; } if( x < 0 || y < 0 || x >= getWidth() || y >= getHeight() ) { return; // .. invalid .. @@ -2013,10 +2052,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(!mouseInWindow) { mouseInWindow = true; eEntered = new MouseEvent(MouseEvent.EVENT_MOUSE_ENTERED, this, when, - modifiers, x, y, lastMouseClickCount, button, 0); - lastMousePressed = 0; // clear state - mouseButtonPressed = 0; // clear state - mouseButtonModMask = 0; // clear state + modifiers, x, y, (short)0, (short)0, (short)0); + // clear states + lastMousePressed = 0; + lastMouseClickCount = (short)0; + mouseButtonPressed = 0; + mouseButtonModMask = 0; } else if( lastMousePosition.getX() == x && lastMousePosition.getY()==y ) { if(DEBUG_MOUSE_EVENT) { System.err.println("doMouseEvent: skip EVENT_MOUSE_MOVED w/ same position: "+lastMousePosition); @@ -2046,7 +2087,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if( when - lastMousePressed < MouseEvent.getClickTimeout() ) { lastMouseClickCount++; } else { - lastMouseClickCount=1; + lastMouseClickCount=(short)1; } lastMousePressed = when; mouseButtonPressed = button; @@ -2060,7 +2101,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer eClicked = new MouseEvent(MouseEvent.EVENT_MOUSE_CLICKED, this, when, modifiers, x, y, lastMouseClickCount, button, 0); } else { - lastMouseClickCount = 0; + lastMouseClickCount = (short)0; lastMousePressed = 0; } mouseButtonPressed = 0; @@ -2068,15 +2109,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } else if( MouseEvent.EVENT_MOUSE_MOVED == eventType ) { if ( mouseButtonPressed > 0 ) { e = new MouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, this, when, - modifiers, x, y, 1, mouseButtonPressed, 0); + modifiers, x, y, (short)1, mouseButtonPressed, 0); } else { e = new MouseEvent(eventType, this, when, - modifiers, x, y, 0, button, 0); + modifiers, x, y, (short)0, button, (short)0); } } else if( MouseEvent.EVENT_MOUSE_WHEEL_MOVED == eventType ) { - e = new MouseEvent(eventType, this, when, modifiers, x, y, 0, button, rotation); + e = new MouseEvent(eventType, this, when, modifiers, x, y, (short)0, button, rotation); } else { - e = new MouseEvent(eventType, this, when, modifiers, x, y, 0, button, 0); + e = new MouseEvent(eventType, this, when, modifiers, x, y, (short)0, button, 0); } if( null != eEntered ) { if(DEBUG_MOUSE_EVENT) { @@ -2169,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(); } } @@ -2203,23 +2244,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return 0 <= keyCode && keyCode < keyRepeatState.capacity(); } - public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { - doKeyEvent(false, false, eventType, modifiers, keyCode, keyChar); + public void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + // Always add currently pressed mouse buttons to modifier mask + consumeKeyEvent(new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers | mouseButtonModMask, keyCode, keySym, keyChar) ); } - public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - doKeyEvent(true, wait, eventType, modifiers, keyCode, keyChar); + public void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + // Always add currently pressed mouse buttons to modifier mask + enqueueEvent(wait, new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers | mouseButtonModMask, keyCode, keySym, keyChar) ); } - protected void doKeyEvent(boolean enqueue, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - modifiers |= mouseButtonModMask; // Always add currently pressed mouse buttons to modifier mask - if( enqueue ) { - enqueueEvent(wait, new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); - } else { - consumeKeyEvent(new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); - } - } - public void addKeyListener(KeyListener l) { addKeyListener(-1, l); } @@ -2290,6 +2324,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return keyListeners.toArray(new KeyListener[keyListeners.size()]); } + @SuppressWarnings("deprecation") private final boolean propagateKeyEvent(KeyEvent e, KeyListener l) { switch(e.getEventType()) { case KeyEvent.EVENT_KEY_PRESSED: @@ -2304,24 +2339,52 @@ 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") protected void consumeKeyEvent(KeyEvent e) { - boolean consumed; + boolean consumedE = false, consumedTyped = false; + if( KeyEvent.EVENT_KEY_TYPED == e.getEventType() ) { + throw new InternalError("Deprecated KeyEvent.EVENT_KEY_TYPED is synthesized - don't send/enqueue it!"); + } + + // Synthesize deprecated event KeyEvent.EVENT_KEY_TYPED + final KeyEvent eTyped; + if( KeyEvent.EVENT_KEY_RELEASED == e.getEventType() && e.isPrintableKey() && !e.isAutoRepeat() ) { + eTyped = new KeyEvent(KeyEvent.EVENT_KEY_TYPED, e.getSource(), e.getWhen(), e.getModifiers(), e.getKeyCode(), e.getKeySymbol(), e.getKeyChar()); + } else { + eTyped = null; + } if(null != keyboardFocusHandler) { - consumed = propagateKeyEvent(e, keyboardFocusHandler); + consumedE = propagateKeyEvent(e, keyboardFocusHandler); if(DEBUG_KEY_EVENT) { - System.err.println("consumeKeyEvent: "+e+", keyboardFocusHandler consumed: "+consumed); + System.err.println("consumeKeyEvent: "+e+", keyboardFocusHandler consumed: "+consumedE); } - } else { - consumed = false; - if(DEBUG_KEY_EVENT) { + if( null != eTyped ) { + consumedTyped = propagateKeyEvent(eTyped, keyboardFocusHandler); + if(DEBUG_KEY_EVENT) { + System.err.println("consumeKeyEvent: "+eTyped+", keyboardFocusHandler consumed: "+consumedTyped); + } + } + } + if(DEBUG_KEY_EVENT) { + if( !consumedE ) { System.err.println("consumeKeyEvent: "+e); } } - for(int i = 0; !consumed && i < keyListeners.size(); i++ ) { - consumed = propagateKeyEvent(e, keyListeners.get(i)); + for(int i = 0; !consumedE && i < keyListeners.size(); i++ ) { + consumedE = propagateKeyEvent(e, keyListeners.get(i)); + } + if( null != eTyped ) { + if(DEBUG_KEY_EVENT) { + if( !consumedTyped ) { + System.err.println("consumeKeyEvent: "+eTyped); + } + } + for(int i = 0; !consumedTyped && i < keyListeners.size(); i++ ) { + consumedTyped = propagateKeyEvent(eTyped, keyListeners.get(i)); + } } } @@ -2329,11 +2392,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // WindowListener/Event Support // public void sendWindowEvent(int eventType) { - consumeWindowEvent( new WindowEvent(eventType, this, System.currentTimeMillis()) ); + consumeWindowEvent( new WindowEvent((short)eventType, this, System.currentTimeMillis()) ); // FIXME } public void enqueueWindowEvent(boolean wait, int eventType) { - enqueueEvent( wait, new WindowEvent(eventType, this, System.currentTimeMillis()) ); + enqueueEvent( wait, new WindowEvent((short)eventType, this, System.currentTimeMillis()) ); // FIXME } public void addWindowListener(WindowListener l) { @@ -2382,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: @@ -2411,6 +2475,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer new NativeWindowException("Unexpected window event type " + e.getEventType()); } + consumed = NEWTEvent.consumedTag == e.getAttachment(); } } @@ -2551,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); + System.err.println("Window.windowDestroyNotify(force: "+force+", mode "+defMode+" -> "+mode+") "+getThreadName()+": "+this); } - if(force) { - setDefaultCloseOperation(WindowClosingMode.DISPOSE_ON_CLOSE); - } - - // 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/AWTNewtEventFactory.java b/src/newt/classes/jogamp/newt/awt/event/AWTNewtEventFactory.java index e11d79ddc..b90c2106e 100644 --- a/src/newt/classes/jogamp/newt/awt/event/AWTNewtEventFactory.java +++ b/src/newt/classes/jogamp/newt/awt/event/AWTNewtEventFactory.java @@ -28,8 +28,6 @@ package jogamp.newt.awt.event; -import com.jogamp.common.util.IntIntHashMap; - /** * * <a name="AWTEventModifierMapping"><h5>AWT Event Modifier Mapping</h5></a> @@ -76,47 +74,10 @@ import com.jogamp.common.util.IntIntHashMap; */ public class AWTNewtEventFactory { - protected static final IntIntHashMap eventTypeAWT2NEWT; - /** zero-based AWT button mask array filled by {@link #getAWTButtonDownMask(int)}, allowing fast lookup. */ private static int awtButtonDownMasks[] ; static { - IntIntHashMap map = new IntIntHashMap(); - map.setKeyNotFoundValue(0xFFFFFFFF); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_OPENED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_OPENED); - map.put(java.awt.event.WindowEvent.WINDOW_CLOSING, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); - map.put(java.awt.event.WindowEvent.WINDOW_CLOSED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROYED); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_ICONIFIED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_ICONIFIED); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_DEICONIFIED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DEICONIFIED); - map.put(java.awt.event.WindowEvent.WINDOW_ACTIVATED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS); - map.put(java.awt.event.WindowEvent.WINDOW_GAINED_FOCUS, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS); - map.put(java.awt.event.FocusEvent.FOCUS_GAINED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS); - map.put(java.awt.event.WindowEvent.WINDOW_DEACTIVATED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS); - map.put(java.awt.event.WindowEvent.WINDOW_LOST_FOCUS, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS); - map.put(java.awt.event.FocusEvent.FOCUS_LOST, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_STATE_CHANGED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_STATE_CHANGED); - - map.put(java.awt.event.ComponentEvent.COMPONENT_MOVED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_MOVED); - map.put(java.awt.event.ComponentEvent.COMPONENT_RESIZED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_RESIZED); - // n/a map.put(java.awt.event.ComponentEvent.COMPONENT_SHOWN, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_SHOWN); - // n/a map.put(java.awt.event.ComponentEvent.COMPONENT_HIDDEN, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_HIDDEN); - - map.put(java.awt.event.MouseEvent.MOUSE_CLICKED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED); - map.put(java.awt.event.MouseEvent.MOUSE_PRESSED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED); - map.put(java.awt.event.MouseEvent.MOUSE_RELEASED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED); - map.put(java.awt.event.MouseEvent.MOUSE_MOVED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED); - map.put(java.awt.event.MouseEvent.MOUSE_ENTERED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED); - map.put(java.awt.event.MouseEvent.MOUSE_EXITED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED); - map.put(java.awt.event.MouseEvent.MOUSE_DRAGGED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED); - map.put(java.awt.event.MouseEvent.MOUSE_WHEEL, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED); - - map.put(java.awt.event.KeyEvent.KEY_PRESSED, com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED); - map.put(java.awt.event.KeyEvent.KEY_RELEASED, com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED); - map.put(java.awt.event.KeyEvent.KEY_TYPED, com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED); - - eventTypeAWT2NEWT = map; - // There is an assumption in awtModifiers2Newt(int,int,boolean) // that the awtButtonMasks and newtButtonMasks are peers, i.e. // a given index refers to the same button in each array. @@ -135,6 +96,41 @@ public class AWTNewtEventFactory { } } + public static final short eventTypeAWT2NEWT(int awtType) { + switch( awtType ) { + // n/a case java.awt.event.WindowEvent.WINDOW_OPENED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_OPENED; + case java.awt.event.WindowEvent.WINDOW_CLOSING: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY; + case java.awt.event.WindowEvent.WINDOW_CLOSED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROYED; + // n/a case java.awt.event.WindowEvent.WINDOW_ICONIFIED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_ICONIFIED; + // n/a case java.awt.event.WindowEvent.WINDOW_DEICONIFIED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DEICONIFIED; + case java.awt.event.WindowEvent.WINDOW_ACTIVATED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; + case java.awt.event.WindowEvent.WINDOW_GAINED_FOCUS: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; + case java.awt.event.FocusEvent.FOCUS_GAINED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; + case java.awt.event.WindowEvent.WINDOW_DEACTIVATED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS; + case java.awt.event.WindowEvent.WINDOW_LOST_FOCUS: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS; + case java.awt.event.FocusEvent.FOCUS_LOST: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS; + // n/a case java.awt.event.WindowEvent.WINDOW_STATE_CHANGED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_STATE_CHANGED; + + case java.awt.event.ComponentEvent.COMPONENT_MOVED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_MOVED; + case java.awt.event.ComponentEvent.COMPONENT_RESIZED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_RESIZED; + // n/a case java.awt.event.ComponentEvent.COMPONENT_SHOWN: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_SHOWN; + // n/a case java.awt.event.ComponentEvent.COMPONENT_HIDDEN: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_HIDDEN; + + case java.awt.event.MouseEvent.MOUSE_CLICKED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED; + case java.awt.event.MouseEvent.MOUSE_PRESSED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; + case java.awt.event.MouseEvent.MOUSE_RELEASED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; + case java.awt.event.MouseEvent.MOUSE_MOVED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED; + case java.awt.event.MouseEvent.MOUSE_ENTERED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED; + case java.awt.event.MouseEvent.MOUSE_EXITED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED; + case java.awt.event.MouseEvent.MOUSE_DRAGGED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED; + case java.awt.event.MouseEvent.MOUSE_WHEEL: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + + case java.awt.event.KeyEvent.KEY_PRESSED: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; + case java.awt.event.KeyEvent.KEY_RELEASED: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; + } + return (short)0; + } + private static int getAWTButtonDownMaskImpl(int button) { /** * java.awt.event.InputEvent.getMaskForButton(button); @@ -227,48 +223,48 @@ public class AWTNewtEventFactory { return newtMods; } - public static final int awtButton2Newt(int awtButton) { + public static final short awtButton2Newt(int awtButton) { if( 0 < awtButton && awtButton <= com.jogamp.newt.event.MouseEvent.BUTTON_NUMBER ) { - return awtButton; + return (short)awtButton; } else { - return 0; + return (short)0; } } public static final com.jogamp.newt.event.WindowEvent createWindowEvent(java.awt.event.WindowEvent event, com.jogamp.newt.Window newtSource) { - final int newtType = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != newtType) { + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { return new com.jogamp.newt.event.WindowEvent(newtType, ((null==newtSource)?(Object)event.getComponent():(Object)newtSource), System.currentTimeMillis()); } return null; // no mapping .. } public static final com.jogamp.newt.event.WindowEvent createWindowEvent(java.awt.event.ComponentEvent event, com.jogamp.newt.Window newtSource) { - final int newtType = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != newtType) { + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { return new com.jogamp.newt.event.WindowEvent(newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); } return null; // no mapping .. } public static final com.jogamp.newt.event.WindowEvent createWindowEvent(java.awt.event.FocusEvent event, com.jogamp.newt.Window newtSource) { - final int newtType = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != newtType) { + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { return new com.jogamp.newt.event.WindowEvent(newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); } return null; // no mapping .. } public static final com.jogamp.newt.event.MouseEvent createMouseEvent(java.awt.event.MouseEvent event, com.jogamp.newt.Window newtSource) { - final int newtType = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != newtType) { + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { float rotation = 0; if (event instanceof java.awt.event.MouseWheelEvent) { // AWT/NEWT rotation is reversed - AWT +1 is down, NEWT +1 is up. rotation = -1f * ((java.awt.event.MouseWheelEvent)event).getWheelRotation(); } - final int newtButton = awtButton2Newt(event.getButton()); + final short newtButton = awtButton2Newt(event.getButton()); int mods = awtModifiers2Newt(event.getModifiers(), event.getModifiersEx()); mods |= com.jogamp.newt.event.InputEvent.getButtonMask(newtButton); // always include NEWT BUTTON_MASK if(null!=newtSource) { @@ -281,25 +277,25 @@ public class AWTNewtEventFactory { } return new com.jogamp.newt.event.MouseEvent( newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), - mods, event.getX(), event.getY(), event.getClickCount(), + mods, event.getX(), event.getY(), (short)event.getClickCount(), newtButton, rotation); } return null; // no mapping .. } public static final com.jogamp.newt.event.KeyEvent createKeyEvent(java.awt.event.KeyEvent event, com.jogamp.newt.Window newtSource) { - return createKeyEvent(eventTypeAWT2NEWT.get(event.getID()), event, newtSource); + return createKeyEvent(eventTypeAWT2NEWT(event.getID()), event, newtSource); } - public static final com.jogamp.newt.event.KeyEvent createKeyEvent(int newtType, java.awt.event.KeyEvent event, com.jogamp.newt.Window newtSource) { - if(0xFFFFFFFF != newtType) { + public static final com.jogamp.newt.event.KeyEvent createKeyEvent(short newtType, java.awt.event.KeyEvent event, com.jogamp.newt.Window newtSource) { + if( (short)0 != newtType ) { + final short keyCode = (short)event.getKeyCode(); return new com.jogamp.newt.event.KeyEvent( newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), awtModifiers2Newt(event.getModifiers(), event.getModifiersEx()), - event.getKeyCode(), event.getKeyChar()); + keyCode, keyCode, event.getKeyChar()); } return null; // no mapping .. } } - diff --git a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java index 747e2368e..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; @@ -98,7 +96,7 @@ public class AWTParentWindowAdapter public void run() { int cw = comp.getWidth(); int ch = comp.getHeight(); - if( 0 < cw * ch ) { + if( 0 < cw && 0 < ch ) { if( newtWindow.getWidth() != cw || newtWindow.getHeight() != ch ) { newtWindow.setSize(cw, ch); if(comp.isVisible() != newtWindow.isVisible()) { diff --git a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java index 55fbbe603..4f19e9c4d 100644 --- a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java +++ b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java @@ -42,78 +42,62 @@ public class AndroidNewtEventFactory { /** API Level 12: {@link android.view.MotionEvent#ACTION_SCROLL} = {@value} */ private static final int ACTION_SCROLL = 8; - private static final int aMotionEventType2Newt(int aType) { - final int nType; + private static final short aMotionEventType2Newt(int aType) { switch( aType ) { case android.view.MotionEvent.ACTION_DOWN: - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; - break; + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; case android.view.MotionEvent.ACTION_UP: - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; - break; + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; case android.view.MotionEvent.ACTION_MOVE: - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED; - break; + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED; case android.view.MotionEvent.ACTION_CANCEL: - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; - break; + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; case android.view.MotionEvent.ACTION_OUTSIDE: - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED; - break; - // + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED; case android.view.MotionEvent.ACTION_POINTER_DOWN: - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; - break; + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; case android.view.MotionEvent.ACTION_POINTER_UP: - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; - break; + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; // case ACTION_HOVER_MOVE case ACTION_SCROLL: // API Level 12 ! - nType = com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - break; + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED; // case ACTION_HOVER_ENTER // case ACTION_HOVER_EXIT - default: - nType = 0xFFFFFFFF; } - return nType; + return (short)0; } - private static final int aAccessibilityEventType2Newt(int aType) { - final int nType; + private static final short aAccessibilityEventType2Newt(int aType) { switch( aType ) { case android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED: - nType = com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; break; - default: - nType = 0xFFFFFFFF; + return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; } - return nType; + return (short)0; } - private static final int aKeyEventType2NewtEventType(int androidKeyAction) { + private static final short aKeyEventType2NewtEventType(int androidKeyAction) { switch(androidKeyAction) { case android.view.KeyEvent.ACTION_DOWN: case android.view.KeyEvent.ACTION_MULTIPLE: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; case android.view.KeyEvent.ACTION_UP: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; - default: - return 0; } + return (short)0; } - private static final int aKeyCode2NewtKeyCode(int androidKeyCode) { + private static final short aKeyCode2NewtKeyCode(int androidKeyCode) { if(android.view.KeyEvent.KEYCODE_0 <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_9) { - return com.jogamp.newt.event.KeyEvent.VK_0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_0 ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_0 ) ); } if(android.view.KeyEvent.KEYCODE_A <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_Z) { - return com.jogamp.newt.event.KeyEvent.VK_A + ( androidKeyCode - android.view.KeyEvent.KEYCODE_A ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_A + ( androidKeyCode - android.view.KeyEvent.KEYCODE_A ) ); } if(android.view.KeyEvent.KEYCODE_F1 <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_F12) { - return com.jogamp.newt.event.KeyEvent.VK_F1 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_F1 ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_F1 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_F1 ) ); } if(android.view.KeyEvent.KEYCODE_NUMPAD_0 <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_NUMPAD_9) { - return com.jogamp.newt.event.KeyEvent.VK_NUMPAD0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_NUMPAD_0 ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_NUMPAD0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_NUMPAD_0 ) ); } switch(androidKeyCode) { case android.view.KeyEvent.KEYCODE_COMMA: return com.jogamp.newt.event.KeyEvent.VK_COMMA; @@ -144,7 +128,7 @@ public class AndroidNewtEventFactory { case android.view.KeyEvent.KEYCODE_CTRL_LEFT: return com.jogamp.newt.event.KeyEvent.VK_CONTROL; case android.view.KeyEvent.KEYCODE_CTRL_RIGHT: return com.jogamp.newt.event.KeyEvent.VK_CONTROL; // ?? } - return 0; + return (short)0; } private static final int aKeyModifiers2Newt(int androidMods) { @@ -170,38 +154,29 @@ public class AndroidNewtEventFactory { public com.jogamp.newt.event.WindowEvent createWindowEvent(android.view.accessibility.AccessibilityEvent event, com.jogamp.newt.Window newtSource) { final int aType = event.getEventType(); - final int nType = aAccessibilityEventType2Newt(aType); + final short nType = aAccessibilityEventType2Newt(aType); - if(0xFFFFFFFF != nType) { + if( (short)0 != nType) { return new com.jogamp.newt.event.WindowEvent(nType, ((null==newtSource)?null:(Object)newtSource), event.getEventTime()); } return null; // no mapping .. } - public com.jogamp.newt.event.KeyEvent[] createKeyEvents(int keyCode, android.view.KeyEvent event, com.jogamp.newt.Window newtSource) { - final int type = aKeyEventType2NewtEventType(event.getAction()); + public com.jogamp.newt.event.KeyEvent createKeyEvent(int keyCode, android.view.KeyEvent event, com.jogamp.newt.Window newtSource) { + final short type = aKeyEventType2NewtEventType(event.getAction()); if(Window.DEBUG_MOUSE_EVENT) { System.err.println("createKeyEvent: type 0x"+Integer.toHexString(type)+", keyCode 0x"+Integer.toHexString(keyCode)+", "+event); } - if(0xFFFFFFFF != type) { - final int newtKeyCode = aKeyCode2NewtKeyCode(keyCode); - if(0 != newtKeyCode) { + if( (short)0 != type) { + final short newtKeyCode = aKeyCode2NewtKeyCode(keyCode); + if( (short)0 != newtKeyCode ) { final Object src = (null==newtSource)?null:(Object)newtSource; final long unixTime = System.currentTimeMillis() + ( event.getEventTime() - android.os.SystemClock.uptimeMillis() ); final int newtMods = aKeyModifiers2Newt(event.getMetaState()); - final com.jogamp.newt.event.KeyEvent ke1 = new com.jogamp.newt.event.KeyEvent( - type, src, unixTime, newtMods, newtKeyCode, (char) event.getUnicodeChar()); - - if( com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED == type ) { - return new com.jogamp.newt.event.KeyEvent[] { ke1, - new com.jogamp.newt.event.KeyEvent( - com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED, - src, unixTime, newtMods, newtKeyCode, (char) event.getUnicodeChar()) }; - } else { - return new com.jogamp.newt.event.KeyEvent[] { ke1 }; - } + return new com.jogamp.newt.event.KeyEvent( + type, src, unixTime, newtMods, newtKeyCode, newtKeyCode, (char) event.getUnicodeChar()); } } return null; @@ -218,7 +193,8 @@ public class AndroidNewtEventFactory { // // Prefilter Android Event (Gesture, ..) and determine final type // - final int aType, nType; + final int aType; + final short nType; float[] rotationXY = null; int rotationSource = 0; // 1 - Gesture, 2 - ACTION_SCROLL { @@ -265,8 +241,8 @@ public class AndroidNewtEventFactory { } } - if(0xFFFFFFFF != nType) { - final int clickCount = 1; + if( (short)0 != nType ) { + final short clickCount = 1; int modifiers = 0; if( null == rotationXY && AndroidVersion.SDK_INT >= 12 && ACTION_SCROLL == aType ) { // API Level 12 @@ -299,7 +275,7 @@ public class AndroidNewtEventFactory { // final int pCount; final int pIndex; - final int button; + final short button; switch( aType ) { case android.view.MotionEvent.ACTION_POINTER_DOWN: case android.view.MotionEvent.ACTION_POINTER_UP: { @@ -307,7 +283,7 @@ public class AndroidNewtEventFactory { pCount = 1; final int b = event.getPointerId(pIndex) + 1; // FIXME: Assumption that Pointer-ID starts w/ 0 ! if( com.jogamp.newt.event.MouseEvent.BUTTON1 <= b && b <= com.jogamp.newt.event.MouseEvent.BUTTON_NUMBER ) { - button = b; + button = (short)b; } else { button = com.jogamp.newt.event.MouseEvent.BUTTON1; } @@ -326,7 +302,7 @@ public class AndroidNewtEventFactory { final int[] x = new int[pCount]; final int[] y = new int[pCount]; final float[] pressure = new float[pCount]; - final int[] pointerIds = new int[pCount]; + final short[] pointerIds = new short[pCount]; { if(Window.DEBUG_MOUSE_EVENT) { System.err.println("createMouseEvent: collect ptr-data ["+pIndex+".."+(pIndex+pCount-1)+", "+pCount+"], aType "+aType+", button "+button+", gestureScrollPointerDown "+gestureScrollPointerDown); @@ -337,7 +313,7 @@ public class AndroidNewtEventFactory { x[j] = (int)event.getX(i); y[j] = (int)event.getY(i); pressure[j] = event.getPressure(i); - pointerIds[j] = event.getPointerId(i); + pointerIds[j] = (short)event.getPointerId(i); if(Window.DEBUG_MOUSE_EVENT) { System.err.println("createMouseEvent: ptr-data["+i+" -> "+j+"] "+x[j]+"/"+y[j]+", pressure "+pressure[j]+", id "+pointerIds[j]); } diff --git a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java index ee0c8f8b1..2d972f752 100644 --- a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java +++ b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java @@ -38,11 +38,9 @@ public class AndroidNewtEventTranslator implements View.OnKeyListener, View.OnTo @Override public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { - final com.jogamp.newt.event.KeyEvent[] newtEvents = factory.createKeyEvents(keyCode, event, newtWindow); - if(null != newtEvents) { - for(int i=0; i<newtEvents.length; i++) { - newtWindow.enqueueEvent(false, newtEvents[i]); - } + final com.jogamp.newt.event.KeyEvent newtEvent = factory.createKeyEvent(keyCode, event, newtWindow); + if(null != newtEvent) { + newtWindow.enqueueEvent(false, newtEvent); return true; } return false; 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/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java index 5efce2524..2c0e6d3dd 100644 --- a/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java @@ -210,9 +210,9 @@ public class LinuxEventDeviceTracker implements WindowListener { short code; int value; - int keyCode=KeyEvent.VK_UNDEFINED; + short keyCode=KeyEvent.VK_UNDEFINED; char keyChar=' '; - int eventType=0; + short eventType=0; int modifiers=0; loop: @@ -263,24 +263,20 @@ public class LinuxEventDeviceTracker implements WindowListener { case 0: modifiers=0; eventType=KeyEvent.EVENT_KEY_RELEASED; - focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyChar); - //Send syntetic typed - focusedWindow.sendKeyEvent(KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, (char) keyChar); + focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyCode, keyChar); break; case 1: eventType=KeyEvent.EVENT_KEY_PRESSED; - focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyChar); + focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyCode, keyChar); break; case 2: eventType=KeyEvent.EVENT_KEY_PRESSED; modifiers=InputEvent.AUTOREPEAT_MASK; //Send syntetic autorepeat release - focusedWindow.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyChar); - //Send syntetic typed - focusedWindow.sendKeyEvent(KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); + focusedWindow.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyCode, keyChar); - focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyChar); + focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyCode, keyChar); break; } break; @@ -314,14 +310,15 @@ public class LinuxEventDeviceTracker implements WindowListener { stop=true; } - private char NewtVKey2Unicode(int VK){ - if(KeyEvent.isPrintableKey(VK)){ + private char NewtVKey2Unicode(short VK){ + if( KeyEvent.isPrintableKey(VK) ){ return (char)VK; } return 0; } - private char LinuxEVKey2Unicode(short EVKey) { + @SuppressWarnings("unused") + private char LinuxEVKey2Unicode(short EVKey) { // This is the stuff normally mapped by a system keymap switch(EVKey){ @@ -377,9 +374,8 @@ public class LinuxEventDeviceTracker implements WindowListener { return 0; } - private int LinuxEVKey2NewtVKey(short EVKey) { - char vkCode = KeyEvent.VK_UNDEFINED; - + private short LinuxEVKey2NewtVKey(short EVKey) { + switch(EVKey) { case 1: // ESC return KeyEvent.VK_ESCAPE; @@ -886,8 +882,9 @@ public class LinuxEventDeviceTracker implements WindowListener { if(Window.DEBUG_KEY_EVENT) { System.out.println("LinuxEVKey2NewtVKey: Unmapped EVKey "+EVKey); - } - return vkCode; + } + + return KeyEvent.VK_UNDEFINED; } } } diff --git a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java index b8a326a6c..0d6278bfb 100644 --- a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java @@ -65,10 +65,10 @@ public class LinuxMouseTracker implements WindowListener { private volatile boolean stop = false; private int x = 0; private int y = 0; - private int buttonDown = 0; + private short buttonDown = 0; private int old_x = 0; private int old_y = 0; - private int old_buttonDown = 0; + private short old_buttonDown = 0; private WindowImpl focusedWindow = null; private MouseDevicePoller mouseDevicePoller = new MouseDevicePoller(); @@ -183,19 +183,15 @@ public class LinuxMouseTracker implements WindowListener { if(old_x != x || old_y != y) { // mouse moved - focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_MOVED, 0, wx, wy, 0, 0 ); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_MOVED, 0, wx, wy, (short)0, 0 ); } if(old_buttonDown != buttonDown) { // press/release if( 0 != buttonDown ) { - focusedWindow.sendMouseEvent( - MouseEvent.EVENT_MOUSE_PRESSED, - 0, wx, wy, buttonDown, 0 ); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_PRESSED, 0, wx, wy, buttonDown, 0 ); } else { - focusedWindow.sendMouseEvent( - MouseEvent.EVENT_MOUSE_RELEASED, - 0, wx, wy, old_buttonDown, 0 ); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_RELEASED, 0, wx, wy, old_buttonDown, 0 ); } } } else { diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java b/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java index 5966bd30f..c07576901 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java @@ -31,56 +31,129 @@ import com.jogamp.newt.event.KeyEvent; public class MacKeyUtil { - // KeyCodes (independent) - private static final int kVK_Return = 0x24; - private static final int kVK_Tab = 0x30; - private static final int kVK_Space = 0x31; - private static final int kVK_Delete = 0x33; - private static final int kVK_Escape = 0x35; - private static final int kVK_Command = 0x37; - private static final int kVK_Shift = 0x38; - private static final int kVK_CapsLock = 0x39; - private static final int kVK_Option = 0x3A; - private static final int kVK_Control = 0x3B; - private static final int kVK_RightShift = 0x3C; - private static final int kVK_RightOption = 0x3D; - private static final int kVK_RightControl = 0x3E; - private static final int kVK_Function = 0x3F; - private static final int kVK_F17 = 0x40; - private static final int kVK_VolumeUp = 0x48; - private static final int kVK_VolumeDown = 0x49; - private static final int kVK_Mute = 0x4A; - private static final int kVK_F18 = 0x4F; - private static final int kVK_F19 = 0x50; - private static final int kVK_F20 = 0x5A; - private static final int kVK_F5 = 0x60; - private static final int kVK_F6 = 0x61; - private static final int kVK_F7 = 0x62; - private static final int kVK_F3 = 0x63; - private static final int kVK_F8 = 0x64; - private static final int kVK_F9 = 0x65; - private static final int kVK_F11 = 0x67; - private static final int kVK_F13 = 0x69; - private static final int kVK_F16 = 0x6A; - private static final int kVK_F14 = 0x6B; - private static final int kVK_F10 = 0x6D; - private static final int kVK_F12 = 0x6F; - private static final int kVK_F15 = 0x71; - private static final int kVK_Help = 0x72; - private static final int kVK_Home = 0x73; - private static final int kVK_PageUp = 0x74; - private static final int kVK_ForwardDelete = 0x75; - private static final int kVK_F4 = 0x76; - private static final int kVK_End = 0x77; - private static final int kVK_F2 = 0x78; - private static final int kVK_PageDown = 0x79; - private static final int kVK_F1 = 0x7A; - private static final int kVK_LeftArrow = 0x7B; - private static final int kVK_RightArrow = 0x7C; - private static final int kVK_DownArrow = 0x7D; - private static final int kVK_UpArrow = 0x7E; + // + // KeyCodes (Layout Dependent) + // + private static final short kVK_ANSI_A = 0x00; + private static final short kVK_ANSI_S = 0x01; + private static final short kVK_ANSI_D = 0x02; + private static final short kVK_ANSI_F = 0x03; + private static final short kVK_ANSI_H = 0x04; + private static final short kVK_ANSI_G = 0x05; + private static final short kVK_ANSI_Z = 0x06; + private static final short kVK_ANSI_X = 0x07; + private static final short kVK_ANSI_C = 0x08; + private static final short kVK_ANSI_V = 0x09; + private static final short kVK_ANSI_B = 0x0B; + private static final short kVK_ANSI_Q = 0x0C; + private static final short kVK_ANSI_W = 0x0D; + private static final short kVK_ANSI_E = 0x0E; + private static final short kVK_ANSI_R = 0x0F; + private static final short kVK_ANSI_Y = 0x10; + private static final short kVK_ANSI_T = 0x11; + private static final short kVK_ANSI_1 = 0x12; + private static final short kVK_ANSI_2 = 0x13; + private static final short kVK_ANSI_3 = 0x14; + private static final short kVK_ANSI_4 = 0x15; + private static final short kVK_ANSI_6 = 0x16; + private static final short kVK_ANSI_5 = 0x17; + private static final short kVK_ANSI_Equal = 0x18; + private static final short kVK_ANSI_9 = 0x19; + private static final short kVK_ANSI_7 = 0x1A; + private static final short kVK_ANSI_Minus = 0x1B; + private static final short kVK_ANSI_8 = 0x1C; + private static final short kVK_ANSI_0 = 0x1D; + private static final short kVK_ANSI_RightBracket = 0x1E; + private static final short kVK_ANSI_O = 0x1F; + private static final short kVK_ANSI_U = 0x20; + private static final short kVK_ANSI_LeftBracket = 0x21; + private static final short kVK_ANSI_I = 0x22; + private static final short kVK_ANSI_P = 0x23; + private static final short kVK_ANSI_L = 0x25; + private static final short kVK_ANSI_J = 0x26; + private static final short kVK_ANSI_Quote = 0x27; + private static final short kVK_ANSI_K = 0x28; + private static final short kVK_ANSI_Semicolon = 0x29; + private static final short kVK_ANSI_Backslash = 0x2A; + private static final short kVK_ANSI_Comma = 0x2B; + private static final short kVK_ANSI_Slash = 0x2C; + private static final short kVK_ANSI_N = 0x2D; + private static final short kVK_ANSI_M = 0x2E; + private static final short kVK_ANSI_Period = 0x2F; + private static final short kVK_ANSI_Grave = 0x32; + private static final short kVK_ANSI_KeypadDecimal = 0x41; + private static final short kVK_ANSI_KeypadMultiply = 0x43; + private static final short kVK_ANSI_KeypadPlus = 0x45; + private static final short kVK_ANSI_KeypadClear = 0x47; + private static final short kVK_ANSI_KeypadDivide = 0x4B; + private static final short kVK_ANSI_KeypadEnter = 0x4C; + private static final short kVK_ANSI_KeypadMinus = 0x4E; + private static final short kVK_ANSI_KeypadEquals = 0x51; + private static final short kVK_ANSI_Keypad0 = 0x52; + private static final short kVK_ANSI_Keypad1 = 0x53; + private static final short kVK_ANSI_Keypad2 = 0x54; + private static final short kVK_ANSI_Keypad3 = 0x55; + private static final short kVK_ANSI_Keypad4 = 0x56; + private static final short kVK_ANSI_Keypad5 = 0x57; + private static final short kVK_ANSI_Keypad6 = 0x58; + private static final short kVK_ANSI_Keypad7 = 0x59; + private static final short kVK_ANSI_Keypad8 = 0x5B; + private static final short kVK_ANSI_Keypad9 = 0x5C; + + // + // KeyCodes (Layout Independent) + // + private static final short kVK_Return = 0x24; + private static final short kVK_Tab = 0x30; + private static final short kVK_Space = 0x31; + private static final short kVK_Delete = 0x33; + private static final short kVK_Escape = 0x35; + private static final short kVK_Command = 0x37; + private static final short kVK_Shift = 0x38; + private static final short kVK_CapsLock = 0x39; + private static final short kVK_Option = 0x3A; + private static final short kVK_Control = 0x3B; + private static final short kVK_RightShift = 0x3C; + private static final short kVK_RightOption = 0x3D; + private static final short kVK_RightControl = 0x3E; + // private static final short kVK_Function = 0x3F; + private static final short kVK_F17 = 0x40; + // private static final short kVK_VolumeUp = 0x48; + // private static final short kVK_VolumeDown = 0x49; + // private static final short kVK_Mute = 0x4A; + private static final short kVK_F18 = 0x4F; + private static final short kVK_F19 = 0x50; + private static final short kVK_F20 = 0x5A; + private static final short kVK_F5 = 0x60; + private static final short kVK_F6 = 0x61; + private static final short kVK_F7 = 0x62; + private static final short kVK_F3 = 0x63; + private static final short kVK_F8 = 0x64; + private static final short kVK_F9 = 0x65; + private static final short kVK_F11 = 0x67; + private static final short kVK_F13 = 0x69; + private static final short kVK_F16 = 0x6A; + private static final short kVK_F14 = 0x6B; + private static final short kVK_F10 = 0x6D; + private static final short kVK_F12 = 0x6F; + private static final short kVK_F15 = 0x71; + private static final short kVK_Help = 0x72; + private static final short kVK_Home = 0x73; + private static final short kVK_PageUp = 0x74; + private static final short kVK_ForwardDelete = 0x75; + private static final short kVK_F4 = 0x76; + private static final short kVK_End = 0x77; + private static final short kVK_F2 = 0x78; + private static final short kVK_PageDown = 0x79; + private static final short kVK_F1 = 0x7A; + private static final short kVK_LeftArrow = 0x7B; + private static final short kVK_RightArrow = 0x7C; + private static final short kVK_DownArrow = 0x7D; + private static final short kVK_UpArrow = 0x7E; + // // Key constants handled differently on Mac OS X than other platforms + // private static final char NSUpArrowFunctionKey = 0xF700; private static final char NSDownArrowFunctionKey = 0xF701; private static final char NSLeftArrowFunctionKey = 0xF702; @@ -109,6 +182,7 @@ public class MacKeyUtil { private static final char NSF22FunctionKey = 0xF719; private static final char NSF23FunctionKey = 0xF71A; private static final char NSF24FunctionKey = 0xF71B; + /** private static final char NSF25FunctionKey = 0xF71C; private static final char NSF26FunctionKey = 0xF71D; private static final char NSF27FunctionKey = 0xF71E; @@ -120,6 +194,7 @@ public class MacKeyUtil { private static final char NSF33FunctionKey = 0xF724; private static final char NSF34FunctionKey = 0xF725; private static final char NSF35FunctionKey = 0xF726; + */ private static final char NSInsertFunctionKey = 0xF727; private static final char NSDeleteFunctionKey = 0xF728; private static final char NSHomeFunctionKey = 0xF729; @@ -130,10 +205,11 @@ public class MacKeyUtil { private static final char NSPrintScreenFunctionKey = 0xF72E; private static final char NSScrollLockFunctionKey = 0xF72F; private static final char NSPauseFunctionKey = 0xF730; - private static final char NSSysReqFunctionKey = 0xF731; - private static final char NSBreakFunctionKey = 0xF732; - private static final char NSResetFunctionKey = 0xF733; + // private static final char NSSysReqFunctionKey = 0xF731; + // private static final char NSBreakFunctionKey = 0xF732; + // private static final char NSResetFunctionKey = 0xF733; private static final char NSStopFunctionKey = 0xF734; + /** private static final char NSMenuFunctionKey = 0xF735; private static final char NSUserFunctionKey = 0xF736; private static final char NSSystemFunctionKey = 0xF737; @@ -153,10 +229,83 @@ public class MacKeyUtil { private static final char NSFindFunctionKey = 0xF745; private static final char NSHelpFunctionKey = 0xF746; private static final char NSModeSwitchFunctionKey = 0xF747; + */ - static int validateKeyCode(int keyCode, char keyChar) { + static short validateKeyCode(short keyCode, char keyChar) { // OS X Virtual Keycodes switch(keyCode) { + // + // KeyCodes (Layout Dependent) + // + case kVK_ANSI_A: return KeyEvent.VK_A; + case kVK_ANSI_S: return KeyEvent.VK_S; + case kVK_ANSI_D: return KeyEvent.VK_D; + case kVK_ANSI_F: return KeyEvent.VK_F; + case kVK_ANSI_H: return KeyEvent.VK_H; + case kVK_ANSI_G: return KeyEvent.VK_G; + case kVK_ANSI_Z: return KeyEvent.VK_Z; + case kVK_ANSI_X: return KeyEvent.VK_X; + case kVK_ANSI_C: return KeyEvent.VK_C; + case kVK_ANSI_V: return KeyEvent.VK_V; + case kVK_ANSI_B: return KeyEvent.VK_B; + case kVK_ANSI_Q: return KeyEvent.VK_Q; + case kVK_ANSI_W: return KeyEvent.VK_W; + case kVK_ANSI_E: return KeyEvent.VK_E; + case kVK_ANSI_R: return KeyEvent.VK_R; + case kVK_ANSI_Y: return KeyEvent.VK_Y; + case kVK_ANSI_T: return KeyEvent.VK_T; + case kVK_ANSI_1: return KeyEvent.VK_1; + case kVK_ANSI_2: return KeyEvent.VK_2; + case kVK_ANSI_3: return KeyEvent.VK_3; + case kVK_ANSI_4: return KeyEvent.VK_4; + case kVK_ANSI_6: return KeyEvent.VK_6; + case kVK_ANSI_5: return KeyEvent.VK_5; + case kVK_ANSI_Equal: return KeyEvent.VK_EQUALS; + case kVK_ANSI_9: return KeyEvent.VK_9; + case kVK_ANSI_7: return KeyEvent.VK_7; + case kVK_ANSI_Minus: return KeyEvent.VK_MINUS; + case kVK_ANSI_8: return KeyEvent.VK_8; + case kVK_ANSI_0: return KeyEvent.VK_0; + case kVK_ANSI_RightBracket: return KeyEvent.VK_CLOSE_BRACKET; + case kVK_ANSI_O: return KeyEvent.VK_O; + case kVK_ANSI_U: return KeyEvent.VK_U; + case kVK_ANSI_LeftBracket: return KeyEvent.VK_OPEN_BRACKET; + case kVK_ANSI_I: return KeyEvent.VK_I; + case kVK_ANSI_P: return KeyEvent.VK_P; + case kVK_ANSI_L: return KeyEvent.VK_L; + case kVK_ANSI_J: return KeyEvent.VK_J; + case kVK_ANSI_Quote: return KeyEvent.VK_QUOTE; + case kVK_ANSI_K: return KeyEvent.VK_K; + case kVK_ANSI_Semicolon: return KeyEvent.VK_SEMICOLON; + case kVK_ANSI_Backslash: return KeyEvent.VK_BACK_SLASH; + case kVK_ANSI_Comma: return KeyEvent.VK_COMMA; + case kVK_ANSI_Slash: return KeyEvent.VK_SLASH; + case kVK_ANSI_N: return KeyEvent.VK_N; + case kVK_ANSI_M: return KeyEvent.VK_M; + case kVK_ANSI_Period: return KeyEvent.VK_PERIOD; + case kVK_ANSI_Grave: return KeyEvent.VK_BACK_QUOTE; // KeyEvent.VK_DEAD_GRAVE + case kVK_ANSI_KeypadDecimal: return KeyEvent.VK_DECIMAL; + case kVK_ANSI_KeypadMultiply: return KeyEvent.VK_MULTIPLY; + case kVK_ANSI_KeypadPlus: return KeyEvent.VK_PLUS; + case kVK_ANSI_KeypadClear: return KeyEvent.VK_CLEAR; + case kVK_ANSI_KeypadDivide: return KeyEvent.VK_DIVIDE; + case kVK_ANSI_KeypadEnter: return KeyEvent.VK_ENTER; + case kVK_ANSI_KeypadMinus: return KeyEvent.VK_MINUS; + case kVK_ANSI_KeypadEquals: return KeyEvent.VK_EQUALS; + case kVK_ANSI_Keypad0: return KeyEvent.VK_0; + case kVK_ANSI_Keypad1: return KeyEvent.VK_1; + case kVK_ANSI_Keypad2: return KeyEvent.VK_2; + case kVK_ANSI_Keypad3: return KeyEvent.VK_3; + case kVK_ANSI_Keypad4: return KeyEvent.VK_4; + case kVK_ANSI_Keypad5: return KeyEvent.VK_5; + case kVK_ANSI_Keypad6: return KeyEvent.VK_6; + case kVK_ANSI_Keypad7: return KeyEvent.VK_7; + case kVK_ANSI_Keypad8: return KeyEvent.VK_8; + case kVK_ANSI_Keypad9: return KeyEvent.VK_9; + + // + // KeyCodes (Layout Independent) + // case kVK_Return: return KeyEvent.VK_ENTER; case kVK_Tab: return KeyEvent.VK_TAB; case kVK_Space: return KeyEvent.VK_SPACE; @@ -270,15 +419,8 @@ public class MacKeyUtil { // NSFindFunctionKey // NSHelpFunctionKey // NSModeSwitchFunctionKey - case 0x60: return KeyEvent.VK_BACK_QUOTE; // ` - case 0x27: return KeyEvent.VK_QUOTE; // ' - case '\r': return KeyEvent.VK_ENTER; - } - - if ('a' <= keyChar && keyChar <= 'z') { - return KeyEvent.VK_A + ( keyChar - 'a' ) ; } - return (int) keyChar; // let's hope for the best (compatibility of keyChar/keyCode's) + return (short) keyChar; // let's hope for the best (compatibility of keyChar/keyCode's) } } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java index 5755bdf11..6e9335f08 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java @@ -41,7 +41,6 @@ import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.MutableSurface; import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.util.Insets; -import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; @@ -50,6 +49,7 @@ import jogamp.newt.WindowImpl; import jogamp.newt.driver.DriverClearFocus; import jogamp.newt.driver.DriverUpdatePosition; +import com.jogamp.common.util.Function; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; @@ -86,7 +86,10 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl sscSurfaceHandle = 0; isOffscreenInstance = false; if (0 != handle) { - close0(handle); + OSXUtil.RunOnMainThread(true, new Runnable() { + public void run() { + close0( handle ); + } } ); } } catch (Throwable t) { if(DEBUG_IMPLEMENTATION) { @@ -140,6 +143,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl setTitle0(getWindowHandle(), title); } + @Override protected void requestFocusImpl(boolean force) { if(!isOffscreenInstance) { requestFocus0(getWindowHandle(), force); @@ -148,6 +152,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } } + @Override public final void clearFocus() { if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow: clearFocus(), isOffscreenInstance "+isOffscreenInstance); @@ -159,29 +164,59 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } } + @Override public void updatePosition() { - final Point pS = getTopLevelLocationOnScreen(getX(), getY()); - if(DEBUG_IMPLEMENTATION) { - System.err.println("MacWindow: updatePosition() - isOffscreenInstance "+isOffscreenInstance+", new abs pos: pS "+pS); + final long handle = getWindowHandle(); + if( 0 != handle && !isOffscreenInstance ) { + final Point pS = getLocationOnScreenImpl(0, 0); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: updatePosition() -> abs child-client-pos: "+pS); + } + setWindowClientTopLeftPoint0(handle, pS.getX(), pS.getY()); + // no native event (fullscreen, some reparenting) + positionChanged(true, pS.getX(), pS.getY()); } - if( !isOffscreenInstance ) { - setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), pS.getX(), pS.getY()); - } // else no offscreen position - // no native event (fullscreen, some reparenting) - super.positionChanged(true, getX(), getY()); - } - + } + @Override + protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { + final long handle = getWindowHandle(); + if( 0 != handle && !isOffscreenInstance ) { + final NativeWindow parent = getParent(); + final boolean useParent = null != parent && 0 != parent.getWindowHandle() ; + if( useParent && ( getWidth() != newWidth || getHeight() != newHeight ) ) { + final Point p0S = getLocationOnScreenImpl(0, 0); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: sizeChanged() "+newWidth+"x"+newHeight+" -> abs child-client-pos "+p0S); + } + setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY()); + } + } + super.sizeChanged(defer, newWidth, newHeight, force); + } + + @Override protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { final boolean _isOffscreenInstance = isOffscreenInstance(this, this.getParent()); isOffscreenInstance = 0 != sscSurfaceHandle || _isOffscreenInstance; - final PointImmutable pS = isOffscreenInstance ? new Point(0, 0) : getTopLevelLocationOnScreen(x, y); + final PointImmutable pClientLevelOnSreen; + if( isOffscreenInstance ) { + pClientLevelOnSreen = new Point(0, 0); + } else { + final NativeWindow parent = getParent(); + final boolean useParent = null != parent && 0 != parent.getWindowHandle() ; + if( useParent ) { + pClientLevelOnSreen = getLocationOnScreenImpl(x, y); + } else { + pClientLevelOnSreen = new Point(x, y); + } + } if(DEBUG_IMPLEMENTATION) { final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration(); final NativeWindow pWin = getParent(); final AbstractGraphicsConfiguration pWinCfg = null != pWin ? pWin.getGraphicsConfiguration() : null; - System.err.println("MacWindow reconfig: "+x+"/"+y+" -> "+pS+" - "+width+"x"+height+ + System.err.println("MacWindow reconfig.0: "+x+"/"+y+" -> clientPos "+pClientLevelOnSreen+" - "+width+"x"+height+ ",\n\t parent type "+(null != pWin ? pWin.getClass().getName() : null)+ ",\n\t this-chosenCaps "+(null != cWinCfg ? cWinCfg.getChosenCapabilities() : null)+ ",\n\t parent-chosenCaps "+(null != pWinCfg ? pWinCfg.getChosenCapabilities() : null)+ @@ -189,6 +224,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl ", ioi: "+_isOffscreenInstance+ ") -> "+isOffscreenInstance+ "\n\t, "+getReconfigureFlagsAsString(null, flags)); + Thread.dumpStack(); } if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && 0 == ( FLAG_IS_VISIBLE & flags) ) { @@ -203,25 +239,20 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl 0 != ( FLAG_CHANGE_PARENTING & flags) || 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) { if(isOffscreenInstance) { - createWindow(true, 0 != getWindowHandle(), pS, 64, 64, false); + createWindow(true, 0 != getWindowHandle(), pClientLevelOnSreen, 64, 64, false); } else { - createWindow(false, 0 != getWindowHandle(), pS, width, height, 0 != ( FLAG_IS_FULLSCREEN & flags)); + createWindow(false, 0 != getWindowHandle(), pClientLevelOnSreen, width, height, 0 != ( FLAG_IS_FULLSCREEN & flags)); } if(isVisible()) { flags |= FLAG_CHANGE_VISIBILITY; } } - if(x>=0 && y>=0) { - if( !isOffscreenInstance ) { - setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), pS.getX(), pS.getY()); - } // else no offscreen position - // no native event (fullscreen, some reparenting) - super.positionChanged(true, x, y); - } - if(width>0 && height>0) { + if( width>0 && height>0 && x>=0 && y>=0 ) { if( !isOffscreenInstance ) { - setContentSize0(getWindowHandle(), width, height); + // setContentSize0(getWindowHandle(), width, height); + setWindowClientTopLeftPointAndSize0(getWindowHandle(), pClientLevelOnSreen.getX(), pClientLevelOnSreen.getY(), width, height); } // else offscreen size is realized via recreation // no native event (fullscreen, some reparenting) - sizeChanged(true, width, height, false); // incl. validation (incl. repositioning) + positionChanged(true, x, y); + sizeChanged(true, width, height, false); } if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && 0 != ( FLAG_IS_VISIBLE & flags) ) { if( !isOffscreenInstance ) { @@ -233,9 +264,13 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl if( !isOffscreenInstance ) { setAlwaysOnTop0(getWindowHandle(), 0 != ( FLAG_IS_ALWAYSONTOP & flags)); } + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow reconfig.X: clientPos "+pClientLevelOnSreen+", "+width+"x"+height+" -> clientPos "+getLocationOnScreenImpl(0, 0)+", insets: "+getInsets()); + } return true; } + @Override protected Point getLocationOnScreenImpl(int x, int y) { final NativeWindow parent = getParent(); final boolean useParent = null != parent && 0 != parent.getWindowHandle() ; @@ -253,39 +288,28 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } return p; } - - private Point getTopLevelLocationOnScreen(int x, int y) { - final InsetsImmutable _insets = getInsets(); // zero if undecorated - // client position -> top-level window position - x -= _insets.getLeftWidth() ; - y -= _insets.getTopHeight() ; - return getLocationOnScreenImpl(x, y); - } - + + @Override protected void updateInsetsImpl(Insets insets) { // nop - using event driven insetsChange(..) } - @Override - protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { - if(getWidth() != newWidth || getHeight() != newHeight) { - final Point p0S = getTopLevelLocationOnScreen(getX(), getY()); - setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), p0S.getX(), p0S.getY()); - } - super.sizeChanged(defer, newWidth, newHeight, force); - } - - @Override - protected void positionChanged(boolean defer, int newX, int newY) { + /** Callback for native screen position change event of the client area. */ + protected void screenPositionChanged(boolean defer, int newX, int newY) { // passed coordinates are in screen position of the client area - if(getWindowHandle()!=0) { - // screen position -> window position - Point absPos = new Point(newX, newY); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged (Screen Pos): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); + } + if(getWindowHandle()!=0) { final NativeWindow parent = getParent(); - if(null != parent) { + if(null == parent) { + positionChanged(defer, newX, newY); + } else { + // screen position -> rel child window position + Point absPos = new Point(newX, newY); absPos.translate( parent.getLocationOnScreen(null).scale(-1, -1) ); + positionChanged(defer, absPos.getX(), absPos.getY()); } - super.positionChanged(defer, absPos.getX(), absPos.getY()); } } @@ -312,20 +336,21 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } // else may need offscreen solution ? FIXME } - private final void emitKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - if( send ) { - super.sendKeyEvent(eventType, modifiers, keyCode, keyChar); - } else { - super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keyChar); - } + @Override + public final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); } - - private final void handleKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int _keyCode, char keyChar) { + + @Override + public final void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short _keyCode, short keySym, char keyChar) { // Note that we send the key char for the key code on this // platform -- we do not get any useful key codes out of the system - final int keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar); - // final boolean isModifierKeyCode = KeyEvent.isModifierKey(keyCode); - // System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", key 0x"+Integer.toHexString(_keyCode)+" -> 0x"+Integer.toHexString(keyCode)+", mods "+toHexString(modifiers)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isKeyInAutoRepeat(keyCode)+", isModifierKeyCode "+isModifierKeyCode); + final short keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar); + /* { + final boolean isModifierKeyCode = KeyEvent.isModifierKey(keyCode); + System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", key 0x"+Integer.toHexString(_keyCode)+" -> 0x"+Integer.toHexString(keyCode)+", mods "+toHexString(modifiers)+ + ", was: pressed "+isKeyPressed(keyCode)+", repeat "+isKeyInAutoRepeat(keyCode)+", isModifierKeyCode "+isModifierKeyCode); + } */ // 1:1 Order: OSX and NEWT delivery order is PRESSED, RELEASED and TYPED // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE and TYPED keys _before_ PRESSED @@ -342,25 +367,12 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl // key was already pressed keyRepeatState.put(keyCode, true); // prev == false -> AR in modifiers |= InputEvent.AUTOREPEAT_MASK; - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyChar); // RELEASED - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); // TYPED + super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyCode, keyChar); // RELEASED } } break; - case KeyEvent.EVENT_KEY_TYPED: - break; } - emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); - } - - @Override - public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { - handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); - } - - @Override - public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - handleKeyEvent(false, wait, eventType, modifiers, keyCode, keyChar); + super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keyCode, keyChar); } //---------------------------------------------------------------------- @@ -378,24 +390,36 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl try { if(0!=getWindowHandle()) { // save the view .. close the window - surfaceHandle = changeContentView0(getParentWindowHandle(), getWindowHandle(), 0); + surfaceHandle = OSXUtil.RunOnMainThread(true, new Function<Long, Object>() { + public Long eval(Object... args) { + return Long.valueOf( + changeContentView0(getParentWindowHandle(), getWindowHandle(), 0) ); + } } ).longValue(); if(recreate && 0==surfaceHandle) { throw new NativeWindowException("Internal Error - recreate, window but no view"); } - close0(getWindowHandle()); + OSXUtil.RunOnMainThread(true, new Runnable() { + public void run() { + close0( getWindowHandle() ); + } } ); setWindowHandle(0); } else { surfaceHandle = 0; } - setWindowHandle(createWindow0(getParentWindowHandle(), - pS.getX(), pS.getY(), width, height, - (getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance), - fullscreen, - ((isUndecorated() || offscreenInstance) ? - NSBorderlessWindowMask : - NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask), - NSBackingStoreBuffered, - getScreen().getIndex(), surfaceHandle)); + + setWindowHandle( OSXUtil.RunOnMainThread(true, new Function<Long, Object>() { + public Long eval(Object... args) { + return Long.valueOf( + createWindow0( getParentWindowHandle(), + pS.getX(), pS.getY(), width, height, + (getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance), + fullscreen, + ( (isUndecorated() || offscreenInstance) ? NSBorderlessWindowMask : + NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask ), + NSBackingStoreBuffered, + getScreen().getIndex(), surfaceHandle) ); + } } ).longValue() ); + if (getWindowHandle() == 0) { throw new NativeWindowException("Could create native window "+Thread.currentThread().getName()+" "+this); } @@ -411,6 +435,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } protected static native boolean initIDs0(); + /** Must be called on Main-Thread */ private native long createWindow0(long parentWindowHandle, int x, int y, int w, int h, boolean opaque, boolean fullscreen, int windowStyle, int backingStoreType, @@ -422,12 +447,14 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl /** in case of a child window, it actually only issues orderBack(..) */ private native void orderOut0(long window); private native void orderFront0(long window); + /** Must be called on Main-Thread */ private native void close0(long window); private native void setTitle0(long window, String title); private native long contentView0(long window); + /** Must be called on Main-Thread */ private native long changeContentView0(long parentWindowOrViewHandle, long window, long view); - private native void setContentSize0(long window, int w, int h); - private native void setFrameTopLeftPoint0(long parentWindowHandle, long window, int x, int y); + private native void setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h); + private native void setWindowClientTopLeftPoint0(long window, int x, int y); private native void setAlwaysOnTop0(long window, boolean atop); private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y); private static native boolean setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index 650958f25..475687eb4 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -46,7 +46,6 @@ import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; -import com.jogamp.common.util.IntIntHashMap; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.MouseAdapter; @@ -261,87 +260,55 @@ public class WindowDriver extends WindowImpl { // nop - using event driven insetsChange(..) } - private final void emitKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - if( send ) { - super.sendKeyEvent(eventType, modifiers, keyCode, keyChar); - } else { - super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keyChar); - } + private final boolean handlePressTypedAutoRepeat(boolean isModifierKey, int modifiers, short keyCode, short keySym, char keyChar) { + if( isKeyCodeTracked(keyCode) && keyPressedState.put(keyCode, true) ) { + final boolean preKeyRepeatState = keyRepeatState.put(keyCode, true); + if( !isModifierKey ) { + // AR: Key was already pressed: Either [enter | within] AR mode + modifiers |= InputEvent.AUTOREPEAT_MASK; + if( preKeyRepeatState ) { + // AR: Within AR mode + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); + } // else { AR: Enter AR mode - skip already send PRESSED ; or ALT } + super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); + } + return true; + } + return false; } - /** FIXME: We have to store the keyChar for typed events, since keyChar from pressed/released may be wrong (Uppercase: SHIFT-1, etc ..). */ - private IntIntHashMap typedKeyCode2KeyChar = new IntIntHashMap(KeyEvent.VK_CONTEXT_MENU+1); - - private final void handleKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - final boolean isPrintableKey = KeyEvent.isPrintableKey(keyCode); - final boolean isModifierKey = KeyEvent.isModifierKey(keyCode); - // System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar+">, mods "+toHexString(modifiers)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isKeyInAutoRepeat(keyCode)+", printableKey "+isPrintableKey+" [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); + @Override + public final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + final boolean isModifierKey = KeyEvent.isModifierKey(keySym); + // System.err.println("*** sendKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar+">, mods "+toHexString(modifiers)+ + // ", isKeyCodeTracked "+isKeyCodeTracked(keyCode)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isKeyInAutoRepeat(keyCode)+", printableKey "+KeyEvent.isPrintableKey(keyCode)+" [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); // Reorder: WINDOWS delivery order is PRESSED (t0), TYPED (t0) and RELEASED (t1) -> NEWT order: PRESSED (t0), RELEASED (t1) and TYPED (t1) // Auto-Repeat: WINDOWS delivers only PRESSED (t0) and TYPED (t0). switch(eventType) { case KeyEvent.EVENT_KEY_RELEASED: - final int keyCharTyped = typedKeyCode2KeyChar.put(keyCode, 0); - if( 0 != keyCharTyped ) { - keyChar = (char)keyCharTyped; - } if( isKeyCodeTracked(keyCode) ) { if( keyRepeatState.put(keyCode, false) && !isModifierKey ) { // AR out - send out missing PRESSED - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers | InputEvent.AUTOREPEAT_MASK, keyCode, keyChar); + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers | InputEvent.AUTOREPEAT_MASK, keyCode, keySym, keyChar); } keyPressedState.put(keyCode, false); } - emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); + super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); break; case KeyEvent.EVENT_KEY_PRESSED: - // TYPED is delivered right after PRESSED for printable keys and contains the key-char information, - // hence we only deliver non printable keys (action and modifier keys) here. - // Modifier keys shall not AR. - if( !isPrintableKey ) { - if( !handlePressTypedAutoRepeat(isModifierKey, send, wait, modifiers, keyCode, keyChar) ) { - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keyChar); - } - } - break; - case KeyEvent.EVENT_KEY_TYPED: - if( isPrintableKey ) { - typedKeyCode2KeyChar.put(keyCode, keyChar); - if( !handlePressTypedAutoRepeat(isModifierKey, send, wait, modifiers, keyCode, keyChar) ) { - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keyChar); - } + if( !handlePressTypedAutoRepeat(isModifierKey, modifiers, keyCode, keySym, keyChar) ) { + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); } break; + // case KeyEvent.EVENT_KEY_TYPED: + // break; } } - private final boolean handlePressTypedAutoRepeat(boolean isModifierKey, boolean send, boolean wait, int modifiers, int keyCode, char keyChar) { - if( isKeyCodeTracked(keyCode) && keyPressedState.put(keyCode, true) ) { - final boolean preKeyRepeatState = keyRepeatState.put(keyCode, true); - if( !isModifierKey ) { - // AR: Key was already pressed: Either [enter | within] AR mode - modifiers |= InputEvent.AUTOREPEAT_MASK; - if( preKeyRepeatState ) { - // AR: Within AR mode - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keyChar); - } // else { AR: Enter AR mode - skip already send PRESSED ; or ALT } - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyChar); - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); - } - return true; - } - return false; - } - - @Override - public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { - handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); - } - @Override - public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - handleKeyEvent(false, wait, eventType, modifiers, keyCode, keyChar); + public final void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); } //---------------------------------------------------------------------- diff --git a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java index c55fddd07..8d33d4d73 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java @@ -48,6 +48,7 @@ import javax.media.nativewindow.util.Point; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.MouseEvent; public class WindowDriver extends WindowImpl { @@ -227,8 +228,8 @@ public class WindowDriver extends WindowImpl { } @Override - protected void doMouseEvent(boolean enqueue, boolean wait, int eventType, int modifiers, - int x, int y, int button, float rotation) { + protected final void doMouseEvent(boolean enqueue, boolean wait, short eventType, int modifiers, + int x, int y, short button, float rotation) { switch(eventType) { case MouseEvent.EVENT_MOUSE_PRESSED: switch(button) { @@ -270,6 +271,34 @@ public class WindowDriver extends WindowImpl { super.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, rotation); } + @Override + public final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + // handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); + final boolean isModifierKey = KeyEvent.isModifierKey(keyCode); + final boolean isAutoRepeat = 0 != ( KeyEvent.AUTOREPEAT_MASK & modifiers ); + // System.err.println("*** sendKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar+">, mods "+toHexString(modifiers)+ + // ", isKeyCodeTracked "+isKeyCodeTracked(keyCode)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isAutoRepeat+", [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); + + if( !isAutoRepeat || !isModifierKey ) { // ! ( isModifierKey && isAutoRepeat ) + switch(eventType) { + case KeyEvent.EVENT_KEY_PRESSED: + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); + break; + + case KeyEvent.EVENT_KEY_RELEASED: + super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); + break; + + // case KeyEvent.EVENT_KEY_TYPED: + // break; + } + } + } + + @Override + public final void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } //---------------------------------------------------------------------- // Internals only diff --git a/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java index 0fc487e91..1ebb714fa 100644 --- a/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java +++ b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java @@ -32,7 +32,6 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; -import com.jogamp.common.util.IntIntHashMap; import com.jogamp.newt.event.InputEvent; /** @@ -43,26 +42,22 @@ import com.jogamp.newt.event.InputEvent; */ public class SWTNewtEventFactory { - protected static final IntIntHashMap eventTypeSWT2NEWT; + public static final short eventTypeSWT2NEWT(int swtType) { + switch( swtType ) { + // case SWT.MouseXXX: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED; + case SWT.MouseDown: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; + case SWT.MouseUp: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; + case SWT.MouseMove: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED; + case SWT.MouseEnter: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED; + case SWT.MouseExit: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED; + // case SWT.MouseXXX: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED; + case SWT.MouseVerticalWheel: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - static { - IntIntHashMap map = new IntIntHashMap(); - map.setKeyNotFoundValue(0xFFFFFFFF); - - // map.put(SWT.MouseXXX, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED); - map.put(SWT.MouseDown, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED); - map.put(SWT.MouseUp, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED); - map.put(SWT.MouseMove, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED); - map.put(SWT.MouseEnter, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED); - map.put(SWT.MouseExit, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED); - // map.put(SWT.MouseXXX, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED); - map.put(SWT.MouseVerticalWheel, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED); - - map.put(SWT.KeyDown, com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED); - map.put(SWT.KeyUp, com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED); - // map.put(SWT.KeyXXX, com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED); - - eventTypeSWT2NEWT = map; + case SWT.KeyDown: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; + case SWT.KeyUp: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; + // case SWT.KeyXXX: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED; + } + return (short)0; } public static final int swtModifiers2Newt(int awtMods, boolean mouseHint) { @@ -93,8 +88,8 @@ public class SWTNewtEventFactory { default: return null; } - int type = eventTypeSWT2NEWT.get(event.type); - if(0xFFFFFFFF != type) { + final short type = eventTypeSWT2NEWT(event.type); + if( (short)0 != type ) { float rotation = 0; if (SWT.MouseVerticalWheel == event.type) { // SWT/NEWT rotation is reversed - AWT +1 is down, NEWT +1 is up. @@ -116,7 +111,7 @@ public class SWTNewtEventFactory { return new com.jogamp.newt.event.MouseEvent( type, (null==source)?(Object)event.data:source, (0xFFFFFFFFL & (long)event.time), - mods, event.x, event.y, event.count, event.button, rotation); + mods, event.x, event.y, (short)event.count, (short)event.button, rotation); } return null; // no mapping .. } @@ -129,12 +124,12 @@ public class SWTNewtEventFactory { default: return null; } - int type = eventTypeSWT2NEWT.get(event.type); - if(0xFFFFFFFF != type) { + final short type = eventTypeSWT2NEWT(event.type); + if( (short)0 != type ) { return new com.jogamp.newt.event.KeyEvent( type, (null==source)?(Object)event.data:source, (0xFFFFFFFFL & (long)event.time), swtModifiers2Newt(event.stateMask, false), - event.keyCode, event.character); + (short)event.keyCode, (short)event.keyCode, event.character); } return null; // no mapping .. } @@ -143,7 +138,7 @@ public class SWTNewtEventFactory { // // - int dragButtonDown = 0; + short dragButtonDown = 0; public SWTNewtEventFactory() { resetButtonsDown(); @@ -159,7 +154,7 @@ public class SWTNewtEventFactory { if(null != l) { switch(event.type) { case SWT.MouseDown: - dragButtonDown = event.button; + dragButtonDown = (short) event.button; l.mousePressed(res); break; case SWT.MouseUp: dragButtonDown = 0; @@ -214,7 +209,6 @@ public class SWTNewtEventFactory { break; case SWT.KeyUp: l.keyReleased(res); - l.keyTyped(res); break; } } diff --git a/src/newt/native/KDWindow.c b/src/newt/native/KDWindow.c index 3d059c336..cfec60dc1 100644 --- a/src/newt/native/KDWindow.c +++ b/src/newt/native/KDWindow.c @@ -161,14 +161,14 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_kd_DisplayDriver_DispatchMessages if(KD_INPUT_POINTER_SELECT==ptr->index) { DBG_PRINT( "event mouse click: src: %p, s:%d, (%d,%d)\n", userData, ptr->select, ptr->x, ptr->y); (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, - (ptr->select==0) ? (jint) EVENT_MOUSE_RELEASED : (jint) EVENT_MOUSE_PRESSED, + (ptr->select==0) ? (jshort) EVENT_MOUSE_RELEASED : (jshort) EVENT_MOUSE_PRESSED, (jint) 0, - (jint) ptr->x, (jint) ptr->y, 1, 0.0f); + (jint) ptr->x, (jint) ptr->y, (short)1, 0.0f); } else { DBG_PRINT( "event mouse: src: %d, s:%p, i:0x%X (%d,%d)\n", userData, ptr->select, ptr->index, ptr->x, ptr->y); - (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, (jint) EVENT_MOUSE_MOVED, + (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, (jshort) EVENT_MOUSE_MOVED, 0, - (jint) ptr->x, (jint) ptr->y, 0, 0.0f); + (jint) ptr->x, (jint) ptr->y, (jshort)0, 0.0f); } } break; @@ -193,8 +193,8 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_kd_WindowDriver_initIDs sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V"); visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V"); windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z"); - sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIIF)V"); - sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(SIIISF)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(SISSC)V"); if (windowCreatedID == NULL || sizeChangedID == NULL || visibleChangedID == NULL || diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m index b9c339285..1895b98a5 100644 --- a/src/newt/native/MacWindow.m +++ b/src/newt/native/MacWindow.m @@ -62,14 +62,25 @@ static NSString* jstringToNSString(JNIEnv* env, jstring jstr) return str; } -static void setFrameTopLeftPoint(NSWindow* pWin, NewtMacWindow* mWin, jint x, jint y) { - NSPoint pS = [mWin newtScreenWinPos2OSXScreenPos: NSMakePoint(x, y)]; +static void setWindowClientTopLeftPoint(NewtMacWindow* mWin, jint x, jint y) { + NSPoint pS = [mWin newtAbsClientTLWinPos2AbsBLScreenPos: NSMakePoint(x, y)]; [mWin setFrameOrigin: pS]; NSView* mView = [mWin contentView]; [mWin invalidateCursorRectsForView: mView]; } +static void setWindowClientTopLeftPointAndSize(NewtMacWindow* mWin, jint x, jint y, jint width, jint height) { + NSSize sz = NSMakeSize(width, height); + NSPoint pS = [mWin newtAbsClientTLWinPos2AbsBLScreenPos: NSMakePoint(x, y) size: sz]; + NSRect rect = { pS, sz }; + [mWin setFrame: rect display:YES]; + + // -> display:YES + // NSView* mView = [mWin contentView]; + // [mWin invalidateCursorRectsForView: mView]; +} + #ifdef VERBOSE_ON static int getRetainCount(NSObject * obj) { return ( NULL == obj ) ? -1 : (int)([obj retainCount]) ; @@ -553,7 +564,9 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initIDs0 return (jboolean) res; } -/* +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * * Class: jogamp_newt_driver_macosx_WindowDriver * Method: createWindow0 * Signature: (JIIIIZIIIJ)J @@ -573,21 +586,21 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createWindow if(screen_idx<0) screen_idx=0; if(screen_idx>=[screens count]) screen_idx=0; NSScreen *myScreen = (NSScreen *) [screens objectAtIndex: screen_idx]; - NSRect rect; + NSRect rectWin; if (fullscreen) { styleMask = NSBorderlessWindowMask; - rect = [myScreen frame]; + rectWin = [myScreen frame]; x = 0; y = 0; - w = (jint) (rect.size.width); - h = (jint) (rect.size.height); + w = (jint) (rectWin.size.width); + h = (jint) (rectWin.size.height); } else { - rect = NSMakeRect(x, y, w, h); + rectWin = NSMakeRect(x, y, w, h); } // Allocate the window - NewtMacWindow* myWindow = [[NewtMacWindow alloc] initWithContentRect: rect + NewtMacWindow* myWindow = [[NewtMacWindow alloc] initWithContentRect: rectWin styleMask: (NSUInteger) styleMask backing: (NSBackingStoreType) bufferingType defer: NO @@ -644,7 +657,8 @@ NS_ENDHANDLER // Use given NewtView or allocate an NewtView if NULL if(NULL == myView) { - myView = [[NewtView alloc] initWithFrame: rect] ; + NSRect rectView = NSMakeRect(0, 0, w, h); + myView = [[NewtView alloc] initWithFrame: rectView] ; DBG_PRINT( "createWindow0.%d - use new view: %p,%d\n", dbgIdx++, myView, getRetainCount(myView)); } else { DBG_PRINT( "createWindow0.%d - use given view: %p,%d\n", dbgIdx++, myView, getRetainCount(myView)); @@ -664,7 +678,7 @@ NS_ENDHANDLER } // Immediately re-position the window based on an upper-left coordinate system - setFrameTopLeftPoint(parentWindow, myWindow, x, y); + setWindowClientTopLeftPoint(myWindow, x, y); // force surface creation [myView lockFocus]; @@ -706,19 +720,10 @@ NS_ENDHANDLER return (jlong) ((intptr_t) myWindow); } -// Footnote: Our view handling produces random 'Assertion failure' even w/o parenting: -// -// [NSThemeFrame lockFocus], /SourceCache/AppKit/AppKit-1138.23/AppKit.subproj/NSView.m:6053 -// [NSThemeFrame(0x7fe94bc72c80) lockFocus] failed with window=0x7fe94bc445a0, windowNumber=9425, [self isHiddenOrHasHiddenAncestor]=0 -// .. -// AppKit 0x00007fff89621001 -[NSView lockFocus] + 250 -// AppKit 0x00007fff8961eafa -[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:] + 3780 -// AppKit 0x00007fff8961793e -[NSView displayIfNeeded] + 1676 -// AppKit 0x00007fff8961707d _handleWindowNeedsDisplayOrLayoutOrUpdateConstraints + 648 -// - -/* +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * * Class: jogamp_newt_driver_macosx_WindowDriver * Method: close0 * Signature: (J)V @@ -761,8 +766,6 @@ NS_DURING [mView exitFullScreenModeWithOptions: NULL]; } // Note: mWin's release will also release it's mView! - // [mWin setContentView: nil]; - // [mView release]; } NS_HANDLER NS_ENDHANDLER @@ -770,7 +773,8 @@ NS_ENDHANDLER if(NULL!=pWin) { [mWin detachFromParent: pWin]; } - [mWin performSelectorOnMainThread:@selector(orderOut:) withObject:mWin waitUntilDone:NO]; + // [mWin performSelectorOnMainThread:@selector(orderOut:) withObject:mWin waitUntilDone:NO]; + [mWin orderOut: mWin]; DBG_PRINT( "windowClose.1 - %p,%d view %p,%d, parent %p\n", mWin, getRetainCount(mWin), mView, getRetainCount(mView), pWin); @@ -778,7 +782,8 @@ NS_ENDHANDLER // Only release window, if release is not yet in process. // E.g. destroyNotifySent:=true set by NewtMacWindow::windowWillClose(), i.e. window-close was clicked. if(!destroyNotifySent) { - [mWin performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + // [mWin performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + [mWin release]; } DBG_PRINT( "windowClose.X - %p,%d, released %d, view %p,%d, parent %p\n", @@ -960,7 +965,9 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_contentView0 return res; } -/* +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * * Class: jogamp_newt_driver_macosx_WindowDriver * Method: changeContentView * Signature: (J)J @@ -1005,50 +1012,40 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_changeConten /* * Class: jogamp_newt_driver_macosx_WindowDriver - * Method: setContentSize - * Signature: (JII)V + * Method: setWindowClientTopLeftPointAndSize0 + * Signature: (JIIII)V */ -JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setContentSize0 - (JNIEnv *env, jobject unused, jlong window, jint w, jint h) +JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setWindowClientTopLeftPointAndSize0 + (JNIEnv *env, jobject unused, jlong window, jint x, jint y, jint w, jint h) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSWindow* win = (NSWindow*) ((intptr_t) window); + NewtMacWindow* mWin = (NewtMacWindow*) ((intptr_t) window); - DBG_PRINT( "setContentSize0 - window: %p (START)\n", win); + DBG_PRINT( "setWindowClientTopLeftPointAndSize - window: %p (START)\n", mWin); - NSSize sz = NSMakeSize(w, h); - [win setContentSize: sz]; + setWindowClientTopLeftPointAndSize(mWin, x, y, w, h); - DBG_PRINT( "setContentSize0 - window: %p (END)\n", win); + DBG_PRINT( "setWindowClientTopLeftPointAndSize - window: %p (END)\n", mWin); [pool release]; } /* * Class: jogamp_newt_driver_macosx_WindowDriver - * Method: setFrameTopLeftPoint - * Signature: (JJII)V + * Method: setWindowClientTopLeftPoint0 + * Signature: (JII)V */ -JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setFrameTopLeftPoint0 - (JNIEnv *env, jobject unused, jlong parent, jlong window, jint x, jint y) +JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setWindowClientTopLeftPoint0 + (JNIEnv *env, jobject unused, jlong window, jint x, jint y) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NewtMacWindow* mWin = (NewtMacWindow*) ((intptr_t) window); - NSObject *nsParentObj = (NSObject*) ((intptr_t) parent); - NSWindow* pWin = NULL; - if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSWindow class]] ) { - pWin = (NSWindow*) nsParentObj; - } else if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSView class]] ) { - NSView* pView = (NSView*) nsParentObj; - pWin = [pView window]; - } - - DBG_PRINT( "setFrameTopLeftPoint0 - window: %p, parent %p (START)\n", mWin, pWin); + DBG_PRINT( "setWindowClientTopLeftPoint - window: %p (START)\n", mWin); - setFrameTopLeftPoint(pWin, mWin, x, y); + setWindowClientTopLeftPoint(mWin, x, y); - DBG_PRINT( "setFrameTopLeftPoint0 - window: %p, parent %p (END)\n", mWin, pWin); + DBG_PRINT( "setWindowClientTopLeftPoint - window: %p (END)\n", mWin); [pool release]; } @@ -1134,6 +1131,6 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_warpPointer0 (JNIEnv *env, jclass clazz, jlong window, jint x, jint y) { NewtMacWindow *mWin = (NewtMacWindow*) ((intptr_t) window); - [mWin setMousePosition: [mWin newtClientWinPos2OSXScreenPos: NSMakePoint(x, y)]]; + [mWin setMousePosition: [mWin newtRelClientTLWinPos2AbsBLScreenPos: NSMakePoint(x, y)]]; } diff --git a/src/newt/native/NewtMacWindow.h b/src/newt/native/NewtMacWindow.h index 29b646fbf..6d1bcca00 100644 --- a/src/newt/native/NewtMacWindow.h +++ b/src/newt/native/NewtMacWindow.h @@ -134,8 +134,9 @@ - (void) attachToParent: (NSWindow*) parent; - (void) detachFromParent: (NSWindow*) parent; -- (NSPoint) newtScreenWinPos2OSXScreenPos: (NSPoint) p; -- (NSPoint) newtClientWinPos2OSXScreenPos: (NSPoint) p; +- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p; +- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p size: (NSSize) nsz; +- (NSPoint) newtRelClientTLWinPos2AbsBLScreenPos: (NSPoint) p; - (NSPoint) getLocationOnScreen: (NSPoint) p; - (NSPoint) screenPos2NewtClientWinPos: (NSPoint) p; @@ -145,9 +146,9 @@ - (void) setMouseConfined:(BOOL)v; - (void) setMousePosition:(NSPoint)p; -- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType; -- (void) sendKeyEvent: (jint) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jint) evType; -- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType; +- (void) sendKeyEvent: (NSEvent*) event eventType: (jshort) evType; +- (void) sendKeyEvent: (jshort) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jshort) evType; +- (void) sendMouseEvent: (NSEvent*) event eventType: (jshort) evType; - (void) focusChanged: (BOOL) gained; - (BOOL) becomeFirstResponder; diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m index 5b826566b..282c13fd3 100644 --- a/src/newt/native/NewtMacWindow.m +++ b/src/newt/native/NewtMacWindow.m @@ -107,6 +107,7 @@ static jmethodID windowRepaintID = NULL; - (id)initWithFrame:(NSRect)frameRect { + id res = [super initWithFrame:frameRect]; javaWindowObject = NULL; jvmHandle = NULL; @@ -129,28 +130,35 @@ static jmethodID windowRepaintID = NULL; */ myCursor = NULL; - return [super initWithFrame:frameRect]; + DBG_PRINT("NewtView::create: %p (refcnt %d)\n", res, (int)[res retainCount]); + return res; } - (void) release { + DBG_PRINT("NewtView::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); #ifdef VERBOSE_ON - NSLog(@"NewtView::release\n"); - NSLog(@"%@",[NSThread callStackSymbols]); + // NSLog(@"%@",[NSThread callStackSymbols]); #endif [super release]; } - (void) dealloc { + DBG_PRINT("NewtView::dealloc.0: %p (refcnt %d), ptrTrackingTag %d\n", self, (int)[self retainCount], (int)ptrTrackingTag); if(softLocked) { NSLog(@"NewtView::dealloc: softLock still hold @ dealloc!\n"); } + if(0 != ptrTrackingTag) { + // [self removeCursorRect: ptrRect cursor: myCursor]; + [self removeTrackingRect: ptrTrackingTag]; + ptrTrackingTag = 0; + } pthread_mutex_destroy(&softLockSync); #ifdef VERBOSE_ON - NSLog(@"NewtView::dealloc\n"); - NSLog(@"%@",[NSThread callStackSymbols]); + //NSLog(@"%@",[NSThread callStackSymbols]); #endif + DBG_PRINT("NewtView::dealloc.X: %p\n", self); [super dealloc]; } @@ -341,14 +349,14 @@ static jmethodID windowRepaintID = NULL; + (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz { - enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZIIIIIF)V"); - sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIIF)V"); - enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZIIIC)V"); - sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZSIIISF)V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(SIIISF)V"); + enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZSISSC)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(SISSC)V"); sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V"); visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V"); insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(ZIIII)V"); - positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(ZII)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "screenPositionChanged", "(ZII)V"); focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(ZZ)V"); windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z"); windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(ZIIII)V"); @@ -390,25 +398,31 @@ static jmethodID windowRepaintID = NULL; mouseInside = NO; cursorIsHidden = NO; realized = YES; + DBG_PRINT("NewtWindow::create: %p (refcnt %d)\n", res, (int)[res retainCount]); return res; } - (void) release { + DBG_PRINT("NewtWindow::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); #ifdef VERBOSE_ON - NSLog(@"NewtWindow::release\n"); - NSLog(@"%@",[NSThread callStackSymbols]); + // NSLog(@"%@",[NSThread callStackSymbols]); #endif [super release]; } - (void) dealloc { + DBG_PRINT("NewtWindow::dealloc.0: %p (refcnt %d)\n", self, (int)[self retainCount]); #ifdef VERBOSE_ON - NSLog(@"NewtWindow::dealloc\n"); - NSLog(@"%@",[NSThread callStackSymbols]); + // NSLog(@"%@",[NSThread callStackSymbols]); #endif + NewtView* mView = (NewtView *)[self contentView]; + if( NULL != mView ) { + [mView release]; + } [super dealloc]; + DBG_PRINT("NewtWindow::dealloc.X: %p\n", self); } - (void) setUnrealized @@ -470,19 +484,28 @@ static jmethodID windowRepaintID = NULL; } /** - * p abs screen position w/ top-left origin + * p abs screen position of client-area pos w/ top-left origin, using contentView's client NSSize * returns: abs screen position w/ bottom-left origin */ -- (NSPoint) newtScreenWinPos2OSXScreenPos: (NSPoint) p +- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p { NSView* mView = [self contentView]; NSRect mViewFrame = [mView frame]; - int totalHeight = mViewFrame.size.height + cachedInsets[2] + cachedInsets[3]; // height + insets[top+bottom] + return [self newtAbsClientTLWinPos2AbsBLScreenPos: p size: mViewFrame.size]; +} + +/** + * p abs screen position of client-area pos w/ top-left origin, using given client NSSize + * returns: abs screen position w/ bottom-left origin + */ +- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p size: (NSSize) nsz +{ + int totalHeight = nsz.height + cachedInsets[3]; // height + insets.bottom NSScreen* screen = [self screen]; NSRect screenFrame = [screen frame]; - return NSMakePoint(screenFrame.origin.x + p.x + cachedInsets[0], + return NSMakePoint(screenFrame.origin.x + p.x, screenFrame.origin.y + screenFrame.size.height - p.y - totalHeight); } @@ -490,7 +513,7 @@ static jmethodID windowRepaintID = NULL; * p rel client window position w/ top-left origin * returns: abs screen position w/ bottom-left origin */ -- (NSPoint) newtClientWinPos2OSXScreenPos: (NSPoint) p +- (NSPoint) newtRelClientTLWinPos2AbsBLScreenPos: (NSPoint) p { NSRect winFrame = [self frame]; @@ -612,15 +635,15 @@ static jint mods2JavaMods(NSUInteger mods) return javaMods; } -- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType +- (void) sendKeyEvent: (NSEvent*) event eventType: (jshort) evType { - jint keyCode = (jint) [event keyCode]; + jshort keyCode = (jshort) [event keyCode]; NSString* chars = [event charactersIgnoringModifiers]; NSUInteger mods = [event modifierFlags]; [self sendKeyEvent: keyCode characters: chars modifiers: mods eventType: evType]; } -- (void) sendKeyEvent: (jint) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jint) evType +- (void) sendKeyEvent: (jshort) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jshort) evType { NSView* nsview = [self contentView]; if( ! [nsview isMemberOfClass:[NewtView class]] ) { @@ -654,10 +677,10 @@ static jint mods2JavaMods(NSUInteger mods) #ifdef USE_SENDIO_DIRECT (*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID, - evType, javaMods, keyCode, keyChar); + evType, javaMods, keyCode, keyCode, keyChar); #else (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE, - evType, javaMods, keyCode, keyChar); + evType, javaMods, keyCode, keyCode, keyChar); #endif } } else { @@ -668,10 +691,10 @@ static jint mods2JavaMods(NSUInteger mods) #ifdef USE_SENDIO_DIRECT (*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID, - evType, javaMods, keyCode, keyChar); + evType, javaMods, keyCode, keyCode, keyChar); #else (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE, - evType, javaMods, keyCode, keyChar); + evType, javaMods, keyCode, keyCode, keyChar); #endif } @@ -680,7 +703,7 @@ static jint mods2JavaMods(NSUInteger mods) } } -- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType +- (void) sendMouseEvent: (NSEvent*) event eventType: (jshort) evType { NSView* nsview = [self contentView]; if( ! [nsview isMemberOfClass:[NewtView class]] ) { @@ -704,7 +727,7 @@ static jint mods2JavaMods(NSUInteger mods) // convert to 1-based button number (or use zero if no button is involved) // TODO: detect mouse button when mouse wheel scrolled - jint javaButtonNum = 0; + jshort javaButtonNum = 0; jfloat scrollDeltaY = 0.0f; switch ([event type]) { case NSScrollWheel: { @@ -836,13 +859,12 @@ static jint mods2JavaMods(NSUInteger mods) - (void) keyDown: (NSEvent*) theEvent { - [self sendKeyEvent: theEvent eventType: EVENT_KEY_PRESSED]; + [self sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_PRESSED]; } - (void) keyUp: (NSEvent*) theEvent { - [self sendKeyEvent: theEvent eventType: EVENT_KEY_RELEASED]; - [self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED]; + [self sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_RELEASED]; } #define kVK_Shift 0x38 @@ -854,11 +876,10 @@ static jint mods2JavaMods(NSUInteger mods) { if ( NO == modsDown[keyIdx] && 0 != ( mods & keyMask ) ) { modsDown[keyIdx] = YES; - [self sendKeyEvent: keyCode characters: NULL modifiers: mods|keyMask eventType: EVENT_KEY_PRESSED]; + [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_PRESSED]; } else if ( YES == modsDown[keyIdx] && 0 == ( mods & keyMask ) ) { modsDown[keyIdx] = NO; - [self sendKeyEvent: keyCode characters: NULL modifiers: mods|keyMask eventType: EVENT_KEY_RELEASED]; - [self sendKeyEvent: keyCode characters: NULL modifiers: mods|keyMask eventType: EVENT_KEY_TYPED]; + [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_RELEASED]; } } diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c index 24d513c68..05953cb86 100644 --- a/src/newt/native/WindowsWindow.c +++ b/src/newt/native/WindowsWindow.c @@ -132,9 +132,7 @@ static jmethodID focusChangedID = NULL; static jmethodID visibleChangedID = NULL; static jmethodID windowDestroyNotifyID = NULL; static jmethodID windowRepaintID = NULL; -static jmethodID enqueueMouseEventID = NULL; static jmethodID sendMouseEventID = NULL; -static jmethodID enqueueKeyEventID = NULL; static jmethodID sendKeyEventID = NULL; static jmethodID requestFocusID = NULL; @@ -146,345 +144,294 @@ typedef struct { } WindowUserData; typedef struct { - UINT javaKey; - UINT windowsKey; + USHORT javaKey; + USHORT windowsKey; + USHORT windowsScanCodeUS; } KeyMapEntry; // Static table, arranged more or less spatially. static KeyMapEntry keyMapTable[] = { // Modifier keys - {J_VK_CAPS_LOCK, VK_CAPITAL}, - {J_VK_SHIFT, VK_SHIFT}, - {J_VK_CONTROL, VK_CONTROL}, - {J_VK_ALT, VK_MENU}, - {J_VK_NUM_LOCK, VK_NUMLOCK}, + {J_VK_CAPS_LOCK, VK_CAPITAL, 0}, + {J_VK_SHIFT, VK_SHIFT, 0}, + {J_VK_SHIFT, VK_LSHIFT, 0}, + {J_VK_SHIFT, VK_RSHIFT, 0}, + {J_VK_CONTROL, VK_CONTROL, 0}, + {J_VK_CONTROL, VK_LCONTROL, 0}, + {J_VK_CONTROL, VK_RCONTROL, 0}, + {J_VK_ALT, VK_MENU, 0}, + {J_VK_ALT, VK_LMENU, 0}, + {J_VK_ALT, VK_RMENU, 0}, + {J_VK_NUM_LOCK, VK_NUMLOCK, 0}, // Miscellaneous Windows keys - {J_VK_WINDOWS, VK_LWIN}, - {J_VK_WINDOWS, VK_RWIN}, - {J_VK_CONTEXT_MENU, VK_APPS}, + {J_VK_WINDOWS, VK_LWIN, 0}, + {J_VK_WINDOWS, VK_RWIN, 0}, + {J_VK_CONTEXT_MENU, VK_APPS, 0}, // Alphabet - {J_VK_A, 'A'}, - {J_VK_B, 'B'}, - {J_VK_C, 'C'}, - {J_VK_D, 'D'}, - {J_VK_E, 'E'}, - {J_VK_F, 'F'}, - {J_VK_G, 'G'}, - {J_VK_H, 'H'}, - {J_VK_I, 'I'}, - {J_VK_J, 'J'}, - {J_VK_K, 'K'}, - {J_VK_L, 'L'}, - {J_VK_M, 'M'}, - {J_VK_N, 'N'}, - {J_VK_O, 'O'}, - {J_VK_P, 'P'}, - {J_VK_Q, 'Q'}, - {J_VK_R, 'R'}, - {J_VK_S, 'S'}, - {J_VK_T, 'T'}, - {J_VK_U, 'U'}, - {J_VK_V, 'V'}, - {J_VK_W, 'W'}, - {J_VK_X, 'X'}, - {J_VK_Y, 'Y'}, - {J_VK_Z, 'Z'}, - {J_VK_0, '0'}, - {J_VK_1, '1'}, - {J_VK_2, '2'}, - {J_VK_3, '3'}, - {J_VK_4, '4'}, - {J_VK_5, '5'}, - {J_VK_6, '6'}, - {J_VK_7, '7'}, - {J_VK_8, '8'}, - {J_VK_9, '9'}, - {J_VK_ENTER, VK_RETURN}, - {J_VK_SPACE, VK_SPACE}, - {J_VK_BACK_SPACE, VK_BACK}, - {J_VK_TAB, VK_TAB}, - {J_VK_ESCAPE, VK_ESCAPE}, - {J_VK_INSERT, VK_INSERT}, - {J_VK_DELETE, VK_DELETE}, - {J_VK_HOME, VK_HOME}, - {J_VK_END, VK_END}, - {J_VK_PAGE_UP, VK_PRIOR}, - {J_VK_PAGE_DOWN, VK_NEXT}, - {J_VK_CLEAR, VK_CLEAR}, // NumPad 5 + {J_VK_A, 'A', 0}, + {J_VK_B, 'B', 0}, + {J_VK_C, 'C', 0}, + {J_VK_D, 'D', 0}, + {J_VK_E, 'E', 0}, + {J_VK_F, 'F', 0}, + {J_VK_G, 'G', 0}, + {J_VK_H, 'H', 0}, + {J_VK_I, 'I', 0}, + {J_VK_J, 'J', 0}, + {J_VK_K, 'K', 0}, + {J_VK_L, 'L', 0}, + {J_VK_M, 'M', 0}, + {J_VK_N, 'N', 0}, + {J_VK_O, 'O', 0}, + {J_VK_P, 'P', 0}, + {J_VK_Q, 'Q', 0}, + {J_VK_R, 'R', 0}, + {J_VK_S, 'S', 0}, + {J_VK_T, 'T', 0}, + {J_VK_U, 'U', 0}, + {J_VK_V, 'V', 0}, + {J_VK_W, 'W', 0}, + {J_VK_X, 'X', 0}, + {J_VK_Y, 'Y', 0}, + {J_VK_Z, 'Z', 0}, + {J_VK_0, '0', 0}, + {J_VK_1, '1', 0}, + {J_VK_2, '2', 0}, + {J_VK_3, '3', 0}, + {J_VK_4, '4', 0}, + {J_VK_5, '5', 0}, + {J_VK_6, '6', 0}, + {J_VK_7, '7', 0}, + {J_VK_8, '8', 0}, + {J_VK_9, '9', 0}, + {J_VK_ENTER, VK_RETURN, 0}, + {J_VK_SPACE, VK_SPACE, 0}, + {J_VK_BACK_SPACE, VK_BACK, 0}, + {J_VK_TAB, VK_TAB, 0}, + {J_VK_ESCAPE, VK_ESCAPE, 0}, + {J_VK_INSERT, VK_INSERT, 0}, + {J_VK_DELETE, VK_DELETE, 0}, + {J_VK_HOME, VK_HOME, 0}, + {J_VK_END, VK_END, 0}, + {J_VK_PAGE_UP, VK_PRIOR, 0}, + {J_VK_PAGE_DOWN, VK_NEXT, 0}, + {J_VK_CLEAR, VK_CLEAR, 0}, // NumPad 5 // NumPad with NumLock off & extended arrows block (triangular) - {J_VK_LEFT, VK_LEFT}, - {J_VK_RIGHT, VK_RIGHT}, - {J_VK_UP, VK_UP}, - {J_VK_DOWN, VK_DOWN}, + {J_VK_LEFT, VK_LEFT, 0}, + {J_VK_RIGHT, VK_RIGHT, 0}, + {J_VK_UP, VK_UP, 0}, + {J_VK_DOWN, VK_DOWN, 0}, // NumPad with NumLock on: numbers - {J_VK_NUMPAD0, VK_NUMPAD0}, - {J_VK_NUMPAD1, VK_NUMPAD1}, - {J_VK_NUMPAD2, VK_NUMPAD2}, - {J_VK_NUMPAD3, VK_NUMPAD3}, - {J_VK_NUMPAD4, VK_NUMPAD4}, - {J_VK_NUMPAD5, VK_NUMPAD5}, - {J_VK_NUMPAD6, VK_NUMPAD6}, - {J_VK_NUMPAD7, VK_NUMPAD7}, - {J_VK_NUMPAD8, VK_NUMPAD8}, - {J_VK_NUMPAD9, VK_NUMPAD9}, + {J_VK_NUMPAD0, VK_NUMPAD0, 0}, + {J_VK_NUMPAD1, VK_NUMPAD1, 0}, + {J_VK_NUMPAD2, VK_NUMPAD2, 0}, + {J_VK_NUMPAD3, VK_NUMPAD3, 0}, + {J_VK_NUMPAD4, VK_NUMPAD4, 0}, + {J_VK_NUMPAD5, VK_NUMPAD5, 0}, + {J_VK_NUMPAD6, VK_NUMPAD6, 0}, + {J_VK_NUMPAD7, VK_NUMPAD7, 0}, + {J_VK_NUMPAD8, VK_NUMPAD8, 0}, + {J_VK_NUMPAD9, VK_NUMPAD9, 0}, // NumPad with NumLock on - {J_VK_MULTIPLY, VK_MULTIPLY}, - {J_VK_ADD, VK_ADD}, - {J_VK_SEPARATOR, VK_SEPARATOR}, - {J_VK_SUBTRACT, VK_SUBTRACT}, - {J_VK_DECIMAL, VK_DECIMAL}, - {J_VK_DIVIDE, VK_DIVIDE}, + {J_VK_MULTIPLY, VK_MULTIPLY, 0}, + {J_VK_ADD, VK_ADD, 0}, + {J_VK_SEPARATOR, VK_SEPARATOR, 0}, + {J_VK_SUBTRACT, VK_SUBTRACT, 0}, + {J_VK_DECIMAL, VK_DECIMAL, 0}, + {J_VK_DIVIDE, VK_DIVIDE, 0}, // Functional keys - {J_VK_F1, VK_F1}, - {J_VK_F2, VK_F2}, - {J_VK_F3, VK_F3}, - {J_VK_F4, VK_F4}, - {J_VK_F5, VK_F5}, - {J_VK_F6, VK_F6}, - {J_VK_F7, VK_F7}, - {J_VK_F8, VK_F8}, - {J_VK_F9, VK_F9}, - {J_VK_F10, VK_F10}, - {J_VK_F11, VK_F11}, - {J_VK_F12, VK_F12}, - {J_VK_F13, VK_F13}, - {J_VK_F14, VK_F14}, - {J_VK_F15, VK_F15}, - {J_VK_F16, VK_F16}, - {J_VK_F17, VK_F17}, - {J_VK_F18, VK_F18}, - {J_VK_F19, VK_F19}, - {J_VK_F20, VK_F20}, - {J_VK_F21, VK_F21}, - {J_VK_F22, VK_F22}, - {J_VK_F23, VK_F23}, - {J_VK_F24, VK_F24}, - - {J_VK_PRINTSCREEN, VK_SNAPSHOT}, - {J_VK_SCROLL_LOCK, VK_SCROLL}, - {J_VK_PAUSE, VK_PAUSE}, - {J_VK_CANCEL, VK_CANCEL}, - {J_VK_HELP, VK_HELP}, + {J_VK_F1, VK_F1, 0}, + {J_VK_F2, VK_F2, 0}, + {J_VK_F3, VK_F3, 0}, + {J_VK_F4, VK_F4, 0}, + {J_VK_F5, VK_F5, 0}, + {J_VK_F6, VK_F6, 0}, + {J_VK_F7, VK_F7, 0}, + {J_VK_F8, VK_F8, 0}, + {J_VK_F9, VK_F9, 0}, + {J_VK_F10, VK_F10, 0}, + {J_VK_F11, VK_F11, 0}, + {J_VK_F12, VK_F12, 0}, + {J_VK_F13, VK_F13, 0}, + {J_VK_F14, VK_F14, 0}, + {J_VK_F15, VK_F15, 0}, + {J_VK_F16, VK_F16, 0}, + {J_VK_F17, VK_F17, 0}, + {J_VK_F18, VK_F18, 0}, + {J_VK_F19, VK_F19, 0}, + {J_VK_F20, VK_F20, 0}, + {J_VK_F21, VK_F21, 0}, + {J_VK_F22, VK_F22, 0}, + {J_VK_F23, VK_F23, 0}, + {J_VK_F24, VK_F24, 0}, + + {J_VK_PRINTSCREEN, VK_SNAPSHOT, 0}, + {J_VK_SCROLL_LOCK, VK_SCROLL, 0}, + {J_VK_PAUSE, VK_PAUSE, 0}, + {J_VK_CANCEL, VK_CANCEL, 0}, + {J_VK_HELP, VK_HELP, 0}, + + // Since we unify mappings via US kbd layout .. this is valid: + {J_VK_SEMICOLON, VK_OEM_1, 0}, // US only ';:' + {J_VK_EQUALS, VK_OEM_PLUS, 0}, // '=+' + {J_VK_COMMA, VK_OEM_COMMA, 0}, // ',<' + {J_VK_MINUS, VK_OEM_MINUS, 0}, // '-_' + {J_VK_PERIOD, VK_OEM_PERIOD, 0}, // '.>' + {J_VK_SLASH, VK_OEM_2, 0}, // US only '/?' + {J_VK_BACK_QUOTE, VK_OEM_3, 0}, // US only '`~' + {J_VK_OPEN_BRACKET, VK_OEM_4, 0}, // US only '[}' + {J_VK_BACK_SLASH, VK_OEM_5, 0}, // US only '\|' + {J_VK_CLOSE_BRACKET, VK_OEM_6, 0}, // US only ']}' + {J_VK_QUOTE, VK_OEM_7, 0}, // US only ''"' + // {J_VK_????, VK_OEM_8, 0}, // varies .. + // {J_VK_????, VK_OEM_102, 0}, // angle-bracket or backslash key on RT 102-key kbd // Japanese /* - {J_VK_CONVERT, VK_CONVERT}, - {J_VK_NONCONVERT, VK_NONCONVERT}, - {J_VK_INPUT_METHOD_ON_OFF, VK_KANJI}, - {J_VK_ALPHANUMERIC, VK_DBE_ALPHANUMERIC}, - {J_VK_KATAKANA, VK_DBE_KATAKANA}, - {J_VK_HIRAGANA, VK_DBE_HIRAGANA}, - {J_VK_FULL_WIDTH, VK_DBE_DBCSCHAR}, - {J_VK_HALF_WIDTH, VK_DBE_SBCSCHAR}, - {J_VK_ROMAN_CHARACTERS, VK_DBE_ROMAN}, + {J_VK_CONVERT, VK_CONVERT, 0}, + {J_VK_NONCONVERT, VK_NONCONVERT, 0}, + {J_VK_INPUT_METHOD_ON_OFF, VK_KANJI, 0}, + {J_VK_ALPHANUMERIC, VK_DBE_ALPHANUMERIC, 0}, + {J_VK_KATAKANA, VK_DBE_KATAKANA, 0}, + {J_VK_HIRAGANA, VK_DBE_HIRAGANA, 0}, + {J_VK_FULL_WIDTH, VK_DBE_DBCSCHAR, 0}, + {J_VK_HALF_WIDTH, VK_DBE_SBCSCHAR, 0}, + {J_VK_ROMAN_CHARACTERS, VK_DBE_ROMAN, 0}, */ - {J_VK_UNDEFINED, 0} + {J_VK_UNDEFINED, 0, 0} }; -/* -Dynamic mapping table for OEM VK codes. This table is refilled -by BuildDynamicKeyMapTable when keyboard layout is switched. -(see NT4 DDK src/input/inc/vkoem.h for OEM VK_ values). -*/ -typedef struct { - // OEM VK codes known in advance - UINT windowsKey; - // depends on input langauge (kbd layout) - UINT javaKey; -} DynamicKeyMapEntry; - -static DynamicKeyMapEntry dynamicKeyMapTable[] = { - {0x00BA, J_VK_UNDEFINED}, // VK_OEM_1 - {0x00BB, J_VK_UNDEFINED}, // VK_OEM_PLUS - {0x00BC, J_VK_UNDEFINED}, // VK_OEM_COMMA - {0x00BD, J_VK_UNDEFINED}, // VK_OEM_MINUS - {0x00BE, J_VK_UNDEFINED}, // VK_OEM_PERIOD - {0x00BF, J_VK_UNDEFINED}, // VK_OEM_2 - {0x00C0, J_VK_UNDEFINED}, // VK_OEM_3 - {0x00DB, J_VK_UNDEFINED}, // VK_OEM_4 - {0x00DC, J_VK_UNDEFINED}, // VK_OEM_5 - {0x00DD, J_VK_UNDEFINED}, // VK_OEM_6 - {0x00DE, J_VK_UNDEFINED}, // VK_OEM_7 - {0x00DF, J_VK_UNDEFINED}, // VK_OEM_8 - {0x00E2, J_VK_UNDEFINED}, // VK_OEM_102 - {0, 0} -}; +#ifndef KLF_ACTIVATE + #define KLF_ACTIVATE 0x00000001 +#endif +#ifndef MAPVK_VK_TO_VSC + #define MAPVK_VK_TO_VSC 0 +#endif +#ifndef MAPVK_VSC_TO_VK + #define MAPVK_VSC_TO_VK 1 +#endif +#ifndef MAPVK_VK_TO_CHAR + #define MAPVK_VK_TO_CHAR 2 +#endif +#ifndef MAPVK_VSC_TO_VK_EX + #define MAPVK_VSC_TO_VK_EX 3 +#endif +#ifndef MAPVK_VK_TO_VSC_EX + #define MAPVK_VK_TO_VSC_EX 4 +#endif -// Auxiliary tables used to fill the above dynamic table. We first -// find the character for the OEM VK code using ::MapVirtualKey and -// then go through these auxiliary tables to map it to Java VK code. +static HKL kbdLayoutUS = 0; +static const LPCSTR US_LAYOUT_NAME = "00000409"; -typedef struct { - WCHAR c; - UINT javaKey; -} CharToVKEntry; - -static const CharToVKEntry charToVKTable[] = { - {L'!', J_VK_EXCLAMATION_MARK}, - {L'"', J_VK_QUOTEDBL}, - {L'#', J_VK_NUMBER_SIGN}, - {L'$', J_VK_DOLLAR}, - {L'&', J_VK_AMPERSAND}, - {L'\'', J_VK_QUOTE}, - {L'(', J_VK_LEFT_PARENTHESIS}, - {L')', J_VK_RIGHT_PARENTHESIS}, - {L'*', J_VK_ASTERISK}, - {L'+', J_VK_PLUS}, - {L',', J_VK_COMMA}, - {L'-', J_VK_MINUS}, - {L'.', J_VK_PERIOD}, - {L'/', J_VK_SLASH}, - {L':', J_VK_COLON}, - {L';', J_VK_SEMICOLON}, - {L'<', J_VK_LESS}, - {L'=', J_VK_EQUALS}, - {L'>', J_VK_GREATER}, - {L'@', J_VK_AT}, - {L'[', J_VK_OPEN_BRACKET}, - {L'\\', J_VK_BACK_SLASH}, - {L']', J_VK_CLOSE_BRACKET}, - {L'^', J_VK_CIRCUMFLEX}, - {L'_', J_VK_UNDERSCORE}, - {L'`', J_VK_BACK_QUOTE}, - {L'{', J_VK_BRACELEFT}, - {L'}', J_VK_BRACERIGHT}, - {0x00A1, J_VK_INVERTED_EXCLAMATION_MARK}, - {0x20A0, J_VK_EURO_SIGN}, // ???? - {0,0} -}; +static BYTE kbdState[256]; +static USHORT spaceScanCode; -// For dead accents some layouts return ASCII punctuation, while some -// return spacing accent chars, so both should be listed. NB: MS docs -// say that conversion routings return spacing accent character, not -// combining. -static const CharToVKEntry charToDeadVKTable[] = { - {L'`', J_VK_DEAD_GRAVE}, - {L'\'', J_VK_DEAD_ACUTE}, - {0x00B4, J_VK_DEAD_ACUTE}, - {L'^', J_VK_DEAD_CIRCUMFLEX}, - {L'~', J_VK_DEAD_TILDE}, - {0x02DC, J_VK_DEAD_TILDE}, - {0x00AF, J_VK_DEAD_MACRON}, - {0x02D8, J_VK_DEAD_BREVE}, - {0x02D9, J_VK_DEAD_ABOVEDOT}, - {L'"', J_VK_DEAD_DIAERESIS}, - {0x00A8, J_VK_DEAD_DIAERESIS}, - {0x02DA, J_VK_DEAD_ABOVERING}, - {0x02DD, J_VK_DEAD_DOUBLEACUTE}, - {0x02C7, J_VK_DEAD_CARON}, // aka hacek - {L',', J_VK_DEAD_CEDILLA}, - {0x00B8, J_VK_DEAD_CEDILLA}, - {0x02DB, J_VK_DEAD_OGONEK}, - {0x037A, J_VK_DEAD_IOTA}, // ASCII ??? - {0x309B, J_VK_DEAD_VOICED_SOUND}, - {0x309C, J_VK_DEAD_SEMIVOICED_SOUND}, - {0,0} -}; +static void InitKeyMapTableScanCode(JNIEnv *env) { + HKL hkl = GetKeyboardLayout(0); + int i; -// ANSI CP identifiers are no longer than this -#define MAX_ACP_STR_LEN 7 + kbdLayoutUS = LoadKeyboardLayout( US_LAYOUT_NAME, 0 /* ? KLF_ACTIVATE ? */ ); + if( 0 == kbdLayoutUS ) { + int lastError = (int) GetLastError(); + kbdLayoutUS = hkl; // use prev. layout .. well + STD_PRINT("Warning: NEWT Windows: LoadKeyboardLayout(US, ..) failed: winErr 0x%X %d\n", lastError, lastError); + } + ActivateKeyboardLayout(hkl, 0); + + spaceScanCode = MapVirtualKeyEx(VK_SPACE, MAPVK_VK_TO_VSC, hkl); + + // Setup keyMapTable's windowsScanCodeUS + for (i = 0; keyMapTable[i].windowsKey != 0; i++) { + USHORT scancode = (USHORT) MapVirtualKeyEx(keyMapTable[i].windowsKey, MAPVK_VK_TO_VSC_EX, kbdLayoutUS); + #ifdef DEBUG_KEYS + if( 0 == scancode ) { + int lastError = (int) GetLastError(); + STD_PRINT("*** WindowsWindow: InitKeyMapTableScanCode: No ScanCode for windows vkey 0x%X (item %d), winErr 0x%X %d\n", + keyMapTable[i].windowsKey, i, lastError, lastError); + } + STD_PRINT("*** WindowsWindow: InitKeyMapTableScanCode: %3.3d windows vkey 0x%X -> scancode 0x%X\n", + i, keyMapTable[i].windowsKey, scancode); + #endif + keyMapTable[i].windowsScanCodeUS = scancode; + } +} + +static void ParseWmVKeyAndScanCode(USHORT winVKey, BYTE winScanCode, BYTE flags, USHORT *outJavaVKeyUS, USHORT *outJavaVKeyXX, USHORT *outUTF16Char) { + wchar_t uniChars[2] = { L'\0', L'\0' }; // uint16_t + USHORT winVKeyUS = 0; + int nUniChars, i, j; + USHORT javaVKeyUS = J_VK_UNDEFINED; + USHORT javaVKeyXX = J_VK_UNDEFINED; -static void BuildDynamicKeyMapTable() -{ HKL hkl = GetKeyboardLayout(0); - // Will need this to reset layout after dead keys. - UINT spaceScanCode = MapVirtualKeyEx(VK_SPACE, 0, hkl); - DynamicKeyMapEntry *dynamic; - - LANGID idLang = LOWORD(GetKeyboardLayout(0)); - UINT codePage; - TCHAR strCodePage[MAX_ACP_STR_LEN]; - // use the LANGID to create a LCID - LCID idLocale = MAKELCID(idLang, SORT_DEFAULT); - // get the ANSI code page associated with this locale - if (GetLocaleInfo(idLocale, LOCALE_IDEFAULTANSICODEPAGE, - strCodePage, sizeof(strCodePage)/sizeof(TCHAR)) > 0 ) - { - codePage = _ttoi(strCodePage); - } else { - codePage = GetACP(); + + // + // winVKey, winScanCode -> UTF16 w/ current KeyboardLayout + // + GetKeyboardState(kbdState); + kbdState[winVKey] |= 0x80; + nUniChars = ToUnicodeEx(winVKey, winScanCode, kbdState, uniChars, 2, 0, hkl); + kbdState[winVKey] &= ~0x80; + + *outUTF16Char = (USHORT)(uniChars[0]); // Note: Even dead key are written in uniChar's .. + + if ( 0 > nUniChars ) { // Dead key + char junkbuf[2] = { '\0', '\0'}; + + // We need to reset layout so that next translation + // is unaffected by the dead status. We do this by + // translating <SPACE> key. + kbdState[VK_SPACE] |= 0x80; + ToAsciiEx(VK_SPACE, spaceScanCode, kbdState, (WORD*)junkbuf, 0, hkl); + kbdState[VK_SPACE] &= ~0x80; } - // Entries in dynamic table that maps between Java VK and Windows - // VK are built in three steps: - // 1. Map windows VK to ANSI character (cannot map to unicode - // directly, since ::ToUnicode is not implemented on win9x) - // 2. Convert ANSI char to Unicode char - // 3. Map Unicode char to Java VK via two auxilary tables. + // Assume extended scan code 0xE0 if extended flags is set (no 0xE1 from WM_KEYUP/WM_KEYDOWN) + USHORT winScanCodeExt = winScanCode; + if( 0 != ( 0x01 & flags ) ) { + winScanCodeExt |= 0xE000; + } - for (dynamic = dynamicKeyMapTable; dynamic->windowsKey != 0; ++dynamic) - { - char cbuf[2] = { '\0', '\0'}; - WCHAR ucbuf[2] = { L'\0', L'\0' }; - int nchars; - UINT scancode; - const CharToVKEntry *charMap; - int nconverted; - WCHAR uc; - BYTE kbdState[256]; - - // Defaults to J_VK_UNDEFINED - dynamic->javaKey = J_VK_UNDEFINED; - - GetKeyboardState(kbdState); - - kbdState[dynamic->windowsKey] |= 0x80; // Press the key. - - // Unpress modifiers, since they are most likely pressed as - // part of the keyboard switching shortcut. - kbdState[VK_CONTROL] &= ~0x80; - kbdState[VK_SHIFT] &= ~0x80; - kbdState[VK_MENU] &= ~0x80; - - scancode = MapVirtualKeyEx(dynamic->windowsKey, 0, hkl); - nchars = ToAsciiEx(dynamic->windowsKey, scancode, kbdState, - (WORD*)cbuf, 0, hkl); - - // Auxiliary table used to map Unicode character to Java VK. - // Will assign a different table for dead keys (below). - charMap = charToVKTable; - - if (nchars < 0) { // Dead key - char junkbuf[2] = { '\0', '\0'}; - // Use a different table for dead chars since different layouts - // return different characters for the same dead key. - charMap = charToDeadVKTable; - - // We also need to reset layout so that next translation - // is unaffected by the dead status. We do this by - // translating <SPACE> key. - kbdState[dynamic->windowsKey] &= ~0x80; - kbdState[VK_SPACE] |= 0x80; - - ToAsciiEx(VK_SPACE, spaceScanCode, kbdState, - (WORD*)junkbuf, 0, hkl); + // + // winVKey, winScanCodeExt -> javaVKeyUS w/ US KeyboardLayout + // + for (i = 0; keyMapTable[i].windowsKey != 0; i++) { + if ( keyMapTable[i].windowsScanCodeUS == winScanCodeExt ) { + javaVKeyUS = keyMapTable[i].javaKey; + winVKeyUS = keyMapTable[i].windowsKey; + break; } + } - nconverted = MultiByteToWideChar(codePage, 0, - cbuf, 1, ucbuf, 2); - - uc = ucbuf[0]; - { - const CharToVKEntry *map; - for (map = charMap; map->c != 0; ++map) { - if (uc == map->c) { - dynamic->javaKey = map->javaKey; - break; - } - } + // + // winVKey -> javaVKeyXX + // + for (i = 0; keyMapTable[i].windowsKey != 0; i++) { + if ( keyMapTable[i].windowsKey == winVKey ) { + javaVKeyXX = keyMapTable[i].javaKey; + break; } + } + + *outJavaVKeyUS = javaVKeyUS; + *outJavaVKeyXX = javaVKeyXX; - } // for each VK_OEM_* +#ifdef DEBUG_KEYS + STD_PRINT("*** WindowsWindow: ParseWmVKeyAndScanCode winVKey 0x%X, winScanCode 0x%X, winScanCodeExt 0x%X, flags 0x%X -> UTF(0x%X, %c, res %d, sizeof %d), vKeys( US(win 0x%X, java 0x%X), XX(win 0x%X, java 0x%X))\n", + (int)winVKey, (int)winScanCode, winScanCodeExt, (int)flags, + *outUTF16Char, *outUTF16Char, nUniChars, sizeof(uniChars[0]), + winVKeyUS, javaVKeyUS, winVKey, javaVKeyXX); +#endif } -static jint GetModifiers(BOOL altKeyFlagged, UINT jkey) { +static jint GetModifiers(BOOL altKeyFlagged, USHORT jkey) { jint modifiers = 0; // have to do &0xFFFF to avoid runtime assert caused by compiling with // /RTCcsu @@ -516,121 +463,38 @@ static BOOL IsAltKeyDown(BYTE flags, BOOL system) { return system && ( flags & (1<<5) ) != 0; } -UINT WindowsKeyToJavaKey(UINT windowsKey) -{ - int i, j, javaKey = J_VK_UNDEFINED; - // for the general case, use a bi-directional table - for (i = 0; keyMapTable[i].windowsKey != 0; i++) { - if (keyMapTable[i].windowsKey == windowsKey) { - javaKey = keyMapTable[i].javaKey; - } - } - if( J_VK_UNDEFINED == javaKey ) { - for (j = 0; dynamicKeyMapTable[j].windowsKey != 0; j++) { - if (dynamicKeyMapTable[j].windowsKey == windowsKey) { - if (dynamicKeyMapTable[j].javaKey != J_VK_UNDEFINED) { - javaKey = dynamicKeyMapTable[j].javaKey; - } else { - break; - } - } - } - } - -#ifdef DEBUG_KEYS - STD_PRINT("*** WindowsWindow: WindowsKeyToJavaKey 0x%X -> 0x%X\n", windowsKey, javaKey); -#endif - return javaKey; -} - -#ifndef MAPVK_VSC_TO_VK - #define MAPVK_VSC_TO_VK 1 -#endif -#ifndef MAPVK_VK_TO_CHAR - #define MAPVK_VK_TO_CHAR 2 -#endif - -static UINT WmVKey2ShiftedChar(UINT wkey, UINT modifiers) { - UINT c = MapVirtualKey(wkey, MAPVK_VK_TO_CHAR); - if( 0 != ( modifiers & EVENT_SHIFT_MASK ) ) { - return islower(c) ? toupper(c) : c; - } - return isupper(c) ? tolower(c) : c; -} - -static int WmChar(JNIEnv *env, jobject window, UINT character, WORD repCnt, BYTE scanCode, BYTE flags, BOOL system) { - UINT modifiers = 0, jkey = 0, wkey = 0; - - wkey = MapVirtualKey(scanCode, MAPVK_VSC_TO_VK); - jkey = WindowsKeyToJavaKey(wkey); - modifiers = GetModifiers( IsAltKeyDown(flags, system), 0 ); - - if (character == VK_RETURN) { - character = J_VK_ENTER; - } - - (*env)->CallVoidMethod(env, window, sendKeyEventID, - (jint) EVENT_KEY_TYPED, - modifiers, - (jint) jkey, - (jchar) character); - return 1; -} - -static int WmKeyDown(JNIEnv *env, jobject window, UINT wkey, WORD repCnt, BYTE scanCode, BYTE flags, BOOL system) { - UINT modifiers = 0, jkey = 0, character = 0; +static int WmKeyDown(JNIEnv *env, jobject window, USHORT wkey, WORD repCnt, BYTE scanCode, BYTE flags, BOOL system) { + UINT modifiers = 0; + USHORT javaVKeyUS=0, javaVKeyXX=0, utf16Char=0; if (wkey == VK_PROCESSKEY) { return 1; } - jkey = WindowsKeyToJavaKey(wkey); - modifiers = GetModifiers( IsAltKeyDown(flags, system), jkey ); - character = WmVKey2ShiftedChar(wkey, modifiers); + ParseWmVKeyAndScanCode(wkey, scanCode, flags, &javaVKeyUS, &javaVKeyXX, &utf16Char); -/* - character = WindowsKeyToJavaChar(wkey, modifiers, SAVE); -*/ + modifiers = GetModifiers( IsAltKeyDown(flags, system), javaVKeyXX ); (*env)->CallVoidMethod(env, window, sendKeyEventID, - (jint) EVENT_KEY_PRESSED, - modifiers, - (jint) jkey, - (jchar) character); - - /* windows does not create a WM_CHAR for the Del key - for some reason, so we need to create the KEY_TYPED event on the - WM_KEYDOWN. - */ - if (jkey == J_VK_DELETE) { - (*env)->CallVoidMethod(env, window, sendKeyEventID, - (jint) EVENT_KEY_TYPED, - modifiers, - (jint) jkey, - (jchar) '\177'); - } + (jshort) EVENT_KEY_PRESSED, + (jint) modifiers, (jshort) javaVKeyUS, (jshort) javaVKeyXX, (jchar) utf16Char); return 0; } -static int WmKeyUp(JNIEnv *env, jobject window, UINT wkey, WORD repCnt, BYTE scanCode, BYTE flags, BOOL system) { - UINT modifiers = 0, jkey = 0, character = 0; +static int WmKeyUp(JNIEnv *env, jobject window, USHORT wkey, WORD repCnt, BYTE scanCode, BYTE flags, BOOL system) { + UINT modifiers = 0; + USHORT javaVKeyUS=0, javaVKeyXX=0, utf16Char=0; if (wkey == VK_PROCESSKEY) { return 1; } - jkey = WindowsKeyToJavaKey(wkey); - modifiers = GetModifiers( IsAltKeyDown(flags, system), jkey ); - character = WmVKey2ShiftedChar(wkey, modifiers); + ParseWmVKeyAndScanCode(wkey, scanCode, flags, &javaVKeyUS, &javaVKeyXX, &utf16Char); -/* - character = WindowsKeyToJavaChar(wkey, modifiers, SAVE); -*/ + modifiers = GetModifiers( IsAltKeyDown(flags, system), javaVKeyXX ); (*env)->CallVoidMethod(env, window, sendKeyEventID, - (jint) EVENT_KEY_RELEASED, - modifiers, - (jint) jkey, - (jchar) character); + (jshort) EVENT_KEY_RELEASED, + (jint) modifiers, (jshort) javaVKeyUS, (jshort) javaVKeyXX, (jchar) utf16Char); return 0; } @@ -933,46 +797,35 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP break; case WM_SYSCHAR: - repCnt = HIWORD(lParam); scanCode = LOBYTE(repCnt); flags = HIBYTE(repCnt); - repCnt = LOWORD(lParam); -#ifdef DEBUG_KEYS - STD_PRINT("*** WindowsWindow: windProc WM_SYSCHAR sending window %p -> %p, char 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); -#endif - useDefWindowProc = WmChar(env, window, wParam, repCnt, scanCode, flags, TRUE); + useDefWindowProc = 1; break; case WM_SYSKEYDOWN: repCnt = HIWORD(lParam); scanCode = LOBYTE(repCnt); flags = HIBYTE(repCnt); repCnt = LOWORD(lParam); #ifdef DEBUG_KEYS - STD_PRINT("*** WindowsWindow: windProc WM_SYSKEYDOWN sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); + DBG_PRINT("*** WindowsWindow: windProc WM_SYSKEYDOWN sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); #endif - useDefWindowProc = WmKeyDown(env, window, wParam, repCnt, scanCode, flags, TRUE); + useDefWindowProc = WmKeyDown(env, window, (USHORT)wParam, repCnt, scanCode, flags, TRUE); break; case WM_SYSKEYUP: repCnt = HIWORD(lParam); scanCode = LOBYTE(repCnt); flags = HIBYTE(repCnt); repCnt = LOWORD(lParam); #ifdef DEBUG_KEYS - STD_PRINT("*** WindowsWindow: windProc WM_SYSKEYUP sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); + DBG_PRINT("*** WindowsWindow: windProc WM_SYSKEYUP sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); #endif - useDefWindowProc = WmKeyUp(env, window, wParam, repCnt, scanCode, flags, TRUE); + useDefWindowProc = WmKeyUp(env, window, (USHORT)wParam, repCnt, scanCode, flags, TRUE); break; case WM_CHAR: - repCnt = HIWORD(lParam); scanCode = LOBYTE(repCnt); flags = HIBYTE(repCnt); - repCnt = LOWORD(lParam); -#ifdef DEBUG_KEYS - STD_PRINT("*** WindowsWindow: windProc WM_CHAR sending window %p -> %p, char 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); -#endif - useDefWindowProc = WmChar(env, window, wParam, repCnt, scanCode, flags, FALSE); + useDefWindowProc = 1; break; case WM_KEYDOWN: repCnt = HIWORD(lParam); scanCode = LOBYTE(repCnt); flags = HIBYTE(repCnt); - repCnt = LOWORD(lParam); #ifdef DEBUG_KEYS - STD_PRINT("*** WindowsWindow: windProc WM_KEYDOWN sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); + DBG_PRINT("*** WindowsWindow: windProc WM_KEYDOWN sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); #endif useDefWindowProc = WmKeyDown(env, window, wParam, repCnt, scanCode, flags, FALSE); break; @@ -981,7 +834,7 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP repCnt = HIWORD(lParam); scanCode = LOBYTE(repCnt); flags = HIBYTE(repCnt); repCnt = LOWORD(lParam); #ifdef DEBUG_KEYS - STD_PRINT("*** WindowsWindow: windProc WM_KEYUP sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); + DBG_PRINT("*** WindowsWindow: windProc WM_KEYUP sending window %p -> %p, code 0x%X, repCnt %d, scanCode 0x%X, flags 0x%X\n", wnd, window, (int)wParam, (int)repCnt, (int)scanCode, (int)flags); #endif useDefWindowProc = WmKeyUp(env, window, wParam, repCnt, scanCode, flags, FALSE); break; @@ -1005,19 +858,19 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP DBG_PRINT("*** WindowsWindow: LBUTTONDOWN\n"); (*env)->CallVoidMethod(env, window, requestFocusID, JNI_FALSE); (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_PRESSED, + (jshort) EVENT_MOUSE_PRESSED, GetModifiers( FALSE, 0 ), (jint) GET_X_LPARAM(lParam), (jint) GET_Y_LPARAM(lParam), - (jint) 1, (jfloat) 0.0f); + (jshort) 1, (jfloat) 0.0f); useDefWindowProc = 1; break; case WM_LBUTTONUP: (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_RELEASED, + (jshort) EVENT_MOUSE_RELEASED, GetModifiers( FALSE, 0 ), (jint) GET_X_LPARAM(lParam), (jint) GET_Y_LPARAM(lParam), - (jint) 1, (jfloat) 0.0f); + (jshort) 1, (jfloat) 0.0f); useDefWindowProc = 1; break; @@ -1025,19 +878,19 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP DBG_PRINT("*** WindowsWindow: MBUTTONDOWN\n"); (*env)->CallVoidMethod(env, window, requestFocusID, JNI_FALSE); (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_PRESSED, + (jshort) EVENT_MOUSE_PRESSED, GetModifiers( FALSE, 0 ), (jint) GET_X_LPARAM(lParam), (jint) GET_Y_LPARAM(lParam), - (jint) 2, (jfloat) 0.0f); + (jshort) 2, (jfloat) 0.0f); useDefWindowProc = 1; break; case WM_MBUTTONUP: (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_RELEASED, + (jshort) EVENT_MOUSE_RELEASED, GetModifiers( FALSE, 0 ), (jint) GET_X_LPARAM(lParam), (jint) GET_Y_LPARAM(lParam), - (jint) 2, (jfloat) 0.0f); + (jshort) 2, (jfloat) 0.0f); useDefWindowProc = 1; break; @@ -1045,36 +898,36 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP DBG_PRINT("*** WindowsWindow: RBUTTONDOWN\n"); (*env)->CallVoidMethod(env, window, requestFocusID, JNI_FALSE); (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_PRESSED, + (jshort) EVENT_MOUSE_PRESSED, GetModifiers( FALSE, 0 ), (jint) GET_X_LPARAM(lParam), (jint) GET_Y_LPARAM(lParam), - (jint) 3, (jfloat) 0.0f); + (jshort) 3, (jfloat) 0.0f); useDefWindowProc = 1; break; case WM_RBUTTONUP: (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_RELEASED, + (jshort) EVENT_MOUSE_RELEASED, GetModifiers( FALSE, 0 ), (jint) GET_X_LPARAM(lParam), (jint) GET_Y_LPARAM(lParam), - (jint) 3, (jfloat) 0.0f); + (jshort) 3, (jfloat) 0.0f); useDefWindowProc = 1; break; case WM_MOUSEMOVE: (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_MOVED, + (jshort) EVENT_MOUSE_MOVED, GetModifiers( FALSE, 0 ), (jint) GET_X_LPARAM(lParam), (jint) GET_Y_LPARAM(lParam), - (jint) 0, (jfloat) 0.0f); + (jshort) 0, (jfloat) 0.0f); useDefWindowProc = 1; break; case WM_MOUSELEAVE: - (*env)->CallVoidMethod(env, window, enqueueMouseEventID, JNI_FALSE, - (jint) EVENT_MOUSE_EXITED, + (*env)->CallVoidMethod(env, window, sendMouseEventID, JNI_FALSE, + (jshort) EVENT_MOUSE_EXITED, 0, (jint) -1, (jint) -1, // fake - (jint) 0, (jfloat) 0.0f); + (jshort) 0, (jfloat) 0.0f); useDefWindowProc = 1; break; // Java synthesizes EVENT_MOUSE_ENTERED @@ -1099,10 +952,10 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP } DBG_PRINT("*** WindowsWindow: WM_HSCROLL 0x%X, rotation %f, mods 0x%X\n", sb, rotation, modifiers); (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_WHEEL_MOVED, + (jshort) EVENT_MOUSE_WHEEL_MOVED, modifiers, (jint) 0, (jint) 0, - (jint) 1, (jfloat) rotation); + (jshort) 1, (jfloat) rotation); useDefWindowProc = 1; break; } @@ -1128,10 +981,10 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP (int)eventPt.x, (int)eventPt.y, rotationOrTilt, vKeys, modifiers); } (*env)->CallVoidMethod(env, window, sendMouseEventID, - (jint) EVENT_MOUSE_WHEEL_MOVED, + (jshort) EVENT_MOUSE_WHEEL_MOVED, modifiers, (jint) eventPt.x, (jint) eventPt.y, - (jint) 1, (jfloat) rotationOrTilt); + (jshort) 1, (jfloat) rotationOrTilt); useDefWindowProc = 1; break; } @@ -1208,7 +1061,7 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_windows_DisplayDriver_DispatchMes // DBG_PRINT("*** WindowsWindow.DispatchMessages0: thread 0x%X - gotOne %d\n", (int)GetCurrentThreadId(), (int)gotOne); if (gotOne) { ++i; - TranslateMessage(&msg); + // TranslateMessage(&msg); // No more needed: We translate V_KEY -> UTF Char manually in key up/down DispatchMessage(&msg); } } while (gotOne && i < 100); @@ -1474,10 +1327,8 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_windows_WindowDriver_initIDs0 visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V"); windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z"); windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(ZIIII)V"); - enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZIIIIIF)V"); - sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIIF)V"); - enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZIIIC)V"); - sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(SIIISF)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(SISSC)V"); requestFocusID = (*env)->GetMethodID(env, clazz, "requestFocus", "(Z)V"); if (insetsChangedID == NULL || @@ -1487,14 +1338,12 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_windows_WindowDriver_initIDs0 visibleChangedID == NULL || windowDestroyNotifyID == NULL || windowRepaintID == NULL || - enqueueMouseEventID == NULL || sendMouseEventID == NULL || - enqueueKeyEventID == NULL || sendKeyEventID == NULL || requestFocusID == NULL) { return JNI_FALSE; } - BuildDynamicKeyMapTable(); + InitKeyMapTableScanCode(env); return JNI_TRUE; } diff --git a/src/newt/native/X11Display.c b/src/newt/native/X11Display.c index 85b3a14c7..9b11ff0dd 100644 --- a/src/newt/native/X11Display.c +++ b/src/newt/native/X11Display.c @@ -28,8 +28,6 @@ #include "X11Common.h" -#define USE_SENDIO_DIRECT 1 - jclass X11NewtWindowClazz = NULL; jmethodID insetsChangedID = NULL; jmethodID visibleChangedID = NULL; @@ -46,9 +44,7 @@ static jmethodID focusChangedID = NULL; static jmethodID reparentNotifyID = NULL; static jmethodID windowDestroyNotifyID = NULL; static jmethodID windowRepaintID = NULL; -static jmethodID enqueueMouseEventID = NULL; static jmethodID sendMouseEventID = NULL; -static jmethodID enqueueKeyEventID = NULL; static jmethodID sendKeyEventID = NULL; static jmethodID requestFocusID = NULL; @@ -59,10 +55,18 @@ static jmethodID requestFocusID = NULL; #define IS_WITHIN(k,a,b) ((a)<=(k)&&(k)<=(b)) static jint X11KeySym2NewtVKey(KeySym keySym) { - if(IS_WITHIN(keySym,XK_F1,XK_F12)) - return (keySym-XK_F1)+J_VK_F1; - if(IS_WITHIN(keySym,XK_KP_0,XK_KP_9)) - return (keySym-XK_KP_0)+J_VK_NUMPAD0; + if( IS_WITHIN( keySym, XK_a, XK_z ) ) { + return ( keySym - XK_a ) + J_VK_A ; + } + if( IS_WITHIN( keySym, XK_0, XK_9 ) ) { + return ( keySym - XK_0 ) + J_VK_0 ; + } + if( IS_WITHIN( keySym, XK_KP_0, XK_KP_9 ) ) { + return ( keySym - XK_KP_0 ) + J_VK_NUMPAD0 ; + } + if( IS_WITHIN( keySym, XK_F1, XK_F12 ) ) { + return ( keySym - XK_F1 ) + J_VK_F1 ; + } switch(keySym) { case XK_Return: @@ -154,15 +158,15 @@ static jint X11KeySym2NewtVKey(KeySym keySym) { return keySym; } -static jint X11InputState2NewtModifiers(unsigned int xstate, KeySym keySym) { +static jint X11InputState2NewtModifiers(unsigned int xstate, int javaVKey) { jint modifiers = 0; - if ( (ControlMask & xstate) != 0 || J_VK_CONTROL == keySym ) { + if ( (ControlMask & xstate) != 0 || J_VK_CONTROL == javaVKey ) { modifiers |= EVENT_CTRL_MASK; } - if ( (ShiftMask & xstate) != 0 || J_VK_SHIFT == keySym ) { + if ( (ShiftMask & xstate) != 0 || J_VK_SHIFT == javaVKey ) { modifiers |= EVENT_SHIFT_MASK; } - if ( (Mod1Mask & xstate) != 0 || J_VK_ALT == keySym ) { + if ( (Mod1Mask & xstate) != 0 || J_VK_ALT == javaVKey ) { modifiers |= EVENT_ALT_MASK; } if ( (Button1Mask & xstate) != 0 ) { @@ -218,10 +222,8 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_initIDs0 reparentNotifyID = (*env)->GetMethodID(env, X11NewtWindowClazz, "reparentNotify", "(J)V"); windowDestroyNotifyID = (*env)->GetMethodID(env, X11NewtWindowClazz, "windowDestroyNotify", "(Z)Z"); windowRepaintID = (*env)->GetMethodID(env, X11NewtWindowClazz, "windowRepaint", "(ZIIII)V"); - enqueueMouseEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "enqueueMouseEvent", "(ZIIIIIF)V"); - sendMouseEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "sendMouseEvent", "(IIIIIF)V"); - enqueueKeyEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "enqueueKeyEvent", "(ZIIIC)V"); - sendKeyEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "sendKeyEvent", "(IIIC)V"); + sendMouseEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "sendMouseEvent", "(SIIISF)V"); + sendKeyEventID = (*env)->GetMethodID(env, X11NewtWindowClazz, "sendKeyEvent", "(SISSC)V"); requestFocusID = (*env)->GetMethodID(env, X11NewtWindowClazz, "requestFocus", "(Z)V"); if (displayCompletedID == NULL || @@ -235,9 +237,7 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_initIDs0 reparentNotifyID == NULL || windowDestroyNotifyID == NULL || windowRepaintID == NULL || - enqueueMouseEventID == NULL || sendMouseEventID == NULL || - enqueueKeyEventID == NULL || sendKeyEventID == NULL || requestFocusID == NULL) { return JNI_FALSE; @@ -328,6 +328,9 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage jobject jwindow = NULL; XEvent evt; KeySym keySym = 0; + KeyCode keyCode = 0; + jshort javaVKeyUS = 0; + jshort javaVKeyNN = 0; jint modifiers = 0; char keyChar = 0; char text[255]; @@ -386,18 +389,23 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage autoRepeatModifiers &= ~EVENT_AUTOREPEAT_MASK; } // fall through intended - case KeyPress: - if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) { - KeySym lower_return = 0, upper_return = 0; - keyChar=text[0]; - XConvertCase(keySym, &lower_return, &upper_return); - // always return upper case, set modifier masks (SHIFT, ..) - keySym = X11KeySym2NewtVKey(upper_return); - } else { - keyChar=0; - keySym = X11KeySym2NewtVKey(keySym); + case KeyPress: { + KeySym shiftedKeySym; // layout depending keySym w/ SHIFT + + keyCode = evt.xkey.keycode; + keySym = XkbKeycodeToKeysym(dpy, keyCode, 0 /* group */, 0 /* shift level */); // layout depending keySym w/o SHIFT + + if( XLookupString(&evt.xkey, text, 255, &shiftedKeySym, 0) == 1 ) { + keyChar=text[0]; + } + + javaVKeyNN = X11KeySym2NewtVKey(keySym); + javaVKeyUS = javaVKeyNN; // FIXME! + modifiers |= X11InputState2NewtModifiers(evt.xkey.state, javaVKeyNN) | autoRepeatModifiers; + + fprintf(stderr, "NEWT X11 Key: keyCode 0x%X keySym 0x%X (shifted: 0x%X), keyChar '%c', javaVKey[US 0x%X, NN 0x%X]\n", + (int)keyCode, (int)keySym, (int)shiftedKeySym, (int)keyChar, (int)javaVKeyUS, (int)javaVKeyNN); } - modifiers |= X11InputState2NewtModifiers(evt.xkey.state, keySym) | autoRepeatModifiers; break; case ButtonPress: @@ -413,87 +421,39 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage switch(evt.type) { case ButtonPress: (*env)->CallVoidMethod(env, jwindow, requestFocusID, JNI_FALSE); - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jint) EVENT_MOUSE_PRESSED, - modifiers, - (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/); - #else - (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_PRESSED, + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jshort) EVENT_MOUSE_PRESSED, modifiers, - (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/); - #endif + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jshort) evt.xbutton.button, 0.0f /*rotation*/); break; case ButtonRelease: - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jint) EVENT_MOUSE_RELEASED, - modifiers, - (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/); - #else - (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_RELEASED, + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jshort) EVENT_MOUSE_RELEASED, modifiers, - (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/); - #endif + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jshort) evt.xbutton.button, 0.0f /*rotation*/); break; case MotionNotify: - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jint) EVENT_MOUSE_MOVED, + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jshort) EVENT_MOUSE_MOVED, modifiers, - (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0.0f /*rotation*/); - #else - (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_MOVED, - modifiers, - (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0.0f /*rotation*/); - #endif + (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jshort) 0, 0.0f /*rotation*/); break; case EnterNotify: DBG_PRINT( "X11: event . EnterNotify call %p %d/%d\n", (void*)evt.xcrossing.window, evt.xcrossing.x, evt.xcrossing.y); - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jint) EVENT_MOUSE_ENTERED, - modifiers, - (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/); - #else - (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_ENTERED, + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jshort) EVENT_MOUSE_ENTERED, modifiers, - (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/); - #endif + (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jshort) 0, 0.0f /*rotation*/); break; case LeaveNotify: DBG_PRINT( "X11: event . LeaveNotify call %p %d/%d\n", (void*)evt.xcrossing.window, evt.xcrossing.x, evt.xcrossing.y); - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jint) EVENT_MOUSE_EXITED, + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jshort) EVENT_MOUSE_EXITED, modifiers, - (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/); - #else - (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_EXITED, - modifiers, - (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/); - #endif + (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jshort) 0, 0.0f /*rotation*/); break; case KeyPress: - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jint) EVENT_KEY_PRESSED, - modifiers, keySym, (jchar) keyChar); - #else - (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_PRESSED, - modifiers, keySym, (jchar) keyChar); - #endif - + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jshort) EVENT_KEY_PRESSED, + modifiers, javaVKeyUS, javaVKeyNN, (jchar) keyChar); break; case KeyRelease: - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jint) EVENT_KEY_RELEASED, - modifiers, keySym, (jchar) keyChar); - - (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jint) EVENT_KEY_TYPED, - modifiers, keySym, (jchar) keyChar); - #else - (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_RELEASED, - modifiers, keySym, (jchar) keyChar); - - (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_TYPED, - modifiers, keySym, (jchar) keyChar); - #endif - + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jshort) EVENT_KEY_RELEASED, + modifiers, javaVKeyUS, javaVKeyNN, (jchar) keyChar); break; case DestroyNotify: DBG_PRINT( "X11: event . DestroyNotify call %p, parent %p, child-event: %d\n", diff --git a/src/newt/native/bcm_vc_iv.c b/src/newt/native/bcm_vc_iv.c index bbddf764b..f3474ee34 100644 --- a/src/newt/native/bcm_vc_iv.c +++ b/src/newt/native/bcm_vc_iv.c @@ -50,8 +50,6 @@ static jmethodID windowCreatedID = NULL; static jmethodID sizeChangedID = NULL; static jmethodID visibleChangedID = NULL; static jmethodID windowDestroyNotifyID = NULL; -static jmethodID sendMouseEventID = NULL; -static jmethodID sendKeyEventID = NULL; /** * Display @@ -117,14 +115,10 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_bcm_vc_iv_WindowDriver_initID sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V"); visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V"); windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z"); - sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIIF)V"); - sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); if (windowCreatedID == NULL || sizeChangedID == NULL || visibleChangedID == NULL || - windowDestroyNotifyID == NULL || - sendMouseEventID == NULL || - sendKeyEventID == NULL) { + windowDestroyNotifyID == NULL) { DBG_PRINT( "initIDs failed\n" ); return JNI_FALSE; } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove01GLCanvasSwingAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove01GLCanvasSwingAWT.java new file mode 100644 index 000000000..ce8f9adc8 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove01GLCanvasSwingAWT.java @@ -0,0 +1,241 @@ +/** + * 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.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import jogamp.nativewindow.jawt.JAWTUtil; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.UITestCase; + +public class TestAddRemove01GLCanvasSwingAWT extends UITestCase { + static long durationPerTest = 50; + static int addRemoveCount = 15; + static boolean noOnscreenTest = false; + static boolean noOffscreenTest = false; + static boolean shallUseOffscreenPBufferLayer = false; + static GLProfile glp; + static int width, height; + static boolean waitForKey = false; + + @BeforeClass + public static void initClass() { + if(GLProfile.isAvailable(GLProfile.GL2ES2)) { + glp = GLProfile.get(GLProfile.GL2ES2); + Assert.assertNotNull(glp); + width = 640; + height = 480; + } else { + setTestSupported(false); + } + } + + @AfterClass + public static void releaseClass() { + } + + protected JPanel create(final JFrame[] top, final int width, final int height, final int num) + throws InterruptedException, InvocationTargetException + { + final JPanel[] jPanel = new JPanel[] { null }; + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jPanel[0] = new JPanel(); + jPanel[0].setLayout(new BorderLayout()); + + final JFrame jFrame1 = new JFrame("JFrame #"+num); + // jFrame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jFrame1.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // equivalent to Frame, use windowClosing event! + jFrame1.getContentPane().add(jPanel[0]); + jFrame1.setSize(width, height); + + top[0] = jFrame1; + } } ); + return jPanel[0]; + } + + protected void add(final Container cont, final Component comp) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + cont.add(comp, BorderLayout.CENTER); + } } ); + } + + protected void dispose(final GLCanvas glc) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + glc.destroy(); + } } ); + } + + protected void setVisible(final JFrame jFrame, final boolean visible) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + if( visible ) { + jFrame.pack(); + jFrame.validate(); + } + jFrame.setVisible(visible); + } } ) ; + } + + protected void dispose(final JFrame jFrame) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jFrame.dispose(); + } } ) ; + } + + protected void runTestGL(boolean onscreen, GLCapabilities caps, int addRemoveOpCount) + throws AWTException, InterruptedException, InvocationTargetException + { + + for(int i=0; i<addRemoveOpCount; i++) { + System.err.println("Loop # "+i+" / "+addRemoveCount); + final GLCanvas glc = new GLCanvas(caps); + Assert.assertNotNull(glc); + if( !onscreen ) { + glc.setShallUseOffscreenLayer(true); + } + Dimension glc_sz = new Dimension(width, height); + glc.setMinimumSize(glc_sz); + glc.setPreferredSize(glc_sz); + glc.setSize(glc_sz); + final GearsES2 gears = new GearsES2(1); + gears.setVerbose(false); + glc.addGLEventListener(gears); + + final JFrame[] top = new JFrame[] { null }; + final Container glcCont = create(top, width, height, i); + add(glcCont, glc); + + setVisible(top[0], true); + + final long t0 = System.currentTimeMillis(); + do { + glc.display(); + Thread.sleep(10); + } while ( ( System.currentTimeMillis() - t0 ) < durationPerTest ) ; + + System.err.println("GLCanvas isOffscreenLayerSurfaceEnabled: "+glc.isOffscreenLayerSurfaceEnabled()+": "+glc.getChosenGLCapabilities()); + + dispose(top[0]); + } + } + + @Test + public void test01Onscreen() + throws AWTException, InterruptedException, InvocationTargetException + { + if( noOnscreenTest || JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("No onscreen test requested or platform doesn't support onscreen rendering."); + return; + } + GLCapabilities caps = new GLCapabilities(glp); + if(shallUseOffscreenPBufferLayer) { + caps.setPBuffer(true); + caps.setOnscreen(true); // simulate normal behavior .. + } + runTestGL(true, caps, addRemoveCount); + } + + @Test + public void test02Offscreen() + throws AWTException, InterruptedException, InvocationTargetException + { + if( noOffscreenTest || !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("No offscreen test requested or platform doesn't support offscreen rendering."); + return; + } + GLCapabilities caps = new GLCapabilities(glp); + if(shallUseOffscreenPBufferLayer) { + caps.setPBuffer(true); + caps.setOnscreen(true); // simulate normal behavior .. + } + runTestGL(false, caps, addRemoveCount); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + durationPerTest = Long.parseLong(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-loops")) { + i++; + try { + addRemoveCount = Integer.parseInt(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-noOnscreen")) { + noOnscreenTest = true; + } else if(args[i].equals("-noOffscreen")) { + noOffscreenTest = true; + } else if(args[i].equals("-layeredPBuffer")) { + shallUseOffscreenPBufferLayer = true; + } else if(args[i].equals("-wait")) { + waitForKey = true; + } + } + System.err.println("waitForKey "+waitForKey); + + System.err.println("addRemoveCount "+addRemoveCount); + + System.err.println("noOnscreenTest "+noOnscreenTest); + System.err.println("noOffscreenTest "+noOffscreenTest); + System.err.println("shallUseOffscreenPBufferLayer "+shallUseOffscreenPBufferLayer); + if(waitForKey) { + UITestCase.waitForKey("Start"); + } + org.junit.runner.JUnitCore.main(TestAddRemove01GLCanvasSwingAWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove02GLWindowNewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove02GLWindowNewtCanvasAWT.java new file mode 100644 index 000000000..3d78943f9 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove02GLWindowNewtCanvasAWT.java @@ -0,0 +1,246 @@ +/** + * 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.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import jogamp.nativewindow.jawt.JAWTUtil; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.UITestCase; + +public class TestAddRemove02GLWindowNewtCanvasAWT extends UITestCase { + static long durationPerTest = 50; + static int addRemoveCount = 15; + static boolean noOnscreenTest = false; + static boolean noOffscreenTest = false; + static boolean shallUseOffscreenPBufferLayer = false; + static GLProfile glp; + static int width, height; + static boolean waitForKey = false; + + @BeforeClass + public static void initClass() { + if(GLProfile.isAvailable(GLProfile.GL2ES2)) { + glp = GLProfile.get(GLProfile.GL2ES2); + Assert.assertNotNull(glp); + width = 640; + height = 480; + } else { + setTestSupported(false); + } + } + + @AfterClass + public static void releaseClass() { + } + + protected JPanel create(final JFrame[] top, final int width, final int height, final int num) + throws InterruptedException, InvocationTargetException + { + final JPanel[] jPanel = new JPanel[] { null }; + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jPanel[0] = new JPanel(); + jPanel[0].setLayout(new BorderLayout()); + + final JFrame jFrame1 = new JFrame("JFrame #"+num); + // jFrame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jFrame1.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // equivalent to Frame, use windowClosing event! + jFrame1.getContentPane().add(jPanel[0]); + jFrame1.setSize(width, height); + + top[0] = jFrame1; + } } ); + return jPanel[0]; + } + + protected void add(final Container cont, final Component comp) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + cont.add(comp, BorderLayout.CENTER); + } } ); + } + + protected void dispose(final GLCanvas glc) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + glc.destroy(); + } } ); + } + + protected void setVisible(final JFrame jFrame, final boolean visible) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + if( visible ) { + jFrame.pack(); + jFrame.validate(); + } + jFrame.setVisible(visible); + } } ) ; + } + + protected void dispose(final JFrame jFrame) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jFrame.dispose(); + } } ) ; + } + + protected void runTestGL(boolean onscreen, GLCapabilities caps, int addRemoveOpCount) + throws AWTException, InterruptedException, InvocationTargetException + { + + for(int i=0; i<addRemoveOpCount; i++) { + System.err.println("Loop # "+i+" / "+addRemoveCount); + final GLWindow glw = GLWindow.create(caps); + Assert.assertNotNull(glw); + final NewtCanvasAWT glc = new NewtCanvasAWT(glw); + Assert.assertNotNull(glc); + if( !onscreen ) { + glc.setShallUseOffscreenLayer(true); + } + Dimension glc_sz = new Dimension(width, height); + glc.setMinimumSize(glc_sz); + glc.setPreferredSize(glc_sz); + glc.setSize(glc_sz); + final GearsES2 gears = new GearsES2(1); + gears.setVerbose(false); + glw.addGLEventListener(gears); + + final JFrame[] top = new JFrame[] { null }; + final Container glcCont = create(top, width, height, i); + add(glcCont, glc); + + setVisible(top[0], true); + + final long t0 = System.currentTimeMillis(); + do { + glw.display(); + Thread.sleep(10); + } while ( ( System.currentTimeMillis() - t0 ) < durationPerTest ) ; + + System.err.println("GLCanvas isOffscreenLayerSurfaceEnabled: "+glc.isOffscreenLayerSurfaceEnabled()+": "+glw.getChosenGLCapabilities()); + + dispose(top[0]); + glw.destroy(); + } + } + + @Test + public void test01Onscreen() + throws AWTException, InterruptedException, InvocationTargetException + { + if( noOnscreenTest || JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("No onscreen test requested or platform doesn't support onscreen rendering."); + return; + } + GLCapabilities caps = new GLCapabilities(glp); + if(shallUseOffscreenPBufferLayer) { + caps.setPBuffer(true); + caps.setOnscreen(true); // simulate normal behavior .. + } + runTestGL(true, caps, addRemoveCount); + } + + @Test + public void test02Offscreen() + throws AWTException, InterruptedException, InvocationTargetException + { + if( noOffscreenTest || !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("No offscreen test requested or platform doesn't support offscreen rendering."); + return; + } + GLCapabilities caps = new GLCapabilities(glp); + if(shallUseOffscreenPBufferLayer) { + caps.setPBuffer(true); + caps.setOnscreen(true); // simulate normal behavior .. + } + runTestGL(false, caps, addRemoveCount); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + durationPerTest = Long.parseLong(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-loops")) { + i++; + try { + addRemoveCount = Integer.parseInt(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-noOnscreen")) { + noOnscreenTest = true; + } else if(args[i].equals("-noOffscreen")) { + noOffscreenTest = true; + } else if(args[i].equals("-layeredPBuffer")) { + shallUseOffscreenPBufferLayer = true; + } else if(args[i].equals("-wait")) { + waitForKey = true; + } + } + System.err.println("waitForKey "+waitForKey); + + System.err.println("addRemoveCount "+addRemoveCount); + + System.err.println("noOnscreenTest "+noOnscreenTest); + System.err.println("noOffscreenTest "+noOffscreenTest); + System.err.println("shallUseOffscreenPBufferLayer "+shallUseOffscreenPBufferLayer); + if(waitForKey) { + UITestCase.waitForKey("Start"); + } + org.junit.runner.JUnitCore.main(TestAddRemove02GLWindowNewtCanvasAWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove03GLWindowNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove03GLWindowNEWT.java new file mode 100644 index 000000000..9d7a4026b --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestAddRemove03GLWindowNEWT.java @@ -0,0 +1,130 @@ +/** + * 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 java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.UITestCase; + +public class TestAddRemove03GLWindowNEWT extends UITestCase { + static long durationPerTest = 50; + static int addRemoveCount = 15; + static GLProfile glp; + static int width, height; + static boolean waitForKey = false; + + @BeforeClass + public static void initClass() { + if(GLProfile.isAvailable(GLProfile.GL2ES2)) { + glp = GLProfile.get(GLProfile.GL2ES2); + Assert.assertNotNull(glp); + width = 640; + height = 480; + } else { + setTestSupported(false); + } + } + + @AfterClass + public static void releaseClass() { + } + + protected void runTestGL(GLCapabilities caps, int addRemoveOpCount) + throws InterruptedException, InvocationTargetException + { + + for(int i=0; i<addRemoveOpCount; i++) { + System.err.println("Loop # "+i+" / "+addRemoveCount); + final GLWindow glw = GLWindow.create(caps); + Assert.assertNotNull(glw); + glw.setTitle("GLWindow #"+i); + glw.setSize(width, height); + final GearsES2 gears = new GearsES2(1); + gears.setVerbose(false); + glw.addGLEventListener(gears); + + glw.setVisible(true); + + final long t0 = System.currentTimeMillis(); + do { + glw.display(); + Thread.sleep(10); + } while ( ( System.currentTimeMillis() - t0 ) < durationPerTest ) ; + + System.err.println("GLWindow: "+glw.getChosenGLCapabilities()); + + glw.destroy(); + } + } + + @Test + public void test01Onscreen() + throws InterruptedException, InvocationTargetException + { + GLCapabilities caps = new GLCapabilities(glp); + runTestGL(caps, addRemoveCount); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + durationPerTest = Long.parseLong(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-loops")) { + i++; + try { + addRemoveCount = Integer.parseInt(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-wait")) { + waitForKey = true; + } + } + System.err.println("waitForKey "+waitForKey); + + System.err.println("addRemoveCount "+addRemoveCount); + + if(waitForKey) { + UITestCase.waitForKey("Start"); + } + org.junit.runner.JUnitCore.main(TestAddRemove03GLWindowNEWT.class.getName()); + } +} 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/TestOffscreenLayer01GLCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestOffscreenLayer01GLCanvasAWT.java index 90407166f..8cc094276 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestOffscreenLayer01GLCanvasAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestOffscreenLayer01GLCanvasAWT.java @@ -33,9 +33,7 @@ import java.awt.Button; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import javax.media.opengl.GLAnimatorControl; @@ -70,6 +68,7 @@ public class TestOffscreenLayer01GLCanvasAWT extends UITestCase { static Dimension frameSize1; static Dimension preferredGLSize; static long durationPerTest = 1000; + static boolean waitForKey = false; @BeforeClass public static void initClass() { @@ -189,7 +188,10 @@ public class TestOffscreenLayer01GLCanvasAWT extends UITestCase { Thread.sleep(durationPerTest/2); - end(animator1, frame1, null); + end(animator1, frame1, null); + if( waitForKey ) { + UITestCase.waitForKey("Continue"); + } } public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { @@ -214,7 +216,6 @@ public class TestOffscreenLayer01GLCanvasAWT extends UITestCase { } public static void main(String args[]) throws IOException { - boolean waitForKey = false; for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { durationPerTest = atoi(args[++i]); @@ -234,11 +235,7 @@ public class TestOffscreenLayer01GLCanvasAWT extends UITestCase { } } if(waitForKey) { - BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); - System.err.println("Press enter to continue"); - try { - System.err.println(stdin.readLine()); - } catch (IOException e) { } + UITestCase.waitForKey("Start"); } String tstname = TestOffscreenLayer01GLCanvasAWT.class.getName(); /* diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestOffscreenLayer02NewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestOffscreenLayer02NewtCanvasAWT.java index e4a3bce71..2236f4b05 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestOffscreenLayer02NewtCanvasAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestOffscreenLayer02NewtCanvasAWT.java @@ -33,9 +33,7 @@ import java.awt.Button; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import javax.media.opengl.GLAnimatorControl; @@ -70,6 +68,7 @@ public class TestOffscreenLayer02NewtCanvasAWT extends UITestCase { static Dimension frameSize1; static Dimension preferredGLSize; static long durationPerTest = 1000; + static boolean waitForKey = false; @BeforeClass public static void initClass() { @@ -79,7 +78,6 @@ public class TestOffscreenLayer02NewtCanvasAWT extends UITestCase { } private void setupFrameAndShow(final Frame f, java.awt.Component comp) throws InterruptedException, InvocationTargetException { - Container c = new Container(); c.setLayout(new BorderLayout()); c.add(new Button("north"), BorderLayout.NORTH); @@ -169,7 +167,7 @@ public class TestOffscreenLayer02NewtCanvasAWT extends UITestCase { Assert.assertEquals(true, AWTRobotUtil.waitForVisible(glWindow1, true)); Assert.assertEquals(newtCanvasAWT1.getNativeWindow(),glWindow1.getParent()); Assert.assertEquals(true, newtCanvasAWT1.isOffscreenLayerSurfaceEnabled()); - + GLAnimatorControl animator1 = new Animator(glWindow1); if(!noAnimation) { animator1.start(); @@ -187,6 +185,9 @@ public class TestOffscreenLayer02NewtCanvasAWT extends UITestCase { Thread.sleep(durationPerTest/2); end(animator1, frame1, glWindow1); + if( waitForKey ) { + UITestCase.waitForKey("Continue"); + } } public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { @@ -211,7 +212,6 @@ public class TestOffscreenLayer02NewtCanvasAWT extends UITestCase { } public static void main(String args[]) throws IOException { - boolean waitForKey = false; for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { durationPerTest = atoi(args[++i]); @@ -231,11 +231,7 @@ public class TestOffscreenLayer02NewtCanvasAWT extends UITestCase { } } if(waitForKey) { - BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); - System.err.println("Press enter to continue"); - try { - System.err.println(stdin.readLine()); - } catch (IOException e) { } + UITestCase.waitForKey("Start"); } String tstname = TestOffscreenLayer02NewtCanvasAWT.class.getName(); /* 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/jogl/awt/TestBug664GLCanvasSetVisibleSwingAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestBug664GLCanvasSetVisibleSwingAWT.java new file mode 100644 index 000000000..d59bc2230 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestBug664GLCanvasSetVisibleSwingAWT.java @@ -0,0 +1,283 @@ +/** + * 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.awt; + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import jogamp.nativewindow.jawt.JAWTUtil; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +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.util.Animator; + +public class TestBug664GLCanvasSetVisibleSwingAWT extends UITestCase { + static long durationPerTest = 500; + static boolean shallUseOffscreenFBOLayer = false; + static boolean shallUseOffscreenPBufferLayer = false; + static GLProfile glp; + static int width, height; + static boolean waitForKey = false; + + @BeforeClass + public static void initClass() { + if(GLProfile.isAvailable(GLProfile.GL2ES2)) { + glp = GLProfile.get(GLProfile.GL2ES2); + Assert.assertNotNull(glp); + width = 640; + height = 480; + } else { + setTestSupported(false); + } + } + + @AfterClass + public static void releaseClass() { + } + + protected JPanel create(final JFrame[] top, final int width, final int height, final int num) + throws InterruptedException, InvocationTargetException + { + final JPanel[] jPanel = new JPanel[] { null }; + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jPanel[0] = new JPanel(); + jPanel[0].setLayout(new BorderLayout()); + + final JFrame jFrame1 = new JFrame("JFrame #"+num); + // jFrame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jFrame1.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // equivalent to Frame, use windowClosing event! + jFrame1.getContentPane().add(jPanel[0]); + jFrame1.setSize(width, height); + + top[0] = jFrame1; + } } ); + return jPanel[0]; + } + + protected void add(final Container cont, final Component comp, final JFrame jFrame) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + cont.add(comp, BorderLayout.CENTER); + jFrame.pack(); + jFrame.validate(); + } } ); + } + + protected void dispose(final GLCanvas glc) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + glc.destroy(); + } } ); + } + + protected void setFrameVisible(final JFrame jFrame, final boolean visible) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jFrame.setVisible(visible); + } } ) ; + } + + protected void setComponentVisible(final Component comp, final boolean visible) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + comp.setVisible(visible); + } } ) ; + } + + protected void dispose(final JFrame jFrame) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jFrame.dispose(); + } } ) ; + } + + private volatile int frameCount = 0; + + protected void runTestGL(boolean onscreen, GLCapabilities caps) + throws AWTException, InterruptedException, InvocationTargetException + { + + for(int i=0; i<1; i++) { + Animator anim = new Animator(); + final GLCanvas glc = new GLCanvas(caps); + Assert.assertNotNull(glc); + anim.add(glc); + if( !onscreen ) { + glc.setShallUseOffscreenLayer(true); + } + Dimension glc_sz = new Dimension(width, height); + glc.setMinimumSize(glc_sz); + glc.setPreferredSize(glc_sz); + glc.setSize(glc_sz); + glc.addGLEventListener(new GLEventListener() { + @Override + public void init(GLAutoDrawable drawable) {} + @Override + public void dispose(GLAutoDrawable drawable) {} + @Override + public void display(GLAutoDrawable drawable) { + frameCount++; + } + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} + }); + final GearsES2 gears = new GearsES2(1); + gears.setVerbose(false); + glc.addGLEventListener(gears); + + final JFrame[] top = new JFrame[] { null }; + final Container glcCont = create(top, width, height, i); + add(glcCont, glc, top[0]); + + frameCount = 0; + setFrameVisible(top[0], true); + Assert.assertTrue("Component didn't become visible", AWTRobotUtil.waitForVisible(glc, true)); + Assert.assertTrue("Component didn't become realized", AWTRobotUtil.waitForRealized(glc, true)); + + anim.setUpdateFPSFrames(60, null); + anim.start(); + anim.resetFPSCounter(); + System.err.println("Visible Part 1/3"); + + while( anim.getTotalFPSDuration() < durationPerTest ) { + Thread.sleep(60); + } + + setComponentVisible(glc, false); + Assert.assertTrue("Component didn't become invisible", AWTRobotUtil.waitForVisible(glc, false)); + final int frameCountT0 = frameCount; + anim.resetFPSCounter(); + System.err.println("Invisible Part 2/3"); + + while( anim.getTotalFPSDuration() < durationPerTest ) { + Thread.sleep(60); + } + + final int frameCountT1 = frameCount; + System.err.println("GLCanvas invisible frame count: Before "+frameCountT0+", after "+frameCountT1); + Assert.assertTrue("GLCanvas rendered more that 4 times while being invisible, before "+frameCountT0+", after "+frameCountT1, + 4 >= frameCountT1 - frameCountT0); + + setComponentVisible(glc, true); + Assert.assertTrue("Component didn't become visible", AWTRobotUtil.waitForVisible(glc, true)); + anim.resetFPSCounter(); + System.err.println("Visible Part 3/3"); + + while( anim.getTotalFPSDuration() < durationPerTest ) { + Thread.sleep(60); + } + + System.err.println("GLCanvas isOffscreenLayerSurfaceEnabled: "+glc.isOffscreenLayerSurfaceEnabled()+": "+glc.getChosenGLCapabilities()); + + dispose(top[0]); + } + } + + @Test + public void test01Onscreen() + throws AWTException, InterruptedException, InvocationTargetException + { + if( shallUseOffscreenFBOLayer || shallUseOffscreenPBufferLayer || JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("Offscreen test requested or platform requires it."); + return; + } + GLCapabilities caps = new GLCapabilities(GLProfile.getDefault()); + if(shallUseOffscreenPBufferLayer) { + caps.setPBuffer(true); + caps.setOnscreen(true); // simulate normal behavior .. + } + runTestGL(true, caps); + } + + @Test + public void test02Offscreen() + throws AWTException, InterruptedException, InvocationTargetException + { + if( !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("Platform doesn't support offscreen test."); + return; + } + GLCapabilities caps = new GLCapabilities(GLProfile.getDefault()); + if(shallUseOffscreenPBufferLayer) { + caps.setPBuffer(true); + caps.setOnscreen(true); // simulate normal behavior .. + } + runTestGL(false, caps); + } + + public static void main(String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + durationPerTest = Long.parseLong(args[i]); + } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-layeredFBO")) { + shallUseOffscreenFBOLayer = true; + } else if(args[i].equals("-layeredPBuffer")) { + shallUseOffscreenPBufferLayer = true; + } else if(args[i].equals("-wait")) { + waitForKey = true; + } + } + System.err.println("waitForKey "+waitForKey); + + System.err.println("shallUseOffscreenFBOLayer "+shallUseOffscreenFBOLayer); + System.err.println("shallUseOffscreenPBufferLayer "+shallUseOffscreenPBufferLayer); + if(waitForKey) { + UITestCase.waitForKey("Start"); + } + org.junit.runner.JUnitCore.main(TestBug664GLCanvasSetVisibleSwingAWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestBug675BeansInDesignTimeAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestBug675BeansInDesignTimeAWT.java new file mode 100644 index 000000000..0c0975308 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestBug675BeansInDesignTimeAWT.java @@ -0,0 +1,111 @@ +/** + * 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.awt; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Window; +import java.beans.Beans; +import java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; + +public class TestBug675BeansInDesignTimeAWT extends UITestCase { + static boolean waitForKey = false; + static long durationPerTest = 200; + + @Test + public void test01() throws InterruptedException, InvocationTargetException { + Beans.setDesignTime(true); + + final GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2()); + final GLCanvas glCanvas = new GLCanvas(caps); + final Dimension preferredGLSize = new Dimension(400,200); + glCanvas.setPreferredSize(preferredGLSize); + glCanvas.setMinimumSize(preferredGLSize); + glCanvas.setSize(preferredGLSize); + + glCanvas.addGLEventListener(new GearsES2()); + + final Window window = new JFrame(this.getSimpleTestName(" - ")); + window.setLayout(new BorderLayout()); + window.add(glCanvas, BorderLayout.CENTER); + + // trigger realization on AWT-EDT, otherwise it won't immediatly .. + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + window.pack(); + window.validate(); + window.setVisible(true); + } + } ); + + // Immediately displayable after issuing initial setVisible(true) on AWT-EDT! + Assert.assertTrue("GLCanvas didn't become displayable", glCanvas.isDisplayable()); + if( !Beans.isDesignTime() ) { + Assert.assertTrue("GLCanvas didn't become realized", glCanvas.isRealized()); + } + + Thread.sleep(durationPerTest); + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + window.dispose(); + } + } ); + } + + public static void main(String args[]) { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + durationPerTest = MiscUtils.atol(args[++i], durationPerTest); + } else if(args[i].equals("-wait")) { + waitForKey = true; + } + } + if(waitForKey) { + UITestCase.waitForKey("Start"); + } + org.junit.runner.JUnitCore.main(TestBug675BeansInDesignTimeAWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java index 9f62e4a49..aca2b675c 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java @@ -40,11 +40,14 @@ import com.jogamp.newt.event.TraceKeyAdapter; import com.jogamp.newt.event.TraceWindowAdapter; 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.UITestCase; import com.jogamp.opengl.test.junit.util.QuitAdapter; import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Frame; @@ -55,25 +58,27 @@ import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import org.junit.Assert; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.AfterClass; import org.junit.Test; public class TestGearsES2AWT extends UITestCase { + public enum FrameLayout { None, TextOnBottom, BorderCenterSurrounded, DoubleBorderCenterSurrounded }; static int width, height; static boolean forceES2 = false; static boolean forceGL3 = false; - static boolean shallUseOffscreenLayer = false; + static boolean shallUseOffscreenFBOLayer = false; static boolean shallUseOffscreenPBufferLayer = false; static boolean useMSAA = false; static boolean useStencil = false; - static boolean addComp = true; static boolean shutdownRemoveGLCanvas = true; static boolean shutdownDisposeFrame = true; static boolean shutdownSystemExit = false; static int swapInterval = 1; static boolean exclusiveContext = false; static Thread awtEDT; + static Dimension rwsize = null; @BeforeClass public static void initClass() { @@ -94,25 +99,70 @@ public class TestGearsES2AWT extends UITestCase { public static void releaseClass() { } - protected void runTestGL(GLCapabilities caps) throws InterruptedException, InvocationTargetException { + static void setGLCanvasSize(final Frame frame, final GLCanvas glc, final Dimension new_sz) { + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + glc.setMinimumSize(new_sz); + glc.setPreferredSize(new_sz); + glc.setSize(new_sz); + if( null != frame ) { + frame.pack(); + } + } } ); + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + } + + protected void runTestGL(GLCapabilities caps, FrameLayout frameLayout) throws InterruptedException, InvocationTargetException { final Frame frame = new Frame("GearsES2 AWT Test"); Assert.assertNotNull(frame); final GLCanvas glCanvas = new GLCanvas(caps); Assert.assertNotNull(glCanvas); - Dimension glc_sz = new Dimension(width, height); - glCanvas.setMinimumSize(glc_sz); - glCanvas.setPreferredSize(glc_sz); - glCanvas.setSize(glc_sz); - if(addComp) { - final TextArea ta = new TextArea(2, 20); - ta.append("0123456789"); - ta.append(Platform.getNewline()); - ta.append("Some Text"); - ta.append(Platform.getNewline()); - frame.add(ta, BorderLayout.SOUTH); + setGLCanvasSize(null, glCanvas, new Dimension(width, height)); + + switch( frameLayout) { + case None: + frame.add(glCanvas); + break; + case TextOnBottom: + final TextArea ta = new TextArea(2, 20); + ta.append("0123456789"); + ta.append(Platform.getNewline()); + ta.append("Some Text"); + ta.append(Platform.getNewline()); + frame.setLayout(new BorderLayout()); + frame.add(ta, BorderLayout.SOUTH); + frame.add(glCanvas, BorderLayout.CENTER); + break; + case BorderCenterSurrounded: + frame.setLayout(new BorderLayout()); + frame.add(new Button("NORTH"), BorderLayout.NORTH); + frame.add(new Button("SOUTH"), BorderLayout.SOUTH); + frame.add(new Button("EAST"), BorderLayout.EAST); + frame.add(new Button("WEST"), BorderLayout.WEST); + frame.add(glCanvas, BorderLayout.CENTER); + break; + case DoubleBorderCenterSurrounded: + Container c = new Container(); + c.setLayout(new BorderLayout()); + c.add(new Button("north"), BorderLayout.NORTH); + c.add(new Button("south"), BorderLayout.SOUTH); + c.add(new Button("east"), BorderLayout.EAST); + c.add(new Button("west"), BorderLayout.WEST); + c.add(glCanvas, BorderLayout.CENTER); + + frame.setLayout(new BorderLayout()); + frame.add(new Button("NORTH"), BorderLayout.NORTH); + frame.add(new Button("SOUTH"), BorderLayout.SOUTH); + frame.add(new Button("EAST"), BorderLayout.EAST); + frame.add(new Button("WEST"), BorderLayout.WEST); + frame.add(c, BorderLayout.CENTER); + break; } - frame.add(glCanvas, BorderLayout.CENTER); frame.setTitle("Gears AWT Test (translucent "+!caps.isBackgroundOpaque()+"), swapInterval "+swapInterval); glCanvas.addGLEventListener(new GearsES2(swapInterval)); @@ -130,13 +180,24 @@ public class TestGearsES2AWT extends UITestCase { public void run() { frame.pack(); frame.setVisible(true); - }}); + }}); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glCanvas, true)); + animator.start(); Assert.assertTrue(animator.isStarted()); Assert.assertTrue(animator.isAnimating()); Assert.assertEquals(exclusiveContext ? awtEDT : null, glCanvas.getExclusiveContextThread()); animator.setUpdateFPSFrames(60, System.err); + System.err.println("canvas pos/siz: "+glCanvas.getX()+"/"+glCanvas.getY()+" "+glCanvas.getWidth()+"x"+glCanvas.getHeight()); + + if( null != rwsize ) { + Thread.sleep(500); // 500ms delay + setGLCanvasSize(frame, glCanvas, rwsize); + System.err.println("window resize pos/siz: "+glCanvas.getX()+"/"+glCanvas.getY()+" "+glCanvas.getWidth()+"x"+glCanvas.getHeight()); + } + while(!quitAdapter.shouldQuit() /* && animator.isAnimating() */ && animator.getTotalFPSDuration()<duration) { Thread.sleep(100); } @@ -184,19 +245,21 @@ public class TestGearsES2AWT extends UITestCase { if(useStencil) { caps.setStencilBits(1); } - if(shallUseOffscreenLayer) { + if(shallUseOffscreenFBOLayer) { caps.setOnscreen(false); } if(shallUseOffscreenPBufferLayer) { caps.setPBuffer(true); } - runTestGL(caps); + runTestGL(caps, frameLayout); } static long duration = 500; // ms - + static FrameLayout frameLayout = FrameLayout.None; + public static void main(String args[]) { boolean waitForKey = false; + int rw=-1, rh=-1; for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { @@ -204,6 +267,15 @@ public class TestGearsES2AWT extends UITestCase { try { duration = Integer.parseInt(args[i]); } catch (Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-rwidth")) { + i++; + rw = MiscUtils.atoi(args[i], rw); + } else if(args[i].equals("-rheight")) { + i++; + rh = MiscUtils.atoi(args[i], rh); + } else if(args[i].equals("-layout")) { + i++; + frameLayout = FrameLayout.valueOf(args[i]); } else if(args[i].equals("-es2")) { forceES2 = true; } else if(args[i].equals("-gl3")) { @@ -213,8 +285,8 @@ public class TestGearsES2AWT extends UITestCase { swapInterval = MiscUtils.atoi(args[i], swapInterval); } else if(args[i].equals("-exclctx")) { exclusiveContext = true; - } else if(args[i].equals("-layered")) { - shallUseOffscreenLayer = true; + } else if(args[i].equals("-layeredFBO")) { + shallUseOffscreenFBOLayer = true; } else if(args[i].equals("-layeredPBuffer")) { shallUseOffscreenPBufferLayer = true; } else if(args[i].equals("-msaa")) { @@ -223,8 +295,6 @@ public class TestGearsES2AWT extends UITestCase { useStencil = true; } else if(args[i].equals("-wait")) { waitForKey = true; - } else if(args[i].equals("-justGears")) { - addComp = false; } else if(args[i].equals("-shutdownKeepGLCanvas")) { shutdownRemoveGLCanvas = false; } else if(args[i].equals("-shutdownKeepFrame")) { @@ -236,11 +306,18 @@ public class TestGearsES2AWT extends UITestCase { shutdownSystemExit = true; } } + if( 0 < rw && 0 < rh ) { + rwsize = new Dimension(rw, rh); + } + + System.err.println("resize "+rwsize); + System.err.println("frameLayout "+frameLayout); System.err.println("forceES2 "+forceES2); System.err.println("forceGL3 "+forceGL3); System.err.println("swapInterval "+swapInterval); System.err.println("exclusiveContext "+exclusiveContext); - System.err.println("shallUseOffscreenLayer "+shallUseOffscreenLayer); + System.err.println("shallUseOffscreenFBOLayer "+shallUseOffscreenFBOLayer); + System.err.println("shallUseOffscreenPBufferLayer "+shallUseOffscreenPBufferLayer); if(waitForKey) { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java index 3910d1881..47891a8df 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java @@ -71,7 +71,7 @@ import org.junit.Test; public class TestGearsES2NEWT extends UITestCase { static int screenIdx = 0; static PointImmutable wpos; - static DimensionImmutable wsize; + static DimensionImmutable wsize, rwsize=null; static long duration = 500; // ms static boolean opaque = true; @@ -265,6 +265,11 @@ public class TestGearsES2NEWT extends UITestCase { System.err.println("GL chosen: "+glWindow.getChosenCapabilities()); System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets()); + if( null != rwsize ) { + Thread.sleep(500); // 500ms delay + glWindow.setSize(rwsize.getWidth(), rwsize.getHeight()); + System.err.println("window resize pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets()); + } while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration()<duration) { Thread.sleep(100); @@ -318,7 +323,7 @@ public class TestGearsES2NEWT extends UITestCase { public static void main(String args[]) throws IOException { mainRun = true; - int x=0, y=0, w=640, h=480; + int x=0, y=0, w=640, h=480, rw=-1, rh=-1; boolean usePos = false; for(int i=0; i<args.length; i++) { @@ -369,6 +374,12 @@ public class TestGearsES2NEWT extends UITestCase { i++; y = MiscUtils.atoi(args[i], y); usePos = true; + } else if(args[i].equals("-rwidth")) { + i++; + rw = MiscUtils.atoi(args[i], rw); + } else if(args[i].equals("-rheight")) { + i++; + rh = MiscUtils.atoi(args[i], rh); } else if(args[i].equals("-screen")) { i++; screenIdx = MiscUtils.atoi(args[i], 0); @@ -380,12 +391,16 @@ public class TestGearsES2NEWT extends UITestCase { } } wsize = new Dimension(w, h); + if( 0 < rw && 0 < rh ) { + rwsize = new Dimension(rw, rh); + } if(usePos) { wpos = new Point(x, y); } System.err.println("position "+wpos); System.err.println("size "+wsize); + System.err.println("resize "+rwsize); System.err.println("screen "+screenIdx); System.err.println("translucent "+(!opaque)); System.err.println("forceAlpha "+forceAlpha); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java index 5fca50593..ad2165301 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java @@ -29,10 +29,15 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.newt; import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Component; +import java.awt.Container; import java.awt.Frame; +import java.awt.TextArea; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import com.jogamp.common.os.Platform; import com.jogamp.newt.Display; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; @@ -62,14 +67,18 @@ import javax.media.opengl.GLProfile; import javax.swing.SwingUtilities; import org.junit.Assert; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.AfterClass; import org.junit.Test; public class TestGearsES2NewtCanvasAWT extends UITestCase { + public enum FrameLayout { None, TextOnBottom, BorderBottom, BorderBottom2, BorderCenter, BorderCenterSurrounded, DoubleBorderCenterSurrounded }; + public enum ResizeBy { GLWindow, Component, Frame }; + static int screenIdx = 0; static PointImmutable wpos; - static DimensionImmutable wsize; + static DimensionImmutable wsize, rwsize = null; static long duration = 500; // ms static boolean opaque = true; @@ -80,6 +89,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { static boolean showFPS = false; static int loops = 1; static boolean loop_shutdown = false; + static boolean shallUseOffscreenFBOLayer = false; static boolean forceES2 = false; static boolean forceGL3 = false; static boolean mainRun = false; @@ -96,7 +106,69 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { public static void releaseClass() { } - protected void runTestGL(GLCapabilitiesImmutable caps) throws InterruptedException, InvocationTargetException { + static void setGLWindowSize(final Frame frame, final GLWindow glw, final DimensionImmutable new_sz) { + try { + glw.setSize(new_sz.getWidth(), new_sz.getHeight()); + if( null != frame ) { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame.pack(); + } } ); + } + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + } + static void setComponentSize(final Frame frame, final Component comp, final DimensionImmutable new_sz) { + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + java.awt.Dimension d = new java.awt.Dimension(new_sz.getWidth(), new_sz.getHeight()); + comp.setMinimumSize(d); + comp.setPreferredSize(d); + comp.setSize(d); + if( null != frame ) { + frame.pack(); + } + } } ); + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + } + static void setFrameSize(final Frame frame, final boolean frameLayout, final DimensionImmutable new_sz) { + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + java.awt.Dimension d = new java.awt.Dimension(new_sz.getWidth(), new_sz.getHeight()); + frame.setSize(d); + if( frameLayout ) { + frame.validate(); + } + } } ); + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + } + + static void setSize(final ResizeBy resizeBy, final Frame frame, final boolean frameLayout, final Component comp, final GLWindow glw, final DimensionImmutable new_sz) { + switch( resizeBy ) { + case GLWindow: + setGLWindowSize(frameLayout ? frame : null, glw, new_sz); + break; + case Component: + setComponentSize(frameLayout ? frame : null, comp, new_sz); + break; + case Frame: + setFrameSize(frame, frameLayout, new_sz); + break; + } + } + + // public enum ResizeBy { GLWindow, Component, Frame }; + protected void runTestGL(final GLCapabilitiesImmutable caps, final ResizeBy resizeBy, final FrameLayout frameLayout) throws InterruptedException, InvocationTargetException { System.err.println("requested: vsync "+swapInterval+", "+caps); Display dpy = NewtFactory.createDisplay(null); Screen screen = NewtFactory.createScreen(dpy, screenIdx); @@ -104,13 +176,68 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { Assert.assertNotNull(glWindow); final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow); + if ( shallUseOffscreenFBOLayer ) { + newtCanvasAWT.setShallUseOffscreenLayer(true); + } + + final Frame frame = new Frame("AWT Parent Frame"); + + setSize(resizeBy, frame, false, newtCanvasAWT, glWindow, wsize); + + switch( frameLayout) { + case None: + frame.add(newtCanvasAWT); + break; + case TextOnBottom: + final TextArea ta = new TextArea(2, 20); + ta.append("0123456789"); + ta.append(Platform.getNewline()); + ta.append("Some Text"); + ta.append(Platform.getNewline()); + frame.setLayout(new BorderLayout()); + frame.add(ta, BorderLayout.SOUTH); + frame.add(newtCanvasAWT, BorderLayout.CENTER); + break; + case BorderBottom: + frame.setLayout(new BorderLayout()); + frame.add(newtCanvasAWT, BorderLayout.SOUTH); + break; + case BorderBottom2: + frame.setLayout(new BorderLayout()); + frame.add(newtCanvasAWT, BorderLayout.SOUTH); + frame.add(new Button("North"), BorderLayout.NORTH); + break; + case BorderCenter: + frame.setLayout(new BorderLayout()); + frame.add(newtCanvasAWT, BorderLayout.CENTER); + break; + case BorderCenterSurrounded: + frame.setLayout(new BorderLayout()); + frame.add(new Button("NORTH"), BorderLayout.NORTH); + frame.add(new Button("SOUTH"), BorderLayout.SOUTH); + frame.add(new Button("EAST"), BorderLayout.EAST); + frame.add(new Button("WEST"), BorderLayout.WEST); + frame.add(newtCanvasAWT, BorderLayout.CENTER); + break; + case DoubleBorderCenterSurrounded: + Container c = new Container(); + c.setLayout(new BorderLayout()); + c.add(new Button("north"), BorderLayout.NORTH); + c.add(new Button("south"), BorderLayout.SOUTH); + c.add(new Button("east"), BorderLayout.EAST); + c.add(new Button("west"), BorderLayout.WEST); + c.add(newtCanvasAWT, BorderLayout.CENTER); + + frame.setLayout(new BorderLayout()); + frame.add(new Button("NORTH"), BorderLayout.NORTH); + frame.add(new Button("SOUTH"), BorderLayout.SOUTH); + frame.add(new Button("EAST"), BorderLayout.EAST); + frame.add(new Button("WEST"), BorderLayout.WEST); + frame.add(c, BorderLayout.CENTER); + break; + } - final Frame frame1 = new Frame("AWT Parent Frame"); - frame1.setLayout(new BorderLayout()); - frame1.add(newtCanvasAWT, BorderLayout.CENTER); - frame1.setSize(wsize.getWidth(), wsize.getHeight()); - frame1.setLocation(0, 0); - frame1.setTitle("Gears NewtCanvasAWT Test (translucent "+!caps.isBackgroundOpaque()+"), swapInterval "+swapInterval+", size "+wsize+", pos "+wpos); + frame.setTitle("Gears NewtCanvasAWT Test (translucent "+!caps.isBackgroundOpaque()+"), swapInterval "+swapInterval+", size "+wsize+", pos "+wpos); final GearsES2 demo = new GearsES2(swapInterval); demo.setPMVUseBackingArray(pmvUseBackingArray); @@ -158,9 +285,16 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { SwingUtilities.invokeAndWait(new Runnable() { public void run() { - frame1.setVisible(true); + if( ResizeBy.Frame == resizeBy ) { + frame.validate(); + } else { + frame.pack(); + } + frame.setVisible(true); } - }); + }); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow, true)); animator.setUpdateFPSFrames(60, showFPS ? System.err : null); @@ -168,6 +302,12 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { System.err.println("GL chosen: "+glWindow.getChosenCapabilities()); System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets()); + if( null != rwsize ) { + Thread.sleep(500); // 500ms delay + setSize(resizeBy, frame, true, newtCanvasAWT, glWindow, rwsize); + System.err.println("window resize "+rwsize+" -> pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets()); + } + while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration()<duration) { Thread.sleep(100); } @@ -179,7 +319,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { Assert.assertEquals(null, glWindow.getExclusiveContextThread()); SwingUtilities.invokeAndWait(new Runnable() { public void run() { - frame1.dispose(); + frame.dispose(); } }); glWindow.destroy(); @@ -203,7 +343,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { if(-1 < forceAlpha) { caps.setAlphaBits(forceAlpha); } - runTestGL(caps); + runTestGL(caps, resizeBy, frameLayout); if(loop_shutdown) { GLProfile.shutdown(); } @@ -219,19 +359,35 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } final GLProfile glp = GLProfile.get(GLProfile.GL3); final GLCapabilities caps = new GLCapabilities( glp ); - runTestGL(caps); + runTestGL(caps, resizeBy, frameLayout); } + static FrameLayout frameLayout = FrameLayout.None; + static ResizeBy resizeBy = ResizeBy.Component; + public static void main(String args[]) throws IOException { mainRun = true; int x=0, y=0, w=640, h=480; + int rw=-1, rh=-1; boolean usePos = false; for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { i++; duration = MiscUtils.atol(args[i], duration); + } else if(args[i].equals("-rwidth")) { + i++; + rw = MiscUtils.atoi(args[i], rw); + } else if(args[i].equals("-rheight")) { + i++; + rh = MiscUtils.atoi(args[i], rh); + } else if(args[i].equals("-layout")) { + i++; + frameLayout = FrameLayout.valueOf(args[i]); + } else if(args[i].equals("-resizeBy")) { + i++; + resizeBy = ResizeBy.valueOf(args[i]); } else if(args[i].equals("-translucent")) { opaque = false; } else if(args[i].equals("-forceAlpha")) { @@ -244,6 +400,8 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } else if(args[i].equals("-vsync")) { i++; swapInterval = MiscUtils.atoi(args[i], swapInterval); + } else if(args[i].equals("-layeredFBO")) { + shallUseOffscreenFBOLayer = true; } else if(args[i].equals("-exclctx")) { exclusiveContext = true; } else if(args[i].equals("-es2")) { @@ -277,12 +435,19 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } } wsize = new Dimension(w, h); + if( 0 < rw && 0 < rh ) { + rwsize = new Dimension(rw, rh); + } if(usePos) { wpos = new Point(x, y); } + + System.err.println("frameLayout "+frameLayout); + System.err.println("resizeBy "+resizeBy); System.err.println("position "+wpos); System.err.println("size "+wsize); + System.err.println("resize "+rwsize); System.err.println("screen "+screenIdx); System.err.println("translucent "+(!opaque)); System.err.println("forceAlpha "+forceAlpha); @@ -290,6 +455,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { System.err.println("pmvDirect "+(!pmvUseBackingArray)); System.err.println("loops "+loops); System.err.println("loop shutdown "+loop_shutdown); + System.err.println("shallUseOffscreenFBOLayer "+shallUseOffscreenFBOLayer); System.err.println("forceES2 "+forceES2); System.err.println("forceGL3 "+forceGL3); System.err.println("swapInterval "+swapInterval); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasSWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasSWT.java new file mode 100644 index 000000000..cb2cf064f --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasSWT.java @@ -0,0 +1,375 @@ +/** + * Copyright 2011 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.demos.es2.newt; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.event.KeyAdapter; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.newt.swt.NewtCanvasSWT; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.test.junit.util.QuitAdapter; + +import com.jogamp.opengl.util.Animator; + +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; + +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PointImmutable; +import javax.media.nativewindow.util.DimensionImmutable; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLProfile; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.AfterClass; +import org.junit.Test; + +public class TestGearsES2NewtCanvasSWT extends UITestCase { + static int screenIdx = 0; + static PointImmutable wpos; + static DimensionImmutable wsize, rwsize = null; + + static long duration = 500; // ms + static boolean opaque = true; + static int forceAlpha = -1; + static boolean fullscreen = false; + static boolean pmvUseBackingArray = true; + static int swapInterval = 1; + static boolean showFPS = false; + static int loops = 1; + static boolean loop_shutdown = false; + static boolean forceES2 = false; + static boolean forceGL3 = false; + static boolean mainRun = false; + static boolean exclusiveContext = false; + + @BeforeClass + public static void initClass() { + if(null == wsize) { + wsize = new Dimension(640, 480); + } + } + + @AfterClass + public static void releaseClass() { + } + + Display display = null; + Shell shell = null; + Composite composite = null; + + @Before + public void init() { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display = new Display(); + Assert.assertNotNull( display ); + }}); + display.syncExec(new Runnable() { + public void run() { + shell = new Shell( display ); + Assert.assertNotNull( shell ); + shell.setLayout( new FillLayout() ); + composite = new Composite( shell, SWT.NONE ); + composite.setLayout( new FillLayout() ); + Assert.assertNotNull( composite ); + }}); + } + + @After + public void release() { + Assert.assertNotNull( display ); + Assert.assertNotNull( shell ); + Assert.assertNotNull( composite ); + try { + display.syncExec(new Runnable() { + public void run() { + composite.dispose(); + shell.dispose(); + }}); + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display.dispose(); + }}); + } + catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + display = null; + shell = null; + composite = null; + } + + protected void runTestGL(GLCapabilitiesImmutable caps) throws InterruptedException, InvocationTargetException { + System.err.println("requested: vsync "+swapInterval+", "+caps); + com.jogamp.newt.Display dpy = NewtFactory.createDisplay(null); + com.jogamp.newt.Screen screen = NewtFactory.createScreen(dpy, screenIdx); + final GLWindow glWindow = GLWindow.create(screen, caps); + Assert.assertNotNull(glWindow); + + final GearsES2 demo = new GearsES2(swapInterval); + demo.setPMVUseBackingArray(pmvUseBackingArray); + glWindow.addGLEventListener(demo); + + Animator animator = new Animator(); + animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD); + animator.setExclusiveContext(exclusiveContext); + + QuitAdapter quitAdapter = new QuitAdapter(); + //glWindow.addKeyListener(new TraceKeyAdapter(quitAdapter)); + //glWindow.addWindowListener(new TraceWindowAdapter(quitAdapter)); + glWindow.addKeyListener(quitAdapter); + glWindow.addWindowListener(quitAdapter); + + glWindow.addWindowListener(new WindowAdapter() { + public void windowResized(WindowEvent e) { + System.err.println("window resized: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()); + } + public void windowMoved(WindowEvent e) { + System.err.println("window moved: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()); + } + }); + + glWindow.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + if(e.getKeyChar()=='f') { + new Thread() { + public void run() { + final Thread t = glWindow.setExclusiveContextThread(null); + System.err.println("[set fullscreen pre]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets()); + glWindow.setFullscreen(!glWindow.isFullscreen()); + System.err.println("[set fullscreen post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets()); + glWindow.setExclusiveContextThread(t); + } }.start(); + } + } + }); + + animator.add(glWindow); + animator.start(); + Assert.assertTrue(animator.isStarted()); + Assert.assertTrue(animator.isAnimating()); + Assert.assertEquals(exclusiveContext ? animator.getThread() : null, glWindow.getExclusiveContextThread()); + + final NewtCanvasSWT canvas1 = NewtCanvasSWT.create( composite, 0, glWindow ); + Assert.assertNotNull( canvas1 ); + + display.syncExec( new Runnable() { + public void run() { + shell.setText( getSimpleTestName(".") ); + shell.setSize( wsize.getWidth(), wsize.getHeight() ); + if( null != wpos ) { + shell.setLocation( wpos.getX(), wpos.getY() ); + } + shell.open(); + } + }); + + animator.setUpdateFPSFrames(60, showFPS ? System.err : null); + + System.err.println("NW chosen: "+glWindow.getDelegatedWindow().getChosenCapabilities()); + System.err.println("GL chosen: "+glWindow.getChosenCapabilities()); + System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets()); + + if( null != rwsize ) { + for(int i=0; i<50; i++) { // 500 ms dispatched delay + if( !display.readAndDispatch() ) { + // blocks on linux .. display.sleep(); + Thread.sleep(10); + } + } + display.syncExec( new Runnable() { + public void run() { + shell.setSize( rwsize.getWidth(), rwsize.getHeight() ); + } + }); + System.err.println("window resize pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", "+glWindow.getInsets()); + } + + while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration()<duration) { + if( !display.readAndDispatch() ) { + // blocks on linux .. display.sleep(); + Thread.sleep(10); + } + } + + Assert.assertEquals(exclusiveContext ? animator.getThread() : null, glWindow.getExclusiveContextThread()); + animator.stop(); + Assert.assertFalse(animator.isAnimating()); + Assert.assertFalse(animator.isStarted()); + Assert.assertEquals(null, glWindow.getExclusiveContextThread()); + + canvas1.dispose(); + glWindow.destroy(); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow, false)); + } + + @Test + public void test01GL2ES2() throws InterruptedException, InvocationTargetException { + for(int i=1; i<=loops; i++) { + System.err.println("Loop "+i+"/"+loops); + final GLProfile glp; + if(forceGL3) { + glp = GLProfile.get(GLProfile.GL3); + } else if(forceES2) { + glp = GLProfile.get(GLProfile.GLES2); + } else { + glp = GLProfile.getGL2ES2(); + } + final GLCapabilities caps = new GLCapabilities( glp ); + caps.setBackgroundOpaque(opaque); + if(-1 < forceAlpha) { + caps.setAlphaBits(forceAlpha); + } + runTestGL(caps); + if(loop_shutdown) { + GLProfile.shutdown(); + } + } + } + + @Test + public void test02GL3() throws InterruptedException, InvocationTargetException { + if(mainRun) return; + + if( !GLProfile.isAvailable(GLProfile.GL3) ) { + System.err.println("GL3 n/a"); + } + final GLProfile glp = GLProfile.get(GLProfile.GL3); + final GLCapabilities caps = new GLCapabilities( glp ); + runTestGL(caps); + } + + public static void main(String args[]) throws IOException { + mainRun = true; + + int x=0, y=0, w=640, h=480, rw=-1, rh=-1; + boolean usePos = false; + + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + duration = MiscUtils.atol(args[i], duration); + } else if(args[i].equals("-translucent")) { + opaque = false; + } else if(args[i].equals("-forceAlpha")) { + i++; + forceAlpha = MiscUtils.atoi(args[i], 0); + } else if(args[i].equals("-fullscreen")) { + fullscreen = true; + } else if(args[i].equals("-pmvDirect")) { + pmvUseBackingArray = false; + } else if(args[i].equals("-vsync")) { + i++; + swapInterval = MiscUtils.atoi(args[i], swapInterval); + } else if(args[i].equals("-exclctx")) { + exclusiveContext = true; + } else if(args[i].equals("-es2")) { + forceES2 = true; + } else if(args[i].equals("-gl3")) { + forceGL3 = true; + } else if(args[i].equals("-showFPS")) { + showFPS = true; + } else if(args[i].equals("-width")) { + i++; + w = MiscUtils.atoi(args[i], w); + } else if(args[i].equals("-height")) { + i++; + h = MiscUtils.atoi(args[i], h); + } else if(args[i].equals("-x")) { + i++; + x = MiscUtils.atoi(args[i], x); + usePos = true; + } else if(args[i].equals("-y")) { + i++; + y = MiscUtils.atoi(args[i], y); + usePos = true; + } else if(args[i].equals("-rwidth")) { + i++; + rw = MiscUtils.atoi(args[i], rw); + } else if(args[i].equals("-rheight")) { + i++; + rh = MiscUtils.atoi(args[i], rh); + } else if(args[i].equals("-screen")) { + i++; + screenIdx = MiscUtils.atoi(args[i], 0); + } else if(args[i].equals("-loops")) { + i++; + loops = MiscUtils.atoi(args[i], 1); + } else if(args[i].equals("-loop-shutdown")) { + loop_shutdown = true; + } + } + wsize = new Dimension(w, h); + if( 0 < rw && 0 < rh ) { + rwsize = new Dimension(rw, rh); + } + + if(usePos) { + wpos = new Point(x, y); + } + System.err.println("position "+wpos); + System.err.println("size "+wsize); + System.err.println("resize "+rwsize); + System.err.println("screen "+screenIdx); + System.err.println("translucent "+(!opaque)); + System.err.println("forceAlpha "+forceAlpha); + System.err.println("fullscreen "+fullscreen); + System.err.println("pmvDirect "+(!pmvUseBackingArray)); + System.err.println("loops "+loops); + System.err.println("loop shutdown "+loop_shutdown); + System.err.println("forceES2 "+forceES2); + System.err.println("forceGL3 "+forceGL3); + System.err.println("swapInterval "+swapInterval); + System.err.println("exclusiveContext "+exclusiveContext); + + org.junit.runner.JUnitCore.main(TestGearsES2NewtCanvasSWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/swt/TestGearsES2SWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/swt/TestGearsES2SWT.java new file mode 100644 index 000000000..c6f299e83 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/swt/TestGearsES2SWT.java @@ -0,0 +1,340 @@ +/** + * Copyright 2011 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.demos.es2.swt; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.opengl.swt.GLCanvas; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; + +import com.jogamp.opengl.util.Animator; + +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; + +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PointImmutable; +import javax.media.nativewindow.util.DimensionImmutable; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLProfile; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.AfterClass; +import org.junit.Test; + +public class TestGearsES2SWT extends UITestCase { + static int screenIdx = 0; + static PointImmutable wpos; + static DimensionImmutable wsize, rwsize=null; + + static long duration = 500; // ms + static boolean opaque = true; + static int forceAlpha = -1; + static boolean fullscreen = false; + static boolean pmvUseBackingArray = true; + static int swapInterval = 1; + static boolean showFPS = false; + static int loops = 1; + static boolean loop_shutdown = false; + static boolean forceES2 = false; + static boolean forceGL3 = false; + static boolean mainRun = false; + static boolean exclusiveContext = false; + + @BeforeClass + public static void initClass() { + if(null == wsize) { + wsize = new Dimension(640, 480); + } + } + + @AfterClass + public static void releaseClass() { + } + + Display display = null; + Shell shell = null; + Composite composite = null; + + @Before + public void init() { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display = new Display(); + Assert.assertNotNull( display ); + }}); + display.syncExec(new Runnable() { + public void run() { + shell = new Shell( display ); + Assert.assertNotNull( shell ); + shell.setLayout( new FillLayout() ); + composite = new Composite( shell, SWT.NONE ); + composite.setLayout( new FillLayout() ); + Assert.assertNotNull( composite ); + }}); + } + + @After + public void release() { + Assert.assertNotNull( display ); + Assert.assertNotNull( shell ); + Assert.assertNotNull( composite ); + try { + display.syncExec(new Runnable() { + public void run() { + composite.dispose(); + shell.dispose(); + }}); + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display.dispose(); + }}); + } + catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + display = null; + shell = null; + composite = null; + } + + protected void runTestGL(GLCapabilitiesImmutable caps) throws InterruptedException, InvocationTargetException { + System.err.println("requested: vsync "+swapInterval+", "+caps); + + final GLCanvas canvas = GLCanvas.create( composite, 0, caps, null, null); + Assert.assertNotNull( canvas ); + + final GearsES2 demo = new GearsES2(swapInterval); + demo.setPMVUseBackingArray(pmvUseBackingArray); + canvas.addGLEventListener(demo); + + Animator animator = new Animator(); + animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD); + animator.setExclusiveContext(exclusiveContext); + + animator.add(canvas); + animator.start(); + Assert.assertTrue(animator.isStarted()); + Assert.assertTrue(animator.isAnimating()); + Assert.assertEquals(exclusiveContext ? animator.getThread() : null, canvas.getExclusiveContextThread()); + + display.syncExec( new Runnable() { + public void run() { + shell.setText( getSimpleTestName(".") ); + shell.setSize( wsize.getWidth(), wsize.getHeight() ); + if( null != wpos ) { + shell.setLocation( wpos.getX(), wpos.getY() ); + } + shell.open(); + } + }); + + animator.setUpdateFPSFrames(60, showFPS ? System.err : null); + + while(animator.isAnimating() && !canvas.isRealized() && animator.getTotalFPSDuration()<duration) { + if( !display.readAndDispatch() ) { + // blocks on linux .. display.sleep(); + Thread.sleep(10); + } + } + System.err.println("NW chosen: "+canvas.getDelegatedDrawable().getChosenGLCapabilities()); + System.err.println("GL chosen: "+canvas.getChosenGLCapabilities()); + System.err.println("window pos/siz: "+canvas.getLocation()+" "+canvas.getWidth()+"x"+canvas.getHeight()); + + if( null != rwsize ) { + for(int i=0; i<50; i++) { // 500 ms dispatched delay + if( !display.readAndDispatch() ) { + // blocks on linux .. display.sleep(); + Thread.sleep(10); + } + } + display.syncExec( new Runnable() { + public void run() { + shell.setSize( rwsize.getWidth(), rwsize.getHeight() ); + } + }); + System.err.println("window resize pos/siz: "+canvas.getLocation()+" "+canvas.getWidth()+"x"+canvas.getHeight()); + } + + while(animator.isAnimating() && animator.getTotalFPSDuration()<duration) { + if( !display.readAndDispatch() ) { + // blocks on linux .. display.sleep(); + Thread.sleep(10); + } + } + + Assert.assertEquals(exclusiveContext ? animator.getThread() : null, canvas.getExclusiveContextThread()); + animator.stop(); + Assert.assertFalse(animator.isAnimating()); + Assert.assertFalse(animator.isStarted()); + Assert.assertEquals(null, canvas.getExclusiveContextThread()); + + display.syncExec(new Runnable() { + public void run() { + canvas.dispose(); + } } ); + } + + @Test + public void test01GL2ES2() throws InterruptedException, InvocationTargetException { + for(int i=1; i<=loops; i++) { + System.err.println("Loop "+i+"/"+loops); + final GLProfile glp; + if(forceGL3) { + glp = GLProfile.get(GLProfile.GL3); + } else if(forceES2) { + glp = GLProfile.get(GLProfile.GLES2); + } else { + glp = GLProfile.getGL2ES2(); + } + final GLCapabilities caps = new GLCapabilities( glp ); + caps.setBackgroundOpaque(opaque); + if(-1 < forceAlpha) { + caps.setAlphaBits(forceAlpha); + } + runTestGL(caps); + if(loop_shutdown) { + GLProfile.shutdown(); + } + } + } + + @Test + public void test02GL3() throws InterruptedException, InvocationTargetException { + if(mainRun) return; + + if( !GLProfile.isAvailable(GLProfile.GL3) ) { + System.err.println("GL3 n/a"); + } + final GLProfile glp = GLProfile.get(GLProfile.GL3); + final GLCapabilities caps = new GLCapabilities( glp ); + runTestGL(caps); + } + + public static void main(String args[]) throws IOException { + mainRun = true; + + int x=0, y=0, w=640, h=480, rw=-1, rh=-1; + boolean usePos = false; + + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + duration = MiscUtils.atol(args[i], duration); + } else if(args[i].equals("-translucent")) { + opaque = false; + } else if(args[i].equals("-forceAlpha")) { + i++; + forceAlpha = MiscUtils.atoi(args[i], 0); + } else if(args[i].equals("-fullscreen")) { + fullscreen = true; + } else if(args[i].equals("-pmvDirect")) { + pmvUseBackingArray = false; + } else if(args[i].equals("-vsync")) { + i++; + swapInterval = MiscUtils.atoi(args[i], swapInterval); + } else if(args[i].equals("-exclctx")) { + exclusiveContext = true; + } else if(args[i].equals("-es2")) { + forceES2 = true; + } else if(args[i].equals("-gl3")) { + forceGL3 = true; + } else if(args[i].equals("-showFPS")) { + showFPS = true; + } else if(args[i].equals("-width")) { + i++; + w = MiscUtils.atoi(args[i], w); + } else if(args[i].equals("-height")) { + i++; + h = MiscUtils.atoi(args[i], h); + } else if(args[i].equals("-x")) { + i++; + x = MiscUtils.atoi(args[i], x); + usePos = true; + } else if(args[i].equals("-y")) { + i++; + y = MiscUtils.atoi(args[i], y); + usePos = true; + } else if(args[i].equals("-rwidth")) { + i++; + rw = MiscUtils.atoi(args[i], rw); + } else if(args[i].equals("-rheight")) { + i++; + rh = MiscUtils.atoi(args[i], rh); + } else if(args[i].equals("-screen")) { + i++; + screenIdx = MiscUtils.atoi(args[i], 0); + } else if(args[i].equals("-loops")) { + i++; + loops = MiscUtils.atoi(args[i], 1); + } else if(args[i].equals("-loop-shutdown")) { + loop_shutdown = true; + } + } + wsize = new Dimension(w, h); + if( 0 < rw && 0 < rh ) { + rwsize = new Dimension(rw, rh); + } + + if(usePos) { + wpos = new Point(x, y); + } + System.err.println("position "+wpos); + System.err.println("size "+wsize); + System.err.println("resize "+rwsize); + System.err.println("screen "+screenIdx); + System.err.println("translucent "+(!opaque)); + System.err.println("forceAlpha "+forceAlpha); + System.err.println("fullscreen "+fullscreen); + System.err.println("pmvDirect "+(!pmvUseBackingArray)); + System.err.println("loops "+loops); + System.err.println("loop shutdown "+loop_shutdown); + System.err.println("forceES2 "+forceES2); + System.err.println("forceGL3 "+forceGL3); + System.err.println("swapInterval "+swapInterval); + System.err.println("exclusiveContext "+exclusiveContext); + + org.junit.runner.JUnitCore.main(TestGearsES2SWT.class.getName()); + } +} 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/TestFocus01SwingAWTRobot.java b/src/test/com/jogamp/opengl/test/junit/newt/TestFocus01SwingAWTRobot.java index 5d6218df4..a87cbe0ac 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/TestFocus01SwingAWTRobot.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestFocus01SwingAWTRobot.java @@ -115,7 +115,8 @@ public class TestFocus01SwingAWTRobot extends UITestCase { // Wrap the window in a canvas. final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow1); - + // newtCanvasAWT.setShallUseOffscreenLayer(true); + // Monitor AWT focus and keyboard events. AWTKeyAdapter newtCanvasAWTKA = new AWTKeyAdapter("NewtCanvasAWT"); newtCanvasAWT.addKeyListener(newtCanvasAWTKA); @@ -146,7 +147,7 @@ public class TestFocus01SwingAWTRobot extends UITestCase { frame1.setVisible(true); } } ); Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame1, true)); - Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow1, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow1, true)); AWTRobotUtil.clearAWTFocus(robot); Assert.assertTrue(AWTRobotUtil.toFrontAndRequestFocus(robot, frame1)); 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 d63f0d2a7..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 @@ -87,11 +90,18 @@ public class TestWindowClosingProtocol01AWT extends UITestCase { glCanvas.setDefaultCloseOperation(WindowClosingMode.DISPOSE_ON_CLOSE); op = glCanvas.getDefaultCloseOperation(); Assert.assertEquals(WindowClosingMode.DISPOSE_ON_CLOSE, op); + + Thread.sleep(300); - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false)); // no frame close - Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glCanvas, false)); + 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); + } Assert.assertEquals(false, glCanvas.isRealized()); SwingUtilities.invokeAndWait(new Runnable() { @@ -103,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); @@ -126,7 +137,9 @@ 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 + Thread.sleep(300); + + Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false, closingListener)); // nop Thread.sleep(100); Assert.assertEquals(true, frame.isDisplayable()); Assert.assertEquals(true, glCanvas.isValid()); @@ -147,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 1657fcbe9..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); @@ -69,11 +69,13 @@ public class TestWindowClosingProtocol02NEWT extends UITestCase { glWindow.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); op = glWindow.getDefaultCloseOperation(); Assert.assertEquals(WindowClosingMode.DO_NOTHING_ON_CLOSE, op); + + 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 @@ -82,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 65068e9e8..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,18 +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); + } - Assert.assertEquals(true, AWTRobotUtil.closeWindow(frame, false)); + Thread.sleep(300); + + 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() { @@ -105,18 +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/newt/event/BaseNewtEventModifiers.java b/src/test/com/jogamp/opengl/test/junit/newt/event/BaseNewtEventModifiers.java index 31fa11e9d..4dd3bb98a 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/BaseNewtEventModifiers.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/BaseNewtEventModifiers.java @@ -33,6 +33,7 @@ import java.util.ArrayList ; import javax.media.opengl.GLProfile ; +import org.junit.After; import org.junit.Assert ; import org.junit.BeforeClass ; import org.junit.Test ; @@ -335,33 +336,32 @@ public abstract class BaseNewtEventModifiers extends UITestCase { _robot.setAutoDelay( MS_ROBOT_AUTO_DELAY ) ; { // Make sure all the buttons and modifier keys are released. - _releaseModifiers() ; - _escape() ; - eventDispatch(); eventDispatch(); eventDispatch(); - Thread.sleep( MS_ROBOT_POST_TEST_DELAY ) ; - eventDispatch(); eventDispatch(); eventDispatch(); + clearKeyboadAndMouse(); } _testMouseListener.setModifierCheckEnabled( true ) ; Throwable throwable = null; - final Object sync = new Object(); - final RunnableTask rt = new RunnableTask( testAction, sync, true ); + // final Object sync = new Object(); + final RunnableTask rt = new RunnableTask( testAction, null, true ); try { - new Thread(rt, "Test-Thread").start(); - while( !rt.isExecuted() && null == throwable ) { - eventDispatch(); - } - if(null==throwable) { - throwable = rt.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); - } + // synchronized(sync) { + new Thread(rt, "Test-Thread").start(); + int i=0; + while( !rt.isExecuted() && null == throwable ) { + System.err.println("WAIT-till-done: eventDispatch() #"+i++); + eventDispatch(); + } + if(null==throwable) { + throwable = rt.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + // } } finally { - _testMouseListener.setModifierCheckEnabled( false ) ; - eventDispatch(); eventDispatch(); eventDispatch(); - Thread.sleep( MS_ROBOT_POST_TEST_DELAY ) ; - eventDispatch(); eventDispatch(); eventDispatch(); + System.err.println("WAIT-till-done: DONE"); + _testMouseListener.setModifierCheckEnabled( false ) ; + clearKeyboadAndMouse(); } } @@ -677,19 +677,24 @@ public abstract class BaseNewtEventModifiers extends UITestCase { //////////////////////////////////////////////////////////////////////////// - public static void baseAfterClass() throws Exception { - + public void eventDispatchedPostTestDelay() throws Exception { + eventDispatch(); eventDispatch(); eventDispatch(); + Thread.sleep( MS_ROBOT_POST_TEST_DELAY ) ; + eventDispatch(); eventDispatch(); eventDispatch(); + _testMouseListener.clear(); + } + + public void clearKeyboadAndMouse() throws Exception { // Make sure all modifiers are released, otherwise the user's // desktop can get locked up (ask me how I know this). - _releaseModifiers() ; _escape() ; - Thread.sleep( MS_ROBOT_POST_TEST_DELAY ) ; + eventDispatchedPostTestDelay(); } //////////////////////////////////////////////////////////////////////////// - private static void _releaseModifiers() { + private void _releaseModifiers() { if (_robot != null) { @@ -714,7 +719,7 @@ public abstract class BaseNewtEventModifiers extends UITestCase { } } - private static void _escape() { + private void _escape() { if (_robot != null) { _robot.keyPress( java.awt.event.KeyEvent.VK_ESCAPE ) ; _robot.keyRelease( java.awt.event.KeyEvent.VK_ESCAPE ) ; diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersAWTCanvas.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersAWTCanvas.java index 17f81c583..a847ca671 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersAWTCanvas.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersAWTCanvas.java @@ -62,25 +62,27 @@ public class TestNewtEventModifiersAWTCanvas extends BaseNewtEventModifiers { _testFrame = new JFrame( "Event Modifier Test AWTCanvas" ) ; _testFrame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ) ; - _testFrame.getContentPane().add( canvas ) ; SwingUtilities.invokeAndWait(new Runnable() { public void run() { + _testFrame.getContentPane().add( canvas ) ; _testFrame.setBounds( TEST_FRAME_X, TEST_FRAME_Y, TEST_FRAME_WIDTH, TEST_FRAME_HEIGHT ) ; _testFrame.setVisible( true ) ; } }) ; - Assert.assertEquals(true, AWTRobotUtil.waitForVisible(_testFrame, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(_testFrame, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(canvas, true)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(canvas, true)); + AWTRobotUtil.assertRequestFocusAndWait(null, canvas, canvas, null, null); // programmatic Assert.assertNotNull(_robot); - AWTRobotUtil.requestFocus(_robot, canvas, false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows keyboard input + AWTRobotUtil.requestFocus(_robot, canvas, false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows keyboard input } //////////////////////////////////////////////////////////////////////////// @AfterClass public static void afterClass() throws Exception { - baseAfterClass(); SwingUtilities.invokeAndWait(new Runnable() { public void run() { _testFrame.dispose() ; diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNEWTWindowAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNEWTWindowAWT.java index 5428958a0..71191c863 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNEWTWindowAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNEWTWindowAWT.java @@ -58,7 +58,9 @@ public class TestNewtEventModifiersNEWTWindowAWT extends BaseNewtEventModifiers _glWindow.setPosition(TEST_FRAME_X, TEST_FRAME_Y); _glWindow.setVisible(true); - Assert.assertEquals(true, AWTRobotUtil.waitForRealized(_glWindow, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(_glWindow, true)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(_glWindow, true)); + AWTRobotUtil.assertRequestFocusAndWait(null, _glWindow, _glWindow, null, null); // programmatic Assert.assertNotNull(_robot); AWTRobotUtil.requestFocus(_robot, _glWindow, false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows keyboard input @@ -68,7 +70,6 @@ public class TestNewtEventModifiersNEWTWindowAWT extends BaseNewtEventModifiers @AfterClass public static void afterClass() throws Exception { - baseAfterClass(); _glWindow.destroy(); } diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasAWT.java index 6a3f4a54b..968d1af79 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasAWT.java @@ -82,6 +82,9 @@ public class TestNewtEventModifiersNewtCanvasAWT extends BaseNewtEventModifiers } } ) ; Assert.assertEquals(true, AWTRobotUtil.waitForVisible(_testFrame, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(_glWindow, true)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(_glWindow, true)); + AWTRobotUtil.assertRequestFocusAndWait(null, _glWindow, _glWindow, null, null); // programmatic Assert.assertNotNull(_robot); AWTRobotUtil.requestFocus(_robot, _glWindow, false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows keyboard input @@ -93,7 +96,6 @@ public class TestNewtEventModifiersNewtCanvasAWT extends BaseNewtEventModifiers @AfterClass public static void afterClass() throws Exception { - baseAfterClass(); SwingUtilities.invokeAndWait( new Runnable() { public void run() { _testFrame.dispose() ; diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java index cafc3dd46..6279b70dc 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java @@ -62,22 +62,23 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers //////////////////////////////////////////////////////////////////////////// - protected static void eventDispatch2xImpl() { - eventDispatchImpl(); + protected static void eventDispatchImpl() { + final int maxEvents = 10; try { Thread.sleep(100); } catch (InterruptedException e) { } - eventDispatchImpl(); - } - - protected static void eventDispatchImpl() { - if( !_display.isDisposed() ) { - if( !_display.readAndDispatch() ) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { } - } - } + final boolean[] res = { false }; + int i=0; + do { + SWTAccessor.invoke(_display, true, new Runnable() { + public void run() { + if( !_display.isDisposed() ) { + res[0] = _display.readAndDispatch(); + } else { + res[0] = false; + } + } } ); + } while( i<maxEvents && res[0] ); } @Override @@ -96,7 +97,7 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers }}); Assert.assertNotNull( _display ); - _display.syncExec(new Runnable() { + SWTAccessor.invoke(_display, true, new Runnable() { public void run() { _shell = new Shell( _display ); Assert.assertNotNull( _shell ); @@ -115,7 +116,7 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers NewtCanvasSWT.create( _composite, SWT.NO_BACKGROUND, _glWindow ) ; } - _display.syncExec( new Runnable() { + SWTAccessor.invoke(_display, true, new Runnable() { public void run() { _shell.setBounds( TEST_FRAME_X, TEST_FRAME_Y, TEST_FRAME_WIDTH, TEST_FRAME_HEIGHT ) ; _shell.open(); @@ -128,9 +129,9 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers // no waiting for results .. AWTRobotUtil.requestFocus(null, _glWindow, false); // programmatic - eventDispatch2xImpl(); + eventDispatchImpl(); AWTRobotUtil.requestFocus(_robot, _glWindow, INITIAL_MOUSE_X, INITIAL_MOUSE_Y); - eventDispatch2xImpl(); + eventDispatchImpl(); _glWindow.addMouseListener( _testMouseListener ) ; } @@ -139,23 +140,17 @@ public class TestNewtEventModifiersNewtCanvasSWT extends BaseNewtEventModifiers @AfterClass public static void afterClass() throws Exception { - baseAfterClass(); - _glWindow.destroy() ; try { - _display.syncExec(new Runnable() { - public void run() { - _composite.dispose(); - _shell.dispose(); - }}); - - if(!_display.isDisposed()) { - SWTAccessor.invoke(true, new Runnable() { - public void run() { + SWTAccessor.invoke(_display, true, new Runnable() { + public void run() { + _composite.dispose(); + _shell.dispose(); + if(!_display.isDisposed()) { _display.dispose(); - }}); - } + } + }}); } catch( Throwable throwable ) { throwable.printStackTrace(); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java index ec06379e0..08a181e10 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java @@ -47,6 +47,8 @@ import javax.swing.JFrame; import java.io.IOException; +import jogamp.nativewindow.jawt.JAWTUtil; + import org.junit.BeforeClass; import org.junit.Test; @@ -61,6 +63,11 @@ import com.jogamp.opengl.test.junit.util.*; /** * Testing combinations of key code modifiers of key event. + * + * <p> + * Due to limitation of AWT Robot, the test machine needs to have US keyboard enabled, + * even though we do unify VK codes to US keyboard across all layouts. + * </p> */ public class TestNewtKeyCodeModifiersAWT extends UITestCase { static int width, height; @@ -99,12 +106,14 @@ public class TestNewtKeyCodeModifiersAWT extends UITestCase { glWindow.destroy(); } - @Test - public void test02NewtCanvasAWT() throws AWTException, InterruptedException, InvocationTargetException { + private void testNewtCanvasAWT_Impl(boolean onscreen) throws AWTException, InterruptedException, InvocationTargetException { GLWindow glWindow = GLWindow.create(glCaps); // Wrap the window in a canvas. final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow); + if( !onscreen ) { + newtCanvasAWT.setShallUseOffscreenLayer(true); + } // Add the canvas to a frame, and make it all visible. final JFrame frame1 = new JFrame("Swing AWT Parent Frame: "+ glWindow.getTitle()); @@ -129,42 +138,63 @@ public class TestNewtKeyCodeModifiersAWT extends UITestCase { throwable.printStackTrace(); Assume.assumeNoException( throwable ); } - glWindow.destroy(); + glWindow.destroy(); + } + + @Test + public void test02NewtCanvasAWT_Onscreen() throws AWTException, InterruptedException, InvocationTargetException { + if( JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("Platform doesn't support onscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(true); } - static void testKeyCodeModifier(Robot robot, NEWTKeyAdapter keyAdapter, int modifierKey, int modifierMask) { + @Test + public void test03NewtCanvasAWT_Offsccreen() throws AWTException, InterruptedException, InvocationTargetException { + if( !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("Platform doesn't support offscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(false); + } + + @SuppressWarnings("deprecation") + static void testKeyCodeModifier(Robot robot, NEWTKeyAdapter keyAdapter, int modifierKey, int modifierMask, int keyCode, char keyCharOnly, char keyCharMod) { keyAdapter.reset(); - AWTRobotUtil.keyPress(0, robot, true, KeyEvent.VK_P, 10); // press P - AWTRobotUtil.keyPress(0, robot, false, KeyEvent.VK_P, 100); // release+typed P + AWTRobotUtil.keyPress(0, robot, true, keyCode, 10); // press keyCode + AWTRobotUtil.keyPress(0, robot, false, keyCode, 100); // release+typed keyCode robot.waitForIdle(); - for(int j=0; j < 10 && keyAdapter.getQueueSize() < 3; j++) { // wait until events are collected + for(int j=0; j < 40 && keyAdapter.getQueueSize() < 3; j++) { // wait until events are collected robot.delay(100); } AWTRobotUtil.keyPress(0, robot, true, modifierKey, 10); // press MOD - AWTRobotUtil.keyPress(0, robot, true, KeyEvent.VK_P, 10); // press P - AWTRobotUtil.keyPress(0, robot, false, KeyEvent.VK_P, 10); // release+typed P - AWTRobotUtil.keyPress(0, robot, false, modifierKey, 100); // release+typed MOD + AWTRobotUtil.keyPress(0, robot, true, keyCode, 10); // press keyCode + AWTRobotUtil.keyPress(0, robot, false, keyCode, 10); // release+typed keyCode + AWTRobotUtil.keyPress(0, robot, false, modifierKey, 100); // release MOD robot.waitForIdle(); - for(int j=0; j < 20 && keyAdapter.getQueueSize() < 3+6; j++) { // wait until events are collected + for(int j=0; j < 40 && keyAdapter.getQueueSize() < 3+5; j++) { // wait until events are collected robot.delay(100); } - NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, 3+6, 0); + NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, + 3 /* press-SI */, 3 /* release-SI */, 2 /* typed-SI */, + 0 /* press-AR */, 0 /* release-AR */, 0 /* typed-AR */ ); final List<EventObject> queue = keyAdapter.getQueued(); int i=0; - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, 0, KeyEvent.VK_P); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, 0, KeyEvent.VK_P); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, 0, KeyEvent.VK_P); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, 0, keyCode, keyCharOnly); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, 0, keyCode, keyCharOnly); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, 0, keyCode, keyCharOnly); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, modifierMask, modifierKey); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, modifierMask, KeyEvent.VK_P); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, modifierMask, KeyEvent.VK_P); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, modifierMask, KeyEvent.VK_P); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, modifierMask, modifierKey); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, modifierMask, modifierKey); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, modifierMask, modifierKey, (char)0); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, modifierMask, keyCode, keyCharMod); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, modifierMask, keyCode, keyCharMod); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, modifierMask, keyCode, keyCharMod); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, modifierMask, modifierKey, (char)0); } + @SuppressWarnings("deprecation") static void testKeyCodeAllModifierV1(Robot robot, NEWTKeyAdapter keyAdapter) { final int m1k = KeyEvent.VK_ALT; final int m1m = InputEvent.ALT_MASK; @@ -180,32 +210,31 @@ public class TestNewtKeyCodeModifiersAWT extends UITestCase { AWTRobotUtil.keyPress(0, robot, true, KeyEvent.VK_P, 10); // press P AWTRobotUtil.keyPress(0, robot, false, KeyEvent.VK_P, 100); // release+typed P - AWTRobotUtil.keyPress(0, robot, false, m3k, 10); // release+typed MOD - AWTRobotUtil.keyPress(0, robot, false, m2k, 10); // release+typed MOD - AWTRobotUtil.keyPress(0, robot, false, m1k, 10); // release+typed MOD + AWTRobotUtil.keyPress(0, robot, false, m3k, 10); // release MOD + AWTRobotUtil.keyPress(0, robot, false, m2k, 10); // release MOD + AWTRobotUtil.keyPress(0, robot, false, m1k, 10); // release MOD robot.waitForIdle(); - for(int j=0; j < 20 && keyAdapter.getQueueSize() < 3*4; j++) { // wait until events are collected + for(int j=0; j < 40 && keyAdapter.getQueueSize() < 4+4+1; j++) { // wait until events are collected robot.delay(100); } - NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, 3*4, 0); + NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, + 4 /* press-SI */, 4 /* release-SI */, 1 /* typed-SI */, + 0 /* press-AR */, 0 /* release-AR */, 0 /* typed-AR */ ); final List<EventObject> queue = keyAdapter.getQueued(); int i=0; - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m, m1k); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m|m2m, m2k); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m|m2m|m3m, m3k); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m, m1k, (char)0); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m|m2m, m2k, (char)0); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m|m2m|m3m, m3k, (char)0); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m|m2m|m3m, KeyEvent.VK_P); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m|m2m|m3m, KeyEvent.VK_P); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, m1m|m2m|m3m, KeyEvent.VK_P); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_PRESSED, m1m|m2m|m3m, KeyEvent.VK_P, (char)0); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m|m2m|m3m, KeyEvent.VK_P, (char)0); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, m1m|m2m|m3m, KeyEvent.VK_P, (char)0); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m|m2m|m3m, m3k); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, m1m|m2m|m3m, m3k); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m|m2m, m2k); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, m1m|m2m, m2k); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m, m1k); - NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_TYPED, m1m, m1k); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m|m2m|m3m, m3k, (char)0); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m|m2m, m2k, (char)0); + NEWTKeyUtil.validateKeyEvent((KeyEvent) queue.get(i++), KeyEvent.EVENT_KEY_RELEASED, m1m, m1k, (char)0); } void testImpl(GLWindow glWindow) throws AWTException, InterruptedException, InvocationTargetException { @@ -233,9 +262,11 @@ public class TestNewtKeyCodeModifiersAWT extends UITestCase { AWTRobotUtil.requestFocus(robot, glWindow, false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows keyboard input glWindow1KA.reset(); - testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_SHIFT, InputEvent.SHIFT_MASK); - testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_CONTROL, InputEvent.CTRL_MASK); - testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_ALT, InputEvent.ALT_MASK); + testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_SHIFT, InputEvent.SHIFT_MASK, KeyEvent.VK_1, '1', '!'); + testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_SHIFT, InputEvent.SHIFT_MASK, KeyEvent.VK_Y, 'y', 'Y'); // US: Y, DE: Z + testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_SHIFT, InputEvent.SHIFT_MASK, KeyEvent.VK_P, 'p', 'P'); + testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_CONTROL, InputEvent.CTRL_MASK, KeyEvent.VK_P, 'p', (char)0); + testKeyCodeModifier(robot, glWindow1KA, KeyEvent.VK_ALT, InputEvent.ALT_MASK, KeyEvent.VK_P, 'p', (char)0); testKeyCodeAllModifierV1(robot, glWindow1KA); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java index ef4b17375..b3ba71795 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java @@ -48,10 +48,13 @@ import javax.swing.JFrame; import java.io.IOException; +import jogamp.nativewindow.jawt.JAWTUtil; + import org.junit.BeforeClass; import org.junit.Test; import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.test.junit.jogl.demos.es2.RedSquareES2; @@ -99,12 +102,14 @@ public class TestNewtKeyCodesAWT extends UITestCase { glWindow.destroy(); } - @Test - public void test02NewtCanvasAWT() throws AWTException, InterruptedException, InvocationTargetException { + private void testNewtCanvasAWT_Impl(boolean onscreen) throws AWTException, InterruptedException, InvocationTargetException { GLWindow glWindow = GLWindow.create(glCaps); // Wrap the window in a canvas. final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow); + if( !onscreen ) { + newtCanvasAWT.setShallUseOffscreenLayer(true); + } // Add the canvas to a frame, and make it all visible. final JFrame frame1 = new JFrame("Swing AWT Parent Frame: "+ glWindow.getTitle()); @@ -131,7 +136,25 @@ public class TestNewtKeyCodesAWT extends UITestCase { } glWindow.destroy(); } + + @Test + public void test02NewtCanvasAWT_Onscreen() throws AWTException, InterruptedException, InvocationTargetException { + if( JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("Platform doesn't support onscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(true); + } + @Test + public void test03NewtCanvasAWT_Offsccreen() throws AWTException, InterruptedException, InvocationTargetException { + if( !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("Platform doesn't support offscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(false); + } + static CodeSeg[] codeSegments = new CodeSeg[] { new CodeSeg(0x008, 0x008, "bs"), // new CodeSeg(0x009, 0x009, "tab"), // TAB functions as focus traversal key @@ -167,14 +190,29 @@ public class TestNewtKeyCodesAWT extends UITestCase { keyAdapter.reset(); final CodeSeg codeSeg = codeSegments[i]; // System.err.println("*** Segment "+codeSeg.description); - for(int c=codeSeg.min; c<=codeSeg.max; c++) { + int eventCount = 0; + for(short c=codeSeg.min; c<=codeSeg.max; c++) { // System.err.println("*** KeyCode 0x"+Integer.toHexString(c)); - AWTRobotUtil.keyPress(0, robot, true, c, 10); - AWTRobotUtil.keyPress(0, robot, false, c, 100); + try { + AWTRobotUtil.keyPress(0, robot, true, c, 10); + } catch (Exception e) { + System.err.println("Exception @ AWT Robot.PRESS "+MiscUtils.toHexString(c)+" - "+e.getMessage()); + break; + } + eventCount++; + try { + AWTRobotUtil.keyPress(0, robot, false, c, 100); + } catch (Exception e) { + System.err.println("Exception @ AWT Robot.RELEASE "+MiscUtils.toHexString(c)+" - "+e.getMessage()); + break; + } + eventCount++; + if( KeyEvent.isPrintableKey(c) ) { + eventCount++; + } robot.waitForIdle(); } - final int codeCount = codeSeg.max - codeSeg.min + 1; - for(int j=0; j < 20 && keyAdapter.getQueueSize() < 3 * codeCount; j++) { // wait until events are collected + for(int j=0; j < 20 && keyAdapter.getQueueSize() < eventCount; j++) { // wait until events are collected robot.delay(100); } final ArrayList<EventObject> events = new ArrayList<EventObject>(keyAdapter.getQueued()); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java index 00fbc0500..d7de4e735 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java @@ -153,6 +153,7 @@ public class TestNewtKeyEventAutoRepeatAWT extends UITestCase { glWindow.destroy(); } + @SuppressWarnings("deprecation") static void testKeyEventAutoRepeat(Robot robot, NEWTKeyAdapter keyAdapter, int loops, int pressDurationMS) { System.err.println("KEY Event Auto-Repeat Test: "+loops); EventObject[][] first = new EventObject[loops][3]; @@ -201,9 +202,19 @@ public class TestNewtKeyEventAutoRepeatAWT extends UITestCase { final boolean hasAR = 0 < keyAdapter.getKeyPressedCount(true) ; { - final int expTotal = keyEvents.size(); - final int expAR = hasAR ? expTotal - 3 * 2 * loops : 0; // per loop: 3 for non AR events and 3 for non AR 'B' - NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, expTotal, expAR); + final int perLoopSI = 2; // per loop: 1 non AR event and 1 for non AR 'B' + final int expSI, expAR; + if( hasAR ) { + expSI = perLoopSI * loops; + expAR = ( keyEvents.size() - expSI*3 ) / 2; // AR: no typed -> 2, SI: typed -> 3 + } else { + expSI = keyEvents.size() / 3; // all typed events + expAR = 0; + } + + NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, + expSI /* press-SI */, expSI /* release-SI */, expSI /* typed-SI */, + expAR /* press-AR */, expAR /* release-AR */, 0 /* typed-AR */ ); } if( !hasAR ) { @@ -216,7 +227,7 @@ public class TestNewtKeyEventAutoRepeatAWT extends UITestCase { NEWTKeyUtil.dumpKeyEvents(Arrays.asList(first[i])); System.err.println("Auto-Repeat Loop "+i+" - Tail:"); NEWTKeyUtil.dumpKeyEvents(Arrays.asList(last[i])); - } + } for(int i=0; i<loops; i++) { KeyEvent e = (KeyEvent) first[i][0]; Assert.assertTrue("1st Shall be A, but is "+e, KeyEvent.VK_A == e.getKeyCode() ); @@ -230,7 +241,7 @@ public class TestNewtKeyEventAutoRepeatAWT extends UITestCase { e = (KeyEvent) first[i][2]; Assert.assertTrue("3rd Shall be A, but is "+e, KeyEvent.VK_A == e.getKeyCode() ); - Assert.assertTrue("3rd Shall be TYPED, but is "+e, KeyEvent.EVENT_KEY_TYPED == e.getEventType() ); + Assert.assertTrue("3rd Shall be PRESSED, but is "+e, KeyEvent.EVENT_KEY_PRESSED == e.getEventType() ); Assert.assertTrue("3rd Shall be AR, but is "+e, 0 != ( InputEvent.AUTOREPEAT_MASK & e.getModifiers() ) ); e = (KeyEvent) last[i][0]; diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventOrderAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventOrderAWT.java index 256536bbb..bdf932904 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventOrderAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventOrderAWT.java @@ -45,6 +45,8 @@ import javax.swing.JFrame; import java.io.IOException; +import jogamp.nativewindow.jawt.JAWTUtil; + import org.junit.BeforeClass; import org.junit.Test; @@ -104,12 +106,14 @@ public class TestNewtKeyEventOrderAWT extends UITestCase { glWindow.destroy(); } - @Test - public void test02NewtCanvasAWT() throws AWTException, InterruptedException, InvocationTargetException { + private void testNewtCanvasAWT_Impl(boolean onscreen) throws AWTException, InterruptedException, InvocationTargetException { GLWindow glWindow = GLWindow.create(glCaps); // Wrap the window in a canvas. final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow); + if( !onscreen ) { + newtCanvasAWT.setShallUseOffscreenLayer(true); + } // Add the canvas to a frame, and make it all visible. final JFrame frame1 = new JFrame("Swing AWT Parent Frame: "+ glWindow.getTitle()); @@ -137,6 +141,24 @@ public class TestNewtKeyEventOrderAWT extends UITestCase { glWindow.destroy(); } + @Test + public void test02NewtCanvasAWT_Onscreen() throws AWTException, InterruptedException, InvocationTargetException { + if( JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("Platform doesn't support onscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(true); + } + + @Test + public void test03NewtCanvasAWT_Offsccreen() throws AWTException, InterruptedException, InvocationTargetException { + if( !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("Platform doesn't support offscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(false); + } + static void testKeyEventOrder(Robot robot, NEWTKeyAdapter keyAdapter, int loops) { System.err.println("KEY Event Order Test: "+loops); keyAdapter.reset(); @@ -167,7 +189,11 @@ public class TestNewtKeyEventOrderAWT extends UITestCase { NEWTKeyUtil.validateKeyEventOrder(keyAdapter.getQueued()); - NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, 6*3*loops, 0); + final int expTotal = 6*loops; // all typed events + NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, + expTotal /* press-SI */, expTotal /* release-SI */, expTotal /* typed-SI */, + 0 /* press-AR */, 0 /* release-AR */, 0 /* typed-AR */ ); + } void testImpl(GLWindow glWindow) throws AWTException, InterruptedException, InvocationTargetException { diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyPressReleaseUnmaskRepeatAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyPressReleaseUnmaskRepeatAWT.java index 61579998e..2a35a15eb 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyPressReleaseUnmaskRepeatAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyPressReleaseUnmaskRepeatAWT.java @@ -45,6 +45,8 @@ import javax.swing.JFrame; import java.io.IOException; +import jogamp.nativewindow.jawt.JAWTUtil; + import org.junit.BeforeClass; import org.junit.Test; @@ -98,12 +100,14 @@ public class TestNewtKeyPressReleaseUnmaskRepeatAWT extends UITestCase { glWindow.destroy(); } - @Test - public void test02NewtCanvasAWT() throws AWTException, InterruptedException, InvocationTargetException { + private void testNewtCanvasAWT_Impl(boolean onscreen) throws AWTException, InterruptedException, InvocationTargetException { GLWindow glWindow = GLWindow.create(glCaps); // Wrap the window in a canvas. final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow); + if( !onscreen ) { + newtCanvasAWT.setShallUseOffscreenLayer(true); + } // Add the canvas to a frame, and make it all visible. final JFrame frame1 = new JFrame("Swing AWT Parent Frame: "+ glWindow.getTitle()); @@ -131,6 +135,24 @@ public class TestNewtKeyPressReleaseUnmaskRepeatAWT extends UITestCase { glWindow.destroy(); } + @Test + public void test02NewtCanvasAWT_Onscreen() throws AWTException, InterruptedException, InvocationTargetException { + if( JAWTUtil.isOffscreenLayerRequired() ) { + System.err.println("Platform doesn't support onscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(true); + } + + @Test + public void test03NewtCanvasAWT_Offsccreen() throws AWTException, InterruptedException, InvocationTargetException { + if( !JAWTUtil.isOffscreenLayerSupported() ) { + System.err.println("Platform doesn't support offscreen rendering."); + return; + } + testNewtCanvasAWT_Impl(false); + } + void testImpl(GLWindow glWindow) throws AWTException, InterruptedException, InvocationTargetException { final Robot robot = new Robot(); robot.setAutoWaitForIdle(true); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParentingFocusTraversal01AWT.java b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParentingFocusTraversal01AWT.java index d6f1f817a..122138f6d 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParentingFocusTraversal01AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParentingFocusTraversal01AWT.java @@ -191,6 +191,7 @@ public class TestParentingFocusTraversal01AWT extends UITestCase { frame1.setVisible(true); }}); Assert.assertEquals(true, AWTRobotUtil.waitForVisible(glWindow1, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow1, true)); Assert.assertEquals(newtCanvasAWT1.getNativeWindow(),glWindow1.getParent()); AWTRobotUtil.clearAWTFocus(robot); Assert.assertTrue(AWTRobotUtil.toFrontAndRequestFocus(robot, frame1)); 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+"]"; } } diff --git a/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java b/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java index c230f0aa1..0aee0f087 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java +++ b/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java @@ -51,6 +51,22 @@ public class MiscUtils { return def; } + public static String toHexString(byte hex) { + return "0x" + Integer.toHexString( (int)hex & 0x000000FF ); + } + + public static String toHexString(short hex) { + return "0x" + Integer.toHexString( (int)hex & 0x0000FFFF ); + } + + public static String toHexString(int hex) { + return "0x" + Integer.toHexString( hex ); + } + + public static String toHexString(long hex) { + return "0x" + Long.toHexString( hex ); + } + public static void assertFloatBufferEquals(String errmsg, FloatBuffer expected, FloatBuffer actual, float delta) { if(null == expected && null == actual) { return; diff --git a/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java b/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java index e090ed4cf..d007d2424 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java +++ b/src/test/com/jogamp/opengl/test/junit/util/NEWTKeyUtil.java @@ -38,28 +38,28 @@ import com.jogamp.newt.event.KeyEvent; public class NEWTKeyUtil { public static class CodeSeg { - public final int min; - public final int max; + public final short min; + public final short max; public final String description; public CodeSeg(int min, int max, String description) { - this.min = min; - this.max = max; + this.min = (short)min; + this.max = (short)max; this.description = description; } } public static class CodeEvent { - public final int code; + public final short code; public final String description; public final KeyEvent event; - public CodeEvent(int code, String description, KeyEvent event) { + public CodeEvent(short code, String description, KeyEvent event) { this.code = code; this.description = description; this.event = event; } public String toString() { - return "Code 0x"+Integer.toHexString(code)+" != "+event+" // "+description; + return "Code 0x"+Integer.toHexString( (int)code & 0x0000FFFF )+" != "+event+" // "+description; } } @@ -90,46 +90,63 @@ public class NEWTKeyUtil { public static boolean validateKeyCodes(List<CodeEvent> missCodes, CodeSeg codeSeg, List<EventObject> keyEvents, boolean verbose) { final int codeCount = codeSeg.max - codeSeg.min + 1; int misses = 0; + int evtIdx = 0; for(int i=0; i<codeCount; i++) { - final int c = codeSeg.min + i; - final int j = i*3+0; // KEY_PRESSED - final KeyEvent e = (KeyEvent) ( j < keyEvents.size() ? keyEvents.get(j) : null ); - if( null == e || c != e.getKeyCode() ) { + // evtIdx -> KEY_PRESSED ! + final short c = (short) ( codeSeg.min + i ); + final KeyEvent e = (KeyEvent) ( evtIdx < keyEvents.size() ? keyEvents.get(evtIdx) : null ); + if( null == e ) { missCodes.add(new CodeEvent(c, codeSeg.description, e)); misses++; + evtIdx++; + } else { + if( c != e.getKeyCode() ) { + missCodes.add(new CodeEvent(c, codeSeg.description, e)); + misses++; + } + if( KeyEvent.isPrintableKey(c) ) { + evtIdx += 3; // w/ TYPED + } else { + evtIdx += 2; + } } } - final boolean res = 3*codeCount == keyEvents.size() && 0 == missCodes.size(); + final boolean res = evtIdx == keyEvents.size() && 0 == missCodes.size(); if(verbose) { System.err.println("+++ Code Segment "+codeSeg.description+", Misses: "+misses+" / "+codeCount+", events "+keyEvents.size()+", valid "+res); } return res; } - public static void validateKeyEvent(KeyEvent e, int eventType, int modifier, int keyCode) { - if(0 <= keyCode) { - Assert.assertTrue("KeyEvent code mismatch, expected 0x"+Integer.toHexString(keyCode)+", has "+e, keyCode == e.getKeyCode()); - } + public static void validateKeyEvent(KeyEvent e, int eventType, int modifier, int keyCode, char keyChar) { if(0 <= eventType) { Assert.assertTrue("KeyEvent type mismatch, expected 0x"+Integer.toHexString(eventType)+", has "+e, eventType == e.getEventType()); } if(0 <= modifier) { Assert.assertTrue("KeyEvent modifier mismatch, expected 0x"+Integer.toHexString(modifier)+", has "+e, modifier == e.getModifiers()); } + if(0 < keyCode) { + Assert.assertTrue("KeyEvent code mismatch, expected 0x"+Integer.toHexString(keyCode)+", has "+e, keyCode == e.getKeyCode()); + } + if(0 < keyChar) { + Assert.assertTrue("KeyEvent char mismatch, expected 0x"+Integer.toHexString(keyChar)+", has "+e, keyChar == e.getKeyChar()); + } } - public static int getNextKeyEventType(int et) { + @SuppressWarnings("deprecation") + public static short getNextKeyEventType(KeyEvent e) { + final int et = e.getEventType(); switch( et ) { case KeyEvent.EVENT_KEY_PRESSED: return KeyEvent.EVENT_KEY_RELEASED; case KeyEvent.EVENT_KEY_RELEASED: - return KeyEvent.EVENT_KEY_TYPED; + return e.isPrintableKey() && !e.isAutoRepeat() ? KeyEvent.EVENT_KEY_TYPED : KeyEvent.EVENT_KEY_PRESSED; case KeyEvent.EVENT_KEY_TYPED: return KeyEvent.EVENT_KEY_PRESSED; default: - Assert.assertTrue("Invalid event type "+et, false); + Assert.assertTrue("Invalid event "+e, false); return 0; - } + } } public static void validateKeyEventOrder(List<EventObject> keyEvents) { @@ -141,45 +158,68 @@ public class NEWTKeyUtil { eet = KeyEvent.EVENT_KEY_PRESSED; } final int et = e.getEventType(); - Assert.assertEquals("Key event not in proper order", eet, et); - eet = getNextKeyEventType(et); + Assert.assertEquals("Key event not in proper order "+i+"/"+keyEvents.size()+" - event "+e, eet, et); + eet = getNextKeyEventType(e); keyCode2NextEvent.put(e.getKeyCode(), eet); } } /** - * * @param keyAdapter - * @param expTotalCount number of key press/release/types events - * @param expARCount number of key press/release/types Auto-Release events + * @param expPressedCountSI number of single key press events + * @param expReleasedCountSI number of single key release events + * @param expTypedCountSI number of single key types events + * @param expPressedCountAR number of auto-repeat key press events + * @param expReleasedCountAR number of auto-repeat key release events + * @param expTypedCountAR number of auto-repeat key types events */ - public static void validateKeyAdapterStats(NEWTKeyAdapter keyAdapter, int expTotalCount, int expARCount) { - final int keyPressed = keyAdapter.getKeyPressedCount(false); + public static void validateKeyAdapterStats(NEWTKeyAdapter keyAdapter, + int expPressedCountSI, int expReleasedCountSI, int expTypedCountSI, + int expPressedCountAR, int expReleasedCountAR, int expTypedCountAR) { + final int expTotalCountSI = expPressedCountSI + expReleasedCountSI + expTypedCountSI; + final int expTotalCountAR = expPressedCountAR + expReleasedCountAR + expTypedCountAR; + final int expTotalCountALL = expTotalCountSI + expTotalCountAR; + + final int keyPressedALL = keyAdapter.getKeyPressedCount(false); final int keyPressedAR = keyAdapter.getKeyPressedCount(true); - final int keyReleased = keyAdapter.getKeyReleasedCount(false); + final int keyReleasedALL = keyAdapter.getKeyReleasedCount(false); final int keyReleasedAR = keyAdapter.getKeyReleasedCount(true); - final int keyTyped = keyAdapter.getKeyTypedCount(false); + final int keyTypedALL = keyAdapter.getKeyTypedCount(false); final int keyTypedAR = keyAdapter.getKeyTypedCount(true); - final int keyPressedNR = keyPressed-keyPressedAR; - final int keyReleasedNR = keyReleased-keyReleasedAR; - final int keyTypedNR = keyTyped-keyTypedAR; - System.err.println("Total Press "+keyPressed +", Release "+keyReleased +", Typed "+keyTyped); - System.err.println("AutoR Press "+keyPressedAR+", Release "+keyReleasedAR+", Typed "+keyTypedAR); - System.err.println("No AR Press "+keyPressedNR+", Release "+keyReleasedNR+", Typed "+keyTypedNR); + + final int keyPressedSI = keyPressedALL-keyPressedAR; + final int keyReleasedSI = keyReleasedALL-keyReleasedAR; + final int keyTypedSI = keyTypedALL-keyTypedAR; + + final int totalCountALL = keyPressedALL + keyReleasedALL + keyTypedALL; + final int totalCountSI = keyPressedSI + keyReleasedSI + keyTypedSI; + final int totalCountAR = keyPressedAR + keyReleasedAR + keyTypedAR; + + System.err.println("Expec Single Press "+expPressedCountSI +", Release "+expReleasedCountSI +", Typed "+expTypedCountSI +", Events "+expTotalCountSI); + System.err.println("Expec AutoRp Press "+expPressedCountAR +", Release "+expReleasedCountAR +", Typed "+expTypedCountAR +", Events "+expTotalCountAR); + + System.err.println("Total Single Press "+keyPressedSI +", Release "+keyReleasedSI +", Typed "+keyTypedSI +", Events "+totalCountSI); + System.err.println("Total AutoRp Press "+keyPressedAR +", Release "+keyReleasedAR +", Typed "+keyTypedAR +", Events "+totalCountAR); + System.err.println("Total ALL Press "+keyPressedALL +", Release "+keyReleasedALL +", Typed "+keyTypedALL+", Events "+totalCountALL); + + Assert.assertEquals("Internal Error: totalSI != totalALL - totalAR", totalCountSI, totalCountALL - totalCountAR); + + Assert.assertEquals("Invalid: Has AR Typed events", 0, keyTypedAR); + Assert.assertEquals("Invalid: Exp AR Typed events", 0, expTypedCountAR); + + Assert.assertEquals("Key press count failure (SI)", expPressedCountSI, keyPressedSI); + Assert.assertEquals("Key released count failure (SI)", expReleasedCountSI, keyReleasedSI); + Assert.assertEquals("Key typed count failure (SI)", expTypedCountSI, keyTypedSI); + + Assert.assertEquals("Key press count failure (AR)", expPressedCountAR, keyPressedAR); + Assert.assertEquals("Key released count failure (AR)", expReleasedCountAR, keyReleasedAR); + + Assert.assertEquals("Key total count failure (SI)", expTotalCountSI, totalCountSI); + Assert.assertEquals("Key total count failure (AR)", expTotalCountAR, totalCountAR); final List<EventObject> keyEvents = keyAdapter.getQueued(); - Assert.assertEquals("Key event count not multiple of 3", 0, keyEvents.size()%3); - Assert.assertEquals("Key event count not 3 * press_release_count", expTotalCount, keyEvents.size()); - Assert.assertEquals("Key press count failure", expTotalCount/3, keyPressed); - Assert.assertEquals("Key press count failure (AR)", expARCount/3, keyPressedAR); - Assert.assertEquals("Key released count failure", expTotalCount/3, keyReleased); - Assert.assertEquals("Key released count failure (AR)", expARCount/3, keyReleasedAR); - Assert.assertEquals("Key typed count failure", expTotalCount/3, keyTyped); - Assert.assertEquals("Key typed count failure (AR)", expARCount/3, keyTypedAR); - // should be true - always, reaching this point - duh! - Assert.assertEquals( ( expTotalCount - expARCount ) / 3, keyPressedNR); - Assert.assertEquals( ( expTotalCount - expARCount ) / 3, keyReleasedNR); - Assert.assertEquals( ( expTotalCount - expARCount ) / 3, keyTypedNR); - } + Assert.assertEquals("Key total count failure (ALL) w/ list sum ", expTotalCountALL, totalCountALL); + Assert.assertEquals("Key total count failure (ALL) w/ list size ", expTotalCountALL, keyEvents.size()); + } } diff --git a/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java b/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java index b028df377..44e2eeda7 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java +++ b/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java @@ -28,7 +28,10 @@ package com.jogamp.opengl.test.junit.util; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.Iterator; import java.util.List; @@ -141,6 +144,14 @@ public abstract class UITestCase { System.err.println("++++ UITestCase.tearDown: "+getFullTestName(" - ")); } + public static void waitForKey(String preMessage) { + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println(preMessage+"> Press enter to continue"); + try { + System.err.println(stdin.readLine()); + } catch (IOException e) { } + } + static final String unsupportedTestMsg = "Test not supported on this platform."; public String getSnapshotFilename(int sn, String postSNDetail, GLCapabilitiesImmutable caps, int width, int height, boolean sinkHasAlpha, String fileSuffix, String destPath) { |