diff --git a/README.md b/README.md
index 70c4350..8b36d5a 100644
--- a/README.md
+++ b/README.md
@@ -62,8 +62,12 @@ SystemTray.TRAY_SIZE (type int, default value '22')
SystemTray.FORCE_GTK2 (type boolean, default value 'false')
- - Forces the system tray to always choose GTK2 (even when GTK3 might be available).
+ - Forces the system tray to always choose GTK2 (even when GTK3 might be available).
+
+SystemTray.FORCE_LINUX_TYPE (type int, default value '0')
+ - If != 0, forces the system tray in linux to be GTK (1) or AppIndicator (2). This is an advanced feature.
+
SystemTray.COMPATIBILITY_MODE (type boolean, default value 'false')
- Forces the system to enter into JavaFX/SWT compatibility mode, where it will use GTK2 AND will not start/stop the GTK main loop.
diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java
index 6b50651..821ec2a 100644
--- a/src/dorkbox/systemTray/SystemTray.java
+++ b/src/dorkbox/systemTray/SystemTray.java
@@ -19,7 +19,7 @@ import dorkbox.systemTray.linux.AppIndicatorTray;
import dorkbox.systemTray.linux.GnomeShellExtension;
import dorkbox.systemTray.linux.GtkSystemTray;
import dorkbox.systemTray.linux.jna.AppIndicator;
-import dorkbox.systemTray.linux.jna.GtkSupport;
+import dorkbox.systemTray.linux.jna.Gtk;
import dorkbox.systemTray.swing.SwingSystemTray;
import dorkbox.util.OS;
import dorkbox.util.Property;
@@ -53,6 +53,9 @@ public abstract
class SystemTray {
protected static final Logger logger = LoggerFactory.getLogger(SystemTray.class);
+ public static final int LINUX_GTK = 1;
+ public static final int LINUX_APP_INDICATOR = 2;
+
@Property
/** How long to wait when updating menu entries before the request times-out */
public static final int TIMEOUT = 2;
@@ -65,6 +68,10 @@ class SystemTray {
/** Forces the system tray to always choose GTK2 (even when GTK3 might be available). */
public static boolean FORCE_GTK2 = false;
+ @Property
+ /** If != 0, forces the system tray in linux to be GTK (1) or AppIndicator (2). This is an advanced feature. */
+ public static int FORCE_LINUX_TYPE = 0;
+
@Property
/**
* Forces the system to enter into JavaFX/SWT compatibility mode, where it will use GTK2 AND will not start/stop the GTK main loop.
@@ -152,87 +159,118 @@ class SystemTray {
// 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
- String XDG = System.getenv("XDG_CURRENT_DESKTOP");
- if ("Unity".equalsIgnoreCase(XDG)) {
- try {
- trayType = AppIndicatorTray.class;
- } catch (Throwable e) {
- if (DEBUG) {
- e.printStackTrace();
- }
+ // load up our libraries
+ // NOTE:
+ // ALSO WHAT VERSION OF GTK to use? appindiactor1 -> GTk2, appindicator3 -> GTK3.
+ // appindicator3 doesn't support menu icons via GTK2!!
+ if (Gtk.isGtk2 || AppIndicator.isVersion3) {
+ if (DEBUG) {
+ logger.trace("Loading libraries");
}
}
- else if ("XFCE".equalsIgnoreCase(XDG)) {
- try {
- trayType = AppIndicatorTray.class;
- } catch (Throwable e) {
- if (DEBUG) {
- e.printStackTrace();
- }
- // we can fail on AppIndicator, so this is the fallback
- //noinspection EmptyCatchBlock
- try {
- trayType = GtkSystemTray.class;
- } catch (Throwable e1) {
- if (DEBUG) {
- e1.printStackTrace();
- }
- }
- }
- }
- else if ("LXDE".equalsIgnoreCase(XDG)) {
+ if (SystemTray.FORCE_LINUX_TYPE == SystemTray.LINUX_GTK) {
try {
trayType = GtkSystemTray.class;
- } catch (Throwable e) {
+ } catch (Throwable e1) {
if (DEBUG) {
- e.printStackTrace();
+ e1.printStackTrace();
}
}
}
- else if ("KDE".equalsIgnoreCase(XDG)) {
- isKDE = true;
+ else if (SystemTray.FORCE_LINUX_TYPE == SystemTray.LINUX_APP_INDICATOR) {
try {
trayType = AppIndicatorTray.class;
- } catch (Throwable e) {
+ } catch (Throwable e1) {
if (DEBUG) {
- e.printStackTrace();
+ e1.printStackTrace();
}
}
}
- else if ("GNOME".equalsIgnoreCase(XDG)) {
- // check other DE
- String GDM = System.getenv("GDMSESSION");
- if ("cinnamon".equalsIgnoreCase(GDM)) {
- try {
- trayType = GtkSystemTray.class;
- } catch (Throwable e) {
- if (DEBUG) {
- e.printStackTrace();
- }
- }
- }
- else if ("gnome-classic".equalsIgnoreCase(GDM)) {
- try {
- trayType = GtkSystemTray.class;
- } catch (Throwable e) {
- if (DEBUG) {
- e.printStackTrace();
- }
- }
- }
- else if ("gnome-fallback".equalsIgnoreCase(GDM)) {
- try {
- trayType = GtkSystemTray.class;
- } catch (Throwable e) {
- if (DEBUG) {
- e.printStackTrace();
- }
- }
- }
+ // 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) {
+ String XDG = System.getenv("XDG_CURRENT_DESKTOP");
+ if ("Unity".equalsIgnoreCase(XDG)) {
+ try {
+ trayType = AppIndicatorTray.class;
+ } catch (Throwable e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+ else if ("XFCE".equalsIgnoreCase(XDG)) {
+ try {
+ trayType = AppIndicatorTray.class;
+ } catch (Throwable e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+
+ // we can fail on AppIndicator, so this is the fallback
+ //noinspection EmptyCatchBlock
+ try {
+ trayType = GtkSystemTray.class;
+ } catch (Throwable e1) {
+ if (DEBUG) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+ else if ("LXDE".equalsIgnoreCase(XDG)) {
+ try {
+ trayType = GtkSystemTray.class;
+ } catch (Throwable e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+ else if ("KDE".equalsIgnoreCase(XDG)) {
+ isKDE = true;
+ try {
+ trayType = AppIndicatorTray.class;
+ } catch (Throwable e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+ else if ("GNOME".equalsIgnoreCase(XDG)) {
+ // check other DE
+ String GDM = System.getenv("GDMSESSION");
+
+ if ("cinnamon".equalsIgnoreCase(GDM)) {
+ try {
+ trayType = GtkSystemTray.class;
+ } catch (Throwable e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+ else if ("gnome-classic".equalsIgnoreCase(GDM)) {
+ try {
+ trayType = GtkSystemTray.class;
+ } catch (Throwable e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+ else if ("gnome-fallback".equalsIgnoreCase(GDM)) {
+ try {
+ trayType = GtkSystemTray.class;
+ } catch (Throwable e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
// is likely 'gnome', but it can also be unknown (or something completely different), install extension and go from there
if (trayType == null) {
@@ -292,8 +330,6 @@ class SystemTray {
if (readLine != null && readLine.contains("indicator-app")) {
// make sure we can also load the library (it might be the wrong version)
try {
- //noinspection unused
- final AppIndicator instance = AppIndicator.INSTANCE;
trayType = AppIndicatorTray.class;
} catch (Throwable e) {
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK");
@@ -364,15 +400,11 @@ class SystemTray {
try {
ImageUtil.init();
- // the order of checking here is critical -- AppIndicator.IS_VERSION_3 initializes `appindicator` and `gtk`
if (OS.isLinux() &&
trayType == AppIndicatorTray.class &&
- AppIndicator.IS_VERSION_3 && // this initializes the appindicator (since we specified that via the trayType)
- GtkSupport.isGtk2) {
+ Gtk.isGtk2 &&
+ AppIndicator.isVersion3) {
- // NOTE:
- // ALSO WHAT VERSION OF GTK to use? appindiactor1 -> GTk2, appindicator3 -> GTK3.
- // appindicator3 doesn't support menu icons via GTK2. AT THIS POINT, we DO NOT have GTK3
try {
trayType = GtkSystemTray.class;
logger.warn("AppIndicator3 detected with GTK2, falling back to GTK2 system tray type. " +
@@ -382,8 +414,8 @@ class SystemTray {
e.printStackTrace();
}
logger.error("AppIndicator3 detected with GTK2 and unable to fallback to using GTK2 system tray type." +
- "AppIndicator3 requires GTK3 to be fully functional, and while this will work -- the menu icons WILL " +
- "NOT be visible." +
+ "AppIndicator3 requires GTK3 to be fully functional, and while this will work -- " +
+ "the menu icons WILL NOT be visible." +
" Please install libappindicator1 OR GTK3, for example: 'sudo apt-get install libappindicator1'");
}
}
diff --git a/src/dorkbox/systemTray/linux/AppIndicatorTray.java b/src/dorkbox/systemTray/linux/AppIndicatorTray.java
index e5bed2b..f4c4792 100644
--- a/src/dorkbox/systemTray/linux/AppIndicatorTray.java
+++ b/src/dorkbox/systemTray/linux/AppIndicatorTray.java
@@ -16,8 +16,11 @@
package dorkbox.systemTray.linux;
import com.sun.jna.Pointer;
+import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.linux.jna.AppIndicator;
-import dorkbox.systemTray.linux.jna.GtkSupport;
+import dorkbox.systemTray.linux.jna.AppIndicatorInstanceStruct;
+import dorkbox.systemTray.linux.jna.Gobject;
+import dorkbox.systemTray.linux.jna.Gtk;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,9 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public
class AppIndicatorTray extends GtkTypeSystemTray {
- private static final AppIndicator appindicator = AppIndicator.INSTANCE;
-
- private AppIndicator.AppIndicatorInstanceStruct appIndicator;
+ private AppIndicatorInstanceStruct appIndicator;
private boolean isActive = false;
// This is required if we have JavaFX or SWT shutdown hooks (to prevent us from shutting down twice...)
@@ -41,14 +42,18 @@ class AppIndicatorTray extends GtkTypeSystemTray {
public
AppIndicatorTray() {
- GtkSupport.startGui();
+ if (SystemTray.FORCE_LINUX_TYPE == SystemTray.LINUX_GTK) {
+ // if we force GTK type system tray, don't attempt to load AppIndicator libs
+ throw new IllegalArgumentException("Unable to start AppIndicator if 'SystemTray.FORCE_LINUX_TYPE' is set to GTK");
+ }
+
+ Gtk.startGui();
dispatch(new Runnable() {
@Override
public
void run() {
- appIndicator = appindicator.app_indicator_new(System.nanoTime() + "DBST", "",
- AppIndicator.CATEGORY_APPLICATION_STATUS);
+ appIndicator = AppIndicator.app_indicator_new(System.nanoTime() + "DBST", "", AppIndicator.CATEGORY_APPLICATION_STATUS);
}
});
}
@@ -62,9 +67,9 @@ class AppIndicatorTray extends GtkTypeSystemTray {
public
void run() {
// STATUS_PASSIVE hides the indicator
- appindicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_PASSIVE);
+ AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_PASSIVE);
Pointer p = appIndicator.getPointer();
- gobject.g_object_unref(p);
+ Gobject.g_object_unref(p);
appIndicator = null;
}
@@ -81,12 +86,12 @@ class AppIndicatorTray extends GtkTypeSystemTray {
@Override
public
void run() {
- appindicator.app_indicator_set_icon(appIndicator, iconPath);
+ AppIndicator.app_indicator_set_icon(appIndicator, iconPath);
if (!isActive) {
isActive = true;
- appindicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_ACTIVE);
+ AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_ACTIVE);
}
}
});
@@ -98,6 +103,6 @@ class AppIndicatorTray extends GtkTypeSystemTray {
protected
void onMenuAdded(final Pointer menu) {
// see: https://code.launchpad.net/~mterry/libappindicator/fix-menu-leak/+merge/53247
- appindicator.app_indicator_set_menu(appIndicator, menu);
+ AppIndicator.app_indicator_set_menu(appIndicator, menu);
}
}
diff --git a/src/dorkbox/systemTray/linux/GtkMenuEntry.java b/src/dorkbox/systemTray/linux/GtkMenuEntry.java
index 318f870..082e8a5 100644
--- a/src/dorkbox/systemTray/linux/GtkMenuEntry.java
+++ b/src/dorkbox/systemTray/linux/GtkMenuEntry.java
@@ -20,10 +20,9 @@ import com.sun.jna.Pointer;
import dorkbox.systemTray.ImageUtil;
import dorkbox.systemTray.MenuEntry;
import dorkbox.systemTray.SystemTrayMenuAction;
+import dorkbox.systemTray.linux.jna.GCallback;
import dorkbox.systemTray.linux.jna.Gobject;
-import dorkbox.systemTray.linux.jna.Gobject.GCallback;
import dorkbox.systemTray.linux.jna.Gtk;
-import dorkbox.systemTray.linux.jna.GtkSupport;
import java.io.InputStream;
import java.net.URL;
@@ -33,9 +32,6 @@ class GtkMenuEntry implements MenuEntry, GCallback {
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
private final int id = ID_COUNTER.getAndIncrement();
- private static final Gtk gtk = Gtk.INSTANCE;
- private static final Gobject gobject = Gobject.INSTANCE;
-
final Pointer menuItem;
final GtkTypeSystemTray parent;
@@ -56,20 +52,20 @@ class GtkMenuEntry implements MenuEntry, GCallback {
this.text = label;
this.callback = callback;
- menuItem = gtk.gtk_image_menu_item_new_with_label(label);
+ menuItem = Gtk.gtk_image_menu_item_new_with_label(label);
if (imagePath != null && !imagePath.isEmpty()) {
// NOTE: XFCE uses 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
- image = gtk.gtk_image_new_from_file(imagePath);
+ image = Gtk.gtk_image_new_from_file(imagePath);
- gtk.gtk_image_menu_item_set_image(menuItem, image);
+ Gtk.gtk_image_menu_item_set_image(menuItem, image);
// must always re-set always-show after setting the image
- gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE);
+ Gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE);
}
- nativeLong = gobject.g_signal_connect_object(menuItem, "activate", this, null, 0);
+ nativeLong = Gobject.g_signal_connect_object(menuItem, "activate", this, null, 0);
}
@@ -98,41 +94,41 @@ class GtkMenuEntry implements MenuEntry, GCallback {
@Override
public
void setText(final String newText) {
- GtkSupport.dispatch(new Runnable() {
+ Gtk.dispatch(new Runnable() {
@Override
public
void run() {
text = newText;
- gtk.gtk_menu_item_set_label(menuItem, newText);
+ Gtk.gtk_menu_item_set_label(menuItem, newText);
- gtk.gtk_widget_show_all(menuItem);
+ Gtk.gtk_widget_show_all(menuItem);
}
});
}
private
void setImage_(final String imagePath) {
- GtkSupport.dispatch(new Runnable() {
+ Gtk.dispatch(new Runnable() {
@Override
public
void run() {
if (image != null) {
- gtk.gtk_widget_destroy(image);
+ Gtk.gtk_widget_destroy(image);
image = null;
}
- gtk.gtk_widget_show_all(menuItem);
+ Gtk.gtk_widget_show_all(menuItem);
if (imagePath != null && !imagePath.isEmpty()) {
- image = gtk.gtk_image_new_from_file(imagePath);
- gtk.gtk_image_menu_item_set_image(menuItem, image);
- gobject.g_object_ref_sink(image);
+ image = Gtk.gtk_image_new_from_file(imagePath);
+ Gtk.gtk_image_menu_item_set_image(menuItem, image);
+ Gobject.g_object_ref_sink(image);
// must always re-set always-show after setting the image
- gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE);
+ Gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE);
}
- gtk.gtk_widget_show_all(menuItem);
+ Gtk.gtk_widget_show_all(menuItem);
}
});
}
@@ -193,7 +189,7 @@ class GtkMenuEntry implements MenuEntry, GCallback {
*/
public
void remove() {
- GtkSupport.dispatch(new Runnable() {
+ Gtk.dispatch(new Runnable() {
@Override
public
void run() {
@@ -208,13 +204,13 @@ class GtkMenuEntry implements MenuEntry, GCallback {
void removePrivate() {
callback = null;
- gtk.gtk_menu_shell_deactivate(parent.getMenu(), menuItem);
+ Gtk.gtk_menu_shell_deactivate(parent.getMenu(), menuItem);
if (image != null) {
- gtk.gtk_widget_destroy(image);
+ Gtk.gtk_widget_destroy(image);
}
- gtk.gtk_widget_destroy(menuItem);
+ Gtk.gtk_widget_destroy(menuItem);
}
@Override
diff --git a/src/dorkbox/systemTray/linux/GtkSystemTray.java b/src/dorkbox/systemTray/linux/GtkSystemTray.java
index 00d39db..c8cad3e 100644
--- a/src/dorkbox/systemTray/linux/GtkSystemTray.java
+++ b/src/dorkbox/systemTray/linux/GtkSystemTray.java
@@ -17,9 +17,10 @@ package dorkbox.systemTray.linux;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
+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 dorkbox.systemTray.linux.jna.GtkSupport;
import java.util.ArrayList;
import java.util.List;
@@ -45,29 +46,28 @@ class GtkSystemTray extends GtkTypeSystemTray {
public
GtkSystemTray() {
super();
- GtkSupport.startGui();
+ Gtk.startGui();
dispatch(new Runnable() {
@Override
public
void run() {
- final Pointer trayIcon_ = gtk.gtk_status_icon_new();
- gtk.gtk_status_icon_set_title(trayIcon_, GnomeShellExtension.UID);
- gtk.gtk_status_icon_set_name(trayIcon_, "SystemTray");
+ final Pointer trayIcon_ = Gtk.gtk_status_icon_new();
+ Gtk.gtk_status_icon_set_name(trayIcon_, "SystemTray");
trayIcon = trayIcon_;
- final Gobject.GEventCallback gtkCallback = new Gobject.GEventCallback() {
+ final GEventCallback gtkCallback = new GEventCallback() {
@Override
public
- void callback(Pointer notUsed, final Gtk.GdkEventButton event) {
+ void callback(Pointer notUsed, final GdkEventButton event) {
// BUTTON_PRESS only (any mouse click)
if (event.type == 4) {
- gtk.gtk_menu_popup(getMenu(), null, null, Gtk.gtk_status_icon_position_menu, trayIcon, 0, event.time);
+ Gtk.gtk_menu_popup(getMenu(), null, null, Gtk.gtk_status_icon_position_menu, trayIcon, 0, event.time);
}
}
};
- final NativeLong button_press_event = gobject.g_signal_connect_object(trayIcon, "button_press_event", gtkCallback, null, 0);
+ final NativeLong button_press_event = Gobject.g_signal_connect_object(trayIcon, "button_press_event", gtkCallback, null, 0);
// have to do this to prevent GC on these objects
gtkCallbacks.add(gtkCallback);
@@ -86,8 +86,8 @@ class GtkSystemTray extends GtkTypeSystemTray {
public
void run() {
// this hides the indicator
- gtk.gtk_status_icon_set_visible(trayIcon, false);
- gobject.g_object_unref(trayIcon);
+ Gtk.gtk_status_icon_set_visible(trayIcon, false);
+ Gobject.g_object_unref(trayIcon);
// mark for GC
trayIcon = null;
@@ -106,11 +106,11 @@ class GtkSystemTray extends GtkTypeSystemTray {
@Override
public
void run() {
- gtk.gtk_status_icon_set_from_file(trayIcon, iconPath);
+ Gtk.gtk_status_icon_set_from_file(trayIcon, iconPath);
if (!isActive) {
isActive = true;
- gtk.gtk_status_icon_set_visible(trayIcon, true);
+ Gtk.gtk_status_icon_set_visible(trayIcon, true);
}
}
});
diff --git a/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java b/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java
index d1883da..c73eaa6 100644
--- a/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java
+++ b/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java
@@ -22,7 +22,6 @@ import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.SystemTrayMenuAction;
import dorkbox.systemTray.linux.jna.Gobject;
import dorkbox.systemTray.linux.jna.Gtk;
-import dorkbox.systemTray.linux.jna.GtkSupport;
import java.io.InputStream;
import java.net.URL;
@@ -33,9 +32,6 @@ import java.net.URL;
*/
public abstract
class GtkTypeSystemTray extends SystemTray {
- protected static final Gobject gobject = Gobject.INSTANCE;
- protected static final Gtk gtk = Gtk.INSTANCE;
-
private volatile Pointer menu;
private volatile Pointer connectionStatusItem;
@@ -44,19 +40,19 @@ class GtkTypeSystemTray extends SystemTray {
@Override
protected
void dispatch(final Runnable runnable) {
- GtkSupport.dispatch(runnable);
+ Gtk.dispatch(runnable);
}
@Override
public
void shutdown() {
- GtkSupport.dispatch(new Runnable() {
+ Gtk.dispatch(new Runnable() {
@Override
public
void run() {
obliterateMenu();
- GtkSupport.shutdownGui();
+ Gtk.shutdownGui();
}
});
}
@@ -72,7 +68,7 @@ class GtkTypeSystemTray extends SystemTray {
void setStatus(final String statusText) {
this.statusText = statusText;
- GtkSupport.dispatch(new Runnable() {
+ Gtk.dispatch(new Runnable() {
@Override
public
void run() {
@@ -81,16 +77,16 @@ class GtkTypeSystemTray extends SystemTray {
if (connectionStatusItem == null && statusText != null && !statusText.isEmpty()) {
deleteMenu();
- connectionStatusItem = gtk.gtk_menu_item_new_with_label("");
+ connectionStatusItem = Gtk.gtk_menu_item_new_with_label("");
// evil hacks abound...
- Pointer label = gtk.gtk_bin_get_child(connectionStatusItem);
- gtk.gtk_label_set_use_markup(label, Gtk.TRUE);
- Pointer markup = gobject.g_markup_printf_escaped("%s", statusText);
- gtk.gtk_label_set_markup(label, markup);
- gobject.g_free(markup);
+ Pointer label = Gtk.gtk_bin_get_child(connectionStatusItem);
+ Gtk.gtk_label_set_use_markup(label, Gtk.TRUE);
+ Pointer markup = Gobject.g_markup_printf_escaped("%s", statusText);
+ Gtk.gtk_label_set_markup(label, markup);
+ Gobject.g_free(markup);
- gtk.gtk_widget_set_sensitive(connectionStatusItem, Gtk.FALSE);
+ Gtk.gtk_widget_set_sensitive(connectionStatusItem, Gtk.FALSE);
createMenu();
}
@@ -98,10 +94,10 @@ class GtkTypeSystemTray extends SystemTray {
if (statusText == null || statusText.isEmpty()) {
// this means the status text already exists, and we are removing it
- gtk.gtk_container_remove(menu, connectionStatusItem);
+ Gtk.gtk_container_remove(menu, connectionStatusItem);
connectionStatusItem = null; // because we manually delete it
- gtk.gtk_widget_show_all(menu);
+ Gtk.gtk_widget_show_all(menu);
deleteMenu();
createMenu();
@@ -113,13 +109,13 @@ class GtkTypeSystemTray extends SystemTray {
// libgtk.gtk_menu_item_set_label(this.connectionStatusItem, statusText);
// evil hacks abound...
- Pointer label = gtk.gtk_bin_get_child(connectionStatusItem);
- gtk.gtk_label_set_use_markup(label, Gtk.TRUE);
- Pointer markup = gobject.g_markup_printf_escaped("%s", statusText);
- gtk.gtk_label_set_markup(label, markup);
- gobject.g_free(markup);
+ Pointer label = Gtk.gtk_bin_get_child(connectionStatusItem);
+ Gtk.gtk_label_set_use_markup(label, Gtk.TRUE);
+ Pointer markup = Gobject.g_markup_printf_escaped("%s", statusText);
+ Gtk.gtk_label_set_markup(label, markup);
+ Gobject.g_free(markup);
- gtk.gtk_widget_show_all(menu);
+ Gtk.gtk_widget_show_all(menu);
}
}
}
@@ -135,8 +131,8 @@ class GtkTypeSystemTray extends SystemTray {
if (menu != null) {
// have to remove status from menu (but not destroy the object)
if (connectionStatusItem != null) {
- gobject.g_object_force_floating(connectionStatusItem);
- gtk.gtk_container_remove(menu, connectionStatusItem);
+ Gobject.g_object_force_floating(connectionStatusItem);
+ Gtk.gtk_container_remove(menu, connectionStatusItem);
}
// have to remove all other menu entries
@@ -144,16 +140,16 @@ class GtkTypeSystemTray extends SystemTray {
for (int i = 0; i < menuEntries.size(); i++) {
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
- gobject.g_object_force_floating(menuEntry__.menuItem);
- gtk.gtk_container_remove(menu, menuEntry__.menuItem);
+ Gobject.g_object_force_floating(menuEntry__.menuItem);
+ Gtk.gtk_container_remove(menu, menuEntry__.menuItem);
}
- gtk.gtk_widget_destroy(menu);
+ Gtk.gtk_widget_destroy(menu);
}
}
// makes a new one
- menu = gtk.gtk_menu_new();
+ menu = Gtk.gtk_menu_new();
}
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
@@ -161,8 +157,8 @@ class GtkTypeSystemTray extends SystemTray {
void createMenu() {
// now add status
if (connectionStatusItem != null) {
- gtk.gtk_menu_shell_append(this.menu, this.connectionStatusItem);
- gobject.g_object_ref_sink(connectionStatusItem);
+ Gtk.gtk_menu_shell_append(this.menu, this.connectionStatusItem);
+ Gobject.g_object_ref_sink(connectionStatusItem);
}
// now add back other menu entries
@@ -171,12 +167,12 @@ class GtkTypeSystemTray extends SystemTray {
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
- gtk.gtk_menu_shell_append(this.menu, menuEntry__.menuItem);
- gobject.g_object_ref_sink(menuEntry__.menuItem);
+ Gtk.gtk_menu_shell_append(this.menu, menuEntry__.menuItem);
+ Gobject.g_object_ref_sink(menuEntry__.menuItem);
}
onMenuAdded(menu);
- gtk.gtk_widget_show_all(menu);
+ Gtk.gtk_widget_show_all(menu);
}
}
@@ -188,7 +184,7 @@ class GtkTypeSystemTray extends SystemTray {
if (menu != null) {
// have to remove status from menu
if (connectionStatusItem != null) {
- gtk.gtk_widget_destroy(connectionStatusItem);
+ Gtk.gtk_widget_destroy(connectionStatusItem);
connectionStatusItem = null;
}
@@ -201,7 +197,7 @@ class GtkTypeSystemTray extends SystemTray {
}
menuEntries.clear();
- gtk.gtk_widget_destroy(menu);
+ Gtk.gtk_widget_destroy(menu);
}
}
}
@@ -226,7 +222,7 @@ class GtkTypeSystemTray extends SystemTray {
throw new NullPointerException("Menu text cannot be null");
}
- GtkSupport.dispatch(new Runnable() {
+ Gtk.dispatch(new Runnable() {
@Override
public
void run() {
diff --git a/src/dorkbox/systemTray/linux/jna/AppIndicator.java b/src/dorkbox/systemTray/linux/jna/AppIndicator.java
index 9106945..13ad8ce 100644
--- a/src/dorkbox/systemTray/linux/jna/AppIndicator.java
+++ b/src/dorkbox/systemTray/linux/jna/AppIndicator.java
@@ -15,55 +15,176 @@
*/
package dorkbox.systemTray.linux.jna;
-import com.sun.jna.Library;
+import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-import dorkbox.util.Keep;
+import dorkbox.systemTray.SystemTray;
-import java.util.Arrays;
-import java.util.List;
-
-/* bindings for libappindicator */
+/**
+ * bindings for libappindicator
+ *
+ * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
+ */
+@SuppressWarnings("Duplicates")
public
-interface AppIndicator extends Library {
- // effing retarded. There are DIFFERENT versions, of which they all share the same basic compatibility (of the methods that
- // we use), however -- we cannot just LOAD via the 'base-name', we actually have to try each one. There are bash commands that
- // will tell us the linked library name, however - I'd rather not run bash commands to determine this.
- // This is so hacky it makes me sick.
- AppIndicator INSTANCE = AppIndicatorQuery.get();
+class AppIndicator {
+ public static boolean isVersion3 = false;
- /** Necessary to provide warnings, because libappindicator3 won't properly work with GTK2 */
- boolean IS_VERSION_3 = AppIndicatorQuery.isVersion3;
+ private static boolean isLoaded = false;
- int CATEGORY_APPLICATION_STATUS = 0;
- int CATEGORY_COMMUNICATIONS = 1;
- int CATEGORY_SYSTEM_SERVICES = 2;
- int CATEGORY_HARDWARE = 3;
- int CATEGORY_OTHER = 4;
+ /**
+ * Loader for AppIndicator, because it is absolutely mindboggling how those whom maintain the standard, can't agree to what that
+ * standard library naming convention or features/API set is. We just try until we find one that work, and are able to map the
+ * symbols we need. There are bash commands that will tell us the linked library name, however - I'd rather not run bash commands
+ * to determine this.
+ *
+ * This is so hacky it makes me sick.
+ */
+ static {
+ // objdump -T /usr/lib/x86_64-linux-gnu/libappindicator.so.1 | grep foo
+ // objdump -T /usr/lib/x86_64-linux-gnu/libappindicator3.so.1 | grep foo
- int STATUS_PASSIVE = 0;
- int STATUS_ACTIVE = 1;
- int STATUS_ATTENTION = 2;
+ // NOTE:
+ // ALSO WHAT VERSION OF GTK to use? appindiactor1 -> GTk2, appindicator3 -> GTK3.
+ // appindicator3 doesn't support menu icons via GTK2!!
+
+ if (SystemTray.FORCE_LINUX_TYPE == SystemTray.LINUX_GTK) {
+ // if we force GTK type system tray, don't attempt to load AppIndicator libs
+ isLoaded = true;
+ }
+
+ if (!isLoaded && (SystemTray.FORCE_GTK2 || SystemTray.COMPATIBILITY_MODE)) {
+ // if specified, try loading appindicator1 first, maybe it's there?
+ try {
+ final NativeLibrary library = JnaHelper.register("appindicator1", AppIndicator.class);
+ if (library != null) {
+ isLoaded = true;
+ }
+ } catch (Throwable ignored) {
+ }
+ }
+
+ String nameToCheck1;
+ String nameToCheck2;
+ if (Gtk.isGtk2) {
+ nameToCheck1 = "appindicator";
+ }
+ else {
+ nameToCheck1 = "appindicator3";
+ }
+
+ // start with base version using whatever the OS specifies as the proper symbolic link
+ if (!isLoaded) {
+ try {
+ final NativeLibrary library = JnaHelper.register(nameToCheck1, AppIndicator.class);
+ String s = library.getName();
+ if (s.contains("appindicator3")) {
+ isVersion3 = true;
+ }
+
+ isLoaded = true;
+ } catch (Throwable ignored) {
+ }
+ }
+
+ // whoops. Symbolic links are bugged out. Look manually for it...
+ // Super hacky way to do this.
+ if (!isLoaded) {
+ if (Gtk.isGtk2) {
+ // have to check gtk2 first
+ for (int i = 0; i <= 10; i++) {
+ if (!isLoaded) {
+ try {
+ final NativeLibrary library = JnaHelper.register("appindicator" + i, AppIndicator.class);
+
+ String s = library.getName();
+ // version 3 WILL NOT work with icons in the menu. This allows us to show a warning (in the System tray initialization)
+ if (i == 3 || s.contains("appindicator3")) {
+ isVersion3 = true;
+ }
+
+ isLoaded = true;
+ break;
+ } catch (Throwable ignored) {
+ }
+ }
+ }
+
+ } else {
+ // have to check gtk3 first (maybe it's there?)
+ for (int i = 10; i >= 0; i--) {
+ if (!isLoaded) {
+ try {
+ final NativeLibrary library = JnaHelper.register("appindicator" + i, AppIndicator.class);
+
+ String s = library.getName();
+ // version 3 WILL NOT work with icons in the menu. This allows us to show a warning (in the System tray initialization)
+ if (i == 3 || s.contains("appindicator3")) {
+ isVersion3 = true;
+ }
+
+ isLoaded = true;
+ break;
+ } catch (Throwable ignored) {
+ }
+ }
+ }
+ }
- @Keep
- class AppIndicatorInstanceStruct extends Structure {
- public Gobject.GObjectStruct parent;
- public Pointer priv;
+ }
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("parent", "priv");
+ // If we are GTK2, change the order we check and load libraries
+
+ if (Gtk.isGtk2) {
+ nameToCheck1 = "appindicator-gtk";
+ nameToCheck2 = "appindicator-gtk3";
+ }
+ else {
+ nameToCheck1 = "appindicator-gtk3";
+ nameToCheck2 = "appindicator-gtk";
+ }
+
+ // another type. who knows...
+ if (!isLoaded) {
+ try {
+ JnaHelper.register(nameToCheck1, AppIndicator.class);
+ isLoaded = true;
+ } catch (Throwable ignored) {
+ }
+ }
+
+ // this is HORRID. such a PITA
+ if (!isLoaded) {
+ try {
+ JnaHelper.register(nameToCheck2, AppIndicator.class);
+ isLoaded = true;
+ } catch (Throwable ignored) {
+ }
+ }
+
+ if (!isLoaded) {
+ throw new RuntimeException("We apologize for this, but we are unable to determine which the appIndicator library is in use, if " +
+ "or even if it is in use... Please create an issue for this and include your OS type and configuration.");
}
}
// Note: AppIndicators DO NOT support tooltips, as per mark shuttleworth. Rather stupid IMHO.
// See: https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12
- AppIndicatorInstanceStruct app_indicator_new(String id, String icon_name, int category);
+ public static final int CATEGORY_APPLICATION_STATUS = 0;
+ public static final int CATEGORY_COMMUNICATIONS = 1;
+ public static final int CATEGORY_SYSTEM_SERVICES = 2;
+ public static final int CATEGORY_HARDWARE = 3;
+ public static final int CATEGORY_OTHER = 4;
- void app_indicator_set_status(AppIndicatorInstanceStruct self, int status);
- void app_indicator_set_menu(AppIndicatorInstanceStruct self, Pointer menu);
- void app_indicator_set_icon(AppIndicatorInstanceStruct self, String icon_name);
+ public static final int STATUS_PASSIVE = 0;
+ public static final int STATUS_ACTIVE = 1;
+ public static final int STATUS_ATTENTION = 2;
+
+
+ public static native AppIndicatorInstanceStruct app_indicator_new(String id, String icon_name, int category);
+
+ public static native void app_indicator_set_status(AppIndicatorInstanceStruct self, int status);
+ public static native void app_indicator_set_menu(AppIndicatorInstanceStruct self, Pointer menu);
+ public static native void app_indicator_set_icon(AppIndicatorInstanceStruct self, String icon_name);
}
diff --git a/src/dorkbox/systemTray/linux/jna/AppIndicatorInstanceStruct.java b/src/dorkbox/systemTray/linux/jna/AppIndicatorInstanceStruct.java
new file mode 100644
index 0000000..6ac662b
--- /dev/null
+++ b/src/dorkbox/systemTray/linux/jna/AppIndicatorInstanceStruct.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 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.linux.jna;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import dorkbox.util.Keep;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Keep
+public
+class AppIndicatorInstanceStruct extends Structure {
+ public GObjectStruct parent;
+ public Pointer priv;
+
+ @Override
+ protected
+ List getFieldOrder() {
+ return Arrays.asList("parent", "priv");
+ }
+}
diff --git a/src/dorkbox/systemTray/linux/jna/AppIndicatorQuery.java b/src/dorkbox/systemTray/linux/jna/AppIndicatorQuery.java
deleted file mode 100644
index 7052c50..0000000
--- a/src/dorkbox/systemTray/linux/jna/AppIndicatorQuery.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2015 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.linux.jna;
-
-import com.sun.jna.Native;
-import dorkbox.systemTray.SystemTray;
-
-/**
- * Helper for AppIndicator, because it is absolutely mindboggling how those whom maintain the standard, can't agree to what that standard
- * library naming convention or features set is. We just try until we find one that work, and are able to map the symbols we need.
- */
-class AppIndicatorQuery {
-
- /**
- * must call get() before accessing this! Only "AppIndicator" interface should access this!
- */
- static volatile boolean isVersion3 = false;
-
- /**
- * Is AppIndicator loaded yet?
- */
- static volatile boolean isLoaded = false;
-
-
- public static
- AppIndicator get() {
- // objdump -T /usr/lib/x86_64-linux-gnu/libappindicator.so.1 | grep foo
- // objdump -T /usr/lib/x86_64-linux-gnu/libappindicator3.so.1 | grep foo
-
- Object library;
-
- // NOTE: GtkSupport uses this info to figure out WHAT VERSION OF GTK to use: appindiactor1 -> GTk2, appindicator3 -> GTK3.
-
- if (SystemTray.FORCE_GTK2 || SystemTray.COMPATIBILITY_MODE) {
- // try loading appindicator1 first, maybe it's there?
-
- try {
- library = Native.loadLibrary("appindicator1", AppIndicator.class);
- if (library != null) {
- return (AppIndicator) library;
- }
- } catch (Throwable ignored) {
- }
- }
-
- // start with base version
- try {
- library = Native.loadLibrary("appindicator", AppIndicator.class);
- if (library != null) {
- String s = library.toString();
- if (s.indexOf("appindicator3") > 0) {
- isVersion3 = true;
- }
-
- isLoaded = true;
- return (AppIndicator) library;
- }
- } catch (Throwable ignored) {
- }
-
- // whoops. Symbolic links are bugged out. Look manually for it...
-
- try {
- library = Native.loadLibrary("appindicator1", AppIndicator.class);
- if (library != null) {
- return (AppIndicator) library;
- }
- } catch (Throwable ignored) {
- }
-
- // now check all others. super hacky way to do this.
- for (int i = 10; i >= 0; i--) {
- try {
- library = Native.loadLibrary("appindicator" + i, AppIndicator.class);
- } catch (Throwable ignored) {
- library = null;
- }
-
- if (library != null) {
- String s = library.toString();
- // version 3 WILL NOT work with icons in the menu. This allows us to show a warning (in the System tray initialization)
- if (i == 3 || s.indexOf("appindicator3") > 0) {
- isVersion3 = true;
- }
- return (AppIndicator) library;
- }
- }
-
- // another type. who knows...
- try {
- library = Native.loadLibrary("appindicator-gtk", AppIndicator.class);
- if (library != null) {
- return (AppIndicator) library;
- }
- } catch (Throwable ignored) {
- }
-
- // this is HORRID. such a PITA
- try {
- library = Native.loadLibrary("appindicator-gtk3", AppIndicator.class);
- if (library != null) {
- return (AppIndicator) library;
- }
- } catch (Throwable ignored) {
- }
-
- throw new RuntimeException("We apologize for this, but we are unable to determine which the appIndicator library is in use, if " +
- "or even if it is in use... Please create an issue for this and include your OS type and configuration.");
- }
-}
diff --git a/src/dorkbox/systemTray/linux/jna/FuncCallback.java b/src/dorkbox/systemTray/linux/jna/FuncCallback.java
new file mode 100644
index 0000000..7c79012
--- /dev/null
+++ b/src/dorkbox/systemTray/linux/jna/FuncCallback.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 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.linux.jna;
+
+import com.sun.jna.Callback;
+import com.sun.jna.Pointer;
+import dorkbox.util.Keep;
+
+@Keep
+interface FuncCallback extends Callback {
+ /**
+ * @return Gtk.FALSE if it will be automatically removed from the stack once it's handled
+ */
+ int callback(Pointer data);
+}
diff --git a/src/dorkbox/systemTray/linux/jna/GCallback.java b/src/dorkbox/systemTray/linux/jna/GCallback.java
new file mode 100644
index 0000000..a4d2df6
--- /dev/null
+++ b/src/dorkbox/systemTray/linux/jna/GCallback.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 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.linux.jna;
+
+import com.sun.jna.Callback;
+import com.sun.jna.Pointer;
+import dorkbox.util.Keep;
+
+@Keep
+public
+interface GCallback extends Callback {
+ /**
+ * @return Gtk.TRUE if we handled this event
+ */
+ int callback(Pointer instance, Pointer data);
+}
diff --git a/src/dorkbox/systemTray/linux/jna/GEventCallback.java b/src/dorkbox/systemTray/linux/jna/GEventCallback.java
new file mode 100644
index 0000000..ceb955a
--- /dev/null
+++ b/src/dorkbox/systemTray/linux/jna/GEventCallback.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 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.linux.jna;
+
+import com.sun.jna.Callback;
+import com.sun.jna.Pointer;
+import dorkbox.util.Keep;
+
+@Keep
+public
+interface GEventCallback extends Callback {
+ void callback(Pointer instance, GdkEventButton event);
+}
diff --git a/src/dorkbox/systemTray/linux/jna/GObjectStruct.java b/src/dorkbox/systemTray/linux/jna/GObjectStruct.java
new file mode 100644
index 0000000..21aa9d3
--- /dev/null
+++ b/src/dorkbox/systemTray/linux/jna/GObjectStruct.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 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.linux.jna;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import dorkbox.util.Keep;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Keep
+public
+class GObjectStruct extends Structure {
+ public
+ class ByValue extends GObjectStruct implements Structure.ByValue {}
+
+
+ public
+ class ByReference extends GObjectStruct implements Structure.ByReference {}
+
+
+ public GTypeInstanceStruct g_type_instance;
+ public int ref_count;
+ public Pointer qdata;
+
+ @Override
+ protected
+ List getFieldOrder() {
+ return Arrays.asList("g_type_instance", "ref_count", "qdata");
+ }
+}
diff --git a/src/dorkbox/systemTray/linux/jna/GThread.java b/src/dorkbox/systemTray/linux/jna/GThread.java
index 3ce9e09..a78a407 100644
--- a/src/dorkbox/systemTray/linux/jna/GThread.java
+++ b/src/dorkbox/systemTray/linux/jna/GThread.java
@@ -15,13 +15,18 @@
*/
package dorkbox.systemTray.linux.jna;
-import com.sun.jna.Library;
-import com.sun.jna.Native;
import com.sun.jna.Pointer;
+/**
+ * bindings for libgthread
+ *
+ * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
+ */
public
-interface GThread extends Library {
- GThread INSTANCE = (GThread) Native.loadLibrary("gthread-2.0", GThread.class);
+class GThread {
+ static {
+ JnaHelper.register("gthread-2.0", GThread.class);
+ }
- void g_thread_init(Pointer GThreadFunctions);
+ public static native void g_thread_init(Pointer GThreadFunctions);
}
diff --git a/src/dorkbox/systemTray/linux/jna/GTypeInstanceStruct.java b/src/dorkbox/systemTray/linux/jna/GTypeInstanceStruct.java
new file mode 100644
index 0000000..ee9d6c3
--- /dev/null
+++ b/src/dorkbox/systemTray/linux/jna/GTypeInstanceStruct.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 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.linux.jna;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import dorkbox.util.Keep;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Keep
+public
+class GTypeInstanceStruct extends Structure {
+ public
+ class ByValue extends GTypeInstanceStruct implements Structure.ByValue {}
+
+
+ public
+ class ByReference extends GTypeInstanceStruct implements Structure.ByReference {}
+
+
+ public Pointer g_class;
+
+ @Override
+ protected
+ List getFieldOrder() {
+ return Arrays.asList("g_class");
+ }
+}
diff --git a/src/dorkbox/systemTray/linux/jna/GdkEventButton.java b/src/dorkbox/systemTray/linux/jna/GdkEventButton.java
new file mode 100644
index 0000000..332afaf
--- /dev/null
+++ b/src/dorkbox/systemTray/linux/jna/GdkEventButton.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 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.linux.jna;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import dorkbox.util.Keep;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Keep
+public
+class GdkEventButton extends Structure {
+ public int type;
+ public Pointer window;
+ public int send_event;
+ public int time;
+ public double x;
+ public double y;
+ public Pointer axes;
+ public int state;
+ public int button;
+ public Pointer device;
+ public double x_root;
+ public double y_root;
+
+ @Override
+ protected
+ List getFieldOrder() {
+ return Arrays.asList("type", "window", "send_event", "time", "x", "y", "axes", "state", "button", "device", "x_root", "y_root");
+ }
+}
diff --git a/src/dorkbox/systemTray/linux/jna/Gobject.java b/src/dorkbox/systemTray/linux/jna/Gobject.java
index 25671f1..b5f95ae 100644
--- a/src/dorkbox/systemTray/linux/jna/Gobject.java
+++ b/src/dorkbox/systemTray/linux/jna/Gobject.java
@@ -16,169 +16,28 @@
package dorkbox.systemTray.linux.jna;
import com.sun.jna.Callback;
-import com.sun.jna.Library;
-import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-import dorkbox.util.Keep;
-
-import java.util.Arrays;
-import java.util.List;
+/**
+ * bindings for libgobject-2.0
+ *
+ * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
+ */
public
-interface Gobject extends Library {
- Gobject INSTANCE = (Gobject) Native.loadLibrary("gobject-2.0", Gobject.class);
+class Gobject {
- @Keep
- class GTypeClassStruct extends Structure {
- public
- class ByValue extends GTypeClassStruct implements Structure.ByValue {}
-
-
- public
- class ByReference extends GTypeClassStruct implements Structure.ByReference {}
-
-
- public NativeLong g_type;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("g_type");
- }
+ static {
+ JnaHelper.register("gobject-2.0", Gobject.class);
}
+ public static native void g_free(Pointer object);
+ public static native void g_object_unref(Pointer object);
- @Keep
- class GTypeInstanceStruct extends Structure {
- public
- class ByValue extends GTypeInstanceStruct implements Structure.ByValue {}
+ public static native void g_object_force_floating(Pointer object);
+ public static native void g_object_ref_sink(Pointer object);
+ public static native NativeLong g_signal_connect_object(Pointer instance, String detailed_signal, Callback c_handler, Pointer object, int connect_flags);
- public
- class ByReference extends GTypeInstanceStruct implements Structure.ByReference {}
-
-
- public Pointer g_class;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("g_class");
- }
- }
-
-
- @Keep
- class GObjectStruct extends Structure {
- public
- class ByValue extends GObjectStruct implements Structure.ByValue {}
-
-
- public
- class ByReference extends GObjectStruct implements Structure.ByReference {}
-
-
- public GTypeInstanceStruct g_type_instance;
- public int ref_count;
- public Pointer qdata;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("g_type_instance", "ref_count", "qdata");
- }
- }
-
-
- @Keep
- class GObjectClassStruct extends Structure {
- public
- class ByValue extends GObjectClassStruct implements Structure.ByValue {}
-
-
- public
- class ByReference extends GObjectClassStruct implements Structure.ByReference {}
-
-
- public GTypeClassStruct g_type_class;
- public Pointer construct_properties;
- public Pointer constructor;
- public Pointer set_property;
- public Pointer get_property;
- public Pointer dispose;
- public Pointer finalize;
- public Pointer dispatch_properties_changed;
- public Pointer notify;
- public Pointer constructed;
- public NativeLong flags;
- public Pointer dummy1;
- public Pointer dummy2;
- public Pointer dummy3;
- public Pointer dummy4;
- public Pointer dummy5;
- public Pointer dummy6;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("g_type_class", "construct_properties", "constructor", "set_property", "get_property", "dispose",
- "finalize", "dispatch_properties_changed", "notify", "constructed", "flags", "dummy1", "dummy2", "dummy3",
- "dummy4", "dummy5", "dummy6");
- }
- }
-
-
- @Keep
- interface FuncCallback extends Callback {
- /**
- * @return Gtk.FALSE if it will be automatically removed from the stack once it's handled
- */
- int callback(Pointer data);
- }
-
- @Keep
- interface GCallback extends Callback {
- /**
- * @return Gtk.TRUE if we handled this event
- */
- int callback(Pointer instance, Pointer data);
- }
-
-
- @Keep
- interface GEventCallback extends Callback {
- void callback(Pointer instance, Gtk.GdkEventButton event);
- }
-
-
- @Keep
- class xyPointer extends Structure {
- public int value;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("value");
- }
- }
-
-
- @Keep
- interface GPositionCallback extends Callback {
- void callback(Pointer menu, xyPointer x, xyPointer y, Pointer push_in_bool, Pointer user_data);
- }
-
-
-
- void g_free(Pointer object);
- void g_object_unref(Pointer object);
-
- void g_object_force_floating(Pointer object);
- void g_object_ref_sink(Pointer object);
-
- NativeLong g_signal_connect_object(Pointer instance, String detailed_signal, Callback c_handler, Pointer object, int connect_flags);
-
- Pointer g_markup_printf_escaped(String pattern, String inputString);
+ public static native Pointer g_markup_printf_escaped(String pattern, String inputString);
}
diff --git a/src/dorkbox/systemTray/linux/jna/Gtk.java b/src/dorkbox/systemTray/linux/jna/Gtk.java
index 57a03d5..3fea125 100644
--- a/src/dorkbox/systemTray/linux/jna/Gtk.java
+++ b/src/dorkbox/systemTray/linux/jna/Gtk.java
@@ -16,132 +16,297 @@
package dorkbox.systemTray.linux.jna;
import com.sun.jna.Function;
-import com.sun.jna.Library;
import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-import dorkbox.util.Keep;
+import dorkbox.systemTray.SystemTray;
-import java.util.Arrays;
-import java.util.List;
+import java.util.LinkedList;
+import java.util.concurrent.CountDownLatch;
+/**
+ * bindings for gtk 2 or 3
+ *
+ * note: gtk2/3 loading is SENSITIVE, and which AppIndicator symbols are loaded depends on this being loaded first
+ *
+ * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
+ */
+@SuppressWarnings("Duplicates")
public
-interface Gtk extends Library {
+class Gtk {
+ // For funsies to look at, SyncThing did a LOT of work on compatibility in python (unfortunate for us, but interesting).
+ // https://github.com/syncthing/syncthing-gtk/blob/b7a3bc00e3bb6d62365ae62b5395370f3dcc7f55/syncthing_gtk/statusicon.py
+
+ // NOTE: AppIndicator uses this info to figure out WHAT VERSION OF appindicator to use: GTK2 -> appindiactor1, GTK3 -> appindicator3
+ public static volatile boolean isGtk2 = false;
+
+
+ public static Function gtk_status_icon_position_menu = null;
+
+ private static boolean alreadyRunningGTK = false;
+ private static boolean isLoaded = false;
+
+
+ /**
+ * We can have GTK v3 or v2.
+ *
+ * Observations:
+ * 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
- Gtk INSTANCE = GtkSupport.get();
- Function gtk_status_icon_position_menu = GtkSupport.gtk_status_icon_position_menu;
+ static {
+ boolean shouldUseGtk2 = SystemTray.FORCE_GTK2 || SystemTray.COMPATIBILITY_MODE;
- int FALSE = 0;
- int TRUE = 1;
+ // for more info on JavaFX: 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+.
+ // in some cases, we ALWAYS want to try GTK2 first
+ if (shouldUseGtk2) {
+ try {
+ JnaHelper.register("gtk-x11-2.0", Gtk.class);
+ gtk_status_icon_position_menu = Function.getFunction("gtk-x11-2.0", "gtk_status_icon_position_menu");
+ isGtk2 = true;
- @Keep
- class GdkEventButton extends Structure {
- public int type;
- public Pointer window;
- public int send_event;
- public int time;
- public double x;
- public double y;
- public Pointer axes;
- public int state;
- public int button;
- public Pointer device;
- public double x_root;
- public double y_root;
+ // when running inside of JavaFX, this will be '1'. All other times this should be '0'
+ // when it's '1', it means that someone else has stared GTK -- so we DO NOT NEED TO.
+ alreadyRunningGTK = gtk_main_level() != 0;
+ isLoaded = true;
+ } catch (Throwable ignored) {
+ }
+ }
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("type", "window", "send_event", "time", "x", "y", "axes", "state", "button", "device", "x_root", "y_root");
+ // now for the defaults...
+
+ // start with version 3
+ if (!isLoaded) {
+ try {
+ JnaHelper.register("libgtk-3.so.0", Gtk.class);
+ gtk_status_icon_position_menu = Function.getFunction("libgtk-3.so.0", "gtk_status_icon_position_menu");
+ // when running inside of JavaFX, this will be '1'. All other times this should be '0'
+ // when it's '1', it means that someone else has stared GTK -- so we DO NOT NEED TO.
+ alreadyRunningGTK = gtk_main_level() != 0;
+ isLoaded = true;
+ } catch (Throwable ignored) {
+ }
+ }
+
+ // now version 2
+ if (!isLoaded) {
+ try {
+ JnaHelper.register("gtk-x11-2.0", Gtk.class);
+ gtk_status_icon_position_menu = Function.getFunction("gtk-x11-2.0", "gtk_status_icon_position_menu");
+ isGtk2 = true;
+
+ // when running inside of JavaFX, this will be '1'. All other times this should be '0'
+ // when it's '1', it means that someone else has stared GTK -- so we DO NOT NEED TO.
+ alreadyRunningGTK = gtk_main_level() != 0;
+ isLoaded = true;
+ } catch (Throwable ignored) {
+ }
+ }
+ if (!isLoaded) {
+ throw new RuntimeException("We apologize for this, but we are unable to determine the GTK library is in use, " +
+ "or even if it is in use... Please create an issue for this and include your OS type and configuration.");
}
}
- boolean gtk_init_check(int argc, String[] argv);
+ 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)
+ private static final LinkedList