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