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