/* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package com.jogamp.opengl.awt; import java.beans.Beans; import java.awt.Canvas; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Rectangle2D; import java.awt.EventQueue; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import com.jogamp.nativewindow.AbstractGraphicsConfiguration; import com.jogamp.nativewindow.OffscreenLayerOption; import com.jogamp.nativewindow.ScalableSurface; import com.jogamp.nativewindow.VisualIDHolder; import com.jogamp.nativewindow.WindowClosingProtocol; import com.jogamp.nativewindow.AbstractGraphicsDevice; import com.jogamp.nativewindow.AbstractGraphicsScreen; import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.NativeSurface; import com.jogamp.nativewindow.NativeWindowFactory; import com.jogamp.opengl.GL; import com.jogamp.opengl.GLAnimatorControl; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilitiesChooser; import com.jogamp.opengl.GLCapabilitiesImmutable; import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLDrawable; import com.jogamp.opengl.GLDrawableFactory; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLOffscreenAutoDrawable; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.GLRunnable; import com.jogamp.opengl.GLSharedContextSetter; import com.jogamp.opengl.Threading; import com.jogamp.common.GlueGenVersion; import com.jogamp.common.util.VersionUtil; import com.jogamp.common.util.awt.AWTEDTExecutor; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; import com.jogamp.nativewindow.awt.AWTGraphicsDevice; import com.jogamp.nativewindow.awt.AWTGraphicsScreen; import com.jogamp.nativewindow.awt.AWTPrintLifecycle; import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.nativewindow.awt.JAWTWindow; import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.util.GLDrawableUtil; import com.jogamp.opengl.util.TileRenderer; import jogamp.nativewindow.SurfaceScaleUtils; import jogamp.nativewindow.jawt.JAWTUtil; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableHelper; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.awt.AWTTilePainter; // FIXME: Subclasses need to call resetGLFunctionAvailability() on their // context whenever the displayChanged() function is called on our // GLEventListeners /** A heavyweight AWT component which provides OpenGL rendering support. This is the primary implementation of an AWT {@link GLDrawable}; {@link GLJPanel} is provided for compatibility with Swing user interfaces when adding a heavyweight doesn't work either because of Z-ordering or LayoutManager problems. * *
* {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)}
* is being called if {@link GLCapabilitiesImmutable#isOnscreen()} is false
.
*
sun.java2d.opengl=false
sun.java2d.noddraw=true
sun.java2d.opengl=true
sun.java2d.noddraw=true
disableBeackgroundErase(java.awt.Canvas)
.sun.awt.noerasebackground=true
See details about OpenGL context sharing.
* @throws GLException if no default profile is available for the default desktop device. */ public GLCanvas() throws GLException { this(null); } /** Creates a new GLCanvas component with the requested set of OpenGL capabilities, using the default OpenGL capabilities selection mechanism, on the default screen device.See details about OpenGL context sharing.
* @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. * @see GLCanvas#GLCanvas(com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesChooser, com.jogamp.opengl.GLContext, java.awt.GraphicsDevice) */ public GLCanvas(final GLCapabilitiesImmutable capsReqUser) throws GLException { this(capsReqUser, null, null); } /** Creates a new GLCanvas component. The passed GLCapabilities specifies the OpenGL capabilities for the component; if null, a default set of capabilities is used. The GLCapabilitiesChooser specifies the algorithm for selecting one of the available GLCapabilities for the component; a DefaultGLCapabilitesChooser is used if null is passed for this argument. The passed GraphicsDevice indicates the screen on which to create the GLCanvas; the GLDrawableFactory uses the default screen device of the local GraphicsEnvironment if null is passed for this argument.See details about OpenGL context sharing.
* @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. */ public GLCanvas(final GLCapabilitiesImmutable capsReqUser, final GLCapabilitiesChooser chooser, final GraphicsDevice device) throws GLException { /* * Determination of the native window is made in 'super.addNotify()', * which creates the native peer using AWT's GraphicsConfiguration. * GraphicsConfiguration is returned by this class overwritten * 'getGraphicsConfiguration()', which returns our OpenGL compatible * 'chosen' GraphicsConfiguration. */ super(); if(null==capsReqUser) { this.capsReqUser = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDevice())); } else { // don't allow the user to change data this.capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable(); } if( !this.capsReqUser.isOnscreen() ) { setShallUseOffscreenLayer(true); // trigger offscreen layer - if supported } // One time user AWT GraphicsDevice request awtDeviceReq = device; // instantiation will be issued in addNotify() this.chooser = chooser; this.addHierarchyListener(hierarchyListener); this.isShowing = isShowing(); } @Override public final void setSharedContext(final GLContext sharedContext) throws IllegalStateException { helper.setSharedContext(this.context, sharedContext); } @Override public final void setSharedAutoDrawable(final GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { helper.setSharedAutoDrawable(this, sharedAutoDrawable); } @Override public final Object getUpstreamWidget() { return this; } @Override public final RecursiveLock getUpstreamLock() { return lock; } @Override public final boolean isThreadGLCapable() { return Threading.isOpenGLThread(); } @Override public void setShallUseOffscreenLayer(final boolean v) { shallUseOffscreenLayer = v; } @Override public final boolean getShallUseOffscreenLayer() { return shallUseOffscreenLayer; } @Override public final boolean isOffscreenLayerSurfaceEnabled() { final JAWTWindow _jawtWindow = jawtWindow; if(null != _jawtWindow) { return _jawtWindow.isOffscreenLayerSurfaceEnabled(); } return false; } /** * {@inheritDoc} ** Overridden to choose a {@link GraphicsConfiguration} from a parent container's * {@link GraphicsDevice}. *
** Method also intercepts {@link GraphicsConfiguration} changes regarding to * its capabilities and its {@link GraphicsDevice}. This may happen in case * the display changes its configuration or the component is moved to another screen. *
*/ @Override public GraphicsConfiguration getGraphicsConfiguration() { /** * parentGC will be null unless: * - A native peer has assigned it. This means we have a native * peer, and are already committed to a graphics configuration. * - This canvas has been added to a component hierarchy and has * an ancestor with a non-null GC, but the native peer has not * yet been created. This means we can still choose the GC on * all platforms since the peer hasn't been created. */ final GraphicsConfiguration parentGC = super.getGraphicsConfiguration(); if( Beans.isDesignTime() ) { return parentGC; } final GraphicsConfiguration oldGC = null != awtConfig ? awtConfig.getAWTGraphicsConfiguration() : null; if ( null != parentGC && null != oldGC && !oldGC.equals(parentGC) ) { // Previous oldGC != parentGC of native peer if ( !oldGC.getDevice().getIDstring().equals(parentGC.getDevice().getIDstring()) ) { // Previous oldGC's GraphicsDevice != parentGC's GraphicsDevice of native peer /** * Here we select a GraphicsConfiguration on the alternate device. * In case the new configuration differs (-> !equalCaps), * we might need a reconfiguration, */ final AWTGraphicsConfiguration newConfig = chooseGraphicsConfiguration( (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(), (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(), chooser, parentGC.getDevice()); final GraphicsConfiguration newGC = newConfig.getAWTGraphicsConfiguration(); final boolean equalCaps = newConfig.getChosenCapabilities().equals(awtConfig.getChosenCapabilities()); if(DEBUG) { System.err.println(getThreadName()+": getGraphicsConfiguration() Info: Changed GC and GD"); System.err.println("Created Config (n): Old GC "+oldGC); System.err.println("Created Config (n): Old GD "+oldGC.getDevice().getIDstring()); System.err.println("Created Config (n): Parent GC "+parentGC); System.err.println("Created Config (n): Parent GD "+parentGC.getDevice().getIDstring()); System.err.println("Created Config (n): New GC "+newGC); System.err.println("Created Config (n): New GD "+newGC.getDevice().getIDstring()); System.err.println("Created Config (n): Old CF "+awtConfig); System.err.println("Created Config (n): New CF "+newConfig); System.err.println("Created Config (n): EQUALS CAPS "+equalCaps); // Thread.dumpStack(); } if ( null != newGC ) { if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) { // complete destruction! destroyImpl( true ); // recreation! setAWTGraphicsConfiguration(newConfig); createJAWTDrawableAndContext(); validateGLDrawable(); } else { setAWTGraphicsConfiguration(newConfig); } /** * Return the newGC, which covers the desired capabilities and is compatible * with the available GC's of its devices. */ if(DEBUG) { System.err.println(getThreadName()+": Info: getGraphicsConfiguration - end.01: newGC "+newGC); } return newGC; } else { if(DEBUG) { System.err.println(getThreadName()+": Info: getGraphicsConfiguration - end.00: oldGC "+oldGC); } } } /** * If a new GC was _not_ found/defined above, * method returns oldGC as selected in the constructor or first addNotify(). * This may cause an exception in Component.checkGD when adding to a * container, and is the desired behavior. */ return oldGC; } else if (null == parentGC) { /** * The parentGC is null, which means we have no native peer, and are not * part of a (realized) component hierarchy. So we return the * desired visual that was selected in the constructor (possibly * null). */ return oldGC; } else { /** * Otherwise we have not explicitly selected a GC in the constructor, so * just return what Canvas would have. */ return parentGC; } } @Override public GLContext createContext(final GLContext shareWith) { final RecursiveLock _lock = lock; _lock.lock(); try { if(drawable != null) { final GLContext _ctx = drawable.createContext(shareWith); _ctx.setContextCreationFlags(additionalCtxCreationFlags); return _ctx; } return null; } finally { _lock.unlock(); } } private final void setRealizedImpl(final boolean realized) { final RecursiveLock _lock = lock; _lock.lock(); try { final GLDrawable _drawable = drawable; if( null == _drawable || realized == _drawable.isRealized() || realized && ( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) ) { return; } _drawable.setRealized(realized); if( realized && _drawable.isRealized() ) { sendReshape=true; // ensure a reshape is being send .. } } finally { _lock.unlock(); } } private final Runnable realizeOnEDTAction = new Runnable() { @Override public void run() { setRealizedImpl(true); } }; private final Runnable unrealizeOnEDTAction = new Runnable() { @Override public void run() { setRealizedImpl(false); } }; @Override public final void setRealized(final boolean realized) { // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock! AWTEDTExecutor.singleton.invoke(getTreeLock(), false /* allowOnNonEDT */, true /* wait */, realized ? realizeOnEDTAction : unrealizeOnEDTAction); } @Override public boolean isRealized() { final GLDrawable _drawable = drawable; return ( null != _drawable ) ? _drawable.isRealized() : false; } @Override public WindowClosingMode getDefaultCloseOperation() { return awtWindowClosingProtocol.getDefaultCloseOperation(); } @Override public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { return awtWindowClosingProtocol.setDefaultCloseOperation(op); } @Override public void display() { if( !validateGLDrawable() ) { if(DEBUG) { System.err.println(getThreadName()+": Info: GLCanvas display - skipped GL render, drawable not valid yet"); } return; // not yet available .. } if( isShowing && !printActive ) { Threading.invoke(true, displayOnEDTAction, getTreeLock()); } } /** * {@inheritDoc} * ** This impl. only destroys all GL related resources. *
** This impl. does not remove the GLCanvas from it's parent AWT container * so this class's {@link #removeNotify()} AWT override won't get called. * To do so, remove this component from it's parent AWT container. *
*/ @Override public void destroy() { destroyImpl( false ); } protected void destroyImpl(final boolean destroyJAWTWindowAndAWTDevice) { Threading.invoke(true, destroyOnEDTAction, getTreeLock()); if( destroyJAWTWindowAndAWTDevice ) { AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, disposeJAWTWindowAndAWTDeviceOnEDT); } } /** Overridden to cause OpenGL rendering to be performed during repaint cycles. Subclasses which override this method must call super.paint() in their paint() method in order to function properly. */ @Override public void paint(final Graphics g) { if( Beans.isDesignTime() ) { // Make GLCanvas behave better in NetBeans GUI builder g.setColor(Color.BLACK); g.fillRect(0, 0, getWidth(), getHeight()); final FontMetrics fm = g.getFontMetrics(); String name = getName(); if (name == null) { name = getClass().getName(); final int idx = name.lastIndexOf('.'); if (idx >= 0) { name = name.substring(idx + 1); } } final Rectangle2D bounds = fm.getStringBounds(name, g); g.setColor(Color.WHITE); g.drawString(name, (int) ((getWidth() - bounds.getWidth()) / 2), (int) ((getHeight() + bounds.getHeight()) / 2)); } else if( !this.helper.isAnimatorAnimatingOnOtherThread() ) { display(); } } /** Overridden to track when this component is added to a container. Subclasses which override this method must call super.addNotify() in their addNotify() method in order to function properly.Overrides:
addNotify
in class java.awt.Component
* This implementation returns false, i.e. not supporting manual change of pixel-scale. *
*/ @Override public final boolean canSetSurfaceScale() { return false; } /** * {@inheritDoc} ** Ignored for an AWT widget since pixelScale is dictated by AWT mechanisms. *
*/ @Override public final boolean setSurfaceScale(final float[] pixelScale) { return false; } private final boolean updatePixelScale() { if( jawtWindow.hasPixelScaleChanged() ) { if(DEBUG) { final float[] old = { hasPixelScale[0], hasPixelScale[1] }; jawtWindow.getCurrentSurfaceScale(hasPixelScale); System.err.printf("GLCanvas.updatePixelScale hasPixelScale %.2f %.2f -> %.2f %.2f\n", old[0], old[1], hasPixelScale[0], hasPixelScale[1]); } else { jawtWindow.getCurrentSurfaceScale(hasPixelScale); } return true; } else { return false; } } /** * {@inheritDoc} ** Returns {@link ScalableSurface#AUTOMAX_PIXELSCALE}, always. *
*/ @Override public final float[] getRequestedSurfaceScale(final float[] result) { result[0] = ScalableSurface.AUTOMAX_PIXELSCALE; result[1] = ScalableSurface.AUTOMAX_PIXELSCALE; return result; } @Override public final float[] getCurrentSurfaceScale(final float[] result) { System.arraycopy(hasPixelScale, 0, result, 0, 2); return result; } /** * {@inheritDoc} ** Returns 1.0, always. *
*/ @Override public float[] getMinimumSurfaceScale(final float[] result) { result[0] = 1f; result[1] = 1f; return result; } /** * {@inheritDoc} ** Returns {@link #getCurrentSurfaceScale(float[])}. *
*/ @Override public float[] getMaximumSurfaceScale(final float[] result) { System.arraycopy(hasPixelScale, 0, result, 0, 2); return result; } private void createJAWTDrawableAndContext() { if ( !Beans.isDesignTime() ) { /** * FIXME: Bug 1373, 1374: Implement general High-DPI for even non native DPI toolkit aware platforms (Linux, Windows) JAWTUtil.getPixelScale(awtConfig.getAWTGraphicsConfiguration(), minPixelScale, maxPixelScale); SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null); */ jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); jawtWindow.lockSurface(); try { drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); createContextImpl(drawable); jawtWindow.getCurrentSurfaceScale(hasPixelScale); } finally { jawtWindow.unlockSurface(); } } } private boolean createContextImpl(final GLDrawable drawable) { final GLContext[] shareWith = { null }; if( !helper.isSharedGLContextPending(shareWith) ) { context = (GLContextImpl) drawable.createContext(shareWith[0]); context.setContextCreationFlags(additionalCtxCreationFlags); if(DEBUG) { System.err.println(getThreadName()+": Context created: has shared "+(null != shareWith[0])); } return true; } else { if(DEBUG) { System.err.println(getThreadName()+": Context !created: pending share"); } return false; } } private boolean validateGLDrawable() { if( Beans.isDesignTime() || !isDisplayable() ) { return false; // early out! } final GLDrawable _drawable = drawable; if ( null != _drawable ) { boolean res = _drawable.isRealized(); if( !res ) { // re-try drawable creation if( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) { return false; // early out! } setRealized(true); res = _drawable.isRealized(); if(DEBUG) { System.err.println(getThreadName()+": Realized Drawable: isRealized "+res+", "+_drawable.toString()); // Thread.dumpStack(); } } if( res && null == context ) { // re-try context creation res = createContextImpl(_drawable); // pending creation. } return res; } return false; } private void setAWTGraphicsConfiguration(final AWTGraphicsConfiguration config) { // Cache awtConfig awtConfig = config; if( null != jawtWindow ) { // Notify JAWTWindow .. jawtWindow.setAWTGraphicsConfiguration(config); } } /**Overridden to track when this component is removed from a container. Subclasses which override this method must call super.removeNotify() in their removeNotify() method in order to function properly.
User shall not call this method outside of EDT, read the AWT/Swing specs about this.
Overrides:removeNotify
in class java.awt.Component
Overrides:
reshape
in class java.awt.Component
* The drawable and context handle are null'ed as well, assuming {@link #destroy()} has been called already. *
* * @see #chooseGraphicsConfiguration(com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice) */ private final Runnable disposeJAWTWindowAndAWTDeviceOnEDT = new Runnable() { @Override public void run() { context=null; drawable=null; if( null != jawtWindow ) { jawtWindow.destroy(); if(DEBUG) { System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow); } jawtWindow=null; } hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; if(null != awtConfig) { final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); final String adeviceMsg; if(DEBUG) { adeviceMsg = adevice.toString(); } else { adeviceMsg = null; } final boolean closed = adevice.close(); if(DEBUG) { System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed); } } awtConfig = null; } }; private final Runnable initAction = new Runnable() { @Override public void run() { helper.init(GLCanvas.this, !sendReshape); } }; private final Runnable displayAction = new Runnable() { @Override public void run() { if (sendReshape) { if(DEBUG) { System.err.println(getThreadName()+": Reshape: "+getSurfaceWidth()+"x"+getSurfaceHeight()); } // Note: we ignore the given x and y within the parent component // since we are drawing directly into this heavyweight component. helper.reshape(GLCanvas.this, 0, 0, getSurfaceWidth(), getSurfaceHeight()); sendReshape = false; } helper.display(GLCanvas.this); } }; private final Runnable displayOnEDTAction = new Runnable() { @Override public void run() { final RecursiveLock _lock = lock; _lock.lock(); try { if( null != drawable && drawable.isRealized() ) { if( GLCanvas.this.updatePixelScale() ) { GLCanvas.this.reshapeImpl(getWidth(), getHeight()); } helper.invokeGL(drawable, context, displayAction, initAction); } } finally { _lock.unlock(); } } }; private final Runnable swapBuffersOnEDTAction = new Runnable() { @Override public void run() { final RecursiveLock _lock = lock; _lock.lock(); try { if( null != drawable && drawable.isRealized() ) { drawable.swapBuffers(); } } finally { _lock.unlock(); } } }; private class DisposeGLEventListenerAction implements Runnable { GLEventListener listener; private final boolean remove; private DisposeGLEventListenerAction(final GLEventListener listener, final boolean remove) { this.listener = listener; this.remove = remove; } @Override public void run() { final RecursiveLock _lock = lock; _lock.lock(); try { listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove); } finally { _lock.unlock(); } } }; /** * Issues the GraphicsConfigurationFactory's choosing facility within EDT, * since resources created (X11: Display), must be destroyed in the same thread, where they have been created. * * @param capsChosen * @param capsRequested * @param chooser * @param device * @return the chosen AWTGraphicsConfiguration * * @see #disposeJAWTWindowAndAWTDeviceOnEDT */ private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final GraphicsDevice device) { // Make GLCanvas behave better in NetBeans GUI builder if( Beans.isDesignTime() ) { return null; } if( null == device ) { throw new GLException("Error: NULL AWT GraphicsDevice"); } final AbstractGraphicsScreen aScreen = AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT); AWTGraphicsConfiguration config = null; if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) { config = (AWTGraphicsConfiguration) GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, aScreen, VisualIDHolder.VID_UNDEFINED); } else { try { final ArrayList