summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/com/jogamp/opengl/swt
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/swt')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java1035
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();