Fixed swing threading issues. Fixed GTK threading issues. Code cleanup

This commit is contained in:
nathan 2014-12-12 02:25:50 +01:00
parent 8ddf3291ac
commit 9a48be4c74
5 changed files with 69 additions and 219 deletions

View File

@ -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();

View File

@ -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<Pointer> widgets = new ArrayList<Pointer>(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);
}

View File

@ -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<String, JMenuItem> 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<String, JMenuItem> 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);
}
}
});
}
}

View File

@ -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();

View File

@ -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<GraphicsDevice> lstDevices = new ArrayList<GraphicsDevice>(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<String, JMenuItem> 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<String, JMenuItem> 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);
}
}
});
}
}