Cleaned up how SWT/JavaFX work with regards to detection and distpatch.
This commit is contained in:
parent
d58812acaf
commit
9f15411205
@ -16,19 +16,18 @@
|
|||||||
package dorkbox.util;
|
package dorkbox.util;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.sun.jna.JNIEnv;
|
||||||
import com.sun.jna.Library;
|
import com.sun.jna.Library;
|
||||||
import com.sun.jna.Native;
|
import com.sun.jna.Native;
|
||||||
import com.sun.jna.NativeLibrary;
|
import com.sun.jna.NativeLibrary;
|
||||||
import com.sun.jna.Pointer;
|
|
||||||
import com.sun.jna.Structure;
|
|
||||||
import com.sun.jna.ptr.PointerByReference;
|
|
||||||
import com.sun.jna.win32.StdCallFunctionMapper;
|
import com.sun.jna.win32.StdCallFunctionMapper;
|
||||||
|
|
||||||
|
// http://hg.openjdk.java.net/jdk/jdk10/file/b09e56145e11/src/java.base/share/native/libjava/ClassLoader.c
|
||||||
// http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/native/libjava/ClassLoader.c
|
// http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/native/libjava/ClassLoader.c
|
||||||
// http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/65464a307408/src/java.base/share/native/libjava/ClassLoader.c
|
// http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/65464a307408/src/java.base/share/native/libjava/ClassLoader.c
|
||||||
// http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/be698ac28848/src/share/native/java/lang/ClassLoader.c
|
// http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/be698ac28848/src/share/native/java/lang/ClassLoader.c
|
||||||
@ -44,175 +43,185 @@ import com.sun.jna.win32.StdCallFunctionMapper;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class ClassLoaderUtil {
|
public class ClassLoaderUtil {
|
||||||
|
private static JVM libjvm;
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "unused"})
|
// Note: this does not work in java8 x86 *on windows XP windows7, etc. It only works on x64
|
||||||
public static class Bootstrap {
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
public static final int JNI_VERSION_1_1 = 0x00010001;
|
public
|
||||||
public static final int JNI_VERSION_1_2 = 0x00010002;
|
interface JVM extends com.sun.jna.Library {
|
||||||
public static final int JNI_VERSION_1_4 = 0x00010004;
|
void JVM_DefineClass(JNIEnv env, String name, Object classLoader, byte[] buffer, int length, Object protectionDomain);
|
||||||
public static final int JNI_VERSION_1_6 = 0x00010006;
|
Class JVM_FindLoadedClass(JNIEnv env, Object classLoader, String name);
|
||||||
public static final int JNI_VERSION_1_7 = 0x00010007;
|
}
|
||||||
public static final int JNI_VERSION_1_8 = 0x00010008;
|
|
||||||
|
|
||||||
// if we want to change the JNI version, this is how we do it.
|
private static final String libName;
|
||||||
public static int JNI_VERSION = JNI_VERSION_1_4;
|
static {
|
||||||
|
if (OS.isMacOsX()) {
|
||||||
private static JVM libjvm;
|
if (OS.javaVersion < 7) {
|
||||||
|
libName = "JavaVM";
|
||||||
public static
|
|
||||||
class JavaVM extends Structure {
|
|
||||||
public static
|
|
||||||
class ByReference extends JavaVM implements Structure.ByReference {}
|
|
||||||
|
|
||||||
public volatile JNIInvokeInterface.ByReference functions;
|
|
||||||
|
|
||||||
JavaVM() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
List<String> getFieldOrder() {
|
|
||||||
//noinspection ArraysAsListWithZeroOrOneArgument
|
|
||||||
return Arrays.asList("functions");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: this does not work in java8 x86 *on windows XP windows7, etc. It only works on x64
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
|
||||||
public
|
|
||||||
interface JVM extends com.sun.jna.Library {
|
|
||||||
void JVM_DefineClass(Pointer env, String name, Object loader, byte[] buffer, int length, Object protectionDomain);
|
|
||||||
int JNI_GetCreatedJavaVMs(JavaVM.ByReference[] vmArray, int bufsize, int[] vmCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public static
|
|
||||||
class JNIInvokeInterface extends Structure {
|
|
||||||
public static
|
|
||||||
class ByReference extends JNIInvokeInterface implements Structure.ByReference {}
|
|
||||||
|
|
||||||
public volatile Pointer reserved0;
|
|
||||||
public volatile Pointer reserved1;
|
|
||||||
public volatile Pointer reserved2;
|
|
||||||
|
|
||||||
public volatile Pointer DestroyJavaVM;
|
|
||||||
public volatile Pointer AttachCurrentThread;
|
|
||||||
public volatile Pointer DetachCurrentThread;
|
|
||||||
|
|
||||||
public volatile GetEnv GetEnv;
|
|
||||||
public volatile Pointer AttachCurrentThreadAsDaemon;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
List getFieldOrder() {
|
|
||||||
return Arrays.asList("reserved0",
|
|
||||||
"reserved1",
|
|
||||||
"reserved2",
|
|
||||||
"DestroyJavaVM",
|
|
||||||
"AttachCurrentThread",
|
|
||||||
"DetachCurrentThread",
|
|
||||||
"GetEnv",
|
|
||||||
"AttachCurrentThreadAsDaemon");
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
interface GetEnv extends com.sun.jna.Callback {
|
|
||||||
int callback(JavaVM.ByReference vm, PointerByReference penv, int version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
String libName;
|
|
||||||
if (OS.isMacOsX()) {
|
|
||||||
if (OS.javaVersion < 7) {
|
|
||||||
libName = "JavaVM";
|
|
||||||
} else {
|
|
||||||
String javaLocation = System.getProperty("java.home");
|
|
||||||
|
|
||||||
// have to explicitly specify the JVM library via full path
|
|
||||||
// this is OK, because for java on MacOSX, this is the only location it can exist
|
|
||||||
libName = javaLocation + "/lib/server/libjvm.dylib";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
libName = "jvm";
|
|
||||||
}
|
|
||||||
|
|
||||||
// function name is SLIGHTLY different on windows x32 java builds.
|
|
||||||
// For actual name use: http://www.nirsoft.net/utils/dll_export_viewer.html
|
|
||||||
if (OS.isWindows() && OS.is32bit()) {
|
|
||||||
Map options = new HashMap();
|
|
||||||
options.put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
String getFunctionName(NativeLibrary library, Method method) {
|
|
||||||
String methodName = method.getName();
|
|
||||||
if (methodName.equals("JVM_DefineClass")) {
|
|
||||||
// specifically Oracle Java 32bit builds. Tested on XP and Win7
|
|
||||||
return "_JVM_DefineClass@24";
|
|
||||||
}
|
|
||||||
return methodName;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
libjvm = Native.loadLibrary(libName, JVM.class, options);
|
|
||||||
} else {
|
} else {
|
||||||
libjvm = Native.loadLibrary(libName, JVM.class);
|
String javaLocation = System.getProperty("java.home");
|
||||||
|
|
||||||
|
// have to explicitly specify the JVM library via full path
|
||||||
|
// this is OK, because for java on MacOSX, this is the only location it can exist
|
||||||
|
libName = javaLocation + "/lib/server/libjvm.dylib";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
libName = "jvm";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject class bytes directly into the bootstrap classloader.
|
|
||||||
* <p>
|
|
||||||
* This is a VERY DANGEROUS method to use!
|
|
||||||
*
|
|
||||||
* @param classBytes
|
|
||||||
* the bytes to inject
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
void defineClass(byte[] classBytes) throws Exception {
|
|
||||||
// get the number of JVM's running
|
|
||||||
int[] jvmCount = {100};
|
|
||||||
libjvm.JNI_GetCreatedJavaVMs(null, 0, jvmCount);
|
|
||||||
|
|
||||||
// actually get the JVM's
|
// function name is SLIGHTLY different on windows x32 java builds.
|
||||||
JavaVM.ByReference[] vms = new JavaVM.ByReference[jvmCount[0]];
|
// For actual name use: http://www.nirsoft.net/utils/dll_export_viewer.html
|
||||||
for (int i = 0, vmsLength = vms.length; i < vmsLength; i++) {
|
Map options = new HashMap();
|
||||||
vms[i] = new JavaVM.ByReference();
|
options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);
|
||||||
}
|
|
||||||
|
|
||||||
// now get the JVM's
|
if (OS.isWindows() && OS.is32bit()) {
|
||||||
libjvm.JNI_GetCreatedJavaVMs(vms, vms.length, jvmCount);
|
options.put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper() {
|
||||||
|
@Override
|
||||||
Exception exception = null;
|
public
|
||||||
for (int i = 0; i < jvmCount[0]; ++i) {
|
String getFunctionName(NativeLibrary library, Method method) {
|
||||||
JavaVM.ByReference vm = vms[i];
|
String methodName = method.getName();
|
||||||
PointerByReference penv = new PointerByReference();
|
if (methodName.equals("JVM_DefineClass")) {
|
||||||
vm.functions.GetEnv.callback(vm, penv, ClassLoaderUtil.Bootstrap.JNI_VERSION);
|
// specifically Oracle Java 32bit builds. Tested on XP and Win7
|
||||||
|
return "_JVM_DefineClass@24";
|
||||||
// inject into all JVM's that are started by us (is USUALLY 1, but not always)
|
}
|
||||||
try {
|
return methodName;
|
||||||
libjvm.JVM_DefineClass(penv.getValue(), null, null, classBytes, classBytes.length, null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
exception = e;
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
libjvm = Native.load(libName, JVM.class, options);
|
||||||
// something failed, just show us THE LAST of the failures
|
} else {
|
||||||
if (exception != null) {
|
libjvm = Native.load(libName, JVM.class, options);
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a class in the current threads class-loader
|
* Defines a class in the current threads class-loader
|
||||||
* @param bytes the bytes of the class to define
|
*
|
||||||
|
* @param classBytes the bytes of the class to define
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
void defineClass(final byte[] bytes) throws Exception {
|
void defineClass(final byte[] classBytes) throws Exception {
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
|
||||||
defineClass.setAccessible(true);
|
defineClass(classLoader, classBytes);
|
||||||
defineClass.invoke(classLoader, bytes, 0, bytes.length);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject class bytes directly into the bootstrap classloader.
|
||||||
|
* <p>
|
||||||
|
* This is a VERY DANGEROUS method to use!
|
||||||
|
*
|
||||||
|
* @param classLoader the classLoader to use. null will use the BOOTSTRAP classloader
|
||||||
|
* @param classBytes the bytes to inject
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
void defineClass(ClassLoader classLoader, byte[] classBytes) throws Exception {
|
||||||
|
// inject into the FIRST JVM that are started by us (is USUALLY 1, but not always)
|
||||||
|
libjvm.JVM_DefineClass(JNIEnv.CURRENT, null, classLoader, classBytes, classBytes.length, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if a class is already loaded or not, WITHOUT linking/loading the class!
|
||||||
|
*
|
||||||
|
* You should check all accessible classLoaders!
|
||||||
|
*
|
||||||
|
* @param classLoader the classLoader to use. null will use the BOOTSTRAP classloader
|
||||||
|
* @param className the name to check
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
boolean isClassLoaded(ClassLoader classLoader, String className) {
|
||||||
|
// Pointer javaEnv = getJavaEnv();
|
||||||
|
//
|
||||||
|
// NativeLibrary library = ((Library.Handler) libjvm).getNativeLibrary();
|
||||||
|
// Function jvm_findLoadedClass = library.getFunction("JVM_FindLoadedClass");
|
||||||
|
// jvm_findLoadedClass.invokeObject()
|
||||||
|
|
||||||
|
// this.peer = library.getSymbolAddress(functionName);
|
||||||
|
|
||||||
|
|
||||||
|
// result = Native.invokeObject(this, this.peer, callFlags, args);
|
||||||
|
// jstring NewStringUTF(JNIEnv *env, const char *bytes);
|
||||||
|
// libjvm.JVM_FindLoadedClass(JNIEnv.CURRENT, classLoader, new JString(className));
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// if (OS.javaVersion < 9) {
|
||||||
|
// // so we can use reflection to check, but only if we are < java9
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// // if we are java9+, we are "screwed" in that we cannot use reflection to tell if any of the SWT classes are ALREADY loaded.
|
||||||
|
//
|
||||||
|
// // // there are problems
|
||||||
|
// //
|
||||||
|
// // Class c = new ClassLoader() {
|
||||||
|
// // Class c = findLoadedClass("org.eclipse.swt.widgets.Display");
|
||||||
|
// //
|
||||||
|
// // }.c;
|
||||||
|
// //
|
||||||
|
// // // isSwtLoaded_ = SWT.isLoadable() && c != null;
|
||||||
|
// // isSwtLoaded_= null != Class.forName("org.eclipse.swt.SWTError", false, Swt.class.getClassLoader());
|
||||||
|
// }
|
||||||
|
// // isSwtLoaded_= null != Class.forName("org.eclipse.swt.widgets.Display", false, Swt.class.getClassLoader());
|
||||||
|
//
|
||||||
|
// // final String SWT_INTERNAL_CLASS = "org.eclipse.swt.internal.gtk.OS";
|
||||||
|
//
|
||||||
|
// // FindClassLoader cl = (FindClassLoader) FindClassLoader.class.getClassLoader();
|
||||||
|
// // isSwtLoaded_= null != FindClassLoader.find(cl, "org.eclipse.swt.widgets.Display");
|
||||||
|
// } catch (Throwable e) {
|
||||||
|
// LoggerFactory.getLogger(Swt.class).debug("Error detecting if SWT is loaded", e);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
Boolean run() {
|
||||||
|
try {
|
||||||
|
Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||||
|
m.setAccessible(true);
|
||||||
|
|
||||||
|
return null != m.invoke(classLoader, className);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if a class is already loaded or not.
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
boolean isLibraryLoaded(String libraryName) {
|
||||||
|
// JVM_FindLoadedClass
|
||||||
|
|
||||||
|
// JVM_FindLibraryEntry
|
||||||
|
|
||||||
|
// /*
|
||||||
|
// * Class: java_lang_ClassLoader_NativeLibrary
|
||||||
|
// * Method: find
|
||||||
|
// * Signature: (Ljava/lang/String;)J
|
||||||
|
// */
|
||||||
|
// JNIEXPORT jlong JNICALL
|
||||||
|
// Java_java_lang_ClassLoader_00024NativeLibrary_find
|
||||||
|
// (JNIEnv *env, jobject this, jstring name)
|
||||||
|
// {
|
||||||
|
// jlong handle;
|
||||||
|
// const char *cname;
|
||||||
|
// jlong res;
|
||||||
|
//
|
||||||
|
// if (!initIDs(env))
|
||||||
|
// return jlong_zero;
|
||||||
|
//
|
||||||
|
// handle = (*env)->GetLongField(env, this, handleID);
|
||||||
|
// cname = (*env)->GetStringUTFChars(env, name, 0);
|
||||||
|
// if (cname == 0)
|
||||||
|
// return jlong_zero;
|
||||||
|
// res = ptr_to_jlong(JVM_FindLibraryEntry(jlong_to_ptr(handle), cname));
|
||||||
|
// (*env)->ReleaseStringUTFChars(env, name, cname);
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util;
|
|
||||||
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility methods for JavaFX.
|
|
||||||
* <p>
|
|
||||||
* We use reflection for these methods so that we can compile everything under Java 1.6 (which doesn't have JavaFX).
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
class JavaFX {
|
|
||||||
public final static boolean isLoaded;
|
|
||||||
public final static boolean isGtk3;
|
|
||||||
|
|
||||||
// Methods are cached for performance
|
|
||||||
private static final Method dispatchMethod;
|
|
||||||
private static final Method isEventThreadMethod;
|
|
||||||
private static final Object isEventThreadObject;
|
|
||||||
|
|
||||||
|
|
||||||
static {
|
|
||||||
boolean isJavaFxLoaded_ = false;
|
|
||||||
boolean isJavaFxGtk3_ = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// this is important to use reflection, because if JavaFX is not being used, calling getToolkit() will initialize it...
|
|
||||||
java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
|
||||||
m.setAccessible(true);
|
|
||||||
ClassLoader cl = JavaFX.class.getClassLoader();
|
|
||||||
|
|
||||||
// JavaFX Java7,8 is GTK2 only. Java9 can have it be GTK3 if -Djdk.gtk.version=3 is specified
|
|
||||||
// see http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
|
|
||||||
isJavaFxLoaded_ = (null != m.invoke(cl, "com.sun.javafx.tk.Toolkit")) || (null != m.invoke(cl, "javafx.application.Application"));
|
|
||||||
|
|
||||||
if (isJavaFxLoaded_) {
|
|
||||||
// JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified
|
|
||||||
// see
|
|
||||||
// http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
|
|
||||||
// https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
|
|
||||||
// from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
|
|
||||||
|
|
||||||
isJavaFxGtk3_ = OS.javaVersion >= 9 && System.getProperty("jdk.gtk.version", "2").equals("3");
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LoggerFactory.getLogger(JavaFX.class).debug("Error detecting if JavaFX is loaded", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
isLoaded = isJavaFxLoaded_;
|
|
||||||
isGtk3 = isJavaFxGtk3_;
|
|
||||||
|
|
||||||
|
|
||||||
Method _isEventThreadMethod = null;
|
|
||||||
Method _dispatchMethod = null;
|
|
||||||
Object _isEventThreadObject = null;
|
|
||||||
|
|
||||||
if (isJavaFxLoaded_) {
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName("javafx.application.Platform");
|
|
||||||
_dispatchMethod = clazz.getMethod("runLater", Runnable.class);
|
|
||||||
|
|
||||||
// JAVA 7
|
|
||||||
// javafx.application.Platform.isFxApplicationThread();
|
|
||||||
|
|
||||||
// JAVA 8
|
|
||||||
// com.sun.javafx.tk.Toolkit.getToolkit().isFxUserThread();
|
|
||||||
if (OS.javaVersion <= 7) {
|
|
||||||
clazz = Class.forName("javafx.application.Platform");
|
|
||||||
_isEventThreadMethod = clazz.getMethod("isFxApplicationThread");
|
|
||||||
_isEventThreadObject = null;
|
|
||||||
} else {
|
|
||||||
clazz = Class.forName("com.sun.javafx.tk.Toolkit");
|
|
||||||
_isEventThreadMethod = clazz.getMethod("getToolkit");
|
|
||||||
|
|
||||||
_isEventThreadObject = _isEventThreadMethod.invoke(null);
|
|
||||||
_isEventThreadMethod = _isEventThreadObject.getClass().getMethod("isFxUserThread", (java.lang.Class<?>[])null);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LoggerFactory.getLogger(JavaFX.class).error("Cannot initialize JavaFX", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchMethod = _dispatchMethod;
|
|
||||||
isEventThreadMethod = _isEventThreadMethod;
|
|
||||||
isEventThreadObject = _isEventThreadObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
void dispatch(final Runnable runnable) {
|
|
||||||
// javafx.application.Platform.runLater(runnable);
|
|
||||||
|
|
||||||
try {
|
|
||||||
dispatchMethod.invoke(null, runnable);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LoggerFactory.getLogger(JavaFX.class)
|
|
||||||
.error("Unable to execute JavaFX runLater(). Please create an issue with your OS and Java " +
|
|
||||||
"version so we may further investigate this issue.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
|
||||||
public static
|
|
||||||
boolean isEventThread() {
|
|
||||||
// JAVA 7
|
|
||||||
// javafx.application.Platform.isFxApplicationThread();
|
|
||||||
|
|
||||||
// JAVA 8
|
|
||||||
// com.sun.javafx.tk.Toolkit.getToolkit().isFxUserThread();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (OS.javaVersion <= 7) {
|
|
||||||
return (Boolean) isEventThreadMethod.invoke(null);
|
|
||||||
} else {
|
|
||||||
return (Boolean) isEventThreadMethod.invoke(isEventThreadObject);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LoggerFactory.getLogger(JavaFX.class)
|
|
||||||
.error("Unable to check if JavaFX is in the event thread. Please create an issue with your OS and Java " +
|
|
||||||
"version so we may further investigate this issue.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
void onShutdown(final Runnable runnable) {
|
|
||||||
// com.sun.javafx.tk.Toolkit.getToolkit()
|
|
||||||
// .addShutdownHook(runnable);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName("com.sun.javafx.tk.Toolkit");
|
|
||||||
Method method = clazz.getMethod("getToolkit");
|
|
||||||
Object o = method.invoke(null);
|
|
||||||
Method m = o.getClass().getMethod("addShutdownHook", Runnable.class);
|
|
||||||
m.invoke(o, runnable);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LoggerFactory.getLogger(JavaFX.class)
|
|
||||||
.error("Unable to insert shutdown hook into JavaFX. Please create an issue with your OS and Java " +
|
|
||||||
"version so we may further investigate this issue.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util;
|
|
||||||
|
|
||||||
import org.eclipse.swt.SWT;
|
|
||||||
import org.eclipse.swt.widgets.Display;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility methods for SWT. Some of the methods will be overwritten via Javassist so we don't require a hard dependency on SWT.
|
|
||||||
* <p>
|
|
||||||
* SWT system tray types are GtkStatusIcon trays (so we don't want to use them)
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("Convert2Lambda")
|
|
||||||
public
|
|
||||||
class Swt {
|
|
||||||
public final static boolean isLoaded;
|
|
||||||
public final static boolean isGtk3;
|
|
||||||
|
|
||||||
private static final Display currentDisplay;
|
|
||||||
private static final Thread currentDisplayThread;
|
|
||||||
|
|
||||||
private static final int version;
|
|
||||||
|
|
||||||
static {
|
|
||||||
boolean isSwtLoaded_ = false;
|
|
||||||
boolean isSwtGtk3_ = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// maybe we should load the SWT version? (In order for us to work with SWT, BOTH must be the same!!
|
|
||||||
// SWT is GTK2, but if -DSWT_GTK3=1 is specified, it can be GTK3
|
|
||||||
isSwtLoaded_ = null != Class.forName("org.eclipse.swt.widgets.Display");
|
|
||||||
|
|
||||||
if (isSwtLoaded_) {
|
|
||||||
// Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to
|
|
||||||
// System.setProperty("SWT_GTK3", "0"); (or -DSWT_GTK3=1)
|
|
||||||
|
|
||||||
// was SWT forced?
|
|
||||||
String swt_gtk3 = System.getProperty("SWT_GTK3");
|
|
||||||
isSwtGtk3_ = swt_gtk3 != null && !swt_gtk3.equals("0");
|
|
||||||
if (!isSwtGtk3_) {
|
|
||||||
// check a different property
|
|
||||||
String property = System.getProperty("org.eclipse.swt.internal.gtk.version");
|
|
||||||
isSwtGtk3_ = property != null && !property.startsWith("2.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LoggerFactory.getLogger(Swt.class).debug("Error detecting if SWT is loaded", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int _version = 0;
|
|
||||||
|
|
||||||
// we MUST save the currentDisplay now, otherwise it is "null" when methods are run from the swing EDT.
|
|
||||||
// also save the SWT version
|
|
||||||
// NOTE: we cannot check if there is a default display, because JUST CHECKING will initialize a new one
|
|
||||||
Display _currentDisplay = null;
|
|
||||||
Thread _currentDisplayThread = null;
|
|
||||||
|
|
||||||
|
|
||||||
if (isSwtLoaded_ && SWT.isLoadable()) {
|
|
||||||
try {
|
|
||||||
_version = SWT.getVersion();
|
|
||||||
_currentDisplay = Display.getCurrent();
|
|
||||||
|
|
||||||
if (_currentDisplay != null) {
|
|
||||||
_currentDisplayThread = Display.getCurrent().getThread();
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LoggerFactory.getLogger(Swt.class).error("Cannot initialize SWT", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we use SWT incorrectly (ie, it's available on the classpath, but we didn't start the application) then
|
|
||||||
// '_currentDisplay' will be null. This is a reasonable way to detect if SWT is being used or not.
|
|
||||||
if (_currentDisplay == null) {
|
|
||||||
_version = 0;
|
|
||||||
isSwtLoaded_ = false;
|
|
||||||
isSwtGtk3_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDisplay = _currentDisplay;
|
|
||||||
currentDisplayThread = _currentDisplayThread;
|
|
||||||
version = _version;
|
|
||||||
isLoaded = isSwtLoaded_;
|
|
||||||
isGtk3 = isSwtGtk3_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static
|
|
||||||
void onShutdown(final Display currentDisplay, final Runnable runnable) {
|
|
||||||
// currentDisplay.getShells() must only be called inside the event thread!
|
|
||||||
|
|
||||||
org.eclipse.swt.widgets.Shell shell = currentDisplay.getShells()[0];
|
|
||||||
shell.addListener(org.eclipse.swt.SWT.Close, new org.eclipse.swt.widgets.Listener() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void handleEvent(final org.eclipse.swt.widgets.Event event) {
|
|
||||||
runnable.run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
void dispatch(final Runnable runnable) {
|
|
||||||
currentDisplay.syncExec(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isEventThread() {
|
|
||||||
return Thread.currentThread() == currentDisplayThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
void onShutdown(final Runnable runnable) {
|
|
||||||
// currentDisplay.getShells() must only be called inside the event thread!
|
|
||||||
if (isEventThread()) {
|
|
||||||
onShutdown(currentDisplay, runnable);
|
|
||||||
} else {
|
|
||||||
dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
onShutdown(currentDisplay, runnable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
int getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
}
|
|
97
src/dorkbox/util/javaFx/JavaFX.java
Normal file
97
src/dorkbox/util/javaFx/JavaFX.java
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.util.javaFx;
|
||||||
|
|
||||||
|
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
|
import dorkbox.util.ClassLoaderUtil;
|
||||||
|
import dorkbox.util.OS;
|
||||||
|
import dorkbox.util.swt.Swt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for JavaFX.
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
class JavaFX {
|
||||||
|
public final static boolean isLoaded;
|
||||||
|
public final static boolean isGtk3;
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
// There is a silly amount of redirection, simply because we have to be able to access JavaFX, but only if it's in use.
|
||||||
|
// Since this class is the place other code interacts with, we can use JavaFX stuff if necessary without loading/linking
|
||||||
|
// the JavaFX classes by accident
|
||||||
|
|
||||||
|
// We cannot use getToolkit(), because if JavaFX is not being used, calling getToolkit() will initialize it...
|
||||||
|
// see: https://bugs.openjdk.java.net/browse/JDK-8090933
|
||||||
|
|
||||||
|
|
||||||
|
boolean isJavaFxLoaded_ = ClassLoaderUtil.isClassLoaded(ClassLoader.getSystemClassLoader(), "javafx.application.Platform");
|
||||||
|
if (!isJavaFxLoaded_) {
|
||||||
|
// check both classloaders
|
||||||
|
isJavaFxLoaded_ = ClassLoaderUtil.isClassLoaded(Thread.currentThread().getContextClassLoader(), "javafx.application.Platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isJavaFxGtk3_ = false;
|
||||||
|
if (isJavaFxLoaded_) {
|
||||||
|
// JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified
|
||||||
|
// see
|
||||||
|
// http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
|
||||||
|
// https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
|
||||||
|
// from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
|
||||||
|
|
||||||
|
|
||||||
|
if (OS.javaVersion >= 9) {
|
||||||
|
// HILARIOUSLY enough, you can use JavaFX + SWT..... And the javaFX GTK version info SHOULD
|
||||||
|
// be based on what SWT has loaded
|
||||||
|
|
||||||
|
// https://github.com/teamfx/openjfx-9-dev-rt/blob/master/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java
|
||||||
|
|
||||||
|
if (Swt.isLoaded && !Swt.isGtk3) {
|
||||||
|
isJavaFxGtk3_ = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
Boolean run() {
|
||||||
|
String version = System.getProperty("jdk.gtk.version", "2");
|
||||||
|
return "3".equals(version) || version.startsWith("3.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isLoaded = isJavaFxLoaded_;
|
||||||
|
isGtk3 = isJavaFxGtk3_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
void dispatch(final Runnable runnable) {
|
||||||
|
JavaFxDispatch.dispatch(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
boolean isEventThread() {
|
||||||
|
return JavaFxDispatch.isEventThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
void onShutdown(final Runnable runnable) {
|
||||||
|
JavaFxDispatch.onShutdown(runnable);
|
||||||
|
}
|
||||||
|
}
|
39
src/dorkbox/util/javaFx/JavaFxDispatch.java
Normal file
39
src/dorkbox/util/javaFx/JavaFxDispatch.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.util.javaFx;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for JavaFX. Redirection is used here so we don't load/link the JavaFX classes if we aren't using them
|
||||||
|
*/
|
||||||
|
class JavaFxDispatch {
|
||||||
|
static
|
||||||
|
void dispatch(final Runnable runnable) {
|
||||||
|
Platform.runLater(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
boolean isEventThread() {
|
||||||
|
return Platform.isFxApplicationThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void onShutdown(final Runnable runnable) {
|
||||||
|
com.sun.javafx.tk.Toolkit.getToolkit().addShutdownHook(runnable);
|
||||||
|
}
|
||||||
|
}
|
@ -15,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.util.jna.linux;
|
package dorkbox.util.jna.linux;
|
||||||
|
|
||||||
import dorkbox.util.JavaFX;
|
import dorkbox.util.javaFx.JavaFX;
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
import dorkbox.util.Swt;
|
import dorkbox.util.swt.Swt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accessor methods/logic for determining if GTK is already loaded by the Swing/JavaFX/SWT, or if GTK has been manually loaded via
|
* Accessor methods/logic for determining if GTK is already loaded by the Swing/JavaFX/SWT, or if GTK has been manually loaded via
|
||||||
|
@ -26,8 +26,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
import dorkbox.util.JavaFX;
|
import dorkbox.util.javaFx.JavaFX;
|
||||||
import dorkbox.util.Swt;
|
import dorkbox.util.swt.Swt;
|
||||||
|
|
||||||
public
|
public
|
||||||
class GtkEventDispatch {
|
class GtkEventDispatch {
|
||||||
@ -282,12 +282,10 @@ class GtkEventDispatch {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Swt.isLoaded) {
|
if (Swt.isLoaded && Swt.isEventThread()) {
|
||||||
if (Swt.isEventThread()) {
|
// Run directly on the SWT event thread. If it's not on the dispatch thread, we will use GTK to put it there
|
||||||
// Run directly on the SWT event thread. If it's not on the dispatch thread, we can use raw GTK to put it there
|
runnable.run();
|
||||||
runnable.run();
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import com.sun.jna.Function;
|
|||||||
import com.sun.jna.NativeLibrary;
|
import com.sun.jna.NativeLibrary;
|
||||||
|
|
||||||
import dorkbox.util.OS;
|
import dorkbox.util.OS;
|
||||||
import dorkbox.util.Swt;
|
import dorkbox.util.swt.Swt;
|
||||||
import dorkbox.util.jna.JnaHelper;
|
import dorkbox.util.jna.JnaHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +37,7 @@ import dorkbox.util.FileUtil;
|
|||||||
import dorkbox.util.MathUtil;
|
import dorkbox.util.MathUtil;
|
||||||
import dorkbox.util.OS;
|
import dorkbox.util.OS;
|
||||||
import dorkbox.util.OSUtil;
|
import dorkbox.util.OSUtil;
|
||||||
import dorkbox.util.Swt;
|
import dorkbox.util.swt.Swt;
|
||||||
import dorkbox.util.jna.linux.structs.GtkRequisition;
|
import dorkbox.util.jna.linux.structs.GtkRequisition;
|
||||||
import dorkbox.util.jna.linux.structs.GtkStyle;
|
import dorkbox.util.jna.linux.structs.GtkStyle;
|
||||||
import dorkbox.util.jna.linux.structs.PangoRectangle;
|
import dorkbox.util.jna.linux.structs.PangoRectangle;
|
||||||
|
78
src/dorkbox/util/swt/Swt.java
Normal file
78
src/dorkbox/util/swt/Swt.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.util.swt;
|
||||||
|
|
||||||
|
import dorkbox.util.ClassLoaderUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for SWT.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("Convert2Lambda")
|
||||||
|
public
|
||||||
|
class Swt {
|
||||||
|
public final static boolean isLoaded;
|
||||||
|
public final static boolean isGtk3;
|
||||||
|
private static final int version;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// There is a silly amount of redirection, simply because we have to be able to access SWT, but only if it's in use.
|
||||||
|
// Since this class is the place other code interacts with, we can use SWT stuff if necessary without loading/linking
|
||||||
|
// the SWT classes by accident
|
||||||
|
|
||||||
|
boolean isSwtLoaded_ = ClassLoaderUtil.isClassLoaded(ClassLoader.getSystemClassLoader(), "org.eclipse.swt.widgets.Display");
|
||||||
|
if (!isSwtLoaded_) {
|
||||||
|
// check both classloaders
|
||||||
|
isSwtLoaded_ = ClassLoaderUtil.isClassLoaded(Thread.currentThread().getContextClassLoader(), "org.eclipse.swt.widgets.Display");
|
||||||
|
}
|
||||||
|
|
||||||
|
int _version = 0;
|
||||||
|
if (isSwtLoaded_) {
|
||||||
|
_version = SwtDispatch.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
version = _version;
|
||||||
|
isLoaded = isSwtLoaded_;
|
||||||
|
isGtk3 = isSwtLoaded_ && SwtDispatch.isGtk3();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
void main(String[] args) {
|
||||||
|
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
boolean classLo2aded = ClassLoaderUtil.isClassLoaded(contextClassLoader, "dorkbox/util/swt/Swt.java");
|
||||||
|
boolean classLoaded = ClassLoaderUtil.isClassLoaded(contextClassLoader, "org.eclipse.swt.SWTError");
|
||||||
|
boolean classLo3aded = ClassLoaderUtil.isClassLoaded(contextClassLoader, ":asd");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
int getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
void dispatch(final Runnable runnable) {
|
||||||
|
SwtDispatch.dispatch(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
boolean isEventThread() {
|
||||||
|
return SwtDispatch.isEventThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static
|
||||||
|
void onShutdown(final Runnable runnable) {
|
||||||
|
SwtDispatch.onShutdown(runnable);
|
||||||
|
}
|
||||||
|
}
|
202
src/dorkbox/util/swt/SwtDispatch.java
Normal file
202
src/dorkbox/util/swt/SwtDispatch.java
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.util.swt;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import dorkbox.util.OS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for SWT.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("Convert2Lambda")
|
||||||
|
class SwtDispatch {
|
||||||
|
private static final Display currentDisplay;
|
||||||
|
private static final Thread currentDisplayThread;
|
||||||
|
|
||||||
|
private static final int version;
|
||||||
|
|
||||||
|
static {
|
||||||
|
int _version = 0;
|
||||||
|
|
||||||
|
// Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to
|
||||||
|
// System.setProperty("SWT_GTK3", "0"); (or -DSWT_GTK3=1)
|
||||||
|
|
||||||
|
|
||||||
|
// we MUST save the currentDisplay now, otherwise it is "null" when methods are run from the swing EDT.
|
||||||
|
// also save the SWT version
|
||||||
|
// NOTE: we cannot check if there is a default display, because JUST CHECKING will initialize a new one
|
||||||
|
Display _currentDisplay = null;
|
||||||
|
Thread _currentDisplayThread = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
_version = SWT.getVersion();
|
||||||
|
_currentDisplay = Display.getCurrent();
|
||||||
|
|
||||||
|
if (_currentDisplay != null) {
|
||||||
|
_currentDisplayThread = Display.getCurrent().getThread();
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LoggerFactory.getLogger(SwtDispatch.class).error("Cannot initialize SWT", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we use SWT incorrectly (ie, it's available on the classpath, but we didn't start the application) then
|
||||||
|
// '_currentDisplay' will be null. This is a reasonable way to detect if SWT is being used or not.
|
||||||
|
if (_currentDisplay == null) {
|
||||||
|
_version = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDisplay = _currentDisplay;
|
||||||
|
currentDisplayThread = _currentDisplayThread;
|
||||||
|
version = _version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an indirect reference to SWT, so we only check if SWT is loadable if certain classes are available.
|
||||||
|
* @return true if SWT is loadable
|
||||||
|
*/
|
||||||
|
static boolean isLoadable() {
|
||||||
|
Class<?> swtErrorClass = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
Class<?> run() {
|
||||||
|
try {
|
||||||
|
return Class.forName("org.eclipse.swt.SWTError", true, ClassLoader.getSystemClassLoader());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Class.forName("org.eclipse.swt.SWTError", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return null != swtErrorClass && SWT.isLoadable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is only necessary for linux.
|
||||||
|
*
|
||||||
|
* @return true if SWT is GTK3. False if SWT is GTK2. If for some reason we DO NOT KNOW, then we return false (GTK2).
|
||||||
|
*/
|
||||||
|
static boolean isGtk3() {
|
||||||
|
if (!OS.isLinux()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String SWT_INTERNAL_CLASS = "org.eclipse.swt.internal.gtk.OS";
|
||||||
|
Class<?> osClass = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
Class<?> run() {
|
||||||
|
try {
|
||||||
|
return Class.forName(SWT_INTERNAL_CLASS, true, ClassLoader.getSystemClassLoader());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Class.forName(SWT_INTERNAL_CLASS, true, Thread.currentThread().getContextClassLoader());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (osClass == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Method method = AccessController.doPrivileged(new PrivilegedAction<Method>() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
Method run() {
|
||||||
|
try {
|
||||||
|
return osClass.getMethod("gtk_major_version");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (method == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int version = 0;
|
||||||
|
try {
|
||||||
|
version = ((Number)method.invoke(osClass)).intValue();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// this method doesn't exist.
|
||||||
|
}
|
||||||
|
|
||||||
|
return version == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static
|
||||||
|
void onShutdown(final Display currentDisplay, final Runnable runnable) {
|
||||||
|
// currentDisplay.getShells() must only be called inside the event thread!
|
||||||
|
|
||||||
|
org.eclipse.swt.widgets.Shell shell = currentDisplay.getShells()[0];
|
||||||
|
shell.addListener(SWT.Close, new org.eclipse.swt.widgets.Listener() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void handleEvent(final org.eclipse.swt.widgets.Event event) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void dispatch(final Runnable runnable) {
|
||||||
|
currentDisplay.syncExec(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
boolean isEventThread() {
|
||||||
|
return Thread.currentThread() == currentDisplayThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void onShutdown(final Runnable runnable) {
|
||||||
|
// currentDisplay.getShells() must only be called inside the event thread!
|
||||||
|
if (isEventThread()) {
|
||||||
|
onShutdown(currentDisplay, runnable);
|
||||||
|
} else {
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
onShutdown(currentDisplay, runnable);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user