/**
* Copyright 2011 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package com.jogamp.opengl.swt;
import java.util.List;
import com.jogamp.nativewindow.AbstractGraphicsConfiguration;
import com.jogamp.nativewindow.AbstractGraphicsDevice;
import com.jogamp.nativewindow.AbstractGraphicsScreen;
import com.jogamp.nativewindow.GraphicsConfigurationFactory;
import com.jogamp.nativewindow.NativeSurface;
import com.jogamp.nativewindow.NativeWindowException;
import com.jogamp.nativewindow.ProxySurface;
import com.jogamp.nativewindow.UpstreamSurfaceHook;
import com.jogamp.nativewindow.VisualIDHolder;
import com.jogamp.nativewindow.VisualIDHolder.VIDType;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GLAnimatorControl;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLCapabilitiesChooser;
import com.jogamp.opengl.GLCapabilitiesImmutable;
import com.jogamp.opengl.GLContext;
import com.jogamp.opengl.GLDrawable;
import com.jogamp.opengl.GLDrawableFactory;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLException;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.GLRunnable;
import com.jogamp.opengl.GLSharedContextSetter;
import com.jogamp.opengl.Threading;
import jogamp.nativewindow.x11.X11Util;
import jogamp.opengl.Debug;
import jogamp.opengl.GLContextImpl;
import jogamp.opengl.GLDrawableHelper;
import jogamp.opengl.GLDrawableImpl;
import org.eclipse.swt.SWT;
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
*
* Implementation allows use of custom {@link GLCapabilities}.
*
*
* OpenGL Context Sharing
* To share a {@link GLContext} see the following note in the documentation overview:
* context sharing
* as well as {@link GLSharedContextSetter}.
*
*/
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 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 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 additionalCtxCreationFlags = 0;
/* Flag indicating whether an unprocessed reshape is pending. */
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(final int v) { return "0x"+Integer.toHexString(v); }
private static String toHexString(final 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() {
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) {
helper.reshape(GLCanvas.this, 0, 0, clientArea.width, clientArea.height);
sendReshape = false;
}
helper.display(GLCanvas.this);
}
};
/* Action to make specified context current prior to running displayAction */
private final Runnable makeCurrentAndDisplayOnGLAction = new Runnable() {
@Override
public void run() {
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 swapBuffersOnGLAction = new Runnable() {
@Override
public void run() {
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 disposeOnEDTGLAction = new Runnable() {
@Override
public void run() {
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;
}
GLException exceptionOnDisposeGL = null;
if( null != context ) {
if( context.isCreated() ) {
try {
if( !GLCanvas.this.isDisposed() ) {
helper.disposeGL(GLCanvas.this, context, true);
} else {
context.destroy();
}
} catch (final GLException gle) {
exceptionOnDisposeGL = gle;
}
}
context = null;
}
Throwable exceptionOnUnrealize = null;
if( null != drawable ) {
try {
drawable.setRealized(false);
} catch( final Throwable re ) {
exceptionOnUnrealize = re;
}
drawable = null;
}
Throwable exceptionOnDeviceClose = null;
try {
if( 0 != x11Window) {
SWTAccessor.destroyX11Window(screen.getDevice(), x11Window);
x11Window = 0;
} else if( 0 != gdkWindow) {
SWTAccessor.destroyGDKWindow(gdkWindow);
gdkWindow = 0;
}
screen.getDevice().close();
} catch (final Throwable re) {
exceptionOnDeviceClose = re;
}
if (animatorPaused) {
animator.resume();
}
// throw exception in order of occurrence ..
if( null != exceptionOnDisposeGL ) {
throw exceptionOnDisposeGL;
}
if( null != exceptionOnUnrealize ) {
throw GLException.newGLException(exceptionOnUnrealize);
}
if( null != exceptionOnDeviceClose ) {
throw GLException.newGLException(exceptionOnDeviceClose);
}
} finally {
_lock.unlock();
}
}
};
private class DisposeGLEventListenerAction implements Runnable {
private GLEventListener listener;
private final boolean remove;
private DisposeGLEventListenerAction(final GLEventListener listener, final 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();
}
}
};
/**
* 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
*/
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.
*
* @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, final GLCapabilitiesImmutable capsReqUser,
final GLCapabilitiesChooser capsChooser) {
/* 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).
* 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 use cloned provided caps */
if(null == capsReqUser) {
this.capsRequested = new GLCapabilities(GLProfile.getDefault(screen.getDevice()));
} else {
this.capsRequested = (GLCapabilitiesImmutable) capsReqUser.cloneMutable();
}
this.capsChooser = capsChooser;
// post create .. when ready
gdkWindow = 0;
x11Window = 0;
drawable = null;
context = null;
final Listener listener = new Listener () {
@Override
public void handleEvent (final 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(final GLContext sharedContext) throws IllegalStateException {
helper.setSharedContext(this.context, sharedContext);
}
@Override
public final void setSharedAutoDrawable(final GLAutoDrawable sharedAutoDrawable) throws IllegalStateException {
helper.setSharedAutoDrawable(this, sharedAutoDrawable);
}
private final UpstreamSurfaceHook swtCanvasUpStreamHook = new UpstreamSurfaceHook() {
@Override
public final void create(final ProxySurface s) { /* nop */ }
@Override
public final void destroy(final ProxySurface s) { /* nop */ }
@Override
public final int getSurfaceWidth(final ProxySurface s) {
return clientArea.width;
}
@Override
public final int getSurfaceHeight(final ProxySurface s) {
return clientArea.height;
}
@Override
public String toString() {
return "SWTCanvasUpstreamSurfaceHook[upstream: "+GLCanvas.this.toString()+", "+clientArea.width+"x"+clientArea.height+"]";
}
/**
* {@inheritDoc}
*
* Returns null
.
*
*/
@Override
public final NativeSurface getUpstreamSurface() {
return null;
}
};
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
final 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 final RecursiveLock getUpstreamLock() { return lock; }
@Override
public int getSurfaceWidth() {
return clientArea.width;
}
@Override
public int getSurfaceHeight() {
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(final int index) throws IndexOutOfBoundsException {
return helper.getGLEventListener(index);
}
@Override
public boolean areAllGLEventListenerInitialized() {
return helper.areAllGLEventListenerInitialized();
}
@Override
public boolean getGLEventListenerInitState(final GLEventListener listener) {
return helper.getGLEventListenerInitState(listener);
}
@Override
public void setGLEventListenerInitState(final GLEventListener listener, final boolean initialized) {
helper.setGLEventListenerInitState(listener, initialized);
}
@Override
public GLEventListener disposeGLEventListener(final GLEventListener listener, final boolean remove) {
final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
runInGLThread(r);
return r.listener;
}
@Override
public GLEventListener removeGLEventListener(final GLEventListener listener) {
return helper.removeGLEventListener(listener);
}
/**
* {@inheritDoc}
*
*
* This impl. calls this class's {@link #dispose()} SWT override,
* where the actual implementation resides.
*
*/
@Override
public void destroy() {
dispose();
}
@Override
public GLAnimatorControl getAnimator() {
return helper.getAnimator();
}
@Override
public final Thread setExclusiveContextThread(final Thread t) throws GLException {
return helper.setExclusiveContextThread(t, context);
}
@Override
public final Thread getExclusiveContextThread() {
return helper.getExclusiveContextThread();
}
@Override
public boolean getAutoSwapBufferMode() {
return helper.getAutoSwapBufferMode();
}
@Override
public final GLDrawable getDelegatedDrawable() {
return drawable;
}
@Override
public GLContext getContext() {
return context;
}
@Override
public int getContextCreationFlags() {
return additionalCtxCreationFlags;
}
@Override
public GL getGL() {
final GLContext _context = context;
return (null == _context) ? null : _context.getGL();
}
@Override
public boolean invoke(final boolean wait, final GLRunnable runnable) throws IllegalStateException {
return helper.invoke(this, wait, runnable);
}
@Override
public boolean invoke(final boolean wait, final List runnables) throws IllegalStateException {
return helper.invoke(this, wait, runnables);
}
@Override
public void flushGLRunnables() {
helper.flushGLRunnables();
}
@Override
public void setAnimator(final GLAnimatorControl arg0) throws GLException {
helper.setAnimator(arg0);
}
@Override
public void setAutoSwapBufferMode(final boolean arg0) {
helper.setAutoSwapBufferMode(arg0);
}
@Override
public GLContext setContext(final GLContext newCtx, final 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) {
additionalCtxCreationFlags = arg0;
final GLContext _context = context;
if(null != _context) {
_context.setContextCreationFlags(additionalCtxCreationFlags);
}
}
@Override
public GL setGL(final GL arg0) {
final GLContext _context = context;
if (null != _context) {
_context.setGL(arg0);
return arg0;
}
return null;
}
@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() {
final GLDrawable _drawable = drawable;
return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getChosenGLCapabilities() : null;
}
@Override
public GLCapabilitiesImmutable getRequestedGLCapabilities() {
final GLDrawable _drawable = drawable;
return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getRequestedGLCapabilities() : null;
}
@Override
public GLDrawableFactory getFactory() {
final GLDrawable _drawable = drawable;
return (_drawable != null) ? _drawable.getFactory() : null;
}
@Override
public GLProfile getGLProfile() {
return capsRequested.getGLProfile();
}
@Override
public long getHandle() {
final GLDrawable _drawable = drawable;
return (_drawable != null) ? _drawable.getHandle() : 0;
}
@Override
public NativeSurface getNativeSurface() {
final GLDrawable _drawable = drawable;
return (_drawable != null) ? _drawable.getNativeSurface() : null;
}
@Override
public boolean isRealized() {
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(swapBuffersOnGLAction);
}
/**
* {@inheritDoc}
*
* Implementation always supports multithreading, hence method always returns true
.
*
*/
@Override
public final boolean isThreadGLCapable() { return true; }
/**
* Runs the specified action in an SWT compatible thread, which is:
*
* - Mac OSX
*
* - Linux, Windows, ..
*
*
* 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()
*/
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();
}
private void runOnEDTIfAvail(final 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);
}
}
@Override
public String toString() {
final GLDrawable _drawable = drawable;
final int dw = (null!=_drawable) ? _drawable.getSurfaceWidth() : -1;
final int dh = (null!=_drawable) ? _drawable.getSurfaceHeight() : -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 "+getSurfaceWidth()+"x"+getSurfaceHeight()+"]";
}
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, 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);
canvas.addGLEventListener(new GLEventListener() {
@Override
public void init(final GLAutoDrawable drawable) {
final GL gl = drawable.getGL();
System.err.println(JoglVersion.getGLInfo(gl, null));
}
@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) {}
@Override
public void dispose(final GLAutoDrawable drawable) {}
});
shell.open();
canvas.display();
display.dispose();
}
}