Jogamp
Code cleanup: override, imports, StringBuilder, ..
[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
48 import java.awt.Canvas;
49 import java.awt.Color;
50 import java.awt.FontMetrics;
51 import java.awt.Frame;
52 import java.awt.Graphics;
53 import java.awt.GraphicsConfiguration;
54 import java.awt.GraphicsDevice;
55 import java.awt.geom.Rectangle2D;
56
57 import java.awt.EventQueue;
58 import java.lang.reflect.InvocationTargetException;
59 import java.util.ArrayList;
60 import java.util.List;
61
62 import javax.media.nativewindow.WindowClosingProtocol;
63 import javax.media.nativewindow.AbstractGraphicsConfiguration;
64 import javax.media.nativewindow.AbstractGraphicsDevice;
65 import javax.media.nativewindow.AbstractGraphicsScreen;
66 import javax.media.nativewindow.GraphicsConfigurationFactory;
67 import javax.media.nativewindow.NativeSurface;
68 import javax.media.nativewindow.NativeWindowFactory;
69 import javax.media.nativewindow.awt.AWTGraphicsConfiguration;
70 import javax.media.nativewindow.awt.AWTGraphicsDevice;
71 import javax.media.nativewindow.awt.AWTGraphicsScreen;
72 import javax.media.nativewindow.awt.AWTWindowClosingProtocol;
73
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.Threading;
88
89 import com.jogamp.nativewindow.NativeWindowVersion;
90 import com.jogamp.common.GlueGenVersion;
91 import com.jogamp.common.util.VersionUtil;
92 import com.jogamp.opengl.JoglVersion;
93
94 import com.jogamp.common.util.locks.RecursiveLock;
95 import jogamp.opengl.Debug;
96 import jogamp.opengl.GLContextImpl;
97 import jogamp.opengl.GLDrawableHelper;
98 import jogamp.opengl.ThreadingImpl;
99
100 // FIXME: Subclasses need to call resetGLFunctionAvailability() on their
101 // context whenever the displayChanged() function is called on our
102 // GLEventListeners
103
104 /** A heavyweight AWT component which provides OpenGL rendering
105     support. This is the primary implementation of an AWT {@link GLDrawable};
106     {@link GLJPanel} is provided for compatibility with Swing user
107     interfaces when adding a heavyweight doesn't work either because
108     of Z-ordering or LayoutManager problems.
109  *
110  * <h5><A NAME="java2dgl">Java2D OpenGL Remarks</A></h5>
111  *
112  * To avoid any conflicts with a potential Java2D OpenGL context,<br>
113  * you shall consider setting the following JVM properties:<br>
114  * <ul>
115  *    <li><pre>sun.java2d.opengl=false</pre></li>
116  *    <li><pre>sun.java2d.noddraw=true</pre></li>
117  * </ul>
118  * This is especially true in case you want to utilize a GLProfile other than
119  * {@link GLProfile#GL2}, eg. using {@link GLProfile#getMaxFixedFunc()}.<br>
120  * On the other hand, if you like to experiment with GLJPanel's utilization
121  * of Java2D's OpenGL pipeline, you have to set them to
122  * <ul>
123  *    <li><pre>sun.java2d.opengl=true</pre></li>
124  *    <li><pre>sun.java2d.noddraw=true</pre></li>
125  * </ul>
126  *
127  * <h5><A NAME="backgrounderase">Disable Background Erase</A></h5>
128  *
129  * GLCanvas tries to disable background erase for the AWT Canvas
130  * before native peer creation (X11) and after it (Windows), <br>
131  * utilizing the optional {@link java.awt.Toolkit} method <code>disableBeackgroundErase(java.awt.Canvas)</code>.<br>
132  * However if this does not give you the desired results, you may want to disable AWT background erase in general:
133  * <ul>
134  *   <li><pre>sun.awt.noerasebackground=true</pre></li>
135  * </ul>
136  */
137
138 public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol {
139
140   private static final boolean DEBUG;
141
142   static {
143       DEBUG = Debug.debug("GLCanvas");
144   }
145
146   private GLDrawableHelper drawableHelper = new GLDrawableHelper();
147   private AWTGraphicsConfiguration awtConfig;
148   private GLDrawable drawable;
149   private GLContextImpl context;
150   private boolean sendReshape = false;
151   
152   // copy of the cstr args, mainly for recreation
153   private GLCapabilitiesImmutable capsReqUser;
154   private GLCapabilitiesChooser chooser;
155   private GLContext shareWith;  
156   private GraphicsDevice device;
157
158   private AWTWindowClosingProtocol awtWindowClosingProtocol =
159           new AWTWindowClosingProtocol(this, new Runnable() {
160                 public void run() {
161                     GLCanvas.this.destroy();
162                 }
163             });
164
165   /** Creates a new GLCanvas component with a default set of OpenGL
166       capabilities, using the default OpenGL capabilities selection
167       mechanism, on the default screen device. */
168   public GLCanvas() {
169     this(null);
170   }
171
172   /** Creates a new GLCanvas component with the requested set of
173       OpenGL capabilities, using the default OpenGL capabilities
174       selection mechanism, on the default screen device. 
175    * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
176    */
177   public GLCanvas(GLCapabilitiesImmutable capsReqUser) {
178     this(capsReqUser, null, null, null);
179   }
180
181   /** Creates a new GLCanvas component with the requested set of
182       OpenGL capabilities, using the default OpenGL capabilities
183       selection mechanism, on the default screen device.
184    *  This constructor variant also supports using a shared GLContext.
185    *
186    * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
187    */
188   public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLContext shareWith) {
189     this(capsReqUser, null, shareWith, null);
190   }
191
192   /** Creates a new GLCanvas component. The passed GLCapabilities
193       specifies the OpenGL capabilities for the component; if null, a
194       default set of capabilities is used. The GLCapabilitiesChooser
195       specifies the algorithm for selecting one of the available
196       GLCapabilities for the component; a DefaultGLCapabilitesChooser
197       is used if null is passed for this argument. The passed
198       GLContext specifies an OpenGL context with which to share
199       textures, display lists and other OpenGL state, and may be null
200       if sharing is not desired. See the note in the overview
201       documentation on <a
202       href="../../../overview-summary.html#SHARING">context
203       sharing</a>. The passed GraphicsDevice indicates the screen on
204       which to create the GLCanvas; the GLDrawableFactory uses the
205       default screen device of the local GraphicsEnvironment if null
206       is passed for this argument. */
207   public GLCanvas(GLCapabilitiesImmutable capsReqUser,
208                   GLCapabilitiesChooser chooser,
209                   GLContext shareWith,
210                   GraphicsDevice device) {
211     /*
212      * Determination of the native window is made in 'super.addNotify()',
213      * which creates the native peer using AWT's GraphicsConfiguration.
214      * GraphicsConfiguration is returned by this class overwritten
215      * 'getGraphicsConfiguration()', which returns our OpenGL compatible
216      * 'chosen' GraphicsConfiguration.
217      */
218     super();
219
220     if(null==capsReqUser) {
221         capsReqUser = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDesktopDevice()));
222     } else {
223         // don't allow the user to change data
224         capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable();
225     }
226
227     if(null==device) {
228         GraphicsConfiguration gc = super.getGraphicsConfiguration();
229         if(null!=gc) {
230             device = gc.getDevice();
231         }
232     }
233
234     // instantiation will be issued in addNotify()
235     this.capsReqUser = capsReqUser;
236     this.chooser = chooser;
237     this.shareWith = shareWith;
238     this.device = device;
239   }
240
241   /**
242    * Overridden to choose a GraphicsConfiguration on a parent container's
243    * GraphicsDevice because both devices
244    */
245     @Override
246   public GraphicsConfiguration getGraphicsConfiguration() {
247     /*
248      * Workaround for problems with Xinerama and java.awt.Component.checkGD
249      * when adding to a container on a different graphics device than the
250      * one that this Canvas is associated with.
251      * 
252      * GC will be null unless:
253      *   - A native peer has assigned it. This means we have a native
254      *     peer, and are already comitted to a graphics configuration.
255      *   - This canvas has been added to a component hierarchy and has
256      *     an ancestor with a non-null GC, but the native peer has not
257      *     yet been created. This means we can still choose the GC on
258      *     all platforms since the peer hasn't been created.
259      */
260     final GraphicsConfiguration gc = super.getGraphicsConfiguration();
261     /*
262      * chosen is only non-null on platforms where the GLDrawableFactory
263      * returns a non-null GraphicsConfiguration (in the GLCanvas
264      * constructor).
265      * 
266      * if gc is from this Canvas' native peer then it should equal chosen,
267      * otherwise it is from an ancestor component that this Canvas is being
268      * added to, and we go into this block.
269      */
270     GraphicsConfiguration chosen =  awtConfig.getGraphicsConfiguration();
271
272     if (gc != null && chosen != null && !chosen.equals(gc)) {
273       /*
274        * Check for compatibility with gc. If they differ by only the
275        * device then return a new GCconfig with the super-class' GDevice
276        * (and presumably the same visual ID in Xinerama).
277        * 
278        */
279       if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) {
280         /*
281          * Here we select a GraphicsConfiguration on the alternate
282          * device that is presumably identical to the chosen
283          * configuration, but on the other device.
284          * 
285          * Should really check to ensure that we select a configuration
286          * with the same X visual ID for Xinerama screens, otherwise the
287          * GLDrawable may have the wrong visual ID (I don't think this
288          * ever gets updated). May need to add a method to
289          * X11GLDrawableFactory to do this in a platform specific
290          * manner.
291          * 
292          * However, on platforms where we can actually get into this
293          * block, both devices should have the same visual list, and the
294          * same configuration should be selected here.
295          */
296         AWTGraphicsConfiguration config = chooseGraphicsConfiguration( (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(),
297                                                                        (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(),
298                                                                        chooser, gc.getDevice());
299         final GraphicsConfiguration compatible = (null!=config)?config.getGraphicsConfiguration():null;
300         boolean equalCaps = config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities());
301         if(DEBUG) {
302             Exception e = new Exception("Info: Call Stack: "+Thread.currentThread().getName());
303             e.printStackTrace();
304             System.err.println("!!! Created Config (n): HAVE    GC "+chosen);
305             System.err.println("!!! Created Config (n): THIS    GC "+gc);
306             System.err.println("!!! Created Config (n): Choosen GC "+compatible);
307             System.err.println("!!! Created Config (n): HAVE    CF "+awtConfig);
308             System.err.println("!!! Created Config (n): Choosen CF "+config);
309             System.err.println("!!! Created Config (n): EQUALS CAPS "+equalCaps);
310         }
311
312         if (compatible != null) {
313           /*
314            * Save the new GC for equals test above, and to return to
315            * any outside callers of this method.
316            */
317           chosen = compatible;
318
319           awtConfig = config;
320
321           if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) {
322               dispose(true);
323           }
324         }
325       }
326
327       /*
328        * If a compatible GC was not found in the block above, this will
329        * return the GC that was selected in the constructor (and might
330        * cause an exception in Component.checkGD when adding to a
331        * container, but in this case that would be the desired behavior).
332        * 
333        */
334       return chosen;
335     } else if (gc == null) {
336       /*
337        * The GC is null, which means we have no native peer, and are not
338        * part of a (realized) component hierarchy. So we return the
339        * desired visual that was selected in the constructor (possibly
340        * null).
341        */
342       return chosen;
343     }
344
345     /*
346      * Otherwise we have not explicitly selected a GC in the constructor, so
347      * just return what Canvas would have.
348      */
349     return gc;
350   }
351   
352   public GLContext createContext(GLContext shareWith) {
353       drawableSync.lock();
354       try {
355         return (null != drawable) ? drawable.createContext(shareWith) : null;
356       } finally {
357         drawableSync.unlock();
358       }
359   }
360
361   public void setRealized(boolean realized) {
362   }
363
364   public boolean isRealized() {
365       drawableSync.lock();
366       try {
367         return ( null != drawable ) ? drawable.isRealized() : false;
368       } finally {
369         drawableSync.unlock();
370       }
371   }
372
373   public int getDefaultCloseOperation() {
374       return awtWindowClosingProtocol.getDefaultCloseOperation();
375   }
376
377   public int setDefaultCloseOperation(int op) {
378       return awtWindowClosingProtocol.setDefaultCloseOperation(op);
379   }
380
381   public void display() {
382     if( !validateGLDrawable() ) {
383         return; // not yet available ..
384     }
385     maybeDoSingleThreadedWorkaround(displayOnEventDispatchThreadAction,
386                                     displayAction);
387
388     awtWindowClosingProtocol.addClosingListenerOneShot();
389   }
390
391   private void dispose(boolean regenerate) {
392     drawableSync.lock();
393     try {
394         if(DEBUG) {
395             Exception ex1 = new Exception("Info: dispose("+regenerate+") - start, hasContext " +
396                     (null!=context) + ", hasDrawable " + (null!=drawable));
397             ex1.printStackTrace();
398         }
399
400         if(null!=context) {
401             boolean animatorPaused = false;
402             GLAnimatorControl animator =  getAnimator();
403             if(null!=animator) {
404                 // can't remove us from animator for recreational addNotify()
405                 animatorPaused = animator.pause();
406             }
407
408             disposeRegenerate=regenerate;
409
410             if (Threading.isSingleThreaded() &&
411                 !Threading.isOpenGLThread()) {
412               // Workaround for termination issues with applets --
413               // sun.applet.AppletPanel should probably be performing the
414               // remove() call on the EDT rather than on its own thread
415               // Hint: User should run remove from EDT.
416               if (ThreadingImpl.isAWTMode() &&
417                   Thread.holdsLock(getTreeLock())) {
418                 // The user really should not be invoking remove() from this
419                 // thread -- but since he/she is, we can not go over to the
420                 // EDT at this point. Try to destroy the context from here.
421                 if(context.isCreated()) {
422                     drawableHelper.invokeGL(drawable, context, disposeAction, null);
423                 }
424               } else if(context.isCreated()) {
425                 Threading.invokeOnOpenGLThread(disposeOnEventDispatchThreadAction);
426               }
427             } else if(context.isCreated()) {
428               drawableHelper.invokeGL(drawable, context, disposeAction, null);
429             }
430
431             if(animatorPaused) {
432                 animator.resume();
433             }
434         }
435         if(!regenerate) {
436             disposeAbstractGraphicsDevice();
437         }
438
439         if(DEBUG) {
440             System.err.println("dispose("+regenerate+") - stop");
441         }
442     } finally {
443         drawableSync.unlock();
444     }
445   }
446
447   /**
448    * Just an alias for removeNotify
449    */
450   public void destroy() {
451     removeNotify();
452   }
453
454   /** Overridden to cause OpenGL rendering to be performed during
455       repaint cycles. Subclasses which override this method must call
456       super.paint() in their paint() method in order to function
457       properly. <P>
458
459       <B>Overrides:</B>
460       <DL><DD><CODE>paint</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
461     @Override
462   public void paint(Graphics g) {
463     if (Beans.isDesignTime()) {
464       // Make GLCanvas behave better in NetBeans GUI builder
465       g.setColor(Color.BLACK);
466       g.fillRect(0, 0, getWidth(), getHeight());
467       FontMetrics fm = g.getFontMetrics();
468       String name = getName();
469       if (name == null) {
470         name = getClass().getName();
471         int idx = name.lastIndexOf('.');
472         if (idx >= 0) {
473           name = name.substring(idx + 1);
474         }
475       }
476       Rectangle2D bounds = fm.getStringBounds(name, g);
477       g.setColor(Color.WHITE);
478       g.drawString(name,
479                    (int) ((getWidth()  - bounds.getWidth())  / 2),
480                    (int) ((getHeight() + bounds.getHeight()) / 2));
481       return;
482     }
483     if( ! this.drawableHelper.isExternalAnimatorAnimating() ) {
484         display();
485     }
486   }
487
488   RecursiveLock drawableSync = new RecursiveLock();
489
490   /** Overridden to track when this component is added to a container.
491       Subclasses which override this method must call
492       super.addNotify() in their addNotify() method in order to
493       function properly. <P>
494
495       <B>Overrides:</B>
496       <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
497     @Override
498   public void addNotify() {
499     if(DEBUG) {
500         Exception ex1 = new Exception(Thread.currentThread().getName()+" - Info: addNotify - start, bounds: "+this.getBounds());
501         ex1.printStackTrace();
502     }
503
504     drawableSync.lock();
505     try {
506         /**
507          * 'super.addNotify()' determines the GraphicsConfiguration,
508          * while calling this class's overriden 'getGraphicsConfiguration()' method
509          * after which it creates the native peer.
510          * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration
511          * is being used in getGraphicsConfiguration().
512          * This code order also allows recreation, ie re-adding the GLCanvas.
513          */
514         awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device);
515         if(null==awtConfig) {
516             throw new GLException("Error: NULL AWTGraphicsConfiguration");
517         }
518
519         if (!Beans.isDesignTime()) {
520             // no lock required, since this resource ain't available yet
521             drawable = GLDrawableFactory.getFactory(capsReqUser.getGLProfile())
522                            .createGLDrawable(NativeWindowFactory.getNativeWindow(this, awtConfig));
523             context = (GLContextImpl) drawable.createContext(shareWith);
524             context.setSynchronized(true);
525         }
526
527         // before native peer is valid: X11
528         disableBackgroundErase();
529
530         // issues getGraphicsConfiguration() and creates the native peer
531         super.addNotify();
532
533         // after native peer is valid: Windows
534         disableBackgroundErase();
535
536         // init drawable by paint/display makes the init sequence more equal
537         // for all launch flavors (applet/javaws/..)
538         // validateGLDrawable();
539
540         if(DEBUG) {
541             System.err.println(Thread.currentThread().getName()+" - Info: addNotify - end: peer: "+getPeer());
542         }
543     } finally {
544        drawableSync.unlock();
545     }
546   }
547
548   private boolean validateGLDrawable() {
549     boolean realized = false;
550     if (!Beans.isDesignTime()) {
551         drawableSync.lock();
552         try {
553             if ( null != drawable ) {
554                 realized = drawable.isRealized();
555                 if ( !realized && 0 < drawable.getWidth() * drawable.getHeight() ) {
556                     drawable.setRealized(true);
557                     realized = true;
558                     sendReshape=true; // ensure a reshape is being send ..
559                     if(DEBUG) {
560                         String msg = Thread.currentThread().getName()+" - Realized Drawable: "+drawable.toString();
561                         // System.err.println(msg);
562                         Throwable t = new Throwable(msg);
563                         t.printStackTrace();
564                     }
565                 }
566             }
567         } finally {
568            drawableSync.unlock();
569         }
570     }
571     return realized;
572   }
573
574   /** <p>Overridden to track when this component is removed from a
575       container. Subclasses which override this method must call
576       super.removeNotify() in their removeNotify() method in order to
577       function properly. </p>
578       <p>User shall not call this method outside of EDT, read the AWT/Swing specs
579       about this.</p>
580       <B>Overrides:</B>
581       <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
582     @Override
583   public void removeNotify() {
584     if(DEBUG) {
585         Exception ex1 = new Exception(Thread.currentThread().getName()+" - Info: removeNotify - start");
586         ex1.printStackTrace();
587     }
588
589     awtWindowClosingProtocol.removeClosingListener();
590
591     if (Beans.isDesignTime()) {
592       super.removeNotify();
593     } else {
594       drawableSync.lock();
595       try {
596         dispose(false);
597       } finally {
598         context=null;
599         drawable=null;
600         awtConfig=null;
601         super.removeNotify();
602         drawableSync.unlock();
603       }
604     }
605     if(DEBUG) {
606         System.err.println(Thread.currentThread().getName()+" - Info: removeNotify - end, peer: "+getPeer());
607     }
608   }
609
610   /** Overridden to cause {@link GLDrawableHelper#reshape} to be
611       called on all registered {@link GLEventListener}s. Subclasses
612       which override this method must call super.reshape() in
613       their reshape() method in order to function properly. <P>
614
615       <B>Overrides:</B>
616       <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
617     @Override
618   public void reshape(int x, int y, int width, int height) {
619     super.reshape(x, y, width, height);
620     sendReshape = true;
621   }
622
623   /** <B>Overrides:</B>
624       <DL><DD><CODE>update</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
625   /** 
626    * Overridden from Canvas to prevent the AWT's clearing of the
627    * canvas from interfering with the OpenGL rendering.
628    */
629     @Override
630   public void update(Graphics g) {
631     paint(g);
632   }
633   
634   public void addGLEventListener(GLEventListener listener) {
635     drawableHelper.addGLEventListener(listener);
636   }
637
638   public void addGLEventListener(int index, GLEventListener listener) {
639     drawableHelper.addGLEventListener(index, listener);
640   }
641
642   public void removeGLEventListener(GLEventListener listener) {
643     drawableHelper.removeGLEventListener(listener);
644   }
645
646   public void setAnimator(GLAnimatorControl animatorControl) {
647     drawableHelper.setAnimator(animatorControl);
648   }
649
650   public GLAnimatorControl getAnimator() {
651     return drawableHelper.getAnimator();
652   }
653
654   public void invoke(boolean wait, GLRunnable glRunnable) {
655     drawableHelper.invoke(this, wait, glRunnable);
656   }
657
658   public void setContext(GLContext ctx) {
659     context=(GLContextImpl)ctx;
660   }
661
662   public GLContext getContext() {
663     return context;
664   }
665
666   public GL getGL() {
667     if (Beans.isDesignTime()) {
668       return null;
669     }
670     GLContext ctx = getContext();
671     return (ctx == null) ? null : ctx.getGL();
672   }
673
674   public GL setGL(GL gl) {
675     GLContext ctx = getContext();
676     if (ctx != null) {
677       ctx.setGL(gl);
678       return gl;
679     }
680     return null;
681   }
682
683
684   public void setAutoSwapBufferMode(boolean onOrOff) {
685     drawableHelper.setAutoSwapBufferMode(onOrOff);
686   }
687
688   public boolean getAutoSwapBufferMode() {
689     return drawableHelper.getAutoSwapBufferMode();
690   }
691
692   public void swapBuffers() {
693     maybeDoSingleThreadedWorkaround(swapBuffersOnEventDispatchThreadAction, swapBuffersAction);
694   }
695
696   public GLProfile getGLProfile() {
697     return capsReqUser.getGLProfile();
698   }
699
700   public GLCapabilitiesImmutable getChosenGLCapabilities() {
701     if (awtConfig == null) {
702         throw new GLException("No AWTGraphicsConfiguration: "+this);
703     }
704
705     return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities();
706   }
707
708   public GLCapabilitiesImmutable getRequestedGLCapabilities() {
709     if (awtConfig == null) {
710         throw new GLException("No AWTGraphicsConfiguration: "+this);
711     }
712
713     return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities();
714   }
715
716   public NativeSurface getNativeSurface() {
717       drawableSync.lock();
718       try {
719         return (null != drawable) ? drawable.getNativeSurface() : null;
720       } finally {
721         drawableSync.unlock();
722       }
723   }
724
725   public long getHandle() {
726       drawableSync.lock();
727       try {
728         return (null != drawable) ? drawable.getHandle() : 0;
729       } finally {
730         drawableSync.unlock();
731       }
732   }
733
734   public GLDrawableFactory getFactory() {
735       drawableSync.lock();
736       try {
737         return (null != drawable) ? drawable.getFactory() : null;
738       } finally {
739         drawableSync.unlock();
740       }
741   }
742
743     @Override
744   public String toString() {
745     return "AWT-GLCanvas[ "+awtConfig+", "+((null!=drawable)?drawable.getClass().getName():"null-drawable")+"]";
746   }
747
748   //----------------------------------------------------------------------
749   // Internals only below this point
750   //
751
752   private void maybeDoSingleThreadedWorkaround(Runnable eventDispatchThreadAction,
753                                                Runnable invokeGLAction) {
754     if (Threading.isSingleThreaded() &&
755         !Threading.isOpenGLThread()) {
756       Threading.invokeOnOpenGLThread(eventDispatchThreadAction);
757     } else {
758       drawableHelper.invokeGL(drawable, context, invokeGLAction, initAction);
759     }
760   }
761
762   class DisposeAction implements Runnable {
763     public void run() {
764       drawableHelper.dispose(GLCanvas.this);
765
766       if(null!=context) {
767         context.makeCurrent(); // implicit wait for lock ..
768         context.destroy();
769         context=null;
770       }
771
772       if(null!=drawable) {
773           drawable.setRealized(false);
774           drawable=null;
775       }
776
777       if(disposeRegenerate) {
778           // recreate GLDrawable to reflect it's new graphics configuration
779           drawable = GLDrawableFactory.getFactory(capsReqUser.getGLProfile())
780                         .createGLDrawable(NativeWindowFactory.getNativeWindow(GLCanvas.this, awtConfig));
781           if(DEBUG) {
782             System.err.println("GLCanvas.dispose(true): new drawable: "+drawable);
783           }
784           drawable.setRealized(true);
785           context = (GLContextImpl) drawable.createContext(shareWith);
786           context.setSynchronized(true);
787           sendReshape=true; // ensure a reshape is being send ..
788       }
789     }
790   }
791   private boolean disposeRegenerate;
792   private DisposeAction disposeAction = new DisposeAction();
793
794   private DisposeOnEventDispatchThreadAction disposeOnEventDispatchThreadAction =
795     new DisposeOnEventDispatchThreadAction();
796
797   class DisposeOnEventDispatchThreadAction implements Runnable {
798     public void run() {
799       drawableHelper.invokeGL(drawable, context, disposeAction, null);
800     }
801   }
802
803   class DisposeAbstractGraphicsDeviceAction implements Runnable {
804     public void run() {
805       AbstractGraphicsConfiguration aconfig = (null!=awtConfig) ? awtConfig.getNativeGraphicsConfiguration() : null;
806       AbstractGraphicsScreen ascreen = (null!=aconfig) ? aconfig.getScreen() : null;
807       AbstractGraphicsDevice adevice = (null!=ascreen) ? ascreen.getDevice() : null;
808       if(null!=adevice) {
809           String adeviceMsg=null;
810           if(DEBUG) {
811             adeviceMsg = adevice.toString();
812           }
813           boolean closed = adevice.close();
814           if(DEBUG) {
815             System.err.println(Thread.currentThread().getName() + " - GLCanvas.dispose(false): closed GraphicsDevice: "+adeviceMsg+", result: "+closed);
816           }
817       }
818       awtConfig=null;
819     }
820   }
821   private DisposeAbstractGraphicsDeviceAction disposeAbstractGraphicsDeviceAction = new DisposeAbstractGraphicsDeviceAction();
822
823   /**
824    * Disposes the AbstractGraphicsDevice within EDT,
825    * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
826    *
827    * @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice)
828    */
829   void disposeAbstractGraphicsDevice()  {
830     if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
831         disposeAbstractGraphicsDeviceAction.run();
832     } else {
833         try {
834             EventQueue.invokeAndWait(disposeAbstractGraphicsDeviceAction);
835         } catch (InvocationTargetException e) {
836             throw new GLException(e.getTargetException());
837         } catch (InterruptedException e) {
838             throw new GLException(e);
839         }
840     }
841   }
842
843   class InitAction implements Runnable {
844     public void run() {
845       drawableHelper.init(GLCanvas.this);
846     }
847   }
848   private InitAction initAction = new InitAction();
849   
850   class DisplayAction implements Runnable {
851     public void run() {
852       if (sendReshape) {
853         if(DEBUG) {
854             System.err.println(Thread.currentThread().getName()+" - reshape: "+getWidth()+"x"+getHeight());
855         }
856         // Note: we ignore the given x and y within the parent component
857         // since we are drawing directly into this heavyweight component.
858         drawableHelper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight());
859         sendReshape = false;
860       }
861
862       drawableHelper.display(GLCanvas.this);
863     }
864   }
865   private DisplayAction displayAction = new DisplayAction();
866
867   class SwapBuffersAction implements Runnable {
868     public void run() {
869       drawable.swapBuffers();
870     }
871   }
872   private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
873
874   // Workaround for ATI driver bugs related to multithreading issues
875   // like simultaneous rendering via Animators to canvases that are
876   // being resized on the AWT event dispatch thread
877   class DisplayOnEventDispatchThreadAction implements Runnable {
878     public void run() {
879       drawableHelper.invokeGL(drawable, context, displayAction, initAction);
880     }
881   }
882   private DisplayOnEventDispatchThreadAction displayOnEventDispatchThreadAction =
883     new DisplayOnEventDispatchThreadAction();
884   class SwapBuffersOnEventDispatchThreadAction implements Runnable {
885     public void run() {
886       drawableHelper.invokeGL(drawable, context, swapBuffersAction, initAction);
887     }
888   }
889   private SwapBuffersOnEventDispatchThreadAction swapBuffersOnEventDispatchThreadAction =
890     new SwapBuffersOnEventDispatchThreadAction();
891
892   // Disables the AWT's erasing of this Canvas's background on Windows
893   // in Java SE 6. This internal API is not available in previous
894   // releases, but the system property
895   // -Dsun.awt.noerasebackground=true can be specified to get similar
896   // results globally in previous releases.
897   private static boolean disableBackgroundEraseInitialized;
898   private static Method  disableBackgroundEraseMethod;
899   private void disableBackgroundErase() {
900     if (!disableBackgroundEraseInitialized) {
901       try {
902         AccessController.doPrivileged(new PrivilegedAction() {
903             public Object run() {
904               try {
905                 Class clazz = getToolkit().getClass();
906                 while (clazz != null && disableBackgroundEraseMethod == null) {
907                   try {
908                     disableBackgroundEraseMethod =
909                       clazz.getDeclaredMethod("disableBackgroundErase",
910                                               new Class[] { Canvas.class });
911                     disableBackgroundEraseMethod.setAccessible(true);
912                   } catch (Exception e) {
913                     clazz = clazz.getSuperclass();
914                   }
915                 }
916               } catch (Exception e) {
917               }
918               return null;
919             }
920           });
921       } catch (Exception e) {
922       }
923       disableBackgroundEraseInitialized = true;
924       if(DEBUG) {
925         System.err.println("GLCanvas: TK disableBackgroundErase method found: "+
926                 (null!=disableBackgroundEraseMethod));
927       }
928     }
929     if (disableBackgroundEraseMethod != null) {
930       Throwable t=null;
931       try {
932         disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this });
933       } catch (Exception e) {
934         t = e;
935       }
936       if(DEBUG) {
937         System.err.println("GLCanvas: TK disableBackgroundErase error: "+t);
938       }
939     }
940   }
941
942   /**
943    * Issues the GraphicsConfigurationFactory's choosing facility within EDT,
944    * since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
945    *
946    * @param capsChosen
947    * @param capsRequested
948    * @param chooser
949    * @param device
950    * @return the chosen AWTGraphicsConfiguration
951    *
952    * @see #disposeAbstractGraphicsDevice()
953    */
954   private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen,
955                                                                final GLCapabilitiesImmutable capsRequested,
956                                                                final GLCapabilitiesChooser chooser,
957                                                                final GraphicsDevice device) {
958     // Make GLCanvas behave better in NetBeans GUI builder
959     if (Beans.isDesignTime()) {
960       return null;
961     }
962
963     final AbstractGraphicsScreen aScreen = AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT);
964     AWTGraphicsConfiguration config = null;
965
966     if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
967         config = (AWTGraphicsConfiguration)
968                 GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen,
969                                                                                                              capsRequested,
970                                                                                                              chooser, aScreen);
971     } else {
972         try {
973             final ArrayList bucket = new ArrayList(1);
974             EventQueue.invokeAndWait(new Runnable() {
975                 public void run() {
976                     AWTGraphicsConfiguration c = (AWTGraphicsConfiguration)
977                             GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen,
978                                                                                                                          capsRequested,
979                                                                                                                          chooser, aScreen);
980                     bucket.add(c);
981                 }
982             });
983             config = ( bucket.size() > 0 ) ? (AWTGraphicsConfiguration)bucket.get(0) : null ;
984         } catch (InvocationTargetException e) {
985             throw new GLException(e.getTargetException());
986         } catch (InterruptedException e) {
987             throw new GLException(e);
988         }
989     }
990
991     if (config == null) {
992       throw new GLException("Error: Couldn't fetch AWTGraphicsConfiguration");
993     }
994
995     return config;
996   }
997   
998   /**
999    * A most simple JOGL AWT test entry
1000    */
1001   public static void main(String args[]) {
1002     System.err.println(VersionUtil.getPlatformInfo());
1003     System.err.println(GlueGenVersion.getInstance());
1004     System.err.println(NativeWindowVersion.getInstance());
1005     System.err.println(JoglVersion.getInstance());
1006
1007     GLDrawableFactory factory = GLDrawableFactory.getDesktopFactory();
1008     List/*<GLCapabilitiesImmutable>*/ availCaps = factory.getAvailableCapabilities(null);
1009     for(int i=0; i<availCaps.size(); i++) {
1010         System.err.println(availCaps.get(i));
1011     }
1012
1013     GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDesktopDevice()) );
1014     Frame frame = new Frame("JOGL AWT Test");
1015
1016     GLCanvas glCanvas = new GLCanvas(caps);
1017     frame.add(glCanvas);
1018     frame.setSize(128, 128);
1019
1020     glCanvas.addGLEventListener(new GLEventListener() {
1021         public void init(GLAutoDrawable drawable) {
1022             GL gl = drawable.getGL();
1023             System.err.println(JoglVersion.getGLInfo(gl, null));
1024         }
1025
1026         public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
1027         }
1028
1029         public void display(GLAutoDrawable drawable) {
1030         }
1031
1032         public void dispose(GLAutoDrawable drawable) {
1033         }
1034     });
1035
1036     final Frame _frame = frame;
1037     final GLCanvas _glCanvas = glCanvas;
1038
1039     try {
1040         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
1041             public void run() {
1042                 _frame.setVisible(true);
1043             }});
1044     } catch (Throwable t) {
1045         t.printStackTrace();
1046     }
1047     glCanvas.display();
1048     try {
1049         javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
1050             public void run() {
1051                 _frame.setVisible(false);
1052                 _frame.remove(_glCanvas);
1053                 _frame.dispose();
1054             }});
1055     } catch (Throwable t) {
1056         t.printStackTrace();
1057     }
1058   }
1059
1060 }
http://JogAmp.org git info: FAQ, tutorial and man pages.