diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/swt')
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java | 1035 |
1 files changed, 752 insertions, 283 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 73ad97f5c..cd5aa338d 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -27,9 +27,18 @@ */ package com.jogamp.opengl.swt; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.VisualIDHolder.VIDType; import javax.media.opengl.GL; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; @@ -43,155 +52,254 @@ import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; +import javax.media.opengl.GLSharedContextSetter; import javax.media.opengl.Threading; +import jogamp.nativewindow.x11.X11Util; +import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableHelper; -import jogamp.opengl.ThreadingImpl; +import jogamp.opengl.GLDrawableImpl; 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.layout.FillLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; 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.nativewindow.x11.X11GraphicsDevice; import com.jogamp.opengl.JoglVersion; /** * Native SWT Canvas implementing GLAutoDrawable * <p> - * FIXME: Still needs AWT for threading impl., - * ie. will issue a 'wrong thread' error if runs in headless mode! + * Implementation allows use of custom {@link GLCapabilities}. * </p> */ -public class GLCanvas extends Canvas implements GLAutoDrawable { +public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextSetter { + 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 * by the setting of the threading mode to worker (do not use SWT thread), awt (use SWT thread), or false (always use * calling thread). - * + * * @see Threading - * + * * Now done dynamically to avoid early loading of gluegen library. */ //private static final boolean useSWTThread = ThreadingImpl.getMode() != ThreadingImpl.WORKER; /* GL Stuff */ - private final GLDrawableHelper drawableHelper = new GLDrawableHelper(); - private GLDrawable drawable; - private GLContext context; + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + private final GLDrawableHelper helper = new GLDrawableHelper(); + + private final GLCapabilitiesImmutable capsRequested; + private final GLCapabilitiesChooser capsChooser; + + private volatile Rectangle clientArea; + private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access + private volatile GLContextImpl context; // volatile: avoid locking for read-only access /* Native window surface */ - private AbstractGraphicsDevice device; - private final long nativeWindowHandle; - private final ProxySurface proxySurface; + private final boolean useX11GTK; + private volatile long gdkWindow; // either GDK child window .. + private volatile long x11Window; // .. or X11 child window (for GL rendering) + private final AbstractGraphicsScreen screen; /* Construction parameters stored for GLAutoDrawable accessor methods */ - private int ctxCreationFlags = 0; - - private final GLCapabilitiesImmutable glCapsRequested; + private int additionalCtxCreationFlags = 0; - /* - * Lock for access to GLDrawable, as used in GLCanvas, - */ - private final RecursiveLock lock = LockFactory.createRecursiveLock(); /* 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 + + private static String getThreadName() { return Thread.currentThread().getName(); } + private static String toHexString(int v) { return "0x"+Integer.toHexString(v); } + private static String toHexString(long v) { return "0x"+Long.toHexString(v); } /* * Invokes init(...) on all GLEventListeners. Assumes context is current when run. */ private final Runnable initAction = new Runnable() { + @Override public void run() { - drawableHelper.init(GLCanvas.this); + helper.init(GLCanvas.this, !sendReshape); } }; /* * Action to handle display in OpenGL, also processes reshape since they should be done at the same time. - * + * * Assumes GLContext is current when run. */ private final Runnable displayAction = new Runnable() { + @Override public void run() { if (sendReshape) { - drawableHelper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight()); + helper.reshape(GLCanvas.this, 0, 0, clientArea.width, clientArea.height); sendReshape = false; } - drawableHelper.display(GLCanvas.this); + helper.display(GLCanvas.this); } }; /* Action to make specified context current prior to running displayAction */ - private final Runnable makeCurrentAndDisplayAction = new Runnable() { + private final Runnable makeCurrentAndDisplayOnGLAction = new Runnable() { + @Override public void run() { - drawableHelper.invokeGL(drawable, context, displayAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !GLCanvas.this.isDisposed() ) { + helper.invokeGL(drawable, context, displayAction, initAction); + } + } finally { + _lock.unlock(); + } } }; /* Swaps buffers, assuming the GLContext is current */ - private final Runnable swapBuffersAction = new Runnable() { - public void run() { - drawable.swapBuffers(); - } - }; - - /* Swaps buffers, making the GLContext current first */ - private final Runnable makeCurrentAndSwapBuffersAction = new Runnable() { + private final Runnable swapBuffersOnGLAction = new Runnable() { + @Override public void run() { - drawableHelper.invokeGL(drawable, context, swapBuffersAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final boolean drawableOK = null != drawable && drawable.isRealized(); + if( drawableOK && !GLCanvas.this.isDisposed() ) { + drawable.swapBuffers(); + } + } finally { + _lock.unlock(); + } } }; /* * Disposes of OpenGL resources */ - private final Runnable postDisposeGLAction = new Runnable() { - public void run() { - context = null; - if (null != drawable) { - drawable.setRealized(false); - drawable = null; - } - } - }; - private final Runnable disposeOnEDTGLAction = new Runnable() { + @Override public void run() { - drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeGLAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLAnimatorControl animator = getAnimator(); + final boolean animatorPaused; + if(null!=animator) { + // can't remove us from animator for recreational addNotify() + animatorPaused = animator.pause(); + } else { + animatorPaused = false; + } + + if ( null != context ) { + if( context.isCreated() ) { + // Catch dispose GLExceptions by GLEventListener, just 'print' them + // so we can continue with the destruction. + try { + if( !GLCanvas.this.isDisposed() ) { + helper.disposeGL(GLCanvas.this, context, true); + } else { + context.destroy(); + } + } catch (GLException gle) { + gle.printStackTrace(); + } + } + context = null; + } + if ( null != drawable ) { + drawable.setRealized(false); + drawable = null; + } + if( 0 != x11Window) { + SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); + x11Window = 0; + } else if( 0 != gdkWindow) { + SWTAccessor.destroyGDKWindow(gdkWindow); + gdkWindow = 0; + } + screen.getDevice().close(); + + if (animatorPaused) { + animator.resume(); + } + + } finally { + _lock.unlock(); + } } }; - private final Runnable disposeGraphicsDeviceAction = new Runnable() { - public void run() { - if (null != device) { - device.close(); - device = null; - } - } + private class DisposeGLEventListenerAction implements Runnable { + private GLEventListener listener; + private final boolean remove; + private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) { + this.listener = listener; + this.remove = remove; + } + + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !GLCanvas.this.isDisposed() ) { + listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove); + } + } finally { + _lock.unlock(); + } + } }; /** - * Storage for the client area rectangle so that it may be accessed from outside of the SWT thread. + * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser)} + * 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. + * @return a new instance */ - private volatile Rectangle clientArea; + public static GLCanvas create(final Composite parent, final int style, final GLCapabilitiesImmutable caps, + final GLCapabilitiesChooser chooser) { + final GLCanvas[] res = new GLCanvas[] { null }; + parent.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + res[0] = new GLCanvas( parent, style, caps, chooser ); + } + }); + return res[0]; + } /** - * Creates a new SWT GLCanvas. - * + * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser, GLContext)} + * on the SWT thread. + * * @param parent * Required (non-null) parent Composite. * @param style @@ -206,354 +314,715 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * 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 + * @deprecated Use {@link #create(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser)} + * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}. */ - public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable caps, - final GLCapabilitiesChooser chooser, final GLContext shareWith) { + 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() { + @Override + public void run() { + res[0] = new GLCanvas( parent, style, caps, chooser, shareWith ); + } + }); + return res[0]; + } + + /** + * Creates a new SWT GLCanvas. + * + * @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 capsReqUser + * 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 capsChooser + * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the + * requested GLCapabilities, and the available capabilities of the graphics device. + */ + public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable capsReqUser, + final GLCapabilitiesChooser capsChooser) { + this(parent, style, capsReqUser, capsChooser, null); + } + + /** + * Creates a new SWT GLCanvas. + * + * @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 capsReqUser + * 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 capsChooser + * 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. + * @deprecated Use {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser)} + * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}. + */ + public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable capsReqUser, + final GLCapabilitiesChooser capsChooser, final GLContext shareWith) { /* NO_BACKGROUND required to avoid clearing bg in native SWT widget (we do this in the GL display) */ super(parent, style | SWT.NO_BACKGROUND); - + GLProfile.initSingleton(); // ensure JOGL is completly initialized SWTAccessor.setRealized(this, true); clientArea = GLCanvas.this.getClientArea(); - /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite) */ - device = SWTAccessor.getDevice(this); - /* Native handle for the control, used to associate with GLContext */ - nativeWindowHandle = SWTAccessor.getWindowHandle(this); + /* 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 closing operation will be a NOP. */ + final AbstractGraphicsDevice swtDevice = SWTAccessor.getDevice(this); + + useX11GTK = SWTAccessor.useX11GTK(); + if(useX11GTK) { + // Decoupled X11 Device/Screen allowing X11 display lock-free off-thread rendering + final long x11DeviceHandle = X11Util.openDisplay(swtDevice.getConnection()); + if( 0 == x11DeviceHandle ) { + throw new RuntimeException("Error creating display(EDT): "+swtDevice.getConnection()); + } + final AbstractGraphicsDevice x11Device = new X11GraphicsDevice(x11DeviceHandle, AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */); + screen = SWTAccessor.getScreen(x11Device, -1 /* default */); + } else { + screen = SWTAccessor.getScreen(swtDevice, -1 /* default */); + } /* Select default GLCapabilities if none was provided, otherwise clone provided caps to ensure safety */ - if(null == caps) { - caps = new GLCapabilities(GLProfile.getDefault(device)); + if(null == capsReqUser) { + capsReqUser = new GLCapabilities(GLProfile.getDefault(screen.getDevice())); } - glCapsRequested = caps; - - final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(caps.getGLProfile()); - - /* Create a NativeWindow proxy for the SWT canvas */ - proxySurface = glFactory.createProxySurface(device, nativeWindowHandle, caps, chooser); - - /* Associate a GL surface with the proxy */ - drawable = glFactory.createGLDrawable(proxySurface); - drawable.setRealized(true); - - context = drawable.createContext(shareWith); - - /* 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() { - public void paintControl(final PaintEvent arg0) { - if (!drawableHelper.isExternalAnimatorAnimating()) { - display(); - } - } - }); - - 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; - } - }); + + this.capsRequested = capsReqUser; + this.capsChooser = capsChooser; + if( null != shareWith ) { + helper.setSharedContext(null, shareWith); + } + + // post create .. when ready + gdkWindow = 0; + x11Window = 0; + drawable = null; + context = null; + + final Listener listener = new Listener () { + @Override + public void handleEvent (Event event) { + switch (event.type) { + case SWT.Paint: + displayIfNoAnimatorNoCheck(); + break; + case SWT.Resize: + updateSizeCheck(); + break; + case SWT.Dispose: + GLCanvas.this.dispose(); + break; + } + } + }; + addListener (SWT.Resize, listener); + addListener (SWT.Paint, listener); + addListener (SWT.Dispose, listener); + } + + @Override + public final void setSharedContext(GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.context, sharedContext); + } + + @Override + public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + helper.setSharedAutoDrawable(this, sharedAutoDrawable); + } + + private final UpstreamSurfaceHook swtCanvasUpStreamHook = new 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 "SWTCanvasUpstreamSurfaceHook[upstream: "+GLCanvas.this.toString()+", "+clientArea.width+"x"+clientArea.height+"]"; + } + }; + + protected final void updateSizeCheck() { + 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 + + final GLDrawableImpl _drawable = drawable; + final boolean drawableOK = null != _drawable && _drawable.isRealized(); + if(DEBUG) { + final long dh = drawableOK ? _drawable.getHandle() : 0; + System.err.println(getThreadName()+": GLCanvas.sizeChanged: ("+Thread.currentThread().getName()+"): "+nClientArea.x+"/"+nClientArea.y+" "+nClientArea.width+"x"+nClientArea.height+" - drawableHandle "+toHexString(dh)); + } + if( drawableOK ) { + if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, nClientArea.width, nClientArea.height); + if(_drawable != _drawableNew) { + // write back + drawable = _drawableNew; + } + } finally { + _lock.unlock(); + } + } + } + if(0 != x11Window) { + SWTAccessor.resizeX11Window(screen.getDevice(), clientArea, x11Window); + } else if(0 != gdkWindow) { + SWTAccessor.resizeGDKWindow(clientArea, gdkWindow); + } + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock + } + } + + private boolean isValidAndVisibleOnEDTActionResult; + private final Runnable isValidAndVisibleOnEDTAction = new Runnable() { + @Override + public void run() { + isValidAndVisibleOnEDTActionResult = !GLCanvas.this.isDisposed() && GLCanvas.this.isVisible(); + } }; + + private final boolean isValidAndVisibleOnEDT() { + synchronized(isValidAndVisibleOnEDTAction) { + runOnEDTIfAvail(true, isValidAndVisibleOnEDTAction); + return isValidAndVisibleOnEDTActionResult; + } + } + + /** assumes drawable == null (implying !drawable.isRealized()) ! Checks of !isDispose() and isVisible() */ + protected final boolean validateDrawableAndContextWithCheck() { + if( !isValidAndVisibleOnEDT() ) { + return false; + } + return validateDrawableAndContextPostCheck(); + } + + private final boolean isDrawableAndContextValid() { + // drawable != null implies drawable.isRealized()==true + return null != drawable && null != context; + } + + /** assumes drawable == null (implying !drawable.isRealized()) || context == null ! No check of !isDispose() and isVisible() */ + private final boolean validateDrawableAndContextPostCheck() { + boolean res; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if(null == drawable) { + // 'displayable' (isValidAndVisibleOnEDT()) must have been checked upfront if appropriate! + createDrawableImpl(); // checks clientArea size (i.e. drawable size) and perf. realization + } + final GLDrawable _drawable = drawable; + if ( null != _drawable ) { + // drawable realization goes in-hand w/ it's construction + if( null == context ) { + // re-try context creation + res = createContextImpl(_drawable); // pending creation. + } else { + res = true; + } + if(res) { + sendReshape = true; + } + } else { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": null drawable"); + } + res = false; + } + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate.X "+toHexString(hashCode())+": "+res+", drawable-realized "+drawable.isRealized()+", has context "+(null!=context)); + } + } finally { + _lock.unlock(); + } + return res; + } + private final void createDrawableImpl() { + final Rectangle nClientArea = clientArea; + if(0 >= nClientArea.width || 0 >= nClientArea.height) { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate.X "+toHexString(hashCode())+": drawable could not be created: size < 0x0"); + } + return; // early out + } + final AbstractGraphicsDevice device = screen.getDevice(); + device.open(); + + final long nativeWindowHandle; + if( useX11GTK ) { + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(device, capsRequested); + final AbstractGraphicsConfiguration cfg = factory.chooseGraphicsConfiguration( + capsRequested, capsRequested, capsChooser, screen, VisualIDHolder.VID_UNDEFINED); + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.X11 "+toHexString(hashCode())+": factory: "+factory+", chosen config: "+cfg); + } + if (null == cfg) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + final int visualID = cfg.getVisualID(VIDType.NATIVE); + if( VisualIDHolder.VID_UNDEFINED != visualID ) { + // gdkWindow = SWTAccessor.createCompatibleGDKChildWindow(this, visualID, clientArea.width, clientArea.height); + // nativeWindowHandle = SWTAccessor.gdk_window_get_xwindow(gdkWindow); + x11Window = SWTAccessor.createCompatibleX11ChildWindow(screen, this, visualID, clientArea.width, clientArea.height); + nativeWindowHandle = x11Window; + } else { + throw new GLException("Could not choose valid visualID: "+toHexString(visualID)+", "+this); + } + } else { + nativeWindowHandle = SWTAccessor.getWindowHandle(this); + } + final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(capsRequested.getGLProfile()); + + // Create a NativeWindow proxy for the SWT canvas + ProxySurface proxySurface = glFactory.createProxySurface(device, screen.getIndex(), nativeWindowHandle, + capsRequested, capsChooser, swtCanvasUpStreamHook); + // Associate a GL surface with the proxy + final GLDrawableImpl _drawable = (GLDrawableImpl) glFactory.createGLDrawable(proxySurface); + _drawable.setRealized(true); + if(!_drawable.isRealized()) { + // oops + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate.X "+toHexString(hashCode())+": Drawable could not be realized: "+_drawable); + } + } else { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": Drawable created and realized"); + } + drawable = _drawable; + } + } + private boolean createContextImpl(final GLDrawable drawable) { + final GLContext[] shareWith = { null }; + if( !helper.isSharedGLContextPending(shareWith) ) { + context = (GLContextImpl) drawable.createContext(shareWith[0]); + context.setContextCreationFlags(additionalCtxCreationFlags); + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": Context created: has shared "+(null != shareWith[0])); + } + return true; + } else { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": Context !created: pending share"); + } + return false; + } + } + + @Override + public void update() { + // don't paint background etc .. nop avoids flickering + // super.update(); + } + + /** + @Override + public boolean forceFocus() { + final boolean r = super.forceFocus(); + if(r && 0 != gdkWindow) { + SWTGTKUtil.focusGDKWindow(gdkWindow); + } + return r; + } */ + + @Override + public void dispose() { + runInGLThread(disposeOnEDTGLAction); + super.dispose(); + } + + private final void displayIfNoAnimatorNoCheck() { + if ( !helper.isAnimatorAnimatingOnOtherThread() ) { + if( isDrawableAndContextValid() || validateDrawableAndContextPostCheck() ) { + runInGLThread(makeCurrentAndDisplayOnGLAction); + } + } + } + + // + // GL[Auto]Drawable + // + + @Override + public void display() { + if( isDrawableAndContextValid() || validateDrawableAndContextWithCheck() ) { + runInGLThread(makeCurrentAndDisplayOnGLAction); + } + } + + @Override + public final Object getUpstreamWidget() { + return this; + } + + @Override + public int getWidth() { + return clientArea.width; + } + + @Override + public int getHeight() { + return clientArea.height; + } + + @Override + public boolean isGLOriented() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.isGLOriented() : true; + } + + @Override + public void addGLEventListener(final GLEventListener listener) { + helper.addGLEventListener(listener); + } + + @Override + public void addGLEventListener(final int idx, final GLEventListener listener) throws IndexOutOfBoundsException { + helper.addGLEventListener(idx, listener); + } + + @Override + public int getGLEventListenerCount() { + return helper.getGLEventListenerCount(); + } + + @Override + public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); + } + + @Override + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); + } + + @Override + public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); } - public void addGLEventListener(final GLEventListener arg0) { - drawableHelper.addGLEventListener(arg0); + @Override + public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) { + final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove); + runInGLThread(r); + return r.listener; } - public void addGLEventListener(final int arg0, final GLEventListener arg1) throws IndexOutOfBoundsException { - drawableHelper.addGLEventListener(arg0, arg1); + @Override + public GLEventListener removeGLEventListener(final GLEventListener listener) { + return helper.removeGLEventListener(listener); } /** * {@inheritDoc} + * * <p> - * Also disposes of the SWT component. + * This impl. calls this class's {@link #dispose()} SWT override, + * where the actual implementation resides. + * </p> */ + @Override public void destroy() { - drawable.setRealized(false); dispose(); } - public void display() { - runInGLThread(makeCurrentAndDisplayAction, displayAction); + @Override + public GLAnimatorControl getAnimator() { + return helper.getAnimator(); } - public GLAnimatorControl getAnimator() { - return drawableHelper.getAnimator(); + @Override + public final Thread setExclusiveContextThread(Thread t) throws GLException { + return helper.setExclusiveContextThread(t, context); } + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override public boolean getAutoSwapBufferMode() { - return drawableHelper.getAutoSwapBufferMode(); + return helper.getAutoSwapBufferMode(); + } + + @Override + public final GLDrawable getDelegatedDrawable() { + return drawable; } + @Override public GLContext getContext() { return context; } + @Override public int getContextCreationFlags() { - return ctxCreationFlags; + return additionalCtxCreationFlags; } + @Override public GL getGL() { - final GLContext ctx = getContext(); - return (ctx == null) ? null : ctx.getGL(); + final GLContext _context = context; + return (null == _context) ? null : _context.getGL(); } - public void invoke(final boolean wait, final GLRunnable run) { - /* Queue task for running during the next display(). */ - drawableHelper.invoke(this, wait, run); + @Override + public boolean invoke(final boolean wait, final GLRunnable runnable) { + return helper.invoke(this, wait, runnable); } - public void removeGLEventListener(final GLEventListener arg0) { - drawableHelper.removeGLEventListener(arg0); + @Override + public boolean invoke(final boolean wait, final List<GLRunnable> runnables) { + return helper.invoke(this, wait, runnables); } + @Override public void setAnimator(final GLAnimatorControl arg0) throws GLException { - drawableHelper.setAnimator(arg0); + helper.setAnimator(arg0); } + @Override public void setAutoSwapBufferMode(final boolean arg0) { - drawableHelper.setAutoSwapBufferMode(arg0); + helper.setAutoSwapBufferMode(arg0); } - public void setContext(final GLContext ctx) { - this.context = ctx; - if (ctx instanceof GLContextImpl) { - ((GLContextImpl) ctx).setContextCreationFlags(ctxCreationFlags); + @Override + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLContext oldCtx = context; + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); + context=(GLContextImpl)newCtx; + return oldCtx; + } finally { + _lock.unlock(); } } + @Override public void setContextCreationFlags(final int arg0) { - ctxCreationFlags = arg0; + additionalCtxCreationFlags = arg0; + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); + } } + @Override public GL setGL(final GL arg0) { - final GLContext ctx = getContext(); - if (ctx != null) { - ctx.setGL(arg0); + final GLContext _context = context; + if (null != _context) { + _context.setGL(arg0); return arg0; } return null; } - public GLContext createContext(final GLContext arg0) { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.createContext(arg0) : null; - } finally { - lock.unlock(); - } + @Override + public GLContext createContext(final GLContext shareWith) { + 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(); + } } + @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { - return (GLCapabilitiesImmutable)proxySurface.getGraphicsConfiguration().getChosenCapabilities(); + final GLDrawable _drawable = drawable; + return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getChosenGLCapabilities() : null; } /** * Accessor for the GLCapabilities that were requested (via the constructor parameter). - * + * * @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 public GLDrawableFactory getFactory() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.getFactory() : null; - } finally { - lock.unlock(); - } + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getFactory() : null; } + @Override public GLProfile getGLProfile() { - return glCapsRequested.getGLProfile(); + return capsRequested.getGLProfile(); } + @Override public long getHandle() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.getHandle() : 0; - } finally { - lock.unlock(); - } - } - - public int getHeight() { - final Rectangle clientArea = this.clientArea; - if (clientArea == null) return 0; - return clientArea.height; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getHandle() : 0; } + @Override public NativeSurface getNativeSurface() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.getNativeSurface() : null; - } finally { - lock.unlock(); - } - } - - public int getWidth() { - final Rectangle clientArea = this.clientArea; - if (clientArea == null) return 0; - return clientArea.width; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getNativeSurface() : null; } + @Override public boolean isRealized() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.isRealized() : false; - } finally { - lock.unlock(); - } + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.isRealized() : false; } + @Override public void setRealized(final boolean arg0) { /* Intentionally empty */ } + @Override public void swapBuffers() throws GLException { - runInGLThread(makeCurrentAndSwapBuffersAction, swapBuffersAction); - } - - // FIXME: API of update() method ? - public void update() { - // FIXME: display(); - } - - public void dispose() { - lock.lock(); - try { - final Display display = getDisplay(); - - if (null != context) { - 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()) { - if (Threading.isSingleThreaded() && !Threading.isOpenGLThread()) { - runInDesignatedGLThread(disposeOnEDTGLAction); - } else if (context.isCreated()) { - drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeGLAction); - } - } - - if (animatorPaused) { - animator.resume(); - } - } - if (display.getThread() == Thread.currentThread()) { - disposeGraphicsDeviceAction.run(); - } else { - display.syncExec(disposeGraphicsDeviceAction); - } - } finally { - lock.unlock(); - } - super.dispose(); + runInGLThread(swapBuffersOnGLAction); } /** - * Determines whether the current thread is the appropriate thread to use the GLContext in. If we are using one of - * the single-threaded policies in {@link Threading}, than this is either the SWT event dispatch thread, or the - * OpenGL worker thread depending on the state of {@link #useSWTThread}. Otherwise this always returns true because - * the threading model is user defined. - * <p> - * TODO: should this be moved to {@link Threading}? - * - * @return true if the calling thread is the correct thread to execute OpenGL calls in, false otherwise. + * Runs the specified action in an SWT compatible thread, which is: + * <ul> + * <li>Mac OSX + * <ul> + * <!--li>AWT EDT: In case AWT is available, the AWT EDT is the OSX UI main thread</li--> + * <!--li><i>Main Thread</i>: Run on OSX UI main thread.</li--> + * <li>Current thread</li> + * </ul></li> + * <li>Linux, Windows, .. + * <ul> + * <!--li>Use {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}</li--> + * <li>Current thread</li> + * </ul></li> + * </ul> + * The current thread seems to be valid for all platforms, + * since no SWT lifecycle tasks are being performed w/ this call. + * Only GL task, which are independent from the SWT threading model. + * + * @see Platform#AWT_AVAILABLE + * @see Platform#getOSType() */ - protected boolean isRenderThread() { - if (Threading.isSingleThreaded()) { - if (ThreadingImpl.getMode() != ThreadingImpl.Mode.ST_WORKER) { - final Display display = getDisplay(); - return display != null && display.getThread() == Thread.currentThread(); - } - return Threading.isOpenGLThread(); - } - /* - * For multi-threaded rendering, the render thread is not defined... - */ - return true; + private void runInGLThread(final Runnable action) { + /** + if(Platform.OSType.MACOS == Platform.OS_TYPE) { + SWTAccessor.invoke(true, action); + } else { + Threading.invokeOnOpenGLThread(true, action); + } */ + /** + if( !isDisposed() ) { + final Display d = getDisplay(); + if( d.getThread() == Thread.currentThread() ) { + action.run(); + } else { + d.syncExec(action); + } + } */ + action.run(); } - /** - * Runs the specified action in the designated OpenGL thread. If the current thread is designated, then the - * syncAction is run synchronously, otherwise the asyncAction is dispatched to the appropriate worker thread. - * - * @param asyncAction - * The non-null action to dispatch to an OpenGL worker thread. This action should not assume that a - * GLContext is current when invoked. - * @param syncAction - * The non-null action to run synchronously if the current thread is designated to handle OpenGL calls. - * This action may assume the GLContext is current. - */ - private void runInGLThread(final Runnable asyncAction, final Runnable syncAction) { - if (Threading.isSingleThreaded() && !isRenderThread()) { - /* Run in designated GL thread */ - runInDesignatedGLThread(asyncAction); - } else { - /* Run in current thread... */ - drawableHelper.invokeGL(drawable, context, syncAction, initAction); - } + private void runOnEDTIfAvail(boolean wait, final Runnable action) { + final Display d = isDisposed() ? null : getDisplay(); + if( null == d || d.isDisposed() || d.getThread() == Thread.currentThread() ) { + action.run(); + } else if(wait) { + d.syncExec(action); + } else { + d.asyncExec(action); + } } - /** - * Dispatches the specified runnable to the appropriate OpenGL worker thread (either the SWT event dispatch thread, - * or the OpenGL worker thread depending on the state of {@link #useSWTThread}). - * - * @param makeCurrentAndRunAction - * The non-null action to dispatch. - */ - private void runInDesignatedGLThread(final Runnable makeCurrentAndRunAction) { - if (ThreadingImpl.getMode() != ThreadingImpl.Mode.ST_WORKER) { - final Display display = getDisplay(); - assert display.getThread() != Thread.currentThread() : "Incorrect use of thread dispatching."; - display.syncExec(makeCurrentAndRunAction); - } else { - Threading.invokeOnOpenGLThread(true, makeCurrentAndRunAction); - } + @Override + public String toString() { + final GLDrawable _drawable = drawable; + final int dw = (null!=_drawable) ? _drawable.getWidth() : -1; + final int dh = (null!=_drawable) ? _drawable.getHeight() : -1; + + return "SWT-GLCanvas[Realized "+isRealized()+ + ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+ + ",\n\tFactory "+getFactory()+ + ",\n\thandle "+toHexString(getHandle())+ + ",\n\tDrawable size "+dw+"x"+dh+ + ",\n\tSWT size "+getWidth()+"x"+getHeight()+"]"; } - public static void main(final String[] args) { System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); // System.err.println(NativeWindowVersion.getInstance()); System.err.println(JoglVersion.getInstance()); - System.err.println(JoglVersion.getDefaultOpenGLInfo(null).toString()); - + System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString()); + final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) ); final Display display = new Display(); final Shell shell = new Shell(display); shell.setSize(128,128); shell.setLayout(new FillLayout()); - final GLCanvas canvas = new GLCanvas(shell, 0, caps, null, null); + final GLCanvas canvas = new GLCanvas(shell, 0, caps, null); canvas.addGLEventListener(new GLEventListener() { + @Override public void init(final GLAutoDrawable drawable) { GL gl = drawable.getGL(); System.err.println(JoglVersion.getGLInfo(gl, null)); } - public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {} + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {} + @Override public void display(final GLAutoDrawable drawable) {} - public void dispose(final GLAutoDrawable drawable) {} + @Override + public void dispose(final GLAutoDrawable drawable) {} }); shell.open(); canvas.display(); |