From 20bf031db719f7baa4c6e74734fc999061e08fe2 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 19 Jul 2012 21:15:10 +0200 Subject: Bug 599 - FBObject / Offscreen Support - Part 1 - New FBObject implementation handling FBO and it's attachments *** API CHANGE: Util -> Core *** while it's size and sample-count can be reconfigured on the fly. - com.jogamp.opengl.util.FBObject -> com.jogamp.opengl.FBObject - agnostic to texture unit - separate attachments using OO hierarchy reflecting FBO - handling MSAA and blitting - no FBO destruction for reconfig (attach/detach) - New GLFBODrawableImpl impl. an FBObject based GLDrawable - Instantiated by a dummy native surface (onscreen and invisible) hooked up to a dummy GLDrawable, which is the delegation for context creation. - Utilizies ProxySurface.UpstreamSurfaceHook for dummy surface avoiding specialization for native platforms. - TODO: Allow to utilize common surface interface as a dummy-surface to supporting API seperation of windowing/GL. The latter allows impl. of createGLDrawable(NativeSurface) with FBO. - New OffscreenAutoDrawable (extends GLAutoDrawableDelegate) for all offscreen drawables. Shall replace GLPbuffer. - New GLCapabilities*.isFBO() / setFBO(boolean) to request FBO offscreen, similar to isPBuffer(). Rule: if both are requested, FBO shall be favored. - GLContext adds raw FBO availability query (min. FBO avail), FBObject contains fine grained queries (TODO: Move parts to GLContext for efficiency). - Add framebuffer tracking, allowing fast querying: - GLBase/GLContext: public int getBoundFramebuffer(int target); public int getDefaultDrawFramebuffer(); public int getDefaultReadFramebuffer(); - GLContextImpl public final void setBoundFramebuffer(int target, int framebufferName) .. called by GL impl bind framebuffer - GL: getDefaultDrawFramebuffer(), getDefaultReadFramebuffer() Adding default framebuffer queries being issued by GL.glBindFramebuffer(target, 0) w/ a default framebuffer, o.e. zero. This allows a transparent use of a custom FBO even in case the applications attempts to reset FBO to zero. Value flow: GL <- GLContext <- GLDrawable, - GLCapabilities handle fbo/pbuffer seperate, don't disable the other - GLContext/GL track read/write framebuffer to be queried by FBObject to determine whether to bind/unbind a framebuffer - Test cases for multiple FBO w/ and w/o MSAA Other Features: - New interface ProxySurface.UpstreamSurfaceHook, allowing to hook an upstream surface of unknown type providing lifecycle and information (size, ..) callbacks. Used for all new dummy NativeSurface impl and SWT GLCanvas. - GLContext -> GLDrawable propagation context/drawable lifecycle via ProxySurface.UpstreamSurfaceHook allowing dynamic resources to react (create, init, ..) - contextRealized() - contextMadeCurrent() - SurfaceChangeable -> MutableSurface currently only contains setting the surface handle. TODO: May need to move ProxySurface.UpstreamSurfaceHook -> MutableSurface.UpstreamSurfaceHook, allowing other impl. classes (NEWT OffscreenWindow) to utilize the new upstream hookup mechanism - will allow FBO/Dummy window to work. - SWT GLCanvas using ProxySurface.UpstreamSurfaceHook for proper size propagation. - New GLAutoDrawable::getUpstreamWidget(), allowing GLEventListener to fetch the owning Java side UI element (NEWT, SWT, AWT, ..). - GLDrawableFactory: Removed createOffscreenSurface() - unused and not GL related - EGLDrawableFactory handles device/profile avail. mapping while actually creating context/drawable. This allows us to learn whether the ES context is software/hardware as well as FBO avail. - EGLDrawable: Removed secret buckets of EGL configs :) Employ native surface (X11, WGL, ..) to EGL 'mapping' in EGLDrawableFactory utilizing new EGLUpstreamSurfaceHook (implements ProxySurface.UpstreamSurfaceHook). Other Bugs: - Add CTX_OPTION_DEBUG to ctx/extension cache key since only a debug ctx may expose the ARB debug capability. This bug caused lack of ARB/AMD debug functionality. - Fix GLProfile deadlock (debug mode, w/ EGL/ES, no X11), dump availability information _after_ lock. - ImmModeSink draw(): Use GL's glDrawElements(..), don't cast for GL2ES1. Fixes use for GL2ES2. - Fix KeyEvent.getKeyChar() comment (-> only stable for keyTyped(..)) Misc: - Refined alot of API doc - New GLExtensions holds commonly used GL extension strings, allows better referencing and usage lookup. - Move GL (interface) decl. to GLBase - GLBuffers: Cleanup API doc (format, types) - TextureIO: Add PAM and PPM static suffix identifier - GLCapabilities getNumSamples() returns 0 if sampleBuffers is disabled, this seems to be more natural. - finalized a lot --- .../classes/com/jogamp/opengl/swt/GLCanvas.java | 72 ++++++++++++++++------ 1 file changed, 53 insertions(+), 19 deletions(-) (limited to 'src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java') diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 5ee58b78d..0d9d3ddf5 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -231,6 +231,8 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite) */ device = SWTAccessor.getDevice(this); + /* Since we have no means of querying the screen index yet, assume 0. Good choice due to Xinerama alike settings anyways. */ + final int screenIdx = 0; /* Native handle for the control, used to associate with GLContext */ nativeWindowHandle = SWTAccessor.getWindowHandle(this); @@ -243,7 +245,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(caps.getGLProfile()); /* Create a NativeWindow proxy for the SWT canvas */ - proxySurface = glFactory.createProxySurface(device, nativeWindowHandle, caps, chooser); + proxySurface = glFactory.createProxySurface(device, screenIdx, nativeWindowHandle, caps, chooser, swtCanvasUpStreamHook); /* Associate a GL surface with the proxy */ drawable = glFactory.createGLDrawable(proxySurface); @@ -265,12 +267,58 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { addControlListener(new ControlAdapter() { @Override public void controlResized(final ControlEvent arg0) { - clientArea = GLCanvas.this.getClientArea(); - /* Mark for OpenGL reshape next time the control is painted */ - sendReshape = true; + updateSizeCheck(); } }); } + private final ProxySurface.UpstreamSurfaceHook swtCanvasUpStreamHook = new ProxySurface.UpstreamSurfaceHook() { + @Override + public final void create(ProxySurface s) { /* nop */ } + + @Override + public final void destroy(ProxySurface s) { /* nop */ } + + @Override + public final int getWidth(ProxySurface s) { + return clientArea.width; + } + + @Override + public final int getHeight(ProxySurface s) { + return clientArea.height; + } + + @Override + public String toString() { + return "SETUpstreamSurfaceHook[upstream: "+GLCanvas.this.toString()+"]"; + } + + }; + + protected final void updateSizeCheck() { + clientArea = GLCanvas.this.getClientArea(); + if (clientArea != null && + proxySurface.getWidth() != clientArea.width && + proxySurface.getHeight() != clientArea.height) { + sendReshape = true; // Mark for OpenGL reshape next time the control is painted + } + sendReshape = false; + } + + @Override + public final Object getUpstreamWidget() { + return this; + } + + @Override + public int getWidth() { + return clientArea.width; + } + + @Override + public int getHeight() { + return clientArea.height; + } @Override public void addGLEventListener(final GLEventListener arg0) { @@ -417,25 +465,11 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { return (drawable != null) ? drawable.getHandle() : 0; } - @Override - public int getHeight() { - final Rectangle clientArea = this.clientArea; - if (clientArea == null) return 0; - return clientArea.height; - } - @Override public NativeSurface getNativeSurface() { return (drawable != null) ? drawable.getNativeSurface() : null; } - @Override - public int getWidth() { - final Rectangle clientArea = this.clientArea; - if (clientArea == null) return 0; - return clientArea.width; - } - @Override public boolean isRealized() { return (drawable != null) ? drawable.isRealized() : false; @@ -515,7 +549,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { // System.err.println(NativeWindowVersion.getInstance()); System.err.println(JoglVersion.getInstance()); - System.err.println(JoglVersion.getDefaultOpenGLInfo(null, true).toString()); + System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString()); final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) ); final Display display = new Display(); -- cgit v1.2.3 From 4b5a0f6557d7152ec770bc13ad3c494449de0529 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sun, 22 Jul 2012 04:16:55 +0200 Subject: Fix Bug 606 - New AWT threading implementation breaks .. ; Fix GLAutoDrawable multi-threading w/ proper pattern (hope so) Considering code changes and remarks: 3ed491213f8f7f05d7b9866b50d764370d8ff5f6 1a91ec5c8b6fd9d9db7bc115569c369fe7b38e9b 3334a924309a9361a448d69bc707d4cce416b430 4f27bcecf7484dc041551f52a5c49e2884cb3867 It seems necessary to have - recursive locking employed for all semantic actions which changes drawable & context (and the Window resource) - to avoid deadlock, we have to ensure the locked code segment will not spawn off to another thread, or a thread holds the lock, spawns of an action requiring the lock. .. sure - other read-only methods (flags, ..) shall at least utilize a safe local copy of a volatile field if further use to produce the result is necessary. - flags like sendReshape require to be volatile to guarantee it's being processed Patch impacts: AWT/SWT GLCanvas, GLAutoDrawableBase [and it's specializations] and hopefully closes any loopholes of missing a cache hit, etc. If you review this and find optimizations, i.e. removing a lock due to semantics etc, don't hold back and discuss it, please. --- .../classes/com/jogamp/opengl/swt/GLCanvas.java | 172 ++++++----- .../javax/media/opengl/GLAutoDrawableDelegate.java | 44 ++- .../classes/javax/media/opengl/awt/GLCanvas.java | 317 +++++++++++---------- .../classes/jogamp/opengl/GLAutoDrawableBase.java | 249 +++++++++++----- .../classes/jogamp/opengl/GLDrawableHelper.java | 17 +- src/jogl/classes/jogamp/opengl/GLPbufferImpl.java | 29 +- .../classes/com/jogamp/newt/opengl/GLWindow.java | 36 +-- src/newt/classes/jogamp/newt/WindowImpl.java | 99 +++---- .../jogl/acore/TestGLAutoDrawableDelegateNEWT.java | 10 +- .../acore/TestGLContextDrawableSwitchNEWT.java | 10 +- .../jogl/acore/TestGLContextSurfaceLockNEWT.java | 92 ++++-- 11 files changed, 649 insertions(+), 426 deletions(-) (limited to 'src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java') diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 0d9d3ddf5..64ee1c1ad 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -63,25 +63,13 @@ import org.eclipse.swt.widgets.Shell; import com.jogamp.common.GlueGenVersion; import com.jogamp.common.os.Platform; import com.jogamp.common.util.VersionUtil; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.swt.SWTAccessor; import com.jogamp.opengl.JoglVersion; /** * Native SWT Canvas implementing GLAutoDrawable - *

- * FIXME: If this instance runs in multithreading mode, see {@link Threading#isSingleThreaded()} (impossible), - * proper recursive locking is required for drawable/context @ destroy and display. - * Recreation etc could pull those instances while animating! - * Simply locking before using drawable/context offthread - * would allow a deadlock situation! - *

- *

- * NOTE: [MT-0] Methods utilizing [volatile] drawable/context are not synchronized. - In case any of the methods are called outside of a locked state - extra care should be added. Maybe we shall expose locking facilities to the user. - However, since the user shall stick to the GLEventListener model while utilizing - GLAutoDrawable implementations, she is safe due to the implicit locked state. - *

*/ public class GLCanvas extends Canvas implements GLAutoDrawable { @@ -97,8 +85,9 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { //private static final boolean useSWTThread = ThreadingImpl.getMode() != ThreadingImpl.WORKER; /* GL Stuff */ + private final RecursiveLock lock = LockFactory.createRecursiveLock(); private final GLDrawableHelper helper = new GLDrawableHelper(); - private volatile GLDrawable drawable; // volatile avoids locking all accessors. FIXME still need to sync destroy/display + private volatile GLDrawable drawable; // volatile: avoid locking for read-only access private GLContext context; /* Native window surface */ @@ -112,7 +101,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { private final GLCapabilitiesImmutable glCapsRequested; /* Flag indicating whether an unprocessed reshape is pending. */ - private volatile boolean sendReshape; + private volatile boolean sendReshape; // volatile: maybe written by WindowManager thread w/o locking /* * Invokes init(...) on all GLEventListeners. Assumes context is current when run. @@ -141,10 +130,16 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { }; /* Action to make specified context current prior to running displayAction */ - private final Runnable makeCurrentAndDisplayAction = new Runnable() { + private final Runnable makeCurrentAndDisplayOnEDTAction = new Runnable() { @Override public void run() { - helper.invokeGL(drawable, context, displayAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + helper.invokeGL(drawable, context, displayAction, initAction); + } finally { + _lock.unlock(); + } } }; @@ -157,10 +152,16 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { }; /* Swaps buffers, making the GLContext current first */ - private final Runnable makeCurrentAndSwapBuffersAction = new Runnable() { + private final Runnable makeCurrentAndSwapBuffersOnEDTAction = new Runnable() { @Override public void run() { - helper.invokeGL(drawable, context, swapBuffersAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + helper.invokeGL(drawable, context, swapBuffersAction, initAction); + } finally { + _lock.unlock(); + } } }; @@ -181,16 +182,33 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { private final Runnable disposeOnEDTGLAction = new Runnable() { @Override public void run() { - helper.disposeGL(GLCanvas.this, drawable, context, postDisposeGLAction); - } - }; - - private final Runnable disposeGraphicsDeviceAction = new Runnable() { - @Override - public void run() { - if (null != device) { - device.close(); - device = null; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if (null != drawable && null != context) { + boolean animatorPaused = false; + final GLAnimatorControl animator = getAnimator(); + if (null != animator) { + animatorPaused = animator.pause(); + } + + if(context.isCreated()) { + helper.disposeGL(GLCanvas.this, drawable, context, postDisposeGLAction); + } + + if (animatorPaused) { + animator.resume(); + } + } + // SWT is owner of the device handle, not us. + // Hence close() operation is a NOP. + if (null != device) { + device.close(); + device = null; + } + SWTAccessor.setRealized(GLCanvas.this, false); // unrealize .. + } finally { + _lock.unlock(); } } }; @@ -229,7 +247,8 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { clientArea = GLCanvas.this.getClientArea(); - /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite) */ + /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). + * Note: SWT is owner of the native handle, hence no closing operation will be a NOP. */ device = SWTAccessor.getDevice(this); /* Since we have no means of querying the screen index yet, assume 0. Good choice due to Xinerama alike settings anyways. */ final int screenIdx = 0; @@ -345,7 +364,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public void display() { - runInGLThread(makeCurrentAndDisplayAction); + runInGLThread(makeCurrentAndDisplayOnEDTAction); } @Override @@ -370,7 +389,8 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GL getGL() { - return (null == context) ? null : context.getGL(); + final GLContext _context = context; + return (null == _context) ? null : _context.getGL(); } @Override @@ -400,27 +420,35 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GLContext setContext(GLContext newCtx) { - final GLContext oldCtx = context; - final boolean newCtxCurrent = helper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); - context=(GLContextImpl)newCtx; - if(newCtxCurrent) { - context.makeCurrent(); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLContext oldCtx = context; + final boolean newCtxCurrent = helper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); + context=(GLContextImpl)newCtx; + if(newCtxCurrent) { + context.makeCurrent(); + } + return oldCtx; + } finally { + _lock.unlock(); } - return oldCtx; } @Override public void setContextCreationFlags(final int arg0) { additionalCtxCreationFlags = arg0; - if(null != context) { - context.setContextCreationFlags(additionalCtxCreationFlags); + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); } } @Override public GL setGL(final GL arg0) { - if (null != context) { - context.setGL(arg0); + final GLContext _context = context; + if (null != _context) { + _context.setGL(arg0); return arg0; } return null; @@ -428,12 +456,18 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GLContext createContext(final GLContext shareWith) { - if(drawable != null) { - final GLContext _ctx = drawable.createContext(shareWith); - _ctx.setContextCreationFlags(additionalCtxCreationFlags); - return _ctx; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if(drawable != null) { + final GLContext _ctx = drawable.createContext(shareWith); + _ctx.setContextCreationFlags(additionalCtxCreationFlags); + return _ctx; + } + return null; + } finally { + _lock.unlock(); } - return null; } @Override @@ -452,7 +486,8 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GLDrawableFactory getFactory() { - return (drawable != null) ? drawable.getFactory() : null; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getFactory() : null; } @Override @@ -462,17 +497,20 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public long getHandle() { - return (drawable != null) ? drawable.getHandle() : 0; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getHandle() : 0; } @Override public NativeSurface getNativeSurface() { - return (drawable != null) ? drawable.getNativeSurface() : null; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getNativeSurface() : null; } @Override public boolean isRealized() { - return (drawable != null) ? drawable.isRealized() : false; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.isRealized() : false; } @Override @@ -482,41 +520,19 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public void swapBuffers() throws GLException { - runInGLThread(makeCurrentAndSwapBuffersAction); + runInGLThread(makeCurrentAndSwapBuffersOnEDTAction); } // FIXME: API of update() method ? @Override public void update() { - // FIXME: display(); + // FIXME: display(); ? } @Override public void dispose() { - if (null != drawable && null != context) { // drawable is volatile! - boolean animatorPaused = false; - final GLAnimatorControl animator = getAnimator(); - if (null != animator) { - // can't remove us from animator for recreational addNotify() - animatorPaused = animator.pause(); - } - - if(context.isCreated()) { - runInGLThread(disposeOnEDTGLAction); - } - - if (animatorPaused) { - animator.resume(); - } - } - final Display display = getDisplay(); - - if (display.getThread() == Thread.currentThread()) { - disposeGraphicsDeviceAction.run(); - } else { - display.syncExec(disposeGraphicsDeviceAction); - } - super.dispose(); + runInGLThread(disposeOnEDTGLAction); + super.dispose(); } /** diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawableDelegate.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawableDelegate.java index 1f6166719..76959f3f4 100644 --- a/src/jogl/classes/javax/media/opengl/GLAutoDrawableDelegate.java +++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawableDelegate.java @@ -67,18 +67,18 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase { } // - // make protected methods accessible + // expose default methods // - public void defaultWindowRepaintOp() { + public final void windowRepaintOp() { super.defaultWindowRepaintOp(); } - public void defaultWindowResizedOp() { + public final void windowResizedOp() { super.defaultWindowResizedOp(); } - public void defaultWindowDestroyNotifyOp() { + public final void windowDestroyNotifyOp() { super.defaultWindowDestroyNotifyOp(); } @@ -89,6 +89,9 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase { private final RecursiveLock lock = LockFactory.createRecursiveLock(); // instance wide lock private final Object upstreamWidget; + @Override + protected final RecursiveLock getLock() { return lock; } + @Override public final Object getUpstreamWidget() { return upstreamWidget; @@ -97,39 +100,21 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase { /** * {@inheritDoc} *

- * This implementation calls {@link #defaultDestroyOp()}. + * This implementation calls {@link #defaultDestroy()}. *

*

* User still needs to destroy the upstream window, which details are hidden from this aspect. + * This can be performed by overriding {@link #destroyImplInLock()}. *

*/ @Override - public void destroy() { - lock.lock(); - try { - defaultDestroyOp(); - } finally { - lock.unlock(); - } + public final void destroy() { + defaultDestroy(); } @Override public void display() { - if( sendDestroy ) { - sendDestroy=false; - destroy(); - return; - } - - lock.lock(); // sync: context/drawable could been recreated/destroyed while animating - try { - if( null != drawable && drawable.isRealized() && null != context ) { - // surface is locked/unlocked implicit by context's makeCurrent/release - helper.invokeGL(drawable, context, defaultDisplayAction, defaultInitAction); - } - } finally { - lock.unlock(); - } + defaultDisplay(); } // @@ -145,4 +130,9 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase { public final void setRealized(boolean realized) { } + @Override + public final void swapBuffers() throws GLException { + defaultSwapBuffers(); + } + } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index c2e36ef9b..694a081b8 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -84,6 +84,8 @@ import javax.media.opengl.Threading; import com.jogamp.common.GlueGenVersion; import com.jogamp.common.util.VersionUtil; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; import com.jogamp.nativewindow.awt.AWTGraphicsDevice; import com.jogamp.nativewindow.awt.AWTGraphicsScreen; @@ -132,20 +134,6 @@ import jogamp.opengl.GLDrawableHelper; * - *

- * FIXME: If this instance runs in multithreading mode, see {@link Threading#isSingleThreaded()} (default: single-thread), - * proper recursive locking is required for drawable/context @ destroy and display. - * Recreation etc could pull those instances while animating! - * Simply locking before using drawable/context offthread - * would allow a deadlock situation! - *

- *

- * NOTE: [MT-0] Methods utilizing [volatile] drawable/context are not synchronized. - In case any of the methods are called outside of a locked state - extra care should be added. Maybe we shall expose locking facilities to the user. - However, since the user shall stick to the GLEventListener model while utilizing - GLAutoDrawable implementations, she is safe due to the implicit locked state. - *

*/ @SuppressWarnings("serial") @@ -153,11 +141,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private static final boolean DEBUG = Debug.debug("GLCanvas"); + private final RecursiveLock lock = LockFactory.createRecursiveLock(); private final GLDrawableHelper helper = new GLDrawableHelper(); private AWTGraphicsConfiguration awtConfig; - private volatile GLDrawable drawable; // volatile avoids locking all accessors. FIXME still need to sync destroy/display + private volatile GLDrawable drawable; // volatile: avoid locking for read-only access private GLContextImpl context; - private boolean sendReshape = false; + private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking // copy of the cstr args, mainly for recreation private GLCapabilitiesImmutable capsReqUser; @@ -278,8 +267,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public final boolean isOffscreenLayerSurfaceEnabled() { - if(null != drawable) { - return ((JAWTWindow)drawable.getNativeSurface()).isOffscreenLayerSurfaceEnabled(); + final GLDrawable _drawable = drawable; + if(null != _drawable) { + return ((JAWTWindow)_drawable.getNativeSurface()).isOffscreenLayerSurfaceEnabled(); } return false; } @@ -398,12 +388,18 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public GLContext createContext(final GLContext shareWith) { - if(drawable != null) { - final GLContext _ctx = drawable.createContext(shareWith); - _ctx.setContextCreationFlags(additionalCtxCreationFlags); - return _ctx; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if(drawable != null) { + final GLContext _ctx = drawable.createContext(shareWith); + _ctx.setContextCreationFlags(additionalCtxCreationFlags); + return _ctx; + } + return null; + } finally { + _lock.unlock(); } - return null; } @Override @@ -412,7 +408,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public boolean isRealized() { - return (null != drawable) ? drawable.isRealized() : false; + final GLDrawable _drawable = drawable; + return ( null != _drawable ) ? _drawable.isRealized() : false; } @Override @@ -427,50 +424,13 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public void display() { - if( !validateGLDrawable() ) { - if(DEBUG) { - System.err.println(getThreadName()+": Info: GLCanvas display - skipped GL render, drawable not valid yet"); - } - return; // not yet available .. - } Threading.invoke(true, displayOnEDTAction, getTreeLock()); - awtWindowClosingProtocol.addClosingListenerOneShot(); } private void dispose(boolean regenerate) { - final GLAnimatorControl animator = getAnimator(); - if(DEBUG) { - System.err.println(getThreadName()+": Info: dispose("+regenerate+") - START, hasContext " + - (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator); - Thread.dumpStack(); - } - - if(null!=drawable && null!=context) { // drawable is volatile! - boolean animatorPaused = false; - if(null!=animator) { - // can't remove us from animator for recreational addNotify() - animatorPaused = animator.pause(); - } - - disposeRegenerate=regenerate; - - if(context.isCreated()) { - Threading.invoke(true, disposeOnEDTAction, getTreeLock()); - } - - if(animatorPaused) { - animator.resume(); - } - } - - if(!regenerate) { - disposeAbstractGraphicsDevice(); - } - - if(DEBUG) { - System.err.println(getThreadName()+": dispose("+regenerate+") - END, "+animator); - } + disposeRegenerate=regenerate; + Threading.invoke(true, disposeOnEDTAction, getTreeLock()); } /** @@ -530,43 +490,49 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @SuppressWarnings("deprecation") @Override public void addNotify() { - if(DEBUG) { - System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()); - 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(); - } - - // init drawable by paint/display makes the init sequence more equal - // for all launch flavors (applet/javaws/..) - // validateGLDrawable(); - - if(DEBUG) { - System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer()); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if(DEBUG) { + System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()); + 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(); + } + + // init drawable by paint/display makes the init sequence more equal + // for all launch flavors (applet/javaws/..) + // validateGLDrawable(); + + if(DEBUG) { + System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer()); + } + } finally { + _lock.unlock(); } } @@ -585,23 +551,24 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } private boolean validateGLDrawable() { - boolean realized = false; - if (!Beans.isDesignTime()) { - if ( null != drawable ) { // OK: drawable is volatile - realized = drawable.isRealized(); - if ( !realized && 0 < drawable.getWidth() * drawable.getHeight() ) { - // make sure drawable realization happens on AWT EDT, due to AWTTree lock - AWTEDTExecutor.singleton.invoke(true, setRealizedOnEDTAction); - realized = true; - sendReshape=true; // ensure a reshape is being send .. - if(DEBUG) { - System.err.println(getThreadName()+": Realized Drawable: "+drawable.toString()); - Thread.dumpStack(); - } + final GLDrawable _drawable = drawable; + if ( null != _drawable ) { + if( _drawable.isRealized() ) { + return true; + } + if (!Beans.isDesignTime() && + 0 < _drawable.getWidth() * _drawable.getHeight() ) { + // make sure drawable realization happens on AWT EDT, due to AWTTree lock + AWTEDTExecutor.singleton.invoke(true, setRealizedOnEDTAction); + sendReshape=true; // ensure a reshape is being send .. + if(DEBUG) { + System.err.println(getThreadName()+": Realized Drawable: "+_drawable.toString()); + Thread.dumpStack(); } + return true; } } - return realized; + return false; } private Runnable setRealizedOnEDTAction = new Runnable() { @Override @@ -633,9 +600,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing try { dispose(false); } finally { - context=null; - drawable=null; - awtConfig=null; super.removeNotify(); } } @@ -655,7 +619,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); - if(null != drawable && drawable.isRealized() && !drawable.getChosenGLCapabilities().isOnscreen()) { + final GLDrawable _drawable = drawable; + if(null != _drawable && _drawable.isRealized() && !_drawable.getChosenGLCapabilities().isOnscreen()) { dispose(true); } else { sendReshape = true; @@ -710,13 +675,19 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public GLContext setContext(GLContext newCtx) { - final GLContext oldCtx = context; - final boolean newCtxCurrent = helper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); - context=(GLContextImpl)newCtx; - if(newCtxCurrent) { - context.makeCurrent(); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLContext oldCtx = context; + final boolean newCtxCurrent = helper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); + context=(GLContextImpl)newCtx; + if(newCtxCurrent) { + context.makeCurrent(); + } + return oldCtx; + } finally { + _lock.unlock(); } - return oldCtx; } @Override @@ -729,15 +700,15 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing if (Beans.isDesignTime()) { return null; } - GLContext ctx = getContext(); - return (ctx == null) ? null : ctx.getGL(); + final GLContext _context = context; + return (_context == null) ? null : _context.getGL(); } @Override public GL setGL(GL gl) { - GLContext ctx = getContext(); - if (ctx != null) { - ctx.setGL(gl); + final GLContext _context = context; + if (_context != null) { + _context.setGL(gl); return gl; } return null; @@ -762,8 +733,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public void setContextCreationFlags(int flags) { additionalCtxCreationFlags = flags; - if(null != context) { - context.setContextCreationFlags(additionalCtxCreationFlags); + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); } } @@ -796,26 +768,30 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public NativeSurface getNativeSurface() { - return (null != drawable) ? drawable.getNativeSurface() : null; + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getNativeSurface() : null; } @Override public long getHandle() { - return (null != drawable) ? drawable.getHandle() : 0; + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getHandle() : 0; } @Override public GLDrawableFactory getFactory() { - return (null != drawable) ? drawable.getFactory() : null; + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getFactory() : null; } @Override public String toString() { - final int dw = (null!=drawable) ? drawable.getWidth() : -1; - final int dh = (null!=drawable) ? drawable.getHeight() : -1; + final GLDrawable _drawable = drawable; + final int dw = (null!=_drawable) ? _drawable.getWidth() : -1; + final int dh = (null!=_drawable) ? _drawable.getHeight() : -1; return "AWT-GLCanvas[Realized "+isRealized()+ - ",\n\t"+((null!=drawable)?drawable.getClass().getName():"null-drawable")+ + ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+ ",\n\tFactory "+getFactory()+ ",\n\thandle 0x"+Long.toHexString(getHandle())+ ",\n\tDrawable size "+dw+"x"+dh+ @@ -829,7 +805,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // private boolean disposeRegenerate; - private final Runnable postDisposeAction = new Runnable() { + private final Runnable postDisposeOnEDTAction = new Runnable() { @Override public void run() { context=null; @@ -859,7 +835,47 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private final Runnable disposeOnEDTAction = new Runnable() { @Override public void run() { - helper.disposeGL(GLCanvas.this, drawable, context, postDisposeAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLAnimatorControl animator = getAnimator(); + + if(DEBUG) { + System.err.println(getThreadName()+": Info: dispose("+disposeRegenerate+") - START, hasContext " + + (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator); + Thread.dumpStack(); + } + + if(null!=drawable && null!=context) { + boolean animatorPaused = false; + if(null!=animator) { + // can't remove us from animator for recreational addNotify() + animatorPaused = animator.pause(); + } + + if(context.isCreated()) { + helper.disposeGL(GLCanvas.this, drawable, context, postDisposeOnEDTAction); + } + + if(animatorPaused) { + animator.resume(); + } + } + + if(!disposeRegenerate) { + if(null != awtConfig) { + disposeAbstractGraphicsDevice(); + } + awtConfig=null; + } + + if(DEBUG) { + System.err.println(getThreadName()+": dispose("+disposeRegenerate+") - END, "+animator); + } + + } finally { + _lock.unlock(); + } } }; @@ -879,7 +895,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing if(DEBUG) { System.err.println(getThreadName()+": GLCanvas.dispose(false): closed GraphicsDevice: "+adeviceMsg+", result: "+closed); } - awtConfig=null; } } }; @@ -890,7 +905,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * * @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice) */ - void disposeAbstractGraphicsDevice() { + private void disposeAbstractGraphicsDevice() { if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) { disposeAbstractGraphicsDeviceAction.run(); } else { @@ -941,14 +956,30 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private final Runnable displayOnEDTAction = new Runnable() { @Override public void run() { - helper.invokeGL(drawable, context, displayAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( validateGLDrawable() ) { + helper.invokeGL(drawable, context, displayAction, initAction); + } else if(DEBUG) { + System.err.println(getThreadName()+": Info: GLCanvas display - skipped GL render, drawable not valid yet"); + } + } finally { + _lock.unlock(); + } } }; private final Runnable swapBuffersOnEDTAction = new Runnable() { @Override public void run() { - helper.invokeGL(drawable, context, swapBuffersAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + helper.invokeGL(drawable, context, swapBuffersAction, initAction); + } finally { + _lock.unlock(); + } } }; diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java index 5c6d7446a..fe6d0fd76 100644 --- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -46,6 +46,7 @@ import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.opengl.util.Animator; @@ -63,11 +64,11 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { protected final GLDrawableHelper helper = new GLDrawableHelper(); protected final FPSCounterImpl fpsCounter = new FPSCounterImpl(); - protected GLDrawableImpl drawable; + protected volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access protected GLContextImpl context; protected int additionalCtxCreationFlags = 0; - protected boolean sendReshape = false; - protected boolean sendDestroy = false; + protected volatile boolean sendReshape = false; // volatile: maybe written by WindowManager thread w/o locking + protected volatile boolean sendDestroy = false; // volatile: maybe written by WindowManager thread w/o locking public GLAutoDrawableBase(GLDrawableImpl drawable, GLContextImpl context) { this.drawable = drawable; @@ -75,31 +76,47 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { resetFPSCounter(); } + protected abstract RecursiveLock getLock(); + /** Returns the delegated GLDrawable */ public final GLDrawable getDelegatedDrawable() { return drawable; } /** Default implementation to handle repaint events from the windowing system */ - protected void defaultWindowRepaintOp() { - if( null != drawable && drawable.isRealized() ) { - if( !drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimating() ) { + protected final void defaultWindowRepaintOp() { + final GLDrawable _drawable = drawable; + if( null != _drawable && _drawable.isRealized() ) { + if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimating() ) { display(); } - } + } } /** Default implementation to handle resize events from the windowing system */ - protected void defaultWindowResizedOp() { - if( null!=drawable ) { + protected final void defaultWindowResizedOp() { + final GLDrawable _drawable = drawable; + if( null!=_drawable ) { if(DEBUG) { System.err.println("GLAutoDrawableBase.sizeChanged: ("+Thread.currentThread().getName()+"): "+getWidth()+"x"+getHeight()+" - surfaceHandle 0x"+Long.toHexString(getNativeSurface().getSurfaceHandle())); } - sendReshape = true; - defaultWindowRepaintOp(); + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock + if( _drawable.isRealized() ) { + if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimating() ) { + display(); + } + } } } - /** Default implementation to handle destroy notifications from the windowing system */ - protected void defaultWindowDestroyNotifyOp() { + /** + * Default implementation to handle destroy notifications from the windowing system. + * + *

+ * If the {@link NativeSurface} does not implement {@link WindowClosingProtocol} + * or {@link WindowClosingMode#DISPOSE_ON_CLOSE} is enabled (default), + * {@link #defaultDestroy()} is being called. + *

+ */ + protected final void defaultWindowDestroyNotifyOp() { final NativeSurface ns = getNativeSurface(); final boolean shallClose; if(ns instanceof WindowClosingProtocol) { @@ -108,27 +125,66 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { shallClose = true; } if( shallClose ) { - // Is an animator thread perform rendering? - if (helper.isExternalAnimatorRunning()) { - // Pause animations before initiating safe destroy. - final GLAnimatorControl ctrl = helper.getAnimator(); - final boolean isPaused = ctrl.pause(); - destroy(); - if(isPaused) { - ctrl.resume(); - } - } else if (null != ns && ns.isSurfaceLockedByOtherThread()) { - // surface is locked by another thread - // Flag that destroy should be performed on the next - // attempt to display. - sendDestroy = true; - } else { - // Without an external thread animating or locking the - // surface, we are safe. - destroy (); - } + destroyAvoidAwareOfLocking(); } } + + /** + * Calls {@link #destroy()} + * directly if the following requirements are met: + * + *

+ * Otherwise destroy is being flagged to be called within the next + * call of display(). + *

+ *

+ * This method is being used to avoid deadlock if + * destruction is desired by other threads, e.g. the window manager. + *

+ * @see #defaultWindowDestroyNotifyOp() + * @see #defaultDisplay() + */ + protected final void destroyAvoidAwareOfLocking() { + final NativeSurface ns = getNativeSurface(); + + final GLAnimatorControl ctrl = helper.getAnimator(); + + // Is an animator thread perform rendering? + if ( helper.isAnimatorRunningOnOtherThread() ) { + // Pause animations before initiating safe destroy. + final boolean isPaused = ctrl.pause(); + destroy(); + if(isPaused) { + ctrl.resume(); + } + } else if (null != ns && ns.isSurfaceLockedByOtherThread()) { + // surface is locked by another thread + // Flag that destroy should be performed on the next + // attempt to display. + sendDestroy = true; // async, but avoiding deadlock + } else { + // Without an external thread animating or locking the + // surface, we are safe. + destroy(); + } + } + + /** + * Calls {@link #destroyImplInLock()} while claiming the lock. + */ + protected final void defaultDestroy() { + final RecursiveLock lock = getLock(); + lock.lock(); + try { + destroyImplInLock(); + } finally { + lock.unlock(); + } + } /** * Default implementation to destroys the drawable and context of this GLAutoDrawable: @@ -137,24 +193,42 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { *
  • destroys the GLContext, if valid
  • *
  • destroys the GLDrawable, if valid
  • * + *

    Method assumes the lock is being hold.

    + *

    Override it to extend it to destroy your resources, i.e. the actual window. + * In such case call super.destroyImplInLock first.

    */ - protected void defaultDestroyOp() { - if( null != drawable && drawable.isRealized() ) { - if( null != context && context.isCreated() ) { + protected void destroyImplInLock() { + final GLContext _context = context; + final GLDrawable _drawable = drawable; + if( null != _drawable && _drawable.isRealized() ) { + if( null != _context && _context.isCreated() ) { // Catch dispose GLExceptions by GLEventListener, just 'print' them // so we can continue with the destruction. try { - helper.disposeGL(this, drawable, context, null); + helper.disposeGL(this, _drawable, _context, null); } catch (GLException gle) { gle.printStackTrace(); } } - drawable.setRealized(false); + _drawable.setRealized(false); } context = null; drawable = null; } + public final void defaultSwapBuffers() throws GLException { + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + if(drawable!=null && context != null) { + drawable.swapBuffers(); + helper.invokeGL(drawable, context, defaultSwapAction, defaultInitAction); + } + } finally { + _lock.unlock(); + } + } + // // GLAutoDrawable // @@ -179,6 +253,30 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { fpsCounter.tickFPS(); } }; + protected final void defaultDisplay() { + if( sendDestroy ) { + sendDestroy=false; + destroy(); + return; + } + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + if( null != context ) { + // surface is locked/unlocked implicit by context's makeCurrent/release + helper.invokeGL(drawable, context, defaultDisplayAction, defaultInitAction); + } + } finally { + _lock.unlock(); + } + } + + protected final Runnable defaultSwapAction = new Runnable() { + @Override + public final void run() { + drawable.swapBuffers(); + } } ; + @Override public final GLContext getContext() { return context; @@ -186,27 +284,35 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { @Override public final GLContext setContext(GLContext newCtx) { - final GLContext oldCtx = context; - final boolean newCtxCurrent = helper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); - context=(GLContextImpl)newCtx; - if(newCtxCurrent) { - context.makeCurrent(); + final RecursiveLock lock = getLock(); + lock.lock(); + try { + final GLContext oldCtx = context; + final boolean newCtxCurrent = helper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); + context=(GLContextImpl)newCtx; + if(newCtxCurrent) { + context.makeCurrent(); + } + return oldCtx; + } finally { + lock.unlock(); } - return oldCtx; } @Override public final GL getGL() { - if (context == null) { + final GLContext _context = context; + if (_context == null) { return null; } - return context.getGL(); + return _context.getGL(); } @Override public final GL setGL(GL gl) { - if (context != null) { - context.setGL(gl); + final GLContext _context = context; + if (_context != null) { + _context.setGL(gl); return gl; } return null; @@ -261,8 +367,9 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { @Override public final void setContextCreationFlags(int flags) { additionalCtxCreationFlags = flags; - if(null != context) { - context.setContextCreationFlags(additionalCtxCreationFlags); + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); } } @@ -331,27 +438,36 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { @Override public final GLContext createContext(final GLContext shareWith) { - if(drawable != null) { - final GLContext _ctx = drawable.createContext(shareWith); - _ctx.setContextCreationFlags(additionalCtxCreationFlags); - return _ctx; + final RecursiveLock lock = getLock(); + lock.lock(); + try { + if(drawable != null) { + final GLContext _ctx = drawable.createContext(shareWith); + _ctx.setContextCreationFlags(additionalCtxCreationFlags); + return _ctx; + } + return null; + } finally { + lock.unlock(); } - return null; } @Override public final boolean isRealized() { - return null != drawable ? drawable.isRealized() : false; + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.isRealized() : false; } @Override public int getWidth() { - return null != drawable ? drawable.getWidth() : 0; + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getWidth() : 0; } @Override public int getHeight() { - return null != drawable ? drawable.getHeight() : 0; + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getHeight() : 0; } /** @@ -373,30 +489,27 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { return helper.getSkipContextReleaseThread(); } - @Override - public final void swapBuffers() throws GLException { - if(drawable!=null && context != null) { - drawable.swapBuffers(); - } - } - @Override public final GLCapabilitiesImmutable getChosenGLCapabilities() { - return null != drawable ? drawable.getChosenGLCapabilities() : null; + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getChosenGLCapabilities() : null; } @Override public final GLProfile getGLProfile() { - return null != drawable ? drawable.getGLProfile() : null; + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getGLProfile() : null; } @Override public final NativeSurface getNativeSurface() { - return null != drawable ? drawable.getNativeSurface() : null; + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getNativeSurface() : null; } @Override public final long getHandle() { - return null != drawable ? drawable.getHandle() : 0; + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getHandle() : 0; } } diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index 0c01aa676..090c5fe69 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -189,8 +189,9 @@ public class GLDrawableHelper { */ public final void dispose(GLAutoDrawable drawable) { synchronized(listenersLock) { - for (int i=0; i < listeners.size(); i++) { - listeners.get(i).dispose(drawable); + final ArrayList _listeners = listeners; + for (int i=0; i < _listeners.size(); i++) { + _listeners.get(i).dispose(drawable); } } } @@ -209,8 +210,9 @@ public class GLDrawableHelper { /** The default init action to be called once after ctx is being created @ 1st makeCurrent(). */ public final void init(GLAutoDrawable drawable) { synchronized(listenersLock) { - for (int i=0; i < listeners.size(); i++) { - final GLEventListener listener = listeners.get(i) ; + final ArrayList _listeners = listeners; + for (int i=0; i < _listeners.size(); i++) { + final GLEventListener listener = _listeners.get(i) ; // If make current ctx, invoked by invokGL(..), results in a new ctx, init gets called. // This may happen not just for initial setup, but for ctx recreation due to resource change (drawable/window), @@ -232,8 +234,9 @@ public class GLDrawableHelper { } private final void displayImpl(GLAutoDrawable drawable) { synchronized(listenersLock) { - for (int i=0; i < listeners.size(); i++) { - final GLEventListener listener = listeners.get(i) ; + final ArrayList _listeners = listeners; + for (int i=0; i < _listeners.size(); i++) { + final GLEventListener listener = _listeners.get(i) ; // GLEventListener may need to be init, // in case this one is added after the realization of the GLAutoDrawable init( listener, drawable, true /* sendReshape */) ; @@ -324,7 +327,7 @@ public class GLDrawableHelper { } } - public final boolean isExternalAnimatorRunning() { + public final boolean isAnimatorRunningOnOtherThread() { return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; } diff --git a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java index bbc28e283..6b64120fe 100644 --- a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java @@ -42,10 +42,14 @@ package jogamp.opengl; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLPbuffer; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; + /** Platform-independent class exposing pbuffer functionality to applications. This class is not exposed in the public API as it would probably add no value; however it implements the GLDrawable @@ -101,7 +105,7 @@ public class GLPbufferImpl extends GLAutoDrawableBase implements GLPbuffer { // // GLDrawable delegation // - + @Override public final void setRealized(boolean realized) { } @@ -109,6 +113,10 @@ public class GLPbufferImpl extends GLAutoDrawableBase implements GLPbuffer { // // GLAutoDrawable completion // + private final RecursiveLock lock = LockFactory.createRecursiveLock(); // instance wide lock + + @Override + protected final RecursiveLock getLock() { return lock; } @Override public final Object getUpstreamWidget() { @@ -117,7 +125,7 @@ public class GLPbufferImpl extends GLAutoDrawableBase implements GLPbuffer { @Override public void destroy() { - defaultDestroyOp(); + defaultDestroy(); } @Override @@ -126,12 +134,23 @@ public class GLPbufferImpl extends GLAutoDrawableBase implements GLPbuffer { } @Override - public void display() { - if( null != drawable && drawable.isRealized() && null != context ) { - helper.invokeGL(drawable, context, defaultDisplayAction, initAction); + public final void display() { + final RecursiveLock _lock = lock; + _lock.lock(); // sync: context/drawable could been recreated/destroyed while animating + try { + if( null != context ) { + helper.invokeGL(drawable, context, defaultDisplayAction, initAction); + } + } finally { + _lock.unlock(); } } + @Override + public final void swapBuffers() throws GLException { + defaultSwapBuffers(); + } + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index 32d44502f..2205aec8e 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -53,6 +53,7 @@ import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import jogamp.newt.WindowImpl; @@ -62,6 +63,7 @@ import jogamp.opengl.GLDrawableImpl; import com.jogamp.common.GlueGenVersion; import com.jogamp.common.util.VersionUtil; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; import com.jogamp.newt.Window; @@ -87,13 +89,6 @@ import com.jogamp.opengl.JoglVersion; * you can inject {@link javax.media.opengl.GLRunnable} objects * via {@link #invoke(boolean, javax.media.opengl.GLRunnable)} to the OpenGL command stream.
    *

    - *

    - * NOTE: [MT-0] Methods utilizing [volatile] drawable/context are not synchronized. - In case any of the methods are called outside of a locked state - extra care should be added. Maybe we shall expose locking facilities to the user. - However, since the user shall stick to the GLEventListener model while utilizing - GLAutoDrawable implementations, she is safe due to the implicit locked state. - *

    */ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Window, NEWTEventConsumer, FPSCounter { private final WindowImpl window; @@ -437,7 +432,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind //e1.printStackTrace(); } - defaultDestroyOp(); + destroyImplInLock(); if(Window.DEBUG_IMPLEMENTATION) { System.err.println("GLWindow.destroy() "+WindowImpl.getThreadName()+", fin"); @@ -515,6 +510,11 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind private GLContext sharedContext = null; + @Override + protected final RecursiveLock getLock() { + return window.getLock(); + } + /** * Specifies an {@link javax.media.opengl.GLContext OpenGL context} to share with.
    * At native creation, {@link #setVisible(boolean) setVisible(true)}, @@ -537,19 +537,18 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind return; } - window.lockWindow(); // sync: context/drawable could have been recreated/destroyed while animating + final RecursiveLock lock = window.getLock(); + lock.lock(); // sync: context/drawable could have been recreated/destroyed while animating try { - if( null == context && 0 display + setVisible(true); } } finally { - window.unlockWindow(); + lock.unlock(); } } @@ -559,7 +558,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind private GLDrawableFactory factory; @Override - public GLDrawableFactory getFactory() { + public final GLDrawableFactory getFactory() { return factory; } @@ -567,6 +566,11 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind public final void setRealized(boolean realized) { } + @Override + public final void swapBuffers() throws GLException { + defaultSwapBuffers(); + } + //---------------------------------------------------------------------- // NEWTEventConsumer // diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 002144b2f..b12e42680 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -564,9 +564,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer @Override public final int lockSurface() throws NativeWindowException, RuntimeException { - windowLock.lock(); - surfaceLock.lock(); - int res = surfaceLock.getHoldCount() == 1 ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ? + final RecursiveLock _wlock = windowLock; + final RecursiveLock _slock = surfaceLock; + _wlock.lock(); + _slock.lock(); + int res = _slock.getHoldCount() == 1 ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ? if ( LOCK_SURFACE_NOT_READY == res ) { try { @@ -583,8 +585,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } finally { if (LOCK_SURFACE_NOT_READY >= res) { - surfaceLock.unlock(); - windowLock.unlock(); + _slock.unlock(); + _wlock.unlock(); } } } @@ -593,10 +595,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer @Override public final void unlockSurface() { - surfaceLock.validateLocked(); - windowLock.validateLocked(); + final RecursiveLock _slock = surfaceLock; + final RecursiveLock _wlock = windowLock; + _slock.validateLocked(); + _wlock.validateLocked(); - if (surfaceLock.getHoldCount() == 1) { + if (_slock.getHoldCount() == 1) { final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice(); try { unlockSurfaceImpl(); @@ -604,8 +608,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer adevice.unlock(); } } - surfaceLock.unlock(); - windowLock.unlock(); + _slock.unlock(); + _wlock.unlock(); } @Override @@ -618,21 +622,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return surfaceLock.getOwner(); } - public final void lockWindow() { - windowLock.lock(); - } - public final void unlockWindow() { - windowLock.unlock(); + public final RecursiveLock getLock() { + return windowLock; } - public final boolean isWindowLockedByOtherThread() { - return windowLock.isLockedByOtherThread(); - } - - public final Thread getWindowLockOwner() { - return windowLock.getOwner(); - } - public long getSurfaceHandle() { return windowHandle; // default: return window handle } @@ -670,11 +663,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public Point getLocationOnScreen(Point storage) { if(isNativeValid()) { Point d; - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { d = getLocationOnScreenImpl(0, 0); } finally { - windowLock.unlock(); + _lock.unlock(); } if(null!=d) { if(null!=storage) { @@ -717,7 +711,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer boolean nativeWindowCreated = false; boolean madeVisible = false; - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(null!=lifecycleHook) { lifecycleHook.resetCounter(); @@ -739,7 +734,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer madeVisible = nativeWindowCreated; } // always flag visible, allowing a retry .. - WindowImpl.this.visible = true; + WindowImpl.this.visible = true; } else if(WindowImpl.this.visible != visible) { if(isNativeValid()) { setVisibleImpl(visible, getX(), getY(), getWidth(), getHeight()); @@ -766,7 +761,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer System.err.println("Window setVisible: END ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible); } } finally { - windowLock.unlock(); + _lock.unlock(); } if( nativeWindowCreated || madeVisible ) { sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener @@ -801,7 +796,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public final void run() { boolean recreate = false; - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if ( !isFullscreen() && ( getWidth() != width || getHeight() != height ) ) { recreate = isNativeValid() && !getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); @@ -842,7 +838,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(recreate) { screen.removeReference(); // bring back ref-count } - windowLock.unlock(); + _lock.unlock(); } } } @@ -863,7 +859,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=lifecycleHook) { lifecycleHook.destroyActionPreLock(); } - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(DEBUG_IMPLEMENTATION) { System.err.println("Window DestroyAction() "+getThreadName()); @@ -917,7 +914,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer hasFocus = false; parentWindowHandle = 0; - windowLock.unlock(); + _lock.unlock(); } if(animatorPaused) { lifecycleHook.resumeRenderingAction(); @@ -1002,8 +999,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer int width = getWidth(); int height = getHeight(); boolean wasVisible; - - windowLock.lock(); + + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(isNativeValid()) { // force recreation if offscreen, since it may become onscreen @@ -1220,7 +1218,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer System.err.println("Window.reparentWindow: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+x+"/"+y+" "+width+"x"+height); } } finally { - windowLock.unlock(); + _lock.unlock(); } if(wasVisible) { switch (operation) { @@ -1245,7 +1243,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private class ReparentActionRecreate implements Runnable { public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { visible = true; if(DEBUG_IMPLEMENTATION) { @@ -1253,7 +1252,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } setVisible(true); // native creation } finally { - windowLock.unlock(); + _lock.unlock(); } } } @@ -1291,7 +1290,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(WindowImpl.this.undecorated != undecorated) { // set current state @@ -1311,7 +1311,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } finally { - windowLock.unlock(); + _lock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } @@ -1333,7 +1333,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(WindowImpl.this.alwaysOnTop != alwaysOnTop) { // set current state @@ -1353,7 +1354,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } finally { - windowLock.unlock(); + _lock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } @@ -1545,7 +1546,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer "\n, ParentWindow "+parentWindow+ "\n, ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+ "\n, WindowHandle "+toHexString(getWindowHandle())+ - "\n, SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+isWindowLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ + "\n, SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+windowLock.isLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ "\n, Pos "+getX()+"/"+getY()+" (auto "+autoPosition()+"), size "+getWidth()+"x"+getHeight()+ "\n, Visible "+isVisible()+", focus "+hasFocus()+ "\n, Undecorated "+undecorated+" ("+isUndecorated()+")"+ @@ -1675,7 +1676,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(DEBUG_IMPLEMENTATION) { System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)); @@ -1689,7 +1691,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } finally { - windowLock.unlock(); + _lock.unlock(); } } } @@ -1718,7 +1720,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public boolean fsOn() { return fullscreen; } public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { // set current state WindowImpl.this.fullscreen = fullscreen; @@ -1795,7 +1798,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } finally { - windowLock.unlock(); + _lock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } @@ -1917,7 +1920,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // special repaint treatment case WindowEvent.EVENT_WINDOW_REPAINT: // queue repaint event in case window is locked, ie in operation - if( null != getWindowLockOwner() ) { + if( null != windowLock.getOwner() ) { // make sure only one repaint event is queued if(!repaintQueued) { repaintQueued=true; @@ -1936,7 +1939,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // common treatment case WindowEvent.EVENT_WINDOW_RESIZED: // queue event in case window is locked, ie in operation - if( null != getWindowLockOwner() ) { + if( null != windowLock.getOwner() ) { final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen(); if(DEBUG_IMPLEMENTATION) { System.err.println("Window.consumeEvent: "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); 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 index eb716677d..426b7734f 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLAutoDrawableDelegateNEWT.java @@ -87,8 +87,8 @@ public class TestGLAutoDrawableDelegateNEWT extends UITestCase { final GLAutoDrawableDelegate glad = new GLAutoDrawableDelegate(drawable, context, window) { @Override - public void destroy() { - super.destroy(); // destroys drawable/context + protected void destroyImplInLock() { + super.destroyImplInLock(); // destroys drawable/context window.destroy(); // destroys the actual window } }; @@ -97,15 +97,15 @@ public class TestGLAutoDrawableDelegateNEWT extends UITestCase { window.addWindowListener(new WindowAdapter() { @Override public void windowRepaint(WindowUpdateEvent e) { - glad.defaultWindowRepaintOp(); + glad.windowRepaintOp(); } @Override public void windowResized(WindowEvent e) { - glad.defaultWindowResizedOp(); + glad.windowResizedOp(); } @Override public void windowDestroyNotify(WindowEvent e) { - glad.defaultWindowDestroyNotifyOp(); + glad.windowDestroyNotifyOp(); } }); window.addWindowListener(wl); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java index 06aa29b4f..92b4c5238 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextDrawableSwitchNEWT.java @@ -90,8 +90,8 @@ public class TestGLContextDrawableSwitchNEWT extends UITestCase { final GLAutoDrawableDelegate glad = new GLAutoDrawableDelegate(drawable, null, window) { @Override - public void destroy() { - super.destroy(); // destroys drawable/context + protected void destroyImplInLock() { + super.destroyImplInLock(); window.destroy(); // destroys the actual window } }; @@ -100,15 +100,15 @@ public class TestGLContextDrawableSwitchNEWT extends UITestCase { window.addWindowListener(new WindowAdapter() { @Override public void windowRepaint(WindowUpdateEvent e) { - glad.defaultWindowRepaintOp(); + glad.windowRepaintOp(); } @Override public void windowResized(WindowEvent e) { - glad.defaultWindowResizedOp(); + glad.windowResizedOp(); } @Override public void windowDestroyNotify(WindowEvent e) { - glad.defaultWindowDestroyNotifyOp(); + glad.windowDestroyNotifyOp(); } }); window.addWindowListener(wl); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextSurfaceLockNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextSurfaceLockNEWT.java index 78988c0e5..b3516d6b4 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextSurfaceLockNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLContextSurfaceLockNEWT.java @@ -33,9 +33,9 @@ import java.io.IOException; import javax.media.nativewindow.NativeSurface; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; -import org.junit.Assert; import org.junit.Test; import com.jogamp.newt.opengl.GLWindow; @@ -69,7 +69,7 @@ public class TestGLContextSurfaceLockNEWT extends UITestCase { } public void run() { - System.err.println("Animatr "+id+": PRE: "+Thread.currentThread().getName()); + System.err.println("Animatr "+id+", count "+frameCount+": PRE: "+Thread.currentThread().getName()); for(int c=0; c Date: Wed, 25 Jul 2012 04:23:35 +0200 Subject: SWT Update: SWT GLCanvas creates lazy when resource is ready; Create new NewtCanvasSWT allowing to parent NEWT windows natively. SWT GLCanvas creates lazy when resource is ready - Ensures drawable and context are created when size > zero and native visualID is valid. The latter is platform dependent. - Note that you cannot utilize custom GLCapabilities w/ this one, since the configurations is already realized - use NewtCanvasSWT. Create new NewtCanvasSWT allowing to parent NEWT windows natively: - Similar to NewtCanvasAWT - Allows attaching / detaching NEWT windows NewtCanvasAWT: Public setNEWTChild(..) fixed Added test cases for the above - tested on Linux, OSX and Windows w/ SWT Note: As usual for OSX, add -XstartOnFirstThread Details: - NEWT Display has new method: 'EDTUtil setEDTUtil(EDTUtil)' allowing to set a custom event dispatch utility. We use this to set our SWTEDTUtil for using NEWT w/ SWT complying w/ SWT threading constraints. --- etc/profile.jogl | 5 + make/build-common.xml | 2 + make/build-newt.xml | 19 +- make/build-test.xml | 99 ++-- make/scripts/java-win64-dbg.bat | 4 +- make/scripts/tests-x64.bat | 25 +- make/scripts/tests.sh | 19 +- make/stub_includes/win32/wingdi.h | 2 + .../gluegen/opengl/BuildComposablePipeline.java | 2 +- .../classes/com/jogamp/opengl/swt/GLCanvas.java | 199 +++++--- src/jogl/classes/jogamp/opengl/GLContextImpl.java | 1 - .../com/jogamp/nativewindow/swt/SWTAccessor.java | 25 + .../media/nativewindow/NativeWindowFactory.java | 17 + .../jogamp/nativewindow/macosx/OSXUtil.java | 35 +- src/nativewindow/native/macosx/OSXmisc.m | 74 ++- src/newt/classes/com/jogamp/newt/Display.java | 17 + .../classes/com/jogamp/newt/awt/NewtCanvasAWT.java | 74 ++- .../classes/com/jogamp/newt/opengl/GLWindow.java | 1 - .../classes/com/jogamp/newt/swt/NewtCanvasSWT.java | 508 +++++++++++++++++++++ .../classes/com/jogamp/newt/swt/SWTEDTUtil.java | 99 ++++ src/newt/classes/jogamp/newt/DisplayImpl.java | 40 +- src/newt/classes/jogamp/newt/WindowImpl.java | 11 +- .../newt/awt/event/AWTParentWindowAdapter.java | 21 +- .../classes/jogamp/newt/driver/awt/AWTDisplay.java | 11 +- .../jogamp/newt/driver/macosx/MacWindow.java | 5 +- .../jogamp/newt/driver/windows/WindowsWindow.java | 11 +- src/newt/native/MacWindow.m | 56 ++- .../test/junit/jogl/swt/TestNewtCanvasSWTGLn.java | 233 ++++++++++ .../test/junit/jogl/swt/TestSWTAccessor02GLn.java | 278 ----------- .../junit/jogl/swt/TestSWTJOGLGLCanvas01GLn.java | 55 ++- .../junit/newt/parenting/TestParenting01aSWT.java | 210 +++++++++ .../junit/newt/parenting/TestParenting04AWT.java | 239 ++++++++++ .../junit/newt/parenting/TestParenting04SWT.java | 264 +++++++++++ 33 files changed, 2158 insertions(+), 503 deletions(-) create mode 100644 src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java create mode 100644 src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java create mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java delete mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/swt/TestSWTAccessor02GLn.java create mode 100644 src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting01aSWT.java create mode 100644 src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting04AWT.java create mode 100644 src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting04SWT.java (limited to 'src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java') diff --git a/etc/profile.jogl b/etc/profile.jogl index 4402adde6..7dd51919d 100755 --- a/etc/profile.jogl +++ b/etc/profile.jogl @@ -25,6 +25,9 @@ function concat_jogl_list() ARCH=`uname -m` KERNEL=`uname -s | awk ' { printf "%s",tolower($0) ; } '` +if [ "$KERNEL" = "sunos" ] ; then + KERNEL="solaris" +fi uname -a | grep -i LINUX && OSS=x11 uname -a | grep -i Darwin && OSS=osx @@ -99,6 +102,8 @@ export JOGL_CLASSPATH if [ "$KERNEL" = "linux" ] ; then SWT_SUB="gtk" +elif [ "$KERNEL" = "solaris" ] ; then + SWT_SUB="gtk" elif [ "$KERNEL" = "darwin" ] ; then SWT_SUB="cocoa" KERNEL="macosx" diff --git a/make/build-common.xml b/make/build-common.xml index 8d440a766..4de5de58b 100644 --- a/make/build-common.xml +++ b/make/build-common.xml @@ -362,6 +362,7 @@ + @@ -374,6 +375,7 @@ + diff --git a/make/build-newt.xml b/make/build-newt.xml index a6be5042e..776708857 100644 --- a/make/build-newt.xml +++ b/make/build-newt.xml @@ -107,6 +107,9 @@ + + @@ -135,11 +138,16 @@ + + + + - + @@ -707,6 +715,13 @@ + + + + + + - + diff --git a/make/build-test.xml b/make/build-test.xml index 51eadfebd..f295ae321 100644 --- a/make/build-test.xml +++ b/make/build-test.xml @@ -427,7 +427,7 @@ - - + - - @@ -491,40 +489,71 @@ - - - - - - - - - - - - - - - - + + + + - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/make/scripts/java-win64-dbg.bat b/make/scripts/java-win64-dbg.bat index 109755d2e..c8a90454d 100755 --- a/make/scripts/java-win64-dbg.bat +++ b/make/scripts/java-win64-dbg.bat @@ -18,7 +18,7 @@ set CP_ALL=.;%BLD_DIR%\jar\jogl-all.jar;%BLD_DIR%\jar\jogl-test.jar;..\..\gluege echo CP_ALL %CP_ALL% -set D_ARGS="-Djogl.debug.GLContext" "-Djogl.debug.FBObject" +REM set D_ARGS="-Djogl.debug.GLContext" "-Djogl.debug.FBObject" REM set D_ARGS="-Djogl.debug.GLDrawable" "-Djogl.debug.EGLDrawableFactory.DontQuery" REM set D_ARGS="-Djogl.debug.GLDrawable" "-Djogl.debug.EGLDrawableFactory.QueryNativeTK" REM set D_ARGS="-Djogl.debug=all" "-Dnewt.debug=all" "-Dnativewindow.debug=all" @@ -37,7 +37,7 @@ REM set D_ARGS="-Djogl.debug=all" REM set D_ARGS="-Djogl.debug.GLCanvas" "-Djogl.debug.Animator" "-Djogl.debug.GLContext" "-Djogl.debug.GLContext.TraceSwitch" "-Djogl.debug.DebugGL" "-Djogl.debug.TraceGL" REM set D_ARGS="-Djogl.debug.GLCanvas" "-Djogl.debug.Animator" "-Djogl.debug.GLContext" "-Djogl.debug.GLContext.TraceSwitch" "-Djogl.windows.useWGLVersionOf5WGLGDIFuncSet" REM set D_ARGS="-Djogl.debug.GLCanvas" "-Djogl.debug.Animator" "-Djogl.debug.GLContext" "-Djogl.debug.GLContext.TraceSwitch" -REM set D_ARGS="-Dnewt.debug.Window" +set D_ARGS="-Dnewt.debug.Window" "-Dnewt.debug.Display" REM set D_ARGS="-Djogl.debug.GLDebugMessageHandler" "-Djogl.debug.DebugGL" "-Djogl.debug.TraceGL" REM set D_ARGS="-Djogl.debug.DebugGL" "-Djogl.debug.GLDebugMessageHandler" "-Djogl.debug.GLSLCode" REM set D_ARGS="-Djogl.debug.GLContext" "-Dnewt.debug=all" diff --git a/make/scripts/tests-x64.bat b/make/scripts/tests-x64.bat index 0b3711784..6d3e46af6 100755 --- a/make/scripts/tests-x64.bat +++ b/make/scripts/tests-x64.bat @@ -28,22 +28,29 @@ REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.glsl.TestTransf REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.glsl.TestGLSLSimple01NEWT -time 2000 REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestParenting01AWT -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01bAWT %* -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01cAWT -time 50000 REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestListenerCom01AWT REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01NEWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting02NEWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01bAWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01cAWT -time 50000 +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting03AWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting03bAWT -time 100000 +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParentingFocusTraversal01AWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParentingOffscreenLayer01AWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01aSWT $* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting04AWT $* +scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting04SWT $* + +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.swt.TestSWTAccessor03AWTGLn $* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.swt.TestSWTJOGLGLCanvas01GLn $* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.swt.TestNewtCanvasSWTGLn $* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestWindows01NEWT REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestGLWindows01NEWT REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestGLWindows02NEWTAnimated -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01NEWT REM scripts\java-win64.bat com.jogamp.opengl.test.junit.newt.TestFocus01SwingAWTRobot %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestFocus02SwingAWTRobot %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.nativewindow.TestRecursiveToolkitLockCORE -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting03AWT %* -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParentingFocusTraversal01AWT %* -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParentingOffscreenLayer01AWT %* -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting03bAWT -time 100000 REM scripts\java-win64.bat com.jogamp.opengl.test.junit.newt.TestFocus02SwingAWTRobot REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestScreenMode00NEWT @@ -56,8 +63,6 @@ REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.es2.av.Mo REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.es2.TexCubeES2 %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.TestDisplayLifecycle01NEWT -REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting01NEWT -REM scripts\java-win64.bat com.jogamp.opengl.test.junit.newt.parenting.TestParenting02NEWT %* REM scripts\java-win64.bat com.jogamp.opengl.test.junit.newt.TestCloseNewtAWT REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.caps.TestMultisampleAWT -time 10000 @@ -96,7 +101,7 @@ REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestGPUMe REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.acore.TestMapBuffer01NEWT REM scripts\java-win64.bat com.jogamp.opengl.test.junit.jogl.glsl.TestRulerNEWT01 -scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBODrawableNEWT %* +REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.acore.TestFBODrawableNEWT %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.glsl.TestFBOMRTNEWT01 %* REM scripts\java-win64-dbg.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestElektronenMultipliziererNEWT %* diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 13e172770..af6218968 100755 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -61,7 +61,7 @@ function jrun() { #D_ARGS="-Djogl.debug=all -Dnativewindow.debug=all -Dnewt.debug=all" #D_ARGS="-Djogl.debug=all -Dnativewindow.debug=all -Dnewt.debug=all -Djogamp.debug.Lock" #D_ARGS="-Djogl.debug=all -Dnativewindow.debug=all" - #D_ARGS="-Djogl.debug=all -Dnewt.debug=all" + D_ARGS="-Dnewt.debug.Window" #D_ARGS="-Djogl.debug.GLDrawable" #D_ARGS="-Djogl.debug.EGLDrawableFactory.DontQuery -Djogl.debug.GLDrawable" #D_ARGS="-Djogl.debug.EGLDrawableFactory.QueryNativeTK -Djogl.debug.GLDrawable" @@ -145,7 +145,8 @@ function jrun() { echo CLASSPATH $CLASSPATH X_ARGS="-Djava.awt.headless=false $X_ARGS" else - export CLASSPATH=$JOGAMP_ALL_NOAWT_CLASSPATH + export CLASSPATH=$JOGAMP_ALL_AWT_CLASSPATH + #export CLASSPATH=$JOGAMP_ALL_NOAWT_CLASSPATH #export CLASSPATH=$JOGAMP_MOBILE_CLASSPATH #export CLASSPATH=.:$GLUEGEN_JAR:$JOGL_BUILD_DIR/jar/atomic/jogl-core.jar:$JOGL_BUILD_DIR/jar/atomic/jogl-gldesktop.jar:$JOGL_BUILD_DIR/jar/atomic/jogl-os-x11.jar:$JOGL_BUILD_DIR/jar/atomic/jogl-util.jar:$JOGL_BUILD_DIR/jar/atomic/nativewindow-core.jar:$JOGL_BUILD_DIR/jar/atomic/nativewindow-os-x11.jar:$JOGL_BUILD_DIR/jar/atomic/newt-core.jar:$JOGL_BUILD_DIR/jar/atomic/newt-driver-x11.jar:$JOGL_BUILD_DIR/jar/atomic/newt-ogl.jar:$JOGL_BUILD_DIR/jar/jogl-test.jar:$SWT_CLASSPATH:$JUNIT_JAR:$ANT_JARS X_ARGS="-Djava.awt.headless=true $X_ARGS" @@ -298,13 +299,17 @@ function testawtswt() { # #testswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTEclipseGLCanvas01GLn $* #testswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTJOGLGLCanvas01GLn $* -#testawtswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTJOGLGLCanvas01GLn $* -#testswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTAccessor02GLn $* +#testswt com.jogamp.opengl.test.junit.jogl.swt.TestNewtCanvasSWTGLn $* # -# awtswt (testawtswt) +# awtswt (testawtswt) +# Per default (unit tests) all test are performed this way +# with OSX: -XstartOnFirstThread # +#testawtswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTEclipseGLCanvas01GLn $* #testawtswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTAccessor03AWTGLn $* +#testawtswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTJOGLGLCanvas01GLn $* +#testawtswt com.jogamp.opengl.test.junit.jogl.swt.TestNewtCanvasSWTGLn $* # # newt.awt (testawt) @@ -321,6 +326,9 @@ function testawtswt() { #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting01cSwingAWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting02AWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting03AWT $* +#testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting04AWT $* +#testawtswt com.jogamp.opengl.test.junit.newt.parenting.TestParenting01aSWT $* +testawtswt com.jogamp.opengl.test.junit.newt.parenting.TestParenting04SWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParentingFocusTraversal01AWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParentingOffscreenLayer01GLCanvasAWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParentingOffscreenLayer02NewtCanvasAWT $* @@ -402,7 +410,6 @@ function testawtswt() { #testawt com.jogamp.opengl.test.junit.newt.TestFocus02SwingAWTRobot $* #linux: -testswt com.jogamp.opengl.test.junit.jogl.swt.TestSWTAccessor02GLn $* # ?? # osx: #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParentingFocusTraversal01AWT $* diff --git a/make/stub_includes/win32/wingdi.h b/make/stub_includes/win32/wingdi.h index 2d3e33275..7a88582c2 100644 --- a/make/stub_includes/win32/wingdi.h +++ b/make/stub_includes/win32/wingdi.h @@ -44,6 +44,8 @@ WINUSERAPI BOOL WINAPI DestroyWindow(HWND hWnd); WINUSERAPI DWORD WINAPI GetObjectType(HGDIOBJ h); WINUSERAPI BOOL WINAPI IsWindowVisible(HWND hWnd); WINUSERAPI BOOL WINAPI IsWindow(HWND hWnd); +WINUSERAPI HWND WINAPI GetParent(HWND hWnd); +WINUSERAPI HWND WINAPI SetParent(HWND hWndChild,HWND hWndNewParent); WINUSERAPI HANDLE WINAPI GetCurrentProcess(void); WINUSERAPI BOOL WINAPI GetProcessAffinityMask(HANDLE hProcess,PDWORD_PTR lpProcessAffinityMask,PDWORD_PTR lpSystemAffinityMask); diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java index 1d9cf3668..5334d45cf 100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java @@ -410,7 +410,7 @@ public class BuildComposablePipeline { output.flush(); output.close(); - System.out.println("wrote to file: " + file); // JAU + System.out.println("wrote to file: " + file); } /** Get the name of the object through which API calls should be routed. */ diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 64ee1c1ad..8d237162c 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -45,6 +45,7 @@ import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; import javax.media.opengl.Threading; +import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableHelper; @@ -70,8 +71,12 @@ import com.jogamp.opengl.JoglVersion; /** * Native SWT Canvas implementing GLAutoDrawable + * + *

    Note: To employ custom GLCapabilities, NewtCanvasSWT shall be used instead.

    + * */ public class GLCanvas extends Canvas implements GLAutoDrawable { + private static final boolean DEBUG = Debug.debug("GLCanvas"); /* * Flag for whether the SWT thread should be used for OpenGL calls when in single-threaded mode. This is controlled @@ -87,18 +92,20 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { /* GL Stuff */ private final RecursiveLock lock = LockFactory.createRecursiveLock(); private final GLDrawableHelper helper = new GLDrawableHelper(); + + private final GLContext shareWith; + private final GLCapabilitiesImmutable capsRequested; + private final GLCapabilitiesChooser capsChooser; + private volatile GLDrawable drawable; // volatile: avoid locking for read-only access private GLContext context; /* Native window surface */ private AbstractGraphicsDevice device; - private final long nativeWindowHandle; - private final ProxySurface proxySurface; /* Construction parameters stored for GLAutoDrawable accessor methods */ private int additionalCtxCreationFlags = 0; - private final GLCapabilitiesImmutable glCapsRequested; /* Flag indicating whether an unprocessed reshape is pending. */ private volatile boolean sendReshape; // volatile: maybe written by WindowManager thread w/o locking @@ -218,6 +225,37 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { */ private volatile Rectangle clientArea; + /** + * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser, GLContext)} + * on the SWT thread. + * + * @param parent + * Required (non-null) parent Composite. + * @param style + * Optional SWT style bit-field. The {@link SWT#NO_BACKGROUND} bit is set before passing this up to the + * Canvas constructor, so OpenGL handles the background. + * @param caps + * Optional GLCapabilities. If not provided, the default capabilities for the default GLProfile for the + * graphics device determined by the parent Composite are used. Note that the GLCapabilities that are + * actually used may differ based on the capabilities of the graphics device. + * @param chooser + * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the + * requested GLCapabilities, and the available capabilities of the graphics device. + * @param shareWith + * Optional GLContext to share state (textures, vbos, shaders, etc.) with. + * @return a new instance + */ + public static GLCanvas create(final Composite parent, final int style, final GLCapabilitiesImmutable caps, + final GLCapabilitiesChooser chooser, final GLContext shareWith) { + final GLCanvas[] res = new GLCanvas[] { null }; + parent.getDisplay().syncExec(new Runnable() { + public void run() { + res[0] = new GLCanvas( parent, style, caps, chooser, shareWith ); + } + }); + return res[0]; + } + /** * Creates a new SWT GLCanvas. * @@ -250,35 +288,26 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). * Note: SWT is owner of the native handle, hence no closing operation will be a NOP. */ device = SWTAccessor.getDevice(this); - /* Since we have no means of querying the screen index yet, assume 0. Good choice due to Xinerama alike settings anyways. */ - final int screenIdx = 0; - /* Native handle for the control, used to associate with GLContext */ - nativeWindowHandle = SWTAccessor.getWindowHandle(this); /* Select default GLCapabilities if none was provided, otherwise clone provided caps to ensure safety */ if(null == caps) { caps = new GLCapabilities(GLProfile.getDefault(device)); } - glCapsRequested = caps; - - final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(caps.getGLProfile()); - - /* Create a NativeWindow proxy for the SWT canvas */ - proxySurface = glFactory.createProxySurface(device, screenIdx, nativeWindowHandle, caps, chooser, swtCanvasUpStreamHook); - - /* Associate a GL surface with the proxy */ - drawable = glFactory.createGLDrawable(proxySurface); - drawable.setRealized(true); - - context = drawable.createContext(shareWith); - + this.capsRequested = caps; + this.capsChooser = chooser; + this.shareWith = shareWith; + + // post create .. when ready + drawable = null; + context = null; + /* Register SWT listeners (e.g. PaintListener) to render/resize GL surface. */ /* TODO: verify that these do not need to be manually de-registered when destroying the SWT component */ addPaintListener(new PaintListener() { @Override public void paintControl(final PaintEvent arg0) { - if (!helper.isExternalAnimatorAnimating()) { - display(); + if ( !helper.isExternalAnimatorAnimating() ) { + display(); // checks: null != drawable } } }); @@ -291,39 +320,93 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { }); } private final ProxySurface.UpstreamSurfaceHook swtCanvasUpStreamHook = new ProxySurface.UpstreamSurfaceHook() { - @Override - public final void create(ProxySurface s) { /* nop */ } - - @Override - public final void destroy(ProxySurface s) { /* nop */ } - - @Override - public final int getWidth(ProxySurface s) { - return clientArea.width; - } - - @Override - public final int getHeight(ProxySurface s) { - return clientArea.height; - } - - @Override - public String toString() { - return "SETUpstreamSurfaceHook[upstream: "+GLCanvas.this.toString()+"]"; - } - + @Override + public final void create(ProxySurface s) { /* nop */ } + + @Override + public final void destroy(ProxySurface s) { /* nop */ } + + @Override + public final int getWidth(ProxySurface s) { + return clientArea.width; + } + + @Override + public final int getHeight(ProxySurface s) { + return clientArea.height; + } + + @Override + public String toString() { + return "SWTCanvasUpstreamSurfaceHook[upstream: "+GLCanvas.this.toString()+", "+clientArea.width+"x"+clientArea.height+"]"; + } }; protected final void updateSizeCheck() { - clientArea = GLCanvas.this.getClientArea(); - if (clientArea != null && - proxySurface.getWidth() != clientArea.width && - proxySurface.getHeight() != clientArea.height) { + final Rectangle oClientArea = clientArea; + final Rectangle nClientArea = GLCanvas.this.getClientArea(); + if ( nClientArea != null && + ( nClientArea.width != oClientArea.width || nClientArea.height != oClientArea.height ) + ) { + clientArea = nClientArea; // write back new value sendReshape = true; // Mark for OpenGL reshape next time the control is painted } - sendReshape = false; + } + + @Override + public void display() { + if( null != drawable || validateDrawableAndContext() ) { + runInGLThread(makeCurrentAndDisplayOnEDTAction); + } } + + /** assumes drawable == null ! */ + protected final boolean validateDrawableAndContext() { + if( GLCanvas.this.isDisposed() ) { + return false; + } + final Rectangle nClientArea = clientArea; + if(0 == nClientArea.width * nClientArea.height) { + return false; + } + + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(capsRequested.getGLProfile()); + + /* Native handle for the control, used to associate with GLContext */ + final long nativeWindowHandle = SWTAccessor.getWindowHandle(this); + + /* Create a NativeWindow proxy for the SWT canvas */ + ProxySurface proxySurface = null; + try { + proxySurface = glFactory.createProxySurface(device, 0 /* screenIdx */, nativeWindowHandle, + capsRequested, capsChooser, swtCanvasUpStreamHook); + } catch (GLException gle) { + // not ready yet .. + if(DEBUG) { System.err.println(gle.getMessage()); } + } + + if(null != proxySurface) { + /* Associate a GL surface with the proxy */ + drawable = glFactory.createGLDrawable(proxySurface); + drawable.setRealized(true); + + context = drawable.createContext(shareWith); + } + } finally { + _lock.unlock(); + } + final boolean res = null != drawable; + if(DEBUG && res) { + System.err.println("SWT GLCanvas realized! "+this+", "+drawable); + Thread.dumpStack(); + } + return res; + } + @Override public final Object getUpstreamWidget() { return this; @@ -362,11 +445,6 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { dispose(); } - @Override - public void display() { - runInGLThread(makeCurrentAndDisplayOnEDTAction); - } - @Override public GLAnimatorControl getAnimator() { return helper.getAnimator(); @@ -379,7 +457,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GLContext getContext() { - return context; + return null != drawable ? context : null; } @Override @@ -472,7 +550,8 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { - return (GLCapabilitiesImmutable)proxySurface.getGraphicsConfiguration().getChosenCapabilities(); + final GLDrawable _drawable = drawable; + return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getChosenGLCapabilities() : null; } /** @@ -481,7 +560,8 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * @return Non-null GLCapabilities. */ public GLCapabilitiesImmutable getRequestedGLCapabilities() { - return (GLCapabilitiesImmutable)proxySurface.getGraphicsConfiguration().getRequestedCapabilities(); + final GLDrawable _drawable = drawable; + return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities() : null; } @Override @@ -492,7 +572,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GLProfile getGLProfile() { - return glCapsRequested.getGLProfile(); + return capsRequested.getGLProfile(); } @Override @@ -523,10 +603,9 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { runInGLThread(makeCurrentAndSwapBuffersOnEDTAction); } - // FIXME: API of update() method ? @Override public void update() { - // FIXME: display(); ? + // don't paint background etc .. nop avoids flickering } @Override @@ -551,7 +630,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * @see Platform#AWT_AVAILABLE * @see Platform#getOSType() */ - private void runInGLThread(final Runnable action) { + private static void runInGLThread(final Runnable action) { if(Platform.OSType.MACOS == Platform.OS_TYPE) { SWTAccessor.invoke(true, action); } else { diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index e730bc62e..bf6a0ee6e 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -338,7 +338,6 @@ public abstract class GLContextImpl extends GLContext { if (TRACE_SWITCH) { System.err.println(getThreadName() + ": GLContextImpl.destroy.X: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); - Thread.dumpStack(); // JAU } } } finally { diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java index 0494bb408..ba07d97dc 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java @@ -34,16 +34,21 @@ import java.lang.reflect.Method; import org.eclipse.swt.graphics.GCData; 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; +import javax.media.nativewindow.VisualIDHolder; import com.jogamp.common.util.ReflectionUtil; 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; public class SWTAccessor { static final Field swt_control_handle; @@ -213,6 +218,26 @@ public class SWTAccessor { } throw new UnsupportedOperationException("n/a for this windowing system: "+NativeWindowFactory.getNativeWindowType(false)); } + public static AbstractGraphicsScreen getScreen(AbstractGraphicsDevice device, int screen) { + if( null != OS_gtk_class ) { + return new X11GraphicsScreen((X11GraphicsDevice)device, screen); + } + if( NativeWindowFactory.TYPE_WINDOWS == NativeWindowFactory.getNativeWindowType(false) || + NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false) ) { + return new DefaultGraphicsScreen(device, screen); + } + throw new UnsupportedOperationException("n/a for this windowing system: "+NativeWindowFactory.getNativeWindowType(false)); + } + public static int getNativeVisualID(AbstractGraphicsDevice device, long windowHandle) { + if( null != OS_gtk_class ) { + return X11Lib.GetVisualIDFromWindow(device.getHandle(), windowHandle); + } + if( NativeWindowFactory.TYPE_WINDOWS == NativeWindowFactory.getNativeWindowType(false) || + NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false) ) { + return VisualIDHolder.VID_UNDEFINED; + } + throw new UnsupportedOperationException("n/a for this windowing system: "+NativeWindowFactory.getNativeWindowType(false)); + } public static long getWindowHandle(Control swtControl) { long handle = getHandle(swtControl); diff --git a/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java b/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java index b85ad47f2..6faa9890c 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java +++ b/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java @@ -561,4 +561,21 @@ public abstract class NativeWindowFactory { } return null; } + + /** + * Returns true if the given visualID is valid for further processing, i.e. OpenGL usage, + * otherwise return false. + *

    + * On certain platforms, i.e. X11, a valid visualID is required at window creation. + * Other platforms may determine it later on, e.g. OSX and Windows.

    + *

    + * If the visualID is {@link VisualIDHolder#VID_UNDEFINED} and the platform requires it + * at creation time (see above), it is not valid for further processing. + *

    + */ + public static boolean isNativeVisualIDValidForProcessing(int visualID) { + return NativeWindowFactory.TYPE_X11 != NativeWindowFactory.getNativeWindowType(false) || + VisualIDHolder.VID_UNDEFINED != visualID ; + } + } diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index 4767dff18..894084fce 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -28,6 +28,7 @@ package jogamp.nativewindow.macosx; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; import jogamp.nativewindow.Debug; @@ -63,8 +64,37 @@ public class OSXUtil { return isNSView0(object); } - public static Point GetLocationOnScreen(long windowOrView, int src_x, int src_y) { - return (Point) GetLocationOnScreen0(windowOrView, src_x, src_y); + /** + * In case the windowOrView is top-level, + * you shall set topLevel to true where + * insets gets into account to compute the client position as follows: + *
    +      if(topLevel) {
    +          // top-level position -> client window position
    +          final Insets insets = GetInsets(windowOrView);
    +          los.setX(los.getX() + insets.getLeftWidth());
    +          los.setY(los.getY() + insets.getTopHeight());
    +      }
    +     * 
    + * @param windowOrView + * @param topLevel + * @param src_x + * @param src_y + * @return the client position + */ + public static Point GetLocationOnScreen(long windowOrView, boolean topLevel, int src_x, int src_y) { + final Point los = (Point) GetLocationOnScreen0(windowOrView, src_x, src_y); + if(topLevel) { + // top-level position -> client window position + final Insets insets = GetInsets(windowOrView); + los.setX(los.getX() + insets.getLeftWidth()); + los.setY(los.getY() + insets.getTopHeight()); + } + return los; + } + + public static Insets GetInsets(long windowOrView) { + return (Insets) GetInsets0(windowOrView); } public static long CreateNSWindow(int x, int y, int width, int height) { @@ -135,6 +165,7 @@ public class OSXUtil { private static native boolean initIDs0(); private static native boolean isNSView0(long object); private static native Object GetLocationOnScreen0(long windowOrView, int src_x, int src_y); + private static native Object GetInsets0(long windowOrView); private static native long CreateNSWindow0(int x, int y, int width, int height); private static native void DestroyNSWindow0(long nsWindow); private static native long GetNSView0(long nsWindow); diff --git a/src/nativewindow/native/macosx/OSXmisc.m b/src/nativewindow/native/macosx/OSXmisc.m index 37fa4c88f..2c853a43d 100644 --- a/src/nativewindow/native/macosx/OSXmisc.m +++ b/src/nativewindow/native/macosx/OSXmisc.m @@ -52,12 +52,18 @@ static const char * const ClazzNameRunnable = "java/lang/Runnable"; static jmethodID runnableRunID = NULL; -static const char * const ClazzNamePoint = "javax/media/nativewindow/util/Point"; static const char * const ClazzAnyCstrName = ""; + +static const char * const ClazzNamePoint = "javax/media/nativewindow/util/Point"; static const char * const ClazzNamePointCstrSignature = "(II)V"; static jclass pointClz = NULL; static jmethodID pointCstr = NULL; +static const char * const ClazzNameInsets = "javax/media/nativewindow/util/Insets"; +static const char * const ClazzNameInsetsCstrSignature = "(IIII)V"; +static jclass insetsClz = NULL; +static jmethodID insetsCstr = NULL; + static int _initialized=0; JNIEXPORT jboolean JNICALL @@ -79,6 +85,21 @@ Java_jogamp_nativewindow_macosx_OSXUtil_initIDs0(JNIEnv *env, jclass _unused) { ClazzNamePoint, ClazzAnyCstrName, ClazzNamePointCstrSignature); } + c = (*env)->FindClass(env, ClazzNameInsets); + if(NULL==c) { + NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't find %s", ClazzNameInsets); + } + insetsClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==insetsClz) { + NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't use %s", ClazzNameInsets); + } + insetsCstr = (*env)->GetMethodID(env, insetsClz, ClazzAnyCstrName, ClazzNameInsetsCstrSignature); + if(NULL==insetsCstr) { + NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't fetch %s.%s %s", + ClazzNameInsets, ClazzAnyCstrName, ClazzNameInsetsCstrSignature); + } + c = (*env)->FindClass(env, ClazzNameRunnable); if(NULL==c) { NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't find %s", ClazzNameRunnable); @@ -154,6 +175,49 @@ JNIEXPORT jobject JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetLocationOnS return res; } +/* + * Class: Java_jogamp_nativewindow_macosx_OSXUtil + * Method: getInsets0 + * Signature: (J)Ljavax/media/nativewindow/util/Insets; + */ +JNIEXPORT jobject JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetInsets0 + (JNIEnv *env, jclass unused, jlong winOrView) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSObject *nsObj = (NSObject*) (intptr_t) winOrView; + NSWindow* win = NULL; + NSView* view = NULL; + jint il,ir,it,ib; + + if( [nsObj isKindOfClass:[NSWindow class]] ) { + win = (NSWindow*) nsObj; + view = [win contentView]; + } else if( nsObj != NULL && [nsObj isKindOfClass:[NSView class]] ) { + view = (NSView*) nsObj; + win = [view window]; + } else { + NativewindowCommon_throwNewRuntimeException(env, "neither win not view %p\n", nsObj); + } + + NSRect frameRect = [win frame]; + NSRect contentRect = [win contentRectForFrameRect: frameRect]; + + // note: this is a simplistic implementation which doesn't take + // into account DPI and scaling factor + CGFloat l = contentRect.origin.x - frameRect.origin.x; + il = (jint)l; // l + ir = (jint)(frameRect.size.width - (contentRect.size.width + l)); // r + it = (jint)(frameRect.size.height - contentRect.size.height); // t + ib = (jint)(contentRect.origin.y - frameRect.origin.y); // b + + jobject res = (*env)->NewObject(env, insetsClz, insetsCstr, il, ir, it, ib); + + [pool release]; + + return res; +} + /* * Class: Java_jogamp_nativewindow_macosx_OSXUtil * Method: CreateNSWindow0 @@ -172,6 +236,12 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateNSWindow0 defer: YES]; [myWindow setReleasedWhenClosed: YES]; // default [myWindow setPreservesContentDuringLiveResize: YES]; + // Remove animations +NS_DURING + // Available >= 10.7 - Removes default animations + [myWindow setAnimationBehavior: NSWindowAnimationBehaviorNone]; +NS_HANDLER +NS_ENDHANDLER // invisible .. [myWindow setOpaque: NO]; @@ -229,7 +299,7 @@ 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, x, y, width, height, (int)[layer retainCount]); + 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]); // avoid zero size if(0 == width) { width = 32; } if(0 == height) { height = 32; } diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java index 7b6849a30..1e9a0e9eb 100644 --- a/src/newt/classes/com/jogamp/newt/Display.java +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -141,6 +141,23 @@ public abstract class Display { */ public abstract String getType(); + /** + * Sets a new {@link EDTUtil} and returns the previous one. + *

    + * If newEDTUtil is null, + * the device's default EDTUtil is created and used. + *

    + *

    + * If a previous one exists and it differs from the new one, + * it's being stopped, wait-until-idle and reset to allow restart. + *

    + *

    + * If newEDTUtil is not null and equals the previous one, + * null is returned and no change is being made. + *

    + */ + public abstract EDTUtil setEDTUtil(EDTUtil newEDTUtil); + public abstract EDTUtil getEDTUtil(); public abstract boolean isEDTRunning(); diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index cd0e9aab6..6f0028a77 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -263,17 +263,35 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto private final FocusPropertyChangeListener focusPropertyChangeListener = new FocusPropertyChangeListener(); private volatile KeyboardFocusManager keyboardFocusManager = null; - /** sets a new NEWT child, provoking reparenting. */ - private NewtCanvasAWT setNEWTChild(Window child) { - if(newtChild!=child) { - newtChild = child; - if(isDisplayable()) { - // reparent right away, addNotify has been called already - final java.awt.Container cont = AWTMisc.getContainer(this); - reparentWindow( (null!=child) ? true : false, cont ); - } + /** + * Sets a new NEWT child, provoking reparenting. + *

    + * A previously detached newChild will be released to top-level status + * and made invisible. + *

    + *

    + * Note: When switching NEWT child's, detaching the previous first via setNEWTChild(null) + * produced much cleaner visual results. + *

    + * @return the previous attached newt child. + */ + public Window setNEWTChild(Window newChild) { + final Window prevChild = newtChild; + if(DEBUG) { + System.err.println("NewtCanvasAWT.setNEWTChild.0: win "+newtWinHandleToHexString(prevChild)+" -> "+newtWinHandleToHexString(newChild)); + } + final java.awt.Container cont = AWTMisc.getContainer(this); + // remove old one + if(null != newtChild) { + reparentWindow( false, cont ); + newtChild = null; } - return this; + // add new one, reparent only if ready + newtChild = newChild; + if( isDisplayable() && null != newChild) { + reparentWindow( true, cont ); + } + return prevChild; } /** @return the current NEWT child */ @@ -306,13 +324,13 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto awtKeyAdapter.removeFrom(this); awtKeyAdapter = null; } - newtChild.setKeyboardFocusHandler(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"); @@ -360,7 +378,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto // if ( isShowing() == true ) -> Container is already visible. System.err.println("NewtCanvasAWT.addNotify: "+newtChild+", "+this+", visible "+isVisible()+", showing "+isShowing()+ ", displayable "+isDisplayable()+" -> "+cont); - } + } reparentWindow(true, cont); } @@ -375,6 +393,12 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto ols.detachSurfaceLayer(); } reparentWindow(false, cont); + + if(null != jawtWindow) { + NewtFactoryAWT.destroyNativeWindow(jawtWindow); + jawtWindow=null; + } + super.removeNotify(); } @@ -382,14 +406,19 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto 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) { - jawtWindow = NewtFactoryAWT.getNativeWindow(this, newtChild.getRequestedCapabilities()); - jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); 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()) { @@ -413,7 +442,6 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto newtChild.setVisible(true); configureNewtChild(true); newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener - newtChild.windowRepaint(0, 0, w, h); // force this AWT Canvas to be focus-able, // since this it is completely covered by the newtChild (z-order). @@ -422,10 +450,9 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto configureNewtChild(false); newtChild.setVisible(false); newtChild.reparentWindow(null); - if(null != jawtWindow) { - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - jawtWindow=null; - } + } + if(DEBUG) { + System.err.println("NewtCanvasAWT.reparentWindow.X: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); } } @@ -451,7 +478,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto NewtFactoryAWT.destroyNativeWindow(jawtWindow); jawtWindow=null; } - newtChild.setVisible(false); + newtChild.setVisible(false); newtChild.reparentWindow(null); newtChild.destroy(); newtChild=null; @@ -569,5 +596,12 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } } + + static String newtWinHandleToHexString(Window w) { + return null != w ? toHexString(w.getWindowHandle()) : "nil"; + } + static String toHexString(long l) { + return "0x"+Long.toHexString(l); + } } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index 2205aec8e..d662a743a 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -480,7 +480,6 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } if(Window.DEBUG_IMPLEMENTATION) { System.err.println("GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+WindowImpl.getThreadName()+", fin: dt "+ (System.nanoTime()-t0)/1e6 +"ms"); - Thread.dumpStack(); // JAU } } diff --git a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java new file mode 100644 index 000000000..f45b864fa --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java @@ -0,0 +1,508 @@ +/** + * Copyright 2012 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.newt.swt; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.Capabilities; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.SurfaceUpdatedListener; +import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.nativewindow.util.Point; + +import jogamp.nativewindow.macosx.OSXUtil; +import jogamp.newt.Debug; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; + +import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.newt.Display; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.util.EDTUtil; + +public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { + private static final boolean DEBUG = Debug.debug("Window"); + private static final boolean isOSX = NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false); + + /** SWT EDTUtil associated w/ parent's SWT Display */ + private final EDTUtil swtEDTUtil; + + private final AbstractGraphicsScreen screen; + + private WindowClosingMode newtChildCloseOp = WindowClosingMode.DISPOSE_ON_CLOSE; + private volatile Rectangle clientArea; + + private volatile SWTNativeWindow nativeWindow; + private volatile Window newtChild = null; + + /** + * Creates an instance using {@link #NewtCanvasSWT(Composite, int, Window)} + * on the SWT thread. + * + *

    + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an SWT conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + *

    + * + * @param parent the SWT composite + * @param style additional styles to SWT#NO_BACKGROUND + * @param child optional preassigned {@link #Window}, maybe null + * @return a new instance + */ + public static NewtCanvasSWT create(final Composite parent, final int style, final Window child) { + final NewtCanvasSWT[] res = new NewtCanvasSWT[] { null }; + parent.getDisplay().syncExec( new Runnable() { + public void run() { + res[0] = new NewtCanvasSWT( parent, style, child); + } + }); + return res[0]; + } + + /** + * Instantiates a NewtCanvas with a NEWT child. + * + *

    + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an SWT conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + *

    + * + * @param parent the SWT composite + * @param style additional styles to SWT#NO_BACKGROUND + * @param child optional preassigned {@link #Window}, maybe null + */ + public NewtCanvasSWT(final Composite parent, final int style, Window child) { + super(parent, style | SWT.NO_BACKGROUND); + + swtEDTUtil = new SWTEDTUtil(parent.getDisplay()); + + SWTAccessor.setRealized(this, true); + + clientArea = getClientArea(); + + final AbstractGraphicsDevice device = SWTAccessor.getDevice(this); + screen = SWTAccessor.getScreen(device, 0); + nativeWindow = null; + + if(null != child) { + setNEWTChild(child); + } + + /* Register SWT listeners (e.g. PaintListener) to render/resize GL surface. */ + /* TODO: verify that these do not need to be manually de-registered when destroying the SWT component */ + addPaintListener(new PaintListener() { + @Override + public void paintControl(final PaintEvent arg0) { + if( null != nativeWindow || validateNative() ) { + if( null !=newtChild ) { + newtChild.windowRepaint(0, 0, clientArea.width, clientArea.height); + } + } + } + }); + + addControlListener(new ControlAdapter() { + @Override + public void controlResized(final ControlEvent arg0) { + updateSizeCheck(); + } + }); + } + + /** assumes nativeWindow == null ! */ + protected final boolean validateNative() { + if( isDisposed() ) { + return false; + } + updateSizeCheck(); + final Rectangle nClientArea = clientArea; + if(0 == nClientArea.width * nClientArea.height) { + return false; + } + + /* Native handle for the control, used to associate with GLContext */ + final long nativeWindowHandle = SWTAccessor.getWindowHandle(this); + final int visualID = SWTAccessor.getNativeVisualID(screen.getDevice(), nativeWindowHandle); + final boolean visualIDValid = NativeWindowFactory.isNativeVisualIDValidForProcessing(visualID); + if(DEBUG) { + System.err.println("NewtCanvasSWT.validateNative() windowHandle 0x"+Long.toHexString(nativeWindowHandle)+", visualID 0x"+Integer.toHexString(visualID)+", valid "+visualIDValid); + } + if( visualIDValid ) { + /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). + * Note: SWT is owner of the native handle, hence no closing operation will be a NOP. */ + final CapabilitiesImmutable caps = new Capabilities(); + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(screen.getDevice(), caps); + final AbstractGraphicsConfiguration config = factory.chooseGraphicsConfiguration( caps, caps, null, screen, visualID ); + if(DEBUG) { + System.err.println("NewtCanvasSWT.validateNative() factory: "+factory+", windowHandle 0x"+Long.toHexString(nativeWindowHandle)+", visualID 0x"+Integer.toHexString(visualID)+", chosen config: "+config); + // Thread.dumpStack(); + } + if (null == config) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + + nativeWindow = new SWTNativeWindow(config, nativeWindowHandle); + reparentWindow( true ); + } + + return null != nativeWindow; + } + + protected final void updateSizeCheck() { + final Rectangle oClientArea = clientArea; + final Rectangle nClientArea = getClientArea(); + if ( nClientArea != null && + ( nClientArea.width != oClientArea.width || nClientArea.height != oClientArea.height ) + ) { + clientArea = nClientArea; // write back new value + if( null != newtChild ) { + newtChild.setSize(clientArea.width, clientArea.height); + } + } + } + + @Override + public void update() { + // don't paint background etc .. nop avoids flickering + } + + /** + * Destroys this resource: + *
      + *
    • Make the NEWT Child invisible
    • + *
    • Disconnects the NEWT Child from this Canvas NativeWindow, reparent to NULL
    • + *
    • Issues destroy() on the NEWT Child
    • + *
    • Remove reference to the NEWT Child
    • + *
    + * @see Window#destroy() + */ + @Override + public void dispose() { + if( null != newtChild ) { + if(DEBUG) { + System.err.println("NewtCanvasSWT.dispose.0: EDTUtil cur "+newtChild.getScreen().getDisplay().getEDTUtil()+ + ",\n\t"+newtChild); + } + configureNewtChild(false); + newtChild.setVisible(false); + newtChild.reparentWindow(null); + newtChild.destroy(); + newtChild = null; + } + nativeWindow = null; + super.dispose(); + } + + /** @return this SWT Canvas NativeWindow representation, may be null in case it has not been realized. */ + public NativeWindow getNativeWindow() { return nativeWindow; } + + public WindowClosingMode getDefaultCloseOperation() { + return newtChildCloseOp; // FIXME + } + + public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { + return newtChildCloseOp = op; // FIXME + } + + + boolean isParent() { + return null!=newtChild ; + } + + boolean isFullscreen() { + return null != newtChild && newtChild.isFullscreen(); + } + + /** + * Sets a new NEWT child, provoking reparenting. + *

    + * A previously detached newChild will be released to top-level status + * and made invisible. + *

    + *

    + * Note: When switching NEWT child's, detaching the previous first via setNEWTChild(null) + * produced much cleaner visual results. + *

    + *

    + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an SWT conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + *

    + * @return the previous attached newt child. + */ + public Window setNEWTChild(final Window newChild) { + final Window prevChild = newtChild; + if(DEBUG) { + System.err.println("NewtCanvasSWT.setNEWTChild.0: win "+newtWinHandleToHexString(prevChild)+" -> "+newtWinHandleToHexString(newChild)); + } + // remove old one + if(null != newtChild) { + reparentWindow( false ); + newtChild = null; + } + // add new one, reparent only if ready + newtChild = newChild; + if(null != nativeWindow && null != newChild) { + reparentWindow( true ); + } + return prevChild; + } + + /** @return the current NEWT child */ + public Window getNEWTChild() { + return newtChild; + } + + @Override + public boolean setParent(Composite parent) { + return super.setParent(parent); + } + + /* package */ void configureNewtChild(boolean attach) { + + if( null != newtChild ) { + newtChild.setKeyboardFocusHandler(null); + if(attach) { + newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); + } else { + newtChild.setFocusAction(null); + newtChild.setDefaultCloseOperation(newtChildCloseOp); + } + } + } + + void reparentWindow(boolean add) { + if( null == newtChild ) { + return; // nop + } + if(DEBUG) { + System.err.println("NewtCanvasSWT.reparentWindow.0: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); + } + + newtChild.setFocusAction(null); // no AWT focus traversal .. + if(add) { + updateSizeCheck(); + final int w = clientArea.width; + final int h = clientArea.height; + + newtChild.getScreen().getDisplay().setEDTUtil(swtEDTUtil); + + newtChild.setSize(w, h); + newtChild.reparentWindow(nativeWindow); + newtChild.setVisible(true); + configureNewtChild(true); + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener + + // force this SWT Canvas to be focus-able, + // since this it is completely covered by the newtChild (z-order). + setEnabled(true); + } else { + configureNewtChild(false); + newtChild.setVisible(false); + newtChild.reparentWindow(null); + } + if(DEBUG) { + System.err.println("NewtCanvasSWT.reparentWindow.X: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); + } + } + + private final void requestFocusNEWTChild() { + if( null != newtChild ) { + newtChild.setFocusAction(null); + newtChild.requestFocus(); + } + } + + @Override + public boolean forceFocus() { + final boolean res = NewtCanvasSWT.super.forceFocus(); + requestFocusNEWTChild(); + return res; + } + + private class SWTNativeWindow implements NativeWindow { + private final AbstractGraphicsConfiguration config; + private final long nativeWindowHandle; + private final InsetsImmutable insets; // only required to allow proper client position calculation on OSX + + public SWTNativeWindow(AbstractGraphicsConfiguration config, long nativeWindowHandle) { + this.config = config; + this.nativeWindowHandle = nativeWindowHandle; + if(isOSX) { + this.insets = OSXUtil.GetInsets(nativeWindowHandle); + } else { + this.insets = new Insets(0, 0, 0, 0); + } + } + + @Override + public int lockSurface() throws NativeWindowException, RuntimeException { + return NativeSurface.LOCK_SUCCESS; + } + + @Override + public void unlockSurface() { } + + @Override + public boolean isSurfaceLockedByOtherThread() { + return false; + } + + @Override + public Thread getSurfaceLockOwner() { + return null; + } + + @Override + public boolean surfaceSwap() { + return false; + } + + @Override + public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { } + + @Override + public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + } + + @Override + public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { } + + @Override + public long getSurfaceHandle() { + return 0; + } + + @Override + public int getWidth() { + return clientArea.width; + } + + @Override + public int getHeight() { + return clientArea.height; + } + + @Override + public AbstractGraphicsConfiguration getGraphicsConfiguration() { + return config; + } + + @Override + public long getDisplayHandle() { + return config.getScreen().getDevice().getHandle(); + } + + @Override + public int getScreenIndex() { + return config.getScreen().getIndex(); + } + + @Override + public void surfaceUpdated(Object updater, NativeSurface ns, long when) { } + + @Override + public void destroy() { } + + @Override + public NativeWindow getParent() { + return null; + } + + @Override + public long getWindowHandle() { + return nativeWindowHandle; + } + + @Override + public InsetsImmutable getInsets() { + return insets; + } + + @Override + public int getX() { + return 0; + } + + @Override + public int getY() { + return 0; + } + + @Override + public Point getLocationOnScreen(Point point) { + if( isOSX ) { + final Point los = OSXUtil.GetLocationOnScreen(nativeWindowHandle, false, 0, 0); + // top-level position -> client window position + los.setX(los.getX() + insets.getLeftWidth()); + los.setY(los.getY() + insets.getTopHeight()); + if(null!=point) { + return point.translate(los); + } else { + return los; + } + } else { + // client position on 'normal' windowing systems is 0/0 + if(null == point) { + point = new Point(0, 0); + } + return point; + } + } + + @Override + public boolean hasFocus() { + return isFocusControl(); + } + }; + + static String newtWinHandleToHexString(Window w) { + return null != w ? toHexString(w.getWindowHandle()) : "nil"; + } + static String toHexString(long l) { + return "0x"+Long.toHexString(l); + } +} + diff --git a/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java b/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java new file mode 100644 index 000000000..3538caad2 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java @@ -0,0 +1,99 @@ +/** + * Copyright 2012 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.newt.swt; + +import org.eclipse.swt.widgets.Display; + +import com.jogamp.newt.util.EDTUtil; + +/** + * Simple {@link EDTUtil} implementation utilizing the SWT UI thread + * of the given {@link Display}. + */ +public class SWTEDTUtil implements EDTUtil { + private final Display swtDisplay; + + public SWTEDTUtil(Display swtDisplay) { + this.swtDisplay = swtDisplay; + } + + public final Display getDisplay() { + return swtDisplay; + } + + @Override + public long getPollPeriod() { + return 0; + } + + @Override + public void setPollPeriod(long ms) { + } + + @Override + public void reset() { + } + + @Override + public void start() { + } + + @Override + public boolean isCurrentThreadEDT() { + return swtDisplay.getThread() == Thread.currentThread(); + } + + @Override + public boolean isRunning() { + return true; + } + + @Override + public void invokeStop(Runnable finalTask) { + swtDisplay.syncExec(finalTask); + } + + @Override + public void invoke(boolean wait, Runnable task) { + if(wait) { + swtDisplay.syncExec(task); + } else { + swtDisplay.asyncExec(task); + } + } + + @Override + public void waitUntilIdle() { + // all sync .. + } + + @Override + public void waitUntilStopped() { + // all sync .. + } +} diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index b10561c5b..b178bb5d3 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -101,7 +101,9 @@ public abstract class DisplayImpl extends Display { display.hashCode = display.fqname.hashCode(); displayList.add(display); } - display.createEDTUtil(); + if(null == display.edtUtil) { + display.setEDTUtil(null); // device's default if EDT is used, or null + } if(DEBUG) { System.err.println("Display.create() NEW: "+display+" "+getThreadName()); } @@ -168,15 +170,45 @@ public abstract class DisplayImpl extends Display { return true; } - protected void createEDTUtil() { + protected EDTUtil createEDTUtil() { + final EDTUtil def; if(NewtFactory.useEDT()) { - edtUtil = new DefaultEDTUtil(Thread.currentThread().getThreadGroup(), "Display-"+getFQName(), dispatchMessagesRunnable); + def = new DefaultEDTUtil(Thread.currentThread().getThreadGroup(), "Display-"+getFQName(), dispatchMessagesRunnable); if(DEBUG) { - System.err.println("Display.createNative("+getFQName()+") Create EDTUtil: "+edtUtil.getClass().getName()); + System.err.println("Display.createNative("+getFQName()+") Create EDTUtil: "+def.getClass().getName()); } + } else { + def = null; } + return def; } + @Override + public EDTUtil setEDTUtil(EDTUtil newEDTUtil) { + if(null == newEDTUtil) { + newEDTUtil = createEDTUtil(); + } + if( newEDTUtil == edtUtil ) { + if(DEBUG) { + System.err.println("Display.setEDTUtil: "+newEDTUtil+" - keep!"); + } + return null; // no change + } + final EDTUtil oldEDTUtil = edtUtil; + if(DEBUG) { + System.err.println("Display.setEDTUtil: "+oldEDTUtil+" -> "+newEDTUtil); + } + if(null != oldEDTUtil) { + stopEDT( new Runnable() { public void run() {} } ); + // ready for restart .. + oldEDTUtil.waitUntilStopped(); + oldEDTUtil.reset(); + } + edtUtil = newEDTUtil; + return oldEDTUtil; + } + + @Override public final EDTUtil getEDTUtil() { return edtUtil; } diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index b12e42680..606101ade 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -445,9 +445,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected abstract void closeNativeImpl(); /** - * The native implementation must invoke {@link #focusChanged(boolean, boolean)} - * to change the focus state, if force == false. - * This may happen asynchronous within {@link #TIMEOUT_NATIVEWINDOW}. + * Async request which shall be performed within {@link #TIMEOUT_NATIVEWINDOW}. + *

    + * If if force == false the native implementation + * may only request focus if not yet owner.

    + *

    + * {@link #focusChanged(boolean, boolean)} should be called + * to notify about the focus traversal. + *

    * * @param force if true, bypass {@link #focusChanged(boolean, boolean)} and force focus request */ diff --git a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java index ce8ed7c49..747e2368e 100644 --- a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java +++ b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java @@ -34,6 +34,7 @@ import javax.media.nativewindow.NativeWindow; import jogamp.newt.driver.DriverUpdatePosition; +import com.jogamp.newt.Window; import com.jogamp.newt.event.awt.AWTAdapter; import com.jogamp.newt.event.awt.AWTWindowAdapter; @@ -92,19 +93,20 @@ public class AWTParentWindowAdapter if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentResized: "+comp); } - getNewtWindow().runOnEDTIfAvail(false, new Runnable() { + final Window newtWindow = getNewtWindow(); + newtWindow.runOnEDTIfAvail(false, new Runnable() { public void run() { int cw = comp.getWidth(); int ch = comp.getHeight(); if( 0 < cw * ch ) { - if( getNewtWindow().getWidth() != cw || getNewtWindow().getHeight() != ch ) { - getNewtWindow().setSize(cw, ch); - if(comp.isVisible() != getNewtWindow().isVisible()) { - getNewtWindow().setVisible(comp.isVisible()); + if( newtWindow.getWidth() != cw || newtWindow.getHeight() != ch ) { + newtWindow.setSize(cw, ch); + if(comp.isVisible() != newtWindow.isVisible()) { + newtWindow.setVisible(comp.isVisible()); } } - } else if(getNewtWindow().isVisible()) { - getNewtWindow().setVisible(false); + } else if(newtWindow.isVisible()) { + newtWindow.setVisible(false); } }}); } @@ -113,8 +115,9 @@ public class AWTParentWindowAdapter if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentMoved: "+e); } - if(getNewtWindow().getDelegatedWindow() instanceof DriverUpdatePosition) { - ((DriverUpdatePosition)getNewtWindow().getDelegatedWindow()).updatePosition(); + final Window newtWindow = getNewtWindow(); + if(newtWindow.getDelegatedWindow() instanceof DriverUpdatePosition) { + ((DriverUpdatePosition)newtWindow.getDelegatedWindow()).updatePosition(); } } diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTDisplay.java b/src/newt/classes/jogamp/newt/driver/awt/AWTDisplay.java index 166da5c1c..65f8b4715 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTDisplay.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTDisplay.java @@ -36,6 +36,7 @@ package jogamp.newt.driver.awt; import com.jogamp.nativewindow.awt.AWTGraphicsDevice; import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.util.EDTUtil; import jogamp.newt.DisplayImpl; @@ -54,13 +55,17 @@ public class AWTDisplay extends DisplayImpl { protected void closeNativeImpl() { } @Override - protected void createEDTUtil() { + protected EDTUtil createEDTUtil() { + final EDTUtil def; if(NewtFactory.useEDT()) { - edtUtil = AWTEDTUtil.getSingleton(); + def = AWTEDTUtil.getSingleton(); if(DEBUG) { - System.err.println("AWTDisplay.createNative("+getFQName()+") Create EDTUtil: "+edtUtil.getClass().getName()); + System.err.println("AWTDisplay.createNative("+getFQName()+") Create EDTUtil: "+def.getClass().getName()); } + } else { + def = null; } + return def; } protected void dispatchMessagesNative() { /* nop */ } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java b/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java index 720d4ee4c..27d7a1679 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java @@ -150,7 +150,7 @@ public class MacWindow extends WindowImpl implements MutableSurface, DriverClear System.err.println("MacWindow: clearFocus() - requestFocusParent, isOffscreenInstance "+isOffscreenInstance); } if(!isOffscreenInstance) { - requestFocusParent0(getWindowHandle()); + resignFocus0(getWindowHandle()); } else { focusChanged(false, false); } @@ -360,7 +360,6 @@ public class MacWindow extends WindowImpl implements MutableSurface, DriverClear if(recreate && 0==surfaceHandle) { throw new NativeWindowException("Internal Error - recreate, window but no view"); } - orderOut0(getWindowHandle()); close0(getWindowHandle()); setWindowHandle(0); } else { @@ -397,7 +396,7 @@ public class MacWindow extends WindowImpl implements MutableSurface, DriverClear private native boolean lockSurface0(long window); private native void unlockSurface0(long window); private native void requestFocus0(long window, boolean force); - private native void requestFocusParent0(long window); + private native void resignFocus0(long window); /** in case of a child window, it actually only issues orderBack(..) */ private native void orderOut0(long window); private native void orderFront0(long window); diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java index 34d76a148..6a8c81f3d 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java @@ -151,8 +151,8 @@ public class WindowsWindow extends WindowImpl { } protected void closeNativeImpl() { - if (hdc != 0) { - if(windowHandleClose != 0) { + if(windowHandleClose != 0) { + if (hdc != 0) { try { GDI.ReleaseDC(windowHandleClose, hdc); } catch (Throwable t) { @@ -162,11 +162,8 @@ public class WindowsWindow extends WindowImpl { } } } - hdc = 0; - hdc_old = 0; - } - if(windowHandleClose != 0) { try { + GDI.SetParent(windowHandleClose, 0); // detach first, experience hang w/ SWT parent GDI.DestroyWindow(windowHandleClose); } catch (Throwable t) { if(DEBUG_IMPLEMENTATION) { @@ -177,6 +174,8 @@ public class WindowsWindow extends WindowImpl { windowHandleClose = 0; } } + hdc = 0; + hdc_old = 0; } protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m index 01cbd80ec..bbadc9dd0 100644 --- a/src/newt/native/MacWindow.m +++ b/src/newt/native/MacWindow.m @@ -599,6 +599,15 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_MacWindow_createWindow0 } DBG_PRINT( "createWindow0 - is visible.1: %d\n", [myWindow isVisible]); + // Remove animations for child windows + if(NULL != parentWindow) { +NS_DURING + // Available >= 10.7 - Removes default animations + [myWindow setAnimationBehavior: NSWindowAnimationBehaviorNone]; +NS_HANDLER +NS_ENDHANDLER + } + #ifdef VERBOSE_ON int dbgIdx = 1; #endif @@ -734,7 +743,7 @@ NS_ENDHANDLER if(NULL!=pWin) { [mWin detachFromParent: pWin]; } - [mWin orderOut: mWin]; + [mWin performSelectorOnMainThread:@selector(orderOut:) withObject:mWin waitUntilDone:NO]; DBG_PRINT( "windowClose.1 - %p,%d view %p,%d, parent %p\n", mWin, getRetainCount(mWin), mView, getRetainCount(mView), pWin); @@ -742,11 +751,6 @@ 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 close]' causes a crash at exit. - // This probably happens b/c it sends events to the main loop - // but our resources are gone ?! - // However, issuing a simple release seems to work quite well. - // [mWin release]; [mWin performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; } @@ -806,11 +810,8 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_MacWindow_requestFocus0 DBG_PRINT( "requestFocus - window: %p, force %d, hasFocus %d (START)\n", mWin, force, hasFocus); [mWin makeFirstResponder: nil]; - [mWin performSelectorOnMainThread:@selector(orderFrontRegardless) withObject:nil waitUntilDone:YES]; - [mWin performSelectorOnMainThread:@selector(makeKeyWindow) withObject:nil waitUntilDone:YES]; - // This will occasionally cause a free of non allocated object crash: - // [mWin orderFrontRegardless]; - // [mWin makeKeyWindow]; + [mWin performSelectorOnMainThread:@selector(orderFrontRegardless) withObject:nil waitUntilDone:NO]; + [mWin performSelectorOnMainThread:@selector(makeKeyWindow) withObject:nil waitUntilDone:NO]; DBG_PRINT( "requestFocus - window: %p, force %d (END)\n", mWin, force); @@ -819,26 +820,27 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_MacWindow_requestFocus0 /* * Class: jogamp_newt_driver_macosx_MacWindow - * Method: requestFocusParent0 + * Method: resignFocus0 * Signature: (J)V */ -JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_MacWindow_requestFocusParent0 +JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_MacWindow_resignFocus0 (JNIEnv *env, jobject window, jlong w) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSWindow* mWin = (NSWindow*) ((intptr_t) w); NSWindow* pWin = [mWin parentWindow]; -#ifdef VERBOSE_ON BOOL hasFocus = [mWin isKeyWindow]; -#endif - DBG_PRINT( "requestFocusParent0 - window: %p, parent: %p, hasFocus %d (START)\n", mWin, pWin, hasFocus ); - if(NULL != pWin) { - [pWin performSelectorOnMainThread:@selector(makeKeyWindow) withObject:nil waitUntilDone:YES]; - // This will occasionally cause a free of non allocated object crash: - // [pWin makeKeyWindow]; + DBG_PRINT( "requestFocusParent0 - window: %p, parent %p, hasFocus %d (START)\n", mWin, pWin, hasFocus ); + if( hasFocus ) { + if(NULL != pWin) { + // [mWin performSelectorOnMainThread:@selector(makeFirstResponder:) withObject:pWin waitUntilDone:NO]; + [pWin performSelectorOnMainThread:@selector(makeKeyWindow) withObject:nil waitUntilDone:NO]; + } else { + [mWin performSelectorOnMainThread:@selector(resignKeyWindow) withObject:nil waitUntilDone:NO]; + } } - DBG_PRINT( "requestFocusParent0 - window: %p, parent: %p (END)\n", mWin, pWin); + DBG_PRINT( "requestFocusParent0 - window: %p (END)\n", mWin); [pool release]; } @@ -856,9 +858,7 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_MacWindow_orderFront0 DBG_PRINT( "orderFront0 - window: %p (START)\n", win); - [win performSelectorOnMainThread:@selector(orderFrontRegardless) withObject:nil waitUntilDone:YES]; - // This will occasionally cause a free of non allocated object crash: - // [win orderFrontRegardless]; + [win performSelectorOnMainThread:@selector(orderFrontRegardless) withObject:nil waitUntilDone:NO]; DBG_PRINT( "orderFront0 - window: %p (END)\n", win); @@ -880,13 +880,9 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_MacWindow_orderOut0 DBG_PRINT( "orderOut0 - window: (parent %p) %p (START)\n", pWin, mWin); if(NULL == pWin) { - [mWin performSelectorOnMainThread:@selector(orderOut:) withObject:mWin waitUntilDone:YES]; - // This will occasionally cause a free of non allocated object crash: - // [mWin orderOut: mWin]; + [mWin performSelectorOnMainThread:@selector(orderOut:) withObject:mWin waitUntilDone:NO]; } else { - [mWin performSelectorOnMainThread:@selector(orderBack:) withObject:mWin waitUntilDone:YES]; - // This will occasionally cause a free of non allocated object crash: - // [mWin orderBack: mWin]; + [mWin performSelectorOnMainThread:@selector(orderBack:) withObject:mWin waitUntilDone:NO]; } DBG_PRINT( "orderOut0 - window: (parent %p) %p (END)\n", pWin, mWin); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java new file mode 100644 index 000000000..6a27e6f27 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java @@ -0,0 +1,233 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.swt; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLEventListener; +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.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Test; + +import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.newt.swt.NewtCanvasSWT; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.jogl.demos.es2.MultisampleDemoES2; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.GLReadBufferUtil; +import com.jogamp.opengl.util.texture.TextureIO; + +/** + * Tests that a basic SWT app can open without crashing under different GL profiles + * _and_ custom GLCapabilities. + *

    + * Uses JOGL's NewtCanvasSWT, which allows to be a native container of a NEWT Window.
    + * This method allows utilizing custom GLCapability settings, + * independent from the already instantiated SWT visual. + *

    + *

    + * Note that {@link SWTAccessor#invoke(boolean, Runnable)} is still used to comply w/ + * SWT running on Mac OSX, i.e. to enforce UI action on the main thread. + *

    + */ +public class TestNewtCanvasSWTGLn extends UITestCase { + + static int duration = 250; + + static final int iwidth = 640; + static final int iheight = 480; + + Display display = null; + Shell shell = null; + Composite composite = null; + + @BeforeClass + public static void startup() { + System.out.println( "GLProfile " + GLProfile.glAvailabilityToString() ); + } + + @Before + public void init() { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display = new Display(); + Assert.assertNotNull( display ); + 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 { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + composite.dispose(); + shell.dispose(); + display.dispose(); + }}); + } + catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + display = null; + shell = null; + composite = null; + } + + protected void runTestAGL( GLCapabilitiesImmutable caps, GLEventListener demo, + boolean postAttach, boolean useAnimator ) throws InterruptedException { + final GLReadBufferUtil screenshot = new GLReadBufferUtil(false, false); + + final GLWindow glWindow1 = GLWindow.create(caps); + Assert.assertNotNull(glWindow1); + Assert.assertEquals(false, glWindow1.isVisible()); + Assert.assertEquals(false, glWindow1.isNativeValid()); + Assert.assertNull(glWindow1.getParent()); + glWindow1.addGLEventListener(demo); + glWindow1.addGLEventListener(new GLEventListener() { + int displayCount = 0; + public void init(final GLAutoDrawable drawable) { } + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } + public void display(final GLAutoDrawable drawable) { + if(displayCount < 3) { + snapshot(getSimpleTestName("."), displayCount++, null, drawable.getGL(), screenshot, TextureIO.PNG, null); + } + } + public void dispose(final GLAutoDrawable drawable) { } + }); + + final NewtCanvasSWT canvas1 = NewtCanvasSWT.create( composite, 0, postAttach ? null : glWindow1 ); + Assert.assertNotNull( canvas1 ); + + SWTAccessor.invoke(true, new Runnable() { + public void run() { + shell.setText( getSimpleTestName(".") ); + shell.setSize( 640, 480 ); + shell.open(); + } + }); + + if(postAttach) { + canvas1.setNEWTChild(glWindow1); + } + + // canvas1.update(); + + Animator anim; + if(useAnimator) { + anim = new Animator(glWindow1); + anim.start(); + } else { + anim = null; + } + + long lStartTime = System.currentTimeMillis(); + long lEndTime = lStartTime + duration; + try { + while( (System.currentTimeMillis() < lEndTime) && !canvas1.isDisposed() ) { + if( !display.readAndDispatch() ) { + // blocks on linux .. display.sleep(); + Thread.sleep(10); + } + } + } catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + if(null != anim) { + anim.stop(); + } + + canvas1.dispose(); + } + + @Test + public void preAttach_WithAnimator() throws InterruptedException { + runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(), false /* postAttach */, true /* animator */); + } + + @Test + public void preAttach_NoAnimator() throws InterruptedException { + runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(), false /* postAttach */, false /* animator */); + } + + @Test + public void postAttach_WithAnimator() throws InterruptedException { + runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(), true /* postAttach */, true /* animator */); + } + + @Test + public void test_MultisampleAndAlpha() throws InterruptedException { + GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2()); + caps.setSampleBuffers(true); + caps.setNumSamples(2); + runTestAGL( caps, new MultisampleDemoES2(true), false /* postAttach */, false /* animator */); + } + + static int atoi(String a) { + int i=0; + try { + i = Integer.parseInt(a); + } catch (Exception ex) { ex.printStackTrace(); } + return i; + } + + public static void main(String args[]) { + for(int i=0; i - * Uses JOGL's SWTAccessor only. - *

    - * @author Wade Walker, et.al. - */ -public class TestSWTAccessor02GLn extends UITestCase { - - static int duration = 250; - - static final int iwidth = 640; - static final int iheight = 480; - - Display display = null; - Shell shell = null; - Composite composite = null; - - @BeforeClass - public static void startup() { - System.out.println( "GLProfile " + GLProfile.glAvailabilityToString() ); - } - - @Before - public void init() { - SWTAccessor.invoke(true, new Runnable() { - public void run() { - display = new Display(); - Assert.assertNotNull( display ); - 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 { - SWTAccessor.invoke(true, new Runnable() { - public void run() { - composite.dispose(); - shell.dispose(); - display.dispose(); - }}); - } - catch( Throwable throwable ) { - throwable.printStackTrace(); - Assume.assumeNoException( throwable ); - } - display = null; - shell = null; - composite = null; - } - - protected void runTestAGL( GLProfile glprofile ) throws InterruptedException { - GLCapabilities caps = new GLCapabilities(glprofile); - GLDrawableFactory factory = GLDrawableFactory.getFactory(glprofile); - - // need SWT.NO_BACKGROUND to prevent SWT from clearing the window - // at the wrong times (we use glClear for this instead) - final Canvas canvas = new Canvas( composite, SWT.NO_BACKGROUND); - Assert.assertNotNull( canvas ); - SWTAccessor.setRealized(canvas, true); - - AbstractGraphicsDevice device = SWTAccessor.getDevice(canvas); - long nativeWindowHandle = SWTAccessor.getWindowHandle(canvas); - System.err.println("*** device: " + device); - System.err.println("*** window handle: 0x" + Long.toHexString(nativeWindowHandle)); - - final SWTUpstreamHook swtUpstreamHook = new SWTUpstreamHook(canvas); - canvas.addControlListener(swtUpstreamHook); - - final ProxySurface proxySurface = factory.createProxySurface(device, 0, nativeWindowHandle, caps, null, swtUpstreamHook); - Assert.assertNotNull( proxySurface ); - System.err.println("*** ProxySurface: " + proxySurface); - - final GLDrawable drawable = factory.createGLDrawable(proxySurface); - Assert.assertNotNull( drawable ); - drawable.setRealized(true); - System.err.println("*** Drawable: " + drawable); - Assert.assertTrue( drawable.isRealized() ); - - final GLContext glcontext = drawable.createContext(null); - // trigger native creation .. - if( GLContext.CONTEXT_NOT_CURRENT < glcontext.makeCurrent() ) { - glcontext.release(); - } - - final boolean[] sizeMissing = new boolean[] { false }; - - // fix the viewport when the user resizes the window - canvas.addListener( SWT.Resize, new Listener() { - public void handleEvent( Event event ) { - Rectangle rectangle = canvas.getClientArea(); - boolean glok=false; - if( GLContext.CONTEXT_NOT_CURRENT < glcontext.makeCurrent() ) { - glok=true; - GL2ES1 gl = glcontext.getGL().getGL2ES1(); - OneTriangle.setup( gl, rectangle.width, rectangle.height ); - glcontext.release(); - } else { - sizeMissing[0] = true; - } - System.err.println("resize: glok " + glok); - } - }); - - // draw the triangle when the OS tells us that any part of the window needs drawing - canvas.addPaintListener( new PaintListener() { - public void paintControl( PaintEvent paintevent ) { - Rectangle rectangle = canvas.getClientArea(); - boolean glok=false; - if( GLContext.CONTEXT_NOT_CURRENT < glcontext.makeCurrent() ) { - glok=true; - GL2ES1 gl = glcontext.getGL().getGL2ES1(); - if(sizeMissing[0]) { - OneTriangle.setup( gl, rectangle.width, rectangle.height); - sizeMissing[0] = false; - } - OneTriangle.render( gl, rectangle.width, rectangle.height); - drawable.swapBuffers(); - glcontext.release(); - } - System.err.println("paint: glok " + glok); - } - }); - - shell.setText( getClass().getName() ); - shell.setSize( 640, 480 ); - shell.open(); - - long lStartTime = System.currentTimeMillis(); - long lEndTime = lStartTime + duration; - try { - while( (System.currentTimeMillis() < lEndTime) && !canvas.isDisposed() ) { - if( !display.readAndDispatch() ) { - // blocks on linux .. display.sleep(); - Thread.sleep(10); - } - } - } catch( Throwable throwable ) { - throwable.printStackTrace(); - Assume.assumeNoException( throwable ); - } - glcontext.destroy(); - drawable.setRealized(false); - canvas.dispose(); - } - private static class SWTUpstreamHook implements ProxySurface.UpstreamSurfaceHook, ControlListener { - private Canvas c; - Rectangle clientArea; - public SWTUpstreamHook(Canvas c) { - this.c = c ; - this.clientArea = c.getClientArea(); - } - @Override - public final void create(ProxySurface s) { /* nop */ } - - @Override - public final void destroy(ProxySurface s) { /* nop */ } - - @Override - public final int getWidth(ProxySurface s) { - return clientArea.width; - } - @Override - public final int getHeight(ProxySurface s) { - return clientArea.width; - } - - @Override - public void controlResized(final ControlEvent arg0) { - clientArea = c.getClientArea(); - } - @Override - public void controlMoved(ControlEvent e) { - } - @Override - public String toString() { - final String us_s = null != c ? c.toString() : "nil"; - return "SETUpstreamSurfaceHook[upstream: "+us_s+"]"; - } - }; - - @Test - public void test() throws InterruptedException { - GLProfile glprofile = GLProfile.getGL2ES1(); - runTestAGL( glprofile ); - } - - static int atoi(String a) { - int i=0; - try { - i = Integer.parseInt(a); - } catch (Exception ex) { ex.printStackTrace(); } - return i; - } - - public static void main(String args[]) { - for(int i=0; i *

    + * Note: To employ custom GLCapabilities, NewtCanvasSWT shall be used. + *

    + *

    * Note that {@link SWTAccessor#invoke(boolean, Runnable)} is still used to comply w/ * SWT running on Mac OSX, i.e. to enforce UI action on the main thread. *

    @@ -116,34 +121,32 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { composite = null; } - protected void runTestAGL( GLProfile glprofile ) throws InterruptedException { - // need SWT.NO_BACKGROUND to prevent SWT from clearing the window - // at the wrong times (we use glClear for this instead) - final GLCapabilitiesImmutable caps = new GLCapabilities( glprofile ); + protected void runTestAGL( GLCapabilitiesImmutable caps, GLEventListener demo ) throws InterruptedException { + final GLReadBufferUtil screenshot = new GLReadBufferUtil(false, false); - final GLCanvas canvas = new GLCanvas( composite, 0, caps, null, null); + final GLCanvas canvas = GLCanvas.create( composite, 0, caps, null, null); Assert.assertNotNull( canvas ); + canvas.addGLEventListener( demo ); canvas.addGLEventListener(new GLEventListener() { - public void init(final GLAutoDrawable drawable) { - System.err.println(Thread.currentThread().getName()+" - SWT Canvas - GLEventListener - init()"); - } - public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { - System.err.println(Thread.currentThread().getName()+" - SWT Canvas - GLEventListener - reshape()"); - OneTriangle.setup( drawable.getGL().getGL2(), width, height ); - } + int displayCount = 0; + public void init(final GLAutoDrawable drawable) { } + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } public void display(final GLAutoDrawable drawable) { - OneTriangle.render( drawable.getGL().getGL2(), drawable.getWidth(), drawable.getHeight()); + if(displayCount < 3) { + snapshot(getSimpleTestName("."), displayCount++, null, drawable.getGL(), screenshot, TextureIO.PNG, null); + } } - public void dispose(final GLAutoDrawable drawable) { - System.err.println(Thread.currentThread().getName()+" - SWT Canvas - GLEventListener - dispose()"); - } - }); + public void dispose(final GLAutoDrawable drawable) { } + }); - shell.setText( getClass().getName() ); - shell.setSize( 640, 480 ); - shell.open(); - + SWTAccessor.invoke(true, new Runnable() { + public void run() { + shell.setText( getSimpleTestName(".") ); + shell.setSize( 640, 480 ); + shell.open(); + } } ); + long lStartTime = System.currentTimeMillis(); long lEndTime = lStartTime + duration; try { @@ -157,13 +160,15 @@ public class TestSWTJOGLGLCanvas01GLn extends UITestCase { throwable.printStackTrace(); Assume.assumeNoException( throwable ); } - canvas.dispose(); + SWTAccessor.invoke(true, new Runnable() { + public void run() { + canvas.dispose(); + } } ); } @Test public void test() throws InterruptedException { - GLProfile glprofile = GLProfile.getGL2ES1(); - runTestAGL( glprofile ); + runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2() ); } static int atoi(String a) { diff --git a/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting01aSWT.java b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting01aSWT.java new file mode 100644 index 000000000..11aef7c24 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/parenting/TestParenting01aSWT.java @@ -0,0 +1,210 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.newt.parenting; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; + +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.Test; + +import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.newt.Window; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.newt.swt.NewtCanvasSWT; +import com.jogamp.opengl.test.junit.jogl.demos.es2.RedSquareES2; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; + +/** + * Simple visibility test .. + */ +public class TestParenting01aSWT extends UITestCase { + static int width, height; + static long durationPerTest = 800; + static GLCapabilities glCaps; + + Display display = null; + Shell shell = null; + Composite composite1 = null; + + @BeforeClass + public static void initClass() { + width = 640; + height = 480; + glCaps = new GLCapabilities(null); + } + + @Before + public void init() { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display = new Display(); + Assert.assertNotNull( display ); + shell = new Shell( display ); + Assert.assertNotNull( shell ); + shell.setLayout( new FillLayout() ); + composite1 = new Composite( shell, SWT.NONE ); + composite1.setLayout( new FillLayout() ); + Assert.assertNotNull( composite1 ); + }}); + } + + @After + public void release() { + Assert.assertNotNull( display ); + Assert.assertNotNull( shell ); + Assert.assertNotNull( composite1 ); + try { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + composite1.dispose(); + shell.dispose(); + display.dispose(); + }}); + } + catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + display = null; + shell = null; + composite1 = null; + } + + @Test + public void testWindowParenting01CreateVisibleDestroy1() throws InterruptedException, InvocationTargetException { + + GLWindow glWindow1 = GLWindow.create(glCaps); + Assert.assertNotNull(glWindow1); + Assert.assertEquals(false, glWindow1.isVisible()); + Assert.assertEquals(false, glWindow1.isNativeValid()); + Assert.assertNull(glWindow1.getParent()); + glWindow1.setTitle("testWindowParenting01CreateVisibleDestroy"); + GLEventListener demo1 = new RedSquareES2(); + setDemoFields(demo1, glWindow1, false); + glWindow1.addGLEventListener(demo1); + + final NewtCanvasSWT canvas1 = NewtCanvasSWT.create( composite1, 0, glWindow1 ); + Assert.assertNotNull(canvas1); + Assert.assertEquals(false, glWindow1.isVisible()); + Assert.assertEquals(false, glWindow1.isNativeValid()); + Assert.assertNull(glWindow1.getParent()); + + SWTAccessor.invoke(true, new Runnable() { + public void run() { + shell.setText( getSimpleTestName(".") ); + shell.setSize( 640, 480 ); + shell.open(); + } + }); + + // visible test + Assert.assertEquals(canvas1.getNativeWindow(),glWindow1.getParent()); + + for(int i=0; i*10 2 + if(detachFirst) { + canvas1.setNEWTChild(null); + canvas2.setNEWTChild(null); + } else { + canvas2.setNEWTChild(null); // free g2 of w2 + } + canvas1.setNEWTChild(glWindow2); // put g2 -> w1. free g1 of w1 + canvas2.setNEWTChild(glWindow1); // put g1 -> w2 + frame1.validate(); + frame2.validate(); + } + }); + break; + case 1: + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + // 2 -> 1 + if(detachFirst) { + canvas1.setNEWTChild(null); + canvas2.setNEWTChild(null); + } else { + canvas2.setNEWTChild(null); + } + canvas1.setNEWTChild(glWindow1); + canvas2.setNEWTChild(glWindow2); + frame1.validate(); + frame2.validate(); + } + }); + break; + } + } + + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame1.dispose(); + frame2.dispose(); + } } ); + glWindow1.destroy(); + glWindow2.destroy(); + Assert.assertEquals(false, glWindow1.isNativeValid()); + Assert.assertEquals(false, glWindow2.isNativeValid()); + } + + public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { + Assert.assertNotNull(demo); + Assert.assertNotNull(glWindow); + Window window = glWindow.getDelegatedWindow(); + if(debug) { + MiscUtils.setFieldIfExists(demo, "glDebug", true); + MiscUtils.setFieldIfExists(demo, "glTrace", true); + } + if(!MiscUtils.setFieldIfExists(demo, "window", window)) { + MiscUtils.setFieldIfExists(demo, "glWindow", glWindow); + } + } + + static int atoi(String a) { + int i=0; + try { + i = Integer.parseInt(a); + } catch (Exception ex) { ex.printStackTrace(); } + return i; + } + + public static void main(String args[]) throws IOException { + for(int i=0; i 2 + if(detachFirst) { + canvas1.setNEWTChild(null); + canvas2.setNEWTChild(null); + } else { + canvas2.setNEWTChild(null); // free g2 of w2 + } + canvas1.setNEWTChild(glWindow2); // put g2 -> w1. free g1 of w1 + canvas2.setNEWTChild(glWindow1); // put g1 -> w2 + } } ); + break; + case 1: + SWTAccessor.invoke(true, new Runnable() { + public void run() { + // 2 -> 1 + if(detachFirst) { + canvas1.setNEWTChild(null); + canvas2.setNEWTChild(null); + } else { + canvas2.setNEWTChild(null); + } + canvas1.setNEWTChild(glWindow1); + canvas2.setNEWTChild(glWindow2); + } } ); + break; + } + } + + canvas1.dispose(); + canvas2.dispose(); + Assert.assertEquals(false, glWindow1.isNativeValid()); + Assert.assertEquals(false, glWindow2.isNativeValid()); + } + + public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { + Assert.assertNotNull(demo); + Assert.assertNotNull(glWindow); + Window window = glWindow.getDelegatedWindow(); + if(debug) { + MiscUtils.setFieldIfExists(demo, "glDebug", true); + MiscUtils.setFieldIfExists(demo, "glTrace", true); + } + if(!MiscUtils.setFieldIfExists(demo, "window", window)) { + MiscUtils.setFieldIfExists(demo, "glWindow", glWindow); + } + } + + static int atoi(String a) { + int i=0; + try { + i = Integer.parseInt(a); + } catch (Exception ex) { ex.printStackTrace(); } + return i; + } + + public static void main(String args[]) throws IOException { + for(int i=0; i