Jogamp
Bug 741 HiDPI: Simplify ScalableSurface (2): Add request pixelScale API entry, fixed...
[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 void setSurfaceScale(final int[] pixelScale) {
667       SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null);
668       if( isRealized() ) {
669           final ScalableSurface ns = jawtWindow;
670           if( null != ns ) {
671               ns.setSurfaceScale(reqPixelScale);
672               final int hadPixelScaleX = hasPixelScale[0];
673               final int hadPixelScaleY = hasPixelScale[1];
674               ns.getCurrentSurfaceScale(hasPixelScale);
675               if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) {
676                   reshapeImpl(getWidth(), getHeight());
677                   display();
678               }
679           }
680       }
681   }
682
683   @Override
684   public final int[] getRequestedSurfaceScale(final int[] result) {
685       System.arraycopy(reqPixelScale, 0, result, 0, 2);
686       return result;
687   }
688
689   @Override
690   public final int[] getCurrentSurfaceScale(final int[] result) {
691       System.arraycopy(hasPixelScale, 0, result, 0, 2);
692       return result;
693   }
694
695   private void createJAWTDrawableAndContext() {
696     if ( !Beans.isDesignTime() ) {
697         jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
698         jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer);
699         jawtWindow.setSurfaceScale(reqPixelScale);
700         jawtWindow.lockSurface();
701         try {
702             drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow);
703             createContextImpl(drawable);
704             jawtWindow.getCurrentSurfaceScale(hasPixelScale);
705         } finally {
706             jawtWindow.unlockSurface();
707         }
708     }
709   }
710   private boolean createContextImpl(final GLDrawable drawable) {
711     final GLContext[] shareWith = { null };
712     if( !helper.isSharedGLContextPending(shareWith) ) {
713         context = (GLContextImpl) drawable.createContext(shareWith[0]);
714         context.setContextCreationFlags(additionalCtxCreationFlags);
715         if(DEBUG) {
716             System.err.println(getThreadName()+": Context created: has shared "+(null != shareWith[0]));
717         }
718         return true;
719     } else {
720         if(DEBUG) {
721             System.err.println(getThreadName()+": Context !created: pending share");
722         }
723         return false;
724     }
725   }
726
727   private boolean validateGLDrawable() {
728       if( Beans.isDesignTime() || !isDisplayable() ) {
729           return false; // early out!
730       }
731       final GLDrawable _drawable = drawable;
732       if ( null != _drawable ) {
733           boolean res = _drawable.isRealized();
734           if( !res ) {
735               // re-try drawable creation
736               if( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) {
737                   return false; // early out!
738               }
739               setRealized(true);
740               res = _drawable.isRealized();
741               if(DEBUG) {
742                   System.err.println(getThreadName()+": Realized Drawable: isRealized "+res+", "+_drawable.toString());
743                   // Thread.dumpStack();
744               }
745           }
746           if( res && null == context ) {
747               // re-try context creation
748               res = createContextImpl(_drawable); // pending creation.
749           }
750           return res;
751       }
752       return false;
753   }
754
755   /** <p>Overridden to track when this component is removed from a
756       container. Subclasses which override this method must call
757       super.removeNotify() in their removeNotify() method in order to
758       function properly. </p>
759       <p>User shall not call this method outside of EDT, read the AWT/Swing specs
760       about this.</p>
761       <B>Overrides:</B>
762       <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
763     @SuppressWarnings("deprecation")
764     @Override
765   public void removeNotify() {
766     if(DEBUG) {
767         System.err.println(getThreadName()+": Info: removeNotify - start");
768         // Thread.dumpStack();
769     }
770
771     awtWindowClosingProtocol.removeClosingListener();
772
773     if( Beans.isDesignTime() ) {
774       super.removeNotify();
775     } else {
776       try {
777         destroyImpl( true );
778       } finally {
779         super.removeNotify();
780       }
781     }
782     if(DEBUG) {
783         System.err.println(getThreadName()+": Info: removeNotify - end, peer: "+getPeer());
784     }
785   }
786
787   /** Overridden to cause {@link GLDrawableHelper#reshape} to be
788       called on all registered {@link GLEventListener}s. Subclasses
789       which override this method must call super.reshape() in
790       their reshape() method in order to function properly. <P>
791
792       <B>Overrides:</B>
793       <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
794     @SuppressWarnings("deprecation")
795     @Override
796   public void reshape(int x, int y, int width, int height) {
797     synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape
798         super.reshape(x, y, width, height);
799         reshapeImpl(width, height);
800     }
801   }
802
803   private void reshapeImpl(final int width, final int height) {
804     final int scaledWidth = width * hasPixelScale[0];
805     final int scaledHeight = height * hasPixelScale[1];
806
807     if(DEBUG) {
808         final NativeSurface ns = getNativeSurface();
809         final long nsH = null != ns ? ns.getSurfaceHandle() : 0;
810         System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+
811                 " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+
812                 "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight+
813                 " - surfaceHandle 0x"+Long.toHexString(nsH));
814         // Thread.dumpStack();
815     }
816     if( validateGLDrawable() && !printActive ) {
817         final GLDrawableImpl _drawable = drawable;
818         if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) {
819             final RecursiveLock _lock = lock;
820             _lock.lock();
821             try {
822                 final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, scaledWidth, scaledHeight);
823                 if(_drawable != _drawableNew) {
824                     // write back
825                     drawable = _drawableNew;
826                 }
827             } finally {
828                _lock.unlock();
829             }
830         }
831         sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock
832     }
833   }
834
835   /**
836    * Overridden from Canvas to prevent the AWT's clearing of the
837    * canvas from interfering with the OpenGL rendering.
838    */
839   @Override
840   public void update(Graphics g) {
841     paint(g);
842   }
843
844   private volatile boolean printActive = false;
845   private GLAnimatorControl printAnimator = null;
846   private GLAutoDrawable printGLAD = null;
847   private AWTTilePainter printAWTTiles = null;
848
849   @Override
850   public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) {
851       printActive = true;
852       final int componentCount = isOpaque() ? 3 : 4;
853       final TileRenderer printRenderer = new TileRenderer();
854       printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG);
855       AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT);
856   }
857   private final Runnable setupPrintOnEDT = new Runnable() {
858       @Override
859       public void run() {
860           if( !validateGLDrawable() ) {
861               if(DEBUG) {
862                   System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet");
863               }
864               printActive = false;
865               return; // not yet available ..
866           }
867           if( !isVisible() ) {
868               if(DEBUG) {
869                   System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible");
870               }
871               printActive = false;
872               return; // not yet available ..
873           }
874           sendReshape = false; // clear reshape flag
875           printAnimator =  helper.getAnimator();
876           if( null != printAnimator ) {
877               printAnimator.remove(GLCanvas.this);
878           }
879           printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD
880           final GLCapabilities caps = (GLCapabilities)getChosenGLCapabilities().cloneMutable();
881           final int printNumSamples = printAWTTiles.getNumSamples(caps);
882           GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
883           final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples();
884           final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() ||
885                                          printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight();
886           final boolean reqNewGLADOnscrn = caps.isOnscreen();
887           // It is desired to use a new offscreen GLAD, however Bug 830 forbids this for AA onscreen context.
888           // Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX
889           final boolean reqNewGLAD = !caps.getSampleBuffers() && ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize );
890           if( DEBUG ) {
891               System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+
892                                  ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+
893                                  ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
894                                  ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
895                                  ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
896           }
897           if( reqNewGLAD ) {
898               caps.setDoubleBuffered(false);
899               caps.setOnscreen(false);
900               if( printNumSamples != caps.getNumSamples() ) {
901                   caps.setSampleBuffers(0 < printNumSamples);
902                   caps.setNumSamples(printNumSamples);
903               }
904               final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile());
905               printGLAD = factory.createOffscreenAutoDrawable(null, caps, null,
906                       printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
907                       printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
908               GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD);
909               printDrawable = printGLAD.getDelegatedDrawable();
910           }
911           printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented());
912           printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0);
913           printAWTTiles.renderer.attachAutoDrawable(printGLAD);
914           if( DEBUG ) {
915               System.err.println("AWT print.setup "+printAWTTiles);
916               System.err.println("AWT print.setup AA "+printNumSamples+", "+caps);
917               System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD);
918               System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable);
919           }
920       }
921   };
922
923   @Override
924   public void releasePrint() {
925       if( !printActive || null == printGLAD ) {
926           throw new IllegalStateException("setupPrint() not called");
927       }
928       sendReshape = false; // clear reshape flag
929       AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT);
930   }
931   private final Runnable releasePrintOnEDT = new Runnable() {
932       @Override
933       public void run() {
934           if( DEBUG ) {
935               System.err.println("AWT print.release "+printAWTTiles);
936           }
937           printAWTTiles.dispose();
938           printAWTTiles= null;
939           if( printGLAD != GLCanvas.this ) {
940               GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this);
941               printGLAD.destroy();
942           }
943           printGLAD = null;
944           if( null != printAnimator ) {
945               printAnimator.add(GLCanvas.this);
946               printAnimator = null;
947           }
948           sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
949           printActive = false;
950           display();
951       }
952   };
953
954   @Override
955   public void print(Graphics graphics) {
956       if( !printActive || null == printGLAD ) {
957           throw new IllegalStateException("setupPrint() not called");
958       }
959       if(DEBUG && !EventQueue.isDispatchThread()) {
960           System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT");
961           // we cannot dispatch print on AWT-EDT due to printing internal locking ..
962       }
963       sendReshape = false; // clear reshape flag
964
965       final Graphics2D g2d = (Graphics2D)graphics;
966       try {
967           printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight());
968           final TileRenderer tileRenderer = printAWTTiles.renderer;
969           if( DEBUG ) {
970               System.err.println("AWT print.0: "+tileRenderer);
971           }
972           if( !tileRenderer.eot() ) {
973               try {
974                   do {
975                       if( printGLAD != GLCanvas.this ) {
976                           tileRenderer.display();
977                       } else {
978                           Threading.invoke(true, displayOnEDTAction, getTreeLock());
979                       }
980                   } while ( !tileRenderer.eot() );
981                   if( DEBUG ) {
982                       System.err.println("AWT print.1: "+printAWTTiles);
983                   }
984               } finally {
985                   tileRenderer.reset();
986                   printAWTTiles.resetGraphics2D();
987               }
988           }
989       } catch (NoninvertibleTransformException nte) {
990           System.err.println("Catched: Inversion failed of: "+g2d.getTransform());
991           nte.printStackTrace();
992       }
993       if( DEBUG ) {
994           System.err.println("AWT print.X: "+printAWTTiles);
995       }
996   }
997
998   @Override
999   public void addGLEventListener(GLEventListener listener) {
1000     helper.addGLEventListener(listener);
1001   }
1002
1003   @Override
1004   public void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException {
1005     helper.addGLEventListener(index, listener);
1006   }
1007
1008   @Override
1009   public int getGLEventListenerCount() {
1010       return helper.getGLEventListenerCount();
1011   }
1012
1013   @Override
1014   public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
1015       return helper.getGLEventListener(index);
1016   }
1017
1018   @Override
1019   public boolean areAllGLEventListenerInitialized() {
1020      return helper.areAllGLEventListenerInitialized();
1021   }
1022
1023   @Override
1024   public boolean getGLEventListenerInitState(GLEventListener listener) {
1025       return helper.getGLEventListenerInitState(listener);
1026   }
1027
1028   @Override
1029   public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
1030       helper.setGLEventListenerInitState(listener, initialized);
1031   }
1032
1033   @Override
1034   public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
1035     final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
1036     Threading.invoke(true, r, getTreeLock());
1037     return r.listener;
1038   }
1039
1040   @Override
1041   public GLEventListener removeGLEventListener(GLEventListener listener) {
1042     return helper.removeGLEventListener(listener);
1043   }
1044
1045   @Override
1046   public void setAnimator(GLAnimatorControl animatorControl) {
1047     helper.setAnimator(animatorControl);
1048   }
1049
1050   @Override
1051   public GLAnimatorControl getAnimator() {
1052     return helper.getAnimator();
1053   }
1054
1055   @Override
1056   public final Thread setExclusiveContextThread(Thread t) throws GLException {
1057       return helper.setExclusiveContextThread(t, context);
1058   }
1059
1060   @Override
1061   public final Thread getExclusiveContextThread() {
1062       return helper.getExclusiveContextThread();
1063   }
1064
1065   @Override
1066   public boolean invoke(boolean wait, GLRunnable glRunnable) {
1067     return helper.invoke(this, wait, glRunnable);
1068   }
1069
1070   @Override
1071   public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) {
1072     return helper.invoke(this, wait, glRunnables);
1073   }
1074
1075   @Override
1076   public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) {
1077       final RecursiveLock _lock = lock;
1078       _lock.lock();
1079       try {
1080           final GLContext oldCtx = context;
1081           GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags);
1082           context=(GLContextImpl)newCtx;
1083           return oldCtx;
1084       } finally {
1085           _lock.unlock();
1086       }
1087   }
1088
1089   @Override
1090   public final GLDrawable getDelegatedDrawable() {
1091     return drawable;
1092   }
1093
1094   @Override
1095   public GLContext getContext() {
1096     return context;
1097   }
1098
1099   @Override
1100   public GL getGL() {
1101     if( Beans.isDesignTime() ) {
1102       return null;
1103     }
1104     final GLContext _context = context;
1105     return (_context == null) ? null : _context.getGL();
1106   }
1107
1108   @Override
1109   public GL setGL(GL gl) {
1110     final GLContext _context = context;
1111     if (_context != null) {
1112       _context.setGL(gl);
1113       return gl;
1114     }
1115     return null;
1116   }
1117
1118
1119   @Override
1120   public void setAutoSwapBufferMode(boolean onOrOff) {
1121     helper.setAutoSwapBufferMode(onOrOff);
1122   }
1123
1124   @Override
1125   public boolean getAutoSwapBufferMode() {
1126     return helper.getAutoSwapBufferMode();
1127   }
1128
1129   @Override
1130   public void swapBuffers() {
1131     Threading.invoke(true, swapBuffersOnEDTAction, getTreeLock());
1132   }
1133
1134   @Override
1135   public void setContextCreationFlags(int flags) {
1136     additionalCtxCreationFlags = flags;
1137     final GLContext _context = context;
1138     if(null != _context) {
1139       _context.setContextCreationFlags(additionalCtxCreationFlags);
1140     }
1141   }
1142
1143   @Override
1144   public int getContextCreationFlags() {
1145     return additionalCtxCreationFlags;
1146   }
1147
1148   @Override
1149   public GLProfile getGLProfile() {
1150     return capsReqUser.getGLProfile();
1151   }
1152
1153   @Override
1154   public GLCapabilitiesImmutable getChosenGLCapabilities() {
1155     if( Beans.isDesignTime() ) {
1156         return capsReqUser;
1157     } else if( null == awtConfig ) {
1158         throw new GLException("No AWTGraphicsConfiguration: "+this);
1159     }
1160     return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities();
1161   }
1162
1163   public GLCapabilitiesImmutable getRequestedGLCapabilities() {
1164     if( null == awtConfig ) {
1165         return capsReqUser;
1166     }
1167     return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities();
1168   }
1169
1170   @Override
1171   public int getSurfaceWidth() {
1172       return getWidth() * hasPixelScale[0];
1173   }
1174
1175   @Override
1176   public int getSurfaceHeight() {
1177       return getHeight() * hasPixelScale[1];
1178   }
1179
1180   @Override
1181   public boolean isGLOriented() {
1182     final GLDrawable _drawable = drawable;
1183     return null != _drawable ? _drawable.isGLOriented() : true;
1184   }
1185
1186   @Override
1187   public NativeSurface getNativeSurface() {
1188     final GLDrawable _drawable = drawable;
1189     return (null != _drawable) ? _drawable.getNativeSurface() : null;
1190   }
1191
1192   @Override
1193   public long getHandle() {
1194     final GLDrawable _drawable = drawable;
1195     return (null != _drawable) ? _drawable.getHandle() : 0;
1196   }
1197
1198   @Override
1199   public GLDrawableFactory getFactory() {
1200     final GLDrawable _drawable = drawable;
1201     return (null != _drawable) ? _drawable.getFactory() : null;
1202   }
1203
1204   @Override
1205   public String toString() {
1206     final GLDrawable _drawable = drawable;
1207     final int dw = (null!=_drawable) ? _drawable.getSurfaceWidth() : -1;
1208     final int dh = (null!=_drawable) ? _drawable.getSurfaceHeight() : -1;
1209
1210     return "AWT-GLCanvas[Realized "+isRealized()+
1211                           ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+
1212                           ",\n\tFactory   "+getFactory()+
1213                           ",\n\thandle    0x"+Long.toHexString(getHandle())+
1214                           ",\n\tDrawable size "+dw+"x"+dh+" surface["+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+
1215                           ",\n\tAWT[pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+
1216                           ",\n\tvisible "+isVisible()+", displayable "+isDisplayable()+", showing "+isShowing+
1217                           ",\n\t"+awtConfig+"]]";
1218   }
1219
1220   //----------------------------------------------------------------------
1221   // Internals only below this point
1222   //
1223
1224   private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; }
1225
1226   private final Runnable destroyOnEDTAction = new Runnable() {
1227     @Override
1228     public void run() {
1229         final RecursiveLock _lock = lock;
1230         _lock.lock();
1231         try {
1232             final GLAnimatorControl animator =  getAnimator();
1233
1234             if(DEBUG) {
1235                 System.err.println(getThreadName()+": Info: destroyOnEDTAction() - START, hasContext " +
1236                         (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator);
1237                 // Thread.dumpStack();
1238             }
1239
1240             final boolean animatorPaused;
1241             if(null!=animator) {
1242                 // can't remove us from animator for recreational addNotify()
1243                 animatorPaused = animator.pause();
1244             } else {
1245                 animatorPaused = false;
1246             }
1247
1248             // OLS will be detached by disposeGL's context destruction below
1249             if( null != context ) {
1250                 if( context.isCreated() ) {
1251                     // Catch dispose GLExceptions by GLEventListener, just 'print' them
1252                     // so we can continue with the destruction.
1253                     try {
1254                         helper.disposeGL(GLCanvas.this, context, true);
1255                         if(DEBUG) {
1256                             System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context);
1257                         }
1258                     } catch (GLException gle) {
1259                         gle.printStackTrace();
1260                     }
1261                 }
1262                 context = null;
1263             }
1264             if( null != drawable ) {
1265                 drawable.setRealized(false);
1266                 if(DEBUG) {
1267                     System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable);
1268                 }
1269                 drawable = null;
1270             }
1271
1272             if(animatorPaused) {
1273                 animator.resume();
1274             }
1275
1276             if(DEBUG) {
1277                 System.err.println(getThreadName()+": dispose() - END, animator "+animator);
1278             }
1279
1280         } finally {
1281             _lock.unlock();
1282         }
1283     }
1284   };
1285
1286   /**
1287    * Disposes the JAWTWindow and AbstractGraphicsDevice within EDT,
1288    * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
1289    * <p>
1290    * The drawable and context handle are null'ed as well, assuming {@link #destroy()} has been called already.
1291    * </p>
1292    *
1293    * @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice)
1294    */
1295   private final Runnable disposeJAWTWindowAndAWTDeviceOnEDT = new Runnable() {
1296     @Override
1297     public void run() {
1298         context=null;
1299         drawable=null;
1300
1301         if( null != jawtWindow ) {
1302             jawtWindow.destroy();
1303             if(DEBUG) {
1304                 System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow);
1305             }
1306             jawtWindow=null;
1307         }
1308         hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
1309         hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
1310
1311         if(null != awtConfig) {
1312             final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration();
1313             final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice();
1314             final String adeviceMsg;
1315             if(DEBUG) {
1316                 adeviceMsg = adevice.toString();
1317             } else {
1318                 adeviceMsg = null;
1319             }
1320             boolean closed = adevice.close();
1321             if(DEBUG) {
1322                 System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed);
1323             }
1324         }
1325         awtConfig=null;
1326     }
1327   };
1328
1329   private final Runnable initAction = new Runnable() {
1330     @Override
1331     public void run() {
1332       helper.init(GLCanvas.this, !sendReshape);
1333     }
1334   };
1335
1336   private final Runnable displayAction = new Runnable() {
1337     @Override
1338     public void run() {
1339       if (sendReshape) {
1340         if(DEBUG) {
1341             System.err.println(getThreadName()+": Reshape: "+getSurfaceWidth()+"x"+getSurfaceHeight());
1342         }
1343         // Note: we ignore the given x and y within the parent component
1344         // since we are drawing directly into this heavyweight component.
1345         helper.reshape(GLCanvas.this, 0, 0, getSurfaceWidth(), getSurfaceHeight());
1346         sendReshape = false;
1347       }
1348
1349       helper.display(GLCanvas.this);
1350     }
1351   };
1352
1353   private final Runnable displayOnEDTAction = new Runnable() {
1354     @Override
1355     public void run() {
1356         final RecursiveLock _lock = lock;
1357         _lock.lock();
1358         try {
1359             if( null != drawable && drawable.isRealized() ) {
1360                 helper.invokeGL(drawable, context, displayAction, initAction);
1361             }
1362         } finally {
1363             _lock.unlock();
1364         }
1365     }
1366   };
1367
1368   private final Runnable swapBuffersOnEDTAction = new Runnable() {
1369     @Override
1370     public void run() {
1371         final RecursiveLock _lock = lock;
1372         _lock.lock();
1373         try {
1374             if( null != drawable && drawable.isRealized() ) {
1375                 drawable.swapBuffers();
1376             }
1377         } finally {
1378             _lock.unlock();
1379         }
1380     }
1381   };
1382
1383   private class DisposeGLEventListenerAction implements Runnable {
1384     GLEventListener listener;
1385     private final boolean remove;
1386     private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
1387         this.listener = listener;
1388         this.remove = remove;
1389     }
1390
1391     @Override
1392     public void run() {
1393         final RecursiveLock _lock = lock;
1394         _lock.lock();
1395         try {
1396             listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove);
1397         } finally {
1398             _lock.unlock();
1399         }
1400     }
1401   };
1402
1403   // Disables the AWT's erasing of this Canvas's background on Windows
1404   // in Java SE 6. This internal API is not available in previous
1405   // releases, but the system property
1406   // -Dsun.awt.noerasebackground=true can be specified to get similar
1407   // results globally in previous releases.
1408   private static boolean disableBackgroundEraseInitialized;
1409   private static Method  disableBackgroundEraseMethod;
1410   private void disableBackgroundErase() {
1411     if (!disableBackgroundEraseInitialized) {
1412       try {
1413         AccessController.doPrivileged(new PrivilegedAction<Object>() {
1414             @Override
1415             public Object run() {
1416               try {
1417                 Class<?> clazz = getToolkit().getClass();
1418                 while (clazz != null && disableBackgroundEraseMethod == null) {
1419                   try {
1420                     disableBackgroundEraseMethod =
1421                       clazz.getDeclaredMethod("disableBackgroundErase",
1422                                               new Class[] { Canvas.class });
1423                     disableBackgroundEraseMethod.setAccessible(true);
1424                   } catch (Exception e) {
1425                     clazz = clazz.getSuperclass();
1426                   }
1427                 }
1428               } catch (Exception e) {
1429               }
1430               return null;
1431             }
1432           });
1433       } catch (Exception e) {
1434       }
1435       disableBackgroundEraseInitialized = true;
1436       if(DEBUG) {
1437         System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase method found: "+
1438                 (null!=disableBackgroundEraseMethod));
1439       }
1440     }
1441     if (disableBackgroundEraseMethod != null) {
1442       Throwable t=null;
1443       try {
1444         disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this });
1445       } catch (Exception e) {
1446         t = e;
1447       }
1448       if(DEBUG) {
1449         System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase error: "+t);
1450       }
1451     }
1452   }
1453
1454   /**
1455    * Issues the GraphicsConfigurationFactory's choosing facility within EDT,
1456    * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
1457    *
1458    * @param capsChosen
1459    * @param capsRequested
1460    * @param chooser
1461    * @param device
1462    * @return the chosen AWTGraphicsConfiguration
1463    *
1464    * @see #disposeJAWTWindowAndAWTDeviceOnEDT
1465    */
1466   private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen,
1467                                                                final GLCapabilitiesImmutable capsRequested,
1468                                                                final GLCapabilitiesChooser chooser,
1469                                                                final GraphicsDevice device) {
1470     // Make GLCanvas behave better in NetBeans GUI builder
1471     if( Beans.isDesignTime() ) {
1472       return null;
1473     }
1474
1475     final AbstractGraphicsScreen aScreen = null != device ?
1476             AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT):
1477             AWTGraphicsScreen.createDefault();
1478     AWTGraphicsConfiguration config = null;
1479
1480     if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
1481         config = (AWTGraphicsConfiguration)
1482                 GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen,
1483                                                                                                              capsRequested,
1484                                                                                                              chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
1485     } else {
1486         try {
1487             final ArrayList<AWTGraphicsConfiguration> bucket = new ArrayList<AWTGraphicsConfiguration>(1);
1488             EventQueue.invokeAndWait(new Runnable() {
1489                 @Override
1490                 public void run() {
1491                     AWTGraphicsConfiguration c = (AWTGraphicsConfiguration)
1492                             GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen,
1493                                                                                                                          capsRequested,
1494                                                                                                                          chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
1495                     bucket.add(c);
1496                 }
1497             });
1498             config = ( bucket.size() > 0 ) ? bucket.get(0) : null ;
1499         } catch (InvocationTargetException e) {
1500             throw new GLException(e.getTargetException());
1501         } catch (InterruptedException e) {
1502             throw new GLException(e);
1503         }
1504     }
1505
1506     if (config == null) {
1507       throw new GLException("Error: Couldn't fetch AWTGraphicsConfiguration");
1508     }
1509
1510     return config;
1511   }
1512
1513   protected static String getThreadName() { return Thread.currentThread().getName(); }
1514
1515   /**
1516    * A most simple JOGL AWT test entry
1517    */
1518   public static void main(String args[]) {
1519     System.err.println(VersionUtil.getPlatformInfo());
1520     System.err.println(GlueGenVersion.getInstance());
1521     // System.err.println(NativeWindowVersion.getInstance());
1522     System.err.println(JoglVersion.getInstance());
1523
1524     System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString());
1525
1526     final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) );
1527     final Frame frame = new Frame("JOGL AWT Test");
1528
1529     final GLCanvas glCanvas = new GLCanvas(caps);
1530     frame.add(glCanvas);
1531     frame.setSize(128, 128);
1532
1533     glCanvas.addGLEventListener(new GLEventListener() {
1534         @Override
1535         public void init(GLAutoDrawable drawable) {
1536             GL gl = drawable.getGL();
1537             System.err.println(JoglVersion.getGLInfo(gl, null));
1538         }
1539         @Override
1540         public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }
1541         @Override
1542         public void display(GLAutoDrawable drawable) { }
1543         @Override
1544         public void dispose(GLAutoDrawable drawable) { }
1545     });
1546
1547     try {
1548         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
1549             @Override
1550             public void run() {
1551                 frame.setVisible(true);
1552             }});
1553     } catch (Throwable t) {
1554         t.printStackTrace();
1555     }
1556     glCanvas.display();
1557     try {
1558         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
1559             @Override
1560             public void run() {
1561                 frame.dispose();
1562             }});
1563     } catch (Throwable t) {
1564         t.printStackTrace();
1565     }
1566   }
1567
1568 }
http://JogAmp.org git info: FAQ, tutorial and man pages.