Code polish/cleanup. Moved "autodetect" tray logic to it's own method.

This commit is contained in:
nathan 2017-06-27 00:19:38 +02:00
parent 38110f2dff
commit 6599989e82

View File

@ -42,12 +42,12 @@ import com.sun.java.swing.plaf.gtk.GTKLookAndFeel;
import dorkbox.systemTray.jna.linux.AppIndicator;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.nativeUI.NativeUI;
import dorkbox.systemTray.nativeUI._AppIndicatorNativeTray;
import dorkbox.systemTray.nativeUI._AwtTray;
import dorkbox.systemTray.nativeUI._GtkStatusIconNativeTray;
import dorkbox.systemTray.swingUI.SwingUIFactory;
import dorkbox.systemTray.swingUI._SwingTray;
import dorkbox.systemTray.util.ImageResizeUtil;
import dorkbox.systemTray.util.JavaFX;
import dorkbox.systemTray.util.SizeAndScalingUtil;
import dorkbox.systemTray.util.Swt;
@ -210,8 +210,189 @@ class SystemTray {
return null;
}
// This will return the default "autodetect" tray type
private static
Class<? extends Tray> getAutoDetectTrayType() {
if (OS.isWindows()) {
try {
return selectType(TrayType.Swing);
} catch (Throwable e) {
logger.error("You might need to grant the AWTPermission `accessSystemTray` to the SecurityManager.", e);
}
}
else if (OS.isMacOsX()) {
// macos can ONLY use the AWT if you want it to follow the L&F of the OS. It is the default.
try {
return selectType(TrayType.AWT);
} catch (Throwable e) {
logger.error("You might need to grant the AWTPermission `accessSystemTray` to the SecurityManager.");
}
}
else if ((OS.isLinux() || OS.isUnix())) {
// see: https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running
// For funsies, SyncThing did a LOT of work on compatibility (unfortunate for us) in python.
// https://github.com/syncthing/syncthing-gtk/blob/b7a3bc00e3bb6d62365ae62b5395370f3dcc7f55/syncthing_gtk/statusicon.py
// quick check, because we know that unity uses app-indicator. Maybe REALLY old versions do not. We support 14.04 LTE at least
OSUtil.DesktopEnv.Env de = OSUtil.DesktopEnv.get();
if (DEBUG) {
logger.debug("Currently using the '{}' desktop environment", de);
}
switch (de) {
case Gnome: {
// check other DE / OS combos that are based on gnome
String GDM = System.getenv("GDMSESSION");
if (DEBUG) {
logger.debug("Currently using the '{}' session type", GDM);
}
if ("gnome".equalsIgnoreCase(GDM)) {
Tray.usingGnome = true;
// are we fedora? If so, what version?
// now, what VERSION of fedora? 23/24/25/? don't have AppIndicator installed, so we have to use GtkStatusIcon
if (OSUtil.Linux.isFedora()) {
if (DEBUG) {
logger.debug("Running Fedora");
}
// 23 is gtk, 24/25 is gtk (but also wrong size unless we adjust it. ImageUtil automatically does this)
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if (OSUtil.Linux.isUbuntu()) {
// so far, because of the interaction between gnome3 + ubuntu, the GtkStatusIcon miraculously works.
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if (OSUtil.Unix.isFreeBSD()) {
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else {
// arch likely will have problems unless the correct/appropriate libraries are installed.
return selectTypeQuietly(TrayType.AppIndicator);
}
}
else if ("cinnamon".equalsIgnoreCase(GDM)) {
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("default".equalsIgnoreCase(GDM)) {
// this can be gnome3 on debian
if (OSUtil.Linux.isDebian()) {
// note: Debian Gnome3 does NOT work! (tested on Debian 8.5 and 8.6 default installs)
logger.warn("Debian with Gnome detected. SystemTray support is not known to work.");
}
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("gnome-classic".equalsIgnoreCase(GDM)) {
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("gnome-fallback".equalsIgnoreCase(GDM)) {
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("ubuntu".equalsIgnoreCase(GDM)) {
return selectTypeQuietly(TrayType.AppIndicator);
}
break;
}
case KDE: {
if (OSUtil.Linux.isFedora()) {
// Fedora KDE requires GtkStatusIcon
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else {
// kde (at least, plasma 5.5.6) requires appindicator
return selectTypeQuietly(TrayType.AppIndicator);
}
// kde 5.8+ is "high DPI", so we need to adjust the scale. Image resize will do that
}
case Unity: {
// Ubuntu Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell".
return selectTypeQuietly(TrayType.AppIndicator);
}
case XFCE: {
// NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted.
// see: https://ask.fedoraproject.org/en/question/23116/how-to-fix-missing-icons-in-program-menus-and-context-menus/
// see: https://git.gnome.org/browse/gtk+/commit/?id=627a03683f5f41efbfc86cc0f10e1b7c11e9bb25
// so far, it is OK to use GtkStatusIcon on XFCE <-> XFCE4 inclusive
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
case LXDE: {
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
case Pantheon: {
// elementaryOS. It only supports appindicator (not gtkstatusicon)
// http://bazaar.launchpad.net/~wingpanel-devs/wingpanel/trunk/view/head:/sample/SampleIndicator.vala
// ElementaryOS shows the checkbox on the right, everyone else is on the left.
// With eOS, we CANNOT show the spacer image. It does not work.
return selectTypeQuietly(TrayType.AppIndicator);
}
}
// Try to autodetect if we can use app indicators (or if we need to fallback to GTK indicators)
BufferedReader bin = null;
try {
// the ONLY guaranteed way to determine if indicator-application-service is running (and thus, using app-indicator),
// is to look through all /proc/<pid>/status, and first line should be Name:\tindicator-appli
File proc = new File("/proc");
File[] listFiles = proc.listFiles();
if (listFiles != null) {
for (File procs : listFiles) {
String name = procs.getName();
if (!Character.isDigit(name.charAt(0))) {
continue;
}
File status = new File(procs, "status");
if (!status.canRead()) {
continue;
}
try {
bin = new BufferedReader(new FileReader(status));
String readLine = bin.readLine();
if (readLine != null && readLine.contains("indicator-app")) {
// make sure we can also load the library (it might be the wrong version)
try {
return selectType(TrayType.AppIndicator);
} catch (Exception e) {
if (DEBUG) {
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK",
e);
}
else {
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK");
}
}
break;
}
} finally {
IO.closeQuietly(bin);
}
}
}
} catch (Throwable e) {
if (DEBUG) {
logger.error("Error detecting gnome version", e);
}
}
}
return null;
}
@SuppressWarnings({"ConstantConditions", "StatementWithEmptyBody"})
private static void init() {
private static
void init() {
if (systemTray != null) {
return;
}
@ -225,8 +406,30 @@ class SystemTray {
// }
// }
if (OS.isMacOsX()) {
// cannot mix AWT and JavaFX for MacOSX in java7 (fixed in java8) without special stuff.
// no tray in a headless environment
if (GraphicsEnvironment.isHeadless()) {
logger.error("Cannot use the SystemTray in a headless environment");
systemTrayMenu = null;
systemTray = null;
return;
}
boolean isNix = OS.isLinux() || OS.isUnix();
// Windows can ONLY use Swing (non-native) - AWT/native looks absolutely horrid and is not an option
// OSx can use Swing (non-native) or AWT (native) .
// Linux can use Swing (non-native), AWT (native), GtkStatusIcon (native), or AppIndicator (native)
if (OS.isWindows()) {
if (FORCE_TRAY_TYPE != TrayType.AutoDetect && FORCE_TRAY_TYPE != TrayType.Swing) {
// windows MUST use swing only!
FORCE_TRAY_TYPE = TrayType.AutoDetect;
logger.warn("Windows cannot use the '" + FORCE_TRAY_TYPE + "' SystemTray type, defaulting to Swing");
}
}
else if (OS.isMacOsX()) {
// cannot mix Swing/AWT and JavaFX for MacOSX in java7 (fixed in java8) without special stuff.
// https://bugs.openjdk.java.net/browse/JDK-8116017
// https://bugs.openjdk.java.net/browse/JDK-8118714
if (isJavaFxLoaded && OS.javaVersion <= 7 && !System.getProperty("javafx.macosx.embedded", "false").equals("true")) {
@ -247,35 +450,32 @@ class SystemTray {
// cannot mix Swing and SWT on MacOSX (for all versions of java) so we force native menus instead, which work just fine with SWT
// http://mail.openjdk.java.net/pipermail/bsd-port-dev/2008-December/000173.html
}
if (isSwtLoaded && FORCE_TRAY_TYPE == TrayType.Swing) {
if (AUTO_FIX_INCONSISTENCIES) {
logger.warn("Unable to load Swing + SWT (for all versions of Java). Using the AWT Tray type instead.");
// no tray in a headless environment
if (GraphicsEnvironment.isHeadless()) {
logger.error("Cannot use the SystemTray in a headless environment");
FORCE_TRAY_TYPE = TrayType.AWT;
}
else {
logger.error("Unable to load Swing + SWT (for all versions of Java). " +
"Please set `SystemTray.AUTO_FIX_INCONSISTENCIES=true;` to automatically fix this problem.\"");
systemTrayMenu = null;
systemTray = null;
return;
}
// Windows can ONLY use Swing (non-native) - AWT/native looks absolutely horrid and is not an option
// OSx can use Swing (non-native) or AWT (native) .
// Linux can use Swing (non-native), AWT (native), GtkStatusIcon (native), or AppIndicator (native)
if (OS.isWindows()) {
if (FORCE_TRAY_TYPE != TrayType.AutoDetect && FORCE_TRAY_TYPE != TrayType.Swing) {
// windows MUST use swing only!
FORCE_TRAY_TYPE = TrayType.AutoDetect;
logger.warn("Windows cannot use the '" + FORCE_TRAY_TYPE + "' SystemTray type, defaulting to Swing");
systemTrayMenu = null;
systemTray = null;
return;
}
}
}
else if (OS.isMacOsX()) {
if (FORCE_TRAY_TYPE != TrayType.AutoDetect && FORCE_TRAY_TYPE != TrayType.Swing && FORCE_TRAY_TYPE != TrayType.AWT) {
// MacOsX MUST use swing (and AWT) only!
// MacOsX can only use swing and AWT
FORCE_TRAY_TYPE = TrayType.AutoDetect;
logger.warn("MacOS cannot use the '" + FORCE_TRAY_TYPE + "' SystemTray type, defaulting to Awt");
logger.warn("MacOS cannot use the '" + FORCE_TRAY_TYPE + "' SystemTray type, defaulting to the AWT Tray type instead.");
}
}
else if (OS.isLinux() || OS.isUnix()) {
else if (isNix) {
// linux/unix can use all of the tray types
// NOTE: if the UI uses the 'getSystemLookAndFeelClassName' and is on Linux, this will cause GTK2 to get loaded first,
// which will cause conflicts if one tries to use GTK3
if (!FORCE_GTK2 && !isJavaFxLoaded && !isSwtLoaded) {
@ -300,8 +500,9 @@ class SystemTray {
if (AUTO_FIX_INCONSISTENCIES) {
// we must use GTK2 because Swing is configured to use GTK2
logger.warn("Forcing GTK2 because the Swing UIManager is GTK2");
FORCE_GTK2 = true;
logger.warn("Forcing GTK2 because the Swing UIManager is GTK2");
} else {
logger.error("Unable to use the SystemTray when the Swing UIManager is configured to use the native L&F, which " +
"uses GTK2. This is incompatible with GTK3. " +
@ -309,6 +510,7 @@ class SystemTray {
systemTrayMenu = null;
systemTray = null;
return;
}
}
}
@ -335,8 +537,9 @@ class SystemTray {
return;
} else if (!isSwt_GTK3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) {
// we must use GTK2, because SWT is GTK2
logger.warn("Forcing GTK2 because SWT is GTK2");
FORCE_GTK2 = true;
logger.warn("Forcing GTK2 because SWT is GTK2");
}
}
else if (isJavaFxLoaded) {
@ -349,11 +552,12 @@ class SystemTray {
if (isJava_GTK3_Possible && FORCE_GTK2) {
// if we are java9, then we can change it -- otherwise we cannot.
if (OS.javaVersion == 9 && AUTO_FIX_INCONSISTENCIES) {
FORCE_GTK2 = false;
logger.warn("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is " +
"configured to use GTK2. Please configure JavaFX to use GTK2 (via `System.setProperty(\"jdk.gtk.version\", \"3\");`) " +
"before JavaFX is initialized, or set `SystemTray.FORCE_GTK2=false;` Undoing `FORCE_GTK2`.");
FORCE_GTK2 = false;
} else {
logger.error("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is configured to use " +
"GTK2. Please set `SystemTray.FORCE_GTK2=false;` if that is not possible then it will not work.");
@ -364,13 +568,14 @@ class SystemTray {
}
} else if (!isJava_GTK3_Possible && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) {
// we must use GTK2, because JavaFX is GTK2
logger.warn("Forcing GTK2 because JavaFX is GTK2");
FORCE_GTK2 = true;
logger.warn("Forcing GTK2 because JavaFX is GTK2");
}
}
}
Class<? extends Tray> trayType = null;
if (DEBUG) {
logger.debug("OS: {}", System.getProperty("os.name"));
@ -400,57 +605,24 @@ class SystemTray {
// this has to happen BEFORE any sort of swing system tray stuff is accessed
if (OS.isWindows()) {
try {
trayType = selectType(TrayType.Swing);
} catch (Throwable e) {
logger.error("You might need to grant the AWTPermission `accessSystemTray` to the SecurityManager.");
}
Class<? extends Tray> trayType;
if (SystemTray.FORCE_TRAY_TYPE == TrayType.AutoDetect) {
trayType = getAutoDetectTrayType();
} else {
trayType = selectTypeQuietly(SystemTray.FORCE_TRAY_TYPE);
}
else if (OS.isMacOsX()) {
if (SystemTray.FORCE_TRAY_TYPE != TrayType.AutoDetect) {
// macos can ONLY use the AWT tray indicator, if you want it to follow the L&F of the OS. If we force a specific type,
// then allow that.
try {
trayType = selectType(TrayType.AWT);
} catch (Throwable e) {
logger.error("You might need to grant the AWTPermission `accessSystemTray` to the SecurityManager.");
}
}
}
else if ((OS.isLinux() || OS.isUnix())) {
// see: https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running
// For funsies, SyncThing did a LOT of work on compatibility (unfortunate for us) in python.
// https://github.com/syncthing/syncthing-gtk/blob/b7a3bc00e3bb6d62365ae62b5395370f3dcc7f55/syncthing_gtk/statusicon.py
// this can never be swing
// don't check for SWING type at this spot, it is done elsewhere.
if (SystemTray.FORCE_TRAY_TYPE != TrayType.AutoDetect) {
trayType = selectTypeQuietly(SystemTray.FORCE_TRAY_TYPE);
}
if (SystemTray.FORCE_TRAY_TYPE == TrayType.Swing && isSwtLoaded) {
if (AUTO_FIX_INCONSISTENCIES) {
logger.warn("Forcing AWT because SWT cannot load Swing tray type.");
trayType = selectTypeQuietly(TrayType.AWT);
}
else {
logger.error("Cannot initialize Swing type if SWT is loaded.");
systemTrayMenu = null;
systemTray = null;
return;
}
}
// Ubuntu UNITY has issues with GtkStatusIcon (it won't work...)
// fix various incompatibilities
if (isNix) {
// Ubuntu UNITY has issues with GtkStatusIcon (it won't work at all...)
if (isTrayType(trayType, TrayType.GtkStatusIcon) && OSUtil.DesktopEnv.get() == OSUtil.DesktopEnv.Env.Unity && OSUtil.Linux.isUbuntu()) {
if (AUTO_FIX_INCONSISTENCIES) {
// we must use AppIndicator because Ubuntu Unity removed GtkStatusIcon support
logger.warn("Forcing AppIndicator because Ubuntu Unity display environment removed support for GtkStatusIcons.");
SystemTray.FORCE_TRAY_TYPE = TrayType.AppIndicator; // this is required because of checks inside of AppIndicator...
trayType = selectTypeQuietly(TrayType.AppIndicator);
logger.warn("Forcing AppIndicator because Ubuntu Unity display environment removed support for GtkStatusIcons.");
}
else {
logger.error("Unable to use the GtkStatusIcons when running on Ubuntu with the Unity display environment, and thus" +
@ -463,194 +635,26 @@ class SystemTray {
}
}
// quick check, because we know that unity uses app-indicator. Maybe REALLY old versions do not. We support 14.04 LTE at least
if (trayType == null) {
OSUtil.DesktopEnv.Env de = OSUtil.DesktopEnv.get();
if (DEBUG) {
logger.debug("Currently using the '{}' desktop environment", de);
}
switch (de) {
case Gnome: {
// check other DE / OS combos that are based on gnome
String GDM = System.getenv("GDMSESSION");
if (DEBUG) {
logger.debug("Currently using the '{}' session type", GDM);
}
if ("gnome".equalsIgnoreCase(GDM)) {
Tray.usingGnome = true;
// are we fedora? If so, what version?
// now, what VERSION of fedora? 23/24/25/? don't have AppIndicator installed, so we have to use GtkStatusIcon
if (OSUtil.Linux.isFedora()) {
if (DEBUG) {
logger.debug("Running Fedora");
}
// 23 is gtk, 24/25 is gtk (but also wrong size unless we adjust it. ImageUtil automatically does this)
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if (OSUtil.Linux.isUbuntu()) {
// so far, because of the interaction between gnome3 + ubuntu, the GtkStatusIcon miraculously works.
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if (OSUtil.Unix.isFreeBSD()) {
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
}
else {
// arch likely will have problems unless the correct/appropriate libraries are installed.
trayType = selectTypeQuietly(TrayType.AppIndicator);
}
}
else if ("cinnamon".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("default".equalsIgnoreCase(GDM)) {
// this can be gnome3 on debian
if (OSUtil.Linux.isDebian()) {
// note: Debian Gnome3 does NOT work! (tested on Debian 8.5 and 8.6 default installs)
logger.warn("Debian with Gnome detected. SystemTray support is not known to work.");
}
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("gnome-classic".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("gnome-fallback".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("ubuntu".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(TrayType.AppIndicator);
}
break;
}
case KDE: {
if (OSUtil.Linux.isFedora()) {
// Fedora KDE requires GtkStatusIcon
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
} else {
// kde (at least, plasma 5.5.6) requires appindicator
trayType = selectTypeQuietly(TrayType.AppIndicator);
}
// kde 5.8+ is "high DPI", so we need to adjust the scale. Image resize will do that
break;
}
case Unity: {
// Ubuntu Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell".
trayType = selectTypeQuietly(TrayType.AppIndicator);
break;
}
case XFCE: {
// NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted.
// see: https://ask.fedoraproject.org/en/question/23116/how-to-fix-missing-icons-in-program-menus-and-context-menus/
// see: https://git.gnome.org/browse/gtk+/commit/?id=627a03683f5f41efbfc86cc0f10e1b7c11e9bb25
// so far, it is OK to use GtkStatusIcon on XFCE <-> XFCE4 inclusive
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
break;
}
case LXDE: {
trayType = selectTypeQuietly(TrayType.GtkStatusIcon);
break;
}
case Pantheon: {
// elementaryOS. It only supports appindicator (not gtkstatusicon)
// http://bazaar.launchpad.net/~wingpanel-devs/wingpanel/trunk/view/head:/sample/SampleIndicator.vala
// ElementaryOS shows the checkbox on the right, everyone else is on the left.
// With eOS, we CANNOT show the spacer image. It does not work.
trayType = selectTypeQuietly(TrayType.AppIndicator);
break;
}
}
}
// Try to autodetect if we can use app indicators (or if we need to fallback to GTK indicators)
if (trayType == null) {
BufferedReader bin = null;
try {
// the ONLY guaranteed way to determine if indicator-application-service is running (and thus, using app-indicator),
// is to look through all /proc/<pid>/status, and first line should be Name:\tindicator-appli
File proc = new File("/proc");
File[] listFiles = proc.listFiles();
if (listFiles != null) {
for (File procs : listFiles) {
String name = procs.getName();
if (!Character.isDigit(name.charAt(0))) {
continue;
}
File status = new File(procs, "status");
if (!status.canRead()) {
continue;
}
try {
bin = new BufferedReader(new FileReader(status));
String readLine = bin.readLine();
if (readLine != null && readLine.contains("indicator-app")) {
// make sure we can also load the library (it might be the wrong version)
try {
trayType = selectType(TrayType.AppIndicator);
} catch (Exception e) {
if (DEBUG) {
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK", e);
} else {
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK");
}
}
break;
}
} finally {
IO.closeQuietly(bin);
}
}
}
} catch (Throwable e) {
if (DEBUG) {
logger.error("Error detecting gnome version", e);
}
}
}
// fallback...
if (trayType == null) {
logger.warn("Unable to determine the system window manager type. Using the Swing Tray type instead.");
trayType = selectTypeQuietly(TrayType.Swing);
}
// this is bad...
if (trayType == null) {
logger.error("SystemTray initialization failed. Unable to load the system tray native libraries. " +
"Using the Swing Tray type instead. Please write an issue and include your OS type and configuration");
trayType = selectTypeQuietly(TrayType.Swing);
}
if (isTrayType(trayType, TrayType.AppIndicator) && OSUtil.Linux.isRoot()) {
// if are we running as ROOT, there can be issues (definitely on Ubuntu 16.04, maybe others)!
logger.error("Attempting to load the SystemTray as the 'root/sudo' user. This will likely not work because of dbus " +
"restrictions. Using the Swing Tray type instead.");
if (AUTO_FIX_INCONSISTENCIES) {
trayType = selectTypeQuietly(TrayType.Swing);
trayType = selectTypeQuietly(TrayType.Swing);
logger.warn("Attempting to load the SystemTray as the 'root/sudo' user. This will likely not work because of dbus " +
"restrictions. Using the Swing Tray type instead.");
} else {
logger.error("Attempting to load the SystemTray as the 'root/sudo' user. This will likely NOT WORK because of dbus " +
"restrictions.");
}
}
}
if (trayType == null) {
// unsupported tray, or unknown type
trayType = selectTypeQuietly(TrayType.Swing);
// unsupported tray, or unknown type
logger.error("SystemTray initialization failed. (Unable to discover which implementation to use). Falling back to the Swing Tray.");
}
@ -658,56 +662,55 @@ class SystemTray {
// - appIndicator/gtk require strings (which is the path)
// - swing version loads as an image (which can be stream or path, we use path)
CacheUtil.tempDir = "SysTray";
CacheUtil.tempDir = "SystemTrayImages";
try {
// at this point, the tray type is what it should be. If there are failures or special cases, all types will fall back to
// Swing.
if (OS.isLinux() || OS.isUnix()) {
if (isNix) {
// NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3.
// appindicator3 doesn't support menu icons via GTK2!!
if (!Gtk.isLoaded) {
logger.error("Unable to initialize GTK! Something is severely wrong! Using the Swing Tray type instead.");
trayType = selectTypeQuietly(TrayType.Swing);
logger.error("Unable to initialize GTK! Something is severely wrong! Using the Swing Tray type instead.");
}
else if (OSUtil.Linux.isArch()) {
// arch linux is fun!
if (isTrayType(trayType, TrayType.AppIndicator)) {
if (isTrayType(trayType, TrayType.AppIndicator) && !AppIndicator.isLoaded) {
// appindicators
// requires the install of libappindicator which is GTK2 (as of 25DEC2016)
// requires the install of libappindicator3 which is GTK3 (as of 25DEC2016)
trayType = selectTypeQuietly(TrayType.Swing);
if (!AppIndicator.isLoaded) {
if (Gtk.isGtk2) {
logger.error("Unable to initialize AppIndicator for Arch linux, it requires GTK2! " +
"Please install libappindicator, for example: 'sudo pacman -S libappindicator'. " +
"Using the Swing Tray type instead.");
} else {
logger.error("Unable to initialize AppIndicator for Arch linux, it requires GTK3! " +
"Please install libappindicator3, for example: 'sudo pacman -S libappindicator3'. " +
"Using the Swing Tray type instead."); // GTK3
}
trayType = selectTypeQuietly(TrayType.Swing);
if (Gtk.isGtk2) {
logger.warn("Unable to initialize AppIndicator for Arch linux, it requires GTK2! " +
"Please install libappindicator, for example: 'sudo pacman -S libappindicator'. " +
"Using the Swing Tray type instead.");
} else {
logger.error("Unable to initialize AppIndicator for Arch linux, it requires GTK3! " +
"Please install libappindicator3, for example: 'sudo pacman -S libappindicator3'. " +
"Using the Swing Tray type instead."); // GTK3
}
}
}
else if (isTrayType(trayType, TrayType.AppIndicator)) {
if (Gtk.isGtk2 && AppIndicator.isVersion3) {
trayType = selectTypeQuietly(TrayType.Swing);
logger.warn("AppIndicator3 detected with GTK2, falling back to GTK2 system tray type. " +
"Please install libappindicator1 OR GTK3, for example: 'sudo apt-get install libappindicator1'. " +
"Using the Swing Tray type instead.");
trayType = selectTypeQuietly(TrayType.Swing);
}
else if (!AppIndicator.isLoaded) {
// YIKES. Try to fallback to GtkStatusIndicator, since AppIndicator couldn't load.
logger.warn("Unable to initialize the AppIndicator correctly. Using the Swing Tray type instead.");
trayType = selectTypeQuietly(TrayType.Swing);
logger.warn("Unable to initialize the AppIndicator correctly. Using the Swing Tray type instead.");
}
}
}
@ -724,7 +727,7 @@ class SystemTray {
Swt.init();
}
if (OS.isLinux() || OS.isUnix()) {
if (isNix) {
// linux/unix need access to GTK, so load it up before the tray is loaded!
Gtk.startGui();
Gtk.waitForEventsToComplete();
@ -737,8 +740,9 @@ class SystemTray {
if (AUTO_FIX_INCONSISTENCIES) {
// this logic has to be before we create the system Tray
// this logic has to be before we create the system Tray, but after GTK is started (if applicable)
if (OS.isWindows()) {
// windows hard-codes the image size
SystemTrayFixes.fixWindows(trayImageSize);
@ -747,7 +751,7 @@ class SystemTray {
// macosx doesn't respond to all buttons (but should)
SystemTrayFixes.fixMacOS();
}
else if ((OS.isLinux() || OS.isUnix())) {
else if (isNix) {
// linux/mac doesn't have transparent backgrounds for swing and hard-codes the image size
SystemTrayFixes.fixLinux(trayImageSize);
}
@ -755,8 +759,9 @@ class SystemTray {
if ((isJavaFxLoaded || isSwtLoaded) && SwingUtilities.isEventDispatchThread()) {
// oh boy! This WILL NOT WORK. Let the dev know
// This WILL NOT WORK. Let the dev know
logger.error("SystemTray initialization for JavaFX or SWT **CAN NOT** occur on the Swing Event Dispatch Thread " +
"(EDT). Something is seriously wrong.");
@ -765,23 +770,23 @@ class SystemTray {
return;
}
// javaFX and SWT should not start on the EDT!!
// if it's linux + native menus must not start on the EDT!
// _AwtTray must be constructed on the EDT however...
// javaFX and SWT **CAN NOT** start on the EDT!!
// linux + GTK/AppIndicator menus must not start on the EDT!
// AWT/Swing must be constructed on the EDT however...
if (isJavaFxLoaded || isSwtLoaded ||
((OS.isLinux() || OS.isUnix()) && NativeUI.class.isAssignableFrom(trayType) && trayType != _AwtTray.class)) {
(isNix && (isTrayType(trayType, TrayType.GtkStatusIcon) || isTrayType(trayType, TrayType.AppIndicator)))
) {
try {
reference.set((Tray) trayType.getConstructors()[0].newInstance(systemTray));
} catch (Exception e) {
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'", e);
}
} else {
if (trayType == _AwtTray.class) {
// ensure awt toolkit is initialized.
java.awt.Toolkit.getDefaultToolkit();
}
} else if (isTrayType(trayType, TrayType.Swing) || isTrayType(trayType, TrayType.AWT)) {
// ensure AWT toolkit is initialized.
java.awt.Toolkit.getDefaultToolkit();
// have to construct swing stuff inside the swing EDT
final Class<? extends Menu> finalTrayType = trayType;
@ -796,9 +801,11 @@ class SystemTray {
}
}
});
} else {
logger.error("Unable to create tray type: '{}'. Aborting!", trayType.getSimpleName());
}
} catch (Exception e) {
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'", e);
logger.error("Unable to create tray type: '{}'", trayType.getSimpleName(), e);
}
systemTrayMenu = reference.get();
@ -991,12 +998,12 @@ class SystemTray {
public
void setImage(final File imageFile) {
if (imageFile == null) {
throw new NullPointerException("imageFile cannot be null!");
throw new NullPointerException("imageFile");
}
final Menu menu = systemTrayMenu;
if (menu != null) {
menu.setImage(imageFile);
menu.setImage_(ImageResizeUtil.shouldResizeOrCache(true, imageFile));
}
}
@ -1010,12 +1017,12 @@ class SystemTray {
public
void setImage(final String imagePath) {
if (imagePath == null) {
throw new NullPointerException("imagePath cannot be null!");
throw new NullPointerException("imagePath");
}
final Tray tray = systemTrayMenu;
if (tray != null) {
tray.setImage(imagePath);
tray.setImage_(ImageResizeUtil.shouldResizeOrCache(true, imagePath));
}
}
@ -1029,12 +1036,12 @@ class SystemTray {
public
void setImage(final URL imageUrl) {
if (imageUrl == null) {
throw new NullPointerException("imageUrl cannot be null!");
throw new NullPointerException("imageUrl");
}
final Menu menu = systemTrayMenu;
if (menu != null) {
menu.setImage(imageUrl);
menu.setImage_(ImageResizeUtil.shouldResizeOrCache(true, imageUrl));
}
}
@ -1048,12 +1055,12 @@ class SystemTray {
public
void setImage(final InputStream imageStream) {
if (imageStream == null) {
throw new NullPointerException("imageStream cannot be null!");
throw new NullPointerException("imageStream");
}
final Menu menu = systemTrayMenu;
if (menu != null) {
menu.setImage(imageStream);
menu.setImage_(ImageResizeUtil.shouldResizeOrCache(true, imageStream));
}
}
@ -1067,12 +1074,12 @@ class SystemTray {
public
void setImage(final Image image) {
if (image == null) {
throw new NullPointerException("image cannot be null!");
throw new NullPointerException("image");
}
final Menu menu = systemTrayMenu;
if (menu != null) {
menu.setImage(image);
menu.setImage_(ImageResizeUtil.shouldResizeOrCache(true, image));
}
}
@ -1086,12 +1093,12 @@ class SystemTray {
public
void setImage(final ImageInputStream imageStream) {
if (imageStream == null) {
throw new NullPointerException("image cannot be null!");
throw new NullPointerException("image");
}
final Tray tray = systemTrayMenu;
if (tray != null) {
tray.setImage(imageStream);
tray.setImage_(ImageResizeUtil.shouldResizeOrCache(true, imageStream));
}
}