Jogamp
e95491f6b847e7914368b815a925e38461fa0bed
[jogl.git] / src / jogl / classes / javax / media / opengl / awt / GLCanvas.java
1 /*
2  * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (c) 2010 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * - Redistribution of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistribution in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32  *
33  * You acknowledge that this software is not designed or intended for use
34  * in the design, construction, operation or maintenance of any nuclear
35  * facility.
36  *
37  * Sun gratefully acknowledges that this software was originally authored
38  * and developed by Kenneth Bradley Russell and Christopher John Kline.
39  */
40
41 package javax.media.opengl.awt;
42
43 import java.beans.Beans;
44 import java.lang.reflect.Method;
45 import java.security.AccessController;
46 import java.security.PrivilegedAction;
47 import java.awt.Canvas;
48 import java.awt.Color;
49 import java.awt.FontMetrics;
50 import java.awt.Frame;
51 import java.awt.Graphics;
52 import java.awt.Graphics2D;
53 import java.awt.GraphicsConfiguration;
54 import java.awt.GraphicsDevice;
55 import java.awt.event.HierarchyEvent;
56 import java.awt.event.HierarchyListener;
57 import java.awt.geom.NoninvertibleTransformException;
58 import java.awt.geom.Rectangle2D;
59 import java.awt.EventQueue;
60 import java.lang.reflect.InvocationTargetException;
61 import java.util.ArrayList;
62 import java.util.List;
63
64 import javax.media.nativewindow.AbstractGraphicsConfiguration;
65 import javax.media.nativewindow.OffscreenLayerOption;
66 import javax.media.nativewindow.ScalableSurface;
67 import javax.media.nativewindow.VisualIDHolder;
68 import javax.media.nativewindow.WindowClosingProtocol;
69 import javax.media.nativewindow.AbstractGraphicsDevice;
70 import javax.media.nativewindow.AbstractGraphicsScreen;
71 import javax.media.nativewindow.GraphicsConfigurationFactory;
72 import javax.media.nativewindow.NativeSurface;
73 import javax.media.nativewindow.NativeWindowFactory;
74 import javax.media.opengl.GL;
75 import javax.media.opengl.GLAnimatorControl;
76 import javax.media.opengl.GLAutoDrawable;
77 import javax.media.opengl.GLCapabilities;
78 import javax.media.opengl.GLCapabilitiesChooser;
79 import javax.media.opengl.GLCapabilitiesImmutable;
80 import javax.media.opengl.GLContext;
81 import javax.media.opengl.GLDrawable;
82 import javax.media.opengl.GLDrawableFactory;
83 import javax.media.opengl.GLEventListener;
84 import javax.media.opengl.GLException;
85 import javax.media.opengl.GLProfile;
86 import javax.media.opengl.GLRunnable;
87 import javax.media.opengl.GLSharedContextSetter;
88 import javax.media.opengl.Threading;
89
90 import com.jogamp.common.GlueGenVersion;
91 import com.jogamp.common.util.VersionUtil;
92 import com.jogamp.common.util.awt.AWTEDTExecutor;
93 import com.jogamp.common.util.locks.LockFactory;
94 import com.jogamp.common.util.locks.RecursiveLock;
95 import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration;
96 import com.jogamp.nativewindow.awt.AWTGraphicsDevice;
97 import com.jogamp.nativewindow.awt.AWTGraphicsScreen;
98 import com.jogamp.nativewindow.awt.AWTPrintLifecycle;
99 import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol;
100 import com.jogamp.nativewindow.awt.JAWTWindow;
101 import com.jogamp.opengl.JoglVersion;
102 import com.jogamp.opengl.util.GLDrawableUtil;
103 import com.jogamp.opengl.util.TileRenderer;
104
105 import jogamp.nativewindow.SurfaceScaleUtils;
106 import jogamp.opengl.Debug;
107 import jogamp.opengl.GLContextImpl;
108 import jogamp.opengl.GLDrawableHelper;
109 import jogamp.opengl.GLDrawableImpl;
110 import jogamp.opengl.awt.AWTTilePainter;
111
112 // FIXME: Subclasses need to call resetGLFunctionAvailability() on their
113 // context whenever the displayChanged() function is called on our
114 // GLEventListeners
115
116 /** A heavyweight AWT component which provides OpenGL rendering
117     support. This is the primary implementation of an AWT {@link GLDrawable};
118     {@link GLJPanel} is provided for compatibility with Swing user
119     interfaces when adding a heavyweight doesn't work either because
120     of Z-ordering or LayoutManager problems.
121  *
122  * <h5><a name="offscreenlayer">Offscreen Layer Remarks</a></h5>
123  *
124  * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)}
125  * maybe called to use an offscreen drawable (FBO or PBuffer) allowing
126  * the underlying JAWT mechanism to composite the image, if supported.
127  * <p>
128  * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)}
129  * is being called if {@link GLCapabilitiesImmutable#isOnscreen()} is <code>false</code>.
130  * </p>
131  *
132  * <h5><a name="java2dgl">Java2D OpenGL Remarks</a></h5>
133  *
134  * To avoid any conflicts with a potential Java2D OpenGL context,<br>
135  * you shall consider setting the following JVM properties:<br>
136  * <ul>
137  *    <li><pre>sun.java2d.opengl=false</pre></li>
138  *    <li><pre>sun.java2d.noddraw=true</pre></li>
139  * </ul>
140  * This is especially true in case you want to utilize a GLProfile other than
141  * {@link GLProfile#GL2}, eg. using {@link GLProfile#getMaxFixedFunc()}.<br>
142  * On the other hand, if you like to experiment with GLJPanel's utilization
143  * of Java2D's OpenGL pipeline, you have to set them to
144  * <ul>
145  *    <li><pre>sun.java2d.opengl=true</pre></li>
146  *    <li><pre>sun.java2d.noddraw=true</pre></li>
147  * </ul>
148  *
149  * <h5><a name="backgrounderase">Disable Background Erase</a></h5>
150  *
151  * GLCanvas tries to disable background erase for the AWT Canvas
152  * before native peer creation (X11) and after it (Windows), <br>
153  * utilizing the optional {@link java.awt.Toolkit} method <code>disableBeackgroundErase(java.awt.Canvas)</code>.<br>
154  * However if this does not give you the desired results, you may want to disable AWT background erase in general:
155  * <ul>
156  *   <li><pre>sun.awt.noerasebackground=true</pre></li>
157  * </ul>
158  */
159
160 @SuppressWarnings("serial")
161 public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption,
162                                                 AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface {
163
164   private static final boolean DEBUG = Debug.debug("GLCanvas");
165
166   private final RecursiveLock lock = LockFactory.createRecursiveLock();
167   private final GLDrawableHelper helper = new GLDrawableHelper();
168   private AWTGraphicsConfiguration awtConfig;
169   private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access
170   private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle
171   private volatile GLContextImpl context; // volatile: avoid locking for read-only access
172   private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking
173   private volatile int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
174   final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
175
176   // copy of the cstr args, mainly for recreation
177   private final GLCapabilitiesImmutable capsReqUser;
178   private final GLCapabilitiesChooser chooser;
179   private int additionalCtxCreationFlags = 0;
180   private final GraphicsDevice device;
181   private boolean shallUseOffscreenLayer = false;
182
183   private volatile boolean isShowing;
184   private final HierarchyListener hierarchyListener = new HierarchyListener() {
185       @Override
186       public void hierarchyChanged(HierarchyEvent e) {
187           isShowing = GLCanvas.this.isShowing();
188       }
189   };
190
191   private final AWTWindowClosingProtocol awtWindowClosingProtocol =
192           new AWTWindowClosingProtocol(this, new Runnable() {
193                 @Override
194                 public void run() {
195                     GLCanvas.this.destroyImpl( true );
196                 }
197             }, null);
198
199   /** Creates a new GLCanvas component with a default set of OpenGL
200       capabilities, using the default OpenGL capabilities selection
201       mechanism, on the default screen device.
202    * @throws GLException if no default profile is available for the default desktop device.
203    */
204   public GLCanvas() throws GLException {
205     this(null);
206   }
207
208   /** Creates a new GLCanvas component with the requested set of
209       OpenGL capabilities, using the default OpenGL capabilities
210       selection mechanism, on the default screen device.
211    * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
212    * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
213    */
214   public GLCanvas(GLCapabilitiesImmutable capsReqUser) throws GLException {
215     this(capsReqUser, null, null, null);
216   }
217
218   /** Creates a new GLCanvas component with the requested set of
219       OpenGL capabilities, using the default OpenGL capabilities
220       selection mechanism, on the default screen device.
221    *  This constructor variant also supports using a shared GLContext.
222    *
223    * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
224    * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
225    * @deprecated Use {@link #GLCanvas(GLCapabilitiesImmutable)}
226    *             and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}.
227    */
228   public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLContext shareWith)
229           throws GLException
230   {
231     this(capsReqUser, null, shareWith, null);
232   }
233
234   /** Creates a new GLCanvas component. The passed GLCapabilities
235       specifies the OpenGL capabilities for the component; if null, a
236       default set of capabilities is used. The GLCapabilitiesChooser
237       specifies the algorithm for selecting one of the available
238       GLCapabilities for the component; a DefaultGLCapabilitesChooser
239       is used if null is passed for this argument.
240       The passed GraphicsDevice indicates the screen on
241       which to create the GLCanvas; the GLDrawableFactory uses the
242       default screen device of the local GraphicsEnvironment if null
243       is passed for this argument.
244    * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
245    */
246   public GLCanvas(GLCapabilitiesImmutable capsReqUser,
247                   GLCapabilitiesChooser chooser,
248                   GraphicsDevice device)
249       throws GLException
250   {
251       this(capsReqUser, chooser, null, device);
252   }
253
254   /** Creates a new GLCanvas component. The passed GLCapabilities
255       specifies the OpenGL capabilities for the component; if null, a
256       default set of capabilities is used. The GLCapabilitiesChooser
257       specifies the algorithm for selecting one of the available
258       GLCapabilities for the component; a DefaultGLCapabilitesChooser
259       is used if null is passed for this argument. The passed
260       GLContext specifies an OpenGL context with which to share
261       textures, display lists and other OpenGL state, and may be null
262       if sharing is not desired. See the note in the overview
263       documentation on <a
264       href="../../../spec-overview.html#SHARING">context
265       sharing</a>. The passed GraphicsDevice indicates the screen on
266       which to create the GLCanvas; the GLDrawableFactory uses the
267       default screen device of the local GraphicsEnvironment if null
268       is passed for this argument.
269    * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
270    * @deprecated Use {@link #GLCanvas(GLCapabilitiesImmutable, GLCapabilitiesChooser, GraphicsDevice)}
271    *             and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}.
272    */
273   public GLCanvas(GLCapabilitiesImmutable capsReqUser,
274                   GLCapabilitiesChooser chooser,
275                   GLContext shareWith,
276                   GraphicsDevice device)
277       throws GLException
278   {
279     /*
280      * Determination of the native window is made in 'super.addNotify()',
281      * which creates the native peer using AWT's GraphicsConfiguration.
282      * GraphicsConfiguration is returned by this class overwritten
283      * 'getGraphicsConfiguration()', which returns our OpenGL compatible
284      * 'chosen' GraphicsConfiguration.
285      */
286     super();
287
288     if(null==capsReqUser) {
289         capsReqUser = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDevice()));
290     } else {
291         // don't allow the user to change data
292         capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable();
293     }
294     if(!capsReqUser.isOnscreen()) {
295         setShallUseOffscreenLayer(true); // trigger offscreen layer - if supported
296     }
297
298     if(null==device) {
299         GraphicsConfiguration gc = super.getGraphicsConfiguration();
300         if(null!=gc) {
301             device = gc.getDevice();
302         }
303     }
304
305     // instantiation will be issued in addNotify()
306     this.capsReqUser = capsReqUser;
307     this.chooser = chooser;
308     if( null != shareWith ) {
309         helper.setSharedContext(null, shareWith);
310     }
311     this.device = device;
312
313     this.addHierarchyListener(hierarchyListener);
314     this.isShowing = isShowing();
315   }
316
317   @Override
318   public final void setSharedContext(GLContext sharedContext) throws IllegalStateException {
319       helper.setSharedContext(this.context, sharedContext);
320   }
321
322   @Override
323   public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException {
324       helper.setSharedAutoDrawable(this, sharedAutoDrawable);
325   }
326
327   @Override
328   public final Object getUpstreamWidget() {
329     return this;
330   }
331
332   @Override
333   public void setShallUseOffscreenLayer(boolean v) {
334       shallUseOffscreenLayer = v;
335   }
336
337   @Override
338   public final boolean getShallUseOffscreenLayer() {
339       return shallUseOffscreenLayer;
340   }
341
342   @Override
343   public final boolean isOffscreenLayerSurfaceEnabled() {
344       final JAWTWindow _jawtWindow = jawtWindow;
345       if(null != _jawtWindow) {
346           return _jawtWindow.isOffscreenLayerSurfaceEnabled();
347       }
348       return false;
349   }
350
351
352   /**
353    * Overridden to choose a GraphicsConfiguration on a parent container's
354    * GraphicsDevice because both devices
355    */
356   @Override
357   public GraphicsConfiguration getGraphicsConfiguration() {
358     /*
359      * Workaround for problems with Xinerama and java.awt.Component.checkGD
360      * when adding to a container on a different graphics device than the
361      * one that this Canvas is associated with.
362      *
363      * GC will be null unless:
364      *   - A native peer has assigned it. This means we have a native
365      *     peer, and are already comitted to a graphics configuration.
366      *   - This canvas has been added to a component hierarchy and has
367      *     an ancestor with a non-null GC, but the native peer has not
368      *     yet been created. This means we can still choose the GC on
369      *     all platforms since the peer hasn't been created.
370      */
371     final GraphicsConfiguration gc = super.getGraphicsConfiguration();
372
373     if( Beans.isDesignTime() ) {
374         return gc;
375     }
376
377     /*
378      * chosen is only non-null on platforms where the GLDrawableFactory
379      * returns a non-null GraphicsConfiguration (in the GLCanvas
380      * constructor).
381      *
382      * if gc is from this Canvas' native peer then it should equal chosen,
383      * otherwise it is from an ancestor component that this Canvas is being
384      * added to, and we go into this block.
385      */
386     GraphicsConfiguration chosen =  null != awtConfig ? awtConfig.getAWTGraphicsConfiguration() : null;
387
388     if (gc != null && chosen != null && !chosen.equals(gc)) {
389       /*
390        * Check for compatibility with gc. If they differ by only the
391        * device then return a new GCconfig with the super-class' GDevice
392        * (and presumably the same visual ID in Xinerama).
393        *
394        */
395       if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) {
396         /*
397          * Here we select a GraphicsConfiguration on the alternate
398          * device that is presumably identical to the chosen
399          * configuration, but on the other device.
400          *
401          * Should really check to ensure that we select a configuration
402          * with the same X visual ID for Xinerama screens, otherwise the
403          * GLDrawable may have the wrong visual ID (I don't think this
404          * ever gets updated). May need to add a method to
405          * X11GLDrawableFactory to do this in a platform specific
406          * manner.
407          *
408          * However, on platforms where we can actually get into this
409          * block, both devices should have the same visual list, and the
410          * same configuration should be selected here.
411          */
412         AWTGraphicsConfiguration config = chooseGraphicsConfiguration( (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(),
413                                                                        (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(),
414                                                                        chooser, gc.getDevice());
415         final GraphicsConfiguration compatible = (null!=config)?config.getAWTGraphicsConfiguration():null;
416         boolean equalCaps = config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities());
417         if(DEBUG) {
418             System.err.println(getThreadName()+": Info:");
419             System.err.println("Created Config (n): HAVE    GC "+chosen);
420             System.err.println("Created Config (n): THIS    GC "+gc);
421             System.err.println("Created Config (n): Choosen GC "+compatible);
422             System.err.println("Created Config (n): HAVE    CF "+awtConfig);
423             System.err.println("Created Config (n): Choosen CF "+config);
424             System.err.println("Created Config (n): EQUALS CAPS "+equalCaps);
425             // Thread.dumpStack();
426         }
427
428         if (compatible != null) {
429           /*
430            * Save the new GC for equals test above, and to return to
431            * any outside callers of this method.
432            */
433           chosen = compatible;
434
435           if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) {
436               // complete destruction!
437               destroyImpl( true );
438               // recreation!
439               awtConfig = config;
440               createJAWTDrawableAndContext();
441               validateGLDrawable();
442           } else {
443               awtConfig = config;
444           }
445         }
446       }
447
448       /*
449        * If a compatible GC was not found in the block above, this will
450        * return the GC that was selected in the constructor (and might
451        * cause an exception in Component.checkGD when adding to a
452        * container, but in this case that would be the desired behavior).
453        *
454        */
455       return chosen;
456     } else if (gc == null) {
457       /*
458        * The GC is null, which means we have no native peer, and are not
459        * part of a (realized) component hierarchy. So we return the
460        * desired visual that was selected in the constructor (possibly
461        * null).
462        */
463       return chosen;
464     }
465
466     /*
467      * Otherwise we have not explicitly selected a GC in the constructor, so
468      * just return what Canvas would have.
469      */
470     return gc;
471   }
472
473   @Override
474   public GLContext createContext(final GLContext shareWith) {
475     final RecursiveLock _lock = lock;
476     _lock.lock();
477     try {
478         if(drawable != null) {
479           final GLContext _ctx = drawable.createContext(shareWith);
480           _ctx.setContextCreationFlags(additionalCtxCreationFlags);
481           return _ctx;
482         }
483         return null;
484     } finally {
485         _lock.unlock();
486     }
487   }
488
489   private final void setRealizedImpl(boolean realized) {
490       final RecursiveLock _lock = lock;
491       _lock.lock();
492       try {
493           final GLDrawable _drawable = drawable;
494           if( null == _drawable || realized == _drawable.isRealized() ||
495               realized && ( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) ) {
496               return;
497           }
498          _drawable.setRealized(realized);
499           if( realized && _drawable.isRealized() ) {
500               sendReshape=true; // ensure a reshape is being send ..
501           }
502       } finally {
503           _lock.unlock();
504       }
505   }
506   private final Runnable realizeOnEDTAction = new Runnable() {
507     @Override
508     public void run() { setRealizedImpl(true); }
509   };
510   private final Runnable unrealizeOnEDTAction = new Runnable() {
511     @Override
512     public void run() { setRealizedImpl(false); }
513   };
514
515   @Override
516   public final void setRealized(boolean realized) {
517       // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock!
518       AWTEDTExecutor.singleton.invoke(getTreeLock(), false /* allowOnNonEDT */, true /* wait */, realized ? realizeOnEDTAction : unrealizeOnEDTAction);
519   }
520
521   @Override
522   public boolean isRealized() {
523       final GLDrawable _drawable = drawable;
524       return ( null != _drawable ) ? _drawable.isRealized() : false;
525   }
526
527   @Override
528   public WindowClosingMode getDefaultCloseOperation() {
529       return awtWindowClosingProtocol.getDefaultCloseOperation();
530   }
531
532   @Override
533   public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) {
534       return awtWindowClosingProtocol.setDefaultCloseOperation(op);
535   }
536
537   @Override
538   public void display() {
539     if( !validateGLDrawable() ) {
540         if(DEBUG) {
541             System.err.println(getThreadName()+": Info: GLCanvas display - skipped GL render, drawable not valid yet");
542         }
543         return; // not yet available ..
544     }
545     if( isShowing && !printActive ) {
546         Threading.invoke(true, displayOnEDTAction, getTreeLock());
547     }
548   }
549
550   /**
551    * {@inheritDoc}
552    *
553    * <p>
554    * This impl. only destroys all GL related resources.
555    * </p>
556    * <p>
557    * This impl. does not remove the GLCanvas from it's parent AWT container
558    * so this class's {@link #removeNotify()} AWT override won't get called.
559    * To do so, remove this component from it's parent AWT container.
560    * </p>
561    */
562   @Override
563   public void destroy() {
564     destroyImpl( false );
565   }
566
567   protected void destroyImpl(boolean destroyJAWTWindowAndAWTDevice) {
568     Threading.invoke(true, destroyOnEDTAction, getTreeLock());
569     if( destroyJAWTWindowAndAWTDevice ) {
570         AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, disposeJAWTWindowAndAWTDeviceOnEDT);
571     }
572   }
573
574   /** Overridden to cause OpenGL rendering to be performed during
575       repaint cycles. Subclasses which override this method must call
576       super.paint() in their paint() method in order to function
577       properly.
578     */
579   @Override
580   public void paint(Graphics g) {
581     if( Beans.isDesignTime() ) {
582       // Make GLCanvas behave better in NetBeans GUI builder
583       g.setColor(Color.BLACK);
584       g.fillRect(0, 0, getWidth(), getHeight());
585       FontMetrics fm = g.getFontMetrics();
586       String name = getName();
587       if (name == null) {
588         name = getClass().getName();
589         int idx = name.lastIndexOf('.');
590         if (idx >= 0) {
591           name = name.substring(idx + 1);
592         }
593       }
594       Rectangle2D bounds = fm.getStringBounds(name, g);
595       g.setColor(Color.WHITE);
596       g.drawString(name,
597                    (int) ((getWidth()  - bounds.getWidth())  / 2),
598                    (int) ((getHeight() + bounds.getHeight()) / 2));
599     } else if( !this.helper.isAnimatorAnimatingOnOtherThread() ) {
600         display();
601     }
602   }
603
604   /** Overridden to track when this component is added to a container.
605       Subclasses which override this method must call
606       super.addNotify() in their addNotify() method in order to
607       function properly. <P>
608
609       <B>Overrides:</B>
610       <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
611     @SuppressWarnings("deprecation")
612     @Override
613   public void addNotify() {
614     final RecursiveLock _lock = lock;
615     _lock.lock();
616     try {
617         final boolean isBeansDesignTime = Beans.isDesignTime();
618
619         if(DEBUG) {
620             System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()+", isBeansDesignTime "+isBeansDesignTime);
621             // Thread.dumpStack();
622         }
623
624         if( isBeansDesignTime ) {
625             super.addNotify();
626         } else {
627             /**
628              * 'super.addNotify()' determines the GraphicsConfiguration,
629              * while calling this class's overriden 'getGraphicsConfiguration()' method
630              * after which it creates the native peer.
631              * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration
632              * is being used in getGraphicsConfiguration().
633              * This code order also allows recreation, ie re-adding the GLCanvas.
634              */
635             awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device);
636             if(null==awtConfig) {
637                 throw new GLException("Error: NULL AWTGraphicsConfiguration");
638             }
639
640             // before native peer is valid: X11
641             disableBackgroundErase();
642
643             // issues getGraphicsConfiguration() and creates the native peer
644             super.addNotify();
645
646             // after native peer is valid: Windows
647             disableBackgroundErase();
648
649             createJAWTDrawableAndContext();
650
651             // init drawable by paint/display makes the init sequence more equal
652             // for all launch flavors (applet/javaws/..)
653             // validateGLDrawable();
654         }
655         awtWindowClosingProtocol.addClosingListener();
656
657         if(DEBUG) {
658             System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer());
659         }
660     } finally {
661         _lock.unlock();
662     }
663   }
664
665   @Override
666   public final int[] setSurfaceScale(final int[] result, final int[] pixelScale) {
667       SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null);
668       if( null != result ) {
669           System.arraycopy(reqPixelScale, 0, result, 0, 2);
670       }
671       if( isRealized() ) {
672           final ScalableSurface ns = jawtWindow;
673           if( null != ns ) {
674               ns.setSurfaceScale(result, reqPixelScale);
675               final int hadPixelScaleX = hasPixelScale[0];
676               final int hadPixelScaleY = hasPixelScale[1];
677               ns.getSurfaceScale(hasPixelScale);
678               if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) {
679                   reshapeImpl(getWidth(), getHeight());
680                   display();
681               }
682           }
683       }
684       return result;
685   }
686
687   @Override
688   public final int[] getSurfaceScale(final int[] result) {
689       System.arraycopy(hasPixelScale, 0, result, 0, 2);
690       return result;
691   }
692
693   private void createJAWTDrawableAndContext() {
694     if ( !Beans.isDesignTime() ) {
695         jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
696         jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer);
697         jawtWindow.setSurfaceScale(null, reqPixelScale);
698         jawtWindow.lockSurface();
699         try {
700             drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow);
701             createContextImpl(drawable);
702             jawtWindow.getSurfaceScale(hasPixelScale);
703         } finally {
704             jawtWindow.unlockSurface();
705         }
706     }
707   }
708   private boolean createContextImpl(final GLDrawable drawable) {
709     final GLContext[] shareWith = { null };
710     if( !helper.isSharedGLContextPending(shareWith) ) {
711         context = (GLContextImpl) drawable.createContext(shareWith[0]);
712         context.setContextCreationFlags(additionalCtxCreationFlags);
713         if(DEBUG) {
714             System.err.println(getThreadName()+": Context created: has shared "+(null != shareWith[0]));
715         }
716         return true;
717     } else {
718         if(DEBUG) {
719             System.err.println(getThreadName()+": Context !created: pending share");
720         }
721         return false;
722     }
723   }
724
725   private boolean validateGLDrawable() {
726       if( Beans.isDesignTime() || !isDisplayable() ) {
727           return false; // early out!
728       }
729       final GLDrawable _drawable = drawable;
730       if ( null != _drawable ) {
731           boolean res = _drawable.isRealized();
732           if( !res ) {
733               // re-try drawable creation
734               if( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) {
735                   return false; // early out!
736               }
737               setRealized(true);
738               res = _drawable.isRealized();
739               if(DEBUG) {
740                   System.err.println(getThreadName()+": Realized Drawable: isRealized "+res+", "+_drawable.toString());
741                   // Thread.dumpStack();
742               }
743           }
744           if( res && null == context ) {
745               // re-try context creation
746               res = createContextImpl(_drawable); // pending creation.
747           }
748           return res;
749       }
750       return false;
751   }
752
753   /** <p>Overridden to track when this component is removed from a
754       container. Subclasses which override this method must call
755       super.removeNotify() in their removeNotify() method in order to
756       function properly. </p>
757       <p>User shall not call this method outside of EDT, read the AWT/Swing specs
758       about this.</p>
759       <B>Overrides:</B>
760       <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
761     @SuppressWarnings("deprecation")
762     @Override
763   public void removeNotify() {
764     if(DEBUG) {
765         System.err.println(getThreadName()+": Info: removeNotify - start");
766         // Thread.dumpStack();
767     }
768
769     awtWindowClosingProtocol.removeClosingListener();
770
771     if( Beans.isDesignTime() ) {
772       super.removeNotify();
773     } else {
774       try {
775         destroyImpl( true );
776       } finally {
777         super.removeNotify();
778       }
779     }
780     if(DEBUG) {
781         System.err.println(getThreadName()+": Info: removeNotify - end, peer: "+getPeer());
782     }
783   }
784
785   /** Overridden to cause {@link GLDrawableHelper#reshape} to be
786       called on all registered {@link GLEventListener}s. Subclasses
787       which override this method must call super.reshape() in
788       their reshape() method in order to function properly. <P>
789
790       <B>Overrides:</B>
791       <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
792     @SuppressWarnings("deprecation")
793     @Override
794   public void reshape(int x, int y, int width, int height) {
795     synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape
796         super.reshape(x, y, width, height);
797         reshapeImpl(width, height);
798     }
799   }
800
801   private void reshapeImpl(final int width, final int height) {
802     final int scaledWidth = width * hasPixelScale[0];
803     final int scaledHeight = height * hasPixelScale[1];
804
805     if(DEBUG) {
806         final NativeSurface ns = getNativeSurface();
807         final long nsH = null != ns ? ns.getSurfaceHandle() : 0;
808         System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+
809                 " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+
810                 "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight+
811                 " - surfaceHandle 0x"+Long.toHexString(nsH));
812         // Thread.dumpStack();
813     }
814     if( validateGLDrawable() && !printActive ) {
815         final GLDrawableImpl _drawable = drawable;
816         if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) {
817             final RecursiveLock _lock = lock;
818             _lock.lock();
819             try {
820                 final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, scaledWidth, scaledHeight);
821                 if(_drawable != _drawableNew) {
822                     // write back
823                     drawable = _drawableNew;
824                 }
825             } finally {
826                _lock.unlock();
827             }
828         }
829         sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock
830     }
831   }
832
833   /**
834    * Overridden from Canvas to prevent the AWT's clearing of the
835    * canvas from interfering with the OpenGL rendering.
836    */
837   @Override
838   public void update(Graphics g) {
839     paint(g);
840   }
841
842   private volatile boolean printActive = false;
843   private GLAnimatorControl printAnimator = null;
844   private GLAutoDrawable printGLAD = null;
845   private AWTTilePainter printAWTTiles = null;
846
847   @Override
848   public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) {
849       printActive = true;
850       final int componentCount = isOpaque() ? 3 : 4;
851       final TileRenderer printRenderer = new TileRenderer();
852       printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG);
853       AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT);
854   }
855   private final Runnable setupPrintOnEDT = new Runnable() {
856       @Override
857       public void run() {
858           if( !validateGLDrawable() ) {
859               if(DEBUG) {
860                   System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet");
861               }
862               printActive = false;
863               return; // not yet available ..
864           }
865           if( !isVisible() ) {
866               if(DEBUG) {
867                   System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible");
868               }
869               printActive = false;
870               return; // not yet available ..
871           }
872           sendReshape = false; // clear reshape flag
873           printAnimator =  helper.getAnimator();
874           if( null != printAnimator ) {
875               printAnimator.remove(GLCanvas.this);
876           }
877           printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD
878           final GLCapabilities caps = (GLCapabilities)getChosenGLCapabilities().cloneMutable();
879           final int printNumSamples = printAWTTiles.getNumSamples(caps);
880           GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
881           final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples();
882           final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() ||
883                                          printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight();
884           final boolean reqNewGLADOnscrn = caps.isOnscreen();
885           // It is desired to use a new offscreen GLAD, however Bug 830 forbids this for AA onscreen context.
886           // Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX
887           final boolean reqNewGLAD = !caps.getSampleBuffers() && ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize );
888           if( DEBUG ) {
889               System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+
890                                  ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+
891                                  ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
892                                  ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
893                                  ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
894           }
895           if( reqNewGLAD ) {
896               caps.setDoubleBuffered(false);
897               caps.setOnscreen(false);
898               if( printNumSamples != caps.getNumSamples() ) {
899                   caps.setSampleBuffers(0 < printNumSamples);
900                   caps.setNumSamples(printNumSamples);
901               }
902               final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile());
903               printGLAD = factory.createOffscreenAutoDrawable(null, caps, null,
904                       printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
905                       printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
906               GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD);
907               printDrawable = printGLAD.getDelegatedDrawable();
908           }
909           printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented());
910           printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0);
911           printAWTTiles.renderer.attachAutoDrawable(printGLAD);
912           if( DEBUG ) {
913               System.err.println("AWT print.setup "+printAWTTiles);
914               System.err.println("AWT print.setup AA "+printNumSamples+", "+caps);
915               System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD);
916               System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable);
917           }
918       }
919   };
920
921   @Override
922   public void releasePrint() {
923       if( !printActive || null == printGLAD ) {
924           throw new IllegalStateException("setupPrint() not called");
925       }
926       sendReshape = false; // clear reshape flag
927       AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT);
928   }
929   private final Runnable releasePrintOnEDT = new Runnable() {
930       @Override
931       public void run() {
932           if( DEBUG ) {
933               System.err.println("AWT print.release "+printAWTTiles);
934           }
935           printAWTTiles.dispose();
936           printAWTTiles= null;
937           if( printGLAD != GLCanvas.this ) {
938               GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this);
939               printGLAD.destroy();
940           }
941           printGLAD = null;
942           if( null != printAnimator ) {
943               printAnimator.add(GLCanvas.this);
944               printAnimator = null;
945           }
946           sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
947           printActive = false;
948           display();
949       }
950   };
951
952   @Override
953   public void print(Graphics graphics) {
954       if( !printActive || null == printGLAD ) {
955           throw new IllegalStateException("setupPrint() not called");
956       }
957       if(DEBUG && !EventQueue.isDispatchThread()) {
958           System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT");
959           // we cannot dispatch print on AWT-EDT due to printing internal locking ..
960       }
961       sendReshape = false; // clear reshape flag
962
963       final Graphics2D g2d = (Graphics2D)graphics;
964       try {
965           printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight());
966           final TileRenderer tileRenderer = printAWTTiles.renderer;
967           if( DEBUG ) {
968               System.err.println("AWT print.0: "+tileRenderer);
969           }
970           if( !tileRenderer.eot() ) {
971               try {
972                   do {
973                       if( printGLAD != GLCanvas.this ) {
974                           tileRenderer.display();
975                       } else {
976                           Threading.invoke(true, displayOnEDTAction, getTreeLock());
977                       }
978                   } while ( !tileRenderer.eot() );
979                   if( DEBUG ) {
980                       System.err.println("AWT print.1: "+printAWTTiles);
981                   }
982               } finally {
983                   tileRenderer.reset();
984                   printAWTTiles.resetGraphics2D();
985               }
986           }
987       } catch (NoninvertibleTransformException nte) {
988           System.err.println("Catched: Inversion failed of: "+g2d.getTransform());
989           nte.printStackTrace();
990       }
991       if( DEBUG ) {
992           System.err.println("AWT print.X: "+printAWTTiles);
993       }
994   }
995
996   @Override
997   public void addGLEventListener(GLEventListener listener) {
998     helper.addGLEventListener(listener);
999   }
1000
1001   @Override
1002   public void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException {
1003     helper.addGLEventListener(index, listener);
1004   }
1005
1006   @Override
1007   public int getGLEventListenerCount() {
1008       return helper.getGLEventListenerCount();
1009   }
1010
1011   @Override
1012   public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
1013       return helper.getGLEventListener(index);
1014   }
1015
1016   @Override
1017   public boolean areAllGLEventListenerInitialized() {
1018      return helper.areAllGLEventListenerInitialized();
1019   }
1020
1021   @Override
1022   public boolean getGLEventListenerInitState(GLEventListener listener) {
1023       return helper.getGLEventListenerInitState(listener);
1024   }
1025
1026   @Override
1027   public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
1028       helper.setGLEventListenerInitState(listener, initialized);
1029   }
1030
1031   @Override
1032   public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
1033     final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
1034     Threading.invoke(true, r, getTreeLock());
1035     return r.listener;
1036   }
1037
1038   @Override
1039   public GLEventListener removeGLEventListener(GLEventListener listener) {
1040     return helper.removeGLEventListener(listener);
1041   }
1042
1043   @Override
1044   public void setAnimator(GLAnimatorControl animatorControl) {
1045     helper.setAnimator(animatorControl);
1046   }
1047
1048   @Override
1049   public GLAnimatorControl getAnimator() {
1050     return helper.getAnimator();
1051   }
1052
1053   @Override
1054   public final Thread setExclusiveContextThread(Thread t) throws GLException {
1055       return helper.setExclusiveContextThread(t, context);
1056   }
1057
1058   @Override
1059   public final Thread getExclusiveContextThread() {
1060       return helper.getExclusiveContextThread();
1061   }
1062
1063   @Override
1064   public boolean invoke(boolean wait, GLRunnable glRunnable) {
1065     return helper.invoke(this, wait, glRunnable);
1066   }
1067
1068   @Override
1069   public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) {
1070     return helper.invoke(this, wait, glRunnables);
1071   }
1072
1073   @Override
1074   public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) {
1075       final RecursiveLock _lock = lock;
1076       _lock.lock();
1077       try {
1078           final GLContext oldCtx = context;
1079           GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags);
1080           context=(GLContextImpl)newCtx;
1081           return oldCtx;
1082       } finally {
1083           _lock.unlock();
1084       }
1085   }
1086
1087   @Override
1088   public final GLDrawable getDelegatedDrawable() {
1089     return drawable;
1090   }
1091
1092   @Override
1093   public GLContext getContext() {
1094     return context;
1095   }
1096
1097   @Override
1098   public GL getGL() {
1099     if( Beans.isDesignTime() ) {
1100       return null;
1101     }
1102     final GLContext _context = context;
1103     return (_context == null) ? null : _context.getGL();
1104   }
1105
1106   @Override
1107   public GL setGL(GL gl) {
1108     final GLContext _context = context;
1109     if (_context != null) {
1110       _context.setGL(gl);
1111       return gl;
1112     }
1113     return null;
1114   }
1115
1116
1117   @Override
1118   public void setAutoSwapBufferMode(boolean onOrOff) {
1119     helper.setAutoSwapBufferMode(onOrOff);
1120   }
1121
1122   @Override
1123   public boolean getAutoSwapBufferMode() {
1124     return helper.getAutoSwapBufferMode();
1125   }
1126
1127   @Override
1128   public void swapBuffers() {
1129     Threading.invoke(true, swapBuffersOnEDTAction, getTreeLock());
1130   }
1131
1132   @Override
1133   public void setContextCreationFlags(int flags) {
1134     additionalCtxCreationFlags = flags;
1135     final GLContext _context = context;
1136     if(null != _context) {
1137       _context.setContextCreationFlags(additionalCtxCreationFlags);
1138     }
1139   }
1140
1141   @Override
1142   public int getContextCreationFlags() {
1143     return additionalCtxCreationFlags;
1144   }
1145
1146   @Override
1147   public GLProfile getGLProfile() {
1148     return capsReqUser.getGLProfile();
1149   }
1150
1151   @Override
1152   public GLCapabilitiesImmutable getChosenGLCapabilities() {
1153     if( Beans.isDesignTime() ) {
1154         return capsReqUser;
1155     } else if( null == awtConfig ) {
1156         throw new GLException("No AWTGraphicsConfiguration: "+this);
1157     }
1158     return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities();
1159   }
1160
1161   public GLCapabilitiesImmutable getRequestedGLCapabilities() {
1162     if( null == awtConfig ) {
1163         return capsReqUser;
1164     }
1165     return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities();
1166   }
1167
1168   @Override
1169   public int getSurfaceWidth() {
1170       return getWidth() * hasPixelScale[0];
1171   }
1172
1173   @Override
1174   public int getSurfaceHeight() {
1175       return getHeight() * hasPixelScale[1];
1176   }
1177
1178   @Override
1179   public boolean isGLOriented() {
1180     final GLDrawable _drawable = drawable;
1181     return null != _drawable ? _drawable.isGLOriented() : true;
1182   }
1183
1184   @Override
1185   public NativeSurface getNativeSurface() {
1186     final GLDrawable _drawable = drawable;
1187     return (null != _drawable) ? _drawable.getNativeSurface() : null;
1188   }
1189
1190   @Override
1191   public long getHandle() {
1192     final GLDrawable _drawable = drawable;
1193     return (null != _drawable) ? _drawable.getHandle() : 0;
1194   }
1195
1196   @Override
1197   public GLDrawableFactory getFactory() {
1198     final GLDrawable _drawable = drawable;
1199     return (null != _drawable) ? _drawable.getFactory() : null;
1200   }
1201
1202   @Override
1203   public String toString() {
1204     final GLDrawable _drawable = drawable;
1205     final int dw = (null!=_drawable) ? _drawable.getSurfaceWidth() : -1;
1206     final int dh = (null!=_drawable) ? _drawable.getSurfaceHeight() : -1;
1207
1208     return "AWT-GLCanvas[Realized "+isRealized()+
1209                           ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+
1210                           ",\n\tFactory   "+getFactory()+
1211                           ",\n\thandle    0x"+Long.toHexString(getHandle())+
1212                           ",\n\tDrawable size "+dw+"x"+dh+" surface["+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+
1213                           ",\n\tAWT[pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+
1214                           ",\n\tvisible "+isVisible()+", displayable "+isDisplayable()+", showing "+isShowing+
1215                           ",\n\t"+awtConfig+"]]";
1216   }
1217
1218   //----------------------------------------------------------------------
1219   // Internals only below this point
1220   //
1221
1222   private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; }
1223
1224   private final Runnable destroyOnEDTAction = new Runnable() {
1225     @Override
1226     public void run() {
1227         final RecursiveLock _lock = lock;
1228         _lock.lock();
1229         try {
1230             final GLAnimatorControl animator =  getAnimator();
1231
1232             if(DEBUG) {
1233                 System.err.println(getThreadName()+": Info: destroyOnEDTAction() - START, hasContext " +
1234                         (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator);
1235                 // Thread.dumpStack();
1236             }
1237
1238             final boolean animatorPaused;
1239             if(null!=animator) {
1240                 // can't remove us from animator for recreational addNotify()
1241                 animatorPaused = animator.pause();
1242             } else {
1243                 animatorPaused = false;
1244             }
1245
1246             // OLS will be detached by disposeGL's context destruction below
1247             if( null != context ) {
1248                 if( context.isCreated() ) {
1249                     // Catch dispose GLExceptions by GLEventListener, just 'print' them
1250                     // so we can continue with the destruction.
1251                     try {
1252                         helper.disposeGL(GLCanvas.this, context, true);
1253                         if(DEBUG) {
1254                             System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context);
1255                         }
1256                     } catch (GLException gle) {
1257                         gle.printStackTrace();
1258                     }
1259                 }
1260                 context = null;
1261             }
1262             if( null != drawable ) {
1263                 drawable.setRealized(false);
1264                 if(DEBUG) {
1265                     System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable);
1266                 }
1267                 drawable = null;
1268             }
1269
1270             if(animatorPaused) {
1271                 animator.resume();
1272             }
1273
1274             if(DEBUG) {
1275                 System.err.println(getThreadName()+": dispose() - END, animator "+animator);
1276             }
1277
1278         } finally {
1279             _lock.unlock();
1280         }
1281     }
1282   };
1283
1284   /**
1285    * Disposes the JAWTWindow and AbstractGraphicsDevice within EDT,
1286    * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
1287    * <p>
1288    * The drawable and context handle are null'ed as well, assuming {@link #destroy()} has been called already.
1289    * </p>
1290    *
1291    * @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice)
1292    */
1293   private final Runnable disposeJAWTWindowAndAWTDeviceOnEDT = new Runnable() {
1294     @Override
1295     public void run() {
1296         context=null;
1297         drawable=null;
1298
1299         if( null != jawtWindow ) {
1300             jawtWindow.destroy();
1301             if(DEBUG) {
1302                 System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow);
1303             }
1304             jawtWindow=null;
1305         }
1306         hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
1307         hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
1308
1309         if(null != awtConfig) {
1310             final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration();
1311             final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice();
1312             final String adeviceMsg;
1313             if(DEBUG) {
1314                 adeviceMsg = adevice.toString();
1315             } else {
1316                 adeviceMsg = null;
1317             }
1318             boolean closed = adevice.close();
1319             if(DEBUG) {
1320                 System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed);
1321             }
1322         }
1323         awtConfig=null;
1324     }
1325   };
1326
1327   private final Runnable initAction = new Runnable() {
1328     @Override
1329     public void run() {
1330       helper.init(GLCanvas.this, !sendReshape);
1331     }
1332   };
1333
1334   private final Runnable displayAction = new Runnable() {
1335     @Override
1336     public void run() {
1337       if (sendReshape) {
1338         if(DEBUG) {
1339             System.err.println(getThreadName()+": Reshape: "+getSurfaceWidth()+"x"+getSurfaceHeight());
1340         }
1341         // Note: we ignore the given x and y within the parent component
1342         // since we are drawing directly into this heavyweight component.
1343         helper.reshape(GLCanvas.this, 0, 0, getSurfaceWidth(), getSurfaceHeight());
1344         sendReshape = false;
1345       }
1346
1347       helper.display(GLCanvas.this);
1348     }
1349   };
1350
1351   private final Runnable displayOnEDTAction = new Runnable() {
1352     @Override
1353     public void run() {
1354         final RecursiveLock _lock = lock;
1355         _lock.lock();
1356         try {
1357             if( null != drawable && drawable.isRealized() ) {
1358                 helper.invokeGL(drawable, context, displayAction, initAction);
1359             }
1360         } finally {
1361             _lock.unlock();
1362         }
1363     }
1364   };
1365
1366   private final Runnable swapBuffersOnEDTAction = new Runnable() {
1367     @Override
1368     public void run() {
1369         final RecursiveLock _lock = lock;
1370         _lock.lock();
1371         try {
1372             if( null != drawable && drawable.isRealized() ) {
1373                 drawable.swapBuffers();
1374             }
1375         } finally {
1376             _lock.unlock();
1377         }
1378     }
1379   };
1380
1381   private class DisposeGLEventListenerAction implements Runnable {
1382     GLEventListener listener;
1383     private final boolean remove;
1384     private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
1385         this.listener = listener;
1386         this.remove = remove;
1387     }
1388
1389     @Override
1390     public void run() {
1391         final RecursiveLock _lock = lock;
1392         _lock.lock();
1393         try {
1394             listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove);
1395         } finally {
1396             _lock.unlock();
1397         }
1398     }
1399   };
1400
1401   // Disables the AWT's erasing of this Canvas's background on Windows
1402   // in Java SE 6. This internal API is not available in previous
1403   // releases, but the system property
1404   // -Dsun.awt.noerasebackground=true can be specified to get similar
1405   // results globally in previous releases.
1406   private static boolean disableBackgroundEraseInitialized;
1407   private static Method  disableBackgroundEraseMethod;
1408   private void disableBackgroundErase() {
1409     if (!disableBackgroundEraseInitialized) {
1410       try {
1411         AccessController.doPrivileged(new PrivilegedAction<Object>() {
1412             @Override
1413             public Object run() {
1414               try {
1415                 Class<?> clazz = getToolkit().getClass();
1416                 while (clazz != null && disableBackgroundEraseMethod == null) {
1417                   try {
1418                     disableBackgroundEraseMethod =
1419                       clazz.getDeclaredMethod("disableBackgroundErase",
1420                                               new Class[] { Canvas.class });
1421                     disableBackgroundEraseMethod.setAccessible(true);
1422                   } catch (Exception e) {
1423                     clazz = clazz.getSuperclass();
1424                   }
1425                 }
1426               } catch (Exception e) {
1427               }
1428               return null;
1429             }
1430           });
1431       } catch (Exception e) {
1432       }
1433       disableBackgroundEraseInitialized = true;
1434       if(DEBUG) {
1435         System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase method found: "+
1436                 (null!=disableBackgroundEraseMethod));
1437       }
1438     }
1439     if (disableBackgroundEraseMethod != null) {
1440       Throwable t=null;
1441       try {
1442         disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this });
1443       } catch (Exception e) {
1444         t = e;
1445       }
1446       if(DEBUG) {
1447         System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase error: "+t);
1448       }
1449     }
1450   }
1451
1452   /**
1453    * Issues the GraphicsConfigurationFactory's choosing facility within EDT,
1454    * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
1455    *
1456    * @param capsChosen
1457    * @param capsRequested
1458    * @param chooser
1459    * @param device
1460    * @return the chosen AWTGraphicsConfiguration
1461    *
1462    * @see #disposeJAWTWindowAndAWTDeviceOnEDT
1463    */
1464   private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen,
1465                                                                final GLCapabilitiesImmutable capsRequested,
1466                                                                final GLCapabilitiesChooser chooser,
1467                                                                final GraphicsDevice device) {
1468     // Make GLCanvas behave better in NetBeans GUI builder
1469     if( Beans.isDesignTime() ) {
1470       return null;
1471     }
1472
1473     final AbstractGraphicsScreen aScreen = null != device ?
1474             AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT):
1475             AWTGraphicsScreen.createDefault();
1476     AWTGraphicsConfiguration config = null;
1477
1478     if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
1479         config = (AWTGraphicsConfiguration)
1480                 GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen,
1481                                                                                                              capsRequested,
1482                                                                                                              chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
1483     } else {
1484         try {
1485             final ArrayList<AWTGraphicsConfiguration> bucket = new ArrayList<AWTGraphicsConfiguration>(1);
1486             EventQueue.invokeAndWait(new Runnable() {
1487                 @Override
1488                 public void run() {
1489                     AWTGraphicsConfiguration c = (AWTGraphicsConfiguration)
1490                             GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen,
1491                                                                                                                          capsRequested,
1492                                                                                                                          chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
1493                     bucket.add(c);
1494                 }
1495             });
1496             config = ( bucket.size() > 0 ) ? bucket.get(0) : null ;
1497         } catch (InvocationTargetException e) {
1498             throw new GLException(e.getTargetException());
1499         } catch (InterruptedException e) {
1500             throw new GLException(e);
1501         }
1502     }
1503
1504     if (config == null) {
1505       throw new GLException("Error: Couldn't fetch AWTGraphicsConfiguration");
1506     }
1507
1508     return config;
1509   }
1510
1511   protected static String getThreadName() { return Thread.currentThread().getName(); }
1512
1513   /**
1514    * A most simple JOGL AWT test entry
1515    */
1516   public static void main(String args[]) {
1517     System.err.println(VersionUtil.getPlatformInfo());
1518     System.err.println(GlueGenVersion.getInstance());
1519     // System.err.println(NativeWindowVersion.getInstance());
1520     System.err.println(JoglVersion.getInstance());
1521
1522     System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString());
1523
1524     final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) );
1525     final Frame frame = new Frame("JOGL AWT Test");
1526
1527     final GLCanvas glCanvas = new GLCanvas(caps);
1528     frame.add(glCanvas);
1529     frame.setSize(128, 128);
1530
1531     glCanvas.addGLEventListener(new GLEventListener() {
1532         @Override
1533         public void init(GLAutoDrawable drawable) {
1534             GL gl = drawable.getGL();
1535             System.err.println(JoglVersion.getGLInfo(gl, null));
1536         }
1537         @Override
1538         public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }
1539         @Override
1540         public void display(GLAutoDrawable drawable) { }
1541         @Override
1542         public void dispose(GLAutoDrawable drawable) { }
1543     });
1544
1545     try {
1546         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
1547             @Override
1548             public void run() {
1549                 frame.setVisible(true);
1550             }});
1551     } catch (Throwable t) {
1552         t.printStackTrace();
1553     }
1554     glCanvas.display();
1555     try {
1556         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
1557             @Override
1558             public void run() {
1559                 frame.dispose();
1560             }});
1561     } catch (Throwable t) {
1562         t.printStackTrace();
1563     }
1564   }
1565
1566 }
http://JogAmp.org git info: FAQ, tutorial and man pages.