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