jogamp.nativewindow.x11.X11Util Maven / Gradle / Ivy
Show all versions of jogl-all Show documentation
/*
* 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
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
package jogamp.nativewindow.x11;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.nativewindow.NativeWindowException;
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 implements ToolkitProperties {
public static final boolean DEBUG = Debug.debug("X11Util");
/**
* See Bug 515 - https://jogamp.org/bugzilla/show_bug.cgi?id=515
*
* It is observed that ATI X11 drivers, eg.
*
* - fglrx 8.78.6,
* - fglrx 11.08/8.881 and
* - fglrx 11.11/8.911
*
* are quite sensitive to multiple Display connections.
*
*
* With the above drivers closing displays shall happen in the same order as
* they were opened, or shall not be closed at all!
* If closed, some driver related bug appears and brings down the JVM.
*
*
* You may test this, ie just reverse the destroy order below.
* See also native test: jogl/test-native/displayMultiple02.c
*
*
* Workaround is to not close them at all if driver vendor is ATI
* during operation.
*
*
* With ATI X11 drivers all connections must be closed at JVM shutdown,
* otherwise a SIGSEGV (after JVM safepoint) will be caused.
*
*/
public static final boolean ATI_HAS_XCLOSEDISPLAY_BUG = !Debug.isPropertyDefined("nativewindow.debug.X11Util.ATI_HAS_NO_XCLOSEDISPLAY_BUG", 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 volatile boolean isInit = false;
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 final Object setX11ErrorHandlerLock = new Object();
private static final String X11_EXTENSION_ATIFGLRXDRI = "ATIFGLRXDRI";
private static final String X11_EXTENSION_ATIFGLEXTENSION = "ATIFGLEXTENSION";
/**
* 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 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.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();
}
}
}
}
}
// 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 openDisplayList = new ArrayList(); // open, no close attempt
private static List reusableDisplayList = new ArrayList(); // close attempt, marked uncloseable, for reuse
private static List pendingDisplayList = new ArrayList(); // all open (close attempt or reusable) in creation order
private static final HashMap displayXineramaEnabledMap = new HashMap();
/**
* Cleanup resources.
*
* Called by {@link NativeWindowFactory#shutdown()}
*
* @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();
}
}
}
}
}
}
/**
* 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(final boolean onoff, final boolean quiet) {
synchronized(setX11ErrorHandlerLock) {
setX11ErrorHandler0(onoff, quiet);
}
}
public static String getNullDisplayName() {
return nullDisplayName;
}
public static void markAllDisplaysUnclosable() {
synchronized(globalLock) {
markAllDisplaysUnclosable = true;
for(int i=0; i>> 32 ) ; // hi
hash32 = h32;
}
if(DEBUG) {
this.creationStack=new Throwable("NamedDisplay Created at:");
} else {
this.creationStack=null;
}
}
@Override
public final int hashCode() {
return hash32;
}
@Override
public final boolean equals(final Object obj) {
if(this == obj) { return true; }
if(obj instanceof NamedDisplay) {
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; }
public final void setUncloseable(final boolean v) { unCloseable = v; }
public final boolean isUncloseable() { return unCloseable; }
public final Throwable getCreationStack() { return creationStack; }
@Override
public String toString() {
return "NamedX11Display["+name+", 0x"+Long.toHexString(handle)+", refCount "+refCount+", unCloseable "+unCloseable+"]";
}
}
/**
* Closing pending Display connections in original creation order, if {@link #getMarkAllDisplaysUnclosable()} is true.
*
* @return number of closed Display connections
*/
private static int closePendingDisplayConnections() {
int num=0;
synchronized(globalLock) {
if( getMarkAllDisplaysUnclosable() ) {
for(int i=0; i "+res);
}
displayXineramaEnabledMap.put(displayName, Boolean.valueOf(res));
}
return res;
}
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);
}