Jogamp
NewtVersionActivity: Remove 'gears' test; Version Info: Drop NativeWindow/Newt Versio...
[jogl.git] / src / jogl / classes / javax / media / opengl / awt / GLCanvas.java
index 2dafd69..d861bd7 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (c) 2010 JogAmp Community. All rights reserved.
  * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
 
 package javax.media.opengl.awt;
 
-import javax.media.opengl.*;
-import javax.media.nativewindow.*;
-import javax.media.nativewindow.awt.*;
-
-import com.jogamp.opengl.impl.*;
-import com.jogamp.nativewindow.impl.jawt.JAWTUtil;
+import java.beans.Beans;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 
 import java.awt.Canvas;
 import java.awt.Color;
-import java.awt.Component;
 import java.awt.FontMetrics;
+import java.awt.Frame;
 import java.awt.Graphics;
 import java.awt.GraphicsConfiguration;
 import java.awt.GraphicsDevice;
-import java.awt.Container;
-import java.awt.Window;
-import java.awt.event.WindowEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.geom.*;
-import java.beans.*;
-import java.lang.reflect.*;
-import java.security.*;
+import java.awt.geom.Rectangle2D;
+
+import java.awt.EventQueue;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.media.nativewindow.WindowClosingProtocol;
+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.NativeWindowFactory;
+import javax.media.nativewindow.awt.AWTGraphicsConfiguration;
+import javax.media.nativewindow.awt.AWTGraphicsDevice;
+import javax.media.nativewindow.awt.AWTGraphicsScreen;
+import javax.media.nativewindow.awt.AWTWindowClosingProtocol;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GLAnimatorControl;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilities;
+import javax.media.opengl.GLCapabilitiesChooser;
+import javax.media.opengl.GLCapabilitiesImmutable;
+import javax.media.opengl.GLContext;
+import javax.media.opengl.GLDrawable;
+import javax.media.opengl.GLDrawableFactory;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLException;
+import javax.media.opengl.GLProfile;
+import javax.media.opengl.GLRunnable;
+import javax.media.opengl.Threading;
+
+import com.jogamp.nativewindow.NativeWindowVersion;
+import com.jogamp.common.GlueGenVersion;
+import com.jogamp.common.util.VersionUtil;
+import com.jogamp.opengl.JoglVersion;
+
+import com.jogamp.common.util.locks.RecursiveLock;
+import jogamp.opengl.Debug;
+import jogamp.opengl.GLContextImpl;
+import jogamp.opengl.GLDrawableHelper;
+import jogamp.opengl.ThreadingImpl;
 
 // FIXME: Subclasses need to call resetGLFunctionAvailability() on their
 // context whenever the displayChanged() function is called on our
 // GLEventListeners
 
 /** A heavyweight AWT component which provides OpenGL rendering
-    support. This is the primary implementation of {@link GLDrawable};
+    support. This is the primary implementation of an AWT {@link GLDrawable};
     {@link GLJPanel} is provided for compatibility with Swing user
     interfaces when adding a heavyweight doesn't work either because
-    of Z-ordering or LayoutManager problems. */
+    of Z-ordering or LayoutManager problems.
+ *
+ * <h5><A NAME="java2dgl">Java2D OpenGL Remarks</A></h5>
+ *
+ * To avoid any conflicts with a potential Java2D OpenGL context,<br>
+ * you shall consider setting the following JVM properties:<br>
+ * <ul>
+ *    <li><pre>sun.java2d.opengl=false</pre></li>
+ *    <li><pre>sun.java2d.noddraw=true</pre></li>
+ * </ul>
+ * This is especially true in case you want to utilize a GLProfile other than
+ * {@link GLProfile#GL2}, eg. using {@link GLProfile#getMaxFixedFunc()}.<br>
+ * On the other hand, if you like to experiment with GLJPanel's utilization
+ * of Java2D's OpenGL pipeline, you have to set them to
+ * <ul>
+ *    <li><pre>sun.java2d.opengl=true</pre></li>
+ *    <li><pre>sun.java2d.noddraw=true</pre></li>
+ * </ul>
+ *
+ * <h5><A NAME="backgrounderase">Disable Background Erase</A></h5>
+ *
+ * GLCanvas tries to disable background erase for the AWT Canvas
+ * before native peer creation (X11) and after it (Windows), <br>
+ * utilizing the optional {@link java.awt.Toolkit} method <code>disableBeackgroundErase(java.awt.Canvas)</code>.<br>
+ * However if this does not give you the desired results, you may want to disable AWT background erase in general:
+ * <ul>
+ *   <li><pre>sun.awt.noerasebackground=true</pre></li>
+ * </ul>
+ */
 
-public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
+public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol {
 
   private static final boolean DEBUG;
-  private static final GLProfile defaultGLProfile;
 
   static {
-      NativeWindowFactory.initSingleton();
-      defaultGLProfile = GLProfile.getDefault();
       DEBUG = Debug.debug("GLCanvas");
   }
 
-  private GLProfile glProfile;
   private GLDrawableHelper drawableHelper = new GLDrawableHelper();
-  private GraphicsConfiguration chosen;
   private AWTGraphicsConfiguration awtConfig;
   private GLDrawable drawable;
   private GLContextImpl context;
-  private boolean autoSwapBufferMode = true;
   private boolean sendReshape = false;
   
-  // copy of the cstr args ..
-  private GLCapabilities capabilities;
+  // copy of the cstr args, mainly for recreation
+  private GLCapabilitiesImmutable capsReqUser;
   private GLCapabilitiesChooser chooser;
   private GLContext shareWith;
+  private int additionalCtxCreationFlags = 0;  
   private GraphicsDevice device;
 
+  private AWTWindowClosingProtocol awtWindowClosingProtocol =
+          new AWTWindowClosingProtocol(this, new Runnable() {
+                public void run() {
+                    GLCanvas.this.destroy();
+                }
+            });
+
   /** Creates a new GLCanvas component with a default set of OpenGL
       capabilities, using the default OpenGL capabilities selection
-      mechanism, on the default screen device. */
-  public GLCanvas() {
+      mechanism, on the default screen device. 
+   * @throws GLException if no default profile is available for the default desktop device.
+   */
+  public GLCanvas() throws GLException {
     this(null);
   }
 
   /** Creates a new GLCanvas component with the requested set of
       OpenGL capabilities, using the default OpenGL capabilities
-      selection mechanism, on the default screen device. */
-  public GLCanvas(GLCapabilities capabilities) {
-    this(capabilities, null, null, null);
+      selection mechanism, on the default screen device. 
+   * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
+   * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
+   */
+  public GLCanvas(GLCapabilitiesImmutable capsReqUser) throws GLException {
+    this(capsReqUser, null, null, null);
+  }
+
+  /** Creates a new GLCanvas component with the requested set of
+      OpenGL capabilities, using the default OpenGL capabilities
+      selection mechanism, on the default screen device.
+   *  This constructor variant also supports using a shared GLContext.
+   *
+   * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
+   * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
+   */
+  public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLContext shareWith) 
+          throws GLException 
+  {
+    this(capsReqUser, null, shareWith, null);
   }
 
   /** Creates a new GLCanvas component. The passed GLCapabilities
@@ -126,61 +210,50 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
       sharing</a>. The passed GraphicsDevice indicates the screen on
       which to create the GLCanvas; the GLDrawableFactory uses the
       default screen device of the local GraphicsEnvironment if null
-      is passed for this argument. */
-  public GLCanvas(GLCapabilities capabilities,
+      is passed for this argument. 
+   * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
+   */
+  public GLCanvas(GLCapabilitiesImmutable capsReqUser,
                   GLCapabilitiesChooser chooser,
                   GLContext shareWith,
-                  GraphicsDevice device) {
+                  GraphicsDevice device) 
+      throws GLException 
+  {
     /*
-     * Workaround for Xinerama, always pass null so we can detect whether
-     * super.getGraphicsConfiguration() is returning the Canvas' GC (null),
-     * or an ancestor component's GC (non-null) in the overridden version
-     * below.
+     * Determination of the native window is made in 'super.addNotify()',
+     * which creates the native peer using AWT's GraphicsConfiguration.
+     * GraphicsConfiguration is returned by this class overwritten
+     * 'getGraphicsConfiguration()', which returns our OpenGL compatible
+     * 'chosen' GraphicsConfiguration.
      */
     super();
 
-    if(null==capabilities) {
-        capabilities = new GLCapabilities(defaultGLProfile);
+    if(null==capsReqUser) {
+        capsReqUser = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDesktopDevice()));
+    } else {
+        // don't allow the user to change data
+        capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable();
     }
-    glProfile = capabilities.getGLProfile();
-
-    this.capabilities = capabilities;
-    this.chooser = chooser;
-    this.shareWith=shareWith;
-    this.device = device;
-  }
 
-  protected interface DestroyMethod {
-    public void destroyMethod();
-  }
-
-  protected final static Object  addClosingListener(Component c, final DestroyMethod d) {
-    WindowAdapter cl = null;
-    Window w = getWindow(c);
-    if(null!=w) {
-        cl = new WindowAdapter() {
-                public void windowClosing(WindowEvent e) {
-                  // we have to issue this call rigth away,
-                  // otherwise the window gets destroyed
-                  d.destroyMethod();
-                }
-            };
-        w.addWindowListener(cl);
+    if(null==device) {
+        GraphicsConfiguration gc = super.getGraphicsConfiguration();
+        if(null!=gc) {
+            device = gc.getDevice();
+        }
     }
-    return cl;
-  }
 
-  protected final static Window getWindow(Component c) {
-    while ( c!=null && ! ( c instanceof Window ) ) {
-        c = c.getParent();
-    }
-    return (Window)c;
+    // instantiation will be issued in addNotify()
+    this.capsReqUser = capsReqUser;
+    this.chooser = chooser;
+    this.shareWith = shareWith;
+    this.device = device;
   }
 
   /**
    * Overridden to choose a GraphicsConfiguration on a parent container's
    * GraphicsDevice because both devices
    */
+    @Override
   public GraphicsConfiguration getGraphicsConfiguration() {
     /*
      * Workaround for problems with Xinerama and java.awt.Component.checkGD
@@ -205,6 +278,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
      * otherwise it is from an ancestor component that this Canvas is being
      * added to, and we go into this block.
      */
+    GraphicsConfiguration chosen =  awtConfig.getGraphicsConfiguration();
+
     if (gc != null && chosen != null && !chosen.equals(gc)) {
       /*
        * Check for compatibility with gc. If they differ by only the
@@ -229,11 +304,13 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
          * block, both devices should have the same visual list, and the
          * same configuration should be selected here.
          */
-        AWTGraphicsConfiguration config = chooseGraphicsConfiguration((GLCapabilities)awtConfig.getRequestedCapabilities(), chooser, gc.getDevice());
+        AWTGraphicsConfiguration config = chooseGraphicsConfiguration( (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(),
+                                                                       (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(),
+                                                                       chooser, gc.getDevice());
         final GraphicsConfiguration compatible = (null!=config)?config.getGraphicsConfiguration():null;
         boolean equalCaps = config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities());
         if(DEBUG) {
-            Exception e = new Exception("Call Stack: "+Thread.currentThread().getName());
+            Exception e = new Exception("Info: Call Stack: "+Thread.currentThread().getName());
             e.printStackTrace();
             System.err.println("!!! Created Config (n): HAVE    GC "+chosen);
             System.err.println("!!! Created Config (n): THIS    GC "+gc);
@@ -284,61 +361,100 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
   }
   
   public GLContext createContext(GLContext shareWith) {
-    return drawable.createContext(shareWith);
+      drawableSync.lock();
+      try {
+        return (null != drawable) ? drawable.createContext(shareWith) : null;
+      } finally {
+        drawableSync.unlock();
+      }
   }
 
   public void setRealized(boolean realized) {
   }
 
   public boolean isRealized() {
-    return ( null != drawable ) ? drawable.isRealized() : false;
+      drawableSync.lock();
+      try {
+        return ( null != drawable ) ? drawable.isRealized() : false;
+      } finally {
+        drawableSync.unlock();
+      }
   }
 
-  private Object closingListener = null;
-  private Object closingListenerLock = new Object();
+  public int getDefaultCloseOperation() {
+      return awtWindowClosingProtocol.getDefaultCloseOperation();
+  }
+
+  public int setDefaultCloseOperation(int op) {
+      return awtWindowClosingProtocol.setDefaultCloseOperation(op);
+  }
 
   public void display() {
-    maybeDoSingleThreadedWorkaround(displayOnEventDispatchThreadAction,
-                                    displayAction);
-    if(null==closingListener) {
-      synchronized(closingListenerLock) {
-        if(null==closingListener) {
-            closingListener=addClosingListener(this, new DestroyMethod() { 
-                        public void destroyMethod() { destroy(); } });
+    if( !validateGLDrawable() ) {
+        if(DEBUG) {
+            System.err.println("Info: GLCanvas display - skipped GL render, drawable not valid yet");
         }
-      }
+        return; // not yet available ..
     }
+    maybeDoSingleThreadedWorkaround(displayOnEventDispatchThreadAction,
+                                    displayAction);
+
+    awtWindowClosingProtocol.addClosingListenerOneShot();
   }
 
-  protected void dispose(boolean regenerate) {
-    if(DEBUG) {
-        Exception ex1 = new Exception("dispose("+regenerate+") - start");
-        ex1.printStackTrace();
-    }
-    disposeRegenerate=regenerate;
+  private void dispose(boolean regenerate) {
+    drawableSync.lock();
+    try {
+        if(DEBUG) {
+            Exception ex1 = new Exception("Info: dispose("+regenerate+") - start, hasContext " +
+                    (null!=context) + ", hasDrawable " + (null!=drawable));
+            ex1.printStackTrace();
+        }
 
-    if (Threading.isSingleThreaded() &&
-        !Threading.isOpenGLThread()) {
-      // Workaround for termination issues with applets --
-      // sun.applet.AppletPanel should probably be performing the
-      // remove() call on the EDT rather than on its own thread
-      if (ThreadingImpl.isAWTMode() &&
-          Thread.holdsLock(getTreeLock())) {
-        // The user really should not be invoking remove() from this
-        // thread -- but since he/she is, we can not go over to the
-        // EDT at this point. Try to destroy the context from here.
-        if(context.isCreated()) {
-            drawableHelper.invokeGL(drawable, context, disposeAction, null);
+        if(null!=context) {
+            boolean animatorPaused = false;
+            GLAnimatorControl animator =  getAnimator();
+            if(null!=animator) {
+                // can't remove us from animator for recreational addNotify()
+                animatorPaused = animator.pause();
+            }
+
+            disposeRegenerate=regenerate;
+
+            if (Threading.isSingleThreaded() &&
+                !Threading.isOpenGLThread()) {
+              // Workaround for termination issues with applets --
+              // sun.applet.AppletPanel should probably be performing the
+              // remove() call on the EDT rather than on its own thread
+              // Hint: User should run remove from EDT.
+              if (ThreadingImpl.isAWTMode() &&
+                  Thread.holdsLock(getTreeLock())) {
+                // The user really should not be invoking remove() from this
+                // thread -- but since he/she is, we can not go over to the
+                // EDT at this point. Try to destroy the context from here.
+                if(context.isCreated()) {
+                    drawableHelper.invokeGL(drawable, context, disposeAction, null);
+                }
+              } else if(context.isCreated()) {
+                Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction);
+              }
+            } else if(context.isCreated()) {
+              drawableHelper.invokeGL(drawable, context, disposeAction, null);
+            }
+
+            if(animatorPaused) {
+                animator.resume();
+            }
+        }
+        if(!regenerate) {
+            disposeAbstractGraphicsDevice();
         }
-      } else if(context.isCreated()) {
-        Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction);
-      }
-    } else if(context.isCreated()) {
-      drawableHelper.invokeGL(drawable, context, disposeAction, null);
-    }
 
-    if(DEBUG) {
-        System.err.println("dispose("+regenerate+") - stop");
+        if(DEBUG) {
+            System.err.println("dispose("+regenerate+") - stop");
+        }
+    } finally {
+        drawableSync.unlock();
     }
   }
 
@@ -356,6 +472,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
 
       <B>Overrides:</B>
       <DL><DD><CODE>paint</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+    @Override
   public void paint(Graphics g) {
     if (Beans.isDesignTime()) {
       // Make GLCanvas behave better in NetBeans GUI builder
@@ -377,10 +494,13 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
                    (int) ((getHeight() + bounds.getHeight()) / 2));
       return;
     }
-
-    display();
+    if( ! this.drawableHelper.isExternalAnimatorAnimating() ) {
+        display();
+    }
   }
 
+  RecursiveLock drawableSync = new RecursiveLock();
+
   /** Overridden to track when this component is added to a container.
       Subclasses which override this method must call
       super.addNotify() in their addNotify() method in order to
@@ -388,75 +508,117 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
 
       <B>Overrides:</B>
       <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+    @Override
   public void addNotify() {
-    super.addNotify();
-    if (!Beans.isDesignTime()) {
-        disableBackgroundErase();
-
-        if(null==device) {
-            GraphicsConfiguration gc = super.getGraphicsConfiguration();
-            if(null!=gc) {
-                device = gc.getDevice();
-            }
-        }
+    if(DEBUG) {
+        Exception ex1 = new Exception(Thread.currentThread().getName()+" - Info: addNotify - start, bounds: "+this.getBounds());
+        ex1.printStackTrace();
+    }
 
-        /*
-         * Save the chosen capabilities for use in getGraphicsConfiguration().
+    drawableSync.lock();
+    try {
+        /**
+         * 'super.addNotify()' determines the GraphicsConfiguration,
+         * while calling this class's overriden 'getGraphicsConfiguration()' method
+         * after which it creates the native peer.
+         * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration
+         * is being used in getGraphicsConfiguration().
+         * This code order also allows recreation, ie re-adding the GLCanvas.
          */
-        JAWTUtil.lockToolkit();
-        try {
-            awtConfig = chooseGraphicsConfiguration(capabilities, chooser, device);
-            if(DEBUG) {
-                Exception e = new Exception("Created Config: "+awtConfig);
-                e.printStackTrace();
-            }
-            if(null!=awtConfig) {
-              // update ..
-              chosen = awtConfig.getGraphicsConfiguration();
+        awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device);
+        if(null==awtConfig) {
+            throw new GLException("Error: NULL AWTGraphicsConfiguration");
+        }
 
-            }
-            if(null==awtConfig) {
-              throw new GLException("Error: AWTGraphicsConfiguration is null");
-            }
-            drawable = GLDrawableFactory.getFactory(glProfile).createGLDrawable(NativeWindowFactory.getNativeWindow(this, awtConfig));
+        if (!Beans.isDesignTime()) {
+            // no lock required, since this resource ain't available yet
+            drawable = GLDrawableFactory.getFactory(capsReqUser.getGLProfile())
+                           .createGLDrawable(NativeWindowFactory.getNativeWindow(this, awtConfig));
             context = (GLContextImpl) drawable.createContext(shareWith);
             context.setSynchronized(true);
-        } finally {
-            JAWTUtil.unlockToolkit();
+            context.setContextCreationFlags(additionalCtxCreationFlags);            
         }
 
+        // before native peer is valid: X11
+        disableBackgroundErase();
+
+        // issues getGraphicsConfiguration() and creates the native peer
+        super.addNotify();
+
+        // after native peer is valid: Windows
+        disableBackgroundErase();
+
+        // init drawable by paint/display makes the init sequence more equal
+        // for all launch flavors (applet/javaws/..)
+        // validateGLDrawable();
+
         if(DEBUG) {
-            System.err.println("Created Drawable: "+drawable);
+            System.err.println(Thread.currentThread().getName()+" - Info: addNotify - end: peer: "+getPeer());
         }
-        drawable.setRealized(true);
+    } finally {
+       drawableSync.unlock();
     }
   }
 
-  /** Overridden to track when this component is removed from a
+  private boolean validateGLDrawable() {
+    boolean realized = false;
+    if (!Beans.isDesignTime()) {
+        drawableSync.lock();
+        try {
+            if ( null != drawable ) {
+                realized = drawable.isRealized();
+                if ( !realized && 0 < drawable.getWidth() * drawable.getHeight() ) {
+                    drawable.setRealized(true);
+                    realized = true;
+                    sendReshape=true; // ensure a reshape is being send ..
+                    if(DEBUG) {
+                        String msg = Thread.currentThread().getName()+" - Realized Drawable: "+drawable.toString();
+                        // System.err.println(msg);
+                        Throwable t = new Throwable(msg);
+                        t.printStackTrace();
+                    }
+                }
+            }
+        } finally {
+           drawableSync.unlock();
+        }
+    }
+    return realized;
+  }
+
+  /** <p>Overridden to track when this component is removed from a
       container. Subclasses which override this method must call
       super.removeNotify() in their removeNotify() method in order to
-      function properly. <P>
-
+      function properly. </p>
+      <p>User shall not call this method outside of EDT, read the AWT/Swing specs
+      about this.</p>
       <B>Overrides:</B>
       <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+    @Override
   public void removeNotify() {
     if(DEBUG) {
-        Exception ex1 = new Exception("removeNotify - start");
+        Exception ex1 = new Exception(Thread.currentThread().getName()+" - Info: removeNotify - start");
         ex1.printStackTrace();
     }
 
+    awtWindowClosingProtocol.removeClosingListener();
+
     if (Beans.isDesignTime()) {
       super.removeNotify();
     } else {
+      drawableSync.lock();
       try {
         dispose(false);
       } finally {
+        context=null;
         drawable=null;
+        awtConfig=null;
         super.removeNotify();
+        drawableSync.unlock();
       }
     }
     if(DEBUG) {
-        System.out.println("removeNotify - end");
+        System.err.println(Thread.currentThread().getName()+" - Info: removeNotify - end, peer: "+getPeer());
     }
   }
 
@@ -467,6 +629,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
 
       <B>Overrides:</B>
       <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+    @Override
   public void reshape(int x, int y, int width, int height) {
     super.reshape(x, y, width, height);
     sendReshape = true;
@@ -474,8 +637,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
 
   /** <B>Overrides:</B>
       <DL><DD><CODE>update</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
-  // Overridden from Canvas to prevent the AWT's clearing of the
-  // canvas from interfering with the OpenGL rendering.
+  /** 
+   * Overridden from Canvas to prevent the AWT's clearing of the
+   * canvas from interfering with the OpenGL rendering.
+   */
+    @Override
   public void update(Graphics g) {
     paint(g);
   }
@@ -492,12 +658,23 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
     drawableHelper.removeGLEventListener(listener);
   }
 
+  public void setAnimator(GLAnimatorControl animatorControl) {
+    drawableHelper.setAnimator(animatorControl);
+  }
+
+  public GLAnimatorControl getAnimator() {
+    return drawableHelper.getAnimator();
+  }
+
   public void invoke(boolean wait, GLRunnable glRunnable) {
-    drawableHelper.invoke(wait, glRunnable);
+    drawableHelper.invoke(this, wait, glRunnable);
   }
 
   public void setContext(GLContext ctx) {
     context=(GLContextImpl)ctx;
+    if(null != context) {
+        context.setContextCreationFlags(additionalCtxCreationFlags);
+    }
   }
 
   public GLContext getContext() {
@@ -508,14 +685,14 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
     if (Beans.isDesignTime()) {
       return null;
     }
-    GLContext context = getContext();
-    return (context == null) ? null : context.getGL();
+    GLContext ctx = getContext();
+    return (ctx == null) ? null : ctx.getGL();
   }
 
   public GL setGL(GL gl) {
-    GLContext context = getContext();
-    if (context != null) {
-      context.setGL(gl);
+    GLContext ctx = getContext();
+    if (ctx != null) {
+      ctx.setGL(gl);
       return gl;
     }
     return null;
@@ -534,42 +711,77 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
     maybeDoSingleThreadedWorkaround(swapBuffersOnEventDispatchThreadAction, swapBuffersAction);
   }
 
+  public void setContextCreationFlags(int flags) {
+    additionalCtxCreationFlags = flags;
+  }
+      
+  public int getContextCreationFlags() {
+    return additionalCtxCreationFlags;                
+  }
+          
   public GLProfile getGLProfile() {
-    return glProfile;
+    return capsReqUser.getGLProfile();
   }
 
-  public GLCapabilities getChosenGLCapabilities() {
+  public GLCapabilitiesImmutable getChosenGLCapabilities() {
     if (awtConfig == null) {
         throw new GLException("No AWTGraphicsConfiguration: "+this);
     }
 
-    return (GLCapabilities)awtConfig.getChosenCapabilities();
+    return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities();
   }
 
-  public GLCapabilities getRequestedGLCapabilities() {
+  public GLCapabilitiesImmutable getRequestedGLCapabilities() {
     if (awtConfig == null) {
-        throw new GLException("No AWTGraphicsConfiguration: "+this);
+        return capsReqUser;
     }
 
-    return (GLCapabilities)awtConfig.getRequestedCapabilities();
+    return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities();
   }
 
-  public NativeWindow getNativeWindow() {
-    return drawable.getNativeWindow();
+  public NativeSurface getNativeSurface() {
+      drawableSync.lock();
+      try {
+        return (null != drawable) ? drawable.getNativeSurface() : null;
+      } finally {
+        drawableSync.unlock();
+      }
   }
 
   public long getHandle() {
-    return drawable.getHandle();
+      drawableSync.lock();
+      try {
+        return (null != drawable) ? drawable.getHandle() : 0;
+      } finally {
+        drawableSync.unlock();
+      }
   }
 
   public GLDrawableFactory getFactory() {
-    return drawable.getFactory();
+      drawableSync.lock();
+      try {
+        return (null != drawable) ? drawable.getFactory() : null;
+      } finally {
+        drawableSync.unlock();
+      }
   }
 
+  @Override
   public String toString() {
-    return "AWT-GLCanvas[ "+awtConfig+", "+((null!=drawable)?drawable.getClass().getName():"null-drawable")+", "+drawableHelper+"]";
+    final int dw = (null!=drawable) ? drawable.getWidth() : -1;
+    final int dh = (null!=drawable) ? drawable.getHeight() : -1;
+    
+    return "AWT-GLCanvas[Realized "+isRealized()+
+                          ",\n\t"+((null!=drawable)?drawable.getClass().getName():"null-drawable")+                         
+                          ",\n\tRealized "+isRealized()+
+                          ",\n\tFactory   "+getFactory()+
+                          ",\n\thandle    0x"+Long.toHexString(getHandle())+
+                          ",\n\tDrawable size "+dw+"x"+dh+
+                          ",\n\tAWT pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+
+                          ",\n\tvisible "+isVisible()+
+                          ",\n\t"+awtConfig+"]";
   }
-
+  
   //----------------------------------------------------------------------
   // Internals only below this point
   //
@@ -584,14 +796,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
     }
   }
 
-  private boolean disposeRegenerate;
-  private DisposeAction disposeAction = new DisposeAction(this);
-
   class DisposeAction implements Runnable {
-    private GLCanvas canvas;
-    public DisposeAction(GLCanvas canvas) {
-        this.canvas = canvas;
-    }
     public void run() {
       drawableHelper.dispose(GLCanvas.this);
 
@@ -603,11 +808,13 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
 
       if(null!=drawable) {
           drawable.setRealized(false);
+          drawable=null;
       }
 
       if(disposeRegenerate) {
           // recreate GLDrawable to reflect it's new graphics configuration
-          drawable = GLDrawableFactory.getFactory(glProfile).createGLDrawable(NativeWindowFactory.getNativeWindow(canvas, awtConfig));
+          drawable = GLDrawableFactory.getFactory(capsReqUser.getGLProfile())
+                        .createGLDrawable(NativeWindowFactory.getNativeWindow(GLCanvas.this, awtConfig));
           if(DEBUG) {
             System.err.println("GLCanvas.dispose(true): new drawable: "+drawable);
           }
@@ -618,6 +825,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
       }
     }
   }
+  private boolean disposeRegenerate;
+  private DisposeAction disposeAction = new DisposeAction();
 
   private DisposeOnEventDispatchThreadAction disposeOnEventDispatchThreadAction =
     new DisposeOnEventDispatchThreadAction();
@@ -628,6 +837,46 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
     }
   }
 
+  class DisposeAbstractGraphicsDeviceAction implements Runnable {
+    public void run() {
+      AbstractGraphicsConfiguration aconfig = (null!=awtConfig) ? awtConfig.getNativeGraphicsConfiguration() : null;
+      AbstractGraphicsScreen ascreen = (null!=aconfig) ? aconfig.getScreen() : null;
+      AbstractGraphicsDevice adevice = (null!=ascreen) ? ascreen.getDevice() : null;
+      if(null!=adevice) {
+          String adeviceMsg=null;
+          if(DEBUG) {
+            adeviceMsg = adevice.toString();
+          }
+          boolean closed = adevice.close();
+          if(DEBUG) {
+            System.err.println(Thread.currentThread().getName() + " - GLCanvas.dispose(false): closed GraphicsDevice: "+adeviceMsg+", result: "+closed);
+          }
+      }
+      awtConfig=null;
+    }
+  }
+  private DisposeAbstractGraphicsDeviceAction disposeAbstractGraphicsDeviceAction = new DisposeAbstractGraphicsDeviceAction();
+
+  /**
+   * Disposes the AbstractGraphicsDevice within EDT,
+   * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
+   *
+   * @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice)
+   */
+  void disposeAbstractGraphicsDevice()  {
+    if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
+        disposeAbstractGraphicsDeviceAction.run();
+    } else {
+        try {
+            EventQueue.invokeAndWait(disposeAbstractGraphicsDeviceAction);
+        } catch (InvocationTargetException e) {
+            throw new GLException(e.getTargetException());
+        } catch (InterruptedException e) {
+            throw new GLException(e);
+        }
+    }
+  }
+
   class InitAction implements Runnable {
     public void run() {
       drawableHelper.init(GLCanvas.this);
@@ -638,12 +887,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
   class DisplayAction implements Runnable {
     public void run() {
       if (sendReshape) {
+        if(DEBUG) {
+            System.err.println(Thread.currentThread().getName()+" - reshape: "+getWidth()+"x"+getHeight());
+        }
         // Note: we ignore the given x and y within the parent component
         // since we are drawing directly into this heavyweight component.
-        int width = getWidth();
-        int height = getHeight();
-        getGL().glViewport(0, 0, width, height);
-        drawableHelper.reshape(GLCanvas.this, 0, 0, width, height);
+        drawableHelper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight());
         sendReshape = false;
       }
 
@@ -709,34 +958,141 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
       } catch (Exception e) {
       }
       disableBackgroundEraseInitialized = true;
+      if(DEBUG) {
+        System.err.println("GLCanvas: TK disableBackgroundErase method found: "+
+                (null!=disableBackgroundEraseMethod));
+      }
     }
     if (disableBackgroundEraseMethod != null) {
+      Throwable t=null;
       try {
         disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this });
       } catch (Exception e) {
-        // FIXME: workaround for 6504460 (incorrect backport of 6333613 in 5.0u10)
-        // throw new GLException(e);
+        t = e;
+      }
+      if(DEBUG) {
+        System.err.println("GLCanvas: TK disableBackgroundErase error: "+t);
       }
     }
   }
 
-  private static AWTGraphicsConfiguration chooseGraphicsConfiguration(GLCapabilities capabilities,
-                                                                      GLCapabilitiesChooser chooser,
-                                                                      GraphicsDevice device) {
+  /**
+   * Issues the GraphicsConfigurationFactory's choosing facility within EDT,
+   * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
+   *
+   * @param capsChosen
+   * @param capsRequested
+   * @param chooser
+   * @param device
+   * @return the chosen AWTGraphicsConfiguration
+   *
+   * @see #disposeAbstractGraphicsDevice()
+   */
+  private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen,
+                                                               final GLCapabilitiesImmutable capsRequested,
+                                                               final GLCapabilitiesChooser chooser,
+                                                               final GraphicsDevice device) {
     // Make GLCanvas behave better in NetBeans GUI builder
     if (Beans.isDesignTime()) {
       return null;
     }
 
-    AbstractGraphicsScreen aScreen = AWTGraphicsScreen.createScreenDevice(device);
-    AWTGraphicsConfiguration config = (AWTGraphicsConfiguration)
-      GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capabilities,
-                                                                                                   chooser,
-                                                                                                   aScreen);
+    final AbstractGraphicsScreen aScreen = AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT);
+    AWTGraphicsConfiguration config = null;
+
+    if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
+        config = (AWTGraphicsConfiguration)
+                GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen,
+                                                                                                             capsRequested,
+                                                                                                             chooser, aScreen);
+    } else {
+        try {
+            final ArrayList bucket = new ArrayList(1);
+            EventQueue.invokeAndWait(new Runnable() {
+                public void run() {
+                    AWTGraphicsConfiguration c = (AWTGraphicsConfiguration)
+                            GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen,
+                                                                                                                         capsRequested,
+                                                                                                                         chooser, aScreen);
+                    bucket.add(c);
+                }
+            });
+            config = ( bucket.size() > 0 ) ? (AWTGraphicsConfiguration)bucket.get(0) : null ;
+        } catch (InvocationTargetException e) {
+            throw new GLException(e.getTargetException());
+        } catch (InterruptedException e) {
+            throw new GLException(e);
+        }
+    }
+
     if (config == null) {
       throw new GLException("Error: Couldn't fetch AWTGraphicsConfiguration");
     }
 
     return config;
   }
+  
+  /**
+   * A most simple JOGL AWT test entry
+   */
+  public static void main(String args[]) {
+    System.err.println(VersionUtil.getPlatformInfo());
+    System.err.println(GlueGenVersion.getInstance());
+    // System.err.println(NativeWindowVersion.getInstance());
+    System.err.println(JoglVersion.getInstance());
+
+    GLProfile.initSingleton(false);
+    GLDrawableFactory factory = GLDrawableFactory.getDesktopFactory();
+    List/*<GLCapabilitiesImmutable>*/ availCaps = factory.getAvailableCapabilities(null);
+    for(int i=0; i<availCaps.size(); i++) {
+        System.err.println(availCaps.get(i));
+    }
+
+    GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDesktopDevice()) );
+    Frame frame = new Frame("JOGL AWT Test");
+
+    GLCanvas glCanvas = new GLCanvas(caps);
+    frame.add(glCanvas);
+    frame.setSize(128, 128);
+
+    glCanvas.addGLEventListener(new GLEventListener() {
+        public void init(GLAutoDrawable drawable) {
+            GL gl = drawable.getGL();
+            System.err.println(JoglVersion.getGLInfo(gl, null));
+        }
+
+        public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
+        }
+
+        public void display(GLAutoDrawable drawable) {
+        }
+
+        public void dispose(GLAutoDrawable drawable) {
+        }
+    });
+
+    final Frame _frame = frame;
+    final GLCanvas _glCanvas = glCanvas;
+
+    try {
+        javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+            public void run() {
+                _frame.setVisible(true);
+            }});
+    } catch (Throwable t) {
+        t.printStackTrace();
+    }
+    glCanvas.display();
+    try {
+        javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+            public void run() {
+                _frame.setVisible(false);
+                _frame.remove(_glCanvas);
+                _frame.dispose();
+            }});
+    } catch (Throwable t) {
+        t.printStackTrace();
+    }
+  }
+
 }
http://JogAmp.org git info: FAQ, tutorial and man pages.