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 com.jogamp.opengl.awt; 42 43 import java.beans.Beans; 44 import java.lang.reflect.Method; 45 import java.security.AccessController; 46 import java.security.PrivilegedAction; 47 import java.awt.Canvas; 48 import java.awt.Color; 49 import java.awt.FontMetrics; 50 import java.awt.Frame; 51 import java.awt.Graphics; 52 import java.awt.Graphics2D; 53 import java.awt.GraphicsConfiguration; 54 import java.awt.GraphicsDevice; 55 import java.awt.event.HierarchyEvent; 56 import java.awt.event.HierarchyListener; 57 import java.awt.geom.NoninvertibleTransformException; 58 import java.awt.geom.Rectangle2D; 59 import java.awt.EventQueue; 60 import java.lang.reflect.InvocationTargetException; 61 import java.util.ArrayList; 62 import java.util.List; 63 64 import com.jogamp.nativewindow.AbstractGraphicsConfiguration; 65 import com.jogamp.nativewindow.OffscreenLayerOption; 66 import com.jogamp.nativewindow.ScalableSurface; 67 import com.jogamp.nativewindow.VisualIDHolder; 68 import com.jogamp.nativewindow.WindowClosingProtocol; 69 import com.jogamp.nativewindow.AbstractGraphicsDevice; 70 import com.jogamp.nativewindow.AbstractGraphicsScreen; 71 import com.jogamp.nativewindow.GraphicsConfigurationFactory; 72 import com.jogamp.nativewindow.NativeSurface; 73 import com.jogamp.nativewindow.NativeWindowFactory; 74 import com.jogamp.opengl.GL; 75 import com.jogamp.opengl.GLAnimatorControl; 76 import com.jogamp.opengl.GLAutoDrawable; 77 import com.jogamp.opengl.GLCapabilities; 78 import com.jogamp.opengl.GLCapabilitiesChooser; 79 import com.jogamp.opengl.GLCapabilitiesImmutable; 80 import com.jogamp.opengl.GLContext; 81 import com.jogamp.opengl.GLDrawable; 82 import com.jogamp.opengl.GLDrawableFactory; 83 import com.jogamp.opengl.GLEventListener; 84 import com.jogamp.opengl.GLException; 85 import com.jogamp.opengl.GLOffscreenAutoDrawable; 86 import com.jogamp.opengl.GLProfile; 87 import com.jogamp.opengl.GLRunnable; 88 import com.jogamp.opengl.GLSharedContextSetter; 89 import com.jogamp.opengl.Threading; 90 91 import com.jogamp.common.GlueGenVersion; 92 import com.jogamp.common.util.VersionUtil; 93 import com.jogamp.common.util.awt.AWTEDTExecutor; 94 import com.jogamp.common.util.locks.LockFactory; 95 import com.jogamp.common.util.locks.RecursiveLock; 96 import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; 97 import com.jogamp.nativewindow.awt.AWTGraphicsDevice; 98 import com.jogamp.nativewindow.awt.AWTGraphicsScreen; 99 import com.jogamp.nativewindow.awt.AWTPrintLifecycle; 100 import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; 101 import com.jogamp.nativewindow.awt.JAWTWindow; 102 import com.jogamp.opengl.JoglVersion; 103 import com.jogamp.opengl.util.GLDrawableUtil; 104 import com.jogamp.opengl.util.TileRenderer; 105 106 import jogamp.nativewindow.SurfaceScaleUtils; 107 import jogamp.opengl.Debug; 108 import jogamp.opengl.GLContextImpl; 109 import jogamp.opengl.GLDrawableHelper; 110 import jogamp.opengl.GLDrawableImpl; 111 import jogamp.opengl.awt.AWTTilePainter; 112 113 // FIXME: Subclasses need to call resetGLFunctionAvailability() on their 114 // context whenever the displayChanged() function is called on our 115 // GLEventListeners 116 117 /** A heavyweight AWT component which provides OpenGL rendering 118 support. This is the primary implementation of an AWT {@link GLDrawable}; 119 {@link GLJPanel} is provided for compatibility with Swing user 120 interfaces when adding a heavyweight doesn't work either because 121 of Z-ordering or LayoutManager problems. 122 * 123 * <h5><a name="offscreenlayer">Offscreen Layer Remarks</a></h5> 124 * 125 * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)} 126 * maybe called to use an offscreen drawable (FBO or PBuffer) allowing 127 * the underlying JAWT mechanism to composite the image, if supported. 128 * <p> 129 * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)} 130 * is being called if {@link GLCapabilitiesImmutable#isOnscreen()} is <code>false</code>. 131 * </p> 132 * 133 * <h5><a name="java2dgl">Java2D OpenGL Remarks</a></h5> 134 * 135 * To avoid any conflicts with a potential Java2D OpenGL context,<br> 136 * you shall consider setting the following JVM properties:<br> 137 * <ul> 138 * <li><pre>sun.java2d.opengl=false</pre></li> 139 * <li><pre>sun.java2d.noddraw=true</pre></li> 140 * </ul> 141 * This is especially true in case you want to utilize a GLProfile other than 142 * {@link GLProfile#GL2}, eg. using {@link GLProfile#getMaxFixedFunc()}.<br> 143 * On the other hand, if you like to experiment with GLJPanel's utilization 144 * of Java2D's OpenGL pipeline, you have to set them to 145 * <ul> 146 * <li><pre>sun.java2d.opengl=true</pre></li> 147 * <li><pre>sun.java2d.noddraw=true</pre></li> 148 * </ul> 149 * 150 * <h5><a name="backgrounderase">Disable Background Erase</a></h5> 151 * 152 * GLCanvas tries to disable background erase for the AWT Canvas 153 * before native peer creation (X11) and after it (Windows), <br> 154 * utilizing the optional {@link java.awt.Toolkit} method <code>disableBeackgroundErase(java.awt.Canvas)</code>.<br> 155 * However if this does not give you the desired results, you may want to disable AWT background erase in general: 156 * <ul> 157 * <li><pre>sun.awt.noerasebackground=true</pre></li> 158 * </ul> 159 * 160 * <p> 161 * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> 162 * To share a {@link GLContext} see the following note in the documentation overview: 163 * <a href="../../../../overview-summary.html#SHARING">context sharing</a> 164 * as well as {@link GLSharedContextSetter}. 165 * </p> 166 */ 167 168 @SuppressWarnings("serial") 169 public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption, 170 AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface { 171 172 private static final boolean DEBUG = Debug.debug("GLCanvas"); 173 174 private final RecursiveLock lock = LockFactory.createRecursiveLock(); 175 private final GLDrawableHelper helper = new GLDrawableHelper(); 176 private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access 177 private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle 178 private volatile GLContextImpl context; // volatile: avoid locking for read-only access 179 private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking 180 private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; 181 private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; 182 private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; 183 final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; 184 185 // copy of the cstr args, mainly for recreation 186 private final GLCapabilitiesImmutable capsReqUser; 187 private final GLCapabilitiesChooser chooser; 188 private int additionalCtxCreationFlags = 0; 189 private boolean shallUseOffscreenLayer = false; 190 191 private volatile GraphicsDevice awtDeviceReq; // one time user req. 192 private volatile AWTGraphicsConfiguration awtConfig; 193 private volatile boolean isShowing; 194 private final HierarchyListener hierarchyListener = new HierarchyListener() { 195 @Override 196 public void hierarchyChanged(final HierarchyEvent e) { 197 isShowing = GLCanvas.this.isShowing(); 198 } 199 }; 200 201 private final AWTWindowClosingProtocol awtWindowClosingProtocol = 202 new AWTWindowClosingProtocol(this, new Runnable() { 203 @Override 204 public void run() { 205 GLCanvas.this.destroyImpl( true ); 206 } 207 }, null); 208 209 /** Creates a new GLCanvas component with a default set of OpenGL 210 capabilities, using the default OpenGL capabilities selection 211 mechanism, on the default screen device. 212 <p> 213 See details about <a href="#contextSharing">OpenGL context sharing</a>. 214 </p> 215 * @throws GLException if no default profile is available for the default desktop device. 216 */ GLCanvas()217 public GLCanvas() throws GLException { 218 this(null); 219 } 220 221 /** Creates a new GLCanvas component with the requested set of 222 OpenGL capabilities, using the default OpenGL capabilities 223 selection mechanism, on the default screen device. 224 <p> 225 See details about <a href="#contextSharing">OpenGL context sharing</a>. 226 </p> 227 * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. 228 * @see GLCanvas#GLCanvas(com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesChooser, com.jogamp.opengl.GLContext, java.awt.GraphicsDevice) 229 */ GLCanvas(final GLCapabilitiesImmutable capsReqUser)230 public GLCanvas(final GLCapabilitiesImmutable capsReqUser) throws GLException { 231 this(capsReqUser, null, null); 232 } 233 234 /** Creates a new GLCanvas component. The passed GLCapabilities 235 specifies the OpenGL capabilities for the component; if null, a 236 default set of capabilities is used. The GLCapabilitiesChooser 237 specifies the algorithm for selecting one of the available 238 GLCapabilities for the component; a DefaultGLCapabilitesChooser 239 is used if null is passed for this argument. 240 The passed GraphicsDevice indicates the screen on 241 which to create the GLCanvas; the GLDrawableFactory uses the 242 default screen device of the local GraphicsEnvironment if null 243 is passed for this argument. 244 <p> 245 See details about <a href="#contextSharing">OpenGL context sharing</a>. 246 </p> 247 * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. 248 */ GLCanvas(final GLCapabilitiesImmutable capsReqUser, final GLCapabilitiesChooser chooser, final GraphicsDevice device)249 public GLCanvas(final GLCapabilitiesImmutable capsReqUser, 250 final GLCapabilitiesChooser chooser, 251 final GraphicsDevice device) 252 throws GLException 253 { 254 /* 255 * Determination of the native window is made in 'super.addNotify()', 256 * which creates the native peer using AWT's GraphicsConfiguration. 257 * GraphicsConfiguration is returned by this class overwritten 258 * 'getGraphicsConfiguration()', which returns our OpenGL compatible 259 * 'chosen' GraphicsConfiguration. 260 */ 261 super(); 262 263 if(null==capsReqUser) { 264 this.capsReqUser = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDevice())); 265 } else { 266 // don't allow the user to change data 267 this.capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable(); 268 } 269 if( !this.capsReqUser.isOnscreen() ) { 270 setShallUseOffscreenLayer(true); // trigger offscreen layer - if supported 271 } 272 273 // One time user AWT GraphicsDevice request 274 awtDeviceReq = device; 275 276 // instantiation will be issued in addNotify() 277 this.chooser = chooser; 278 279 this.addHierarchyListener(hierarchyListener); 280 this.isShowing = isShowing(); 281 } 282 283 @Override setSharedContext(final GLContext sharedContext)284 public final void setSharedContext(final GLContext sharedContext) throws IllegalStateException { 285 helper.setSharedContext(this.context, sharedContext); 286 } 287 288 @Override setSharedAutoDrawable(final GLAutoDrawable sharedAutoDrawable)289 public final void setSharedAutoDrawable(final GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { 290 helper.setSharedAutoDrawable(this, sharedAutoDrawable); 291 } 292 293 @Override getUpstreamWidget()294 public final Object getUpstreamWidget() { 295 return this; 296 } 297 298 @Override getUpstreamLock()299 public final RecursiveLock getUpstreamLock() { return lock; } 300 301 @Override isThreadGLCapable()302 public final boolean isThreadGLCapable() { return Threading.isOpenGLThread(); } 303 304 @Override setShallUseOffscreenLayer(final boolean v)305 public void setShallUseOffscreenLayer(final boolean v) { 306 shallUseOffscreenLayer = v; 307 } 308 309 @Override getShallUseOffscreenLayer()310 public final boolean getShallUseOffscreenLayer() { 311 return shallUseOffscreenLayer; 312 } 313 314 @Override isOffscreenLayerSurfaceEnabled()315 public final boolean isOffscreenLayerSurfaceEnabled() { 316 final JAWTWindow _jawtWindow = jawtWindow; 317 if(null != _jawtWindow) { 318 return _jawtWindow.isOffscreenLayerSurfaceEnabled(); 319 } 320 return false; 321 } 322 323 324 /** 325 * {@inheritDoc} 326 * <p> 327 * Overridden to choose a {@link GraphicsConfiguration} from a parent container's 328 * {@link GraphicsDevice}. 329 * </p> 330 * <p> 331 * Method also intercepts {@link GraphicsConfiguration} changes regarding to 332 * its capabilities and its {@link GraphicsDevice}. This may happen in case 333 * the display changes its configuration or the component is moved to another screen. 334 * </p> 335 */ 336 @Override getGraphicsConfiguration()337 public GraphicsConfiguration getGraphicsConfiguration() { 338 /** 339 * parentGC will be null unless: 340 * - A native peer has assigned it. This means we have a native 341 * peer, and are already committed to a graphics configuration. 342 * - This canvas has been added to a component hierarchy and has 343 * an ancestor with a non-null GC, but the native peer has not 344 * yet been created. This means we can still choose the GC on 345 * all platforms since the peer hasn't been created. 346 */ 347 final GraphicsConfiguration parentGC = super.getGraphicsConfiguration(); 348 349 if( Beans.isDesignTime() ) { 350 return parentGC; 351 } 352 final GraphicsConfiguration oldGC = null != awtConfig ? awtConfig.getAWTGraphicsConfiguration() : null; 353 354 if ( null != parentGC && null != oldGC && !oldGC.equals(parentGC) ) { 355 // Previous oldGC != parentGC of native peer 356 357 if ( !oldGC.getDevice().getIDstring().equals(parentGC.getDevice().getIDstring()) ) { 358 // Previous oldGC's GraphicsDevice != parentGC's GraphicsDevice of native peer 359 360 /** 361 * Here we select a GraphicsConfiguration on the alternate device. 362 * In case the new configuration differs (-> !equalCaps), 363 * we might need a reconfiguration, 364 */ 365 final AWTGraphicsConfiguration newConfig = chooseGraphicsConfiguration( (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(), 366 (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(), 367 chooser, parentGC.getDevice()); 368 final GraphicsConfiguration newGC = newConfig.getAWTGraphicsConfiguration(); 369 final boolean equalCaps = newConfig.getChosenCapabilities().equals(awtConfig.getChosenCapabilities()); 370 if(DEBUG) { 371 System.err.println(getThreadName()+": getGraphicsConfiguration() Info: Changed GC and GD"); 372 System.err.println("Created Config (n): Old GC "+oldGC); 373 System.err.println("Created Config (n): Old GD "+oldGC.getDevice().getIDstring()); 374 System.err.println("Created Config (n): Parent GC "+parentGC); 375 System.err.println("Created Config (n): Parent GD "+parentGC.getDevice().getIDstring()); 376 System.err.println("Created Config (n): New GC "+newGC); 377 System.err.println("Created Config (n): New GD "+newGC.getDevice().getIDstring()); 378 System.err.println("Created Config (n): Old CF "+awtConfig); 379 System.err.println("Created Config (n): New CF "+newConfig); 380 System.err.println("Created Config (n): EQUALS CAPS "+equalCaps); 381 // Thread.dumpStack(); 382 } 383 if ( null != newGC ) { 384 if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) { 385 // complete destruction! 386 destroyImpl( true ); 387 // recreation! 388 setAWTGraphicsConfiguration(newConfig); 389 createJAWTDrawableAndContext(); 390 validateGLDrawable(); 391 } else { 392 setAWTGraphicsConfiguration(newConfig); 393 } 394 /** 395 * Return the newGC, which covers the desired capabilities and is compatible 396 * with the available GC's of its devices. 397 */ 398 if(DEBUG) { 399 System.err.println(getThreadName()+": Info: getGraphicsConfiguration - end.01: newGC "+newGC); 400 } 401 return newGC; 402 } else { 403 if(DEBUG) { 404 System.err.println(getThreadName()+": Info: getGraphicsConfiguration - end.00: oldGC "+oldGC); 405 } 406 } 407 } 408 /** 409 * If a new GC was _not_ found/defined above, 410 * method returns oldGC as selected in the constructor or first addNotify(). 411 * This may cause an exception in Component.checkGD when adding to a 412 * container, and is the desired behavior. 413 */ 414 return oldGC; 415 } else if (null == parentGC) { 416 /** 417 * The parentGC is null, which means we have no native peer, and are not 418 * part of a (realized) component hierarchy. So we return the 419 * desired visual that was selected in the constructor (possibly 420 * null). 421 */ 422 return oldGC; 423 } else { 424 /** 425 * Otherwise we have not explicitly selected a GC in the constructor, so 426 * just return what Canvas would have. 427 */ 428 return parentGC; 429 } 430 } 431 432 @Override createContext(final GLContext shareWith)433 public GLContext createContext(final GLContext shareWith) { 434 final RecursiveLock _lock = lock; 435 _lock.lock(); 436 try { 437 if(drawable != null) { 438 final GLContext _ctx = drawable.createContext(shareWith); 439 _ctx.setContextCreationFlags(additionalCtxCreationFlags); 440 return _ctx; 441 } 442 return null; 443 } finally { 444 _lock.unlock(); 445 } 446 } 447 setRealizedImpl(final boolean realized)448 private final void setRealizedImpl(final boolean realized) { 449 final RecursiveLock _lock = lock; 450 _lock.lock(); 451 try { 452 final GLDrawable _drawable = drawable; 453 if( null == _drawable || realized == _drawable.isRealized() || 454 realized && ( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) ) { 455 return; 456 } 457 _drawable.setRealized(realized); 458 if( realized && _drawable.isRealized() ) { 459 sendReshape=true; // ensure a reshape is being send .. 460 } 461 } finally { 462 _lock.unlock(); 463 } 464 } 465 private final Runnable realizeOnEDTAction = new Runnable() { 466 @Override 467 public void run() { setRealizedImpl(true); } 468 }; 469 private final Runnable unrealizeOnEDTAction = new Runnable() { 470 @Override 471 public void run() { setRealizedImpl(false); } 472 }; 473 474 @Override setRealized(final boolean realized)475 public final void setRealized(final boolean realized) { 476 // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock! 477 AWTEDTExecutor.singleton.invoke(getTreeLock(), false /* allowOnNonEDT */, true /* wait */, realized ? realizeOnEDTAction : unrealizeOnEDTAction); 478 } 479 480 @Override isRealized()481 public boolean isRealized() { 482 final GLDrawable _drawable = drawable; 483 return ( null != _drawable ) ? _drawable.isRealized() : false; 484 } 485 486 @Override getDefaultCloseOperation()487 public WindowClosingMode getDefaultCloseOperation() { 488 return awtWindowClosingProtocol.getDefaultCloseOperation(); 489 } 490 491 @Override setDefaultCloseOperation(final WindowClosingMode op)492 public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { 493 return awtWindowClosingProtocol.setDefaultCloseOperation(op); 494 } 495 496 @Override display()497 public void display() { 498 if( !validateGLDrawable() ) { 499 if(DEBUG) { 500 System.err.println(getThreadName()+": Info: GLCanvas display - skipped GL render, drawable not valid yet"); 501 } 502 return; // not yet available .. 503 } 504 if( isShowing && !printActive ) { 505 Threading.invoke(true, displayOnEDTAction, getTreeLock()); 506 } 507 } 508 509 /** 510 * {@inheritDoc} 511 * 512 * <p> 513 * This impl. only destroys all GL related resources. 514 * </p> 515 * <p> 516 * This impl. does not remove the GLCanvas from it's parent AWT container 517 * so this class's {@link #removeNotify()} AWT override won't get called. 518 * To do so, remove this component from it's parent AWT container. 519 * </p> 520 */ 521 @Override destroy()522 public void destroy() { 523 destroyImpl( false ); 524 } 525 destroyImpl(final boolean destroyJAWTWindowAndAWTDevice)526 protected void destroyImpl(final boolean destroyJAWTWindowAndAWTDevice) { 527 Threading.invoke(true, destroyOnEDTAction, getTreeLock()); 528 if( destroyJAWTWindowAndAWTDevice ) { 529 AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, disposeJAWTWindowAndAWTDeviceOnEDT); 530 } 531 } 532 533 /** Overridden to cause OpenGL rendering to be performed during 534 repaint cycles. Subclasses which override this method must call 535 super.paint() in their paint() method in order to function 536 properly. 537 */ 538 @Override paint(final Graphics g)539 public void paint(final Graphics g) { 540 if( Beans.isDesignTime() ) { 541 // Make GLCanvas behave better in NetBeans GUI builder 542 g.setColor(Color.BLACK); 543 g.fillRect(0, 0, getWidth(), getHeight()); 544 final FontMetrics fm = g.getFontMetrics(); 545 String name = getName(); 546 if (name == null) { 547 name = getClass().getName(); 548 final int idx = name.lastIndexOf('.'); 549 if (idx >= 0) { 550 name = name.substring(idx + 1); 551 } 552 } 553 final Rectangle2D bounds = fm.getStringBounds(name, g); 554 g.setColor(Color.WHITE); 555 g.drawString(name, 556 (int) ((getWidth() - bounds.getWidth()) / 2), 557 (int) ((getHeight() + bounds.getHeight()) / 2)); 558 } else if( !this.helper.isAnimatorAnimatingOnOtherThread() ) { 559 display(); 560 } 561 } 562 563 /** Overridden to track when this component is added to a container. 564 Subclasses which override this method must call 565 super.addNotify() in their addNotify() method in order to 566 function properly. <P> 567 568 <B>Overrides:</B> 569 <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ 570 @SuppressWarnings("deprecation") 571 @Override addNotify()572 public void addNotify() { 573 final RecursiveLock _lock = lock; 574 _lock.lock(); 575 try { 576 final boolean isBeansDesignTime = Beans.isDesignTime(); 577 578 if(DEBUG) { 579 System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()+", isBeansDesignTime "+isBeansDesignTime); 580 // Thread.dumpStack(); 581 } 582 583 if( isBeansDesignTime ) { 584 super.addNotify(); 585 } else { 586 /** 587 * 'super.addNotify()' determines the GraphicsConfiguration, 588 * while calling this class's overridden 'getGraphicsConfiguration()' method 589 * after which it creates the native peer. 590 * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration 591 * is being used in getGraphicsConfiguration(). 592 * This code order also allows recreation, ie re-adding the GLCanvas. 593 */ 594 595 // before native peer is valid: X11 596 disableBackgroundErase(); 597 598 final GraphicsDevice awtDevice; 599 if(null==awtDeviceReq) { 600 // Query AWT GraphicsDevice from parent tree, default 601 final GraphicsConfiguration gc = super.getGraphicsConfiguration(); 602 if(null==gc) { 603 throw new GLException("Error: NULL AWT GraphicsConfiguration"); 604 } 605 awtDevice = gc.getDevice(); 606 } else { 607 // Use one time user AWT GraphicsDevice request 608 awtDevice = awtDeviceReq; 609 awtDeviceReq = null; 610 } 611 final AWTGraphicsConfiguration awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, awtDevice); 612 if(null==awtConfig) { 613 throw new GLException("Error: NULL AWTGraphicsConfiguration"); 614 } 615 setAWTGraphicsConfiguration(awtConfig); 616 617 // issues getGraphicsConfiguration() and creates the native peer 618 super.addNotify(); 619 620 // after native peer is valid: Windows 621 disableBackgroundErase(); 622 623 createJAWTDrawableAndContext(); 624 625 // init drawable by paint/display makes the init sequence more equal 626 // for all launch flavors (applet/javaws/..) 627 // validateGLDrawable(); 628 } 629 awtWindowClosingProtocol.addClosingListener(); 630 631 if(DEBUG) { 632 System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer()); 633 } 634 } finally { 635 _lock.unlock(); 636 } 637 } 638 639 @Override setSurfaceScale(final float[] pixelScale)640 public final boolean setSurfaceScale(final float[] pixelScale) { 641 System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); 642 if( isRealized() && isShowing ) { 643 Threading.invoke(true, setSurfaceScaleOnEDTAction, getTreeLock()); 644 return true; 645 } else { 646 return false; 647 } 648 } 649 private final Runnable setSurfaceScaleOnEDTAction = new Runnable() { 650 @Override 651 public void run() { 652 final RecursiveLock _lock = lock; 653 _lock.lock(); 654 try { 655 if( null != drawable && drawable.isRealized() ) { 656 if( setSurfaceScaleImpl(jawtWindow) ) { 657 reshapeImpl(getWidth(), getHeight()); 658 if( !helper.isAnimatorAnimatingOnOtherThread() ) { 659 helper.invokeGL(drawable, context, displayAction, initAction); // display 660 } 661 } 662 } 663 } finally { 664 _lock.unlock(); 665 } 666 } }; setSurfaceScaleImpl(final ScalableSurface ns)667 private final boolean setSurfaceScaleImpl(final ScalableSurface ns) { 668 if( ns.setSurfaceScale(reqPixelScale) ) { 669 ns.getCurrentSurfaceScale(hasPixelScale); 670 return true; 671 } else { 672 return false; 673 } 674 } 675 updatePixelScale()676 private final boolean updatePixelScale() { 677 if( jawtWindow.hasPixelScaleChanged() ) { 678 jawtWindow.getMaximumSurfaceScale(maxPixelScale); 679 jawtWindow.getMinimumSurfaceScale(minPixelScale); 680 return setSurfaceScaleImpl(jawtWindow); 681 } else { 682 return false; 683 } 684 } 685 686 @Override getRequestedSurfaceScale(final float[] result)687 public final float[] getRequestedSurfaceScale(final float[] result) { 688 System.arraycopy(reqPixelScale, 0, result, 0, 2); 689 return result; 690 } 691 692 @Override getCurrentSurfaceScale(final float[] result)693 public final float[] getCurrentSurfaceScale(final float[] result) { 694 System.arraycopy(hasPixelScale, 0, result, 0, 2); 695 return result; 696 } 697 698 @Override getMinimumSurfaceScale(final float[] result)699 public float[] getMinimumSurfaceScale(final float[] result) { 700 System.arraycopy(minPixelScale, 0, result, 0, 2); 701 return result; 702 } 703 704 @Override getMaximumSurfaceScale(final float[] result)705 public float[] getMaximumSurfaceScale(final float[] result) { 706 System.arraycopy(maxPixelScale, 0, result, 0, 2); 707 return result; 708 } 709 createJAWTDrawableAndContext()710 private void createJAWTDrawableAndContext() { 711 if ( !Beans.isDesignTime() ) { 712 jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); 713 jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); 714 jawtWindow.lockSurface(); 715 try { 716 jawtWindow.setSurfaceScale(reqPixelScale); 717 drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); 718 createContextImpl(drawable); 719 jawtWindow.getCurrentSurfaceScale(hasPixelScale); 720 jawtWindow.getMinimumSurfaceScale(minPixelScale); 721 jawtWindow.getMaximumSurfaceScale(maxPixelScale); 722 } finally { 723 jawtWindow.unlockSurface(); 724 } 725 } 726 } createContextImpl(final GLDrawable drawable)727 private boolean createContextImpl(final GLDrawable drawable) { 728 final GLContext[] shareWith = { null }; 729 if( !helper.isSharedGLContextPending(shareWith) ) { 730 context = (GLContextImpl) drawable.createContext(shareWith[0]); 731 context.setContextCreationFlags(additionalCtxCreationFlags); 732 if(DEBUG) { 733 System.err.println(getThreadName()+": Context created: has shared "+(null != shareWith[0])); 734 } 735 return true; 736 } else { 737 if(DEBUG) { 738 System.err.println(getThreadName()+": Context !created: pending share"); 739 } 740 return false; 741 } 742 } 743 validateGLDrawable()744 private boolean validateGLDrawable() { 745 if( Beans.isDesignTime() || !isDisplayable() ) { 746 return false; // early out! 747 } 748 final GLDrawable _drawable = drawable; 749 if ( null != _drawable ) { 750 boolean res = _drawable.isRealized(); 751 if( !res ) { 752 // re-try drawable creation 753 if( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) { 754 return false; // early out! 755 } 756 setRealized(true); 757 res = _drawable.isRealized(); 758 if(DEBUG) { 759 System.err.println(getThreadName()+": Realized Drawable: isRealized "+res+", "+_drawable.toString()); 760 // Thread.dumpStack(); 761 } 762 } 763 if( res && null == context ) { 764 // re-try context creation 765 res = createContextImpl(_drawable); // pending creation. 766 } 767 return res; 768 } 769 return false; 770 } 771 setAWTGraphicsConfiguration(final AWTGraphicsConfiguration config)772 private void setAWTGraphicsConfiguration(final AWTGraphicsConfiguration config) { 773 // Cache awtConfig 774 awtConfig = config; 775 if( null != jawtWindow ) { 776 // Notify JAWTWindow .. 777 jawtWindow.setAWTGraphicsConfiguration(config); 778 } 779 } 780 781 /** <p>Overridden to track when this component is removed from a 782 container. Subclasses which override this method must call 783 super.removeNotify() in their removeNotify() method in order to 784 function properly. </p> 785 <p>User shall not call this method outside of EDT, read the AWT/Swing specs 786 about this.</p> 787 <B>Overrides:</B> 788 <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ 789 @SuppressWarnings("deprecation") 790 @Override removeNotify()791 public void removeNotify() { 792 if(DEBUG) { 793 System.err.println(getThreadName()+": Info: removeNotify - start"); 794 // Thread.dumpStack(); 795 } 796 797 awtWindowClosingProtocol.removeClosingListener(); 798 799 if( Beans.isDesignTime() ) { 800 super.removeNotify(); 801 } else { 802 try { 803 destroyImpl( true ); 804 } finally { 805 super.removeNotify(); 806 } 807 } 808 if(DEBUG) { 809 System.err.println(getThreadName()+": Info: removeNotify - end, peer: "+getPeer()); 810 } 811 } 812 813 /** Overridden to cause {@link GLDrawableHelper#reshape} to be 814 called on all registered {@link GLEventListener}s. Subclasses 815 which override this method must call super.reshape() in 816 their reshape() method in order to function properly. <P> 817 818 <B>Overrides:</B> 819 <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ 820 @SuppressWarnings("deprecation") 821 @Override reshape(final int x, final int y, final int width, final int height)822 public void reshape(final int x, final int y, final int width, final int height) { 823 synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape 824 super.reshape(x, y, width, height); 825 reshapeImpl(width, height); 826 } 827 } reshapeImpl(final int width, final int height)828 private void reshapeImpl(final int width, final int height) { 829 final int scaledWidth = SurfaceScaleUtils.scale(width, hasPixelScale[0]); 830 final int scaledHeight = SurfaceScaleUtils.scale(height, hasPixelScale[1]); 831 832 if(DEBUG) { 833 final NativeSurface ns = getNativeSurface(); 834 final long nsH = null != ns ? ns.getSurfaceHandle() : 0; 835 System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+ 836 " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+ 837 "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight+ 838 " - surfaceHandle 0x"+Long.toHexString(nsH)); 839 // Thread.dumpStack(); 840 } 841 if( validateGLDrawable() && !printActive ) { 842 final GLDrawableImpl _drawable = drawable; 843 if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { 844 final RecursiveLock _lock = lock; 845 _lock.lock(); 846 try { 847 final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, scaledWidth, scaledHeight); 848 if(_drawable != _drawableNew) { 849 // write back 850 drawable = _drawableNew; 851 } 852 } finally { 853 _lock.unlock(); 854 } 855 } 856 sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock 857 } 858 } 859 860 /** 861 * Overridden from Canvas to prevent the AWT's clearing of the 862 * canvas from interfering with the OpenGL rendering. 863 */ 864 @Override update(final Graphics g)865 public void update(final Graphics g) { 866 paint(g); 867 } 868 869 private volatile boolean printActive = false; 870 private GLAnimatorControl printAnimator = null; 871 private GLAutoDrawable printGLAD = null; 872 private AWTTilePainter printAWTTiles = null; 873 874 @Override setupPrint(final double scaleMatX, final double scaleMatY, final int numSamples, final int tileWidth, final int tileHeight)875 public void setupPrint(final double scaleMatX, final double scaleMatY, final int numSamples, final int tileWidth, final int tileHeight) { 876 printActive = true; 877 final int componentCount = isOpaque() ? 3 : 4; 878 final TileRenderer printRenderer = new TileRenderer(); 879 printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG); 880 AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT); 881 } 882 private final Runnable setupPrintOnEDT = new Runnable() { 883 @Override 884 public void run() { 885 final RecursiveLock _lock = lock; 886 _lock.lock(); 887 try { 888 if( !validateGLDrawable() ) { 889 if(DEBUG) { 890 System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet"); 891 } 892 printActive = false; 893 return; // not yet available .. 894 } 895 if( !isVisible() ) { 896 if(DEBUG) { 897 System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible"); 898 } 899 printActive = false; 900 return; // not yet available .. 901 } 902 sendReshape = false; // clear reshape flag 903 printAnimator = helper.getAnimator(); 904 if( null != printAnimator ) { 905 printAnimator.remove(GLCanvas.this); 906 } 907 printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD 908 final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities(); 909 final int printNumSamples = printAWTTiles.getNumSamples(gladCaps); 910 GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); 911 final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples(); 912 final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || 913 printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); 914 final boolean reqNewGLADOnscrn = gladCaps.isOnscreen(); 915 916 final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable(); 917 newGLADCaps.setDoubleBuffered(false); 918 newGLADCaps.setOnscreen(false); 919 if( printNumSamples != newGLADCaps.getNumSamples() ) { 920 newGLADCaps.setSampleBuffers(0 < printNumSamples); 921 newGLADCaps.setNumSamples(printNumSamples); 922 } 923 final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps); 924 925 final boolean reqNewGLAD = ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe; 926 927 if( DEBUG ) { 928 System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+ 929 ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ 930 ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ 931 ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ 932 ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); 933 } 934 if( reqNewGLAD ) { 935 final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile()); 936 GLOffscreenAutoDrawable offGLAD = null; 937 try { 938 offGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null, 939 printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, 940 printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); 941 } catch (final GLException gle) { 942 if( DEBUG ) { 943 System.err.println("Caught: "+gle.getMessage()); 944 gle.printStackTrace(); 945 } 946 } 947 if( null != offGLAD ) { 948 printGLAD = offGLAD; 949 GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD); 950 printDrawable = printGLAD.getDelegatedDrawable(); 951 } 952 } 953 printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented()); 954 printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); 955 printAWTTiles.renderer.attachAutoDrawable(printGLAD); 956 if( DEBUG ) { 957 System.err.println("AWT print.setup "+printAWTTiles); 958 System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps); 959 System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); 960 System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); 961 } 962 } finally { 963 _lock.unlock(); 964 } 965 } 966 }; 967 968 @Override releasePrint()969 public void releasePrint() { 970 if( !printActive || null == printGLAD ) { 971 throw new IllegalStateException("setupPrint() not called"); 972 } 973 sendReshape = false; // clear reshape flag 974 AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT); 975 } 976 private final Runnable releasePrintOnEDT = new Runnable() { 977 @Override 978 public void run() { 979 final RecursiveLock _lock = lock; 980 _lock.lock(); 981 try { 982 if( DEBUG ) { 983 System.err.println("AWT print.release "+printAWTTiles); 984 } 985 printAWTTiles.dispose(); 986 printAWTTiles= null; 987 if( printGLAD != GLCanvas.this ) { 988 GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this); 989 printGLAD.destroy(); 990 } 991 printGLAD = null; 992 if( null != printAnimator ) { 993 printAnimator.add(GLCanvas.this); 994 printAnimator = null; 995 } 996 sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! 997 printActive = false; 998 display(); 999 } finally { 1000 _lock.unlock(); 1001 } 1002 } 1003 }; 1004 1005 @Override print(final Graphics graphics)1006 public void print(final Graphics graphics) { 1007 if( !printActive || null == printGLAD ) { 1008 throw new IllegalStateException("setupPrint() not called"); 1009 } 1010 if(DEBUG && !EventQueue.isDispatchThread()) { 1011 System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT"); 1012 // we cannot dispatch print on AWT-EDT due to printing internal locking .. 1013 } 1014 sendReshape = false; // clear reshape flag 1015 1016 final Graphics2D g2d = (Graphics2D)graphics; 1017 try { 1018 printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight()); 1019 final TileRenderer tileRenderer = printAWTTiles.renderer; 1020 if( DEBUG ) { 1021 System.err.println("AWT print.0: "+tileRenderer); 1022 } 1023 if( !tileRenderer.eot() ) { 1024 try { 1025 do { 1026 if( printGLAD != GLCanvas.this ) { 1027 tileRenderer.display(); 1028 } else { 1029 Threading.invoke(true, displayOnEDTAction, getTreeLock()); 1030 } 1031 } while ( !tileRenderer.eot() ); 1032 if( DEBUG ) { 1033 System.err.println("AWT print.1: "+printAWTTiles); 1034 } 1035 } finally { 1036 tileRenderer.reset(); 1037 printAWTTiles.resetGraphics2D(); 1038 } 1039 } 1040 } catch (final NoninvertibleTransformException nte) { 1041 System.err.println("Caught: Inversion failed of: "+g2d.getTransform()); 1042 nte.printStackTrace(); 1043 } 1044 if( DEBUG ) { 1045 System.err.println("AWT print.X: "+printAWTTiles); 1046 } 1047 } 1048 1049 @Override addGLEventListener(final GLEventListener listener)1050 public void addGLEventListener(final GLEventListener listener) { 1051 helper.addGLEventListener(listener); 1052 } 1053 1054 @Override addGLEventListener(final int index, final GLEventListener listener)1055 public void addGLEventListener(final int index, final GLEventListener listener) throws IndexOutOfBoundsException { 1056 helper.addGLEventListener(index, listener); 1057 } 1058 1059 @Override getGLEventListenerCount()1060 public int getGLEventListenerCount() { 1061 return helper.getGLEventListenerCount(); 1062 } 1063 1064 @Override getGLEventListener(final int index)1065 public GLEventListener getGLEventListener(final int index) throws IndexOutOfBoundsException { 1066 return helper.getGLEventListener(index); 1067 } 1068 1069 @Override areAllGLEventListenerInitialized()1070 public boolean areAllGLEventListenerInitialized() { 1071 return helper.areAllGLEventListenerInitialized(); 1072 } 1073 1074 @Override getGLEventListenerInitState(final GLEventListener listener)1075 public boolean getGLEventListenerInitState(final GLEventListener listener) { 1076 return helper.getGLEventListenerInitState(listener); 1077 } 1078 1079 @Override setGLEventListenerInitState(final GLEventListener listener, final boolean initialized)1080 public void setGLEventListenerInitState(final GLEventListener listener, final boolean initialized) { 1081 helper.setGLEventListenerInitState(listener, initialized); 1082 } 1083 1084 @Override disposeGLEventListener(final GLEventListener listener, final boolean remove)1085 public GLEventListener disposeGLEventListener(final GLEventListener listener, final boolean remove) { 1086 final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove); 1087 Threading.invoke(true, r, getTreeLock()); 1088 return r.listener; 1089 } 1090 1091 @Override removeGLEventListener(final GLEventListener listener)1092 public GLEventListener removeGLEventListener(final GLEventListener listener) { 1093 return helper.removeGLEventListener(listener); 1094 } 1095 1096 @Override setAnimator(final GLAnimatorControl animatorControl)1097 public void setAnimator(final GLAnimatorControl animatorControl) { 1098 helper.setAnimator(animatorControl); 1099 } 1100 1101 @Override getAnimator()1102 public GLAnimatorControl getAnimator() { 1103 return helper.getAnimator(); 1104 } 1105 1106 @Override setExclusiveContextThread(final Thread t)1107 public final Thread setExclusiveContextThread(final Thread t) throws GLException { 1108 return helper.setExclusiveContextThread(t, context); 1109 } 1110 1111 @Override getExclusiveContextThread()1112 public final Thread getExclusiveContextThread() { 1113 return helper.getExclusiveContextThread(); 1114 } 1115 1116 @Override invoke(final boolean wait, final GLRunnable glRunnable)1117 public boolean invoke(final boolean wait, final GLRunnable glRunnable) throws IllegalStateException { 1118 return helper.invoke(this, wait, glRunnable); 1119 } 1120 1121 @Override invoke(final boolean wait, final List<GLRunnable> glRunnables)1122 public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) throws IllegalStateException { 1123 return helper.invoke(this, wait, glRunnables); 1124 } 1125 1126 @Override flushGLRunnables()1127 public void flushGLRunnables() { 1128 helper.flushGLRunnables(); 1129 } 1130 1131 @Override setContext(final GLContext newCtx, final boolean destroyPrevCtx)1132 public GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) { 1133 final RecursiveLock _lock = lock; 1134 _lock.lock(); 1135 try { 1136 final GLContext oldCtx = context; 1137 GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); 1138 context=(GLContextImpl)newCtx; 1139 return oldCtx; 1140 } finally { 1141 _lock.unlock(); 1142 } 1143 } 1144 1145 @Override getDelegatedDrawable()1146 public final GLDrawable getDelegatedDrawable() { 1147 return drawable; 1148 } 1149 1150 @Override getContext()1151 public GLContext getContext() { 1152 return context; 1153 } 1154 1155 @Override getGL()1156 public GL getGL() { 1157 if( Beans.isDesignTime() ) { 1158 return null; 1159 } 1160 final GLContext _context = context; 1161 return (_context == null) ? null : _context.getGL(); 1162 } 1163 1164 @Override setGL(final GL gl)1165 public GL setGL(final GL gl) { 1166 final GLContext _context = context; 1167 if (_context != null) { 1168 _context.setGL(gl); 1169 return gl; 1170 } 1171 return null; 1172 } 1173 1174 1175 @Override setAutoSwapBufferMode(final boolean onOrOff)1176 public void setAutoSwapBufferMode(final boolean onOrOff) { 1177 helper.setAutoSwapBufferMode(onOrOff); 1178 } 1179 1180 @Override getAutoSwapBufferMode()1181 public boolean getAutoSwapBufferMode() { 1182 return helper.getAutoSwapBufferMode(); 1183 } 1184 1185 @Override swapBuffers()1186 public void swapBuffers() { 1187 Threading.invoke(true, swapBuffersOnEDTAction, getTreeLock()); 1188 } 1189 1190 @Override setContextCreationFlags(final int flags)1191 public void setContextCreationFlags(final int flags) { 1192 additionalCtxCreationFlags = flags; 1193 final GLContext _context = context; 1194 if(null != _context) { 1195 _context.setContextCreationFlags(additionalCtxCreationFlags); 1196 } 1197 } 1198 1199 @Override getContextCreationFlags()1200 public int getContextCreationFlags() { 1201 return additionalCtxCreationFlags; 1202 } 1203 1204 @Override getGLProfile()1205 public GLProfile getGLProfile() { 1206 return capsReqUser.getGLProfile(); 1207 } 1208 1209 @Override getChosenGLCapabilities()1210 public GLCapabilitiesImmutable getChosenGLCapabilities() { 1211 if( Beans.isDesignTime() ) { 1212 return capsReqUser; 1213 } else if( null == awtConfig ) { 1214 throw new GLException("No AWTGraphicsConfiguration: "+this); 1215 } 1216 return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(); 1217 } 1218 1219 @Override getRequestedGLCapabilities()1220 public GLCapabilitiesImmutable getRequestedGLCapabilities() { 1221 if( null == awtConfig ) { 1222 return capsReqUser; 1223 } 1224 return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(); 1225 } 1226 1227 @Override getSurfaceWidth()1228 public int getSurfaceWidth() { 1229 return SurfaceScaleUtils.scale(getWidth(), hasPixelScale[0]); 1230 } 1231 1232 @Override getSurfaceHeight()1233 public int getSurfaceHeight() { 1234 return SurfaceScaleUtils.scale(getHeight(), hasPixelScale[1]); 1235 } 1236 1237 @Override isGLOriented()1238 public boolean isGLOriented() { 1239 final GLDrawable _drawable = drawable; 1240 return null != _drawable ? _drawable.isGLOriented() : true; 1241 } 1242 1243 @Override getNativeSurface()1244 public NativeSurface getNativeSurface() { 1245 final GLDrawable _drawable = drawable; 1246 return (null != _drawable) ? _drawable.getNativeSurface() : null; 1247 } 1248 1249 @Override getHandle()1250 public long getHandle() { 1251 final GLDrawable _drawable = drawable; 1252 return (null != _drawable) ? _drawable.getHandle() : 0; 1253 } 1254 1255 @Override getFactory()1256 public GLDrawableFactory getFactory() { 1257 final GLDrawable _drawable = drawable; 1258 return (null != _drawable) ? _drawable.getFactory() : null; 1259 } 1260 1261 @Override toString()1262 public String toString() { 1263 final GLDrawable _drawable = drawable; 1264 final int dw = (null!=_drawable) ? _drawable.getSurfaceWidth() : -1; 1265 final int dh = (null!=_drawable) ? _drawable.getSurfaceHeight() : -1; 1266 1267 return "AWT-GLCanvas[Realized "+isRealized()+ 1268 ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+ 1269 ",\n\tFactory "+getFactory()+ 1270 ",\n\thandle 0x"+Long.toHexString(getHandle())+ 1271 ",\n\tDrawable size "+dw+"x"+dh+" surface["+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+ 1272 ",\n\tAWT[pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+ 1273 ",\n\tvisible "+isVisible()+", displayable "+isDisplayable()+", showing "+isShowing+ 1274 ",\n\t"+awtConfig+"]]"; 1275 } 1276 1277 //---------------------------------------------------------------------- 1278 // Internals only below this point 1279 // 1280 getPixelScaleStr()1281 private final String getPixelScaleStr() { return "["+hasPixelScale[0]+", "+hasPixelScale[1]+"]"; } 1282 1283 private final Runnable destroyOnEDTAction = new Runnable() { 1284 @Override 1285 public void run() { 1286 final RecursiveLock _lock = lock; 1287 _lock.lock(); 1288 try { 1289 final GLAnimatorControl animator = getAnimator(); 1290 1291 if(DEBUG) { 1292 System.err.println(getThreadName()+": Info: destroyOnEDTAction() - START, hasContext " + 1293 (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator); 1294 // Thread.dumpStack(); 1295 } 1296 1297 final boolean animatorPaused; 1298 if(null!=animator) { 1299 // can't remove us from animator for recreational addNotify() 1300 animatorPaused = animator.pause(); 1301 } else { 1302 animatorPaused = false; 1303 } 1304 1305 GLException exceptionOnDisposeGL = null; 1306 1307 // OLS will be detached by disposeGL's context destruction below 1308 if( null != context ) { 1309 if( context.isCreated() ) { 1310 try { 1311 helper.disposeGL(GLCanvas.this, context, true); 1312 if(DEBUG) { 1313 System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context); 1314 } 1315 } catch (final GLException gle) { 1316 exceptionOnDisposeGL = gle; 1317 } 1318 } 1319 context = null; 1320 } 1321 1322 Throwable exceptionOnUnrealize = null; 1323 if( null != drawable ) { 1324 try { 1325 drawable.setRealized(false); 1326 if(DEBUG) { 1327 System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); 1328 } 1329 } catch( final Throwable re ) { 1330 exceptionOnUnrealize = re; 1331 } 1332 drawable = null; 1333 } 1334 1335 if(animatorPaused) { 1336 animator.resume(); 1337 } 1338 1339 // throw exception in order of occurrence .. 1340 if( null != exceptionOnDisposeGL ) { 1341 throw exceptionOnDisposeGL; 1342 } 1343 if( null != exceptionOnUnrealize ) { 1344 throw GLException.newGLException(exceptionOnUnrealize); 1345 } 1346 1347 if(DEBUG) { 1348 System.err.println(getThreadName()+": dispose() - END, animator "+animator); 1349 } 1350 1351 } finally { 1352 _lock.unlock(); 1353 } 1354 } 1355 }; 1356 1357 /** 1358 * Disposes the JAWTWindow and AbstractGraphicsDevice within EDT, 1359 * since resources created (X11: Display), must be destroyed in the same thread, where they have been created. 1360 * <p> 1361 * The drawable and context handle are null'ed as well, assuming {@link #destroy()} has been called already. 1362 * </p> 1363 * 1364 * @see #chooseGraphicsConfiguration(com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice) 1365 */ 1366 private final Runnable disposeJAWTWindowAndAWTDeviceOnEDT = new Runnable() { 1367 @Override 1368 public void run() { 1369 context=null; 1370 drawable=null; 1371 1372 if( null != jawtWindow ) { 1373 jawtWindow.destroy(); 1374 if(DEBUG) { 1375 System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow); 1376 } 1377 jawtWindow=null; 1378 } 1379 hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; 1380 hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; 1381 minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; 1382 minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; 1383 maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; 1384 maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; 1385 1386 if(null != awtConfig) { 1387 final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); 1388 final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); 1389 final String adeviceMsg; 1390 if(DEBUG) { 1391 adeviceMsg = adevice.toString(); 1392 } else { 1393 adeviceMsg = null; 1394 } 1395 final boolean closed = adevice.close(); 1396 if(DEBUG) { 1397 System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed); 1398 } 1399 } 1400 awtConfig = null; 1401 } 1402 }; 1403 1404 private final Runnable initAction = new Runnable() { 1405 @Override 1406 public void run() { 1407 helper.init(GLCanvas.this, !sendReshape); 1408 } 1409 }; 1410 1411 private final Runnable displayAction = new Runnable() { 1412 @Override 1413 public void run() { 1414 if (sendReshape) { 1415 if(DEBUG) { 1416 System.err.println(getThreadName()+": Reshape: "+getSurfaceWidth()+"x"+getSurfaceHeight()); 1417 } 1418 // Note: we ignore the given x and y within the parent component 1419 // since we are drawing directly into this heavyweight component. 1420 helper.reshape(GLCanvas.this, 0, 0, getSurfaceWidth(), getSurfaceHeight()); 1421 sendReshape = false; 1422 } 1423 1424 helper.display(GLCanvas.this); 1425 } 1426 }; 1427 1428 private final Runnable displayOnEDTAction = new Runnable() { 1429 @Override 1430 public void run() { 1431 final RecursiveLock _lock = lock; 1432 _lock.lock(); 1433 try { 1434 if( null != drawable && drawable.isRealized() ) { 1435 if( GLCanvas.this.updatePixelScale() ) { 1436 GLCanvas.this.reshapeImpl(getWidth(), getHeight()); 1437 } 1438 helper.invokeGL(drawable, context, displayAction, initAction); 1439 } 1440 } finally { 1441 _lock.unlock(); 1442 } 1443 } 1444 }; 1445 1446 private final Runnable swapBuffersOnEDTAction = new Runnable() { 1447 @Override 1448 public void run() { 1449 final RecursiveLock _lock = lock; 1450 _lock.lock(); 1451 try { 1452 if( null != drawable && drawable.isRealized() ) { 1453 drawable.swapBuffers(); 1454 } 1455 } finally { 1456 _lock.unlock(); 1457 } 1458 } 1459 }; 1460 1461 private class DisposeGLEventListenerAction implements Runnable { 1462 GLEventListener listener; 1463 private final boolean remove; DisposeGLEventListenerAction(final GLEventListener listener, final boolean remove)1464 private DisposeGLEventListenerAction(final GLEventListener listener, final boolean remove) { 1465 this.listener = listener; 1466 this.remove = remove; 1467 } 1468 1469 @Override run()1470 public void run() { 1471 final RecursiveLock _lock = lock; 1472 _lock.lock(); 1473 try { 1474 listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove); 1475 } finally { 1476 _lock.unlock(); 1477 } 1478 } 1479 }; 1480 1481 // Disables the AWT's erasing of this Canvas's background on Windows 1482 // in Java SE 6. This internal API is not available in previous 1483 // releases, but the system property 1484 // -Dsun.awt.noerasebackground=true can be specified to get similar 1485 // results globally in previous releases. 1486 private static boolean disableBackgroundEraseInitialized; 1487 private static Method disableBackgroundEraseMethod; disableBackgroundErase()1488 private void disableBackgroundErase() { 1489 if (!disableBackgroundEraseInitialized) { 1490 try { 1491 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1492 @Override 1493 public Object run() { 1494 try { 1495 Class<?> clazz = getToolkit().getClass(); 1496 while (clazz != null && disableBackgroundEraseMethod == null) { 1497 try { 1498 disableBackgroundEraseMethod = 1499 clazz.getDeclaredMethod("disableBackgroundErase", 1500 new Class[] { Canvas.class }); 1501 disableBackgroundEraseMethod.setAccessible(true); 1502 } catch (final Exception e) { 1503 clazz = clazz.getSuperclass(); 1504 } 1505 } 1506 } catch (final Exception e) { 1507 } 1508 return null; 1509 } 1510 }); 1511 } catch (final Exception e) { 1512 } 1513 disableBackgroundEraseInitialized = true; 1514 if(DEBUG) { 1515 System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase method found: "+ 1516 (null!=disableBackgroundEraseMethod)); 1517 } 1518 } 1519 if (disableBackgroundEraseMethod != null) { 1520 Throwable t=null; 1521 try { 1522 disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this }); 1523 } catch (final Exception e) { 1524 t = e; 1525 } 1526 if(DEBUG) { 1527 System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase error: "+t); 1528 } 1529 } 1530 } 1531 1532 /** 1533 * Issues the GraphicsConfigurationFactory's choosing facility within EDT, 1534 * since resources created (X11: Display), must be destroyed in the same thread, where they have been created. 1535 * 1536 * @param capsChosen 1537 * @param capsRequested 1538 * @param chooser 1539 * @param device 1540 * @return the chosen AWTGraphicsConfiguration 1541 * 1542 * @see #disposeJAWTWindowAndAWTDeviceOnEDT 1543 */ chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final GraphicsDevice device)1544 private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen, 1545 final GLCapabilitiesImmutable capsRequested, 1546 final GLCapabilitiesChooser chooser, 1547 final GraphicsDevice device) { 1548 // Make GLCanvas behave better in NetBeans GUI builder 1549 if( Beans.isDesignTime() ) { 1550 return null; 1551 } 1552 if( null == device ) { 1553 throw new GLException("Error: NULL AWT GraphicsDevice"); 1554 } 1555 final AbstractGraphicsScreen aScreen = AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT); 1556 AWTGraphicsConfiguration config = null; 1557 1558 if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) { 1559 config = (AWTGraphicsConfiguration) 1560 GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen, 1561 capsRequested, 1562 chooser, aScreen, VisualIDHolder.VID_UNDEFINED); 1563 } else { 1564 try { 1565 final ArrayList<AWTGraphicsConfiguration> bucket = new ArrayList<AWTGraphicsConfiguration>(1); 1566 EventQueue.invokeAndWait(new Runnable() { 1567 @Override 1568 public void run() { 1569 final AWTGraphicsConfiguration c = (AWTGraphicsConfiguration) 1570 GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen, 1571 capsRequested, 1572 chooser, aScreen, VisualIDHolder.VID_UNDEFINED); 1573 bucket.add(c); 1574 } 1575 }); 1576 config = ( bucket.size() > 0 ) ? bucket.get(0) : null ; 1577 } catch (final InvocationTargetException e) { 1578 throw new GLException(e.getTargetException()); 1579 } catch (final InterruptedException e) { 1580 throw new GLException(e); 1581 } 1582 } 1583 1584 if ( null == config ) { 1585 throw new GLException("Error: Couldn't fetch AWTGraphicsConfiguration"); 1586 } 1587 1588 return config; 1589 } 1590 getThreadName()1591 protected static String getThreadName() { return Thread.currentThread().getName(); } 1592 1593 /** 1594 * A most simple JOGL AWT test entry 1595 */ main(final String args[])1596 public static void main(final String args[]) { 1597 System.err.println(VersionUtil.getPlatformInfo()); 1598 System.err.println(GlueGenVersion.getInstance()); 1599 // System.err.println(NativeWindowVersion.getInstance()); 1600 System.err.println(JoglVersion.getInstance()); 1601 1602 System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString()); 1603 1604 final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) ); 1605 final Frame frame = new Frame("JOGL AWT Test"); 1606 1607 final GLCanvas glCanvas = new GLCanvas(caps); 1608 frame.add(glCanvas); 1609 frame.setSize(128, 128); 1610 1611 glCanvas.addGLEventListener(new GLEventListener() { 1612 @Override 1613 public void init(final GLAutoDrawable drawable) { 1614 final GL gl = drawable.getGL(); 1615 System.err.println(JoglVersion.getGLInfo(gl, null)); 1616 } 1617 @Override 1618 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } 1619 @Override 1620 public void display(final GLAutoDrawable drawable) { } 1621 @Override 1622 public void dispose(final GLAutoDrawable drawable) { } 1623 }); 1624 1625 try { 1626 javax.swing.SwingUtilities.invokeAndWait(new Runnable() { 1627 @Override 1628 public void run() { 1629 frame.setVisible(true); 1630 }}); 1631 } catch (final Throwable t) { 1632 t.printStackTrace(); 1633 } 1634 glCanvas.display(); 1635 try { 1636 javax.swing.SwingUtilities.invokeAndWait(new Runnable() { 1637 @Override 1638 public void run() { 1639 frame.dispose(); 1640 }}); 1641 } catch (final Throwable t) { 1642 t.printStackTrace(); 1643 } 1644 } 1645 1646 } 1647