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