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