diff --git a/src/dorkbox/util/tray/SystemTray.java b/src/dorkbox/util/tray/SystemTray.java index 5588f7a..b58e09f 100644 --- a/src/dorkbox/util/tray/SystemTray.java +++ b/src/dorkbox/util/tray/SystemTray.java @@ -17,6 +17,7 @@ package dorkbox.util.tray; import dorkbox.util.OS; import dorkbox.util.jna.linux.AppIndicator; +import dorkbox.util.jna.linux.AppIndicatorQuery; import dorkbox.util.jna.linux.GtkSupport; import dorkbox.util.process.ShellProcessBuilder; import dorkbox.util.tray.linux.AppIndicatorTray; @@ -96,8 +97,13 @@ class SystemTray { } else if ("XFCE".equalsIgnoreCase(XDG)) { // XFCE uses a BAD version of libappindicator by default, which DOES NOT support images in the menu. + // if we have libappindicator1, we are OK. if we don't, fallback to GTKSystemTray try { - trayType = GtkSystemTray.class; + if (AppIndicatorQuery.get_v1() != null) { + trayType = AppIndicatorTray.class; + } else { + trayType = GtkSystemTray.class; + } } catch (Throwable ignored) { } } diff --git a/src/dorkbox/util/tray/linux/AppIndicatorTray.java b/src/dorkbox/util/tray/linux/AppIndicatorTray.java index 646543e..088668b 100644 --- a/src/dorkbox/util/tray/linux/AppIndicatorTray.java +++ b/src/dorkbox/util/tray/linux/AppIndicatorTray.java @@ -30,19 +30,19 @@ import dorkbox.util.jna.linux.GtkSupport; */ public class AppIndicatorTray extends GtkTypeSystemTray { - private static final AppIndicator libappindicator = AppIndicator.INSTANCE; + private static final AppIndicator appindicator = AppIndicator.INSTANCE; private AppIndicator.AppIndicatorInstanceStruct appIndicator; public AppIndicatorTray(String iconName) { - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); String icon_name = iconPath(iconName); - this.appIndicator = libappindicator.app_indicator_new(System.nanoTime() + "DBST", icon_name, - AppIndicator.CATEGORY_APPLICATION_STATUS); - libappindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_ACTIVE); + this.appIndicator = appindicator.app_indicator_new(System.nanoTime() + "DBST", icon_name, + AppIndicator.CATEGORY_APPLICATION_STATUS); + appindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_ACTIVE); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); GtkSupport.startGui(); } @@ -50,26 +50,26 @@ class AppIndicatorTray extends GtkTypeSystemTray { @Override public synchronized void shutdown() { - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); // this hides the indicator - libappindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_PASSIVE); + appindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_PASSIVE); Pointer p = this.appIndicator.getPointer(); - libgobject.g_object_unref(p); + gobject.g_object_unref(p); // GC it this.appIndicator = null; -// libgtk.gdk_threads_leave(); called by parent class +// libgtk.gdk_threads_leave(); called by super class super.shutdown(); } @Override public synchronized void setIcon(final String iconName) { - libgtk.gdk_threads_enter(); - libappindicator.app_indicator_set_icon(this.appIndicator, iconPath(iconName)); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_enter(); + appindicator.app_indicator_set_icon(this.appIndicator, iconPath(iconName)); + gtk.gdk_threads_leave(); } @@ -78,6 +78,6 @@ class AppIndicatorTray extends GtkTypeSystemTray { */ protected void onMenuAdded(final Pointer menu) { - libappindicator.app_indicator_set_menu(this.appIndicator, menu); + appindicator.app_indicator_set_menu(this.appIndicator, menu); } } diff --git a/src/dorkbox/util/tray/linux/GtkMenuEntry.java b/src/dorkbox/util/tray/linux/GtkMenuEntry.java index c13c252..b10ccb3 100644 --- a/src/dorkbox/util/tray/linux/GtkMenuEntry.java +++ b/src/dorkbox/util/tray/linux/GtkMenuEntry.java @@ -24,8 +24,8 @@ import dorkbox.util.tray.MenuEntry; import dorkbox.util.tray.SystemTrayMenuAction; class GtkMenuEntry implements MenuEntry { - private static final Gtk libgtk = Gtk.INSTANCE; - private static final Gobject libgobject = Gobject.INSTANCE; + private static final Gtk gtk = Gtk.INSTANCE; + private static final Gobject gobject = Gobject.INSTANCE; private final GCallback gtkCallback; final Pointer menuItem; @@ -55,20 +55,20 @@ class GtkMenuEntry implements MenuEntry { } }; - menuItem = libgtk.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 = libgtk.gtk_image_new_from_file(imagePath); + image = gtk.gtk_image_new_from_file(imagePath); - libgtk.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 - libgtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE); + gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE); } - nativeLong = libgobject.g_signal_connect_data(menuItem, "activate", gtkCallback, null, null, 0); + nativeLong = gobject.g_signal_connect_data(menuItem, "activate", gtkCallback, null, null, 0); } private @@ -95,38 +95,36 @@ class GtkMenuEntry implements MenuEntry { public void setText(final String newText) { this.text = newText; - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); - libgtk.gtk_menu_item_set_label(menuItem, newText); + gtk.gtk_menu_item_set_label(menuItem, newText); - libgtk.gtk_widget_show_all(parentMenu); + gtk.gtk_widget_show_all(parentMenu); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); } @Override public void setImage(final String imagePath) { - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); if (imagePath != null && !imagePath.isEmpty()) { if (image != null) { - libgtk.gtk_widget_destroy(image); + gtk.gtk_widget_destroy(image); } - libgtk.gtk_widget_show_all(parentMenu); - libgtk.gdk_threads_leave(); + gtk.gtk_widget_show_all(parentMenu); - libgtk.gdk_threads_enter(); - image = libgtk.gtk_image_new_from_file(imagePath); + image = gtk.gtk_image_new_from_file(imagePath); - libgtk.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 - libgtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE); + gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE); } - libgtk.gtk_widget_show_all(parentMenu); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); + gtk.gtk_widget_show_all(parentMenu); } @Override @@ -140,7 +138,7 @@ class GtkMenuEntry implements MenuEntry { */ public void remove() { - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); removePrivate(); @@ -148,17 +146,17 @@ class GtkMenuEntry implements MenuEntry { systemTray.deleteMenu(); systemTray.createMenu(); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); } void removePrivate() { - libgobject.g_signal_handler_disconnect(menuItem, nativeLong); - libgtk.gtk_menu_shell_deactivate(parentMenu, menuItem); + gobject.g_signal_handler_disconnect(menuItem, nativeLong); + gtk.gtk_menu_shell_deactivate(parentMenu, menuItem); if (image != null) { - libgtk.gtk_widget_destroy(image); + gtk.gtk_widget_destroy(image); } - libgtk.gtk_widget_destroy(menuItem); + gtk.gtk_widget_destroy(menuItem); } @Override diff --git a/src/dorkbox/util/tray/linux/GtkSystemTray.java b/src/dorkbox/util/tray/linux/GtkSystemTray.java index 5af2555..457082d 100644 --- a/src/dorkbox/util/tray/linux/GtkSystemTray.java +++ b/src/dorkbox/util/tray/linux/GtkSystemTray.java @@ -40,15 +40,15 @@ class GtkSystemTray extends GtkTypeSystemTray { GtkSystemTray(String iconName) { super(); - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); - final Pointer trayIcon = libgtk.gtk_status_icon_new(); - libgtk.gtk_status_icon_set_title(trayIcon, "SystemTray@Dorkbox"); - libgtk.gtk_status_icon_set_name(trayIcon, "SystemTray"); + final Pointer trayIcon = gtk.gtk_status_icon_new(); + gtk.gtk_status_icon_set_title(trayIcon, "SystemTray@Dorkbox"); + gtk.gtk_status_icon_set_name(trayIcon, "SystemTray"); this.trayIcon = trayIcon; - libgtk.gtk_status_icon_set_from_file(trayIcon, iconPath(iconName)); + gtk.gtk_status_icon_set_from_file(trayIcon, iconPath(iconName)); this.gtkCallback = new Gobject.GEventCallback() { @Override @@ -56,15 +56,15 @@ class GtkSystemTray extends GtkTypeSystemTray { void callback(Pointer notUsed, final Gtk.GdkEventButton event) { // BUTTON_PRESS only (any mouse click) if (event.type == 4) { - libgtk.gtk_menu_popup(menu, null, null, Gtk.gtk_status_icon_position_menu, trayIcon, 0, event.time); + gtk.gtk_menu_popup(menu, null, null, Gtk.gtk_status_icon_position_menu, trayIcon, 0, event.time); } } }; - button_press_event = libgobject.g_signal_connect_data(trayIcon, "button_press_event", gtkCallback, null, null, 0); + button_press_event = gobject.g_signal_connect_data(trayIcon, "button_press_event", gtkCallback, null, null, 0); - libgtk.gtk_status_icon_set_visible(trayIcon, true); + gtk.gtk_status_icon_set_visible(trayIcon, true); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); GtkSupport.startGui(); } @@ -81,11 +81,11 @@ class GtkSystemTray extends GtkTypeSystemTray { @Override public synchronized void shutdown() { - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); // this hides the indicator - libgtk.gtk_status_icon_set_visible(this.trayIcon, false); - libgobject.g_object_unref(this.trayIcon); + gtk.gtk_status_icon_set_visible(this.trayIcon, false); + gobject.g_object_unref(this.trayIcon); // GC it this.trayIcon = null; @@ -97,8 +97,8 @@ class GtkSystemTray extends GtkTypeSystemTray { @Override public synchronized void setIcon(final String iconName) { - libgtk.gdk_threads_enter(); - libgtk.gtk_status_icon_set_from_file(trayIcon, iconPath(iconName)); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_enter(); + gtk.gtk_status_icon_set_from_file(trayIcon, iconPath(iconName)); + gtk.gdk_threads_leave(); } } diff --git a/src/dorkbox/util/tray/linux/GtkTypeSystemTray.java b/src/dorkbox/util/tray/linux/GtkTypeSystemTray.java index 4cbc557..4e0d4b4 100644 --- a/src/dorkbox/util/tray/linux/GtkTypeSystemTray.java +++ b/src/dorkbox/util/tray/linux/GtkTypeSystemTray.java @@ -29,8 +29,9 @@ import java.util.concurrent.Executors; public abstract class GtkTypeSystemTray extends SystemTray { - protected static final Gobject libgobject = Gobject.INSTANCE; - protected static final Gtk libgtk = Gtk.INSTANCE; + protected static final Gobject gobject = Gobject.INSTANCE; + protected static final Gtk gtk = Gtk.INSTANCE; +// protected static final GLib glib = GLib.INSTANCE; final static ExecutorService callbackExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("SysTrayExecutor", false)); @@ -44,38 +45,38 @@ class GtkTypeSystemTray extends SystemTray { obliterateMenu(); GtkSupport.shutdownGui(); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); callbackExecutor.shutdown(); } @Override public synchronized - void setStatus(String infoString) { - libgtk.gdk_threads_enter(); + void setStatus(final String infoString) { + gtk.gdk_threads_enter(); if (this.connectionStatusItem == null && infoString != null && !infoString.isEmpty()) { deleteMenu(); - this.connectionStatusItem = libgtk.gtk_menu_item_new_with_label(""); - libgobject.g_object_ref(connectionStatusItem); // so it matches with 'createMenu' + this.connectionStatusItem = gtk.gtk_menu_item_new_with_label(""); + gobject.g_object_ref(connectionStatusItem); // so it matches with 'createMenu' // evil hacks abound... - Pointer label = libgtk.gtk_bin_get_child(connectionStatusItem); - libgtk.gtk_label_set_use_markup(label, Gtk.TRUE); - Pointer markup = libgobject.g_markup_printf_escaped("%s", infoString); - libgtk.gtk_label_set_markup(label, markup); - libgobject.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", infoString); + gtk.gtk_label_set_markup(label, markup); + gobject.g_free(markup); - libgtk.gtk_widget_set_sensitive(this.connectionStatusItem, Gtk.FALSE); + gtk.gtk_widget_set_sensitive(this.connectionStatusItem, Gtk.FALSE); createMenu(); } else { if (infoString == null || infoString.isEmpty()) { deleteMenu(); - libgtk.gtk_widget_destroy(connectionStatusItem); + gtk.gtk_widget_destroy(connectionStatusItem); connectionStatusItem = null; createMenu(); @@ -85,17 +86,17 @@ class GtkTypeSystemTray extends SystemTray { // libgtk.gtk_menu_item_set_label(this.connectionStatusItem, infoString); // evil hacks abound... - Pointer label = libgtk.gtk_bin_get_child(connectionStatusItem); - libgtk.gtk_label_set_use_markup(label, Gtk.TRUE); - Pointer markup = libgobject.g_markup_printf_escaped("%s", infoString); - libgtk.gtk_label_set_markup(label, markup); - libgobject.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", infoString); + gtk.gtk_label_set_markup(label, markup); + gobject.g_free(markup); - libgtk.gtk_widget_show_all(menu); + gtk.gtk_widget_show_all(menu); } } - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); } /** @@ -115,7 +116,7 @@ class GtkTypeSystemTray extends SystemTray { if (menu != null) { // have to remove status from menu if (connectionStatusItem != null) { - libgtk.gtk_widget_destroy(connectionStatusItem); + gtk.gtk_widget_destroy(connectionStatusItem); connectionStatusItem = null; } @@ -128,7 +129,7 @@ class GtkTypeSystemTray extends SystemTray { menuEntries.clear(); // GTK menu needs a "ref_sink" - libgobject.g_object_ref_sink(menu); + gobject.g_object_ref_sink(menu); } } @@ -139,37 +140,32 @@ class GtkTypeSystemTray extends SystemTray { if (menu != null) { // have to remove status from menu if (connectionStatusItem != null) { - libgobject.g_object_ref(connectionStatusItem); + gobject.g_object_ref(connectionStatusItem); - libgtk.gtk_container_remove(menu, connectionStatusItem); + gtk.gtk_container_remove(menu, connectionStatusItem); } // have to remove all other menu entries for (int i = 0; i < menuEntries.size(); i++) { GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i); - libgobject.g_object_ref(menuEntry__.menuItem); - libgtk.gtk_container_remove(menu, menuEntry__.menuItem); + gobject.g_object_ref(menuEntry__.menuItem); + gtk.gtk_container_remove(menu, menuEntry__.menuItem); } // GTK menu needs a "ref_sink" - libgobject.g_object_ref_sink(menu); - - // have to 'blip' the thread, so that the state can catch up. Stupid, i know, and I am open to suggestions to fixing bizzare - // race conditions with GTK... - libgtk.gdk_threads_leave(); - libgtk.gdk_threads_enter(); + gobject.g_object_ref_sink(menu); } - menu = libgtk.gtk_menu_new(); + menu = gtk.gtk_menu_new(); } // UNSAFE. must be protected inside synchronized, and inside threads_enter/exit void createMenu() { // now add status if (connectionStatusItem != null) { - libgtk.gtk_menu_shell_append(this.menu, this.connectionStatusItem); - libgobject.g_object_unref(connectionStatusItem); + gtk.gtk_menu_shell_append(this.menu, this.connectionStatusItem); + gobject.g_object_unref(connectionStatusItem); } // now add back other menu entries @@ -177,12 +173,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' - libgtk.gtk_menu_shell_append(this.menu, menuEntry__.menuItem); - libgobject.g_object_unref(menuEntry__.menuItem); + gtk.gtk_menu_shell_append(this.menu, menuEntry__.menuItem); + gobject.g_object_unref(menuEntry__.menuItem); } onMenuAdded(menu); - libgtk.gtk_widget_show_all(menu); + gtk.gtk_widget_show_all(menu); } @Override @@ -201,7 +197,7 @@ class GtkTypeSystemTray extends SystemTray { throw new IllegalArgumentException("Menu entry already exists for given label '" + menuText + "'"); } else { - libgtk.gdk_threads_enter(); + gtk.gdk_threads_enter(); // some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator. // To work around this issue, we destroy then recreate the menu every time one is added. @@ -209,12 +205,12 @@ class GtkTypeSystemTray extends SystemTray { menuEntry = new GtkMenuEntry(menu, menuText, imagePath, callback, this); - libgobject.g_object_ref(menuEntry.menuItem); // so it matches with 'createMenu' + gobject.g_object_ref(menuEntry.menuItem); // so it matches with 'createMenu' this.menuEntries.add(menuEntry); createMenu(); - libgtk.gdk_threads_leave(); + gtk.gdk_threads_leave(); } } }