SWT no longer using reflection (using trampoline class for access)

This commit is contained in:
Robinson 2021-01-27 01:37:53 +01:00
parent 01d92c0a86
commit 23ffd02b65
2 changed files with 133 additions and 111 deletions

View File

@ -15,15 +15,9 @@
*/ */
package dorkbox.util.swt; package dorkbox.util.swt;
import java.lang.reflect.Method;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import dorkbox.os.OS;
/** /**
* Utility methods for SWT. SWT is always available for compiling, so it is not necessary to use reflection to compile it. * Utility methods for SWT. SWT is always available for compiling, so it is not necessary to use reflection to compile it.
* <p> * <p>
@ -35,33 +29,13 @@ class Swt {
public final static boolean isGtk3; public final static boolean isGtk3;
private static final int version; private static final int version;
private static final Display currentDisplay; // NOTE: This class cannot have SWT **ANYTHING** in it
private static final Thread currentDisplayThread;
static { 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. // 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 // 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 // the SWT classes by accident
boolean isSwtLoadable_ = isLoadable();
version = SWT.getVersion();
isLoaded = isSwtLoadable_;
isGtk3 = isSwtLoadable_ && isGtk3();
// we MUST save this on init, otherwise it is "null" when methods are run from the swing EDT.
currentDisplay = org.eclipse.swt.widgets.Display.getCurrent();
currentDisplayThread = currentDisplay.getThread();
}
/**
* This uses reflection to check if SWT is loadable and if certain classes are available.
* @return true if SWT is loadable
*/
static boolean isLoadable() {
Class<?> swtErrorClass = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() { Class<?> swtErrorClass = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
@Override @Override
public public
@ -79,78 +53,20 @@ class Swt {
}); });
if (swtErrorClass != null) { if (swtErrorClass != null) {
try { // this means that SWT is available in the system at runtime. We use the error class because that DOES NOT intitialize anything
return org.eclipse.swt.SWT.isLoadable(); boolean isSwtLoadable_ = SwtAccess.isLoadable();
} catch (Exception ignored) { version = SwtAccess.getVersion();
} isLoaded = isSwtLoadable_;
} isGtk3 = isSwtLoadable_ && SwtAccess.isGtk3();
return false; SwtAccess.init();
} else {
version = 0;
isLoaded = false;
isGtk3 = false;
}
} }
/**
* 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;
}
// required to use reflection, because this is an internal class
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;
}
final Class<?> clazz = osClass;
Method method = AccessController.doPrivileged(new PrivilegedAction<Method>() {
@Override
public
Method run() {
try {
return clazz.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;
}
public static public static
int getVersion() { int getVersion() {
return version; return version;
@ -158,27 +74,16 @@ class Swt {
public static public static
void dispatch(final Runnable runnable) { void dispatch(final Runnable runnable) {
currentDisplay.syncExec(runnable); SwtAccess.dispatch(runnable);
} }
public static public static
boolean isEventThread() { boolean isEventThread() {
return Thread.currentThread() == currentDisplayThread; return SwtAccess.isEventThread();
} }
public static public static
void onShutdown(final Runnable runnable) { void onShutdown(final Runnable runnable) {
// currentDisplay.getShells() must only be called inside the event thread! SwtAccess.onShutdown(runnable);
if (isEventThread()) {
SwtAccess.onShutdown(currentDisplay, runnable);
} else {
dispatch(new Runnable() {
@Override
public
void run() {
SwtAccess.onShutdown(currentDisplay, runnable);
}
});
}
} }
} }

View File

@ -1,7 +1,31 @@
package dorkbox.util.swt; 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 dorkbox.os.OS;
public public
class SwtAccess { class SwtAccess {
private static Display currentDisplay = null;
private static Thread currentDisplayThread = null;
public static
void init() {
// we MUST save this on init, otherwise it is "null" when methods are run from the swing EDT.
currentDisplay = org.eclipse.swt.widgets.Display.getCurrent();
currentDisplayThread = currentDisplay.getThread();
}
static
boolean isLoadable() {
return org.eclipse.swt.SWT.isLoadable();
}
static static
void onShutdown(final org.eclipse.swt.widgets.Display currentDisplay, final Runnable runnable) { void onShutdown(final org.eclipse.swt.widgets.Display currentDisplay, final Runnable runnable) {
// currentDisplay.getShells() must only be called inside the event thread! // currentDisplay.getShells() must only be called inside the event thread!
@ -15,4 +39,97 @@ class SwtAccess {
} }
}); });
} }
static
int getVersion() {
return SWT.getVersion();
}
/**
* 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;
}
// required to use reflection, because this is an internal class!
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;
}
final Class<?> clazz = osClass;
Method method = AccessController.doPrivileged(new PrivilegedAction<Method>() {
@Override
public
Method run() {
try {
return clazz.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;
}
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()) {
SwtAccess.onShutdown(currentDisplay, runnable);
} else {
dispatch(new Runnable() {
@Override
public
void run() {
SwtAccess.onShutdown(currentDisplay, runnable);
}
});
}
}
} }