Jogamp
5dab64e394fa2e3efb24772c9345d0ab0aa1d1c2
[jogl.git] / src / newt / classes / jogamp / newt / driver / awt / AWTCanvas.java
1 /*
2  * Copyright (c) 2008 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  */
34
35 package jogamp.newt.driver.awt;
36
37 import java.awt.Canvas;
38 import java.awt.Graphics;
39 import java.awt.GraphicsDevice;
40 import java.awt.GraphicsConfiguration;
41 import java.lang.reflect.Method;
42 import java.security.AccessController;
43 import java.security.PrivilegedAction;
44
45 import javax.media.nativewindow.AbstractGraphicsDevice;
46 import javax.media.nativewindow.AbstractGraphicsScreen;
47 import javax.media.nativewindow.CapabilitiesChooser;
48 import javax.media.nativewindow.CapabilitiesImmutable;
49 import javax.media.nativewindow.GraphicsConfigurationFactory;
50 import javax.media.nativewindow.NativeWindow;
51 import javax.media.nativewindow.NativeWindowException;
52 import javax.media.nativewindow.NativeWindowFactory;
53 import javax.media.nativewindow.VisualIDHolder;
54
55 import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration;
56 import com.jogamp.nativewindow.awt.AWTGraphicsDevice;
57 import com.jogamp.nativewindow.awt.AWTGraphicsScreen;
58 import com.jogamp.nativewindow.awt.JAWTWindow;
59 import com.jogamp.newt.Window;
60
61 @SuppressWarnings("serial")
62 public class AWTCanvas extends Canvas {
63   private GraphicsDevice device;
64   private GraphicsConfiguration chosen;
65   private AWTGraphicsConfiguration awtConfig;
66   private volatile JAWTWindow jawtWindow=null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle
67   private CapabilitiesChooser chooser=null;
68   private final CapabilitiesImmutable capabilities;
69
70   private boolean displayConfigChanged=false;
71
72   public AWTCanvas(CapabilitiesImmutable capabilities, CapabilitiesChooser chooser) {
73     super();
74
75     if(null==capabilities) {
76         throw new NativeWindowException("Capabilities null");
77     }
78     this.capabilities=capabilities;
79     this.chooser=chooser;
80   }
81
82   public AWTGraphicsConfiguration getAWTGraphicsConfiguration() {
83     return awtConfig;
84   }
85
86   /**
87    * Overridden from Canvas to prevent the AWT's clearing of the
88    * canvas from interfering with the OpenGL rendering.
89    */
90   @Override
91   public void update(Graphics g) {
92     // paint(g);
93   }
94
95   /** Overridden to cause OpenGL rendering to be performed during
96       repaint cycles. Subclasses which override this method must call
97       super.paint() in their paint() method in order to function
98       properly.
99    */
100   @Override
101   public void paint(Graphics g) {
102   }
103
104   public boolean hasDeviceChanged() {
105     boolean res = displayConfigChanged;
106     displayConfigChanged=false;
107     return res;
108   }
109
110   @Override
111   public void addNotify() {
112
113     /**
114      * 'super.addNotify()' determines the GraphicsConfiguration,
115      * while calling this class's overriden 'getGraphicsConfiguration()' method
116      * after which it creates the native peer.
117      * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration
118      * is being used in getGraphicsConfiguration().
119      * This code order also allows recreation, ie re-adding the GLCanvas.
120      */
121     awtConfig = chooseGraphicsConfiguration(capabilities, capabilities, chooser, device);
122     if(Window.DEBUG_IMPLEMENTATION) {
123         System.err.println(getThreadName()+": AWTCanvas.addNotify.0: Created Config: "+awtConfig);
124     }
125     if(null==awtConfig) {
126         throw new NativeWindowException("Error: NULL AWTGraphicsConfiguration");
127     }
128     chosen = awtConfig.getAWTGraphicsConfiguration();
129
130     // before native peer is valid: X11
131     disableBackgroundErase();
132
133     // issues getGraphicsConfiguration() and creates the native peer
134     super.addNotify();
135
136     // after native peer is valid: Windows
137     disableBackgroundErase();
138
139     {
140         jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
141         // trigger initialization cycle
142         jawtWindow.lockSurface();
143         jawtWindow.unlockSurface();
144     }
145
146     GraphicsConfiguration gc = super.getGraphicsConfiguration();
147     if(null!=gc) {
148         device = gc.getDevice();
149     }
150     if(Window.DEBUG_IMPLEMENTATION) {
151         System.err.println(getThreadName()+": AWTCanvas.addNotify.X");
152     }
153   }
154
155   public int getPixelScale() {
156     final JAWTWindow _jawtWindow = jawtWindow;
157     return (null != _jawtWindow) ? _jawtWindow.getPixelScale() : 1;
158   }
159   public NativeWindow getNativeWindow() {
160     final JAWTWindow _jawtWindow = jawtWindow;
161     return (null != _jawtWindow) ? _jawtWindow : null;
162   }
163
164   public boolean isOffscreenLayerSurfaceEnabled() {
165       return null != jawtWindow ? jawtWindow.isOffscreenLayerSurfaceEnabled() : false;
166   }
167
168   @Override
169   public void removeNotify() {
170       try {
171         dispose();
172       } finally {
173         super.removeNotify();
174       }
175   }
176
177   private void dispose() {
178     if( null != jawtWindow ) {
179         jawtWindow.destroy();
180         if(Window.DEBUG_IMPLEMENTATION) {
181             System.err.println(getThreadName()+": AWTCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow);
182         }
183         jawtWindow=null;
184     }
185     if(null != awtConfig) {
186         AbstractGraphicsDevice adevice = awtConfig.getNativeGraphicsConfiguration().getScreen().getDevice();
187         String adeviceMsg=null;
188         if(Window.DEBUG_IMPLEMENTATION) {
189             adeviceMsg = adevice.toString();
190         }
191         boolean closed = adevice.close();
192         if(Window.DEBUG_IMPLEMENTATION) {
193             System.err.println(getThreadName()+": AWTCanvas.dispose(): closed GraphicsDevice: "+adeviceMsg+", result: "+closed);
194         }
195     }
196   }
197
198   private String getThreadName() { return Thread.currentThread().getName(); }
199
200   /**
201    * Overridden to choose a GraphicsConfiguration on a parent container's
202    * GraphicsDevice because both devices
203    */
204   @Override
205   public GraphicsConfiguration getGraphicsConfiguration() {
206     /*
207      * Workaround for problems with Xinerama and java.awt.Component.checkGD
208      * when adding to a container on a different graphics device than the
209      * one that this Canvas is associated with.
210      *
211      * GC will be null unless:
212      *   - A native peer has assigned it. This means we have a native
213      *     peer, and are already comitted to a graphics configuration.
214      *   - This canvas has been added to a component hierarchy and has
215      *     an ancestor with a non-null GC, but the native peer has not
216      *     yet been created. This means we can still choose the GC on
217      *     all platforms since the peer hasn't been created.
218      */
219     final GraphicsConfiguration gc = super.getGraphicsConfiguration();
220     /*
221      * chosen is only non-null on platforms where the GLDrawableFactory
222      * returns a non-null GraphicsConfiguration (in the GLCanvas
223      * constructor).
224      *
225      * if gc is from this Canvas' native peer then it should equal chosen,
226      * otherwise it is from an ancestor component that this Canvas is being
227      * added to, and we go into this block.
228      */
229     if (gc != null && chosen != null && !chosen.equals(gc)) {
230       /*
231        * Check for compatibility with gc. If they differ by only the
232        * device then return a new GCconfig with the super-class' GDevice
233        * (and presumably the same visual ID in Xinerama).
234        *
235        */
236       if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) {
237         /*
238          * Here we select a GraphicsConfiguration on the alternate
239          * device that is presumably identical to the chosen
240          * configuration, but on the other device.
241          *
242          * Should really check to ensure that we select a configuration
243          * with the same X visual ID for Xinerama screens, otherwise the
244          * GLDrawable may have the wrong visual ID (I don't think this
245          * ever gets updated). May need to add a method to
246          * X11GLDrawableFactory to do this in a platform specific
247          * manner.
248          *
249          * However, on platforms where we can actually get into this
250          * block, both devices should have the same visual list, and the
251          * same configuration should be selected here.
252          */
253         AWTGraphicsConfiguration config = chooseGraphicsConfiguration(
254                 awtConfig.getChosenCapabilities(), awtConfig.getRequestedCapabilities(), chooser, gc.getDevice());
255         final GraphicsConfiguration compatible = (null!=config)?config.getAWTGraphicsConfiguration():null;
256         if(Window.DEBUG_IMPLEMENTATION) {
257             Exception e = new Exception("Info: Call Stack: "+Thread.currentThread().getName());
258             e.printStackTrace();
259             System.err.println("Created Config (n): HAVE    GC "+chosen);
260             System.err.println("Created Config (n): THIS    GC "+gc);
261             System.err.println("Created Config (n): Choosen GC "+compatible);
262             System.err.println("Created Config (n): HAVE    CF "+awtConfig);
263             System.err.println("Created Config (n): Choosen CF "+config);
264             System.err.println("Created Config (n): EQUALS CAPS "+config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities()));
265         }
266
267         if (compatible != null) {
268           /*
269            * Save the new GC for equals test above, and to return to
270            * any outside callers of this method.
271            */
272           chosen = compatible;
273           if( !config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())) {
274               displayConfigChanged=true;
275           }
276           awtConfig = config;
277         }
278       }
279
280       /*
281        * If a compatible GC was not found in the block above, this will
282        * return the GC that was selected in the constructor (and might
283        * cause an exception in Component.checkGD when adding to a
284        * container, but in this case that would be the desired behavior).
285        *
286        */
287       return chosen;
288     } else if (gc == null) {
289       /*
290        * The GC is null, which means we have no native peer, and are not
291        * part of a (realized) component hierarchy. So we return the
292        * desired visual that was selected in the constructor (possibly
293        * null).
294        */
295       return chosen;
296     }
297
298     /*
299      * Otherwise we have not explicitly selected a GC in the constructor, so
300      * just return what Canvas would have.
301      */
302     return gc;
303   }
304
305   private static AWTGraphicsConfiguration chooseGraphicsConfiguration(CapabilitiesImmutable capsChosen,
306                                                                       CapabilitiesImmutable capsRequested,
307                                                                       CapabilitiesChooser chooser,
308                                                                       GraphicsDevice device) {
309     final AbstractGraphicsScreen aScreen = null != device ?
310             AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT):
311             AWTGraphicsScreen.createDefault();
312     AWTGraphicsConfiguration config = (AWTGraphicsConfiguration)
313       GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, capsChosen.getClass()).chooseGraphicsConfiguration(capsChosen,
314                                                                                                    capsRequested,
315                                                                                                    chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
316     if (config == null) {
317       throw new NativeWindowException("Error: Couldn't fetch AWTGraphicsConfiguration");
318     }
319
320     return config;
321   }
322
323   // Disables the AWT's erasing of this Canvas's background on Windows
324   // in Java SE 6. This internal API is not available in previous
325   // releases, but the system property
326   // -Dsun.awt.noerasebackground=true can be specified to get similar
327   // results globally in previous releases.
328   private static boolean disableBackgroundEraseInitialized;
329   private static Method  disableBackgroundEraseMethod;
330   private void disableBackgroundErase() {
331     if (!disableBackgroundEraseInitialized) {
332       try {
333         AccessController.doPrivileged(new PrivilegedAction<Object>() {
334             @Override
335             public Object run() {
336               try {
337                 Class<?> clazz = getToolkit().getClass();
338                 while (clazz != null && disableBackgroundEraseMethod == null) {
339                   try {
340                     disableBackgroundEraseMethod =
341                       clazz.getDeclaredMethod("disableBackgroundErase",
342                                               new Class[] { Canvas.class });
343                     disableBackgroundEraseMethod.setAccessible(true);
344                   } catch (Exception e) {
345                     clazz = clazz.getSuperclass();
346                   }
347                 }
348               } catch (Exception e) {
349               }
350               return null;
351             }
352           });
353       } catch (Exception e) {
354       }
355       disableBackgroundEraseInitialized = true;
356       if(Window.DEBUG_IMPLEMENTATION) {
357         System.err.println("AWTCanvas: TK disableBackgroundErase method found: "+
358                 (null!=disableBackgroundEraseMethod));
359       }
360     }
361     if (disableBackgroundEraseMethod != null) {
362       Throwable t=null;
363       try {
364         disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this });
365       } catch (Exception e) {
366         // FIXME: workaround for 6504460 (incorrect backport of 6333613 in 5.0u10)
367         // throw new GLException(e);
368         t = e;
369       }
370       if(Window.DEBUG_IMPLEMENTATION) {
371         System.err.println("AWTCanvas: TK disableBackgroundErase error: "+t);
372       }
373     }
374   }
375 }
http://JogAmp.org git info: FAQ, tutorial and man pages.