From 9a48be4c7417686b2e08834f6d303725fbfb401d Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 12 Dec 2014 02:25:50 +0100 Subject: [PATCH] Fixed swing threading issues. Fixed GTK threading issues. Code cleanup --- .../util/tray/SystemTrayMenuPopup.java | 5 +- .../util/tray/linux/AppIndicatorTray.java | 39 +++-- .../util/tray/linux/GtkSystemTray.java | 93 +++-------- src/dorkbox/util/tray/linux/MenuEntry.java | 3 + .../util/tray/swing/SwingSystemTray.java | 148 ++---------------- 5 files changed, 69 insertions(+), 219 deletions(-) diff --git a/src/dorkbox/util/tray/SystemTrayMenuPopup.java b/src/dorkbox/util/tray/SystemTrayMenuPopup.java index 42e81d1e..cbf10a5d 100644 --- a/src/dorkbox/util/tray/SystemTrayMenuPopup.java +++ b/src/dorkbox/util/tray/SystemTrayMenuPopup.java @@ -7,9 +7,9 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JPopupMenu; -import javax.swing.SwingUtilities; import dorkbox.util.DelayTimer; +import dorkbox.util.SwingUtil; public class SystemTrayMenuPopup extends JPopupMenu { private static final long serialVersionUID = 1L; @@ -22,8 +22,7 @@ public class SystemTrayMenuPopup extends JPopupMenu { this.timer = new DelayTimer("PopupMenuHider", true, new DelayTimer.Callback() { @Override public void execute() { - SwingUtilities.invokeLater(new Runnable() { - + SwingUtil.invokeLater(new Runnable() { @Override public void run() { Point location = MouseInfo.getPointerInfo().getLocation(); diff --git a/src/dorkbox/util/tray/linux/AppIndicatorTray.java b/src/dorkbox/util/tray/linux/AppIndicatorTray.java index 56f6d744..fa7081f6 100644 --- a/src/dorkbox/util/tray/linux/AppIndicatorTray.java +++ b/src/dorkbox/util/tray/linux/AppIndicatorTray.java @@ -40,6 +40,8 @@ import dorkbox.util.tray.SystemTrayMenuAction; * Lantern: https://github.com/getlantern/lantern/ Apache 2.0 License Copyright 2010 Brave New Software Project, Inc. */ public class AppIndicatorTray extends SystemTray { + private static final boolean useSWT = GtkSupport.usesSwtMainLoop; + private static final AppIndicator libappindicator = AppIndicator.INSTANCE; private static final Gobject libgobject = Gobject.INSTANCE; private static final Gtk libgtk = Gtk.INSTANCE; @@ -56,10 +58,12 @@ public class AppIndicatorTray extends SystemTray { private final List widgets = new ArrayList(4); - public AppIndicatorTray() {} + public AppIndicatorTray() { + } - @Override + @Override public void createTray(String iconName) { + libgtk.gdk_threads_enter(); this.appIndicator = libappindicator.app_indicator_new(this.appName, "indicator-messages-new", AppIndicator.CATEGORY_APPLICATION_STATUS); @@ -95,22 +99,18 @@ public class AppIndicatorTray extends SystemTray { libappindicator.app_indicator_set_icon_full(this.appIndicator, iconPath(iconName), this.appName); libappindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_ACTIVE); - if (!GtkSupport.usesSwtMainLoop) { + libgtk.gdk_threads_leave(); + + if (!useSWT) { Thread gtkUpdateThread = new Thread() { @Override public void run() { // notify our main thread to continue AppIndicatorTray.this.blockUntilStarted.countDown(); - - try { - libgtk.gtk_main(); - } catch (Throwable t) { - logger.warn("Unable to run main loop", t); - } + libgtk.gtk_main(); } }; gtkUpdateThread.setName("GTK event loop"); - gtkUpdateThread.setDaemon(true); gtkUpdateThread.start(); } @@ -124,6 +124,7 @@ public class AppIndicatorTray extends SystemTray { @Override public void removeTray() { + libgtk.gdk_threads_enter(); for (Pointer widget : this.widgets) { libgtk.gtk_widget_destroy(widget); } @@ -149,12 +150,15 @@ public class AppIndicatorTray extends SystemTray { } this.connectionStatusItem = null; + libgtk.gtk_main_quit(); + libgtk.gdk_threads_leave(); super.removeTray(); } @Override public void setStatus(String infoString, String iconName) { + libgtk.gdk_threads_enter(); if (this.connectionStatusItem == null) { this.connectionStatusItem = libgtk.gtk_menu_item_new_with_label(infoString); this.widgets.add(this.connectionStatusItem); @@ -167,6 +171,7 @@ public class AppIndicatorTray extends SystemTray { libgtk.gtk_widget_show_all(this.connectionStatusItem); libappindicator.app_indicator_set_icon_full(this.appIndicator, iconPath(iconName), this.appName); + libgtk.gdk_threads_leave(); } /** @@ -178,7 +183,11 @@ public class AppIndicatorTray extends SystemTray { MenuEntry menuEntry = this.menuEntries.get(menuText); if (menuEntry == null) { + libgtk.gdk_threads_enter(); + Pointer dashboardItem = libgtk.gtk_menu_item_new_with_label(menuText); + + // have to watch out! These can get garbage collected! Gobject.GCallback gtkCallback = new Gobject.GCallback() { @Override public void callback(Pointer instance, Pointer data) { @@ -195,8 +204,11 @@ public class AppIndicatorTray extends SystemTray { libgtk.gtk_menu_shell_append(this.menu, dashboardItem); libgtk.gtk_widget_show_all(dashboardItem); + libgtk.gdk_threads_leave(); + menuEntry = new MenuEntry(); menuEntry.dashboardItem = dashboardItem; + menuEntry.gtkCallback = gtkCallback; this.menuEntries.put(menuText, menuEntry); } else { @@ -214,9 +226,11 @@ public class AppIndicatorTray extends SystemTray { MenuEntry menuEntry = this.menuEntries.get(origMenuText); if (menuEntry != null) { + libgtk.gdk_threads_enter(); libgtk.gtk_menu_item_set_label(menuEntry.dashboardItem, newMenuText); - Gobject.GCallback gtkCallback = new Gobject.GCallback() { + // have to watch out! These can get garbage collected! + menuEntry.gtkCallback = new Gobject.GCallback() { @Override public void callback(Pointer instance, Pointer data) { AppIndicatorTray.this.callbackExecutor.execute(new Runnable() { @@ -228,9 +242,10 @@ public class AppIndicatorTray extends SystemTray { } }; - libgobject.g_signal_connect_data(menuEntry.dashboardItem, "activate", gtkCallback, null, null, 0); + libgobject.g_signal_connect_data(menuEntry.dashboardItem, "activate", menuEntry.gtkCallback, null, null, 0); libgtk.gtk_widget_show_all(menuEntry.dashboardItem); + libgtk.gdk_threads_leave(); } else { addMenuEntry(origMenuText, newCallback); } diff --git a/src/dorkbox/util/tray/linux/GtkSystemTray.java b/src/dorkbox/util/tray/linux/GtkSystemTray.java index d442bf93..448a8e85 100644 --- a/src/dorkbox/util/tray/linux/GtkSystemTray.java +++ b/src/dorkbox/util/tray/linux/GtkSystemTray.java @@ -18,7 +18,6 @@ package dorkbox.util.tray.linux; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -26,10 +25,10 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import javax.swing.JMenuItem; -import javax.swing.SwingUtilities; import com.sun.jna.Pointer; +import dorkbox.util.SwingUtil; import dorkbox.util.jna.linux.Gobject; import dorkbox.util.jna.linux.Gtk; import dorkbox.util.jna.linux.Gtk.GdkEventButton; @@ -38,14 +37,13 @@ import dorkbox.util.tray.SystemTray; import dorkbox.util.tray.SystemTrayMenuAction; import dorkbox.util.tray.SystemTrayMenuPopup; - /** * Class for handling all system tray interactions via GTK. * * This is the "old" way to do it, and does not work with some desktop environments. */ public class GtkSystemTray extends SystemTray { - + private static final boolean useSWT = GtkSupport.usesSwtMainLoop; private static final Gobject libgobject = Gobject.INSTANCE; private static final Gtk libgtk = Gtk.INSTANCE; @@ -65,6 +63,7 @@ public class GtkSystemTray extends SystemTray { @Override public void createTray(String iconName) { + libgtk.gdk_threads_enter(); this.trayIcon = libgtk.gtk_status_icon_new(); libgtk.gtk_status_icon_set_from_file(this.trayIcon, iconPath(iconName)); libgtk.gtk_status_icon_set_tooltip(this.trayIcon, this.appName); @@ -75,7 +74,7 @@ public class GtkSystemTray extends SystemTray { public void callback(Pointer instance, final GdkEventButton event) { // BUTTON_PRESS only if (event.type == 4) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { if (GtkSystemTray.this.jmenu.isVisible()) { @@ -115,39 +114,27 @@ public class GtkSystemTray extends SystemTray { }; // all the clicks. This is because native menu popups are a pain to figure out, so we cheat and use some java bits to do the popup libgobject.g_signal_connect_data(this.trayIcon, "button_press_event", gtkCallback, null, null, 0); + libgtk.gdk_threads_leave(); + SwingUtil.invokeAndWait(new Runnable() { + @Override + public void run() { + GtkSystemTray.this.jmenu = new SystemTrayMenuPopup(); + } + }); - if (!GtkSupport.usesSwtMainLoop) { + if (!useSWT) { Thread gtkUpdateThread = new Thread() { @Override public void run() { // notify our main thread to continue GtkSystemTray.this.blockUntilStarted.countDown(); - - try { - libgtk.gtk_main(); - } catch (Throwable t) { - logger.warn("Unable to run main loop", t); - } + libgtk.gtk_main(); } }; gtkUpdateThread.setName("GTK event loop"); - gtkUpdateThread.setDaemon(true); gtkUpdateThread.start(); } - try { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - GtkSystemTray.this.jmenu = new SystemTrayMenuPopup(); - } - }); - } catch (InvocationTargetException e) { - logger.error("Error creating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error creating tray menu", e); - } - // we CANNOT continue until the GTK thread has started! (ignored if SWT is used) try { this.blockUntilStarted.await(); @@ -158,6 +145,7 @@ public class GtkSystemTray extends SystemTray { @Override public void removeTray() { + libgtk.gdk_threads_enter(); for (Pointer widget : this.widgets) { libgtk.gtk_widget_destroy(widget); } @@ -182,12 +170,15 @@ public class GtkSystemTray extends SystemTray { this.jmenu = null; this.connectionStatusItem = null; + libgtk.gtk_main_quit(); + libgtk.gdk_threads_leave(); + super.removeTray(); } @Override public void setStatus(final String infoString, String iconName) { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { if (GtkSystemTray.this.connectionStatusItem == null) { @@ -198,21 +189,11 @@ public class GtkSystemTray extends SystemTray { GtkSystemTray.this.connectionStatusItem.setText(infoString); } } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error updating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error updating tray menu", e); - } - } + }); + libgtk.gdk_threads_enter(); libgtk.gtk_status_icon_set_from_file(GtkSystemTray.this.trayIcon, iconPath(iconName)); + libgtk.gdk_threads_leave(); } /** @@ -220,7 +201,7 @@ public class GtkSystemTray extends SystemTray { */ @Override public void addMenuEntry(final String menuText, final SystemTrayMenuAction callback) { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { Map menuEntries2 = GtkSystemTray.this.menuEntries; @@ -251,19 +232,7 @@ public class GtkSystemTray extends SystemTray { } } } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error updating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error updating tray menu", e); - } - } + }); } /** @@ -271,7 +240,7 @@ public class GtkSystemTray extends SystemTray { */ @Override public void updateMenuEntry(final String origMenuText, final String newMenuText, final SystemTrayMenuAction newCallback) { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { Map menuEntries2 = GtkSystemTray.this.menuEntries; @@ -303,18 +272,6 @@ public class GtkSystemTray extends SystemTray { } } } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error updating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error updating tray menu", e); - } - } + }); } } diff --git a/src/dorkbox/util/tray/linux/MenuEntry.java b/src/dorkbox/util/tray/linux/MenuEntry.java index e8b979c5..31a53d42 100644 --- a/src/dorkbox/util/tray/linux/MenuEntry.java +++ b/src/dorkbox/util/tray/linux/MenuEntry.java @@ -17,6 +17,8 @@ package dorkbox.util.tray.linux; import com.sun.jna.Pointer; +import dorkbox.util.jna.linux.Gobject.GCallback; + /** * Can only access this from within a synchronized block! */ @@ -24,6 +26,7 @@ class MenuEntry { private final int hashCode; public Pointer dashboardItem; + public GCallback gtkCallback; public MenuEntry() { long time = System.nanoTime(); diff --git a/src/dorkbox/util/tray/swing/SwingSystemTray.java b/src/dorkbox/util/tray/swing/SwingSystemTray.java index 0df939b2..9e47767f 100644 --- a/src/dorkbox/util/tray/swing/SwingSystemTray.java +++ b/src/dorkbox/util/tray/swing/SwingSystemTray.java @@ -17,9 +17,6 @@ package dorkbox.util.tray.swing; import java.awt.AWTException; import java.awt.Dimension; -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; @@ -29,15 +26,13 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JMenuItem; -import javax.swing.SwingUtilities; +import dorkbox.util.SwingUtil; import dorkbox.util.tray.SystemTrayMenuAction; import dorkbox.util.tray.SystemTrayMenuPopup; @@ -62,32 +57,20 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { @Override public void removeTray() { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { SwingSystemTray.this.tray.remove(SwingSystemTray.this.trayIcon); SwingSystemTray.this.menuEntries.clear(); } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error updating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error updating tray menu", e); - } - } + }); super.removeTray(); } @Override public void createTray(final String iconName) { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { SwingSystemTray.this.tray = SystemTray.getSystemTray(); @@ -106,7 +89,7 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { Dimension size = SwingSystemTray.this.jmenu.getPreferredSize(); Point point = e.getPoint(); - Rectangle bounds = getScreenBoundsAt(point); + Rectangle bounds = SwingUtil.getScreenBoundsAt(point); int x = point.x; int y = point.y; @@ -150,19 +133,7 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { } } } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error creating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error creating tray menu", e); - } - } + }); } Image newImage(String name) { @@ -171,68 +142,9 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { return new ImageIcon(iconPath).getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_SMOOTH); } -// public static Rectangle getSafeScreenBounds(Point pos) { -// Rectangle bounds = getScreenBoundsAt(pos); -// Insets insets = getScreenInsetsAt(pos); -// -// bounds.x += insets.left; -// bounds.y += insets.top; -// bounds.width -= insets.left + insets.right; -// bounds.height -= insets.top + insets.bottom; -// -// return bounds; -// } - -// public static Insets getScreenInsetsAt(Point pos) { -// GraphicsDevice gd = getGraphicsDeviceAt(pos); -// Insets insets = null; -// if (gd != null) { -// insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration()); -// } -// -// return insets; -// } - - private static Rectangle getScreenBoundsAt(Point pos) { - GraphicsDevice gd = getGraphicsDeviceAt(pos); - Rectangle bounds = null; - if (gd != null) { - bounds = gd.getDefaultConfiguration().getBounds(); - } - - return bounds; - } - - private static GraphicsDevice getGraphicsDeviceAt(Point pos) { - GraphicsDevice device; - - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice lstGDs[] = ge.getScreenDevices(); - - ArrayList lstDevices = new ArrayList(lstGDs.length); - - for (GraphicsDevice gd : lstGDs) { - - GraphicsConfiguration gc = gd.getDefaultConfiguration(); - Rectangle screenBounds = gc.getBounds(); - - if (screenBounds.contains(pos)) { - lstDevices.add(gd); - } - } - - if (lstDevices.size() > 0) { - device = lstDevices.get(0); - } else { - device = ge.getDefaultScreenDevice(); - } - - return device; - } - @Override public void setStatus(final String infoString, final String iconName) { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { if (SwingSystemTray.this.connectionStatusItem == null) { @@ -243,19 +155,7 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { SwingSystemTray.this.connectionStatusItem.setText(infoString); } } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error updating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error updating tray menu", e); - } - } + }); Image trayImage = newImage(iconName); SwingSystemTray.this.trayIcon.setImage(trayImage); @@ -266,7 +166,7 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { */ @Override public void addMenuEntry(final String menuText, final SystemTrayMenuAction callback) { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { Map menuEntries2 = SwingSystemTray.this.menuEntries; @@ -297,19 +197,7 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { } } } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error updating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error updating tray menu", e); - } - } + }); } /** @@ -317,7 +205,7 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { */ @Override public void updateMenuEntry(final String origMenuText, final String newMenuText, final SystemTrayMenuAction newCallback) { - Runnable doRun = new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { Map menuEntries2 = SwingSystemTray.this.menuEntries; @@ -349,18 +237,6 @@ public class SwingSystemTray extends dorkbox.util.tray.SystemTray { } } } - }; - - if (SwingUtilities.isEventDispatchThread()) { - doRun.run(); - } else { - try { - SwingUtilities.invokeAndWait(doRun); - } catch (InvocationTargetException e) { - logger.error("Error updating tray menu", e); - } catch (InterruptedException e) { - logger.error("Error updating tray menu", e); - } - } + }); } }