2 * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (c) 2010 JogAmp Community. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
35 package jogamp.newt.driver.awt;
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;
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;
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;
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;
70 private boolean displayConfigChanged=false;
72 public AWTCanvas(CapabilitiesImmutable capabilities, CapabilitiesChooser chooser) {
75 if(null==capabilities) {
76 throw new NativeWindowException("Capabilities null");
78 this.capabilities=capabilities;
82 public AWTGraphicsConfiguration getAWTGraphicsConfiguration() {
87 * Overridden from Canvas to prevent the AWT's clearing of the
88 * canvas from interfering with the OpenGL rendering.
91 public void update(Graphics g) {
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
101 public void paint(Graphics g) {
104 public boolean hasDeviceChanged() {
105 boolean res = displayConfigChanged;
106 displayConfigChanged=false;
111 public void addNotify() {
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.
121 awtConfig = chooseGraphicsConfiguration(capabilities, capabilities, chooser, device);
122 if(Window.DEBUG_IMPLEMENTATION) {
123 System.err.println(getThreadName()+": AWTCanvas.addNotify.0: Created Config: "+awtConfig);
125 if(null==awtConfig) {
126 throw new NativeWindowException("Error: NULL AWTGraphicsConfiguration");
128 chosen = awtConfig.getAWTGraphicsConfiguration();
130 // before native peer is valid: X11
131 disableBackgroundErase();
133 // issues getGraphicsConfiguration() and creates the native peer
136 // after native peer is valid: Windows
137 disableBackgroundErase();
140 jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
141 // trigger initialization cycle
142 jawtWindow.lockSurface();
143 jawtWindow.unlockSurface();
146 GraphicsConfiguration gc = super.getGraphicsConfiguration();
148 device = gc.getDevice();
150 if(Window.DEBUG_IMPLEMENTATION) {
151 System.err.println(getThreadName()+": AWTCanvas.addNotify.X");
155 public int getPixelScale() {
156 final JAWTWindow _jawtWindow = jawtWindow;
157 return (null != _jawtWindow) ? _jawtWindow.getPixelScale() : 1;
159 public NativeWindow getNativeWindow() {
160 final JAWTWindow _jawtWindow = jawtWindow;
161 return (null != _jawtWindow) ? _jawtWindow : null;
164 public boolean isOffscreenLayerSurfaceEnabled() {
165 return null != jawtWindow ? jawtWindow.isOffscreenLayerSurfaceEnabled() : false;
169 public void removeNotify() {
173 super.removeNotify();
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);
185 if(null != awtConfig) {
186 AbstractGraphicsDevice adevice = awtConfig.getNativeGraphicsConfiguration().getScreen().getDevice();
187 String adeviceMsg=null;
188 if(Window.DEBUG_IMPLEMENTATION) {
189 adeviceMsg = adevice.toString();
191 boolean closed = adevice.close();
192 if(Window.DEBUG_IMPLEMENTATION) {
193 System.err.println(getThreadName()+": AWTCanvas.dispose(): closed GraphicsDevice: "+adeviceMsg+", result: "+closed);
198 private String getThreadName() { return Thread.currentThread().getName(); }
201 * Overridden to choose a GraphicsConfiguration on a parent container's
202 * GraphicsDevice because both devices
205 public GraphicsConfiguration getGraphicsConfiguration() {
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.
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.
219 final GraphicsConfiguration gc = super.getGraphicsConfiguration();
221 * chosen is only non-null on platforms where the GLDrawableFactory
222 * returns a non-null GraphicsConfiguration (in the GLCanvas
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.
229 if (gc != null && chosen != null && !chosen.equals(gc)) {
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).
236 if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) {
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.
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
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.
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());
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()));
267 if (compatible != null) {
269 * Save the new GC for equals test above, and to return to
270 * any outside callers of this method.
273 if( !config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())) {
274 displayConfigChanged=true;
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).
288 } else if (gc == null) {
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
299 * Otherwise we have not explicitly selected a GC in the constructor, so
300 * just return what Canvas would have.
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,
315 chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
316 if (config == null) {
317 throw new NativeWindowException("Error: Couldn't fetch AWTGraphicsConfiguration");
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) {
333 AccessController.doPrivileged(new PrivilegedAction<Object>() {
335 public Object run() {
337 Class<?> clazz = getToolkit().getClass();
338 while (clazz != null && disableBackgroundEraseMethod == null) {
340 disableBackgroundEraseMethod =
341 clazz.getDeclaredMethod("disableBackgroundErase",
342 new Class[] { Canvas.class });
343 disableBackgroundEraseMethod.setAccessible(true);
344 } catch (Exception e) {
345 clazz = clazz.getSuperclass();
348 } catch (Exception e) {
353 } catch (Exception e) {
355 disableBackgroundEraseInitialized = true;
356 if(Window.DEBUG_IMPLEMENTATION) {
357 System.err.println("AWTCanvas: TK disableBackgroundErase method found: "+
358 (null!=disableBackgroundEraseMethod));
361 if (disableBackgroundEraseMethod != null) {
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);
370 if(Window.DEBUG_IMPLEMENTATION) {
371 System.err.println("AWTCanvas: TK disableBackgroundErase error: "+t);