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