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