diff --git a/src/dorkbox/util/JavaFX.java b/src/dorkbox/util/JavaFX.java new file mode 100644 index 0000000..9e99c9f --- /dev/null +++ b/src/dorkbox/util/JavaFX.java @@ -0,0 +1,135 @@ +/* + * 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. + *
+ * We use reflection for these methods so that we can compile everything under Java 1.6 (which doesn't have JavaFX). + */ +public +class JavaFX { + // Methods are cached for performance + private static final Method dispatchMethod; + private static final Method isEventThreadMethod; + private static final Object isEventThreadObject; + + static { + Method _isEventThreadMethod = null; + Method _dispatchMethod = null; + Object _isEventThreadObject = null; + + 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 init() { + if (dispatchMethod == null || isEventThreadMethod == null) { + LoggerFactory.getLogger(JavaFX.class) + .error("Unable to initialize JavaFX! Please create an issue with your OS and Java " + + "version so we may further investigate this issue."); + } + } + + + 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, (java.lang.Class>[])null); + } + } 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."); + } + } +} diff --git a/src/dorkbox/util/Swt.java b/src/dorkbox/util/Swt.java new file mode 100644 index 0000000..f84888f --- /dev/null +++ b/src/dorkbox/util/Swt.java @@ -0,0 +1,90 @@ +/* + * 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.slf4j.LoggerFactory; + +/** + * Utility methods for SWT. This will by written by {@link SwtBytecodeOverride}, via Javassist so we don't require a hard dependency on + * SWT. + *
+ * The methods/fields that this originated from * are commented out. + *
+ * SWT system tray types are just GTK trays. + */ +public +class Swt { + private static final org.eclipse.swt.widgets.Display currentDisplay; + private static final Thread currentDisplayThread; + + static { + // we MUST save this, otherwise it is "null" when methods are run from the swing EDT. + currentDisplay = org.eclipse.swt.widgets.Display.getCurrent(); + + currentDisplayThread = currentDisplay.getThread(); + } + + public static + void init() { + if (currentDisplay == null) { + LoggerFactory.getLogger(Swt.class) + .error("Unable to get the current display for SWT. Please create an issue with your OS and Java " + + "version so we may further investigate this issue."); + } +// throw new RuntimeException("This should never happen, as this class is over-written at runtime."); + } + + public static + void dispatch(final Runnable runnable) { + currentDisplay.syncExec(runnable); +// throw new RuntimeException("This should never happen, as this class is over-written at runtime."); + } + + public static + boolean isEventThread() { + return Thread.currentThread() == currentDisplayThread; +// throw new RuntimeException("This should never happen, as this class is over-written at runtime."); + } + + public static + void onShutdown(final Runnable runnable) { + // currentDisplay.getShells() can only happen inside the event thread! + if (isEventThread()) { + currentDisplay.getShells()[0].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(); + } + }); + } else { + dispatch(new Runnable() { + @Override + public + void run() { + currentDisplay.getShells()[0].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(); + } + }); + } + }); + } +// throw new RuntimeException("This should never happen, as this class is over-written at runtime."); + } +} diff --git a/src/dorkbox/util/SwtBytecodeOverride.java b/src/dorkbox/util/SwtBytecodeOverride.java new file mode 100644 index 0000000..cdf95e4 --- /dev/null +++ b/src/dorkbox/util/SwtBytecodeOverride.java @@ -0,0 +1,269 @@ +/* + * 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 static org.objectweb.asm.Opcodes.AALOAD; +import static org.objectweb.asm.Opcodes.ACC_FINAL; +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_SUPER; +import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ARETURN; +import static org.objectweb.asm.Opcodes.BIPUSH; +import static org.objectweb.asm.Opcodes.DUP; +import static org.objectweb.asm.Opcodes.GETSTATIC; +import static org.objectweb.asm.Opcodes.GOTO; +import static org.objectweb.asm.Opcodes.ICONST_0; +import static org.objectweb.asm.Opcodes.ICONST_1; +import static org.objectweb.asm.Opcodes.IFEQ; +import static org.objectweb.asm.Opcodes.IFNONNULL; +import static org.objectweb.asm.Opcodes.IF_ACMPNE; +import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.IRETURN; +import static org.objectweb.asm.Opcodes.NEW; +import static org.objectweb.asm.Opcodes.PUTSTATIC; +import static org.objectweb.asm.Opcodes.RETURN; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + + +/** + * Utility methods for SWT. This will override {@link Swt} methods, and is written in Javassist. This is so we don't require a hard + * dependency on SWT. + *
+ * The methods/fields that this originated from are commented out in the {@link Swt} class.
+ */
+public
+class SwtBytecodeOverride {
+ static {
+ try {
+// byte[] swtClassBytes = getSwtBytes();
+//
+// // whoosh, past the classloader and directly into memory. This will take precedence over the "proper" Swt class
+// BootStrapClassLoader.defineClass(swtClassBytes);
+
+ // now we have to load various classes into the classloader
+ Class> aClass = Class.forName("org.eclipse.swt.widgets.Display");
+ Swt.init();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static
+ void init() {
+ // placeholder to initialize the class.
+ }
+
+ private static
+ byte[] getSwtBytes() throws Exception {
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+ AnnotationVisitor av0;
+
+ cw.visit(52, ACC_PUBLIC + ACC_SUPER, "dorkbox/util/Swt", null, "java/lang/Object", null);
+
+ cw.visitSource("Swt.java", null);
+
+ cw.visitInnerClass("dorkbox/util/Swt$1", null, null, 0);
+
+ cw.visitInnerClass("dorkbox/util/Swt$2", null, null, 0);
+
+ {
+ fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "currentDisplay", "Lorg/eclipse/swt/widgets/Display;", null, null);
+ fv.visitEnd();
+ }
+ {
+ fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "currentDisplayThread", "Ljava/lang/Thread;", null, null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(ACC_STATIC, "