diff options
Diffstat (limited to 'src/nativewindow/classes/jogamp/nativewindow')
34 files changed, 2603 insertions, 751 deletions
diff --git a/src/nativewindow/classes/jogamp/nativewindow/Debug.java b/src/nativewindow/classes/jogamp/nativewindow/Debug.java index e07fd1b57..b7197dbca 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/Debug.java +++ b/src/nativewindow/classes/jogamp/nativewindow/Debug.java @@ -1,21 +1,22 @@ /* - * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -28,17 +29,20 @@ * 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 jogamp.nativewindow; +import java.security.AccessController; +import java.security.PrivilegedAction; + import com.jogamp.common.util.PropertyAccess; /** Helper routines for logging and debugging. */ @@ -47,9 +51,14 @@ public class Debug extends PropertyAccess { // Some common properties private static final boolean verbose; private static final boolean debugAll; - + static { - PropertyAccess.addTrustedPrefix("nativewindow.", Debug.class); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + PropertyAccess.addTrustedPrefix("nativewindow."); + return null; + } } ); verbose = isPropertyDefined("nativewindow.verbose", true); debugAll = isPropertyDefined("nativewindow.debug", true); @@ -61,27 +70,18 @@ public class Debug extends PropertyAccess { } } - public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) { - return PropertyAccess.isPropertyDefined(property, jnlpAlias, null); - } - - public static String getProperty(final String property, final boolean jnlpAlias) { - return PropertyAccess.getProperty(property, jnlpAlias, null); - } - - public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias) { - return PropertyAccess.getBooleanProperty(property, jnlpAlias, null); - } - - public static boolean verbose() { + /** Ensures static init block has been issues, i.e. if calling through to {@link PropertyAccess#isPropertyDefined(String, boolean)}. */ + public static final void initSingleton() {} + + public static final boolean verbose() { return verbose; } - public static boolean debugAll() { + public static final boolean debugAll() { return debugAll; } - public static boolean debug(String subcomponent) { + public static final boolean debug(String subcomponent) { return debugAll() || isPropertyDefined("nativewindow.debug." + subcomponent, true); } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/DefaultGraphicsConfigurationFactoryImpl.java b/src/nativewindow/classes/jogamp/nativewindow/DefaultGraphicsConfigurationFactoryImpl.java index f34b740d4..8fb819251 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/DefaultGraphicsConfigurationFactoryImpl.java +++ b/src/nativewindow/classes/jogamp/nativewindow/DefaultGraphicsConfigurationFactoryImpl.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -36,8 +36,9 @@ package jogamp.nativewindow; import javax.media.nativewindow.*; public class DefaultGraphicsConfigurationFactoryImpl extends GraphicsConfigurationFactory { + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( - CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen screen) { + CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen screen, int nativeVisualID) { return new DefaultGraphicsConfiguration(screen, capsChosen, capsRequested); } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11JAWTToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/GlobalToolkitLock.java index 743d371b7..cadef9bf1 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11JAWTToolkitLock.java +++ b/src/nativewindow/classes/jogamp/nativewindow/GlobalToolkitLock.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2012 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: @@ -25,52 +25,55 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package jogamp.nativewindow.jawt.x11; -import jogamp.nativewindow.jawt.*; -import jogamp.nativewindow.x11.X11Lib; -import jogamp.nativewindow.x11.X11Util; +package jogamp.nativewindow; + import javax.media.nativewindow.ToolkitLock; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; /** - * Implementing a recursive {@link javax.media.nativewindow.ToolkitLock} - * utilizing JAWT's AWT lock via {@link JAWTUtil#lockToolkit()} and {@link X11Util#XLockDisplay(long)}. - * <br> - * This strategy should only be used if AWT is using the underlying native windowing toolkit - * in a not intrinsic thread safe manner, e.g. under X11 where no XInitThreads() call - * is issued before any other X11 usage. This is the current situation for e.g. Webstart or Applets. + * Implementing a global recursive {@link javax.media.nativewindow.ToolkitLock}. + * <p> + * This is the last resort for unstable driver where multiple X11 display connections + * to the same connection name are not treated thread safe within the GL/X11 driver. + * </p> */ -public class X11JAWTToolkitLock implements ToolkitLock { - long displayHandle; - RecursiveLock lock; +public class GlobalToolkitLock implements ToolkitLock { + private static final RecursiveLock globalLock = LockFactory.createRecursiveLock(); + private static GlobalToolkitLock singleton = new GlobalToolkitLock(); - public X11JAWTToolkitLock(long displayHandle) { - this.displayHandle = displayHandle; - if(!X11Util.isNativeLockAvailable()) { - lock = LockFactory.createRecursiveLock(); - } + public static final GlobalToolkitLock getSingleton() { + return singleton; } + private GlobalToolkitLock() { } + + @Override public final void lock() { - if(TRACE_LOCK) { System.err.println("X11JAWTToolkitLock.lock() - native: "+(null==lock)); } - JAWTUtil.lockToolkit(); - if(null == lock) { - X11Lib.XLockDisplay(displayHandle); - } else { - lock.lock(); - } + globalLock.lock(); + if(TRACE_LOCK) { System.err.println("GlobalToolkitLock.lock()"); } } + @Override public final void unlock() { - if(TRACE_LOCK) { System.err.println("X11JAWTToolkitLock.unlock() - native: "+(null==lock)); } - if(null == lock) { - X11Lib.XUnlockDisplay(displayHandle); - } else { - lock.unlock(); - } - JAWTUtil.unlockToolkit(); + if(TRACE_LOCK) { System.err.println("GlobalToolkitLock.unlock()"); } + globalLock.unlock(); // implicit lock validation + } + + @Override + public final void validateLocked() throws RuntimeException { + globalLock.validateLocked(); + } + + @Override + public final void dispose() { + // nop + } + + @Override + public String toString() { + return "GlobalToolkitLock[obj 0x"+Integer.toHexString(hashCode())+", isOwner "+globalLock.isOwner(Thread.currentThread())+", "+globalLock.toString()+"]"; } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/NWJNILibLoader.java b/src/nativewindow/classes/jogamp/nativewindow/NWJNILibLoader.java index 99bc71c4a..e7eb13756 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/NWJNILibLoader.java +++ b/src/nativewindow/classes/jogamp/nativewindow/NWJNILibLoader.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions 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. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.nativewindow; @@ -37,18 +37,17 @@ import com.jogamp.common.os.Platform; import com.jogamp.common.util.cache.TempJarCache; public class NWJNILibLoader extends JNILibLoaderBase { - - public static boolean loadNativeWindow(final String ossuffix) { - return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { - public Boolean run() { - Platform.initSingleton(); - final String libName = "nativewindow_"+ossuffix ; - if(TempJarCache.isInitialized() && null == TempJarCache.findLibrary(libName)) { - addNativeJarLibs(NWJNILibLoader.class, "jogl-all", new String[] { "nativewindow" } ); - } - return new Boolean(loadLibrary(libName, false, NWJNILibLoader.class.getClassLoader())); - } - }).booleanValue(); - } - + public static boolean loadNativeWindow(final String ossuffix) { + return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { + @Override + public Boolean run() { + Platform.initSingleton(); + final String libName = "nativewindow_"+ossuffix ; + if(TempJarCache.isInitialized() && null == TempJarCache.findLibrary(libName)) { + JNILibLoaderBase.addNativeJarLibsJoglCfg(new Class<?>[] { NWJNILibLoader.class }); + } + return Boolean.valueOf(loadLibrary(libName, false, NWJNILibLoader.class.getClassLoader())); + } + }).booleanValue(); + } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java b/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java index 9e18439db..22ac3bd94 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java +++ b/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. 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 @@ -39,7 +39,6 @@ import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.ToolkitLock; -import com.jogamp.common.os.Platform; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.ReflectionUtil.AWTNames; @@ -47,11 +46,12 @@ public class NativeWindowFactoryImpl extends NativeWindowFactory { private static final ToolkitLock nullToolkitLock = new NullToolkitLock(); public static ToolkitLock getNullToolkitLock() { - return nullToolkitLock; + return nullToolkitLock; } - + // This subclass of NativeWindowFactory handles the case of // NativeWindows being passed in + @Override protected NativeWindow getNativeWindowImpl(Object winObj, AbstractGraphicsConfiguration config) throws IllegalArgumentException { if (winObj instanceof NativeWindow) { // Use the NativeWindow directly @@ -70,31 +70,31 @@ public class NativeWindowFactoryImpl extends NativeWindowFactory { winObj.getClass().getName() + " is unsupported; expected " + "javax.media.nativewindow.NativeWindow or "+AWTNames.ComponentClass); } - + private Constructor<?> nativeWindowConstructor = null; private NativeWindow getAWTNativeWindow(Object winObj, AbstractGraphicsConfiguration config) { if (nativeWindowConstructor == null) { try { - String windowingType = getNativeWindowType(true); - String windowClassName = null; + final String windowingType = getNativeWindowType(true); + final String windowClassName; // We break compile-time dependencies on the AWT here to // make it easier to run this code on mobile devices - if (windowingType.equals(TYPE_WINDOWS)) { + if (TYPE_WINDOWS == windowingType) { windowClassName = "jogamp.nativewindow.jawt.windows.WindowsJAWTWindow"; - } else if (windowingType.equals(TYPE_MACOSX)) { + } else if (TYPE_MACOSX == windowingType) { windowClassName = "jogamp.nativewindow.jawt.macosx.MacOSXJAWTWindow"; - } else if (windowingType.equals(TYPE_X11)) { + } else if (TYPE_X11 == windowingType) { // Assume Linux, Solaris, etc. Should probably test for these explicitly. windowClassName = "jogamp.nativewindow.jawt.x11.X11JAWTWindow"; } else { - throw new IllegalArgumentException("OS " + Platform.getOSName() + " not yet supported"); + throw new IllegalArgumentException("Native windowing type " + windowingType + " (custom) not yet supported, platform reported native windowing type: "+getNativeWindowType(false)); } nativeWindowConstructor = ReflectionUtil.getConstructor( - windowClassName, new Class[] { Object.class, AbstractGraphicsConfiguration.class }, + windowClassName, new Class[] { Object.class, AbstractGraphicsConfiguration.class }, getClass().getClassLoader()); } catch (Exception e) { throw new IllegalArgumentException(e); diff --git a/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java index 1af6bf279..bda20522c 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java +++ b/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java @@ -28,18 +28,18 @@ package jogamp.nativewindow; +import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.ToolkitLock; /** - * Implementing a singleton global recursive {@link javax.media.nativewindow.ToolkitLock} - * without any locking. Since there is no locking it all, - * it is intrinsically recursive. + * Implementing a singleton global NOP {@link javax.media.nativewindow.ToolkitLock} + * without any locking. Since there is no locking it all, it is intrinsically recursive. */ public class NullToolkitLock implements ToolkitLock { - /** Singleton via {@link NativeWindowFactoryImpl#getNullToolkitLock()} */ protected NullToolkitLock() { } - + + @Override public final void lock() { if(TRACE_LOCK) { System.err.println("NullToolkitLock.lock()"); @@ -47,7 +47,26 @@ public class NullToolkitLock implements ToolkitLock { } } + @Override public final void unlock() { if(TRACE_LOCK) { System.err.println("NullToolkitLock.unlock()"); } } + + @Override + public final void validateLocked() throws RuntimeException { + if( NativeWindowFactory.requiresToolkitLock() ) { + throw new RuntimeException("NullToolkitLock does not lock, but locking is required."); + } + } + + @Override + public final void dispose() { + // nop + } + + @Override + public String toString() { + return "NullToolkitLock[]"; + } + } diff --git a/src/nativewindow/classes/jogamp/nativewindow/ProxySurfaceImpl.java b/src/nativewindow/classes/jogamp/nativewindow/ProxySurfaceImpl.java new file mode 100644 index 000000000..fbff7128e --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/ProxySurfaceImpl.java @@ -0,0 +1,325 @@ +/** + * Copyright 2010 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.nativewindow; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.SurfaceUpdatedListener; +import javax.media.nativewindow.UpstreamSurfaceHook; + + +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; + +public abstract class ProxySurfaceImpl implements ProxySurface { + private final SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper(); + private AbstractGraphicsConfiguration config; // control access due to delegation + private UpstreamSurfaceHook upstream; + private long surfaceHandle_old; + private final RecursiveLock surfaceLock = LockFactory.createRecursiveLock(); + private int implBitfield; + private boolean upstreamSurfaceHookLifecycleEnabled; + + /** + * @param cfg the {@link AbstractGraphicsConfiguration} to be used + * @param upstream the {@link UpstreamSurfaceHook} to be used + * @param ownsDevice <code>true</code> if this {@link ProxySurface} instance + * owns the {@link AbstractGraphicsConfiguration}'s {@link AbstractGraphicsDevice}, + * otherwise <code>false</code>. Owning the device implies closing it at {@link #destroyNotify()}. + */ + protected ProxySurfaceImpl(AbstractGraphicsConfiguration cfg, UpstreamSurfaceHook upstream, boolean ownsDevice) { + if(null == cfg) { + throw new IllegalArgumentException("null AbstractGraphicsConfiguration"); + } + if(null == upstream) { + throw new IllegalArgumentException("null UpstreamSurfaceHook"); + } + this.config = cfg; + this.upstream = upstream; + this.surfaceHandle_old = 0; + this.implBitfield = 0; + this.upstreamSurfaceHookLifecycleEnabled = true; + if(ownsDevice) { + addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + } + + @Override + public NativeSurface getUpstreamSurface() { return null; } + + @Override + public final UpstreamSurfaceHook getUpstreamSurfaceHook() { return upstream; } + + @Override + public void setUpstreamSurfaceHook(UpstreamSurfaceHook hook) { + if(null == hook) { + throw new IllegalArgumentException("null UpstreamSurfaceHook"); + } + upstream = hook; + } + + @Override + public final void enableUpstreamSurfaceHookLifecycle(boolean enable) { + upstreamSurfaceHookLifecycleEnabled = enable; + } + + @Override + public void createNotify() { + if(upstreamSurfaceHookLifecycleEnabled) { + upstream.create(this); + } + this.surfaceHandle_old = 0; + } + + @Override + public void destroyNotify() { + if(upstreamSurfaceHookLifecycleEnabled) { + upstream.destroy(this); + if( containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ) ) { + final AbstractGraphicsDevice aDevice = getGraphicsConfiguration().getScreen().getDevice(); + aDevice.close(); + clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + invalidateImpl(); + } + this.surfaceHandle_old = 0; + } + + /** + * Must be overridden by implementations allowing having a {@link UpstreamSurfaceHook} being passed. + * @see #destroyNotify() + */ + protected void invalidateImpl() { + throw new InternalError("UpstreamSurfaceHook given, but required method not implemented."); + } + + @Override + public final AbstractGraphicsConfiguration getGraphicsConfiguration() { + return config.getNativeGraphicsConfiguration(); + } + + @Override + public final long getDisplayHandle() { + return config.getNativeGraphicsConfiguration().getScreen().getDevice().getHandle(); + } + + @Override + public final void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg) { + config = cfg; + } + + @Override + public final int getScreenIndex() { + return getGraphicsConfiguration().getScreen().getIndex(); + } + + @Override + public abstract long getSurfaceHandle(); + + @Override + public abstract void setSurfaceHandle(long surfaceHandle); + + @Override + public final int getWidth() { + return upstream.getWidth(this); + } + + @Override + public final int getHeight() { + return upstream.getHeight(this); + } + + @Override + public boolean surfaceSwap() { + return false; + } + + @Override + public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { + surfaceUpdatedHelper.addSurfaceUpdatedListener(l); + } + + @Override + public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + surfaceUpdatedHelper.addSurfaceUpdatedListener(index, l); + } + + @Override + public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { + surfaceUpdatedHelper.removeSurfaceUpdatedListener(l); + } + + @Override + public void surfaceUpdated(Object updater, NativeSurface ns, long when) { + surfaceUpdatedHelper.surfaceUpdated(updater, ns, when); + } + + @Override + public int lockSurface() throws NativeWindowException, RuntimeException { + surfaceLock.lock(); + int res = surfaceLock.getHoldCount() == 1 ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ? + + if ( LOCK_SURFACE_NOT_READY == res ) { + try { + final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice(); + adevice.lock(); + try { + res = lockSurfaceImpl(); + if(LOCK_SUCCESS == res && surfaceHandle_old != getSurfaceHandle()) { + res = LOCK_SURFACE_CHANGED; + if(DEBUG) { + System.err.println("ProxySurfaceImpl: surface change 0x"+Long.toHexString(surfaceHandle_old)+" -> 0x"+Long.toHexString(getSurfaceHandle())); + // Thread.dumpStack(); + } + } + } finally { + if (LOCK_SURFACE_NOT_READY >= res) { + adevice.unlock(); + } + } + } finally { + if (LOCK_SURFACE_NOT_READY >= res) { + surfaceLock.unlock(); + } + } + } + return res; + } + + @Override + public final void unlockSurface() { + surfaceLock.validateLocked(); + surfaceHandle_old = getSurfaceHandle(); + + if (surfaceLock.getHoldCount() == 1) { + final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice(); + try { + unlockSurfaceImpl(); + } finally { + adevice.unlock(); + } + } + surfaceLock.unlock(); + } + + protected abstract int lockSurfaceImpl(); + + protected abstract void unlockSurfaceImpl() ; + + public final void validateSurfaceLocked() { + surfaceLock.validateLocked(); + } + + @Override + public final boolean isSurfaceLockedByOtherThread() { + return surfaceLock.isLockedByOtherThread(); + } + + @Override + public final Thread getSurfaceLockOwner() { + return surfaceLock.getOwner(); + } + + @Override + public final StringBuilder getUpstreamOptionBits(StringBuilder sink) { + if(null == sink) { + sink = new StringBuilder(); + } + sink.append("UOB[ "); + if(0 == implBitfield) { + sink.append("]"); + return sink; + } + boolean needsOr = false; + if( 0 != ( implBitfield & OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + sink.append("OWNS_SURFACE"); + needsOr = true; + } + if( 0 != ( implBitfield & OPT_PROXY_OWNS_UPSTREAM_DEVICE ) ) { + if(needsOr) { + sink.append(" | "); + } + sink.append("OWNS_DEVICE"); + needsOr = true; + } + if( 0 != ( implBitfield & OPT_UPSTREAM_WINDOW_INVISIBLE ) ) { + if(needsOr) { + sink.append(" | "); + } + sink.append("WINDOW_INVISIBLE"); + needsOr = true; + } + sink.append(" ]"); + return sink; + } + + @Override + public final int getUpstreamOptionBits() { return implBitfield; } + + @Override + public final boolean containsUpstreamOptionBits(int v) { + return v == ( implBitfield & v ) ; + } + + @Override + public final void addUpstreamOptionBits(int v) { implBitfield |= v; } + + @Override + public final void clearUpstreamOptionBits(int v) { implBitfield &= ~v; } + + @Override + public StringBuilder toString(StringBuilder sink) { + if(null == sink) { + sink = new StringBuilder(); + } + sink.append("displayHandle 0x" + Long.toHexString(getDisplayHandle())). + append("\n, surfaceHandle 0x" + Long.toHexString(getSurfaceHandle())). + append("\n, size " + getWidth() + "x" + getHeight()).append("\n, "); + getUpstreamOptionBits(sink); + sink.append("\n, "+config). + append("\n, surfaceLock "+surfaceLock+"\n, "). + append(getUpstreamSurfaceHook()). + append("\n, upstreamSurface "+(null != getUpstreamSurface())); + // append("\n, upstreamSurface "+getUpstreamSurface()); + return sink; + } + + @Override + public String toString() { + StringBuilder msg = new StringBuilder(); + msg.append(getClass().getSimpleName()).append("[ "); + toString(msg); + msg.append(" ]"); + return msg.toString(); + } + +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11ToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/ResourceToolkitLock.java index 5166ef577..f1efb8133 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11ToolkitLock.java +++ b/src/nativewindow/classes/jogamp/nativewindow/ResourceToolkitLock.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2012 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: @@ -25,7 +25,8 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package jogamp.nativewindow.x11; + +package jogamp.nativewindow; import javax.media.nativewindow.ToolkitLock; @@ -33,38 +34,47 @@ import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; /** - * Implementing a recursive {@link javax.media.nativewindow.ToolkitLock} - * utilizing {@link X11Util#XLockDisplay(long)}. - * <br> - * This strategy should not be used in case XInitThreads() is being used, - * or a higher level toolkit lock is required, ie AWT lock. + * Implementing a resource based recursive {@link javax.media.nativewindow.ToolkitLock}. + * <p> + * A resource handle maybe used within a unique object + * and can be synchronized across threads via an instance of ResourceToolkitLock. + * </p> */ -public class X11ToolkitLock implements ToolkitLock { - long displayHandle; - RecursiveLock lock; +public class ResourceToolkitLock implements ToolkitLock { + public static final ResourceToolkitLock create() { + return new ResourceToolkitLock(); + } + + private final RecursiveLock lock; - public X11ToolkitLock(long displayHandle) { - this.displayHandle = displayHandle; - if(!X11Util.isNativeLockAvailable()) { - lock = LockFactory.createRecursiveLock(); - } + private ResourceToolkitLock() { + this.lock = LockFactory.createRecursiveLock(); } + @Override public final void lock() { - if(TRACE_LOCK) { System.err.println("X11ToolkitLock.lock() - native: "+(null==lock)); } - if(null == lock) { - X11Lib.XLockDisplay(displayHandle); - } else { - lock.lock(); - } + lock.lock(); + if(TRACE_LOCK) { System.err.println("ResourceToolkitLock.lock()"); } } + @Override public final void unlock() { - if(TRACE_LOCK) { System.err.println("X11ToolkitLock.unlock() - native: "+(null==lock)); } - if(null == lock) { - X11Lib.XUnlockDisplay(displayHandle); - } else { - lock.unlock(); - } + if(TRACE_LOCK) { System.err.println("ResourceToolkitLock.unlock()"); } + lock.unlock(); // implicit lock validation + } + + @Override + public final void validateLocked() throws RuntimeException { + lock.validateLocked(); + } + + @Override + public final void dispose() { + // nop + } + + @Override + public String toString() { + return "ResourceToolkitLock[obj 0x"+Integer.toHexString(hashCode())+", isOwner "+lock.isOwner(Thread.currentThread())+", "+lock.toString()+"]"; } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java new file mode 100644 index 000000000..7b74e1f1f --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java @@ -0,0 +1,149 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.nativewindow; + +import java.util.Iterator; + +import javax.media.nativewindow.ToolkitLock; + +import com.jogamp.common.util.LongObjectHashMap; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; + +/** + * Implementing a shared resource based recursive {@link javax.media.nativewindow.ToolkitLock}. + * <p> + * A resource handle maybe used within many objects + * and can be synchronized across threads via an unique instance of SharedResourceToolkitLock. + * </p> + * <p> + * Implementation holds a synchronized map from handle to reference counted {@link SharedResourceToolkitLock}. + * New elements are added via {@link #get(long)} if new + * and removed via {@link #dispose()} if no more referenced. + * </p> + */ +public class SharedResourceToolkitLock implements ToolkitLock { + private static final LongObjectHashMap handle2Lock; + static { + handle2Lock = new LongObjectHashMap(); + handle2Lock.setKeyNotFoundValue(null); + } + + /** + * @return number of unclosed EGL Displays.<br> + */ + public static int shutdown(boolean verbose) { + if(DEBUG || verbose || handle2Lock.size() > 0 ) { + System.err.println("SharedResourceToolkitLock: Shutdown (open: "+handle2Lock.size()+")"); + if(DEBUG) { + Thread.dumpStack(); + } + if( handle2Lock.size() > 0) { + dumpOpenDisplayConnections(); + } + } + return handle2Lock.size(); + } + + public static void dumpOpenDisplayConnections() { + System.err.println("SharedResourceToolkitLock: Open ResourceToolkitLock's: "+handle2Lock.size()); + int i=0; + for(Iterator<LongObjectHashMap.Entry> iter = handle2Lock.iterator(); iter.hasNext(); i++) { + final LongObjectHashMap.Entry e = iter.next(); + System.err.println("SharedResourceToolkitLock: Open["+i+"]: "+e.value); + } + } + + public static final SharedResourceToolkitLock get(long handle) { + SharedResourceToolkitLock res; + synchronized(handle2Lock) { + res = (SharedResourceToolkitLock) handle2Lock.get(handle); + if( null == res ) { + res = new SharedResourceToolkitLock(handle); + res.refCount++; + handle2Lock.put(handle, res); + if(DEBUG || TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.get() * NEW *: "+res); } + } else { + res.refCount++; + if(DEBUG || TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.get() * EXIST *: "+res); } + } + } + return res; + } + + private final RecursiveLock lock; + private final long handle; + private volatile int refCount; + + private SharedResourceToolkitLock(long handle) { + this.lock = LockFactory.createRecursiveLock(); + this.handle = handle; + this.refCount = 0; + } + + + @Override + public final void lock() { + lock.lock(); + if(TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.lock()"); } + } + + @Override + public final void unlock() { + if(TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.unlock()"); } + lock.unlock(); + } + + @Override + public final void validateLocked() throws RuntimeException { + lock.validateLocked(); + } + + @Override + public final void dispose() { + if(0 < refCount) { // volatile OK + synchronized(handle2Lock) { + refCount--; + if(0 == refCount) { + if(DEBUG || TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.dispose() * REMOV *: "+this); } + handle2Lock.remove(handle); + } else { + if(DEBUG || TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.dispose() * DOWN *: "+this); } + } + } + } else { + if(DEBUG || TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.dispose() * NULL *: "+this); } + } + } + + @Override + public String toString() { + return "SharedResourceToolkitLock[refCount "+refCount+", handle 0x"+Long.toHexString(handle)+", obj 0x"+Integer.toHexString(hashCode())+", isOwner "+lock.isOwner(Thread.currentThread())+", "+lock.toString()+"]"; + } +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/SurfaceUpdatedHelper.java b/src/nativewindow/classes/jogamp/nativewindow/SurfaceUpdatedHelper.java index 4877d5c4f..0b033955e 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/SurfaceUpdatedHelper.java +++ b/src/nativewindow/classes/jogamp/nativewindow/SurfaceUpdatedHelper.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions 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. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -34,50 +34,58 @@ import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.SurfaceUpdatedListener; public class SurfaceUpdatedHelper implements SurfaceUpdatedListener { - private Object surfaceUpdatedListenersLock = new Object(); - private ArrayList<SurfaceUpdatedListener> surfaceUpdatedListeners = new ArrayList<SurfaceUpdatedListener>(); + private final Object surfaceUpdatedListenersLock = new Object(); + private final ArrayList<SurfaceUpdatedListener> surfaceUpdatedListeners = new ArrayList<SurfaceUpdatedListener>(); + private volatile boolean isEmpty = true; // // Management Utils - // - public int size() { return surfaceUpdatedListeners.size(); } - public SurfaceUpdatedListener get(int i) { return surfaceUpdatedListeners.get(i); } - + // + public final int size() { return surfaceUpdatedListeners.size(); } + public final SurfaceUpdatedListener get(int i) { return surfaceUpdatedListeners.get(i); } + // // Implementation of NativeSurface SurfaceUpdatedListener methods - // - - public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { + // + + public final void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { addSurfaceUpdatedListener(-1, l); } - public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) + public final void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { if(l == null) { return; } synchronized(surfaceUpdatedListenersLock) { - if(0>index) { - index = surfaceUpdatedListeners.size(); + if(0>index) { + index = surfaceUpdatedListeners.size(); } surfaceUpdatedListeners.add(index, l); + isEmpty = false; } } - public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { + public final boolean removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { if (l == null) { - return; + return false; } synchronized(surfaceUpdatedListenersLock) { - surfaceUpdatedListeners.remove(l); + final boolean res = surfaceUpdatedListeners.remove(l); + isEmpty = 0 == surfaceUpdatedListeners.size(); + return res; } } - public void surfaceUpdated(Object updater, NativeSurface ns, long when) { + @Override + public final void surfaceUpdated(Object updater, NativeSurface ns, long when) { + if( isEmpty ) { + return; + } synchronized(surfaceUpdatedListenersLock) { for(int i = 0; i < surfaceUpdatedListeners.size(); i++ ) { - SurfaceUpdatedListener l = surfaceUpdatedListeners.get(i); + final SurfaceUpdatedListener l = surfaceUpdatedListeners.get(i); l.surfaceUpdated(updater, ns, when); } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/ToolkitProperties.java b/src/nativewindow/classes/jogamp/nativewindow/ToolkitProperties.java new file mode 100644 index 000000000..47b3e63fa --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/ToolkitProperties.java @@ -0,0 +1,47 @@ +package jogamp.nativewindow; + +import javax.media.nativewindow.NativeWindowFactory; + +/** + * Marker interface. + * <p> + * Implementation requires to provide static methods: + * <pre> + public static void initSingleton() {} + + public static void shutdown() {} + + public static boolean requiresToolkitLock() {} + + public static boolean hasThreadingIssues() {} + * </pre> + * Above static methods are invoked by {@link NativeWindowFactory#initSingleton()}, + * or {@link NativeWindowFactory#shutdown()} via reflection. + * </p> + */ +public interface ToolkitProperties { + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + */ + // void initSingleton(); + + /** + * Cleanup resources. + * <p> + * Called by {@link NativeWindowFactory#shutdown()} + * </p> + */ + // void shutdown(); + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + */ + // boolean requiresToolkitLock(); + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + */ + // boolean hasThreadingIssues(); + +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java b/src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java new file mode 100644 index 000000000..f622db8cc --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java @@ -0,0 +1,100 @@ +/** + * Copyright 2010 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.nativewindow; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; + +/** + * Generic Surface implementation which wraps an existing window handle. + * + * @see ProxySurface + */ +public class WrappedSurface extends ProxySurfaceImpl { + protected long surfaceHandle; + + /** + * Utilizes a {@link UpstreamSurfaceHook.MutableSize} to hold the size information, + * which is being passed to the {@link ProxySurface} instance. + * + * @param cfg the {@link AbstractGraphicsConfiguration} to be used + * @param handle the wrapped pre-existing native surface handle, maybe 0 if not yet determined + * @param initialWidth + * @param initialHeight + * @param ownsDevice <code>true</code> if this {@link ProxySurface} instance + * owns the {@link AbstractGraphicsConfiguration}'s {@link AbstractGraphicsDevice}, + * otherwise <code>false</code>. Owning the device implies closing it at {@link #destroyNotify()}. + */ + public WrappedSurface(AbstractGraphicsConfiguration cfg, long handle, int initialWidth, int initialHeight, boolean ownsDevice) { + super(cfg, new UpstreamSurfaceHookMutableSize(initialWidth, initialHeight), ownsDevice); + surfaceHandle=handle; + } + + /** + * @param cfg the {@link AbstractGraphicsConfiguration} to be used + * @param handle the wrapped pre-existing native surface handle, maybe 0 if not yet determined + * @param upstream the {@link UpstreamSurfaceHook} to be used + * @param ownsDevice <code>true</code> if this {@link ProxySurface} instance + * owns the {@link AbstractGraphicsConfiguration}'s {@link AbstractGraphicsDevice}, + * otherwise <code>false</code>. + */ + public WrappedSurface(AbstractGraphicsConfiguration cfg, long handle, UpstreamSurfaceHook upstream, boolean ownsDevice) { + super(cfg, upstream, ownsDevice); + surfaceHandle=handle; + } + + @Override + protected void invalidateImpl() { + surfaceHandle = 0; + } + + @Override + public final long getSurfaceHandle() { + return surfaceHandle; + } + + @Override + public final void setSurfaceHandle(long surfaceHandle) { + this.surfaceHandle=surfaceHandle; + } + + @Override + protected final int lockSurfaceImpl() { + return LOCK_SUCCESS; + } + + @Override + protected final void unlockSurfaceImpl() { + } + +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/WrappedWindow.java b/src/nativewindow/classes/jogamp/nativewindow/WrappedWindow.java new file mode 100644 index 000000000..edb65eb06 --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/WrappedWindow.java @@ -0,0 +1,98 @@ +package jogamp.nativewindow; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.nativewindow.util.Point; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSizePos; + +public class WrappedWindow extends WrappedSurface implements NativeWindow { + private final InsetsImmutable insets = new Insets(0, 0, 0, 0); + private long windowHandle; + + /** + * Utilizes a {@link UpstreamSurfaceHookMutableSizePos} to hold the size and postion information, + * which is being passed to the {@link ProxySurface} instance. + * + * @param cfg the {@link AbstractGraphicsConfiguration} to be used + * @param surfaceHandle the wrapped pre-existing native surface handle, maybe 0 if not yet determined + * @param initialX + * @param initialY + * @param initialWidth + * @param initialHeight + * @param ownsDevice <code>true</code> if this {@link ProxySurface} instance + * owns the {@link AbstractGraphicsConfiguration}'s {@link AbstractGraphicsDevice}, + * otherwise <code>false</code>. Owning the device implies closing it at {@link #destroyNotify()}. + */ + public WrappedWindow(AbstractGraphicsConfiguration cfg, long surfaceHandle, int initialX, int initialY, int initialWidth, int initialHeight, boolean ownsDevice, long windowHandle) { + this(cfg, surfaceHandle, new UpstreamSurfaceHookMutableSizePos(initialX, initialY, initialWidth, initialHeight), ownsDevice, windowHandle); + } + + /** + * @param cfg the {@link AbstractGraphicsConfiguration} to be used + * @param surfaceHandle the wrapped pre-existing native surface handle, maybe 0 if not yet determined + * @param upstream the {@link UpstreamSurfaceHook} to be used + * @param ownsDevice <code>true</code> if this {@link ProxySurface} instance + * owns the {@link AbstractGraphicsConfiguration}'s {@link AbstractGraphicsDevice}, + * otherwise <code>false</code>. + */ + public WrappedWindow(AbstractGraphicsConfiguration cfg, long surfaceHandle, UpstreamSurfaceHookMutableSizePos upstream, boolean ownsDevice, long windowHandle) { + super(cfg, surfaceHandle, upstream, ownsDevice); + this.windowHandle = windowHandle; + } + + @Override + protected void invalidateImpl() { + super.invalidateImpl(); + windowHandle = 0; + } + + @Override + public void destroy() { + destroyNotify(); + } + + @Override + public NativeWindow getParent() { + return null; + } + + @Override + public long getWindowHandle() { + return windowHandle; + } + + @Override + public InsetsImmutable getInsets() { + return insets; + } + + @Override + public int getX() { + return ((UpstreamSurfaceHookMutableSizePos)getUpstreamSurfaceHook()).getX(); + } + + @Override + public int getY() { + return ((UpstreamSurfaceHookMutableSizePos)getUpstreamSurfaceHook()).getY(); + } + + @Override + public Point getLocationOnScreen(Point point) { + if(null!=point) { + return point; + } else { + return new Point(0, 0); + } + } + + @Override + public boolean hasFocus() { + return false; + } +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java b/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java index d77cd75ef..069cffeb8 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java +++ b/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java @@ -27,15 +27,27 @@ */ package jogamp.nativewindow.awt; +import java.awt.Cursor; +import java.awt.FocusTraversalPolicy; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Toolkit; import java.awt.Window; import java.awt.Component; import java.awt.Container; import java.awt.Frame; +import java.awt.image.BufferedImage; +import java.util.HashMap; + +import javax.swing.JComponent; import javax.swing.JFrame; +import javax.swing.JRootPane; import javax.swing.WindowConstants; - import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelFormatUtil; import javax.swing.MenuSelectionManager; public class AWTMisc { @@ -69,12 +81,145 @@ public class AWTMisc { } /** + * Return insets of the component w/o traversing up to parent, + * i.e. trying Window and JComponent. + * <p> + * Exception is JRootPane. + * Return it's parent's Window component's insets if available, + * otherwise return JRootPane's insets.<br> + * This is due to <i>experience</i> that <i>some</i> JRootPane's + * do not expose valid insets value. + * </p> + * @param topLevelOnly if true only returns insets of top-level components, i.e. Window and JRootPanel, + * otherwise for JComponent as well. + */ + public static Insets getInsets(Component c, boolean topLevelOnly) { + if( c instanceof Window ) { + return ((Window)c).getInsets(); + } + if( c instanceof JRootPane ) { + final Window w = getWindow(c); + if( null != w ) { + return w.getInsets(); + } + return ((JRootPane)c).getInsets(); + } + if( !topLevelOnly && c instanceof JComponent ) { + return ((JComponent)c).getInsets(); + } + return null; + } + + public static interface ComponentAction { + /** + * @param c the component to perform the action on + */ + public void run(Component c); + } + + public static int performAction(Container c, Class<?> cType, ComponentAction action) { + int count = 0; + final int cc = c.getComponentCount(); + for(int i=0; i<cc; i++) { + final Component e = c.getComponent(i); + if( e instanceof Container ) { + count += performAction((Container)e, cType, action); + } else if( cType.isInstance(e) ) { + action.run(e); + count++; + } + } + // we come at last .. + if( cType.isInstance(c) ) { + action.run(c); + count++; + } + return count; + } + + /** + * Traverse to the next forward or backward component using the + * container's FocusTraversalPolicy. + * + * @param comp the assumed current focuse component + * @param forward if true, returns the next focus component, otherwise the previous one. + * @return + */ + public static Component getNextFocus(Component comp, boolean forward) { + Container focusContainer = comp.getFocusCycleRootAncestor(); + while ( focusContainer != null && + ( !focusContainer.isShowing() || !focusContainer.isFocusable() || !focusContainer.isEnabled() ) ) + { + comp = focusContainer; + focusContainer = comp.getFocusCycleRootAncestor(); + } + Component next = null; + if (focusContainer != null) { + final FocusTraversalPolicy policy = focusContainer.getFocusTraversalPolicy(); + next = forward ? policy.getComponentAfter(focusContainer, comp) : policy.getComponentBefore(focusContainer, comp); + if (next == null) { + next = policy.getDefaultComponent(focusContainer); + } + } + return next; + } + + /** * Issue this when your non AWT toolkit gains focus to clear AWT menu path */ public static void clearAWTMenus() { MenuSelectionManager.defaultManager().clearSelectedPath(); } + static final HashMap<Integer, Cursor> cursorMap = new HashMap<Integer, Cursor>(); + static final Cursor nulCursor; + static { + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + final BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR); + nulCursor = toolkit.createCustomCursor(img, new Point(0,0), "nullCursor"); + } + + public static synchronized Cursor getNullCursor() { return nulCursor; } + + public static synchronized Cursor getCursor(PixelRectangle pixelrect, Point hotSpot) { + // 31 * x == (x << 5) - x + int hash = 31 + pixelrect.hashCode(); + hash = ((hash << 5) - hash) + hotSpot.hashCode(); + final Integer key = Integer.valueOf(hash); + + Cursor cursor = cursorMap.get(key); + if( null == cursor ) { + cursor = createCursor(pixelrect, hotSpot); + cursorMap.put(key, cursor); + } + return cursor; + } + private static synchronized Cursor createCursor(PixelRectangle pixelrect, Point hotSpot) { + final int width = pixelrect.getSize().getWidth(); + final int height = pixelrect.getSize().getHeight(); + final BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); // PixelFormat.BGRA8888 + final PixelFormatUtil.PixelSink32 imgSink = new PixelFormatUtil.PixelSink32() { + public void store(int x, int y, int pixel) { + img.setRGB(x, y, pixel); + } + @Override + public final PixelFormat getPixelformat() { + return PixelFormat.BGRA8888; + } + @Override + public int getStride() { + return width*4; + } + @Override + public final boolean isGLOriented() { + return false; + } + }; + PixelFormatUtil.convert32(imgSink, pixelrect); + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + return toolkit.createCustomCursor(img, hotSpot, pixelrect.toString()); + } + public static WindowClosingProtocol.WindowClosingMode AWT2NWClosingOperation(int awtClosingOperation) { switch (awtClosingOperation) { case WindowConstants.DISPOSE_ON_CLOSE: diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTJNILibLoader.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTJNILibLoader.java index 2377c2be6..a5da41424 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTJNILibLoader.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTJNILibLoader.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. 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 @@ -28,11 +28,11 @@ * 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. */ @@ -47,20 +47,21 @@ import java.awt.Toolkit; import java.security.AccessController; import java.security.PrivilegedAction; -public class JAWTJNILibLoader extends NWJNILibLoader { +public class JAWTJNILibLoader extends NWJNILibLoader { static { AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { // Make sure that awt.dll is loaded before loading jawt.dll. Otherwise // a Dialog with "awt.dll not found" might pop up. // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4481947. Toolkit.getDefaultToolkit(); - + // Must pre-load JAWT on all non-Mac platforms to // ensure references from jogl_awt shared object // will succeed since JAWT shared object isn't in // default library path - if ( ! NativeWindowFactory.TYPE_MACOSX.equals( NativeWindowFactory.getNativeWindowType(false) ) ) { + if ( NativeWindowFactory.TYPE_MACOSX != NativeWindowFactory.getNativeWindowType(false) ) { try { loadLibrary("jawt", null, true, JAWTJNILibLoader.class.getClassLoader()); } catch (Throwable t) { @@ -74,9 +75,9 @@ public class JAWTJNILibLoader extends NWJNILibLoader { } }); } - + public static void initSingleton() { - // just exist to ensure static init has been run + // just exist to ensure static init has been run } - + } diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java index 8098bdb1b..5a1d915ce 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -29,7 +29,7 @@ * 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. @@ -48,25 +48,28 @@ import java.util.ArrayList; import java.util.Map; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.ToolkitLock; import jogamp.nativewindow.Debug; import com.jogamp.common.os.Platform; import com.jogamp.common.util.VersionNumber; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; public class JAWTUtil { public static final boolean DEBUG = Debug.debug("JAWT"); /** OSX JAWT version option to use CALayer */ public static final int JAWT_MACOSX_USE_CALAYER = 0x80000000; - + /** OSX JAWT CALayer availability on Mac OS X >= 10.6 Update 4 (recommended) */ public static final VersionNumber JAWT_MacOSXCALayerMinVersion = new VersionNumber(10,6,4); - + /** OSX JAWT CALayer required with Java >= 1.7.0 (implies OS X >= 10.7 */ - public static final VersionNumber JAWT_MacOSXCALayerRequiredForJavaVersion = new VersionNumber(1,7,0); - + public static final VersionNumber JAWT_MacOSXCALayerRequiredForJavaVersion = Platform.Version17; + // See whether we're running in headless mode private static final boolean headlessMode; private static final JAWT jawtLockObject; @@ -79,52 +82,170 @@ public class JAWTUtil { private static final Method sunToolkitAWTUnlockMethod; private static final boolean hasSunToolkitAWTLock; + private static final RecursiveLock jawtLock; private static final ToolkitLock jawtToolkitLock; private static class PrivilegedDataBlob1 { PrivilegedDataBlob1() { ok = false; - } + } Method sunToolkitAWTLockMethod; Method sunToolkitAWTUnlockMethod; boolean ok; } - + /** * Returns true if this platform's JAWT implementation supports offscreen layer. */ public static boolean isOffscreenLayerSupported() { - return Platform.OS_TYPE == Platform.OSType.MACOS && - Platform.OS_VERSION_NUMBER.compareTo(JAWTUtil.JAWT_MacOSXCALayerMinVersion) >= 0; + return Platform.OS_TYPE == Platform.OSType.MACOS && + Platform.OS_VERSION_NUMBER.compareTo(JAWTUtil.JAWT_MacOSXCALayerMinVersion) >= 0; } - + /** * Returns true if this platform's JAWT implementation requires using offscreen layer. */ public static boolean isOffscreenLayerRequired() { - return Platform.OS_TYPE == Platform.OSType.MACOS && - Platform.JAVA_VERSION_NUMBER.compareTo(JAWT_MacOSXCALayerRequiredForJavaVersion)>=0; + return Platform.OS_TYPE == Platform.OSType.MACOS && + Platform.JAVA_VERSION_NUMBER.compareTo(JAWT_MacOSXCALayerRequiredForJavaVersion)>=0; + } + + /** + * CALayer size needs to be set using the AWT component size. + * <p> + * AWT's super-calayer, i.e. the AWT's own component CALayer, + * does not layout our root-calayer in respect to this component's + * position and size, at least when resizing programmatically. + * </p> + * <p> + * As of today, this flag is enabled for all known AWT versions. + * </p> + * <p> + * Sync w/ NativeWindowProtocols.h + * </p> + */ + public static final int JAWT_OSX_CALAYER_QUIRK_SIZE = 1 << 0; + + /** + * CALayer position needs to be set to zero. + * <p> + * AWT's super-calayer, i.e. the AWT's own component CALayer, + * has a broken layout and needs it's sub-layers to be located at position 0/0. + * </p> + * <p> + * See <code>http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7172187</code>. + * </p> + * <p> + * Further more a re-layout seems to be required in this case, + * i.e. a programmatic forced resize +1 and it's inverted resize -1. + * </p> + * <p> + * This flag is enabled w/ AWT < 1.7.0_40. + * </p> + * <p> + * Sync w/ NativeWindowProtocols.h + * </p> + */ + public static final int JAWT_OSX_CALAYER_QUIRK_POSITION = 1 << 1; + + /** + * CALayer position needs to be derived from AWT position + * in relation to super CALayer. + * <p> + * AWT's super-calayer, i.e. the AWT top-container's CALayer, + * does not layout our root-calayer in respect to this component's + * position and size, at least when resizing programmatically. + * </p> + * <p> + * CALayer position has origin 0/0 at bottom/left, + * where AWT component has origin 0/0 at top/left. + * </p> + * <p> + * The super-calayer bounds exclude the frame's heavyweight border/insets. + * </p> + * <p> + * The super-calayer lies within the AWT top-container client space (content). + * </p> + * <p> + * Component's location in super-calayer: + * <pre> + p0 = c.locationOnScreen(); + p0 -= c.getOutterComp.getPos(); + p0 -= c.getOutterComp.getInsets(); + * </pre> + * Where 'locationOnScreen()' is: + * <pre> + p0 = 0/0; + while( null != c ) { + p0 += c.getPos(); + } + * </pre> + * </p> + * <p> + * This flags also sets {@link #JAWT_OSX_CALAYER_QUIRK_SIZE}, + * i.e. they are related. + * </p> + * <p> + * As of today, this flag is enabled for w/ AWT >= 1.7.0_40. + * </p> + * <p> + * Sync w/ NativeWindowProtocols.h + * </p> + */ + public static final int JAWT_OSX_CALAYER_QUIRK_LAYOUT = 1 << 2; + + /** + * Returns bitfield of required JAWT OSX CALayer quirks to mediate AWT impl. bugs. + * <p> + * Returns zero, if platform is not {@link Platform.OSType#MACOS} + * or not supporting CALayer, i.e. OSX < 10.6.4. + * </p> + * <p> + * Otherwise includes + * <ul> + * <li>{@link #JAWT_OSX_CALAYER_QUIRK_SIZE} (always)</li> + * <li>{@link #JAWT_OSX_CALAYER_QUIRK_POSITION} if JVM < 1.7.0_40</li> + * <li>{@link #JAWT_OSX_CALAYER_QUIRK_LAYOUT} if JVM >= 1.7.0_40</li> + * </ul> + * </p> + */ + public static int getOSXCALayerQuirks() { + int res = 0; + if( Platform.OS_TYPE == Platform.OSType.MACOS && + Platform.OS_VERSION_NUMBER.compareTo(JAWTUtil.JAWT_MacOSXCALayerMinVersion) >= 0 ) { + + /** Knowing impl. all expose the SIZE bug */ + res |= JAWT_OSX_CALAYER_QUIRK_SIZE; + + final int c = Platform.JAVA_VERSION_NUMBER.compareTo(Platform.Version17); + if( c < 0 || c == 0 && Platform.JAVA_VERSION_UPDATE < 40 ) { + res |= JAWT_OSX_CALAYER_QUIRK_POSITION; + } else { + res |= JAWT_OSX_CALAYER_QUIRK_LAYOUT; + } + } + return res; } - + /** * @param useOffscreenLayerIfAvailable * @return */ public static JAWT getJAWT(boolean useOffscreenLayerIfAvailable) { - final int jawt_version_flags = JAWTFactory.JAWT_VERSION_1_4; + final int jawt_version_flags = JAWTFactory.JAWT_VERSION_1_4; JAWT jawt = JAWT.create(); - + // default queries - boolean tryOffscreenLayer = false; - boolean tryOnscreenLayer = true; + boolean tryOffscreenLayer; + boolean tryOnscreen; int jawt_version_flags_offscreen = jawt_version_flags; - + if(isOffscreenLayerRequired()) { if(Platform.OS_TYPE == Platform.OSType.MACOS) { if(Platform.OS_VERSION_NUMBER.compareTo(JAWTUtil.JAWT_MacOSXCALayerMinVersion) >= 0) { jawt_version_flags_offscreen |= JAWTUtil.JAWT_MACOSX_USE_CALAYER; tryOffscreenLayer = true; - tryOnscreenLayer = false; + tryOnscreen = false; } else { throw new RuntimeException("OSX: Invalid version of Java ("+Platform.JAVA_VERSION_NUMBER+") / OS X ("+Platform.OS_VERSION_NUMBER+")"); } @@ -135,11 +256,18 @@ public class JAWTUtil { if(Platform.OS_TYPE == Platform.OSType.MACOS) { jawt_version_flags_offscreen |= JAWTUtil.JAWT_MACOSX_USE_CALAYER; tryOffscreenLayer = true; + tryOnscreen = true; } else { throw new InternalError("offscreen requested and supported, but n/a for: "+Platform.OS_TYPE); } + } else { + tryOffscreenLayer = false; + tryOnscreen = true; + } + if(DEBUG) { + System.err.println("JAWTUtil.getJAWT(tryOffscreenLayer "+tryOffscreenLayer+", tryOnscreen "+tryOnscreen+")"); } - + StringBuilder errsb = new StringBuilder(); if(tryOffscreenLayer) { errsb.append("Offscreen 0x").append(Integer.toHexString(jawt_version_flags_offscreen)); @@ -147,22 +275,22 @@ public class JAWTUtil { return jawt; } } - if(tryOnscreenLayer) { + if(tryOnscreen) { if(tryOffscreenLayer) { errsb.append(", "); } errsb.append("Onscreen 0x").append(Integer.toHexString(jawt_version_flags)); if( JAWT.getJAWT(jawt, jawt_version_flags) ) { return jawt; - } + } } throw new RuntimeException("Unable to initialize JAWT, trials: "+errsb.toString()); } - + public static boolean isJAWTUsingOffscreenLayer(JAWT jawt) { return 0 != ( jawt.getCachedVersion() & JAWTUtil.JAWT_MACOSX_USE_CALAYER ); } - + static { if(DEBUG) { System.err.println("JAWTUtil initialization (JAWT/JNI/..."); @@ -191,10 +319,11 @@ public class JAWTUtil { isQueueFlusherThread = m; j2dExist = ok; - PrivilegedDataBlob1 pdb1 = (PrivilegedDataBlob1) AccessController.doPrivileged(new PrivilegedAction<Object>() { + PrivilegedDataBlob1 pdb1 = (PrivilegedDataBlob1) AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { PrivilegedDataBlob1 d = new PrivilegedDataBlob1(); - try { + try { final Class<?> sunToolkitClass = Class.forName("sun.awt.SunToolkit"); d.sunToolkitAWTLockMethod = sunToolkitClass.getDeclaredMethod("awtLock", new Class[]{}); d.sunToolkitAWTLockMethod.setAccessible(true); @@ -209,7 +338,7 @@ public class JAWTUtil { }); sunToolkitAWTLockMethod = pdb1.sunToolkitAWTLockMethod; sunToolkitAWTUnlockMethod = pdb1.sunToolkitAWTUnlockMethod; - + boolean _hasSunToolkitAWTLock = false; if ( pdb1.ok ) { try { @@ -221,15 +350,30 @@ public class JAWTUtil { } hasSunToolkitAWTLock = _hasSunToolkitAWTLock; // hasSunToolkitAWTLock = false; + jawtLock = LockFactory.createRecursiveLock(); jawtToolkitLock = new ToolkitLock() { + @Override public final void lock() { JAWTUtil.lockToolkit(); - } + } + @Override public final void unlock() { JAWTUtil.unlockToolkit(); } - }; + @Override + public final void validateLocked() throws RuntimeException { + JAWTUtil.validateLocked(); + } + @Override + public final void dispose() { + // nop + } + @Override + public String toString() { + return "JAWTToolkitLock[obj 0x"+Integer.toHexString(hashCode())+", isOwner "+jawtLock.isOwner(Thread.currentThread())+", "+jawtLock+"]"; + } + }; // trigger native AWT toolkit / properties initialization Map<?,?> desktophints = null; @@ -239,6 +383,7 @@ public class JAWTUtil { } else { final ArrayList<Map<?,?>> desktophintsBucket = new ArrayList<Map<?,?>>(1); EventQueue.invokeAndWait(new Runnable() { + @Override public void run() { Map<?,?> _desktophints = (Map<?,?>)(Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints")); if(null!=_desktophints) { @@ -260,13 +405,22 @@ public class JAWTUtil { System.err.println("JAWTUtil: Is headless " + headlessMode); int hints = ( null != desktophints ) ? desktophints.size() : 0 ; System.err.println("JAWTUtil: AWT Desktop hints " + hints); + System.err.println("JAWTUtil: OffscreenLayer Supported: "+isOffscreenLayerSupported()+" - Required "+isOffscreenLayerRequired()); } } + /** + * Called by {@link NativeWindowFactory#initSingleton()} + */ public static void initSingleton() { // just exist to ensure static init has been run } + /** + * Called by {@link NativeWindowFactory#shutdown()} + */ + public static void shutdown() { + } public static boolean hasJava2D() { return j2dExist; @@ -287,58 +441,69 @@ public class JAWTUtil { } /** - * Locks the AWT's global ReentrantLock.<br> - * + * Locks the AWT's global ReentrantLock. + * <p> * JAWT's native Lock() function calls SunToolkit.awtLock(), - * which just uses AWT's global ReentrantLock.<br> + * which just uses AWT's global ReentrantLock. + * </p> + * <p> + * AWT locking is wrapped through a recursive lock object. + * </p> */ - private static void awtLock() { - if(hasSunToolkitAWTLock) { - try { - sunToolkitAWTLockMethod.invoke(null, (Object[])null); - } catch (Exception e) { - throw new NativeWindowException("SunToolkit.awtLock failed", e); + public static void lockToolkit() throws NativeWindowException { + jawtLock.lock(); + if( 1 == jawtLock.getHoldCount() ) { + if(!headlessMode && !isJava2DQueueFlusherThread()) { + if(hasSunToolkitAWTLock) { + try { + sunToolkitAWTLockMethod.invoke(null, (Object[])null); + } catch (Exception e) { + throw new NativeWindowException("SunToolkit.awtLock failed", e); + } + } else { + jawtLockObject.Lock(); + } } - } else { - jawtLockObject.Lock(); } + if(ToolkitLock.TRACE_LOCK) { System.err.println("JAWTUtil-ToolkitLock.lock(): "+jawtLock); } } /** - * Unlocks the AWT's global ReentrantLock.<br> - * + * Unlocks the AWT's global ReentrantLock. + * <p> * JAWT's native Unlock() function calls SunToolkit.awtUnlock(), - * which just uses AWT's global ReentrantLock.<br> + * which just uses AWT's global ReentrantLock. + * </p> + * <p> + * AWT unlocking is wrapped through a recursive lock object. + * </p> */ - private static void awtUnlock() { - if(hasSunToolkitAWTLock) { - try { - sunToolkitAWTUnlockMethod.invoke(null, (Object[])null); - } catch (Exception e) { - throw new NativeWindowException("SunToolkit.awtUnlock failed", e); + public static void unlockToolkit() { + jawtLock.validateLocked(); + if(ToolkitLock.TRACE_LOCK) { System.err.println("JAWTUtil-ToolkitLock.unlock(): "+jawtLock); } + if( 1 == jawtLock.getHoldCount() ) { + if(!headlessMode && !isJava2DQueueFlusherThread()) { + if(hasSunToolkitAWTLock) { + try { + sunToolkitAWTUnlockMethod.invoke(null, (Object[])null); + } catch (Exception e) { + throw new NativeWindowException("SunToolkit.awtUnlock failed", e); + } + } else { + jawtLockObject.Unlock(); + } } - } else { - jawtLockObject.Unlock(); - } - } - - public static void lockToolkit() throws NativeWindowException { - if(ToolkitLock.TRACE_LOCK) { System.err.println("JAWTUtil-ToolkitLock.lock()"); } - if(!headlessMode && !isJava2DQueueFlusherThread()) { - awtLock(); } + jawtLock.unlock(); } - public static void unlockToolkit() { - if(ToolkitLock.TRACE_LOCK) { System.err.println("JAWTUtil-ToolkitLock.unlock()"); } - if(!headlessMode && !isJava2DQueueFlusherThread()) { - awtUnlock(); - } + public static final void validateLocked() throws RuntimeException { + jawtLock.validateLocked(); } public static ToolkitLock getJAWTToolkitLock() { return jawtToolkitLock; } - + } diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWT_PlatformInfo.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWT_PlatformInfo.java index 40d7b8032..4f12d1925 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWT_PlatformInfo.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWT_PlatformInfo.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. 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 @@ -28,11 +28,11 @@ * 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. */ diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java index 0ca5cd297..8d46d805a 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -29,17 +29,18 @@ * 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 jogamp.nativewindow.jawt.macosx; +import java.awt.Component; import java.nio.Buffer; import java.security.AccessController; import java.security.PrivilegedAction; @@ -48,12 +49,13 @@ import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.Capabilities; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.SurfaceChangeable; +import javax.media.nativewindow.MutableSurface; import javax.media.nativewindow.util.Point; -import com.jogamp.nativewindow.MutableGraphicsConfiguration; import com.jogamp.nativewindow.awt.JAWTWindow; +import jogamp.nativewindow.Debug; +import jogamp.nativewindow.awt.AWTMisc; import jogamp.nativewindow.jawt.JAWT; import jogamp.nativewindow.jawt.JAWTFactory; import jogamp.nativewindow.jawt.JAWTUtil; @@ -62,7 +64,15 @@ import jogamp.nativewindow.jawt.JAWT_DrawingSurfaceInfo; import jogamp.nativewindow.jawt.macosx.JAWT_MacOSXDrawingSurfaceInfo; import jogamp.nativewindow.macosx.OSXUtil; -public class MacOSXJAWTWindow extends JAWTWindow implements SurfaceChangeable { +public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { + /** May lead to deadlock, due to AWT pos comparison .. don't enable for Applets! */ + private static final boolean DEBUG_CALAYER_POS_CRITICAL; + + static { + Debug.initSingleton(); + DEBUG_CALAYER_POS_CRITICAL = Debug.isPropertyDefined("nativewindow.debug.JAWT.OSXCALayerPos", true /* jnlpAlias */); + } + public MacOSXJAWTWindow(Object comp, AbstractGraphicsConfiguration config) { super(comp, config); if(DEBUG) { @@ -70,70 +80,152 @@ public class MacOSXJAWTWindow extends JAWTWindow implements SurfaceChangeable { } } + @Override protected void invalidateNative() { - surfaceHandle=0; - if(isOffscreenLayerSurfaceEnabled()) { - if(0 != rootSurfaceLayerHandle) { - OSXUtil.DestroyCALayer(rootSurfaceLayerHandle); - rootSurfaceLayerHandle = 0; - } - if(0 != drawable) { - OSXUtil.DestroyNSWindow(drawable); - drawable = 0; + if(DEBUG) { + System.err.println("MacOSXJAWTWindow.invalidateNative(): osh-enabled "+isOffscreenLayerSurfaceEnabled()+ + ", osd-set "+offscreenSurfaceDrawableSet+ + ", osd "+toHexString(offscreenSurfaceDrawable)+ + ", osl "+toHexString(getAttachedSurfaceLayer())+ + ", rsl "+toHexString(rootSurfaceLayer)+ + ", wh "+toHexString(windowHandle)+" - "+Thread.currentThread().getName()); + } + offscreenSurfaceDrawable=0; + offscreenSurfaceDrawableSet=false; + if( isOffscreenLayerSurfaceEnabled() ) { + if(0 != windowHandle) { + OSXUtil.DestroyNSWindow(windowHandle); } + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + if( 0 != rootSurfaceLayer ) { + if( 0 != jawtSurfaceLayersHandle) { + UnsetJAWTRootSurfaceLayer0(jawtSurfaceLayersHandle, rootSurfaceLayer); + } + OSXUtil.DestroyCALayer(rootSurfaceLayer); + rootSurfaceLayer = 0; + } + jawtSurfaceLayersHandle = 0; + } + }); } + windowHandle=0; } + @Override protected void attachSurfaceLayerImpl(final long layerHandle) { - OSXUtil.AddCASublayer(rootSurfaceLayerHandle, layerHandle); - } - - protected void detachSurfaceLayerImpl(final long layerHandle) { - OSXUtil.RemoveCASublayer(rootSurfaceLayerHandle, layerHandle); - } - - public long getSurfaceHandle() { - return isOffscreenLayerSurfaceEnabled() ? surfaceHandle : super.getSurfaceHandle() ; + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + // AWT position is top-left w/ insets, where CALayer position is bottom/left from root CALayer w/o insets. + // Determine p0: components location on screen w/o insets. + // CALayer position will be determined in native code. + // See detailed description in {@link JAWTUtil#JAWT_OSX_CALAYER_QUIRK_LAYOUT} + final Point p0 = new Point(); + final Component outterComp = getLocationOnScreenNonBlocking(p0, component); + final java.awt.Insets outterInsets = AWTMisc.getInsets(outterComp, true); + final Point p1 = (Point)p0.cloneMutable(); + p1.translate(-outterComp.getX(), -outterComp.getY()); + if( null != outterInsets ) { + p1.translate(-outterInsets.left, -outterInsets.top); + } + + if( DEBUG_CALAYER_POS_CRITICAL ) { + final java.awt.Point pA0 = component.getLocationOnScreen(); + final Point pA1 = new Point(pA0.x, pA0.y); + pA1.translate(-outterComp.getX(), -outterComp.getY()); + if( null != outterInsets ) { + pA1.translate(-outterInsets.left, -outterInsets.top); + } + System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], pA "+pA0+" -> "+pA1+ + ", p0 "+p0+" -> "+p1+", bounds "+bounds); + } else if( DEBUG ) { + System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds); + } + OSXUtil.AddCASublayer(rootSurfaceLayer, layerHandle, p1.getX(), p1.getY(), getWidth(), getHeight(), JAWTUtil.getOSXCALayerQuirks()); + } } ); } - - public void setSurfaceHandle(long surfaceHandle) { - if( !isOffscreenLayerSurfaceEnabled() ) { - throw new java.lang.UnsupportedOperationException("Not using CALAYER"); + + @Override + protected void layoutSurfaceLayerImpl(long layerHandle, boolean visible) { + final int caLayerQuirks = JAWTUtil.getOSXCALayerQuirks(); + // AWT position is top-left w/ insets, where CALayer position is bottom/left from root CALayer w/o insets. + // Determine p0: components location on screen w/o insets. + // CALayer position will be determined in native code. + // See detailed description in {@link JAWTUtil#JAWT_OSX_CALAYER_QUIRK_LAYOUT} + final Point p0 = new Point(); + final Component outterComp = getLocationOnScreenNonBlocking(p0, component); + final java.awt.Insets outterInsets = AWTMisc.getInsets(outterComp, true); + final Point p1 = (Point)p0.cloneMutable(); + p1.translate(-outterComp.getX(), -outterComp.getY()); + if( null != outterInsets ) { + p1.translate(-outterInsets.left, -outterInsets.top); } - if(DEBUG) { - System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); + + if( DEBUG_CALAYER_POS_CRITICAL ) { + final java.awt.Point pA0 = component.getLocationOnScreen(); + final Point pA1 = new Point(pA0.x, pA0.y); + pA1.translate(-outterComp.getX(), -outterComp.getY()); + if( null != outterInsets ) { + pA1.translate(-outterInsets.left, -outterInsets.top); + } + System.err.println("JAWTWindow.layoutSurfaceLayerImpl: "+toHexString(layerHandle) + ", quirks "+caLayerQuirks+", visible "+visible+ + ", [ins "+outterInsets+"], pA "+pA0+" -> "+pA1+ + ", p0 "+p0+" -> "+p1+", bounds "+bounds); + } else if( DEBUG ) { + System.err.println("JAWTWindow.layoutSurfaceLayerImpl: "+toHexString(layerHandle) + ", quirks "+caLayerQuirks+", visible "+visible+ + ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds); } - sscSet &= 0 != surfaceHandle; // reset ssc flag if NULL surfaceHandle, ie. back to JAWT - this.surfaceHandle = surfaceHandle; + OSXUtil.FixCALayerLayout(rootSurfaceLayer, layerHandle, visible, p1.getX(), p1.getY(), getWidth(), getHeight(), caLayerQuirks); } - public void surfaceSizeChanged(int width, int height) { - sscSet = true; - sscWidth = width; - sscHeight = height; + @Override + protected void detachSurfaceLayerImpl(final long layerHandle, final Runnable detachNotify) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + detachNotify.run(); + OSXUtil.RemoveCASublayer(rootSurfaceLayer, layerHandle); + } } ); } - public int getWidth() { - return sscSet ? sscWidth : super.getWidth(); + @Override + public final long getWindowHandle() { + return windowHandle; } - public int getHeight() { - return sscSet ? sscHeight: super.getHeight(); + @Override + public final long getSurfaceHandle() { + return offscreenSurfaceDrawableSet ? offscreenSurfaceDrawable : drawable /* super.getSurfaceHandle() */ ; + } + + @Override + public void setSurfaceHandle(long surfaceHandle) { + if( !isOffscreenLayerSurfaceEnabled() ) { + throw new java.lang.UnsupportedOperationException("Not using CALAYER"); + } + if(DEBUG) { + System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): "+toHexString(surfaceHandle)); + } + this.offscreenSurfaceDrawable = surfaceHandle; + this.offscreenSurfaceDrawableSet = true; } + @Override protected JAWT fetchJAWTImpl() throws NativeWindowException { // use offscreen if supported and [ applet or requested ] return JAWTUtil.getJAWT(getShallUseOffscreenLayer() || isApplet()); } + + @Override protected int lockSurfaceImpl() throws NativeWindowException { int ret = NativeWindow.LOCK_SURFACE_NOT_READY; - if(null == ds) { - ds = getJAWT().GetDrawingSurface(component); - if (ds == null) { - // Widget not yet realized - unlockSurfaceImpl(); - return NativeWindow.LOCK_SURFACE_NOT_READY; - } + ds = getJAWT().GetDrawingSurface(component); + if (ds == null) { + // Widget not yet realized + unlockSurfaceImpl(); + return NativeWindow.LOCK_SURFACE_NOT_READY; } int res = ds.Lock(); dsLocked = ( 0 == ( res & JAWTFactory.JAWT_LOCK_ERROR ) ) ; @@ -149,21 +241,20 @@ public class MacOSXJAWTWindow extends JAWTWindow implements SurfaceChangeable { if ((res & JAWTFactory.JAWT_LOCK_SURFACE_CHANGED) != 0) { ret = NativeWindow.LOCK_SURFACE_CHANGED; } - if(null == dsi) { - if (firstLock) { - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - dsi = ds.GetDrawingSurfaceInfo(); - return null; - } - }); - } else { - dsi = ds.GetDrawingSurfaceInfo(); - } - if (dsi == null) { - unlockSurfaceImpl(); - return NativeWindow.LOCK_SURFACE_NOT_READY; - } + if (firstLock) { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + dsi = ds.GetDrawingSurfaceInfo(); + return null; + } + }); + } else { + dsi = ds.GetDrawingSurfaceInfo(); + } + if (dsi == null) { + unlockSurfaceImpl(); + return NativeWindow.LOCK_SURFACE_NOT_READY; } updateBounds(dsi.getBounds()); if (DEBUG && firstLock ) { @@ -177,58 +268,83 @@ public class MacOSXJAWTWindow extends JAWTWindow implements SurfaceChangeable { return NativeWindow.LOCK_SURFACE_NOT_READY; } drawable = macosxdsi.getCocoaViewRef(); - + if (drawable == 0) { unlockSurfaceImpl(); return NativeWindow.LOCK_SURFACE_NOT_READY; } else { + windowHandle = OSXUtil.GetNSWindow(drawable); ret = NativeWindow.LOCK_SUCCESS; } } else { /** * Only create a fake invisible NSWindow for the drawable handle * to please frameworks requiring such (eg. NEWT). - * - * The actual surface/ca-layer shall be created/attached - * by the upper framework (JOGL) since they require more information. + * + * The actual surface/ca-layer shall be created/attached + * by the upper framework (JOGL) since they require more information. */ + String errMsg = null; if(0 == drawable) { - drawable = OSXUtil.CreateNSWindow(0, 0, getBounds().getWidth(), getBounds().getHeight()); - if(0 == drawable) { - unlockSurfaceImpl(); - throw new NativeWindowException("Unable to created dummy NSWindow (layered case)"); + windowHandle = OSXUtil.CreateNSWindow(0, 0, 64, 64); + if(0 == windowHandle) { + errMsg = "Unable to create dummy NSWindow (layered case)"; + } else { + drawable = OSXUtil.GetNSView(windowHandle); + if(0 == drawable) { + errMsg = "Null NSView of NSWindow "+toHexString(windowHandle); + } } - // fix caps reflecting offscreen! - Capabilities caps = (Capabilities) getPrivateGraphicsConfiguration().getChosenCapabilities().cloneMutable(); - caps.setOnscreen(false); - getPrivateGraphicsConfiguration().setChosenCapabilities(caps); - caps = (Capabilities) getGraphicsConfiguration().getChosenCapabilities().cloneMutable(); - caps.setOnscreen(false); - ((MutableGraphicsConfiguration)getGraphicsConfiguration()).setChosenCapabilities(caps); - } - if(0 == rootSurfaceLayerHandle) { - rootSurfaceLayerHandle = OSXUtil.CreateCALayer(); - if(0 == rootSurfaceLayerHandle) { - OSXUtil.DestroyNSWindow(drawable); - drawable = 0; - unlockSurfaceImpl(); - throw new NativeWindowException("Could not create root CALayer: "+this); + if(null == errMsg) { + // Fix caps reflecting offscreen! (no GL available here ..) + final Capabilities caps = (Capabilities) getGraphicsConfiguration().getChosenCapabilities().cloneMutable(); + caps.setOnscreen(false); + setChosenCapabilities(caps); } - if(!AttachJAWTSurfaceLayer0(dsi.getBuffer(), rootSurfaceLayerHandle)) { - OSXUtil.DestroyCALayer(rootSurfaceLayerHandle); - rootSurfaceLayerHandle = 0; - OSXUtil.DestroyNSWindow(drawable); - drawable = 0; - unlockSurfaceImpl(); - throw new NativeWindowException("Could not attach JAWT surfaceLayerHandle: "+this); + } + if(null == errMsg) { + jawtSurfaceLayersHandle = GetJAWTSurfaceLayersHandle0(dsi.getBuffer()); + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + String errMsg = null; + if(0 == rootSurfaceLayer && 0 != jawtSurfaceLayersHandle) { + rootSurfaceLayer = OSXUtil.CreateCALayer(bounds.getWidth(), bounds.getHeight()); + if(0 == rootSurfaceLayer) { + errMsg = "Could not create root CALayer"; + } else { + try { + SetJAWTRootSurfaceLayer0(jawtSurfaceLayersHandle, rootSurfaceLayer); + } catch(Exception e) { + errMsg = "Could not set JAWT rootSurfaceLayerHandle "+toHexString(rootSurfaceLayer)+", cause: "+e.getMessage(); + } + } + if(null != errMsg) { + if(0 != rootSurfaceLayer) { + OSXUtil.DestroyCALayer(rootSurfaceLayer); + rootSurfaceLayer = 0; + } + throw new NativeWindowException(errMsg+": "+MacOSXJAWTWindow.this); + } + } + } } ); + } + if(null != errMsg) { + if(0 != windowHandle) { + OSXUtil.DestroyNSWindow(windowHandle); + windowHandle = 0; } + drawable = 0; + unlockSurfaceImpl(); + throw new NativeWindowException(errMsg+": "+this); } ret = NativeWindow.LOCK_SUCCESS; } - + return ret; } - + + @Override protected void unlockSurfaceImpl() throws NativeWindowException { if(null!=ds) { if (null!=dsi) { @@ -247,7 +363,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements SurfaceChangeable { System.err.println("MaxOSXJAWTWindow: 0x"+Integer.toHexString(this.hashCode())+" - thread: "+Thread.currentThread().getName()); dumpJAWTInfo(); } - + /** * {@inheritDoc} * <p> @@ -257,32 +373,48 @@ public class MacOSXJAWTWindow extends JAWTWindow implements SurfaceChangeable { * .. * ds = getJAWT().GetDrawingSurface(component); * due to a SIGSEGV. - * + * * Hence we have some threading / sync issues with the native JAWT implementation. - * </p> + * </p> */ @Override - public Point getLocationOnScreen(Point storage) { - return getLocationOnScreenNonBlocking(storage, component); - } + public Point getLocationOnScreen(Point storage) { + if( null == storage ) { + storage = new Point(); + } + getLocationOnScreenNonBlocking(storage, component); + return storage; + } + @Override protected Point getLocationOnScreenNativeImpl(final int x0, final int y0) { return null; } - private static native boolean AttachJAWTSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); - // private static native boolean DetachJAWTSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); - + + private static native long GetJAWTSurfaceLayersHandle0(Buffer jawtDrawingSurfaceInfoBuffer); + + /** + * Set the given root CALayer in the JAWT surface + */ + private static native void SetJAWTRootSurfaceLayer0(long jawtSurfaceLayersHandle, long caLayer); + + /** + * Unset the given root CALayer in the JAWT surface, passing the NIO DrawingSurfaceInfo buffer + */ + private static native void UnsetJAWTRootSurfaceLayer0(long jawtSurfaceLayersHandle, long caLayer); + // Variables for lockSurface/unlockSurface private JAWT_DrawingSurface ds; private boolean dsLocked; private JAWT_DrawingSurfaceInfo dsi; - + private long jawtSurfaceLayersHandle; + private JAWT_MacOSXDrawingSurfaceInfo macosxdsi; - - private long rootSurfaceLayerHandle = 0; // attached to the JAWT_SurfaceLayer - - private long surfaceHandle = 0; - private int sscWidth, sscHeight; - private boolean sscSet = false; - + + private volatile long rootSurfaceLayer = 0; // attached to the JAWT_SurfaceLayer + + private long windowHandle = 0; + private long offscreenSurfaceDrawable = 0; + private boolean offscreenSurfaceDrawableSet = false; + // Workaround for instance of 4796548 private boolean firstLock = true; diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/Win32SunJDKReflection.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/Win32SunJDKReflection.java index 74dabb67f..8b9dfabfd 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/Win32SunJDKReflection.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/Win32SunJDKReflection.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -29,11 +29,11 @@ * 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. */ @@ -65,6 +65,7 @@ public class Win32SunJDKReflection { static { AccessController.doPrivileged(new PrivilegedAction() { + @Override public Object run() { try { win32GraphicsDeviceClass = Class.forName("sun.awt.Win32GraphicsDevice"); diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/WindowsJAWTWindow.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/WindowsJAWTWindow.java index 5d1d43792..54bdb34f6 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/WindowsJAWTWindow.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/windows/WindowsJAWTWindow.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -29,11 +29,11 @@ * 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. */ @@ -60,21 +60,17 @@ public class WindowsJAWTWindow extends JAWTWindow { super(comp, config); } + @Override protected void invalidateNative() { windowHandle = 0; } - protected void attachSurfaceLayerImpl(final long layerHandle) { - throw new UnsupportedOperationException("offscreen layer not supported"); - } - protected void detachSurfaceLayerImpl(final long layerHandle) { - throw new UnsupportedOperationException("offscreen layer not supported"); - } - + @Override protected JAWT fetchJAWTImpl() throws NativeWindowException { return JAWTUtil.getJAWT(false); // no offscreen } - + + @Override protected int lockSurfaceImpl() throws NativeWindowException { int ret = NativeWindow.LOCK_SUCCESS; ds = getJAWT().GetDrawingSurface(component); @@ -117,6 +113,7 @@ public class WindowsJAWTWindow extends JAWTWindow { return ret; } + @Override protected void unlockSurfaceImpl() throws NativeWindowException { drawable = 0; // invalid HDC if(null!=ds) { @@ -138,6 +135,7 @@ public class WindowsJAWTWindow extends JAWTWindow { return windowHandle; } + @Override protected Point getLocationOnScreenNativeImpl(int x, int y) { return GDIUtil.GetRelativeLocation( getWindowHandle(), 0 /*root win*/, x, y); } diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11JAWTWindow.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11JAWTWindow.java index 736718de8..4599b9021 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11JAWTWindow.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11JAWTWindow.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -29,7 +29,7 @@ * 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. @@ -57,19 +57,15 @@ public class X11JAWTWindow extends JAWTWindow { super(comp, config); } + @Override protected void invalidateNative() { } - protected void attachSurfaceLayerImpl(final long layerHandle) { - throw new UnsupportedOperationException("offscreen layer not supported"); - } - protected void detachSurfaceLayerImpl(final long layerHandle) { - throw new UnsupportedOperationException("offscreen layer not supported"); - } - + @Override protected JAWT fetchJAWTImpl() throws NativeWindowException { return JAWTUtil.getJAWT(false); // no offscreen } - + + @Override protected int lockSurfaceImpl() throws NativeWindowException { int ret = NativeWindow.LOCK_SUCCESS; ds = getJAWT().GetDrawingSurface(component); @@ -111,6 +107,7 @@ public class X11JAWTWindow extends JAWTWindow { return ret; } + @Override protected void unlockSurfaceImpl() throws NativeWindowException { if(null!=ds) { if (null!=dsi) { @@ -126,14 +123,16 @@ public class X11JAWTWindow extends JAWTWindow { x11dsi = null; } + @Override protected Point getLocationOnScreenNativeImpl(int x, int y) { - return X11Lib.GetRelativeLocation( getDisplayHandle(), getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); + // surface is locked and hence the device + return X11Lib.GetRelativeLocation(getDisplayHandle(), getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); } - + // Variables for lockSurface/unlockSurface private JAWT_DrawingSurface ds; private boolean dsLocked; private JAWT_DrawingSurfaceInfo dsi; private JAWT_X11DrawingSurfaceInfo x11dsi; - + } diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11SunJDKReflection.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11SunJDKReflection.java index 27e0a5e50..b2c3a931b 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11SunJDKReflection.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/x11/X11SunJDKReflection.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -29,11 +29,11 @@ * 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. */ @@ -65,6 +65,7 @@ public class X11SunJDKReflection { static { AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { try { x11GraphicsDeviceClass = Class.forName("sun.awt.X11GraphicsDevice"); diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXDummyUpstreamSurfaceHook.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXDummyUpstreamSurfaceHook.java new file mode 100644 index 000000000..b71af1042 --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXDummyUpstreamSurfaceHook.java @@ -0,0 +1,56 @@ +package jogamp.nativewindow.macosx; + +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; + +public class OSXDummyUpstreamSurfaceHook extends UpstreamSurfaceHookMutableSize { + long nsWindow; + + /** + * @param width the initial width as returned by {@link NativeSurface#getWidth()} via {@link UpstreamSurfaceHook#getWidth(ProxySurface)}, + * not the actual dummy surface width. + * The latter is platform specific and small + * @param height the initial height as returned by {@link NativeSurface#getHeight()} via {@link UpstreamSurfaceHook#getHeight(ProxySurface)}, + * not the actual dummy surface height, + * The latter is platform specific and small + */ + public OSXDummyUpstreamSurfaceHook(int width, int height) { + super(width, height); + nsWindow = 0; + } + + @Override + public final void create(ProxySurface s) { + if(0 == nsWindow && 0 == s.getSurfaceHandle()) { + nsWindow = OSXUtil.CreateNSWindow(0, 0, 64, 64); + if(0 == nsWindow) { + throw new NativeWindowException("Error NS window 0"); + } + long nsView = OSXUtil.GetNSView(nsWindow); + if(0 == nsView) { + throw new NativeWindowException("Error NS view 0"); + } + s.setSurfaceHandle(nsView); + s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } + s.addUpstreamOptionBits(ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE); + } + + @Override + public final void destroy(ProxySurface s) { + if( s.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + if( 0 == nsWindow || 0 == s.getSurfaceHandle() ) { + throw new InternalError("Owns upstream surface, but no OSX view/window: "+s+", nsWindow 0x"+Long.toHexString(nsWindow)); + } + OSXUtil.DestroyNSWindow(nsWindow); + nsWindow = 0; + s.setSurfaceHandle(0); + s.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } + } + +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index 3ca76a84a..bac07b85a 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -28,46 +28,98 @@ package jogamp.nativewindow.macosx; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; +import com.jogamp.common.util.Function; +import com.jogamp.common.util.FunctionTask; +import com.jogamp.common.util.RunnableTask; + import jogamp.nativewindow.Debug; import jogamp.nativewindow.NWJNILibLoader; +import jogamp.nativewindow.ToolkitProperties; -public class OSXUtil { - private static boolean isInit = false; +public class OSXUtil implements ToolkitProperties { + private static boolean isInit = false; private static final boolean DEBUG = Debug.debug("OSXUtil"); - - public static synchronized void initSingleton(boolean firstX11ActionOnProcess) { + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static synchronized void initSingleton() { if(!isInit) { + if(DEBUG) { + System.out.println("OSXUtil.initSingleton()"); + } if(!NWJNILibLoader.loadNativeWindow("macosx")) { throw new NativeWindowException("NativeWindow MacOSX native library load error."); } - + if( !initIDs0() ) { throw new NativeWindowException("MacOSX: Could not initialized native stub"); } - - if(DEBUG) { - System.out.println("OSX.isFirstX11ActionOnProcess: "+firstX11ActionOnProcess); - } - isInit = true; } } - public static boolean requiresToolkitLock() { - return false; + /** + * Called by {@link NativeWindowFactory#shutdown()} + * @see ToolkitProperties + */ + public static void shutdown() { } + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static boolean requiresToolkitLock() { return false; } + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean hasThreadingIssues() { return false; } + + public static boolean isNSView(long object) { + return 0 != object ? isNSView0(object) : false; } - - public static Point GetLocationOnScreen(long windowOrView, int src_x, int src_y) { - return (Point) GetLocationOnScreen0(windowOrView, src_x, src_y); + + public static boolean isNSWindow(long object) { + return 0 != object ? isNSWindow0(object) : false; } - - public static long CreateNSView(int x, int y, int width, int height) { - return CreateNSView0(x, y, width, height); + + /** + * In case the <code>windowOrView</code> is top-level, + * you shall set <code>topLevel</code> to true where + * insets gets into account to compute the client position as follows: + * <pre> + if(topLevel) { + // top-level position -> client window position + final Insets insets = GetInsets(windowOrView); + los.setX(los.getX() + insets.getLeftWidth()); + los.setY(los.getY() + insets.getTopHeight()); + } + * </pre> + * @param windowOrView + * @param topLevel + * @param src_x + * @param src_y + * @return the client position + */ + public static Point GetLocationOnScreen(long windowOrView, boolean topLevel, int src_x, int src_y) { + final Point los = (Point) GetLocationOnScreen0(windowOrView, src_x, src_y); + if(topLevel) { + // top-level position -> client window position + final Insets insets = GetInsets(windowOrView); + los.set(los.getX() + insets.getLeftWidth(), los.getY() + insets.getTopHeight()); + } + return los; } - public static void DestroyNSView(long nsView) { - DestroyNSView0(nsView); + + public static Insets GetInsets(long windowOrView) { + return (Insets) GetInsets0(windowOrView); } public static long CreateNSWindow(int x, int y, int width, int height) { @@ -76,51 +128,241 @@ public class OSXUtil { public static void DestroyNSWindow(long nsWindow) { DestroyNSWindow0(nsWindow); } - - public static long CreateCALayer() { - return CreateCALayer0(); + public static long GetNSView(long nsWindow) { + return GetNSView0(nsWindow); } - public static void AddCASublayer(long rootCALayer, long subCALayer) { + public static long GetNSWindow(long nsView) { + return GetNSWindow0(nsView); + } + + /** + * Create a CALayer suitable to act as a root CALayer. + * @see #DestroyCALayer(long) + * @see #AddCASublayer(long, long) + */ + public static long CreateCALayer(final int width, final int height) { + final long l = CreateCALayer0(width, height); + if(DEBUG) { + System.err.println("OSXUtil.CreateCALayer: 0x"+Long.toHexString(l)+" - "+Thread.currentThread().getName()); + } + return l; + } + + /** + * Attach a sub CALayer to the root CALayer + * <p> + * Method will trigger a <code>display</code> + * call to the CALayer hierarchy to enforce resource creation if required, e.g. an NSOpenGLContext. + * </p> + * <p> + * Hence it is important that related resources are not locked <i>if</i> + * they will be used for creation. + * </p> + * @param caLayerQuirks TODO + * @see #CreateCALayer(int, int) + * @see #RemoveCASublayer(long, long, boolean) + */ + public static void AddCASublayer(final long rootCALayer, final long subCALayer, final int x, final int y, final int width, final int height, final int caLayerQuirks) { if(0==rootCALayer || 0==subCALayer) { throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer)); } - AddCASublayer0(rootCALayer, subCALayer); + if(DEBUG) { + System.err.println("OSXUtil.AttachCALayer: caLayerQuirks "+caLayerQuirks+", 0x"+Long.toHexString(subCALayer)+" - "+Thread.currentThread().getName()); + } + AddCASublayer0(rootCALayer, subCALayer, x, y, width, height, caLayerQuirks); } - public static void RemoveCASublayer(long rootCALayer, long subCALayer) { + + /** + * Fix root and sub CALayer position to 0/0 and size + * <p> + * If the sub CALayer implements the Objective-C NativeWindow protocol NWDedicatedSize (e.g. JOGL's MyNSOpenGLLayer), + * the dedicated size is passed to the layer, which propagates it appropriately. + * </p> + * <p> + * On OSX/Java7 our root CALayer's frame position and size gets corrupted by its NSView, + * hence we have created the NWDedicatedSize protocol. + * </p> + * + * @param rootCALayer the root surface layer, maybe null. + * @param subCALayer the client surface layer, maybe null. + * @param visible TODO + * @param width the expected width + * @param height the expected height + * @param caLayerQuirks TODO + */ + public static void FixCALayerLayout(final long rootCALayer, final long subCALayer, final boolean visible, final int x, final int y, final int width, final int height, final int caLayerQuirks) { + if( 0==rootCALayer && 0==subCALayer ) { + return; + } + FixCALayerLayout0(rootCALayer, subCALayer, visible, x, y, width, height, caLayerQuirks); + } + + /** + * Detach a sub CALayer from the root CALayer. + */ + public static void RemoveCASublayer(final long rootCALayer, final long subCALayer) { if(0==rootCALayer || 0==subCALayer) { throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer)); } + if(DEBUG) { + System.err.println("OSXUtil.DetachCALayer: 0x"+Long.toHexString(subCALayer)+" - "+Thread.currentThread().getName()); + } RemoveCASublayer0(rootCALayer, subCALayer); } - public static void DestroyCALayer(long caLayer) { + + /** + * Destroy a CALayer. + * @see #CreateCALayer(int, int) + */ + public static void DestroyCALayer(final long caLayer) { if(0==caLayer) { throw new IllegalArgumentException("caLayer 0x"+Long.toHexString(caLayer)); } - DestroyCALayer0(caLayer); + if(DEBUG) { + System.err.println("OSXUtil.DestroyCALayer: 0x"+Long.toHexString(caLayer)+" - "+Thread.currentThread().getName()); + } + DestroyCALayer0(caLayer); } - + + /** + * Run on OSX UI main thread. + * <p> + * 'waitUntilDone' is implemented on Java site via lock/wait on {@link RunnableTask} to not freeze OSX main thread. + * </p> + * + * @param waitUntilDone + * @param runnable + */ public static void RunOnMainThread(boolean waitUntilDone, Runnable runnable) { - if(IsMainThread0()) { + if( IsMainThread0() ) { runnable.run(); // don't leave the JVM } else { - RunOnMainThread0(waitUntilDone, runnable); + // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, + // otherwise we may freeze the OSX main thread. + Throwable throwable = null; + final Object sync = new Object(); + final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); + synchronized(sync) { + RunOnMainThread0(rt); + if( waitUntilDone ) { + try { + sync.wait(); + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rt.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } + } + } + } + + /** + * Run later on .. + * @param onMain if true, run on main-thread, otherwise on the current OSX thread. + * @param runnable + * @param delay delay to run the runnable in milliseconds + */ + public static void RunLater(boolean onMain, Runnable runnable, int delay) { + RunLater0(onMain, new RunnableTask( runnable, null, true, System.err ), delay); + } + + private static Runnable _nop = new Runnable() { @Override public void run() {}; }; + + /** Issues a {@link #RunOnMainThread(boolean, Runnable)} w/ an <i>NOP</i> runnable, while waiting until done. */ + public static void WaitUntilFinish() { + RunOnMainThread(true, _nop); + } + + /** + * Run on OSX UI main thread. + * <p> + * 'waitUntilDone' is implemented on Java site via lock/wait on {@link FunctionTask} to not freeze OSX main thread. + * </p> + * + * @param waitUntilDone + * @param func + */ + public static <R,A> R RunOnMainThread(boolean waitUntilDone, Function<R,A> func, A... args) { + if( IsMainThread0() ) { + return func.eval(args); // don't leave the JVM + } else { + // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, + // otherwise we may freeze the OSX main thread. + Throwable throwable = null; + final Object sync = new Object(); + final FunctionTask<R,A> rt = new FunctionTask<R,A>( func, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); + synchronized(sync) { + rt.setArgs(args); + RunOnMainThread0(rt); + if( waitUntilDone ) { + try { + sync.wait(); + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rt.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } + } + return rt.getResult(); } } - + public static boolean IsMainThread() { return IsMainThread0(); } - + + /** Returns the screen refresh rate in Hz. If unavailable, returns 60Hz. */ + public static int GetScreenRefreshRate(int scrn_idx) { + return GetScreenRefreshRate0(scrn_idx); + } + + /*** + private static boolean isAWTEDTMainThreadInit = false; + private static boolean isAWTEDTMainThread; + + public synchronized static boolean isAWTEDTMainThread() { + if(!isAWTEDTMainThreadInit) { + isAWTEDTMainThreadInit = true; + if(Platform.AWT_AVAILABLE) { + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + isAWTEDTMainThread = IsMainThread(); + System.err.println("XXX: "+Thread.currentThread().getName()+" - isAWTEDTMainThread "+isAWTEDTMainThread); + } + }); + } else { + isAWTEDTMainThread = false; + } + } + return isAWTEDTMainThread; + } */ + private static native boolean initIDs0(); + private static native boolean isNSView0(long object); + private static native boolean isNSWindow0(long object); private static native Object GetLocationOnScreen0(long windowOrView, int src_x, int src_y); - private static native long CreateNSView0(int x, int y, int width, int height); - private static native void DestroyNSView0(long nsView); + private static native Object GetInsets0(long windowOrView); private static native long CreateNSWindow0(int x, int y, int width, int height); private static native void DestroyNSWindow0(long nsWindow); - private static native long CreateCALayer0(); - private static native void AddCASublayer0(long rootCALayer, long subCALayer); + private static native long GetNSView0(long nsWindow); + private static native long GetNSWindow0(long nsView); + private static native long CreateCALayer0(int width, int height); + private static native void AddCASublayer0(long rootCALayer, long subCALayer, int x, int y, int width, int height, int caLayerQuirks); + private static native void FixCALayerLayout0(long rootCALayer, long subCALayer, boolean visible, int x, int y, int width, int height, int caLayerQuirks); private static native void RemoveCASublayer0(long rootCALayer, long subCALayer); private static native void DestroyCALayer0(long caLayer); - private static native void RunOnMainThread0(boolean waitUntilDone, Runnable runnable); + private static native void RunOnMainThread0(Runnable runnable); + private static native void RunLater0(boolean onMain, Runnable runnable, int delay); private static native boolean IsMainThread0(); + private static native int GetScreenRefreshRate0(int scrn_idx); } diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIDummyUpstreamSurfaceHook.java b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIDummyUpstreamSurfaceHook.java new file mode 100644 index 000000000..e5de43c0a --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIDummyUpstreamSurfaceHook.java @@ -0,0 +1,50 @@ +package jogamp.nativewindow.windows; + +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; + +public class GDIDummyUpstreamSurfaceHook extends UpstreamSurfaceHookMutableSize { + /** + * @param width the initial width as returned by {@link NativeSurface#getWidth()} via {@link UpstreamSurfaceHook#getWidth(ProxySurface)}, + * not the actual dummy surface width. + * The latter is platform specific and small + * @param height the initial height as returned by {@link NativeSurface#getHeight()} via {@link UpstreamSurfaceHook#getHeight(ProxySurface)}, + * not the actual dummy surface height, + * The latter is platform specific and small + */ + public GDIDummyUpstreamSurfaceHook(int width, int height) { + super(width, height); + } + + @Override + public final void create(ProxySurface s) { + final GDISurface ms = (GDISurface)s; + if(0 == ms.getWindowHandle()) { + final long windowHandle = GDIUtil.CreateDummyWindow(0, 0, 64, 64); + if(0 == windowHandle) { + throw new NativeWindowException("Error windowHandle 0, werr: "+GDI.GetLastError()); + } + ms.setWindowHandle(windowHandle); + ms.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } + s.addUpstreamOptionBits(ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE); + } + + @Override + public final void destroy(ProxySurface s) { + final GDISurface ms = (GDISurface)s; + if( ms.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + if( 0 == ms.getWindowHandle() ) { + throw new InternalError("Owns upstream surface, but no GDI window: "+ms); + } + GDI.ShowWindow(ms.getWindowHandle(), GDI.SW_HIDE); + GDIUtil.DestroyDummyWindow(ms.getWindowHandle()); + ms.setWindowHandle(0); + ms.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } + } +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/GDISurface.java b/src/nativewindow/classes/jogamp/nativewindow/windows/GDISurface.java index bc02ac5dc..3e07b2a9e 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/windows/GDISurface.java +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/GDISurface.java @@ -29,8 +29,13 @@ package jogamp.nativewindow.windows; import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; + +import jogamp.nativewindow.ProxySurfaceImpl; +import jogamp.nativewindow.windows.GDI; /** @@ -38,25 +43,64 @@ import javax.media.nativewindow.ProxySurface; * allowing the use of HDC via lockSurface()/unlockSurface() protocol. * The latter will get and release the HDC. * The size via getWidth()/getHeight() is invalid. + * + * @see ProxySurface */ -public class GDISurface extends ProxySurface { +public class GDISurface extends ProxySurfaceImpl { protected long windowHandle; protected long surfaceHandle; - public GDISurface(AbstractGraphicsConfiguration cfg, long windowHandle) { - super(cfg); - if(0 == windowHandle) { - throw new NativeWindowException("Error hwnd 0, werr: "+GDI.GetLastError()); - } + /** + * @param cfg the {@link AbstractGraphicsConfiguration} to be used + * @param windowHandle the wrapped pre-existing native window handle, maybe 0 if not yet determined + * @param upstream the {@link UpstreamSurfaceHook} to be used + * @param ownsDevice <code>true</code> if this {@link ProxySurface} instance + * owns the {@link AbstractGraphicsConfiguration}'s {@link AbstractGraphicsDevice}, + * otherwise <code>false</code>. Owning the device implies closing it at {@link #destroyNotify()}. + */ + public GDISurface(AbstractGraphicsConfiguration cfg, long windowHandle, UpstreamSurfaceHook upstream, boolean ownsDevice) { + super(cfg, upstream, ownsDevice); this.windowHandle=windowHandle; + this.surfaceHandle=0; + } + + @Override + protected void invalidateImpl() { + if(0 != surfaceHandle) { + throw new NativeWindowException("didn't release surface Handle: "+this); + } + windowHandle = 0; + // surfaceHandle = 0; + } + + /** + * {@inheritDoc} + * <p> + * Actually the window handle (HWND), since the surfaceHandle (HDC) is derived + * from it at {@link #lockSurface()}. + * </p> + */ + @Override + public final void setSurfaceHandle(long surfaceHandle) { + this.windowHandle = surfaceHandle; } - protected final void invalidateImpl() { - windowHandle=0; - surfaceHandle=0; + /** + * Sets the window handle (HWND). + */ + public final void setWindowHandle(long windowHandle) { + this.windowHandle = windowHandle; } + public final long getWindowHandle() { + return windowHandle; + } + + @Override final protected int lockSurfaceImpl() { + if (0 == windowHandle) { + throw new NativeWindowException("null window handle: "+this); + } if (0 != surfaceHandle) { throw new InternalError("surface not released"); } @@ -70,27 +114,18 @@ public class GDISurface extends ProxySurface { return (0 != surfaceHandle) ? LOCK_SUCCESS : LOCK_SURFACE_NOT_READY; } + @Override final protected void unlockSurfaceImpl() { - if (0 == surfaceHandle) { - throw new InternalError("surface not acquired: "+this+", thread: "+Thread.currentThread().getName()); - } - if(0 == GDI.ReleaseDC(windowHandle, surfaceHandle)) { - throw new NativeWindowException("DC not released: "+this+", isWindow "+GDI.IsWindow(windowHandle)+", werr "+GDI.GetLastError()+", thread: "+Thread.currentThread().getName()); + if (0 != surfaceHandle) { + if(0 == GDI.ReleaseDC(windowHandle, surfaceHandle)) { + throw new NativeWindowException("DC not released: "+this+", isWindow "+GDI.IsWindow(windowHandle)+", werr "+GDI.GetLastError()+", thread: "+Thread.currentThread().getName()); + } + surfaceHandle=0; } - surfaceHandle=0; } + @Override final public long getSurfaceHandle() { return surfaceHandle; } - - final public String toString() { - return "GDISurface[config "+getPrivateGraphicsConfiguration()+ - ", displayHandle 0x"+Long.toHexString(getDisplayHandle())+ - ", windowHandle 0x"+Long.toHexString(windowHandle)+ - ", surfaceHandle 0x"+Long.toHexString(getSurfaceHandle())+ - ", size "+getWidth()+"x"+getHeight()+ - ", surfaceLock "+surfaceLock+"]"; - } - } diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java index d17a1898c..720ff9bdb 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java @@ -29,75 +29,119 @@ package jogamp.nativewindow.windows; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; import jogamp.nativewindow.NWJNILibLoader; import jogamp.nativewindow.Debug; -import jogamp.nativewindow.x11.X11Util; +import jogamp.nativewindow.ToolkitProperties; -public class GDIUtil { +public class GDIUtil implements ToolkitProperties { private static final boolean DEBUG = Debug.debug("GDIUtil"); - + private static final String dummyWindowClassNameBase = "_dummyWindow_clazz" ; private static RegisteredClassFactory dummyWindowClassFactory; private static boolean isInit = false; - - public static synchronized void initSingleton(boolean firstX11ActionOnProcess) { + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static synchronized void initSingleton() { if(!isInit) { - synchronized(X11Util.class) { + synchronized(GDIUtil.class) { if(!isInit) { - isInit = true; + if(DEBUG) { + System.out.println("GDI.initSingleton()"); + } if(!NWJNILibLoader.loadNativeWindow("win32")) { throw new NativeWindowException("NativeWindow Windows native library load error."); } - if( !initIDs0() ) { throw new NativeWindowException("GDI: Could not initialized native stub"); } - + dummyWindowClassFactory = new RegisteredClassFactory(dummyWindowClassNameBase, getDummyWndProc0(), + true /* useDummyDispatchThread */, + 0 /* iconSmallHandle */, 0 /* iconBigHandle */); if(DEBUG) { - System.out.println("GDI.isFirstX11ActionOnProcess: "+firstX11ActionOnProcess); + System.out.println("GDI.initSingleton() dummyWindowClassFactory "+dummyWindowClassFactory); } - - dummyWindowClassFactory = new RegisteredClassFactory(dummyWindowClassNameBase, getDummyWndProc0()); + isInit = true; } } } } - + + /** + * Called by {@link NativeWindowFactory#shutdown()} + * @see ToolkitProperties + */ + public static void shutdown() { + } + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ public static boolean requiresToolkitLock() { return false; } - + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean hasThreadingIssues() { return false; } + private static RegisteredClass dummyWindowClass = null; private static Object dummyWindowSync = new Object(); - + public static long CreateDummyWindow(int x, int y, int width, int height) { synchronized(dummyWindowSync) { dummyWindowClass = dummyWindowClassFactory.getSharedClass(); - return CreateDummyWindow0(dummyWindowClass.getHandle(), dummyWindowClass.getName(), dummyWindowClass.getName(), x, y, width, height); + if(DEBUG) { + System.out.println("GDI.CreateDummyWindow() dummyWindowClassFactory "+dummyWindowClassFactory); + System.out.println("GDI.CreateDummyWindow() dummyWindowClass "+dummyWindowClass); + } + return CreateDummyWindow0(dummyWindowClass.getHInstance(), dummyWindowClass.getName(), dummyWindowClass.getHDispThreadContext(), dummyWindowClass.getName(), x, y, width, height); } } - + public static boolean DestroyDummyWindow(long hwnd) { boolean res; synchronized(dummyWindowSync) { if( null == dummyWindowClass ) { throw new InternalError("GDI Error ("+dummyWindowClassFactory.getSharedRefCount()+"): SharedClass is null"); } - res = GDI.DestroyWindow(hwnd); + res = DestroyWindow0(dummyWindowClass.getHDispThreadContext(), hwnd); dummyWindowClassFactory.releaseSharedClass(); } return res; } - + public static Point GetRelativeLocation(long src_win, long dest_win, int src_x, int src_y) { return (Point) GetRelativeLocation0(src_win, dest_win, src_x, src_y); } - - public static native boolean CreateWindowClass(long hInstance, String clazzName, long wndProc); - public static native boolean DestroyWindowClass(long hInstance, String className); - + + public static boolean IsUndecorated(long win) { + return IsUndecorated0(win); + } + + public static boolean IsChild(long win) { + return IsChild0(win); + } + + private static final void dumpStack() { Thread.dumpStack(); } // Callback for JNI + + /** Creates WNDCLASSEX instance */ + static native boolean CreateWindowClass0(long hInstance, String clazzName, long wndProc, long iconSmallHandle, long iconBigHandle); + /** Destroys WNDCLASSEX instance */ + static native boolean DestroyWindowClass0(long hInstance, String className, long dispThreadCtx); + static native long CreateDummyDispatchThread0(); + private static native boolean initIDs0(); - private static native long getDummyWndProc0(); + private static native long getDummyWndProc0(); private static native Object GetRelativeLocation0(long src_win, long dest_win, int src_x, int src_y); - - static native long CreateDummyWindow0(long hInstance, String className, String windowName, int x, int y, int width, int height); + private static native boolean IsChild0(long win); + private static native boolean IsUndecorated0(long win); + + private static native long CreateDummyWindow0(long hInstance, String className, long dispThreadCtx, String windowName, int x, int y, int width, int height); + private static native boolean DestroyWindow0(long dispThreadCtx, long win); } diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClass.java b/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClass.java index afb3daf7c..1f6cb7c05 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClass.java +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClass.java @@ -29,17 +29,25 @@ package jogamp.nativewindow.windows; public class RegisteredClass { - long hInstance; - String className; + private final long hInstance; + private final String className; + private final long hDDTCtx; - RegisteredClass(long hInst, String name) { - hInstance = hInst; - className = name; + RegisteredClass(long hInst, String name, long hDispatchThreadCtx) { + this.hInstance = hInst; + this.className = name; + this.hDDTCtx = hDispatchThreadCtx; } - public final long getHandle() { return hInstance; } + /** Application handle, same as {@link RegisteredClassFactory#getHInstance()}. */ + public final long getHInstance() { return hInstance; } + + /** Unique Window Class Name */ public final String getName() { return className; } + /** Unique associated dispatch thread context for this Window Class, or 0 for none. */ + public final long getHDispThreadContext() { return hDDTCtx; } + @Override - public final String toString() { return "RegisteredClass[handle 0x"+Long.toHexString(hInstance)+", "+className+"]"; } + public final String toString() { return "RegisteredClass[handle 0x"+Long.toHexString(hInstance)+", "+className+", dtx 0x"+Long.toHexString(hDDTCtx)+"]"; } } diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClassFactory.java b/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClassFactory.java index 00bedfc8e..c4b4d145c 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClassFactory.java +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/RegisteredClassFactory.java @@ -29,46 +29,76 @@ package jogamp.nativewindow.windows; import jogamp.nativewindow.Debug; + import java.util.ArrayList; + import javax.media.nativewindow.NativeWindowException; public class RegisteredClassFactory { - static final boolean DEBUG = Debug.debug("RegisteredClass"); - private static ArrayList<RegisteredClassFactory> registeredFactories = new ArrayList<RegisteredClassFactory>(); - - private String classBaseName; - private long wndProc; + private static final boolean DEBUG = Debug.debug("RegisteredClass"); + private static final ArrayList<RegisteredClassFactory> registeredFactories; + private static final long hInstance; + + static { + hInstance = GDI.GetApplicationHandle(); + if( 0 == hInstance ) { + throw new NativeWindowException("Error: Null ModuleHandle for Application"); + } + registeredFactories = new ArrayList<RegisteredClassFactory>(); + } + + private final String classBaseName; + private final long wndProc; + private final boolean useDummyDispatchThread; + private final long iconSmallHandle, iconBigHandle; private RegisteredClass sharedClass = null; private int classIter = 0; private int sharedRefCount = 0; private final Object sync = new Object(); + private String toHexString(long l) { return "0x"+Long.toHexString(l); } + + @Override + public final String toString() { return "RegisteredClassFactory[moduleHandle "+toHexString(hInstance)+", "+classBaseName+ + ", wndProc "+toHexString(wndProc)+", useDDT "+useDummyDispatchThread+", shared[refCount "+sharedRefCount+", class "+sharedClass+"]]"; } + /** - * Release the {@link RegisteredClass} of all {@link RegisteredClassFactory}. + * Release the {@link RegisteredClass} of all {@link RegisteredClassFactory}. */ public static void shutdownSharedClasses() { synchronized(registeredFactories) { + if( DEBUG ) { + System.err.println("RegisteredClassFactory.shutdownSharedClasses: "+registeredFactories.size()); + } for(int j=0; j<registeredFactories.size(); j++) { final RegisteredClassFactory rcf = registeredFactories.get(j); synchronized(rcf.sync) { if(null != rcf.sharedClass) { - GDIUtil.DestroyWindowClass(rcf.sharedClass.getHandle(), rcf.sharedClass.getName()); + GDIUtil.DestroyWindowClass0(rcf.sharedClass.getHInstance(), rcf.sharedClass.getName(), rcf.sharedClass.getHDispThreadContext()); rcf.sharedClass = null; rcf.sharedRefCount = 0; - rcf.classIter = 0; + rcf.classIter = 0; if(DEBUG) { - System.err.println("RegisteredClassFactory #"+j+"/"+registeredFactories.size()+" shutdownSharedClasses : "+rcf.sharedClass); + System.err.println("RegisteredClassFactory #"+j+"/"+registeredFactories.size()+": shutdownSharedClasses : "+rcf.sharedClass); } + } else if(DEBUG) { + System.err.println("RegisteredClassFactory #"+j+"/"+registeredFactories.size()+": null"); } } } } } - public RegisteredClassFactory(String classBaseName, long wndProc) { + /** Application handle. */ + public static long getHInstance() { return hInstance; } + + public RegisteredClassFactory(String classBaseName, long wndProc, boolean useDummyDispatchThread, long iconSmallHandle, long iconBigHandle) { this.classBaseName = classBaseName; this.wndProc = wndProc; + this.useDummyDispatchThread = useDummyDispatchThread; + this.iconSmallHandle = iconSmallHandle; + this.iconBigHandle = iconBigHandle; synchronized(registeredFactories) { registeredFactories.add(this); } @@ -80,23 +110,28 @@ public class RegisteredClassFactory { if( null != sharedClass ) { throw new InternalError("Error ("+sharedRefCount+"): SharedClass not null: "+sharedClass); } - long hInstance = GDI.GetApplicationHandle(); - if( 0 == hInstance ) { - throw new NativeWindowException("Error: Null ModuleHandle for Application"); - } String clazzName = null; boolean registered = false; - final int classIterMark = classIter - 1; + final int classIterMark = classIter - 1; while ( !registered && classIterMark != classIter ) { // Retry with next clazz name, this could happen if more than one JVM is running clazzName = classBaseName + classIter; classIter++; - registered = GDIUtil.CreateWindowClass(hInstance, clazzName, wndProc); + registered = GDIUtil.CreateWindowClass0(hInstance, clazzName, wndProc, iconSmallHandle, iconBigHandle); } if( !registered ) { throw new NativeWindowException("Error: Could not create WindowClass: "+clazzName); } - sharedClass = new RegisteredClass(hInstance, clazzName); + final long hDispatchThread; + if( useDummyDispatchThread ) { + hDispatchThread = GDIUtil.CreateDummyDispatchThread0(); + if( 0 == hDispatchThread ) { + throw new NativeWindowException("Error: Could not create DDT "+clazzName); + } + } else { + hDispatchThread = 0; + } + sharedClass = new RegisteredClass(hInstance, clazzName, hDispatchThread); if(DEBUG) { System.err.println("RegisteredClassFactory getSharedClass ("+sharedRefCount+") initialized: "+sharedClass); } @@ -121,7 +156,7 @@ public class RegisteredClassFactory { throw new InternalError("Error ("+sharedRefCount+"): SharedClass is null"); } if( 0 == sharedRefCount ) { - GDIUtil.DestroyWindowClass(sharedClass.getHandle(), sharedClass.getName()); + GDIUtil.DestroyWindowClass0(sharedClass.getHInstance(), sharedClass.getName(), sharedClass.getHDispThreadContext()); if(DEBUG) { System.err.println("RegisteredClassFactory releaseSharedClass ("+sharedRefCount+") released: "+sharedClass); } diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Capabilities.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Capabilities.java index 0e69c738f..4f8cff8c5 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Capabilities.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Capabilities.java @@ -40,10 +40,12 @@ public class X11Capabilities extends Capabilities { this.xVisualInfo = xVisualInfo; } + @Override public Object cloneMutable() { return clone(); } + @Override public Object clone() { try { return super.clone(); @@ -68,9 +70,10 @@ public class X11Capabilities extends Capabilities { return VisualIDHolder.VID_UNDEFINED; default: throw new NativeWindowException("Invalid type <"+type+">"); - } + } } - + + @Override public StringBuilder toString(StringBuilder sink) { if(null == sink) { sink = new StringBuilder(); diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java new file mode 100644 index 000000000..2c8ef642c --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java @@ -0,0 +1,70 @@ +package jogamp.nativewindow.x11; + +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; + +import jogamp.nativewindow.x11.X11Lib; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; +import com.jogamp.nativewindow.x11.X11GraphicsConfiguration; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; +import com.jogamp.nativewindow.x11.X11GraphicsScreen; + +public class X11DummyUpstreamSurfaceHook extends UpstreamSurfaceHookMutableSize { + /** + * @param width the initial width as returned by {@link NativeSurface#getWidth()} via {@link UpstreamSurfaceHook#getWidth(ProxySurface)}, + * not the actual dummy surface width. + * The latter is platform specific and small + * @param height the initial height as returned by {@link NativeSurface#getHeight()} via {@link UpstreamSurfaceHook#getHeight(ProxySurface)}, + * not the actual dummy surface height, + * The latter is platform specific and small + */ + public X11DummyUpstreamSurfaceHook(int width, int height) { + super(width, height); + } + + @Override + public final void create(ProxySurface s) { + final X11GraphicsConfiguration cfg = (X11GraphicsConfiguration) s.getGraphicsConfiguration(); + final X11GraphicsScreen screen = (X11GraphicsScreen) cfg.getScreen(); + final X11GraphicsDevice device = (X11GraphicsDevice) screen.getDevice(); + device.lock(); + try { + if(0 == device.getHandle()) { + device.open(); + s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + if( 0 == s.getSurfaceHandle() ) { + final long windowHandle = X11Lib.CreateWindow(0, device.getHandle(), screen.getIndex(), cfg.getXVisualID(), 64, 64, false, false); + if(0 == windowHandle) { + throw new NativeWindowException("Creating dummy window failed w/ "+cfg); + } + s.setSurfaceHandle(windowHandle); + s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } + s.addUpstreamOptionBits(ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE); + } finally { + device.unlock(); + } + } + + @Override + public final void destroy(ProxySurface s) { + if( s.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + final X11GraphicsDevice device = (X11GraphicsDevice) s.getGraphicsConfiguration().getScreen().getDevice(); + if( 0 == s.getSurfaceHandle() ) { + throw new InternalError("Owns upstream surface, but no X11 window: "+s); + } + device.lock(); + try { + X11Lib.DestroyWindow(device.getHandle(), s.getSurfaceHandle()); + s.setSurfaceHandle(0); + s.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } finally { + device.unlock(); + } + } + } +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11GraphicsConfigurationFactory.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11GraphicsConfigurationFactory.java index 070f87216..6258632cd 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11GraphicsConfigurationFactory.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11GraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -39,33 +39,40 @@ import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.VisualIDHolder; import com.jogamp.nativewindow.x11.X11GraphicsConfiguration; import com.jogamp.nativewindow.x11.X11GraphicsScreen; public class X11GraphicsConfigurationFactory extends GraphicsConfigurationFactory { public static void registerFactory() { - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, new X11GraphicsConfigurationFactory()); - } + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, CapabilitiesImmutable.class, new X11GraphicsConfigurationFactory()); + } private X11GraphicsConfigurationFactory() { } - + + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( - CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen screen) + CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen screen, int nativeVisualID) throws IllegalArgumentException, NativeWindowException { if(!(screen instanceof X11GraphicsScreen)) { throw new NativeWindowException("Only valid X11GraphicsScreen are allowed"); } - final X11Capabilities x11CapsChosen = new X11Capabilities(getXVisualInfo(screen, capsChosen)); - AbstractGraphicsConfiguration res = new X11GraphicsConfiguration((X11GraphicsScreen)screen, x11CapsChosen, capsRequested, x11CapsChosen.getXVisualInfo()); + final X11Capabilities x11CapsChosen; + if(VisualIDHolder.VID_UNDEFINED == nativeVisualID) { + x11CapsChosen = new X11Capabilities(getXVisualInfo(screen, capsChosen)); + } else { + x11CapsChosen = new X11Capabilities(getXVisualInfo(screen, nativeVisualID)); + } + final AbstractGraphicsConfiguration res = new X11GraphicsConfiguration((X11GraphicsScreen)screen, x11CapsChosen, capsRequested, x11CapsChosen.getXVisualInfo()); if(DEBUG) { - System.err.println("X11GraphicsConfigurationFactory.chooseGraphicsConfigurationImpl("+screen+","+capsChosen+"): "+res); + System.err.println("X11GraphicsConfigurationFactory.chooseGraphicsConfigurationImpl(visualID 0x"+Integer.toHexString(nativeVisualID)+", "+screen+","+capsChosen+"): "+res); } return res; } - public static XVisualInfo getXVisualInfo(AbstractGraphicsScreen screen, long visualID) + public static XVisualInfo getXVisualInfo(AbstractGraphicsScreen screen, int visualID) { XVisualInfo xvi_temp = XVisualInfo.create(); xvi_temp.setVisualid(visualID); @@ -103,7 +110,7 @@ public class X11GraphicsConfigurationFactory extends GraphicsConfigurationFactor XVisualInfo best=null; int rdepth = capabilities.getRedBits() + capabilities.getGreenBits() + capabilities.getBlueBits() + capabilities.getAlphaBits(); for (int i = 0; vinfos!=null && i < num[0]; i++) { - if ( best == null || + if ( best == null || best.getDepth() < vinfos[i].getDepth() ) { best = vinfos[i]; diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java index c42bcf816..02e43791c 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -34,6 +34,7 @@ package jogamp.nativewindow.x11; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -42,22 +43,27 @@ import javax.media.nativewindow.NativeWindowFactory; import jogamp.nativewindow.Debug; import jogamp.nativewindow.NWJNILibLoader; +import jogamp.nativewindow.ToolkitProperties; import com.jogamp.common.util.LongObjectHashMap; +import com.jogamp.common.util.PropertyAccess; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; /** * Contains a thread safe X11 utility to retrieve display connections. */ -public class X11Util { - /** +public class X11Util implements ToolkitProperties { + public static final boolean DEBUG = Debug.debug("X11Util"); + + /** * See Bug 515 - https://jogamp.org/bugzilla/show_bug.cgi?id=515 - * <p> + * <p> * It is observed that ATI X11 drivers, eg. - * <ul> + * <ul> * <li>fglrx 8.78.6,</li> * <li>fglrx 11.08/8.881 and </li> * <li>fglrx 11.11/8.911</li> - * </ul> + * </ul> * are quite sensitive to multiple Display connections. * </p> * <p> @@ -67,113 +73,206 @@ public class X11Util { * </p> * <p> * You may test this, ie just reverse the destroy order below. - * See also native test: jogl/test/native/displayMultiple02.c + * See also native test: jogl/test-native/displayMultiple02.c + * </p> + * <p> + * Workaround is to not close them at all if driver vendor is ATI + * during operation. * </p> * <p> - * Workaround is to not close them at all if driver vendor is ATI. + * With ATI X11 drivers all connections must be closed at JVM shutdown, + * otherwise a SIGSEGV (after JVM safepoint) will be caused. * </p> */ - public static final boolean ATI_HAS_XCLOSEDISPLAY_BUG = true; + public static final boolean ATI_HAS_XCLOSEDISPLAY_BUG = !Debug.isPropertyDefined("nativewindow.debug.X11Util.ATI_HAS_NO_XCLOSEDISPLAY_BUG", true); - /** Value is <code>true</code>, best 'stable' results if always using XInitThreads(). */ - public static final boolean XINITTHREADS_ALWAYS_ENABLED = true; - - /** Value is <code>true</code>, best 'stable' results if not using XLockDisplay/XUnlockDisplay at all. */ - public static final boolean HAS_XLOCKDISPLAY_BUG = true; - - private static final boolean DEBUG = Debug.debug("X11Util"); - private static final boolean TRACE_DISPLAY_LIFECYCLE = Debug.getBooleanProperty("nativewindow.debug.X11Util.TraceDisplayLifecycle", true); + /** See {@link #ATI_HAS_XCLOSEDISPLAY_BUG}. */ + public static final boolean HAS_XCLOSEDISPLAY_BUG = Debug.isPropertyDefined("nativewindow.debug.X11Util.HAS_XCLOSEDISPLAY_BUG", true); + /** + * See Bug 623 - https://jogamp.org/bugzilla/show_bug.cgi?id=623 + */ + public static final boolean ATI_HAS_MULTITHREADING_BUG = !Debug.isPropertyDefined("nativewindow.debug.X11Util.ATI_HAS_NO_MULTITHREADING_BUG", true); + + public static final boolean XSYNC_ENABLED = Debug.isPropertyDefined("nativewindow.debug.X11Util.XSync", true); + public static final boolean XERROR_STACKDUMP = DEBUG || Debug.isPropertyDefined("nativewindow.debug.X11Util.XErrorStackDump", true); + private static final boolean TRACE_DISPLAY_LIFECYCLE = Debug.isPropertyDefined("nativewindow.debug.X11Util.TraceDisplayLifecycle", true); private static String nullDisplayName = null; - private static boolean isX11LockAvailable = false; - private static boolean requiresX11Lock = true; private static volatile boolean isInit = false; - private static boolean markAllDisplaysUnclosable = false; // ATI/AMD X11 driver issues + private static boolean markAllDisplaysUnclosable = false; // ATI/AMD X11 driver issues, or GLRendererQuirks.DontCloseX11Display + private static boolean hasThreadingIssues = false; // ATI/AMD X11 driver issues - private static int setX11ErrorHandlerRecCount = 0; - private static Object setX11ErrorHandlerLock = new Object(); + private static final Object setX11ErrorHandlerLock = new Object(); + private static final String X11_EXTENSION_ATIFGLRXDRI = "ATIFGLRXDRI"; + private static final String X11_EXTENSION_ATIFGLEXTENSION = "ATIFGLEXTENSION"; - - @SuppressWarnings("unused") - public static void initSingleton(final boolean firstX11ActionOnProcess) { + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static void initSingleton() { if(!isInit) { synchronized(X11Util.class) { if(!isInit) { isInit = true; + if(DEBUG) { + System.out.println("X11Util.initSingleton()"); + } if(!NWJNILibLoader.loadNativeWindow("x11")) { throw new NativeWindowException("NativeWindow X11 native library load error."); } - - final boolean callXInitThreads = XINITTHREADS_ALWAYS_ENABLED || firstX11ActionOnProcess; - final boolean isXInitThreadsOK = initialize0( XINITTHREADS_ALWAYS_ENABLED || firstX11ActionOnProcess ); - isX11LockAvailable = isXInitThreadsOK && !HAS_XLOCKDISPLAY_BUG ; - - final long dpy = X11Lib.XOpenDisplay(null); - try { - nullDisplayName = X11Lib.XDisplayString(dpy); - } finally { - X11Lib.XCloseDisplay(dpy); + + final boolean isInitOK = initialize0( XERROR_STACKDUMP ); + + final boolean hasX11_EXTENSION_ATIFGLRXDRI, hasX11_EXTENSION_ATIFGLEXTENSION; + final long dpy = X11Lib.XOpenDisplay(PropertyAccess.getProperty("nativewindow.x11.display.default", true)); + if(0 != dpy) { + if(XSYNC_ENABLED) { + X11Lib.XSynchronize(dpy, true); + } + try { + nullDisplayName = X11Lib.XDisplayString(dpy); + hasX11_EXTENSION_ATIFGLRXDRI = X11Lib.QueryExtension(dpy, X11_EXTENSION_ATIFGLRXDRI); + hasX11_EXTENSION_ATIFGLEXTENSION = X11Lib.QueryExtension(dpy, X11_EXTENSION_ATIFGLEXTENSION); + } finally { + X11Lib.XCloseDisplay(dpy); + } + } else { + nullDisplayName = "nil"; + hasX11_EXTENSION_ATIFGLRXDRI = false; + hasX11_EXTENSION_ATIFGLEXTENSION = false; + } + final boolean isATIFGLRX = hasX11_EXTENSION_ATIFGLRXDRI || hasX11_EXTENSION_ATIFGLEXTENSION ; + hasThreadingIssues = ATI_HAS_MULTITHREADING_BUG && isATIFGLRX; + if ( !markAllDisplaysUnclosable ) { + markAllDisplaysUnclosable = ( ATI_HAS_XCLOSEDISPLAY_BUG && isATIFGLRX ) || HAS_XCLOSEDISPLAY_BUG; } - + if(DEBUG) { - System.err.println("X11Util firstX11ActionOnProcess: "+firstX11ActionOnProcess+ - ", requiresX11Lock "+requiresX11Lock+ - ", XInitThreads [called "+callXInitThreads+", OK "+isXInitThreadsOK+"]"+ - ", isX11LockAvailable "+isX11LockAvailable+ - ", X11 Display(NULL) <"+nullDisplayName+">"); + System.err.println("X11Util.initSingleton(): OK "+isInitOK+"]"+ + ",\n\t X11 Display(NULL) <"+nullDisplayName+">"+ + ",\n\t XSynchronize Enabled: " + XSYNC_ENABLED+ + ",\n\t X11_EXTENSION_ATIFGLRXDRI " + hasX11_EXTENSION_ATIFGLRXDRI+ + ",\n\t X11_EXTENSION_ATIFGLEXTENSION " + hasX11_EXTENSION_ATIFGLEXTENSION+ + ",\n\t requiresToolkitLock "+requiresToolkitLock()+ + ",\n\t hasThreadingIssues "+hasThreadingIssues()+ + ",\n\t markAllDisplaysUnclosable "+getMarkAllDisplaysUnclosable() + ); // Thread.dumpStack(); } } } } } - - public static synchronized boolean isNativeLockAvailable() { - return isX11LockAvailable; + + // not exactly thread safe, but good enough for our purpose, + // which is to tag a NamedDisplay uncloseable after creation. + private static Object globalLock = new Object(); + private static LongObjectHashMap openDisplayMap = new LongObjectHashMap(); // handle -> name + private static List<NamedDisplay> openDisplayList = new ArrayList<NamedDisplay>(); // open, no close attempt + private static List<NamedDisplay> reusableDisplayList = new ArrayList<NamedDisplay>(); // close attempt, marked uncloseable, for reuse + private static List<NamedDisplay> pendingDisplayList = new ArrayList<NamedDisplay>(); // all open (close attempt or reusable) in creation order + private static final HashMap<String /* displayName */, Boolean> displayXineramaEnabledMap = new HashMap<String, Boolean>(); + + /** + * Cleanup resources. + * <p> + * Called by {@link NativeWindowFactory#shutdown()} + * </p> + * @see ToolkitProperties + */ + public static void shutdown() { + if(isInit) { + synchronized(X11Util.class) { + if(isInit) { + final boolean isJVMShuttingDown = NativeWindowFactory.isJVMShuttingDown() ; + if( DEBUG || + ( ( openDisplayMap.size() > 0 || reusableDisplayList.size() > 0 || pendingDisplayList.size() > 0 ) && + ( reusableDisplayList.size() != pendingDisplayList.size() || !markAllDisplaysUnclosable ) + ) ) { + System.err.println("X11Util.Display: Shutdown (JVM shutdown: "+isJVMShuttingDown+ + ", open (no close attempt): "+openDisplayMap.size()+"/"+openDisplayList.size()+ + ", reusable (open, marked uncloseable): "+reusableDisplayList.size()+ + ", pending (open in creation order): "+pendingDisplayList.size()+ + ")"); + if(DEBUG) { + Thread.dumpStack(); + } + if( openDisplayList.size() > 0) { + X11Util.dumpOpenDisplayConnections(); + } + if(DEBUG) { + if( reusableDisplayList.size() > 0 || pendingDisplayList.size() > 0 ) { + X11Util.dumpPendingDisplayConnections(); + } + } + } + + // Only at JVM shutdown time, since AWT impl. seems to + // dislike closing of X11 Display's (w/ ATI driver). + if( isJVMShuttingDown ) { + synchronized(globalLock) { + isInit = false; + closePendingDisplayConnections(); + openDisplayList.clear(); + reusableDisplayList.clear(); + pendingDisplayList.clear(); + openDisplayMap.clear(); + displayXineramaEnabledMap.clear(); + shutdown0(); + } + } + } + } + } } - public static synchronized boolean requiresToolkitLock() { - return requiresX11Lock; + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean requiresToolkitLock() { + return true; // JAWT locking: yes, instead of native X11 locking w use a recursive lock per display connection. + } + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean hasThreadingIssues() { + return hasThreadingIssues; // JOGL impl. may utilize special locking "somewhere" } public static void setX11ErrorHandler(boolean onoff, boolean quiet) { synchronized(setX11ErrorHandlerLock) { - if(onoff) { - if(0==setX11ErrorHandlerRecCount) { - setX11ErrorHandler0(true, quiet); - } - setX11ErrorHandlerRecCount++; - } else { - if(0 >= setX11ErrorHandlerRecCount) { - throw new InternalError(); - } - setX11ErrorHandlerRecCount--; - if(0==setX11ErrorHandlerRecCount) { - setX11ErrorHandler0(false, false); - } - } + setX11ErrorHandler0(onoff, quiet); } } public static String getNullDisplayName() { return nullDisplayName; } - + + public static void markAllDisplaysUnclosable() { + synchronized(globalLock) { + markAllDisplaysUnclosable = true; + for(int i=0; i<openDisplayList.size(); i++) { + openDisplayList.get(i).setUncloseable(true); + } + for(int i=0; i<reusableDisplayList.size(); i++) { + reusableDisplayList.get(i).setUncloseable(true); + } + for(int i=0; i<pendingDisplayList.size(); i++) { + pendingDisplayList.get(i).setUncloseable(true); + } + } + } + public static boolean getMarkAllDisplaysUnclosable() { return markAllDisplaysUnclosable; } - public static void setMarkAllDisplaysUnclosable(boolean v) { - markAllDisplaysUnclosable = v; - } - - private X11Util() {} - // not exactly thread safe, but good enough for our purpose, - // which is to tag a NamedDisplay uncloseable after creation. - private static Object globalLock = new Object(); - private static LongObjectHashMap openDisplayMap = new LongObjectHashMap(); // handle -> name - private static List<NamedDisplay> openDisplayList = new ArrayList<NamedDisplay>(); - private static List<NamedDisplay> pendingDisplayList = new ArrayList<NamedDisplay>(); + private X11Util() {} public static class NamedDisplay { final String name; @@ -201,22 +300,23 @@ public class X11Util { } } + @Override public final int hashCode() { return hash32; } - + + @Override public final boolean equals(Object obj) { if(this == obj) { return true; } if(obj instanceof NamedDisplay) { - NamedDisplay n = (NamedDisplay) obj; - return handle == n.handle; + return handle == ((NamedDisplay) obj).handle; } return false; } - + public final void addRef() { refCount++; } public final void removeRef() { refCount--; } - + public final String getName() { return name; } public final long getHandle() { return handle; } public final int getRefCount() { return refCount; } @@ -226,71 +326,32 @@ public class X11Util { public final Throwable getCreationStack() { return creationStack; } @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - @Override public String toString() { return "NamedX11Display["+name+", 0x"+Long.toHexString(handle)+", refCount "+refCount+", unCloseable "+unCloseable+"]"; } } - /** - * Cleanup resources. - * If <code>realXCloseOpenAndPendingDisplays</code> is <code>false</code>, - * keep alive all references (open display connection) for restart on same ClassLoader. - * - * @return number of unclosed X11 Displays.<br> - * @param realXCloseOpenAndPendingDisplays if true, {@link #closePendingDisplayConnections()} is called. - */ - public static int shutdown(boolean realXCloseOpenAndPendingDisplays, boolean verbose) { - int num=0; - if(DEBUG||verbose||pendingDisplayList.size() > 0) { - System.err.println("X11Util.Display: Shutdown (close open / pending Displays: "+realXCloseOpenAndPendingDisplays+ - ", open (no close attempt): "+openDisplayMap.size()+"/"+openDisplayList.size()+ - ", pending (not closed, marked uncloseable): "+pendingDisplayList.size()+")"); - if(DEBUG) { - Thread.dumpStack(); - } - if( openDisplayList.size() > 0) { - X11Util.dumpOpenDisplayConnections(); - } - if( pendingDisplayList.size() > 0 ) { - X11Util.dumpPendingDisplayConnections(); - } - } - - synchronized(globalLock) { - if(realXCloseOpenAndPendingDisplays) { - closePendingDisplayConnections(); - openDisplayList.clear(); - pendingDisplayList.clear(); - openDisplayMap.clear(); - shutdown0(); - } - } - return num; - } - /** - * Closing pending Display connections in reverse order. + * Closing pending Display connections in original creation order, if {@link #getMarkAllDisplaysUnclosable()} is true. * * @return number of closed Display connections */ - public static int closePendingDisplayConnections() { + private static int closePendingDisplayConnections() { int num=0; synchronized(globalLock) { - if(DEBUG) { - System.err.println("X11Util: Closing Pending X11 Display Connections: "+pendingDisplayList.size()); - } - for(int i=pendingDisplayList.size()-1; i>=0; i--) { - NamedDisplay ndpy = (NamedDisplay) pendingDisplayList.get(i); + if( getMarkAllDisplaysUnclosable() ) { + for(int i=0; i<pendingDisplayList.size(); i++) { + final NamedDisplay ndpy = (NamedDisplay) pendingDisplayList.get(i); + if(DEBUG) { + final boolean closeAttempted = !openDisplayMap.containsKey(ndpy.getHandle()); + System.err.println("X11Util.closePendingDisplayConnections(): Closing ["+i+"]: "+ndpy+" - closeAttempted "+closeAttempted); + } + XCloseDisplay(ndpy.getHandle()); + num++; + } if(DEBUG) { - System.err.println("X11Util.closePendingDisplayConnections(): Closing ["+i+"]: "+ndpy); + System.err.println("X11Util.closePendingDisplayConnections(): Closed "+num+" pending display connections"); } - XCloseDisplay(ndpy.getHandle()); - num++; } } return num; @@ -301,7 +362,7 @@ public class X11Util { return openDisplayList.size(); } } - + public static void dumpOpenDisplayConnections() { synchronized(globalLock) { System.err.println("X11Util: Open X11 Display Connections: "+openDisplayList.size()); @@ -317,7 +378,13 @@ public class X11Util { } } } - + + public static int getReusableDisplayConnectionNumber() { + synchronized(globalLock) { + return reusableDisplayList.size(); + } + } + public static int getPendingDisplayConnectionNumber() { synchronized(globalLock) { return pendingDisplayList.size(); @@ -326,7 +393,18 @@ public class X11Util { public static void dumpPendingDisplayConnections() { synchronized(globalLock) { - System.err.println("X11Util: Pending X11 Display Connections: "+pendingDisplayList.size()); + System.err.println("X11Util: Reusable X11 Display Connections: "+reusableDisplayList.size()); + for(int i=0; i<reusableDisplayList.size(); i++) { + NamedDisplay ndpy = (NamedDisplay) reusableDisplayList.get(i); + System.err.println("X11Util: Reusable["+i+"]: "+ndpy); + if(null!=ndpy) { + Throwable t = ndpy.getCreationStack(); + if(null!=t) { + t.printStackTrace(); + } + } + } + System.err.println("X11Util: Pending X11 Display Connections (creation order): "+pendingDisplayList.size()); for(int i=0; i<pendingDisplayList.size(); i++) { NamedDisplay ndpy = (NamedDisplay) pendingDisplayList.get(i); System.err.println("X11Util: Pending["+i+"]: "+ndpy); @@ -358,11 +436,11 @@ public class X11Util { NamedDisplay namedDpy = null; name = validateDisplayName(name); boolean reused = false; - - synchronized(globalLock) { - for(int i=0; i<pendingDisplayList.size(); i++) { - if(pendingDisplayList.get(i).getName().equals(name)) { - namedDpy = pendingDisplayList.remove(i); + + synchronized(globalLock) { + for(int i=0; i<reusableDisplayList.size(); i++) { + if(reusableDisplayList.get(i).getName().equals(name)) { + namedDpy = reusableDisplayList.remove(i); dpy = namedDpy.getHandle(); reused = true; break; @@ -376,6 +454,7 @@ public class X11Util { // if you like to debug and synchronize X11 commands .. // setSynchronizeDisplay(dpy, true); namedDpy = new NamedDisplay(name, dpy); + pendingDisplayList.add(namedDpy); } namedDpy.addRef(); openDisplayMap.put(dpy, namedDpy); @@ -392,10 +471,8 @@ public class X11Util { } public static void closeDisplay(long handle) { - NamedDisplay namedDpy; - synchronized(globalLock) { - namedDpy = (NamedDisplay) openDisplayMap.remove(handle); + final NamedDisplay namedDpy = (NamedDisplay) openDisplayMap.remove(handle); if(null==namedDpy) { X11Util.dumpPendingDisplayConnections(); throw new RuntimeException("X11Util.Display: Display(0x"+Long.toHexString(handle)+") with given handle is not mapped. Thread "+Thread.currentThread().getName()); @@ -404,20 +481,26 @@ public class X11Util { X11Util.dumpPendingDisplayConnections(); throw new RuntimeException("X11Util.Display: Display(0x"+Long.toHexString(handle)+") Mapping error: "+namedDpy+". Thread "+Thread.currentThread().getName()); } - + namedDpy.removeRef(); if(!openDisplayList.remove(namedDpy)) { throw new RuntimeException("Internal: "+namedDpy); } - - if(!namedDpy.isUncloseable()) { + + if( markAllDisplaysUnclosable ) { + // if set-mark 'slipped' this one .. just to be safe! + namedDpy.setUncloseable(true); + } + if( !namedDpy.isUncloseable() ) { XCloseDisplay(namedDpy.getHandle()); + pendingDisplayList.remove(namedDpy); } else { // for reuse - pendingDisplayList.add(namedDpy); + X11Lib.XSync(namedDpy.getHandle(), true); // flush output buffer and discard all events + reusableDisplayList.add(namedDpy); } - + if(DEBUG) { System.err.println("X11Util.Display: Closed (real: "+(!namedDpy.isUncloseable())+") "+namedDpy+". Thread "+Thread.currentThread().getName()); - } + } } } @@ -427,7 +510,7 @@ public class X11Util { } } - /** + /** * @return If name is null, it returns the previous queried NULL display name, * otherwise the name. */ public static String validateDisplayName(String name) { @@ -448,50 +531,60 @@ public class X11Util { *******************************/ public static long XOpenDisplay(String arg0) { - NativeWindowFactory.getDefaultToolkitLock().lock(); - try { - long handle = X11Lib.XOpenDisplay(arg0); - if(TRACE_DISPLAY_LIFECYCLE) { - System.err.println(Thread.currentThread()+" - X11Util.XOpenDisplay("+arg0+") 0x"+Long.toHexString(handle)); - // Thread.dumpStack(); - } - return handle; - } finally { - NativeWindowFactory.getDefaultToolkitLock().unlock(); + long handle = X11Lib.XOpenDisplay(arg0); + if(XSYNC_ENABLED && 0 != handle) { + X11Lib.XSynchronize(handle, true); + } + if(TRACE_DISPLAY_LIFECYCLE) { + System.err.println(Thread.currentThread()+" - X11Util.XOpenDisplay("+arg0+") 0x"+Long.toHexString(handle)); + // Thread.dumpStack(); } + return handle; } public static int XCloseDisplay(long display) { - NativeWindowFactory.getDefaultToolkitLock().lock(); + if(TRACE_DISPLAY_LIFECYCLE) { + System.err.println(Thread.currentThread()+" - X11Util.XCloseDisplay() 0x"+Long.toHexString(display)); + // Thread.dumpStack(); + } + int res = -1; try { - if(TRACE_DISPLAY_LIFECYCLE) { - System.err.println(Thread.currentThread()+" - X11Util.XCloseDisplay() 0x"+Long.toHexString(display)); - // Thread.dumpStack(); - } - int res = -1; - X11Util.setX11ErrorHandler(true, DEBUG ? false : true); - try { - res = X11Lib.XCloseDisplay(display); - } catch (Exception ex) { - System.err.println("X11Util: Catched Exception:"); - ex.printStackTrace(); - } finally { - X11Util.setX11ErrorHandler(false, false); - } - return res; - } finally { - NativeWindowFactory.getDefaultToolkitLock().unlock(); + res = X11Lib.XCloseDisplay(display); + } catch (Exception ex) { + System.err.println("X11Util: Catched Exception:"); + ex.printStackTrace(); } + return res; } static volatile boolean XineramaFetched = false; static long XineramaLibHandle = 0; static long XineramaQueryFunc = 0; - - public static boolean XineramaIsEnabled(long display) { - if(0==display) { - throw new IllegalArgumentException("Display NULL"); + + public static boolean XineramaIsEnabled(X11GraphicsDevice device) { + if(null == device) { + throw new IllegalArgumentException("X11 Display device is NULL"); + } + device.lock(); + try { + return XineramaIsEnabled(device.getHandle()); + } finally { + device.unlock(); + } + } + + public static boolean XineramaIsEnabled(long displayHandle) { + if( 0 == displayHandle ) { + throw new IllegalArgumentException("X11 Display handle is NULL"); } + final String displayName = X11Lib.XDisplayString(displayHandle); + synchronized(displayXineramaEnabledMap) { + final Boolean b = displayXineramaEnabledMap.get(displayName); + if(null != b) { + return b.booleanValue(); + } + } + final boolean res; if(!XineramaFetched) { // volatile: ok synchronized(X11Util.class) { if( !XineramaFetched ) { @@ -504,19 +597,27 @@ public class X11Util { } } if(0!=XineramaQueryFunc) { - final boolean res = X11Lib.XineramaIsEnabled(XineramaQueryFunc, display); + res = X11Lib.XineramaIsEnabled(XineramaQueryFunc, displayHandle); + } else { if(DEBUG) { - System.err.println("XineramaIsEnabled: "+res); + System.err.println("XineramaIsEnabled: Couldn't bind to Xinerama - lib 0x"+Long.toHexString(XineramaLibHandle)+ + "query 0x"+Long.toHexString(XineramaQueryFunc)); } - return res; - } else if(DEBUG) { - System.err.println("XineramaIsEnabled: Couldn't bind to Xinerama - lib 0x"+Long.toHexString(XineramaLibHandle)+ - "query 0x"+Long.toHexString(XineramaQueryFunc)); + res = false; } - return false; + synchronized(displayXineramaEnabledMap) { + if(DEBUG) { + System.err.println("XineramaIsEnabled Cache: Display "+displayName+" (0x"+Long.toHexString(displayHandle)+") -> "+res); + } + displayXineramaEnabledMap.put(displayName, Boolean.valueOf(res)); + } + return res; } - - private static native boolean initialize0(boolean firstUIActionOnProcess); + + private static final String getCurrentThreadName() { return Thread.currentThread().getName(); } // Callback for JNI + private static final void dumpStack() { Thread.dumpStack(); } // Callback for JNI + + private static native boolean initialize0(boolean debug); private static native void shutdown0(); private static native void setX11ErrorHandler0(boolean onoff, boolean quiet); } diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/awt/X11AWTGraphicsConfigurationFactory.java b/src/nativewindow/classes/jogamp/nativewindow/x11/awt/X11AWTGraphicsConfigurationFactory.java index b6bf63d44..062040232 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/awt/X11AWTGraphicsConfigurationFactory.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/awt/X11AWTGraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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 @@ -59,16 +59,17 @@ import jogamp.nativewindow.x11.X11Lib; import jogamp.nativewindow.x11.X11Util; public class X11AWTGraphicsConfigurationFactory extends GraphicsConfigurationFactory { - + public static void registerFactory() { - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.awt.AWTGraphicsDevice.class, new X11AWTGraphicsConfigurationFactory()); - } + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.awt.AWTGraphicsDevice.class, CapabilitiesImmutable.class, new X11AWTGraphicsConfigurationFactory()); + } private X11AWTGraphicsConfigurationFactory() { } + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { + CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID) { if (absScreen != null && !(absScreen instanceof AWTGraphicsScreen)) { throw new IllegalArgumentException("This GraphicsConfigurationFactory accepts only AWTGraphicsScreen objects"); @@ -77,21 +78,21 @@ public class X11AWTGraphicsConfigurationFactory extends GraphicsConfigurationFac absScreen = AWTGraphicsScreen.createDefault(); } - return chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, (AWTGraphicsScreen)absScreen); + return chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, (AWTGraphicsScreen)absScreen, nativeVisualID); } - + public static AWTGraphicsConfiguration chooseGraphicsConfigurationStatic( CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, AWTGraphicsScreen awtScreen) { + CapabilitiesChooser chooser, AWTGraphicsScreen awtScreen, int nativeVisualID) { if(DEBUG) { System.err.println("X11AWTGraphicsConfigurationFactory: got "+awtScreen); } - + final GraphicsDevice device = ((AWTGraphicsDevice)awtScreen.getDevice()).getGraphicsDevice(); final long displayHandleAWT = X11SunJDKReflection.graphicsDeviceGetDisplay(device); final long displayHandle; - boolean owner = false; + final boolean owner; if(0==displayHandleAWT) { displayHandle = X11Util.openDisplay(null); owner = true; @@ -101,8 +102,8 @@ public class X11AWTGraphicsConfigurationFactory extends GraphicsConfigurationFac } else { /** * Using the AWT display handle works fine with NVidia. - * However we experienced different results w/ AMD drivers, - * some work, but some behave erratic. + * However we experienced different results w/ AMD drivers, + * some work, but some behave erratic. * I.e. hangs in XQueryExtension(..) via X11GraphicsScreen. */ final String displayName = X11Lib.XDisplayString(displayHandleAWT); @@ -112,17 +113,16 @@ public class X11AWTGraphicsConfigurationFactory extends GraphicsConfigurationFac System.err.println(getThreadName()+" - X11AWTGraphicsConfigurationFactory: AWT dpy "+displayName+" / "+toHexString(displayHandleAWT)+", create X11 display "+toHexString(displayHandle)); } } - final ToolkitLock lock = owner ? - NativeWindowFactory.getDefaultToolkitLock(NativeWindowFactory.TYPE_AWT) : // own non-shared X11 display connection, no X11 lock - NativeWindowFactory.createDefaultToolkitLock(NativeWindowFactory.TYPE_X11, NativeWindowFactory.TYPE_AWT, displayHandle); - final X11GraphicsDevice x11Device = new X11GraphicsDevice(displayHandle, AbstractGraphicsDevice.DEFAULT_UNIT, lock, owner); + // Global JAWT lock required - No X11 resource locking due to private display connection + final ToolkitLock lock = NativeWindowFactory.getDefaultToolkitLock(NativeWindowFactory.TYPE_AWT); + final X11GraphicsDevice x11Device = new X11GraphicsDevice(displayHandle, AbstractGraphicsDevice.DEFAULT_UNIT, lock, owner); final X11GraphicsScreen x11Screen = new X11GraphicsScreen(x11Device, awtScreen.getIndex()); if(DEBUG) { System.err.println("X11AWTGraphicsConfigurationFactory: made "+x11Screen); } - - final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(x11Device); - AbstractGraphicsConfiguration aConfig = factory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, x11Screen); + + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(x11Device, capsChosen); + AbstractGraphicsConfiguration aConfig = factory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, x11Screen, nativeVisualID); if (aConfig == null) { throw new NativeWindowException("Unable to choose a GraphicsConfiguration (1): "+capsChosen+",\n\t"+chooser+"\n\t"+x11Screen); } @@ -130,7 +130,7 @@ public class X11AWTGraphicsConfigurationFactory extends GraphicsConfigurationFac System.err.println("X11AWTGraphicsConfigurationFactory: chosen config: "+aConfig); // Thread.dumpStack(); } - + // // Match the X11/GL Visual with AWT: // - choose a config AWT agnostic and then @@ -138,7 +138,7 @@ public class X11AWTGraphicsConfigurationFactory extends GraphicsConfigurationFac // // The resulting GraphicsConfiguration has to be 'forced' on the AWT native peer, // ie. returned by GLCanvas's getGraphicsConfiguration() befor call by super.addNotify(). - // + // final GraphicsConfiguration[] configs = device.getConfigurations(); int visualID = aConfig.getVisualID(VIDType.NATIVE); if(VisualIDHolder.VID_UNDEFINED != visualID) { @@ -160,7 +160,7 @@ public class X11AWTGraphicsConfigurationFactory extends GraphicsConfigurationFac // try again using an AWT Colormodel compatible configuration GraphicsConfiguration gc = device.getDefaultConfiguration(); capsChosen = AWTGraphicsConfiguration.setupCapabilitiesRGBABits(capsChosen, gc); - aConfig = factory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, x11Screen); + aConfig = factory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, x11Screen, nativeVisualID); if (aConfig == null) { throw new NativeWindowException("Unable to choose a GraphicsConfiguration (2): "+capsChosen+",\n\t"+chooser+"\n\t"+x11Screen); } |