forked from dorkbox/SystemTray
Refactored out JavaFX (similar to how SWT was done) so that we can
compile to Java 1.6
This commit is contained in:
parent
efeda2fac3
commit
3d8459ecc3
@ -23,7 +23,6 @@ import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
@ -41,6 +40,8 @@ import dorkbox.systemTray.linux.GtkSystemTray;
|
||||
import dorkbox.systemTray.linux.jna.AppIndicator;
|
||||
import dorkbox.systemTray.linux.jna.Gtk;
|
||||
import dorkbox.systemTray.swing.SwingSystemTray;
|
||||
import dorkbox.systemTray.util.JavaFX;
|
||||
import dorkbox.systemTray.util.Swt;
|
||||
import dorkbox.util.OS;
|
||||
import dorkbox.util.Property;
|
||||
import dorkbox.util.process.ShellProcessBuilder;
|
||||
@ -85,7 +86,7 @@ class SystemTray {
|
||||
/**
|
||||
* This property is provided for debugging any errors in the logic used to determine the system-tray type.
|
||||
*/
|
||||
public static boolean DEBUG = false;
|
||||
public static boolean DEBUG = true;
|
||||
|
||||
|
||||
private static volatile SystemTray systemTray = null;
|
||||
@ -95,21 +96,15 @@ class SystemTray {
|
||||
public final static boolean isSwtLoaded;
|
||||
|
||||
static {
|
||||
// maybe we should load the SWT version? (In order for us to work with SWT, BOTH must be GTK2!!
|
||||
// SWT is GTK2, but if -DSWT_GTK3=1 is specified, it can be GTK3
|
||||
// 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
|
||||
|
||||
boolean isJavaFxLoaded_ = false;
|
||||
boolean isSwtLoaded_ = false;
|
||||
try {
|
||||
// First check if JavaFX is loaded - if it's NOT LOADED, then we only proceed if COMPATIBILITY_MODE is enabled.
|
||||
// this is important, 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 = ClassLoader.getSystemClassLoader();
|
||||
isJavaFxLoaded_ = (null != m.invoke(cl, "com.sun.javafx.tk.Toolkit")) || (null != m.invoke(cl, "javafx.application.Application"));
|
||||
isSwtLoaded_ = null != m.invoke(cl, "org.eclipse.swt.widgets.Display");
|
||||
// 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_ = JavaFX.isLoaded();
|
||||
|
||||
// SWT is GTK2, but if -DSWT_GTK3=1 is specified, it can be GTK3
|
||||
isSwtLoaded_ = Swt.isLoaded();
|
||||
} catch (Throwable e) {
|
||||
if (DEBUG) {
|
||||
logger.debug("Error detecting javaFX/SWT mode", e);
|
||||
@ -547,65 +542,28 @@ class SystemTray {
|
||||
if (isJavaFxLoaded) {
|
||||
// Necessary because javaFX **ALSO** runs a gtk main loop, and when it stops (if we don't stop first), we become unresponsive.
|
||||
// Also, it's nice to have us shutdown at the same time as the main application
|
||||
|
||||
// com.sun.javafx.tk.Toolkit.getToolkit()
|
||||
// .addShutdownHook(new Runnable() {
|
||||
// @Override
|
||||
// public
|
||||
// void run() {
|
||||
// systemTray.shutdown();
|
||||
// }
|
||||
// });
|
||||
|
||||
try {
|
||||
Class<?> clazz = Class.forName("com.sun.javafx.tk.Toolkit");
|
||||
Method method = clazz.getMethod("getToolkit");
|
||||
Object o = method.invoke(null);
|
||||
Method runnable = o.getClass()
|
||||
.getMethod("addShutdownHook", Runnable.class);
|
||||
runnable.invoke(o, new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
if (systemTray != null) {
|
||||
systemTray.shutdown();
|
||||
}
|
||||
JavaFX.onShutdown(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
if (systemTray != null) {
|
||||
systemTray.shutdown();
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
if (DEBUG) {
|
||||
logger.error("Cannot initialize JavaFX", e);
|
||||
}
|
||||
logger.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.");
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (isSwtLoaded) {
|
||||
// this is because SWT **ALSO** runs a gtk main loop, and when it stops (if we don't stop first), we become unresponsive
|
||||
// Also, it's nice to have us shutdown at the same time as the main application
|
||||
|
||||
// During compile time (for production), this class is not compiled, and instead is copied over as a pre-compiled file
|
||||
// This is so we don't have to rely on having SWT as part of the classpath during build.
|
||||
try {
|
||||
Class<?> clazz = Class.forName("dorkbox.systemTray.swt.Swt");
|
||||
Method method = clazz.getMethod("onShutdown", Runnable.class);
|
||||
Object o = method.invoke(null, new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
if (systemTray != null) {
|
||||
systemTray.shutdown();
|
||||
}
|
||||
Swt.onShutdown(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
if (systemTray != null) {
|
||||
systemTray.shutdown();
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
if (DEBUG) {
|
||||
logger.error("Cannot initialize SWT", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
logger.error("Unable to insert shutdown hook into SWT. Please create an issue with your OS and Java " +
|
||||
"version so we may further investigate this issue.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import dorkbox.systemTray.linux.jna.GEventCallback;
|
||||
import dorkbox.systemTray.linux.jna.GdkEventButton;
|
||||
import dorkbox.systemTray.linux.jna.Gobject;
|
||||
import dorkbox.systemTray.linux.jna.Gtk;
|
||||
import javafx.application.Platform;
|
||||
import dorkbox.systemTray.util.JavaFX;
|
||||
|
||||
/**
|
||||
* Class for handling all system tray interactions via GTK.
|
||||
@ -89,7 +89,7 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
||||
});
|
||||
|
||||
if (SystemTray.isJavaFxLoaded) {
|
||||
if (!Platform.isFxApplicationThread()) {
|
||||
if (!JavaFX.isEventThread()) {
|
||||
try {
|
||||
blockUntilStarted.await(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -18,7 +18,6 @@ package dorkbox.systemTray.linux.jna;
|
||||
import static dorkbox.systemTray.SystemTray.logger;
|
||||
import static dorkbox.systemTray.linux.jna.Gobject.g_idle_add;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -27,6 +26,8 @@ import com.sun.jna.Function;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import dorkbox.systemTray.SystemTray;
|
||||
import dorkbox.systemTray.util.JavaFX;
|
||||
import dorkbox.systemTray.util.Swt;
|
||||
|
||||
/**
|
||||
* bindings for gtk 2 or 3
|
||||
@ -50,8 +51,8 @@ class Gtk {
|
||||
private static boolean alreadyRunningGTK = false;
|
||||
private static boolean isLoaded = false;
|
||||
|
||||
private static Method swtDispatchMethod = null;
|
||||
|
||||
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk
|
||||
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
|
||||
|
||||
/**
|
||||
* We can have GTK v3 or v2.
|
||||
@ -60,10 +61,6 @@ class Gtk {
|
||||
* JavaFX uses GTK2, and we can't load GTK3 if GTK2 symbols are loaded
|
||||
* SWT uses GTK2 or GTK3. We do not work with the GTK3 version of SWT.
|
||||
*/
|
||||
|
||||
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk
|
||||
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
|
||||
|
||||
static {
|
||||
boolean shouldUseGtk2 = SystemTray.FORCE_GTK2;
|
||||
|
||||
@ -159,8 +156,10 @@ class Gtk {
|
||||
private static volatile boolean started = false;
|
||||
|
||||
// have to save these in a field to prevent GC on the objects (since they go out-of-scope from java)
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
private static final LinkedList<Object> gtkCallbacks = new LinkedList<Object>();
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private static Thread gtkUpdateThread = null;
|
||||
|
||||
public static final int FALSE = 0;
|
||||
@ -228,20 +227,6 @@ class Gtk {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SystemTray.isSwtLoaded) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName("dorkbox.systemTray.swt.Swt");
|
||||
swtDispatchMethod = clazz.getMethod("dispatch", Runnable.class);
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
logger.error("Cannot initialize SWT dispatch", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
logger.error("Unable to call into dispatch method for SWT. Please create an issue with your OS and Java " +
|
||||
"version so we may further investigate this issue.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,22 +238,16 @@ class Gtk {
|
||||
// SWT/JavaFX
|
||||
|
||||
if (SystemTray.isJavaFxLoaded) {
|
||||
if (javafx.application.Platform.isFxApplicationThread()) {
|
||||
if (JavaFX.isEventThread()) {
|
||||
// Run directly on the JavaFX event thread
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
javafx.application.Platform.runLater(runnable);
|
||||
JavaFX.dispatch(runnable);
|
||||
}
|
||||
}
|
||||
else if (SystemTray.isSwtLoaded) {
|
||||
try {
|
||||
swtDispatchMethod.invoke(null, runnable);
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to call into dispatch method for SWT. Please create an issue with your OS and Java " +
|
||||
"version so we may further investigate this issue.");
|
||||
throw new RuntimeException("Unable to invoke required SWT method");
|
||||
}
|
||||
Swt.dispatch(runnable);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -335,17 +314,6 @@ class Gtk {
|
||||
*/
|
||||
private static native void gtk_main_quit();
|
||||
|
||||
private static native void gdk_threads_init();
|
||||
|
||||
// tricky business. Only when using SWT/JavaFX, because they do it wrong.
|
||||
/**
|
||||
* @return TRUE to run this callback again, FALSE to remove from the list of event sources (and not call it again)
|
||||
*/
|
||||
private static native int gdk_threads_add_idle(FuncCallback callback, Pointer data);
|
||||
private static native void gdk_threads_enter();
|
||||
private static native void gdk_threads_leave();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -371,7 +339,6 @@ class Gtk {
|
||||
|
||||
public static native void gtk_label_set_use_markup(Pointer label, int gboolean);
|
||||
|
||||
public static native Pointer gtk_status_icon_new_from_icon_name(String iconName);;
|
||||
public static native Pointer gtk_status_icon_new();
|
||||
|
||||
public static native void gtk_status_icon_set_from_file(Pointer widget, String label);
|
||||
@ -397,8 +364,6 @@ class Gtk {
|
||||
|
||||
public static native void gtk_container_remove(Pointer menu, Pointer subItem);
|
||||
|
||||
public static native void gtk_widget_show(Pointer widget);
|
||||
|
||||
public static native void gtk_widget_show_all(Pointer widget);
|
||||
|
||||
public static native void gtk_widget_destroy(Pointer widget);
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,46 +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.systemTray.swt;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
|
||||
/**
|
||||
* SWT system tray types are just GTK trays.
|
||||
* <p>
|
||||
* This isn't possible with reflection - so we compile it AHEAD of time and save the bytecode (which is then included with the release)
|
||||
* We **COULD** do some ASM/Bytecode generation, but why bother with even more libraries when it's just as easy to include it pre-compiled.
|
||||
*/
|
||||
public
|
||||
class Swt {
|
||||
public static
|
||||
void dispatch(final Runnable runnable) {
|
||||
Display.getDefault().asyncExec(runnable);
|
||||
}
|
||||
|
||||
public static
|
||||
void onShutdown(final Runnable runnable) {
|
||||
Display.getCurrent().getShells()[0].addListener(SWT.Close, new Listener() {
|
||||
@Override
|
||||
public
|
||||
void handleEvent(final Event event) {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
110
src/dorkbox/systemTray/util/JavaFX.java
Normal file
110
src/dorkbox/systemTray/util/JavaFX.java
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.systemTray.util;
|
||||
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import dorkbox.systemTray.SystemTray;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
// Methods are cached for performance
|
||||
private static Method isEventThread;
|
||||
private static Method dispatchMethod;
|
||||
|
||||
public static
|
||||
boolean isLoaded() throws Exception {
|
||||
// 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
|
||||
|
||||
// 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 = ClassLoader.getSystemClassLoader();
|
||||
return (null != m.invoke(cl, "com.sun.javafx.tk.Toolkit")) || (null != m.invoke(cl, "javafx.application.Application"));
|
||||
}
|
||||
|
||||
public static
|
||||
void dispatch(final Runnable runnable) {
|
||||
// javafx.application.Platform.runLater(runnable);
|
||||
|
||||
try {
|
||||
if (dispatchMethod == null) {
|
||||
Class<?> clazz = Class.forName("javafx.application.Platform");
|
||||
dispatchMethod = clazz.getMethod("runLater");
|
||||
}
|
||||
|
||||
dispatchMethod.invoke(null, runnable);
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
SystemTray.logger.error("Cannot initialize JavaFX", e);
|
||||
}
|
||||
SystemTray.logger.error("Unable to execute JavaFX runLater(). Please create an issue with your OS and Java " +
|
||||
"version so we may further investigate this issue.");
|
||||
}
|
||||
}
|
||||
|
||||
public static
|
||||
boolean isEventThread() {
|
||||
// javafx.application.Platform.isFxApplicationThread();
|
||||
|
||||
try {
|
||||
if (isEventThread == null) {
|
||||
Class<?> clazz = Class.forName("javafx.application.Platform");
|
||||
isEventThread = clazz.getMethod("isFxApplicationThread");
|
||||
}
|
||||
return (Boolean) isEventThread.invoke(null);
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
SystemTray.logger.error("Cannot initialize JavaFX", e);
|
||||
}
|
||||
SystemTray.logger.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) {
|
||||
if (SystemTray.DEBUG) {
|
||||
SystemTray.logger.error("Cannot initialize JavaFX", e);
|
||||
}
|
||||
SystemTray.logger.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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
58
src/dorkbox/systemTray/util/Swt.java
Normal file
58
src/dorkbox/systemTray/util/Swt.java
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.systemTray.util;
|
||||
|
||||
/**
|
||||
* Utility methods for SWT.
|
||||
* <p>
|
||||
* SWT system tray types are just GTK trays.
|
||||
* <p>
|
||||
* Creating custom methods for a class is not possible with reflection - so we compile it AHEAD of time as Java 1.6, and save the bytecode
|
||||
* (which is then included with the release)
|
||||
* <p>
|
||||
* We **COULD** do some ASM/Bytecode generation, but why bother with even more libraries when it's just as easy to include it pre-compiled.
|
||||
*/
|
||||
public
|
||||
class Swt {
|
||||
public static
|
||||
boolean isLoaded() throws Exception {
|
||||
// maybe we should load the SWT version? (In order for us to work with SWT, BOTH must be GTK2!!
|
||||
// SWT is GTK2, but if -DSWT_GTK3=1 is specified, it can be GTK3
|
||||
|
||||
java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
m.setAccessible(true);
|
||||
ClassLoader cl = ClassLoader.getSystemClassLoader();
|
||||
return null != m.invoke(cl, "org.eclipse.swt.widgets.Display");
|
||||
}
|
||||
|
||||
public static
|
||||
void dispatch(final Runnable runnable) {
|
||||
org.eclipse.swt.widgets.Display.getCurrent()
|
||||
.asyncExec(runnable);
|
||||
}
|
||||
|
||||
public static
|
||||
void onShutdown(final Runnable runnable) {
|
||||
org.eclipse.swt.widgets.Display.getCurrent()
|
||||
.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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user