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