From a959c53b7ac91e489bf0959391e892790b9ff248 Mon Sep 17 00:00:00 2001
From: Kenneth Russel <kbrussel@alum.mit.edu>
Date: Mon, 15 Jun 2009 22:57:38 +0000
Subject: Copied JOGL_2_SANDBOX r1957 on to trunk; JOGL_2_SANDBOX branch is now
 closed

git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@1959 232f8b59-042b-4e1e-8c03-345bb8c30851
---
 .../javax/media/opengl/awt/AWTGLAutoDrawable.java  |   52 +
 .../javax/media/opengl/awt/ComponentEvents.java    |   75 +
 .../classes/javax/media/opengl/awt/GLCanvas.java   |  710 +++++++++
 .../classes/javax/media/opengl/awt/GLJPanel.java   | 1582 ++++++++++++++++++++
 4 files changed, 2419 insertions(+)
 create mode 100644 src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java
 create mode 100644 src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java
 create mode 100644 src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
 create mode 100644 src/jogl/classes/javax/media/opengl/awt/GLJPanel.java

(limited to 'src/jogl/classes/javax/media/opengl/awt')

diff --git a/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java
new file mode 100644
index 000000000..d92cec389
--- /dev/null
+++ b/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution 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.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * 
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ * 
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.awt;
+
+import javax.media.opengl.*;
+import javax.media.opengl.glu.*;
+
+public interface AWTGLAutoDrawable extends GLAutoDrawable, ComponentEvents {
+  /** Requests a new width and height for this AWTGLAutoDrawable. */
+  public void setSize(int width, int height);
+
+  /** Schedules a repaint of the component at some point in the
+      future. */
+  public void repaint();
+}
diff --git a/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java b/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java
new file mode 100644
index 000000000..0c4f63c2d
--- /dev/null
+++ b/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution 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.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * 
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ * 
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.awt;
+
+import javax.media.opengl.*;
+import java.awt.event.*;
+import java.beans.PropertyChangeListener;
+
+/** Factors out the listener manipulation for the events supported by
+    all of the {@link GLDrawable} implementations. Provided to reduce
+    clutter in the documentation for GLDrawable. */
+
+public interface ComponentEvents {
+  public void addComponentListener(ComponentListener l);
+  public void removeComponentListener(ComponentListener l);
+  public void addFocusListener(FocusListener l);
+  public void removeFocusListener(FocusListener l);
+  public void addHierarchyBoundsListener(HierarchyBoundsListener l);
+  public void removeHierarchyBoundsListener(HierarchyBoundsListener l);
+  public void addHierarchyListener(HierarchyListener l);
+  public void removeHierarchyListener(HierarchyListener l);
+  public void addInputMethodListener(InputMethodListener l);
+  public void removeInputMethodListener(InputMethodListener l);
+  public void addKeyListener(KeyListener l);
+  public void removeKeyListener(KeyListener l);
+  public void addMouseListener(MouseListener l);
+  public void removeMouseListener(MouseListener l);
+  public void addMouseMotionListener(MouseMotionListener l);
+  public void removeMouseMotionListener(MouseMotionListener l);
+  public void addMouseWheelListener(MouseWheelListener l);
+  public void removeMouseWheelListener(MouseWheelListener l);
+  public void addPropertyChangeListener(PropertyChangeListener listener);
+  public void removePropertyChangeListener(PropertyChangeListener listener);
+  public void addPropertyChangeListener(String propertyName,
+                                        PropertyChangeListener listener);
+  public void removePropertyChangeListener(String propertyName,
+                                           PropertyChangeListener listener);
+}
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
new file mode 100644
index 000000000..4282e9985
--- /dev/null
+++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
@@ -0,0 +1,710 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution 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.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * 
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ * 
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.awt;
+
+import javax.media.opengl.*;
+import javax.media.nativewindow.*;
+import javax.media.nativewindow.awt.*;
+
+import com.sun.opengl.impl.*;
+
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.FontMetrics;
+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.*;
+
+// 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};
+    {@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. */
+
+public class GLCanvas extends Canvas implements AWTGLAutoDrawable {
+
+  private static final boolean DEBUG = Debug.debug("GLCanvas");
+
+  static private GLProfile defaultGLProfile = GLProfile.getDefault();
+  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;
+  private GLCapabilitiesChooser chooser;
+  private GLContext shareWith;
+  private GraphicsDevice device;
+
+  /** 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() {
+    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);
+  }
+
+  /** Creates a new GLCanvas component. The passed GLCapabilities
+      specifies the OpenGL capabilities for the component; if null, a
+      default set of capabilities is used. The GLCapabilitiesChooser
+      specifies the algorithm for selecting one of the available
+      GLCapabilities for the component; a DefaultGLCapabilitesChooser
+      is used if null is passed for this argument. The passed
+      GLContext specifies an OpenGL context with which to share
+      textures, display lists and other OpenGL state, and may be null
+      if sharing is not desired. See the note in the overview
+      documentation on <a
+      href="../../../overview-summary.html#SHARING">context
+      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,
+                  GLCapabilitiesChooser chooser,
+                  GLContext shareWith,
+                  GraphicsDevice device) {
+    /*
+     * 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.
+     */
+    super();
+
+    if(null==capabilities) {
+        capabilities = new GLCapabilities(defaultGLProfile);
+    }
+    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);
+    }
+    return cl;
+  }
+
+  protected final static Window getWindow(Component c) {
+    while ( c!=null && ! ( c instanceof Window ) ) {
+        c = c.getParent();
+    }
+    return (Window)c;
+  }
+
+  /**
+   * Overridden to choose a GraphicsConfiguration on a parent container's
+   * GraphicsDevice because both devices
+   */
+  public GraphicsConfiguration getGraphicsConfiguration() {
+    /*
+     * Workaround for problems with Xinerama and java.awt.Component.checkGD
+     * when adding to a container on a different graphics device than the
+     * one that this Canvas is associated with.
+     * 
+     * GC will be null unless:
+     *   - A native peer has assigned it. This means we have a native
+     *     peer, and are already comitted to a graphics configuration.
+     *   - This canvas has been added to a component hierarchy and has
+     *     an ancestor with a non-null GC, but the native peer has not
+     *     yet been created. This means we can still choose the GC on
+     *     all platforms since the peer hasn't been created.
+     */
+    final GraphicsConfiguration gc = super.getGraphicsConfiguration();
+    /*
+     * chosen is only non-null on platforms where the GLDrawableFactory
+     * returns a non-null GraphicsConfiguration (in the GLCanvas
+     * constructor).
+     * 
+     * if gc is from this Canvas' native peer then it should equal chosen,
+     * otherwise it is from an ancestor component that this Canvas is being
+     * added to, and we go into this block.
+     */
+    if (gc != null && chosen != null && !chosen.equals(gc)) {
+      /*
+       * Check for compatibility with gc. If they differ by only the
+       * device then return a new GCconfig with the super-class' GDevice
+       * (and presumably the same visual ID in Xinerama).
+       * 
+       */
+      if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) {
+        /*
+         * Here we select a GraphicsConfiguration on the alternate
+         * device that is presumably identical to the chosen
+         * configuration, but on the other device.
+         * 
+         * Should really check to ensure that we select a configuration
+         * with the same X visual ID for Xinerama screens, otherwise the
+         * GLDrawable may have the wrong visual ID (I don't think this
+         * ever gets updated). May need to add a method to
+         * X11GLDrawableFactory to do this in a platform specific
+         * manner.
+         * 
+         * However, on platforms where we can actually get into this
+         * 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());
+        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());
+            e.printStackTrace();
+            System.err.println("!!! Created Config (n): HAVE    GC "+chosen);
+            System.err.println("!!! Created Config (n): THIS    GC "+gc);
+            System.err.println("!!! Created Config (n): Choosen GC "+compatible);
+            System.err.println("!!! Created Config (n): HAVE    CF "+awtConfig);
+            System.err.println("!!! Created Config (n): Choosen CF "+config);
+            System.err.println("!!! Created Config (n): EQUALS CAPS "+equalCaps);
+        }
+
+        if (compatible != null) {
+          /*
+           * Save the new GC for equals test above, and to return to
+           * any outside callers of this method.
+           */
+          chosen = compatible;
+
+          awtConfig = config;
+
+          if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) {
+              dispose(true);
+          }
+        }
+      }
+
+      /*
+       * If a compatible GC was not found in the block above, this will
+       * return the GC that was selected in the constructor (and might
+       * cause an exception in Component.checkGD when adding to a
+       * container, but in this case that would be the desired behavior).
+       * 
+       */
+      return chosen;
+    } else if (gc == null) {
+      /*
+       * The GC is null, which means we have no native peer, and are not
+       * part of a (realized) component hierarchy. So we return the
+       * desired visual that was selected in the constructor (possibly
+       * null).
+       */
+      return chosen;
+    }
+
+    /*
+     * Otherwise we have not explicitly selected a GC in the constructor, so
+     * just return what Canvas would have.
+     */
+    return gc;
+  }
+  
+  public GLContext createContext(GLContext shareWith) {
+    return drawable.createContext(shareWith);
+  }
+
+  public void setRealized(boolean realized) {
+  }
+
+  private Object closingListener = null;
+  private Object closingListenerLock = new Object();
+
+  public void display() {
+    maybeDoSingleThreadedWorkaround(displayOnEventDispatchThreadAction,
+                                    displayAction);
+    if(null==closingListener) {
+      synchronized(closingListenerLock) {
+        if(null==closingListener) {
+            closingListener=addClosingListener(this, new DestroyMethod() { 
+                        public void destroyMethod() { destroy(); } });
+        }
+      }
+    }
+  }
+
+  protected void dispose(boolean regenerate) {
+    if(DEBUG) {
+        Exception ex1 = new Exception("dispose("+regenerate+") - start");
+        ex1.printStackTrace();
+    }
+    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
+      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.
+        drawableHelper.invokeGL(drawable, context, disposeAction, null);
+      } else {
+        Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction);
+      }
+    } else {
+      drawableHelper.invokeGL(drawable, context, disposeAction, null);
+    }
+
+    if(DEBUG) {
+        System.err.println("dispose("+regenerate+") - stop");
+    }
+  }
+
+  /**
+   * Just an alias for removeNotify
+   */
+  public void destroy() {
+    removeNotify();
+  }
+
+  /** Overridden to cause OpenGL rendering to be performed during
+      repaint cycles. Subclasses which override this method must call
+      super.paint() in their paint() method in order to function
+      properly. <P>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>paint</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+  public void paint(Graphics g) {
+    if (Beans.isDesignTime()) {
+      // Make GLCanvas behave better in NetBeans GUI builder
+      g.setColor(Color.BLACK);
+      g.fillRect(0, 0, getWidth(), getHeight());
+      FontMetrics fm = g.getFontMetrics();
+      String name = getName();
+      if (name == null) {
+        name = getClass().getName();
+        int idx = name.lastIndexOf('.');
+        if (idx >= 0) {
+          name = name.substring(idx + 1);
+        }
+      }
+      Rectangle2D bounds = fm.getStringBounds(name, g);
+      g.setColor(Color.WHITE);
+      g.drawString(name,
+                   (int) ((getWidth()  - bounds.getWidth())  / 2),
+                   (int) ((getHeight() + bounds.getHeight()) / 2));
+      return;
+    }
+
+    display();
+  }
+
+  /** 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
+      function properly. <P>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+  public void addNotify() {
+    super.addNotify();
+    if (!Beans.isDesignTime()) {
+        disableBackgroundErase();
+
+        if(null==device) {
+            GraphicsConfiguration gc = super.getGraphicsConfiguration();
+            if(null!=gc) {
+                device = gc.getDevice();
+            }
+        }
+
+        /*
+         * Save the chosen capabilities for use in getGraphicsConfiguration().
+         */
+        awtConfig = chooseGraphicsConfiguration(capabilities, chooser, device);
+        if(DEBUG) {
+            Exception e = new Exception("Created Config: "+awtConfig);
+            e.printStackTrace();
+        }
+        if(null!=awtConfig) {
+          // update ..
+          chosen = awtConfig.getGraphicsConfiguration();
+
+        }
+        if(null==awtConfig) {
+          throw new GLException("Error: AWTGraphicsConfiguration is null");
+        }
+        drawable = GLDrawableFactory.getFactory(glProfile).createGLDrawable(NativeWindowFactory.getNativeWindow(this, awtConfig));
+        context = (GLContextImpl) drawable.createContext(shareWith);
+        context.setSynchronized(true);
+
+        if(DEBUG) {
+            System.err.println("Created Drawable: "+drawable);
+        }
+        drawable.setRealized(true);
+    }
+  }
+
+  /** 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>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+  public void removeNotify() {
+    if(DEBUG) {
+        Exception ex1 = new Exception("removeNotify - start");
+        ex1.printStackTrace();
+    }
+
+    if (Beans.isDesignTime()) {
+      super.removeNotify();
+    } else {
+      try {
+        dispose(false);
+      } finally {
+        drawable=null;
+        super.removeNotify();
+      }
+    }
+    if(DEBUG) {
+        System.out.println("removeNotify - end");
+    }
+  }
+
+  /** Overridden to cause {@link GLDrawableHelper#reshape} to be
+      called on all registered {@link GLEventListener}s. Subclasses
+      which override this method must call super.reshape() in
+      their reshape() method in order to function properly. <P>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+  public void reshape(int x, int y, int width, int height) {
+    super.reshape(x, y, width, height);
+    sendReshape = true;
+  }
+
+  /** <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.
+  public void update(Graphics g) {
+    paint(g);
+  }
+  
+  public void addGLEventListener(GLEventListener listener) {
+    drawableHelper.addGLEventListener(listener);
+  }
+
+  public void removeGLEventListener(GLEventListener listener) {
+    drawableHelper.removeGLEventListener(listener);
+  }
+
+  public void setContext(GLContext ctx) {
+    context=(GLContextImpl)ctx;
+  }
+
+  public GLContext getContext() {
+    return context;
+  }
+
+  public GL getGL() {
+    if (Beans.isDesignTime()) {
+      return null;
+    }
+    GLContext context = getContext();
+    return (context == null) ? null : context.getGL();
+  }
+
+  public void setGL(GL gl) {
+    GLContext context = getContext();
+    if (context != null) {
+      context.setGL(gl);
+    }
+  }
+
+
+  public void setAutoSwapBufferMode(boolean onOrOff) {
+    drawableHelper.setAutoSwapBufferMode(onOrOff);
+  }
+
+  public boolean getAutoSwapBufferMode() {
+    return drawableHelper.getAutoSwapBufferMode();
+  }
+
+  public void swapBuffers() {
+    maybeDoSingleThreadedWorkaround(swapBuffersOnEventDispatchThreadAction, swapBuffersAction);
+  }
+
+  public GLProfile getGLProfile() {
+    return glProfile;
+  }
+
+  public GLCapabilities getChosenGLCapabilities() {
+    if (awtConfig == null) {
+        throw new GLException("No AWTGraphicsConfiguration: "+this);
+    }
+
+    return (GLCapabilities)awtConfig.getChosenCapabilities();
+  }
+
+  public GLCapabilities getRequestedGLCapabilities() {
+    if (awtConfig == null) {
+        throw new GLException("No AWTGraphicsConfiguration: "+this);
+    }
+
+    return (GLCapabilities)awtConfig.getRequestedCapabilities();
+  }
+
+  public NativeWindow getNativeWindow() {
+    return drawable.getNativeWindow();
+  }
+
+  public GLDrawableFactory getFactory() {
+    return drawable.getFactory();
+  }
+
+  public String toString() {
+    return "AWT-GLCanvas[ "+awtConfig+", "+((null!=drawable)?drawable.getClass().getName():"null-drawable")+", "+drawableHelper+"]";
+  }
+
+  //----------------------------------------------------------------------
+  // Internals only below this point
+  //
+
+  private void maybeDoSingleThreadedWorkaround(Runnable eventDispatchThreadAction,
+                                               Runnable invokeGLAction) {
+    if (Threading.isSingleThreaded() &&
+        !Threading.isOpenGLThread()) {
+      Threading.invokeOnOpenGLThread(eventDispatchThreadAction);
+    } else {
+      drawableHelper.invokeGL(drawable, context, invokeGLAction, initAction);
+    }
+  }
+
+  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);
+
+      if(null!=context) {
+        context.makeCurrent(); // implicit wait for lock ..
+        context.destroy();
+        context=null;
+      }
+
+      if(null!=drawable) {
+          drawable.setRealized(false);
+      }
+
+      if(disposeRegenerate) {
+          // recreate GLDrawable to reflect it's new graphics configuration
+          drawable = GLDrawableFactory.getFactory(glProfile).createGLDrawable(NativeWindowFactory.getNativeWindow(canvas, awtConfig));
+          if(DEBUG) {
+            System.err.println("GLCanvas.dispose(true): new drawable: "+drawable);
+          }
+          drawable.setRealized(true);
+          context = (GLContextImpl) drawable.createContext(shareWith);
+          context.setSynchronized(true);
+          sendReshape=true; // ensure a reshape is being send ..
+      }
+    }
+  }
+
+  private DisposeOnEventDispatchThreadAction disposeOnEventDispatchThreadAction =
+    new DisposeOnEventDispatchThreadAction();
+
+  class DisposeOnEventDispatchThreadAction implements Runnable {
+    public void run() {
+      drawableHelper.invokeGL(drawable, context, disposeAction, null);
+    }
+  }
+
+  class InitAction implements Runnable {
+    public void run() {
+      drawableHelper.init(GLCanvas.this);
+    }
+  }
+  private InitAction initAction = new InitAction();
+  
+  class DisplayAction implements Runnable {
+    public void run() {
+      if (sendReshape) {
+        // 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);
+        sendReshape = false;
+      }
+
+      drawableHelper.display(GLCanvas.this);
+    }
+  }
+  private DisplayAction displayAction = new DisplayAction();
+
+  class SwapBuffersAction implements Runnable {
+    public void run() {
+      drawable.swapBuffers();
+    }
+  }
+  private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
+
+  // Workaround for ATI driver bugs related to multithreading issues
+  // like simultaneous rendering via Animators to canvases that are
+  // being resized on the AWT event dispatch thread
+  class DisplayOnEventDispatchThreadAction implements Runnable {
+    public void run() {
+      drawableHelper.invokeGL(drawable, context, displayAction, initAction);
+    }
+  }
+  private DisplayOnEventDispatchThreadAction displayOnEventDispatchThreadAction =
+    new DisplayOnEventDispatchThreadAction();
+  class SwapBuffersOnEventDispatchThreadAction implements Runnable {
+    public void run() {
+      drawableHelper.invokeGL(drawable, context, swapBuffersAction, initAction);
+    }
+  }
+  private SwapBuffersOnEventDispatchThreadAction swapBuffersOnEventDispatchThreadAction =
+    new SwapBuffersOnEventDispatchThreadAction();
+
+  // Disables the AWT's erasing of this Canvas's background on Windows
+  // in Java SE 6. This internal API is not available in previous
+  // releases, but the system property
+  // -Dsun.awt.noerasebackground=true can be specified to get similar
+  // results globally in previous releases.
+  private static boolean disableBackgroundEraseInitialized;
+  private static Method  disableBackgroundEraseMethod;
+  private void disableBackgroundErase() {
+    if (!disableBackgroundEraseInitialized) {
+      try {
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+              try {
+                Class clazz = getToolkit().getClass();
+                while (clazz != null && disableBackgroundEraseMethod == null) {
+                  try {
+                    disableBackgroundEraseMethod =
+                      clazz.getDeclaredMethod("disableBackgroundErase",
+                                              new Class[] { Canvas.class });
+                    disableBackgroundEraseMethod.setAccessible(true);
+                  } catch (Exception e) {
+                    clazz = clazz.getSuperclass();
+                  }
+                }
+              } catch (Exception e) {
+              }
+              return null;
+            }
+          });
+      } catch (Exception e) {
+      }
+      disableBackgroundEraseInitialized = true;
+    }
+    if (disableBackgroundEraseMethod != 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);
+      }
+    }
+  }
+
+  private static AWTGraphicsConfiguration chooseGraphicsConfiguration(GLCapabilities capabilities,
+                                                                      GLCapabilitiesChooser chooser,
+                                                                      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);
+    if (config == null) {
+      throw new GLException("Error: Couldn't fetch AWTGraphicsConfiguration");
+    }
+
+    return config;
+  }
+}
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
new file mode 100644
index 000000000..5da6a892f
--- /dev/null
+++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
@@ -0,0 +1,1582 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * - Redistribution of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * - Redistribution 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.
+ * 
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * 
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ * 
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package javax.media.opengl.awt;
+
+import javax.media.opengl.*;
+import javax.media.nativewindow.*;
+import javax.media.nativewindow.awt.*;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.image.*;
+import java.beans.*;
+import javax.swing.*;
+import java.nio.*;
+import java.security.*;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import com.sun.opengl.impl.*;
+import com.sun.opengl.impl.awt.*;
+
+// FIXME: Subclasses need to call resetGLFunctionAvailability() on their
+// context whenever the displayChanged() function is called on their
+// GLEventListeners
+
+/** A lightweight Swing component which provides OpenGL rendering
+    support. Provided for compatibility with Swing user interfaces
+    when adding a heavyweight doesn't work either because of
+    Z-ordering or LayoutManager problems. <P>
+
+    The GLJPanel can be made transparent by creating it with a
+    GLCapabilities object with alpha bits specified and calling {@link
+    #setOpaque}(false). Pixels with resulting OpenGL alpha values less
+    than 1.0 will be overlaid on any underlying Swing rendering. <P>
+
+    Notes specific to the Reference Implementation: This component 
+    attempts to use hardware-accelerated rendering via pbuffers and 
+    falls back on to software rendering if problems occur. 
+    Note that because this component attempts to use pbuffers for
+    rendering, and because pbuffers can not be resized, somewhat
+    surprising behavior may occur during resize operations; the {@link
+    GLEventListener#init} method may be called multiple times as the
+    pbuffer is resized to be able to cover the size of the GLJPanel.
+    This behavior is correct, as the textures and display lists for
+    the GLJPanel will have been lost during the resize operation. The
+    application should attempt to make its GLEventListener.init()
+    methods as side-effect-free as possible. <P>
+
+*/
+
+public class GLJPanel extends JPanel implements AWTGLAutoDrawable {
+  private static final boolean DEBUG = Debug.debug("GLJPanel");
+  private static final boolean VERBOSE = Debug.verbose();
+
+  private GLDrawableHelper drawableHelper = new GLDrawableHelper();
+  private volatile boolean isInitialized;
+
+  // Data used for either pbuffers or pixmap-based offscreen surfaces
+  private GLCapabilities        offscreenCaps;
+  private GLProfile             glProfile;
+  private GLDrawableFactoryImpl factory;
+  private GLCapabilitiesChooser chooser;
+  private GLContext             shareWith;
+  // Width of the actual GLJPanel
+  private int panelWidth   = 0;
+  private int panelHeight  = 0;
+  // Lazy reshape notification
+  private boolean handleReshape = false;
+  private boolean sendReshape = true;
+
+  // The backend in use
+  private Backend backend;
+
+  // Used by all backends either directly or indirectly to hook up callbacks
+  private Updater updater = new Updater();
+
+  // Turns off the pbuffer-based backend (used by default, unless the
+  // Java 2D / OpenGL pipeline is in use)
+  private static boolean hardwareAccelerationDisabled =
+    Debug.isPropertyDefined("jogl.gljpanel.nohw");
+
+  // Turns off the fallback to software-based rendering from
+  // pbuffer-based rendering
+  private static boolean softwareRenderingDisabled =
+    Debug.isPropertyDefined("jogl.gljpanel.nosw");
+
+  // Indicates whether the Java 2D OpenGL pipeline is enabled
+  private boolean oglPipelineEnabled =
+    Java2D.isOGLPipelineActive() &&
+    !Debug.isPropertyDefined("jogl.gljpanel.noogl");
+
+  // For handling reshape events lazily
+  private int reshapeX;
+  private int reshapeY;
+  private int reshapeWidth;
+  private int reshapeHeight;
+
+  // These are always set to (0, 0) except when the Java2D / OpenGL
+  // pipeline is active
+  private int viewportX;
+  private int viewportY;
+
+  static {
+    // Force eager initialization of part of the Java2D class since
+    // otherwise it's likely it will try to be initialized while on
+    // the Queue Flusher Thread, which is not allowed
+    if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) {
+      Java2D.getShareContext(GraphicsEnvironment.
+                             getLocalGraphicsEnvironment().
+                             getDefaultScreenDevice());
+    }
+  }
+
+  /** Creates a new GLJPanel component with a default set of OpenGL
+      capabilities and using the default OpenGL capabilities selection
+      mechanism. */
+  public GLJPanel() {
+    this(null);
+  }
+
+  /** Creates a new GLJPanel component with the requested set of
+      OpenGL capabilities, using the default OpenGL capabilities
+      selection mechanism. */
+  public GLJPanel(GLCapabilities capabilities) {
+    this(capabilities, null, null);
+  }
+
+  /** Creates a new GLJPanel component. The passed GLCapabilities
+      specifies the OpenGL capabilities for the component; if null, a
+      default set of capabilities is used. The GLCapabilitiesChooser
+      specifies the algorithm for selecting one of the available
+      GLCapabilities for the component; a DefaultGLCapabilitesChooser
+      is used if null is passed for this argument. The passed
+      GLContext specifies an OpenGL context with which to share
+      textures, display lists and other OpenGL state, and may be null
+      if sharing is not desired. See the note in the overview documentation on
+      <a href="../../../overview-summary.html#SHARING">context sharing</a>.
+  */
+  public GLJPanel(GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLContext shareWith) {
+    super();
+
+    // Works around problems on many vendors' cards; we don't need a
+    // back buffer for the offscreen surface anyway
+    if (capabilities != null) {
+        offscreenCaps = (GLCapabilities) capabilities.clone();
+    } else {
+        offscreenCaps = new GLCapabilities(null);
+    }
+    offscreenCaps.setDoubleBuffered(false);
+    this.glProfile = offscreenCaps.getGLProfile();
+    this.factory = GLDrawableFactoryImpl.getFactoryImpl(glProfile);
+    this.chooser = ((chooser != null) ? chooser : new DefaultGLCapabilitiesChooser());
+    this.shareWith = shareWith;
+  }
+
+  public void display() {
+    if (EventQueue.isDispatchThread()) {
+      // Want display() to be synchronous, so call paintImmediately()
+      paintImmediately(0, 0, getWidth(), getHeight());
+    } else {
+      // Multithreaded redrawing of Swing components is not allowed,
+      // so do everything on the event dispatch thread
+      try {
+        EventQueue.invokeAndWait(paintImmediatelyAction);
+      } catch (Exception e) {
+        throw new GLException(e);
+      }
+    }
+  }
+
+  protected void dispose(boolean regenerate) {
+    if(DEBUG) {
+        Exception ex1 = new Exception("dispose("+regenerate+") - start");
+        ex1.printStackTrace();
+    }
+    if (backend != null) {
+      disposeRegenerate=regenerate;
+      disposeContext=backend.getContext();
+      disposeDrawable=backend.getDrawable();
+
+      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.
+            drawableHelper.invokeGL(disposeDrawable, disposeContext, disposeAction, null);
+          } else {
+            Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction);
+          }
+      } else {
+          drawableHelper.invokeGL(disposeDrawable, disposeContext, disposeAction, null);
+      }
+
+      backend.setContext(disposeContext);
+      if(null==disposeContext) {
+        isInitialized = false;
+      }
+    }
+
+    if(DEBUG) {
+        System.err.println("dispose("+regenerate+") - stop");
+    }
+  }
+
+  /**
+   * Just an alias for removeNotify
+   */
+  public void destroy() {
+      removeNotify();
+  }
+
+  /** Overridden to cause OpenGL rendering to be performed during
+      repaint cycles. Subclasses which override this method must call
+      super.paintComponent() in their paintComponent() method in order
+      to function properly. <P>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>paintComponent</CODE> in class <CODE>javax.swing.JComponent</CODE></DD></DL> */
+  protected void paintComponent(final Graphics g) {
+    if (Beans.isDesignTime()) {
+      // Make GLJPanel behave better in NetBeans GUI builder
+      g.setColor(Color.BLACK);
+      g.fillRect(0, 0, getWidth(), getHeight());
+      FontMetrics fm = g.getFontMetrics();
+      String name = getName();
+      if (name == null) {
+        name = getClass().getName();
+        int idx = name.lastIndexOf('.');
+        if (idx >= 0) {
+          name = name.substring(idx + 1);
+        }
+      }
+      Rectangle2D bounds = fm.getStringBounds(name, g);
+      g.setColor(Color.WHITE);
+      g.drawString(name,
+                   (int) ((getWidth()  - bounds.getWidth())  / 2),
+                   (int) ((getHeight() + bounds.getHeight()) / 2));
+      return;
+    }
+
+    if (backend == null || !isInitialized) {
+      createAndInitializeBackend();
+    }
+
+    if (!isInitialized) {
+      return;
+    }
+
+    // NOTE: must do this when the context is not current as it may
+    // involve destroying the pbuffer (current context) and
+    // re-creating it -- tricky to do properly while the context is
+    // current
+    if (handleReshape) {
+      handleReshape();
+    }
+
+    updater.setGraphics(g);
+    backend.doPaintComponent(g);
+  }
+
+  /** 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
+      function properly. <P>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+  public void addNotify() {
+    super.addNotify();
+    if (DEBUG) {
+      System.err.println("GLJPanel.addNotify()");
+    }
+  }
+
+  /** 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>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+  public void removeNotify() {
+    if(DEBUG) {
+        Exception ex1 = new Exception("removeNotify - start");
+        ex1.printStackTrace();
+    }
+    dispose(false);
+    if (backend != null) {
+      backend.destroy();
+      backend = null;
+    }
+    isInitialized = false;
+    super.removeNotify();
+    if(DEBUG) {
+        System.out.println("removeNotify - end");
+    }
+  }
+
+  /** Overridden to cause {@link GLDrawableHelper#reshape} to be
+      called on all registered {@link GLEventListener}s. Subclasses
+      which override this method must call super.reshape() in
+      their reshape() method in order to function properly. <P>
+
+      <B>Overrides:</B>
+      <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+  public void reshape(int x, int y, int width, int height) {
+    super.reshape(x, y, width, height);
+
+    reshapeX = x;
+    reshapeY = y;
+    reshapeWidth = width;
+    reshapeHeight = height;
+    handleReshape = true;
+  }
+
+  public void setOpaque(boolean opaque) {
+    if (backend != null) {
+      backend.setOpaque(opaque);
+    }
+    super.setOpaque(opaque);
+  }
+
+  public void addGLEventListener(GLEventListener listener) {
+    drawableHelper.addGLEventListener(listener);
+  }
+
+  public void removeGLEventListener(GLEventListener listener) {
+    drawableHelper.removeGLEventListener(listener);
+  }
+
+  public GLContext createContext(GLContext shareWith) {
+    return backend.createContext(shareWith);
+  }
+
+  public void setRealized(boolean realized) {
+  }
+
+  public void setContext(GLContext ctx) {
+    if (backend == null) {
+      return;
+    }
+    backend.setContext(ctx);
+  }
+
+  public GLContext getContext() {
+    if (backend == null) {
+      return null;
+    }
+    return backend.getContext();
+  }
+
+  public GL getGL() {
+    if (Beans.isDesignTime()) {
+      return null;
+    }
+    GLContext context = getContext();
+    return (context == null) ? null : context.getGL();
+  }
+
+  public void setGL(GL gl) {
+    GLContext context = getContext();
+    if (context != null) {
+      context.setGL(gl);
+    }
+  }
+
+  public void setAutoSwapBufferMode(boolean onOrOff) {
+    // In the current implementation this is a no-op. Both the pbuffer
+    // and pixmap based rendering paths use a single-buffered surface
+    // so swapping the buffers doesn't do anything. We also don't
+    // currently have the provision to skip copying the data to the
+    // Swing portion of the GLJPanel in any of the rendering paths.
+  }
+
+  public boolean getAutoSwapBufferMode() {
+    // In the current implementation this is a no-op. Both the pbuffer
+    // and pixmap based rendering paths use a single-buffered surface
+    // so swapping the buffers doesn't do anything. We also don't
+    // currently have the provision to skip copying the data to the
+    // Swing portion of the GLJPanel in any of the rendering paths.
+    return true;
+  }
+
+  public void swapBuffers() {
+    // In the current implementation this is a no-op. Both the pbuffer
+    // and pixmap based rendering paths use a single-buffered surface
+    // so swapping the buffers doesn't do anything. We also don't
+    // currently have the provision to skip copying the data to the
+    // Swing portion of the GLJPanel in any of the rendering paths.
+  }
+
+  /** For a translucent GLJPanel (one for which {@link #setOpaque
+      setOpaque}(false) has been called), indicates whether the
+      application should preserve the OpenGL color buffer
+      (GL_COLOR_BUFFER_BIT) for correct rendering of the GLJPanel and
+      underlying widgets which may show through portions of the
+      GLJPanel with alpha values less than 1.  Most Swing
+      implementations currently expect the GLJPanel to be completely
+      cleared (e.g., by <code>glClear(GL_COLOR_BUFFER_BIT |
+      GL_DEPTH_BUFFER_BIT)</code>), but for certain optimized Swing
+      implementations which use OpenGL internally, it may be possible
+      to perform OpenGL rendering using the GLJPanel into the same
+      OpenGL drawable as the Swing implementation uses. */
+  public boolean shouldPreserveColorBufferIfTranslucent() {
+    return oglPipelineEnabled;
+  }
+
+  public GLCapabilities getChosenGLCapabilities() {
+    return backend.getChosenGLCapabilities();
+  }
+
+  public final GLProfile getGLProfile() {
+    return glProfile;
+  }
+
+  public NativeWindow getNativeWindow() {
+    throw new GLException("FIXME");
+  }
+
+  public final GLDrawableFactory getFactory() {
+    return factory;
+  }
+
+  //----------------------------------------------------------------------
+  // Internals only below this point
+  //
+
+  private void createAndInitializeBackend() {
+    if (panelWidth == 0 ||
+        panelHeight == 0) {
+      // See whether we have a non-zero size yet and can go ahead with
+      // initialization
+      if (reshapeWidth == 0 ||
+          reshapeHeight == 0) {
+        return;
+      }
+
+      // Pull down reshapeWidth and reshapeHeight into panelWidth and
+      // panelHeight eagerly in order to complete initialization, and
+      // force a reshape later
+      panelWidth = reshapeWidth;
+      panelHeight = reshapeHeight;
+    }
+
+    do {
+      if (backend == null) {
+        if (oglPipelineEnabled) {
+          backend = new J2DOGLBackend();
+        } else {
+          if (!hardwareAccelerationDisabled &&
+              factory.canCreateGLPbuffer()) {
+            backend = new PbufferBackend();
+          } else {
+            if (softwareRenderingDisabled) {
+              throw new GLException("Fallback to software rendering disabled by user");
+            }
+            backend = new SoftwareBackend();
+          }
+        }
+      }
+
+      if (!isInitialized) {
+        backend.initialize();
+      }
+      // The backend might set itself to null, indicating it punted to
+      // a different implementation -- try again
+    } while (backend == null);
+
+    if(null==closingListener) {
+      synchronized(closingListenerLock) {
+        if(null==closingListener) {
+            closingListener=GLCanvas.addClosingListener(this, new GLCanvas.DestroyMethod() {
+                        public void destroyMethod() { destroy(); } });
+        }
+      }
+    }
+  }
+  private Object closingListener = null;
+  private Object closingListenerLock = new Object();
+
+  private void handleReshape() {
+    panelWidth  = reshapeWidth;
+    panelHeight = reshapeHeight;
+
+    if (DEBUG) {
+      System.err.println("GLJPanel.handleReshape: (w,h) = (" +
+                         panelWidth + "," + panelHeight + ")");
+    }
+
+    sendReshape = true;
+    backend.handleReshape();
+    handleReshape = false;
+  }
+
+  // This is used as the GLEventListener for the pbuffer-based backend
+  // as well as the callback mechanism for the other backends
+  class Updater implements GLEventListener {
+    private Graphics g;
+
+    public void setGraphics(Graphics g) {
+      this.g = g;
+    }
+
+    public void init(GLAutoDrawable drawable) {
+      if (!backend.preGL(g)) {
+        return;
+      }
+      drawableHelper.init(GLJPanel.this);
+      backend.postGL(g, false);
+    }
+
+    public void dispose(GLAutoDrawable drawable) {
+      drawableHelper.dispose(GLJPanel.this);
+    }
+
+    public void display(GLAutoDrawable drawable) {
+      if (!backend.preGL(g)) {
+        return;
+      }
+      if (sendReshape) {
+        if (DEBUG) {
+          System.err.println("glViewport(" + viewportX + ", " + viewportY + ", " + panelWidth + ", " + panelHeight + ")");
+        }
+        getGL().getGL2().glViewport(viewportX, viewportY, panelWidth, panelHeight);
+        drawableHelper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight);
+        sendReshape = false;
+      }
+
+      drawableHelper.display(GLJPanel.this);
+      backend.postGL(g, true);
+    }
+
+    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
+      // This is handled above and dispatched directly to the appropriate context
+    }
+
+    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
+    }
+  }
+
+  public String toString() {
+    return "AWT-GLJPanel[ "+((null!=backend)?backend.getDrawable().getClass().getName():"null-drawable")+", "+drawableHelper+"]";
+  }
+
+  private boolean disposeRegenerate;
+  private GLContext disposeContext;
+  private GLDrawable disposeDrawable;
+  private DisposeAction disposeAction = new DisposeAction();
+
+  class DisposeAction implements Runnable {
+    public void run() {
+      updater.dispose(GLJPanel.this);
+
+      if(null!=disposeContext) {
+          disposeContext.destroy();
+          disposeContext=null;
+      }
+      if(null!=disposeDrawable) {
+          disposeDrawable.setRealized(false);
+      }
+      if(disposeRegenerate && null!=disposeDrawable) {
+          disposeDrawable.setRealized(true);
+          disposeContext = (GLContextImpl) disposeDrawable.createContext(shareWith);
+          disposeContext.setSynchronized(true);
+      }
+    }
+  }
+
+  private DisposeOnEventDispatchThreadAction disposeOnEventDispatchThreadAction =
+    new DisposeOnEventDispatchThreadAction();
+
+  class DisposeOnEventDispatchThreadAction implements Runnable {
+    public void run() {
+      drawableHelper.invokeGL(disposeDrawable, disposeContext, disposeAction, null);
+    }
+  }
+
+
+  class InitAction implements Runnable {
+    public void run() {
+      updater.init(GLJPanel.this);
+    }
+  }
+  private InitAction initAction = new InitAction();
+
+  class DisplayAction implements Runnable {
+    public void run() {
+      updater.display(GLJPanel.this);
+    }
+  }
+  private DisplayAction displayAction = new DisplayAction();
+
+  class PaintImmediatelyAction implements Runnable {
+    public void run() {
+      paintImmediately(0, 0, getWidth(), getHeight());
+    }
+  }
+  private PaintImmediatelyAction paintImmediatelyAction = new PaintImmediatelyAction();
+
+  private int getNextPowerOf2(int number) {
+    // Workaround for problems where 0 width or height are transiently
+    // seen during layout
+    if (number == 0) {
+      return 2;
+    }
+
+    if (((number-1) & number) == 0) {
+      //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0
+      return number;
+    }
+    int power = 0;
+    while (number > 0) {
+      number = number>>1;
+      power++;
+    }
+    return (1<<power);
+  }
+
+  private int getGLInteger(GL gl, int which) {
+    int[] tmp = new int[1];
+    gl.glGetIntegerv(which, tmp, 0);
+    return tmp[0];
+  }
+
+  //----------------------------------------------------------------------
+  // Implementations of the various backends
+  //
+
+  // Abstraction of the different rendering backends: i.e., pure
+  // software / pixmap rendering, pbuffer-based acceleration, Java 2D
+  // / JOGL bridge
+  static interface Backend {
+    // Called each time the backend needs to initialize itself
+    public void initialize();
+
+    // Called when the backend should clean up its resources
+    public void destroy();
+
+    // Called when the opacity of the GLJPanel is changed
+    public void setOpaque(boolean opaque);
+
+    // Called to manually create an additional OpenGL context against
+    // this GLJPanel
+    public GLContext createContext(GLContext shareWith);
+
+    // Called to set the current backend's GLContext
+    public void setContext(GLContext ctx);
+
+    // Called to get the current backend's GLContext
+    public GLContext getContext();
+
+    // Called to get the current backend's GLDrawable
+    public GLDrawable getDrawable();
+
+    // Called to fetch the "real" GLCapabilities for the backend
+    public GLCapabilities getChosenGLCapabilities();
+
+    // Called to fetch the "real" GLProfile for the backend
+    public GLProfile getGLProfile();
+
+    // Called to handle a reshape event. When this is called, the
+    // OpenGL context associated with the backend is not current, to
+    // make it easier to destroy and re-create pbuffers if necessary.
+    public void handleReshape();
+
+    // Called before the OpenGL work is done in init() and display().
+    // If false is returned, this render is aborted.
+    public boolean preGL(Graphics g);
+
+    // Called after the OpenGL work is done in init() and display().
+    // The isDisplay argument indicates whether this was called on
+    // behalf of a call to display() rather than init().
+    public void postGL(Graphics g, boolean isDisplay);
+
+    // Called from within paintComponent() to initiate the render
+    public void doPaintComponent(Graphics g);
+  }
+
+  // Base class used by both the software (pixmap) and pbuffer
+  // backends, both of which rely on reading back the OpenGL frame
+  // buffer and drawing it with a BufferedImage
+  abstract class AbstractReadbackBackend implements Backend {
+    // This image is exactly the correct size to render into the panel
+    protected BufferedImage         offscreenImage;
+    // One of these is used to store the read back pixels before storing
+    // in the BufferedImage
+    protected ByteBuffer            readBackBytes;
+    protected IntBuffer             readBackInts;
+    protected int                   readBackWidthInPixels;
+    protected int                   readBackHeightInPixels;
+
+    private int awtFormat;
+    private int glFormat;
+    private int glType;
+
+    // For saving/restoring of OpenGL state during ReadPixels
+    private int[] swapbytes    = new int[1];
+    private int[] rowlength    = new int[1];
+    private int[] skiprows     = new int[1];
+    private int[] skippixels   = new int[1];
+    private int[] alignment    = new int[1];
+
+    public void setOpaque(boolean opaque) {
+      if (opaque != isOpaque()) {
+        if (offscreenImage != null) {
+          offscreenImage.flush();
+          offscreenImage = null;
+        }
+      }
+    }
+
+    public boolean preGL(Graphics g) {
+      // Empty in this implementation
+      return true;
+    }
+
+    public void postGL(Graphics g, boolean isDisplay) {
+      if (isDisplay) {
+        // Must now copy pixels from offscreen context into surface
+        if (offscreenImage == null) {
+          if (panelWidth > 0 && panelHeight > 0) {
+            // It looks like NVidia's drivers (at least the ones on my
+            // notebook) are buggy and don't allow a sub-rectangle to be
+            // read from a pbuffer...this doesn't really matter because
+            // it's the Graphics.drawImage() calls that are the
+            // bottleneck
+
+            int awtFormat = 0;
+
+            // Should be more flexible in these BufferedImage formats;
+            // perhaps see what the preferred image types are on the
+            // given platform
+            if (isOpaque()) {
+              awtFormat = BufferedImage.TYPE_INT_RGB;
+            } else {
+              awtFormat = BufferedImage.TYPE_INT_ARGB;
+            }
+
+            offscreenImage = new BufferedImage(panelWidth,
+                                               panelHeight,
+                                               awtFormat);
+            switch (awtFormat) {
+            case BufferedImage.TYPE_3BYTE_BGR:
+              glFormat = GL2.GL_BGR;
+              glType   = GL2.GL_UNSIGNED_BYTE;
+              readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3);
+              break;
+
+            case BufferedImage.TYPE_INT_RGB:
+            case BufferedImage.TYPE_INT_ARGB:
+              glFormat = GL2.GL_BGRA;
+              glType   = getGLPixelType();
+              readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels);
+              break;
+
+            default:
+              // FIXME: Support more off-screen image types (current
+              // offscreen context implementations don't use others, and
+              // some of the OpenGL formats aren't supported in the 1.1
+              // headers, which we're currently using)
+              throw new GLException("Unsupported offscreen image type " + awtFormat);
+            }
+          }
+        }
+
+        if (offscreenImage != null) {
+          GL2 gl = getGL().getGL2();
+          // Save current modes
+          gl.glGetIntegerv(GL2.GL_PACK_SWAP_BYTES,    swapbytes, 0);
+          gl.glGetIntegerv(GL2.GL_PACK_ROW_LENGTH,    rowlength, 0);
+          gl.glGetIntegerv(GL2.GL_PACK_SKIP_ROWS,     skiprows, 0);
+          gl.glGetIntegerv(GL2.GL_PACK_SKIP_PIXELS,   skippixels, 0);
+          gl.glGetIntegerv(GL2.GL_PACK_ALIGNMENT,     alignment, 0);
+
+          gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES,    GL.GL_FALSE);
+          gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH,    readBackWidthInPixels);
+          gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS,     0);
+          gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS,   0);
+          gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT,     1);
+
+          // Actually read the pixels.
+          gl.glReadBuffer(GL2.GL_FRONT);
+          if (readBackBytes != null) {
+            gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes);
+          } else if (readBackInts != null) {
+            gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts);
+          }
+
+          // Restore saved modes.
+          gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES,  swapbytes[0]);
+          gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH,  rowlength[0]);
+          gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS,   skiprows[0]);
+          gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, skippixels[0]);
+          gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT,   alignment[0]);
+
+          if (readBackBytes != null || readBackInts != null) {
+            // Copy temporary data into raster of BufferedImage for faster
+            // blitting Note that we could avoid this copy in the cases
+            // where !offscreenContext.offscreenImageNeedsVerticalFlip(),
+            // but that's the software rendering path which is very slow
+            // anyway
+            Object src  = null;
+            Object dest = null;
+            int    srcIncr  = 0;
+            int    destIncr = 0;
+
+            if (readBackBytes != null) {
+              src = readBackBytes.array();
+              dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData();
+              srcIncr = readBackWidthInPixels * 3;
+              destIncr = offscreenImage.getWidth() * 3;
+            } else {
+              src = readBackInts.array();
+              dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData();
+              srcIncr = readBackWidthInPixels;
+              destIncr = offscreenImage.getWidth();
+            }
+
+            if (flipVertically()) {
+              int srcPos = 0;
+              int destPos = (offscreenImage.getHeight() - 1) * destIncr;
+              for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) {
+                System.arraycopy(src, srcPos, dest, destPos, destIncr);
+              }
+            } else {
+              int srcPos = 0;
+              int destEnd = destIncr * offscreenImage.getHeight();
+              for (int destPos = 0; destPos < destEnd; srcPos += srcIncr, destPos += destIncr) {
+                System.arraycopy(src, srcPos, dest, destPos, destIncr);
+              }
+            }
+
+            // Note: image will be drawn back in paintComponent() for
+            // correctness on all platforms
+          }
+        }
+      }
+    }
+
+    public void doPaintComponent(Graphics g) {
+      doPaintComponentImpl();
+      if (offscreenImage != null) {
+        // Draw resulting image in one shot
+        g.drawImage(offscreenImage, 0, 0,
+                    offscreenImage.getWidth(),
+                    offscreenImage.getHeight(),
+                    GLJPanel.this);
+      }
+    }
+
+    protected abstract void    doPaintComponentImpl();
+    protected abstract int     getGLPixelType();
+    protected abstract boolean flipVertically();
+  }
+
+  class SoftwareBackend extends AbstractReadbackBackend {
+    // Implementation using software rendering
+    private GLDrawableImpl offscreenDrawable;
+    private GLContextImpl offscreenContext;
+
+    public void initialize() {
+      // Fall-through path: create an offscreen context instead
+      offscreenDrawable = factory.createOffscreenDrawable(offscreenCaps,
+                                                          chooser,
+                                                          Math.max(1, panelWidth),
+                                                          Math.max(1, panelHeight));
+      offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith);
+      offscreenContext.setSynchronized(true);
+      isInitialized = true;
+    }
+
+    public void destroy() {
+      if (offscreenContext != null) {
+        offscreenContext.destroy();
+        offscreenContext = null;
+      }
+      if (offscreenDrawable != null) {
+        offscreenDrawable.destroy();
+        offscreenDrawable = null;
+      }
+    }
+
+    public GLContext createContext(GLContext shareWith) {
+      return offscreenDrawable.createContext(shareWith);
+    }
+
+    public void setContext(GLContext ctx) {
+      offscreenContext=(GLContextImpl)ctx;
+    }
+
+    public GLContext getContext() {
+      return offscreenContext;
+    }
+
+    public GLDrawable getDrawable() {
+        return offscreenDrawable;
+    }
+
+    public GLCapabilities getChosenGLCapabilities() {
+      if (offscreenDrawable == null) {
+        return null;
+      }
+      return offscreenDrawable.getChosenGLCapabilities();
+    }
+
+    public GLProfile getGLProfile() {
+      if (offscreenDrawable == null) {
+        return null;
+      }
+      return offscreenDrawable.getGLProfile();
+    }
+
+    public void handleReshape() {
+      destroy();
+      initialize();
+      readBackWidthInPixels  = Math.max(1, panelWidth);
+      readBackHeightInPixels = Math.max(1, panelHeight);
+
+      if (offscreenImage != null) {
+        offscreenImage.flush();
+        offscreenImage = null;
+      }
+    }
+
+    protected void doPaintComponentImpl() {
+      drawableHelper.invokeGL(offscreenDrawable, offscreenContext, displayAction, initAction);
+    }
+
+    protected int getGLPixelType() {
+      return offscreenContext.getOffscreenContextPixelDataType();
+    }
+
+    protected boolean flipVertically() {
+      return offscreenContext.offscreenImageNeedsVerticalFlip();
+    }
+  }
+
+  class PbufferBackend extends AbstractReadbackBackend {
+    private GLPbuffer pbuffer;
+    private int       pbufferWidth  = 256;
+    private int       pbufferHeight = 256;
+
+    public void initialize() {
+      if (pbuffer != null) {
+        throw new InternalError("Creating pbuffer twice without destroying it (memory leak / correctness bug)");
+      }
+      try {
+        pbuffer = factory.createGLPbuffer(offscreenCaps,
+                                          null,
+                                          pbufferWidth,
+                                          pbufferHeight,
+                                          shareWith);
+        pbuffer.addGLEventListener(updater);
+        isInitialized = true;
+      } catch (GLException e) {
+        if (DEBUG) {
+          e.printStackTrace();
+          System.err.println("GLJPanel: Falling back on software rendering because of problems creating pbuffer");
+        }
+        hardwareAccelerationDisabled = true;
+        backend = null;
+        isInitialized = false;
+        createAndInitializeBackend();
+      }
+    }
+
+    public void destroy() {
+      if (pbuffer != null) {
+        pbuffer.destroy();
+        pbuffer = null;
+      }
+    }
+
+    public GLContext createContext(GLContext shareWith) {
+      return pbuffer.createContext(shareWith);
+    }
+
+    public void setContext(GLContext ctx) {
+      if (pbuffer == null && Beans.isDesignTime()) {
+        return;
+      }
+      pbuffer.setContext(ctx);
+    }
+
+    public GLContext getContext() {
+      // Workaround for crashes in NetBeans GUI builder
+      if (pbuffer == null && Beans.isDesignTime()) {
+        return null;
+      }
+      return pbuffer.getContext();
+    }
+
+    public GLDrawable getDrawable() {
+        return pbuffer;
+    }
+
+    public GLCapabilities getChosenGLCapabilities() {
+      if (pbuffer == null) {
+        return null;
+      }
+      return pbuffer.getChosenGLCapabilities();
+    }
+    
+    public GLProfile getGLProfile() {
+      if (pbuffer == null) {
+        return null;
+      }
+      return pbuffer.getGLProfile();
+    }
+    
+    public void handleReshape() {
+      // Use factor larger than 2 during shrinks for some hysteresis
+      float shrinkFactor = 2.5f;
+      if ((panelWidth > pbufferWidth)                  || (panelHeight > pbufferHeight) ||
+          (panelWidth < (pbufferWidth / shrinkFactor)) || (panelHeight < (pbufferHeight / shrinkFactor))) {
+        if (DEBUG) {
+          System.err.println("Resizing pbuffer from (" + pbufferWidth + ", " + pbufferHeight + ") " +
+                             " to fit (" + panelWidth + ", " + panelHeight + ")");
+        }
+        // Must destroy and recreate pbuffer to fit
+        if (pbuffer != null) {
+          // Watch for errors during pbuffer destruction (due to
+          // buggy / bad OpenGL drivers, in particular SiS) and fall
+          // back to software rendering
+          try {
+            pbuffer.destroy();
+          } catch (GLException e) {
+            hardwareAccelerationDisabled = true;
+            backend = null;
+            isInitialized = false;
+            // Just disabled hardware acceleration during this resize operation; do a fixup
+            readBackWidthInPixels  = Math.max(1, panelWidth);
+            readBackHeightInPixels = Math.max(1, panelHeight);
+            if (DEBUG) {
+              System.err.println("WARNING: falling back to software rendering due to bugs in OpenGL drivers");
+              e.printStackTrace();
+            }
+            createAndInitializeBackend();
+            return;
+          }
+        }
+        pbuffer = null;
+        isInitialized = false;
+        pbufferWidth = getNextPowerOf2(panelWidth);
+        pbufferHeight = getNextPowerOf2(panelHeight);
+        if (DEBUG && !hardwareAccelerationDisabled) {
+          System.err.println("New pbuffer size is (" + pbufferWidth + ", " + pbufferHeight + ")");
+        }
+        initialize();
+      }
+
+      // It looks like NVidia's drivers (at least the ones on my
+      // notebook) are buggy and don't allow a rectangle of less than
+      // the pbuffer's width to be read...this doesn't really matter
+      // because it's the Graphics.drawImage() calls that are the
+      // bottleneck. Should probably make the size of the offscreen
+      // image be the exact size of the pbuffer to save some work on
+      // resize operations...
+      readBackWidthInPixels  = pbufferWidth;
+      readBackHeightInPixels = panelHeight;
+
+      if (offscreenImage != null) {
+        offscreenImage.flush();
+        offscreenImage = null;
+      }
+    }
+
+    protected void doPaintComponentImpl() {
+      pbuffer.display();
+    }
+
+    protected int getGLPixelType() {
+      // This seems to be a good choice on all platforms
+      return GL2.GL_UNSIGNED_INT_8_8_8_8_REV;
+    }
+
+    protected boolean flipVertically() {
+      return true;
+    }
+  }
+
+  class J2DOGLBackend implements Backend {
+    // Opaque Object identifier representing the Java2D surface we are
+    // drawing to; used to determine when to destroy and recreate JOGL
+    // context
+    private Object j2dSurface;
+    // Graphics object being used during Java2D update action
+    // (absolutely essential to cache this)
+    private Graphics cached2DGraphics;
+    // No-op context representing the Java2D OpenGL context
+    private GLContext j2dContext;
+    // Context associated with no-op drawable representing the JOGL
+    // OpenGL context
+    private GLDrawable joglDrawable;
+    // The real OpenGL context JOGL uses to render
+    private GLContext  joglContext;
+    // State captured from Java2D OpenGL context necessary in order to
+    // properly render into Java2D back buffer
+    private int[] drawBuffer   = new int[1];
+    private int[] readBuffer   = new int[1];
+    // This is required when the FBO option of the Java2D / OpenGL
+    // pipeline is active
+    private int[] frameBuffer  = new int[1];
+    // Current (as of this writing) NVidia drivers have a couple of bugs
+    // relating to the sharing of framebuffer and renderbuffer objects
+    // between contexts. It appears we have to (a) reattach the color
+    // attachment and (b) actually create new depth buffer storage and
+    // attach it in order for the FBO to behave properly in our context.
+    private boolean checkedForFBObjectWorkarounds;
+    private boolean fbObjectWorkarounds;
+    private int[] frameBufferDepthBuffer;
+    private int[] frameBufferTexture;
+    private boolean createNewDepthBuffer;
+    // Current (as of this writing) ATI drivers have problems when the
+    // same FBO is bound in two different contexts. Here we check for
+    // this case and explicitly release the FBO from Java2D's context
+    // before switching to ours. Java2D will re-bind the FBO when it
+    // makes its context current the next time. Interestingly, if we run
+    // this code path on NVidia hardware, it breaks the rendering
+    // results -- no output is generated. This doesn't appear to be an
+    // interaction with the abovementioned NVidia-specific workarounds,
+    // as even if we disable that code the FBO is still reported as
+    // incomplete in our context.
+    private boolean checkedGLVendor;
+    private boolean vendorIsATI;
+
+    // Holding on to this GraphicsConfiguration is a workaround for a
+    // problem in the Java 2D / JOGL bridge when FBOs are enabled; see
+    // comment related to Issue 274 below
+    private GraphicsConfiguration workaroundConfig;
+
+    public void initialize() {
+      // No-op in this implementation; everything is done lazily
+      isInitialized = true;
+    }
+
+    public void destroy() {
+      Java2D.invokeWithOGLContextCurrent(null, new Runnable() {
+          public void run() {
+            if (joglContext != null) {
+              joglContext.destroy();
+              joglContext = null;
+            }
+            joglDrawable = null;
+            if (j2dContext != null) {
+              j2dContext.destroy();
+              j2dContext = null;
+            }
+          }
+        });
+    }
+
+    public void setOpaque(boolean opaque) {
+      // Empty in this implementation
+    }
+
+    public GLContext createContext(GLContext shareWith) {
+      // FIXME: should implement this, but it was not properly
+      // implemented before the refactoring anyway
+      throw new GLException("Not yet implemented");
+    }
+
+    public void setContext(GLContext ctx) {
+        joglContext=ctx;
+    }
+
+    public GLContext getContext() {
+      return joglContext;
+    }
+
+    public GLDrawable getDrawable() {
+        return joglDrawable;
+    }
+
+    public GLCapabilities getChosenGLCapabilities() {
+      // FIXME: should do better than this; is it possible to using only platform-independent code?
+      return new GLCapabilities(null);
+    }
+
+    public GLProfile getGLProfile() {
+      // FIXME: should do better than this; is it possible to using only platform-independent code?
+      return GLProfile.getDefault();
+    }
+
+    public void handleReshape() {
+      // Empty in this implementation
+    }
+
+    public boolean preGL(Graphics g) {
+      GL2 gl = joglContext.getGL().getGL2();
+      // Set up needed state in JOGL context from Java2D context
+      gl.glEnable(GL2.GL_SCISSOR_TEST);
+      Rectangle r = Java2D.getOGLScissorBox(g);
+
+      if (r == null) {
+        if (DEBUG && VERBOSE) {
+          System.err.println("Java2D.getOGLScissorBox() returned null");
+        }
+        return false;
+      }
+      if (DEBUG && VERBOSE) {
+        System.err.println("GLJPanel: gl.glScissor(" + r.x + ", " + r.y + ", " + r.width + ", " + r.height + ")");
+      }
+
+      gl.glScissor(r.x, r.y, r.width, r.height);
+      Rectangle oglViewport = Java2D.getOGLViewport(g, panelWidth, panelHeight);
+      // If the viewport X or Y changes, in addition to the panel's
+      // width or height, we need to send a reshape operation to the
+      // client
+      if ((viewportX != oglViewport.x) ||
+          (viewportY != oglViewport.y)) {
+        sendReshape = true;
+        if (DEBUG) {
+          System.err.println("Sending reshape because viewport changed");
+          System.err.println("  viewportX (" + viewportX + ") ?= oglViewport.x (" + oglViewport.x + ")");
+          System.err.println("  viewportY (" + viewportY + ") ?= oglViewport.y (" + oglViewport.y + ")");
+        }
+      }
+      viewportX = oglViewport.x;
+      viewportY = oglViewport.y;
+
+      // If the FBO option is active, bind to the FBO from the Java2D
+      // context.
+      // Note that all of the plumbing in the context sharing stuff will
+      // allow us to bind to this object since it's in our namespace.
+      if (Java2D.isFBOEnabled() &&
+          Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
+        if (DEBUG && VERBOSE) {
+          System.err.println("GLJPanel: Binding to framebuffer object " + frameBuffer[0]);
+        }
+
+        // The texture target for Java2D's OpenGL pipeline when using FBOs
+        // -- either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB
+        int fboTextureTarget = Java2D.getOGLTextureType(g);
+
+        if (!checkedForFBObjectWorkarounds) {
+          checkedForFBObjectWorkarounds = true;
+          gl.glBindTexture(fboTextureTarget, 0);
+          gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, frameBuffer[0]);
+          if (gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER) !=
+              GL2.GL_FRAMEBUFFER_COMPLETE) {
+            // Need to do workarounds
+            fbObjectWorkarounds = true;
+            createNewDepthBuffer = true;
+            if (DEBUG) {
+              System.err.println("-- GLJPanel: discovered frame_buffer_object workarounds to be necessary");
+            }
+          } else {
+            // Don't need the frameBufferTexture temporary any more
+            frameBufferTexture = null;
+          }
+        }
+
+        if (fbObjectWorkarounds && createNewDepthBuffer) {
+          if (frameBufferDepthBuffer == null)
+            frameBufferDepthBuffer = new int[1];
+
+          // Create our own depth renderbuffer and associated storage
+          // If we have an old one, delete it
+          if (frameBufferDepthBuffer[0] != 0) {
+            gl.glDeleteRenderbuffers(1, frameBufferDepthBuffer, 0);
+            frameBufferDepthBuffer[0] = 0;
+          }
+
+          gl.glBindTexture(fboTextureTarget, frameBufferTexture[0]);
+          int[] width = new int[1];
+          int[] height = new int[1];
+          gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_WIDTH, width, 0);
+          gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_HEIGHT, height, 0);
+                    
+          gl.glGenRenderbuffers(1, frameBufferDepthBuffer, 0);
+          if (DEBUG) {
+            System.err.println("GLJPanel: Generated frameBufferDepthBuffer " + frameBufferDepthBuffer[0] +
+                               " with width " + width[0] + ", height " + height[0]);
+          }
+                    
+          gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, frameBufferDepthBuffer[0]);
+          // FIXME: may need a loop here like in Java2D
+          gl.glRenderbufferStorage(GL2.GL_RENDERBUFFER, GL2.GL_DEPTH_COMPONENT24, width[0], height[0]);
+
+          gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, 0);
+          createNewDepthBuffer = false;
+        }
+
+        gl.glBindTexture(fboTextureTarget, 0);
+        gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, frameBuffer[0]);
+
+        if (fbObjectWorkarounds) {
+          // Hook up the color and depth buffer attachment points for this framebuffer
+          gl.glFramebufferTexture2D(GL2.GL_FRAMEBUFFER,
+                                    GL2.GL_COLOR_ATTACHMENT0,
+                                    fboTextureTarget,
+                                    frameBufferTexture[0],
+                                    0);
+          if (DEBUG && VERBOSE) {
+            System.err.println("GLJPanel: frameBufferDepthBuffer: " + frameBufferDepthBuffer[0]);
+          }
+          gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER,
+                                       GL2.GL_DEPTH_ATTACHMENT,
+                                       GL2.GL_RENDERBUFFER,
+                                       frameBufferDepthBuffer[0]);
+        }
+
+        if (DEBUG) {
+          int status = gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER);
+          if (status != GL2.GL_FRAMEBUFFER_COMPLETE) {
+            throw new GLException("Error: framebuffer was incomplete: status = 0x" +
+                                  Integer.toHexString(status));
+          }
+        }
+      } else {
+        if (DEBUG && VERBOSE) {
+          System.err.println("GLJPanel: Setting up drawBuffer " + drawBuffer[0] +
+                             " and readBuffer " + readBuffer[0]);
+        }
+
+        gl.glDrawBuffer(drawBuffer[0]);
+        gl.glReadBuffer(readBuffer[0]);
+      }
+
+      return true;
+    }
+
+    public void postGL(Graphics g, boolean isDisplay) {
+      // Cause OpenGL pipeline to flush its results because
+      // otherwise it's possible we will buffer up multiple frames'
+      // rendering results, resulting in apparent mouse lag
+      GL gl = getGL();
+      gl.glFinish();
+
+      if (Java2D.isFBOEnabled() &&
+          Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
+        // Unbind the framebuffer from our context to work around
+        // apparent driver bugs or at least unspecified behavior causing
+        // OpenGL to run out of memory with certain cards and drivers
+        gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
+      }
+    }
+
+    public void doPaintComponent(final Graphics g) {
+      // This is a workaround for an issue in the Java 2D / JOGL
+      // bridge (reported by an end user as JOGL Issue 274) where Java
+      // 2D can occasionally leave its internal OpenGL context current
+      // to the on-screen window rather than its internal "scratch"
+      // pbuffer surface to which the FBO is attached. JOGL expects to
+      // find a stable OpenGL drawable (on Windows, an HDC) upon which
+      // it can create another OpenGL context. It turns out that, on
+      // Windows, when Java 2D makes its internal OpenGL context
+      // current against the window in order to put pixels on the
+      // screen, it gets the device context for the window, makes its
+      // context current, and releases the device context. This means
+      // that when JOGL's Runnable gets to run below, the HDC is
+      // already invalid. The workaround for this is to force Java 2D
+      // to make its context current to the scratch surface, which we
+      // can do by executing an empty Runnable with the "shared"
+      // context current. This will be fixed in a Java SE 6 update
+      // release, hopefully 6u2.
+      if (Java2D.isFBOEnabled()) {
+        if (workaroundConfig == null) {
+          workaroundConfig = GraphicsEnvironment.
+            getLocalGraphicsEnvironment().
+            getDefaultScreenDevice().
+            getDefaultConfiguration();
+        }
+        Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { public void run() {}});
+      }
+
+      Java2D.invokeWithOGLContextCurrent(g, new Runnable() {
+          public void run() {
+            if (DEBUG && VERBOSE) {
+              System.err.println("-- In invokeWithOGLContextCurrent");
+            }
+
+            // Create no-op context representing Java2D context
+            if (j2dContext == null) {
+              j2dContext = factory.createExternalGLContext();
+              if (DEBUG) {
+                j2dContext.setGL(new DebugGL2(j2dContext.getGL().getGL2()));
+              }
+
+              // Check to see whether we can support the requested
+              // capabilities or need to fall back to a pbuffer
+              // FIXME: add more checks?
+
+              j2dContext.makeCurrent();
+              GL gl = j2dContext.getGL();
+              if ((getGLInteger(gl, GL2.GL_RED_BITS)         < offscreenCaps.getRedBits())        ||
+                  (getGLInteger(gl, GL2.GL_GREEN_BITS)       < offscreenCaps.getGreenBits())      ||
+                  (getGLInteger(gl, GL2.GL_BLUE_BITS)        < offscreenCaps.getBlueBits())       ||
+                  //                  (getGLInteger(gl, GL2.GL_ALPHA_BITS)       < offscreenCaps.getAlphaBits())      ||
+                  (getGLInteger(gl, GL2.GL_ACCUM_RED_BITS)   < offscreenCaps.getAccumRedBits())   ||
+                  (getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) < offscreenCaps.getAccumGreenBits()) ||
+                  (getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS)  < offscreenCaps.getAccumBlueBits())  ||
+                  (getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) < offscreenCaps.getAccumAlphaBits()) ||
+                  //          (getGLInteger(gl, GL2.GL_DEPTH_BITS)       < offscreenCaps.getDepthBits())      ||
+                  (getGLInteger(gl, GL2.GL_STENCIL_BITS)     < offscreenCaps.getStencilBits())) {
+                if (DEBUG) {
+                  System.err.println("GLJPanel: Falling back to pbuffer-based support because Java2D context insufficient");
+                  System.err.println("                    Available              Required");
+                  System.err.println("GL_RED_BITS         " + getGLInteger(gl, GL2.GL_RED_BITS)         + "              " + offscreenCaps.getRedBits());
+                  System.err.println("GL_GREEN_BITS       " + getGLInteger(gl, GL2.GL_GREEN_BITS)       + "              " + offscreenCaps.getGreenBits());
+                  System.err.println("GL_BLUE_BITS        " + getGLInteger(gl, GL2.GL_BLUE_BITS)        + "              " + offscreenCaps.getBlueBits());
+                  System.err.println("GL_ALPHA_BITS       " + getGLInteger(gl, GL2.GL_ALPHA_BITS)       + "              " + offscreenCaps.getAlphaBits());
+                  System.err.println("GL_ACCUM_RED_BITS   " + getGLInteger(gl, GL2.GL_ACCUM_RED_BITS)   + "              " + offscreenCaps.getAccumRedBits());
+                  System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) + "              " + offscreenCaps.getAccumGreenBits());
+                  System.err.println("GL_ACCUM_BLUE_BITS  " + getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS)  + "              " + offscreenCaps.getAccumBlueBits());
+                  System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) + "              " + offscreenCaps.getAccumAlphaBits());
+                  System.err.println("GL_DEPTH_BITS       " + getGLInteger(gl, GL2.GL_DEPTH_BITS)       + "              " + offscreenCaps.getDepthBits());
+                  System.err.println("GL_STENCIL_BITS     " + getGLInteger(gl, GL2.GL_STENCIL_BITS)     + "              " + offscreenCaps.getStencilBits());
+                }
+                isInitialized = false;
+                backend = null;
+                oglPipelineEnabled = false;
+                handleReshape = true;
+                j2dContext.release();
+                j2dContext.destroy();
+                j2dContext = null;
+                return;
+              }
+              j2dContext.release();
+            }
+
+            j2dContext.makeCurrent();
+            try {
+              captureJ2DState(j2dContext.getGL(), g);
+              Object curSurface = Java2D.getOGLSurfaceIdentifier(g);
+              if (curSurface != null) {
+                if (j2dSurface != curSurface) {
+                  if (joglContext != null) {
+                    joglContext.destroy();
+                    joglContext = null;
+                    joglDrawable = null;
+                    sendReshape = true;
+                    if (DEBUG) {
+                      System.err.println("Sending reshape because surface changed");
+                      System.err.println("New surface = " + curSurface);
+                    }
+                  }
+                  j2dSurface = curSurface;
+                }
+                if (joglContext == null) {
+                  if (factory.canCreateExternalGLDrawable()) {
+                    joglDrawable = factory.createExternalGLDrawable();
+                    joglContext = joglDrawable.createContext(shareWith);
+                  } else if (factory.canCreateContextOnJava2DSurface()) {
+                    // Mac OS X code path
+                    joglContext = factory.createContextOnJava2DSurface(g, shareWith);
+                  }
+                  if (DEBUG) {
+                    joglContext.setGL(new DebugGL2(joglContext.getGL().getGL2()));
+                  }
+
+                  if (Java2D.isFBOEnabled() &&
+                      Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT &&
+                      fbObjectWorkarounds) {
+                    createNewDepthBuffer = true;
+                  }
+                }
+                if (joglContext instanceof Java2DGLContext) {
+                  // Mac OS X code path
+                  ((Java2DGLContext) joglContext).setGraphics(g);
+                }
+
+                if (DEBUG && VERBOSE && Java2D.isFBOEnabled()) {
+                  System.err.print("-- Surface type: ");
+                  int surfaceType = Java2D.getOGLSurfaceType(g);
+                  if (surfaceType == Java2D.UNDEFINED) {
+                    System.err.println("UNDEFINED");
+                  } else if (surfaceType == Java2D.WINDOW) {
+                    System.err.println("WINDOW");
+                  } else if (surfaceType == Java2D.PBUFFER) {
+                    System.err.println("PBUFFER");
+                  } else if (surfaceType == Java2D.TEXTURE) {
+                    System.err.println("TEXTURE");
+                  } else if (surfaceType == Java2D.FLIP_BACKBUFFER) {
+                    System.err.println("FLIP_BACKBUFFER");
+                  } else if (surfaceType == Java2D.FBOBJECT) {
+                    System.err.println("FBOBJECT");
+                  } else {
+                    System.err.println("(Unknown surface type " + surfaceType + ")");
+                  }
+                }
+
+                drawableHelper.invokeGL(joglDrawable, joglContext, displayAction, initAction);
+              }
+            } finally {
+              j2dContext.release();
+            }
+          }
+        });
+    }
+
+    private void captureJ2DState(GL gl, Graphics g) {
+      gl.glGetIntegerv(GL2.GL_DRAW_BUFFER, drawBuffer, 0);
+      gl.glGetIntegerv(GL2.GL_READ_BUFFER, readBuffer, 0);
+      if (Java2D.isFBOEnabled() &&
+          Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
+        if (DEBUG && VERBOSE) {
+          System.err.println("GLJPanel: Fetching GL_FRAMEBUFFER_BINDING_EXT");
+        }
+        gl.glGetIntegerv(GL2.GL_FRAMEBUFFER_BINDING, frameBuffer, 0);
+
+        if (fbObjectWorkarounds ||
+            !checkedForFBObjectWorkarounds) {
+          // See above for description of what we are doing here
+          if (frameBufferTexture == null)
+            frameBufferTexture = new int[1];
+
+          // Query the framebuffer for its color buffer so we can hook
+          // it back up in our context (should not be necessary)
+          gl.glGetFramebufferAttachmentParameteriv(GL2.GL_FRAMEBUFFER,
+                                                   GL2.GL_COLOR_ATTACHMENT0,
+                                                   GL2.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+                                                   frameBufferTexture, 0);
+          if (DEBUG && VERBOSE) {
+            System.err.println("GLJPanel: FBO COLOR_ATTACHMENT0: " + frameBufferTexture[0]);
+          }
+        }
+
+        if (!checkedGLVendor) {
+          checkedGLVendor = true;
+          String vendor = gl.glGetString(GL2.GL_VENDOR);
+
+          if ((vendor != null) &&
+              vendor.startsWith("ATI")) {
+            vendorIsATI = true;
+          }
+        }
+
+        if (vendorIsATI) {
+          // Unbind the FBO from Java2D's context as it appears that
+          // driver bugs on ATI's side are causing problems if the FBO is
+          // simultaneously bound to more than one context. Java2D will
+          // re-bind the FBO during the next validation of its context.
+          // Note: this breaks rendering at least on NVidia hardware
+          gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);
+        }
+      }
+    }
+  }
+}
-- 
cgit v1.2.3