diff options
6 files changed, 378 insertions, 161 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 202b7ce38..88d3626cf 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -137,7 +137,7 @@ function jrun() { #D_ARGS="-Djogl.debug.GLSLCode -Djogl.debug.TraceGL" #D_ARGS="-Djogl.debug.GLSLCode -Djogl.debug.DebugGL" #D_ARGS="-Djogl.debug.GLContext -Dnativewindow.debug.JAWT -Dnewt.debug.Window" - #D_ARGS="-Dnativewindow.debug.JAWT -Djogl.debug.GLCanvas" + D_ARGS="-Dnativewindow.debug.JAWT -Djogl.debug.GLCanvas" #D_ARGS="-Dnativewindow.debug.JAWT -Djogamp.debug.TaskBase.TraceSource" #D_ARGS="-Dnativewindow.debug.JAWT" #D_ARGS="-Djogl.debug.GLContext.TraceSwitch" @@ -423,6 +423,7 @@ function testawtswt() { #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT $* +testawt com.jogamp.opengl.test.junit.jogl.demos.gl2.awt.TestGearsAWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.glsl.TestRulerNEWT01 $* #testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtDemo $* #testawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtCanvasAWTDemo $* @@ -512,7 +513,7 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLException01NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestMapBufferRead01NEWT $* -testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestMapBufferRead02NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestMapBufferRead02NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestRedSquareES2NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $* diff --git a/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java index 11d217535..5f59f7e6d 100644 --- a/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java @@ -173,7 +173,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private final RecursiveLock lock = LockFactory.createRecursiveLock(); private final GLDrawableHelper helper = new GLDrawableHelper(); - private AWTGraphicsConfiguration awtConfig; private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle private volatile GLContextImpl context; // volatile: avoid locking for read-only access @@ -187,9 +186,10 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private final GLCapabilitiesImmutable capsReqUser; private final GLCapabilitiesChooser chooser; private int additionalCtxCreationFlags = 0; - private final GraphicsDevice device; private boolean shallUseOffscreenLayer = false; + private volatile GraphicsDevice awtDeviceReq; // one time user req. + private volatile AWTGraphicsConfiguration awtConfig; private volatile boolean isShowing; private final HierarchyListener hierarchyListener = new HierarchyListener() { @Override @@ -270,16 +270,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing setShallUseOffscreenLayer(true); // trigger offscreen layer - if supported } - if(null==device) { - final GraphicsConfiguration gc = super.getGraphicsConfiguration(); - if(null!=gc) { - this.device = gc.getDevice(); - } else { - this.device = null; - } - } else { - this.device = device; - } + // One time user AWT GraphicsDevice request + awtDeviceReq = device; // instantiation will be issued in addNotify() this.chooser = chooser; @@ -330,124 +322,111 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing /** - * Overridden to choose a GraphicsConfiguration on a parent container's - * GraphicsDevice because both devices + * {@inheritDoc} + * <p> + * Overridden to choose a {@link GraphicsConfiguration} from a parent container's + * {@link GraphicsDevice}. + * </p> + * <p> + * 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. + * </p> */ @Override public GraphicsConfiguration getGraphicsConfiguration() { - /* - * Workaround for problems with Xinerama and java.awt.Component.checkGD - * when adding to a container on a different graphics device than the - * one that this Canvas is associated with. - * - * GC will be null unless: - * - A native peer has assigned it. This means we have a native - * peer, and are already comitted 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 gc = super.getGraphicsConfiguration(); - - if( Beans.isDesignTime() ) { - return gc; - } - - /* - * chosen is only non-null on platforms where the GLDrawableFactory - * returns a non-null GraphicsConfiguration (in the GLCanvas - * constructor). - * - * if gc is from this Canvas' native peer then it should equal chosen, - * otherwise it is from an ancestor component that this Canvas is being - * added to, and we go into this block. - */ - GraphicsConfiguration chosen = null != awtConfig ? awtConfig.getAWTGraphicsConfiguration() : null; - - if (gc != null && chosen != null && !chosen.equals(gc)) { - /* - * Check for compatibility with gc. If they differ by only the - * device then return a new GCconfig with the super-class' GDevice - * (and presumably the same visual ID in Xinerama). - * + /** + * 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. */ - if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) { - /* - * Here we select a GraphicsConfiguration on the alternate - * device that is presumably identical to the chosen - * configuration, but on the other device. - * - * Should really check to ensure that we select a configuration - * with the same X visual ID for Xinerama screens, otherwise the - * GLDrawable may have the wrong visual ID (I don't think this - * ever gets updated). May need to add a method to - * X11GLDrawableFactory to do this in a platform specific - * manner. - * - * However, on platforms where we can actually get into this - * block, both devices should have the same visual list, and the - * same configuration should be selected here. - */ - final AWTGraphicsConfiguration config = chooseGraphicsConfiguration( (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(), - (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(), - chooser, gc.getDevice()); - final GraphicsConfiguration compatible = config.getAWTGraphicsConfiguration(); - final boolean equalCaps = config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities()); - if(DEBUG) { - System.err.println(getThreadName()+": Info:"); - System.err.println("Created Config (n): HAVE GC "+chosen); - System.err.println("Created Config (n): THIS GC "+gc); - System.err.println("Created Config (n): Choosen GC "+compatible); - System.err.println("Created Config (n): HAVE CF "+awtConfig); - System.err.println("Created Config (n): Choosen CF "+config); - System.err.println("Created Config (n): EQUALS CAPS "+equalCaps); - // Thread.dumpStack(); - } + final GraphicsConfiguration parentGC = super.getGraphicsConfiguration(); - if (compatible != null) { - /* - * Save the new GC for equals test above, and to return to - * any outside callers of this method. - */ - chosen = compatible; - - if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) { - // complete destruction! - destroyImpl( true ); - // recreation! - awtConfig = config; - createJAWTDrawableAndContext(); - validateGLDrawable(); - } else { - awtConfig = config; + 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; } - - /* - * If a compatible GC was not found in the block above, this will - * return the GC that was selected in the constructor (and might - * cause an exception in Component.checkGD when adding to a - * container, but in this case that would be the desired behavior). - * - */ - return chosen; - } else if (gc == null) { - /* - * The GC 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 chosen; - } - - /* - * Otherwise we have not explicitly selected a GC in the constructor, so - * just return what Canvas would have. - */ - return gc; } @Override @@ -606,20 +585,35 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } else { /** * 'super.addNotify()' determines the GraphicsConfiguration, - * while calling this class's overriden 'getGraphicsConfiguration()' method + * while calling this class's overridden 'getGraphicsConfiguration()' method * after which it creates the native peer. * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration * is being used in getGraphicsConfiguration(). * This code order also allows recreation, ie re-adding the GLCanvas. */ - awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device); - if(null==awtConfig) { - throw new GLException("Error: NULL AWTGraphicsConfiguration"); - } // before native peer is valid: X11 disableBackgroundErase(); + final GraphicsDevice awtDevice; + if(null==awtDeviceReq) { + // Query AWT GraphicsDevice from parent tree, default + final GraphicsConfiguration gc = super.getGraphicsConfiguration(); + if(null==gc) { + throw new GLException("Error: NULL AWT GraphicsConfiguration"); + } + awtDevice = gc.getDevice(); + } else { + // Use one time user AWT GraphicsDevice request + awtDevice = awtDeviceReq; + awtDeviceReq = null; + } + final AWTGraphicsConfiguration awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, awtDevice); + if(null==awtConfig) { + throw new GLException("Error: NULL AWTGraphicsConfiguration"); + } + setAWTGraphicsConfiguration(awtConfig); + // issues getGraphicsConfiguration() and creates the native peer super.addNotify(); @@ -775,6 +769,15 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing return false; } + private void setAWTGraphicsConfiguration(final AWTGraphicsConfiguration config) { + // Cache awtConfig + awtConfig = config; + if( null != jawtWindow ) { + // Notify JAWTWindow .. + jawtWindow.setAWTGraphicsConfiguration(config); + } + } + /** <p>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 @@ -1394,7 +1397,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed); } } - awtConfig=null; + awtConfig = null; } }; @@ -1546,10 +1549,10 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing if( Beans.isDesignTime() ) { return null; } - - final AbstractGraphicsScreen aScreen = null != device ? - AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT): - AWTGraphicsScreen.createDefault(); + 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()) ) { diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTGraphicsConfiguration.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTGraphicsConfiguration.java index 62fd49092..cc70b6b7b 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTGraphicsConfiguration.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/AWTGraphicsConfiguration.java @@ -73,19 +73,42 @@ public class AWTGraphicsConfiguration extends DefaultGraphicsConfiguration imple } /** + * @deprecated Use {@link #create(GraphicsConfiguration, CapabilitiesImmutable, CapabilitiesImmutable)} + * Method constructs a new {@link AWTGraphicsConfiguration} primarily based + * on the given {@link Component}'s {@link GraphicsConfiguration}. + * @param awtComp the {@link Component}, which {@link GraphicsConfiguration} is used for the resulting {@link AWTGraphicsConfiguration} * @param capsChosen if null, <code>capsRequested</code> is copied and aligned * with the graphics {@link Capabilities} of the AWT Component to produce the chosen {@link Capabilities}. * Otherwise the <code>capsChosen</code> is used. * @param capsRequested if null, default {@link Capabilities} are used, otherwise the given values. */ - public static AWTGraphicsConfiguration create(final Component awtComp, CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested) { - final GraphicsConfiguration awtGfxConfig = awtComp.getGraphicsConfiguration(); - if(null==awtGfxConfig) { - throw new NativeWindowException("AWTGraphicsConfiguration.create: Null AWT GraphicsConfiguration @ "+awtComp); + public static AWTGraphicsConfiguration create(final Component awtComp, final CapabilitiesImmutable capsChosen, final CapabilitiesImmutable capsRequested) { + if(null==awtComp) { + throw new IllegalArgumentException("Null AWT Component"); } - final GraphicsDevice awtGraphicsDevice = awtGfxConfig.getDevice(); + final GraphicsConfiguration gc = awtComp.getGraphicsConfiguration(); + if( null == gc ) { + throw new NativeWindowException("Null AWT GraphicsConfiguration @ "+awtComp); + } + return create(gc, capsChosen, capsRequested); + } + + /** + * Method constructs a new {@link AWTGraphicsConfiguration} primarily based + * on the given {@link GraphicsConfiguration}. + * @param gc the {@link GraphicsConfiguration} for the resulting {@link AWTGraphicsConfiguration} + * @param capsChosen if null, <code>capsRequested</code> is copied and aligned + * with the graphics {@link Capabilities} of the AWT Component to produce the chosen {@link Capabilities}. + * Otherwise the <code>capsChosen</code> is used. + * @param capsRequested if null, default {@link Capabilities} are used, otherwise the given values. + */ + public static AWTGraphicsConfiguration create(final GraphicsConfiguration gc, CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested) { + if(null==gc) { + throw new IllegalArgumentException("Null AWT GraphicsConfiguration"); + } + final GraphicsDevice awtGraphicsDevice = gc.getDevice(); if(null==awtGraphicsDevice) { - throw new NativeWindowException("AWTGraphicsConfiguration.create: Null AWT GraphicsDevice @ "+awtGfxConfig); + throw new NativeWindowException("Null AWT GraphicsDevice @ "+gc); } // Create Device/Screen @@ -96,7 +119,6 @@ public class AWTGraphicsConfiguration extends DefaultGraphicsConfiguration imple capsRequested = new Capabilities(); } if(null==capsChosen) { - final GraphicsConfiguration gc = awtGraphicsDevice.getDefaultConfiguration(); capsChosen = AWTGraphicsConfiguration.setupCapabilitiesRGBABits(capsRequested, gc); } final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(awtDevice, capsChosen); @@ -105,7 +127,7 @@ public class AWTGraphicsConfiguration extends DefaultGraphicsConfiguration imple return (AWTGraphicsConfiguration) config; } // System.err.println("Info: AWTGraphicsConfiguration.create: Expected AWTGraphicsConfiguration got: "+config.getClass()+" w/ factory "+factory.getClass()+" - Unable to encapsulate native GraphicsConfiguration."); - return new AWTGraphicsConfiguration(awtScreen, capsChosen, capsRequested, awtGfxConfig); + return new AWTGraphicsConfiguration(awtScreen, capsChosen, capsRequested, gc); } // open access to superclass method diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java index d315e1876..3a8cbefc6 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java @@ -49,7 +49,6 @@ import java.awt.Container; import java.awt.Cursor; import java.awt.EventQueue; import java.awt.GraphicsConfiguration; -import java.awt.Window; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.HierarchyEvent; @@ -91,10 +90,10 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, // lifetime: forever protected final Component component; private final AppContextInfo appContextInfo; - private final AWTGraphicsConfiguration config; // control access due to delegation private final SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper(); private final RecursiveLock surfaceLock = LockFactory.createRecursiveLock(); private final JAWTComponentListener jawtComponentListener; + private volatile AWTGraphicsConfiguration awtConfig; // control access through delegation // lifetime: valid after lock but may change with each 1st lock, purges after invalidate private boolean isApplet; @@ -128,9 +127,9 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } appContextInfo = new AppContextInfo("<init>"); this.component = (Component)comp; - this.config = (AWTGraphicsConfiguration) config; this.jawtComponentListener = new JAWTComponentListener(); invalidate(); + this.awtConfig = (AWTGraphicsConfiguration) config; this.isApplet = false; this.offscreenSurfaceLayer = 0; } @@ -265,6 +264,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } invalidateNative(); jawt = null; + awtConfig = null; isOffscreenLayerSurface = false; drawable= 0; drawable_old = 0; @@ -280,6 +280,34 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } protected abstract void invalidateNative(); + /** + * Set a new {@link AWTGraphicsConfiguration} instance, + * as required if {@link #getAWTComponent() upstream component}'s {@link GraphicsConfiguration} has been changed + * due to reconfiguration, e.g. moving to a different monitor or changed capabilities. + * <p> + * {@link #getAWTComponent() Upstream component} shall override {@link Component#getGraphicsConfiguration()}, + * which shall call this method if detecting a reconfiguration. + * See JOGL's GLCanvas and NewtCanvasAWT. + * </p> + * @param config the new {@link AWTGraphicsConfiguration} + * @see #getAWTGraphicsConfiguration() + */ + public final void setAWTGraphicsConfiguration(final AWTGraphicsConfiguration config) { + if(DEBUG) { + System.err.println(jawtStr()+".setAWTGraphicsConfiguration(): "+this.awtConfig+" -> "+config); + // Thread.dumpStack(); + } + this.awtConfig = config; + } + /** + * Return the current {@link AWTGraphicsConfiguration} instance, + * which also holds its {@link #getAWTComponent() upstream component}'s {@link GraphicsConfiguration} + * @see #setAWTGraphicsConfiguration(AWTGraphicsConfiguration) + */ + public final AWTGraphicsConfiguration getAWTGraphicsConfiguration() { + return awtConfig; + } + @Override public boolean setSurfaceScale(final float[] pixelScale) { System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); @@ -527,7 +555,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, @Override public final void setChosenCapabilities(final CapabilitiesImmutable caps) { ((MutableGraphicsConfiguration)getGraphicsConfiguration()).setChosenCapabilities(caps); - config.setChosenCapabilities(caps); + awtConfig.setChosenCapabilities(caps); } @Override @@ -706,7 +734,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, @Override public final AbstractGraphicsConfiguration getGraphicsConfiguration() { - return config.getNativeGraphicsConfiguration(); + return awtConfig.getNativeGraphicsConfiguration(); } @Override @@ -877,7 +905,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, "], pixels[scale "+getPixelScaleX()+", "+getPixelScaleY()+" -> "+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+ ", visible "+component.isVisible()); sb.append(", lockedExt "+isSurfaceLockedByOtherThread()+ - ",\n\tconfig "+config+ + ",\n\tconfig "+awtConfig+ ",\n\tawtComponent "+getAWTComponent()+ ",\n\tsurfaceLock "+surfaceLock+"]"); diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index 5920d6de8..0a27f7f22 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -37,6 +37,7 @@ import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; import java.awt.KeyboardFocusManager; import java.awt.geom.NoninvertibleTransformException; import java.beans.Beans; @@ -47,6 +48,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Set; +import com.jogamp.nativewindow.CapabilitiesImmutable; import com.jogamp.nativewindow.NativeWindow; import com.jogamp.nativewindow.OffscreenLayerOption; import com.jogamp.nativewindow.WindowClosingProtocol; @@ -71,6 +73,7 @@ import jogamp.opengl.awt.AWTTilePainter; import com.jogamp.common.ExceptionUtils; import com.jogamp.common.util.awt.AWTEDTExecutor; +import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; import com.jogamp.nativewindow.awt.AWTPrintLifecycle; import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.nativewindow.awt.JAWTWindow; @@ -101,7 +104,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto public static final boolean DEBUG = Debug.debug("Window"); private final Object sync = new Object(); - private JAWTWindow jawtWindow = null; + private volatile JAWTWindow jawtWindow = null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle private boolean isApplet = false; private boolean shallUseOffscreenLayer = false; private Window newtChild = null; @@ -112,6 +115,8 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto private final AWTAdapter awtMouseAdapter; private final AWTAdapter awtKeyAdapter; + private volatile AWTGraphicsConfiguration awtConfig; + /** Mitigates Bug 910 (IcedTea-Web), i.e. crash via removeNotify() invoked before Applet.destroy(). */ private boolean destroyJAWTPending = false; /** Mitigates Bug 910 (IcedTea-Web), i.e. crash via removeNotify() invoked before Applet.destroy(). */ @@ -452,14 +457,142 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } + private void setAWTGraphicsConfiguration(final AWTGraphicsConfiguration config) { + // Cache awtConfig + awtConfig = config; + if( null != jawtWindow ) { + // Notify JAWTWindow .. + jawtWindow.setAWTGraphicsConfiguration(config); + } + } + + /** + * {@inheritDoc} + * <p> + * Overridden to choose a {@link GraphicsConfiguration} from a parent container's + * {@link GraphicsDevice}. + * </p> + * <p> + * 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. + * </p> + */ + @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 = AWTGraphicsConfiguration.create(parentGC, + awtConfig.getChosenCapabilities(), + awtConfig.getRequestedCapabilities()); + 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): 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 ) { + 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; + } + } + private static String getThreadName() { return Thread.currentThread().getName(); } + @Override public void addNotify() { if( Beans.isDesignTime() ) { super.addNotify(); } else { + /** + * 'super.addNotify()' determines the GraphicsConfiguration, + * while calling this class's overridden 'getGraphicsConfiguration()' method + * after which it creates the native peer. + * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration + * is being used in getGraphicsConfiguration(). + * This code order also allows recreation, ie re-adding the GLCanvas. + */ // before native peer is valid: X11 disableBackgroundErase(); + // Query AWT GraphicsDevice from parent tree, default + final GraphicsConfiguration gc = super.getGraphicsConfiguration(); + if(null==gc) { + throw new GLException("Error: NULL AWT GraphicsConfiguration"); + } + final CapabilitiesImmutable capsReq = null != newtChild ? newtChild.getRequestedCapabilities() : null; + final AWTGraphicsConfiguration awtConfig = AWTGraphicsConfiguration.create(gc, null, capsReq); + if(null==awtConfig) { + throw new GLException("Error: NULL AWTGraphicsConfiguration"); + } + setAWTGraphicsConfiguration(awtConfig); + // creates the native peer super.addNotify(); @@ -472,7 +605,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto System.err.println("NewtCanvasAWT.addNotify.0 - isApplet "+isApplet+", addedOnAWTEDT "+EventQueue.isDispatchThread()+" @ "+currentThreadName()); ExceptionUtils.dumpStack(System.err); } - jawtWindow = NewtFactoryAWT.getNativeWindow(NewtCanvasAWT.this, null != newtChild ? newtChild.getRequestedCapabilities() : null); + jawtWindow = NewtFactoryAWT.getNativeWindow(NewtCanvasAWT.this, awtConfig); jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); // enforce initial lock on AWT-EDT, allowing acquisition of pixel-scale jawtWindow.lockSurface(); diff --git a/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java b/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java index 8eaca7c0e..cc71fb559 100644 --- a/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java +++ b/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java @@ -28,8 +28,6 @@ package jogamp.newt.awt; -import java.awt.GraphicsDevice; - import com.jogamp.nativewindow.AbstractGraphicsConfiguration; import com.jogamp.nativewindow.CapabilitiesImmutable; import com.jogamp.nativewindow.NativeWindow; @@ -57,6 +55,8 @@ public class NewtFactoryAWT extends NewtFactory { public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); /** + * @deprecated Use {@link #getNativeWindow(java.awt.Component, AWTGraphicsConfiguration)} + * * Wraps an AWT component into a {@link com.jogamp.nativewindow.NativeWindow} utilizing the {@link com.jogamp.nativewindow.NativeWindowFactory},<br> * using a configuration agnostic dummy {@link com.jogamp.nativewindow.DefaultGraphicsConfiguration}.<br> * <p> @@ -78,9 +78,37 @@ public class NewtFactoryAWT extends NewtFactory { return getNativeWindow( (java.awt.Component) awtCompObject, capsRequested ); } + /** + * @deprecated Use {@link #getNativeWindow(java.awt.Component, AWTGraphicsConfiguration)} + * @param awtComp + * @param capsRequested + * @return + */ public static JAWTWindow getNativeWindow(final java.awt.Component awtComp, final CapabilitiesImmutable capsRequested) { - final AWTGraphicsConfiguration config = AWTGraphicsConfiguration.create(awtComp, null, capsRequested); - final NativeWindow nw = NativeWindowFactory.getNativeWindow(awtComp, config); // a JAWTWindow + final AWTGraphicsConfiguration awtConfig = AWTGraphicsConfiguration.create(awtComp, null, capsRequested); + return getNativeWindow(awtComp, awtConfig); + } + + /** + * Wraps an AWT component into a {@link com.jogamp.nativewindow.NativeWindow} utilizing the {@link com.jogamp.nativewindow.NativeWindowFactory},<br> + * using the given {@link AWTGraphicsConfiguration}. + * <p> + * The actual wrapping implementation is {@link com.jogamp.nativewindow.awt.JAWTWindow}. + * </p> + * <p> + * The required {@link AWTGraphicsConfiguration} may be constructed via + * {@link AWTGraphicsConfiguration#create(java.awt.GraphicsConfiguration, CapabilitiesImmutable, CapabilitiesImmutable)} + * </p> + * <p> + * Purpose of this wrapping is to access the AWT window handle,<br> + * not to actually render into it. + * </p> + * + * @param awtComp {@link java.awt.Component} + * @param awtConfig {@link AWTGraphicsConfiguration} reflecting the used {@link java.awt.GraphicsConfiguration} + */ + public static JAWTWindow getNativeWindow(final java.awt.Component awtComp, final AWTGraphicsConfiguration awtConfig) { + final NativeWindow nw = NativeWindowFactory.getNativeWindow(awtComp, awtConfig); // a JAWTWindow if(! ( nw instanceof JAWTWindow ) ) { throw new NativeWindowException("Not an AWT NativeWindow: "+nw); } @@ -101,13 +129,15 @@ public class NewtFactoryAWT extends NewtFactory { * @throws IllegalArgumentException if {@code awtComp} is not {@link java.awt.Component#isDisplayable() displayable} * or has {@code null} {@link java.awt.Component#getGraphicsConfiguration() GraphicsConfiguration}. */ - private static void checkComponentValid(final java.awt.Component awtComp) throws IllegalArgumentException { + private static java.awt.GraphicsConfiguration checkComponentValid(final java.awt.Component awtComp) throws IllegalArgumentException { if( !awtComp.isDisplayable() ) { throw new IllegalArgumentException("Given AWT-Component is not displayable: "+awtComp); } - if( null == awtComp.getGraphicsConfiguration() ) { + final java.awt.GraphicsConfiguration gc = awtComp.getGraphicsConfiguration(); + if( null == gc ) { throw new IllegalArgumentException("Given AWT-Component has no GraphicsConfiguration set: "+awtComp); } + return gc; } /** @@ -120,8 +150,8 @@ public class NewtFactoryAWT extends NewtFactory { * @see #getAbstractGraphicsScreen(java.awt.Component) */ public static Display createDisplay(final java.awt.Component awtComp, final boolean reuse) throws IllegalArgumentException { - checkComponentValid(awtComp); - final GraphicsDevice device = awtComp.getGraphicsConfiguration().getDevice(); + final java.awt.GraphicsConfiguration gc = checkComponentValid(awtComp); + final java.awt.GraphicsDevice device = gc.getDevice(); final String displayConnection; final String nwt = NativeWindowFactory.getNativeWindowType(true); @@ -174,13 +204,13 @@ public class NewtFactoryAWT extends NewtFactory { * @see #createScreen(java.awt.Component, boolean) */ public static MonitorDevice getMonitorDevice(final Screen screen, final java.awt.Component awtComp) throws IllegalArgumentException { - checkComponentValid(awtComp); + final java.awt.GraphicsConfiguration gc = checkComponentValid(awtComp); final String nwt = NativeWindowFactory.getNativeWindowType(true); MonitorDevice res = null; screen.addReference(); try { if( NativeWindowFactory.TYPE_MACOSX == nwt ) { - res = screen.getMonitor( JAWTUtil.getMonitorDisplayID( awtComp.getGraphicsConfiguration().getDevice() ) ); + res = screen.getMonitor( JAWTUtil.getMonitorDisplayID( gc.getDevice() ) ); } if( null == res ) { // Fallback, use AWT component coverage |