Moved Gtk Event Dispatch into it's own class.

This commit is contained in:
nathan 2017-07-11 01:27:19 +02:00
parent 876c5bb591
commit 9bf01aaf04
13 changed files with 432 additions and 393 deletions

View File

@ -17,20 +17,11 @@ package dorkbox.systemTray.jna.linux;
import static dorkbox.systemTray.SystemTray.logger;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.sun.jna.Function;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.util.JavaFX;
import dorkbox.systemTray.util.Swt;
import dorkbox.util.OS;
import dorkbox.util.jna.JnaHelper;
@ -86,33 +77,10 @@ class Gtk {
public static final int FALSE = 0;
public static final int TRUE = 1;
private static final boolean alreadyRunningGTK;
// when debugging the EDT, we need a longer timeout.
private static final boolean debugEDT = true;
// timeout is in seconds
private static final int TIMEOUT = debugEDT ? 10000000 : 2;
// 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<Object> gtkCallbacks = new LinkedList<Object>();
static final boolean alreadyRunningGTK;
public static Function gtk_status_icon_position_menu = null;
// This is required because the EDT needs to have it's own value for this boolean, that is a different value than the main thread
private static ThreadLocal<Boolean> isDispatch = new ThreadLocal<Boolean>() {
@Override
protected
Boolean initialValue() {
return false;
}
};
private static volatile boolean started = false;
@SuppressWarnings("FieldCanBeLocal")
private static Thread gtkUpdateThread = null;
public static final int MAJOR;
public static final int MINOR;
public static final int MICRO;
@ -274,298 +242,6 @@ class Gtk {
}
}
public static
void startGui() {
// only permit one startup per JVM instance
if (!started) {
started = true;
// startup the GTK GUI event loop. There can be multiple/nested loops.
if (!alreadyRunningGTK) {
// If JavaFX/SWT is used, this is UNNECESSARY (we can detect if the GTK main_loop is running)
gtkUpdateThread = new Thread() {
@Override
public
void run() {
Glib.GLogFunc orig = null;
if (SystemTray.DEBUG) {
logger.debug("Running GTK Native Event Loop");
} else {
// NOTE: This can output warnings, so we suppress them
orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
}
// prep for the event loop.
// GThread.g_thread_init(null); would be needed for g_idle_add()
if (!Gtk2.gtk_init_check(0)) {
if (SystemTray.DEBUG) {
logger.error("Error starting GTK");
}
return;
}
// gdk_threads_enter(); would be needed for g_idle_add()
if (orig != null) {
Glib.g_log_set_default_handler(orig, null);
}
// blocks unit quit
Gtk2.gtk_main();
// clean up threads
// gdk_threads_leave(); would be needed for g_idle_add()
}
};
gtkUpdateThread.setDaemon(false); // explicitly NOT daemon so that this will hold the JVM open as necessary
gtkUpdateThread.setName("GTK Native Event Loop");
gtkUpdateThread.start();
}
}
}
/**
* Waits for the all posted events to GTK to finish loading
*/
public static
void waitForEventsToComplete() {
final CountDownLatch blockUntilStarted = new CountDownLatch(1);
dispatch(new Runnable() {
@Override
public
void run() {
blockUntilStarted.countDown();
}
});
if (SystemTray.isJavaFxLoaded) {
if (!JavaFX.isEventThread()) {
try {
if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
new Exception(""));
}
}
// we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
while (true) {
Thread.sleep(100);
synchronized (gtkCallbacks) {
if (gtkCallbacks.isEmpty()) {
break;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
else if (SystemTray.isSwtLoaded) {
if (!Swt.isEventThread()) {
// we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
try {
if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
new Exception(""));
}
}
while (true) {
Thread.sleep(100);
synchronized (gtkCallbacks) {
if (gtkCallbacks.isEmpty()) {
break;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
else {
try {
if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
new Exception(""));
}
}
// we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
while (true) {
Thread.sleep(100);
synchronized (gtkCallbacks) {
if (gtkCallbacks.isEmpty()) {
break;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Best practices for GTK, is to call EVERYTHING for it on the GTK THREAD. This accomplishes that.
*/
public static
void dispatch(final Runnable runnable) {
if (alreadyRunningGTK) {
if (SystemTray.isJavaFxLoaded) {
// JavaFX only
if (JavaFX.isEventThread()) {
// Run directly on the JavaFX event thread
runnable.run();
}
else {
JavaFX.dispatch(runnable);
}
return;
}
if (SystemTray.isSwtLoaded) {
if (Swt.isEventThread()) {
// Run directly on the SWT event thread. If it's not on the dispatch thread, we can use raw GTK to put it there
runnable.run();
return;
}
}
}
// not javafx
// gtk/swt are **mostly** the same in how events are dispatched, so we can use "raw" gtk methods for SWT
if (isDispatch.get()) {
// Run directly on the dispatch thread
runnable.run();
}
else {
final FuncCallback callback = new FuncCallback() {
@Override
public
int callback(final Pointer data) {
synchronized (gtkCallbacks) {
gtkCallbacks.removeFirst(); // now that we've 'handled' it, we can remove it from our callback list
}
isDispatch.set(true);
try {
runnable.run();
} finally {
isDispatch.set(false);
}
return Gtk.FALSE; // don't want to call this again
}
};
synchronized (gtkCallbacks) {
gtkCallbacks.offer(callback); // prevent GC from collecting this object before it can be called
}
// the correct way to do it. Add with a slightly higher value
Gtk2.gdk_threads_add_idle_full(100, callback, null, null);
}
}
public static
void shutdownGui() {
dispatchAndWait(new Runnable() {
@Override
public
void run() {
// If JavaFX/SWT is used, this is UNNECESSARY (and will break SWT/JavaFX shutdown)
if (!alreadyRunningGTK) {
Gtk2.gtk_main_quit();
}
started = false;
}
});
}
public static
void dispatchAndWait(final Runnable runnable) {
if (isDispatch.get()) {
// Run directly on the dispatch thread (should not "redispatch" this again)
runnable.run();
}
else {
final CountDownLatch countDownLatch = new CountDownLatch(1);
Gtk.dispatch(new Runnable() {
@Override
public
void run() {
try {
runnable.run();
} catch (Exception e) {
SystemTray.logger.error("Error during GTK run loop: ", e);
} finally {
countDownLatch.countDown();
}
}
});
// this is slightly different than how swing does it. We have a timeout here so that we can make sure that updates on the GUI
// thread occur in REASONABLE time-frames, and alert the user if not.
try {
if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error(
"Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT + " seconds " +
"to complete.", new Exception(""));
}
else {
throw new RuntimeException("Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT +
" seconds " + "to complete.");
}
}
} catch (InterruptedException e) {
SystemTray.logger.error("Error waiting for dispatch to complete.", new Exception(""));
}
}
}
/**
* required to properly setup the dispatch flag when using native menus
*
* @param callback will never be null.
*/
public static
void proxyClick(final Entry menuEntry, final ActionListener callback) {
Gtk.isDispatch.set(true);
try {
if (menuEntry != null) {
callback.actionPerformed(new ActionEvent(menuEntry, ActionEvent.ACTION_PERFORMED, ""));
}
else {
// checkbox entries will not pass the menuEntry in, because they redispatch the click event so that the checkbox state is
// toggled
callback.actionPerformed(null);
}
} finally {
Gtk.isDispatch.set(false);
}
}
/**
* Creates a new GtkMenu
*/

View File

@ -19,7 +19,7 @@ import com.sun.jna.Pointer;
/**
* bindings for GTK+ 3.
*
* <p>
* Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
*/
public
@ -27,9 +27,14 @@ class Gtk3 {
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
// objdump -T /usr/local/lib/libgtk-3.so.0 | grep gtk
public static native int gtk_get_major_version();
public static native int gtk_get_minor_version();
public static native int gtk_get_micro_version();
public static native
int gtk_get_major_version();
public static native
int gtk_get_minor_version();
public static native
int gtk_get_micro_version();
/**
@ -39,65 +44,75 @@ class Gtk3 {
* @param variant variant to load, for example, "dark", or NULL for the default.
*
* @return a GtkCssProvider with the theme loaded. This memory is owned by GTK+, and you must not free it.
*
* @since 3.0
*/
public static native Pointer gtk_css_provider_get_named(String name, String variant);
public static native
Pointer gtk_css_provider_get_named(String name, String variant);
/**
* Returns the provider containing the style settings used as a fallback for all widgets.
*
* @return a GtkCssProvider with the theme loaded. This memory is owned by GTK+, and you must not free it.
*
* @since 3.0
*/
public static native Pointer gtk_css_provider_get_default();
public static native
Pointer gtk_css_provider_get_default();
/**
* Converts the provider into a string representation in CSS format.
*
* <p>
* Using gtk_css_provider_load_from_data() with the return value from this function on a new provider created with
* gtk_css_provider_new() will basically create a duplicate of this provider .
*
* @since 3.2 (released in 2011)
*/
public static native String gtk_css_provider_to_string(Pointer provider);
public static native
String gtk_css_provider_to_string(Pointer provider);
/**
* Gets the foreground color for a given state.
*
* @since 3.0
*/
public static native void gtk_style_context_get_color(Pointer context, int stateFlags, Pointer color);
public static native
void gtk_style_context_get_color(Pointer context, int stateFlags, Pointer color);
/**
* Returns the state used for style matching.
*
* @since 3.0
*/
public static native int gtk_style_context_get_state(Pointer context);
public static native
int gtk_style_context_get_state(Pointer context);
/**
* Looks up and resolves a color name in the context color map.
*
* @since 3.0 (but not in the documentation...)
*/
public static native boolean gtk_style_context_lookup_color(Pointer widget, String name, Pointer color);
public static native
boolean gtk_style_context_lookup_color(Pointer widget, String name, Pointer color);
/**
* Returns the style context associated to widget . The returned object is guaranteed to be the same for the lifetime of widget .
*
* @since 3.0 (but not in the documentation...)
*/
public static native Pointer gtk_widget_get_style_context(Pointer widget);
public static native
Pointer gtk_widget_get_style_context(Pointer widget);
/**
* Saves the context state, so temporary modifications done through gtk_style_context_add_class(), gtk_style_context_remove_class(),
* gtk_style_context_set_state(), etc. can quickly be reverted in one go through gtk_style_context_restore().
*
* <p>
* The matching call to gtk_style_context_restore() must be done before GTK returns to the main loop.
*
* @since 3.0
*/
public static native void gtk_style_context_save(Pointer context);
public static native
void gtk_style_context_save(Pointer context);
/**
@ -105,7 +120,8 @@ class Gtk3 {
*
* @since 3.0
*/
public static native void gtk_style_context_restore(Pointer context);
public static native
void gtk_style_context_restore(Pointer context);
/**
* Adds a style class to context , so posterior calls to gtk_style_context_get() or any of the gtk_render_*() functions will make
@ -113,32 +129,35 @@ class Gtk3 {
*
* @since 3.0
*/
public static native void gtk_style_context_add_class(Pointer context, String className);
public static native
void gtk_style_context_add_class(Pointer context, String className);
/**
* Gets the padding for a given state as a GtkBorder. See gtk_style_context_get() and GTK_STYLE_PROPERTY_PADDING for details.
*
* @since 3.0
*/
public static native void gtk_style_context_get_padding(Pointer context, int state, Pointer border);
public static native
void gtk_style_context_get_padding(Pointer context, int state, Pointer border);
/**
* Gets the border for a given state as a GtkBorder.
*
* <p>
* See gtk_style_context_get_property() and GTK_STYLE_PROPERTY_BORDER_WIDTH for details.
*
* @since 3.0
*/
public static native void gtk_style_context_get_border(Pointer context, int state, Pointer border);
public static native
void gtk_style_context_get_border(Pointer context, int state, Pointer border);
/**
* Returns the internal scale factor that maps from window coordinates to the actual device pixels. On traditional systems this is 1,
* but on very high density outputs this can be a higher value (often 2).
*
* <p>
* A higher value means that drawing is automatically scaled up to a higher resolution, so any code doing drawing will automatically
* look nicer. However, if you are supplying pixel-based data the scale value can be used to determine whether to use a pixel
* resource with higher resolution data.
*
* <p>
* The scale of a window may change during runtime, if this happens a configure event will be sent to the toplevel window.
*
* @return the scale factor
@ -146,20 +165,19 @@ class Gtk3 {
* @since 3.10
*/
public static native
int gdk_window_get_scale_factor (Pointer window);
int gdk_window_get_scale_factor(Pointer window);
/**
* Retrieves the minimum and natural size of a widget, taking into account the widgets preference for height-for-width management.
*
* <p>
* This is used to retrieve a suitable size by container widgets which do not impose any restrictions on the child placement.
* It can be used to deduce toplevel window and menu sizes as well as child widgets in free-form containers such as GtkLayout.
*
* <p>
* Handle with care. Note that the natural height of a height-for-width widget will generally be a smaller size than the minimum
* height, since the required height for the natural width is generally smaller than the required height for the minimum width.
*
* <p>
* Use gtk_widget_get_preferred_height_and_baseline_for_width() if you want to support baseline alignment.
*
*
* @param widget a GtkWidget instance
* @param minimum_size location for storing the minimum size, or NULL.
* @param natural_size location for storing the natural size, or NULL.

View File

@ -0,0 +1,336 @@
package dorkbox.systemTray.jna.linux;
import static dorkbox.systemTray.SystemTray.logger;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.sun.jna.Pointer;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.util.JavaFX;
import dorkbox.systemTray.util.Swt;
public
class GtkEventDispatch {
// 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<Object> gtkCallbacks = new LinkedList<Object>();
// This is required because the EDT needs to have it's own value for this boolean, that is a different value than the main thread
private static ThreadLocal<Boolean> isDispatch = new ThreadLocal<Boolean>() {
@Override
protected
Boolean initialValue() {
return false;
}
};
private static volatile boolean started = false;
@SuppressWarnings("FieldCanBeLocal")
private static Thread gtkUpdateThread = null;
// when debugging the EDT, we need a longer timeout.
private static final boolean debugEDT = true;
// timeout is in seconds
private static final int TIMEOUT = debugEDT ? 10000000 : 2;
public static
void startGui() {
// only permit one startup per JVM instance
if (!started) {
started = true;
// startup the GTK GUI event loop. There can be multiple/nested loops.
if (!Gtk.alreadyRunningGTK) {
// If JavaFX/SWT is used, this is UNNECESSARY (we can detect if the GTK main_loop is running)
gtkUpdateThread = new Thread() {
@Override
public
void run() {
Glib.GLogFunc orig = null;
if (SystemTray.DEBUG) {
logger.debug("Running GTK Native Event Loop");
} else {
// NOTE: This can output warnings, so we suppress them
orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
}
// prep for the event loop.
// GThread.g_thread_init(null); would be needed for g_idle_add()
if (!Gtk2.gtk_init_check(0)) {
if (SystemTray.DEBUG) {
logger.error("Error starting GTK");
}
return;
}
// gdk_threads_enter(); would be needed for g_idle_add()
if (orig != null) {
Glib.g_log_set_default_handler(orig, null);
}
// blocks unit quit
Gtk2.gtk_main();
// clean up threads
// gdk_threads_leave(); would be needed for g_idle_add()
}
};
gtkUpdateThread.setDaemon(false); // explicitly NOT daemon so that this will hold the JVM open as necessary
gtkUpdateThread.setName("GTK Native Event Loop");
gtkUpdateThread.start();
}
}
}
/**
* Waits for the all posted events to GTK to finish loading
*/
@SuppressWarnings("Duplicates")
public static
void waitForEventsToComplete() {
final CountDownLatch blockUntilStarted = new CountDownLatch(1);
dispatch(new Runnable() {
@Override
public
void run() {
blockUntilStarted.countDown();
}
});
if (SystemTray.isJavaFxLoaded) {
if (!JavaFX.isEventThread()) {
try {
if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
new Exception(""));
}
}
// we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
while (true) {
Thread.sleep(100);
synchronized (gtkCallbacks) {
if (gtkCallbacks.isEmpty()) {
break;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
else if (SystemTray.isSwtLoaded) {
if (!Swt.isEventThread()) {
// we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
try {
if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
new Exception(""));
}
}
while (true) {
Thread.sleep(100);
synchronized (gtkCallbacks) {
if (gtkCallbacks.isEmpty()) {
break;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
else {
try {
if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
new Exception(""));
}
}
// we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
while (true) {
Thread.sleep(100);
synchronized (gtkCallbacks) {
if (gtkCallbacks.isEmpty()) {
break;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Best practices for GTK, is to call EVERYTHING for it on the GTK THREAD. This accomplishes that.
*/
public static
void dispatch(final Runnable runnable) {
if (Gtk.alreadyRunningGTK) {
if (SystemTray.isJavaFxLoaded) {
// JavaFX only
if (JavaFX.isEventThread()) {
// Run directly on the JavaFX event thread
runnable.run();
}
else {
JavaFX.dispatch(runnable);
}
return;
}
if (SystemTray.isSwtLoaded) {
if (Swt.isEventThread()) {
// Run directly on the SWT event thread. If it's not on the dispatch thread, we can use raw GTK to put it there
runnable.run();
return;
}
}
}
// not javafx
// gtk/swt are **mostly** the same in how events are dispatched, so we can use "raw" gtk methods for SWT
if (isDispatch.get()) {
// Run directly on the dispatch thread
runnable.run();
}
else {
final FuncCallback callback = new FuncCallback() {
@Override
public
int callback(final Pointer data) {
synchronized (gtkCallbacks) {
gtkCallbacks.removeFirst(); // now that we've 'handled' it, we can remove it from our callback list
}
isDispatch.set(true);
try {
runnable.run();
} finally {
isDispatch.set(false);
}
return Gtk.FALSE; // don't want to call this again
}
};
synchronized (gtkCallbacks) {
gtkCallbacks.offer(callback); // prevent GC from collecting this object before it can be called
}
// the correct way to do it. Add with a slightly higher value
Gtk2.gdk_threads_add_idle_full(100, callback, null, null);
}
}
public static
void shutdownGui() {
dispatchAndWait(new Runnable() {
@Override
public
void run() {
// If JavaFX/SWT is used, this is UNNECESSARY (and will break SWT/JavaFX shutdown)
if (!Gtk.alreadyRunningGTK) {
Gtk2.gtk_main_quit();
}
started = false;
}
});
}
public static
void dispatchAndWait(final Runnable runnable) {
if (isDispatch.get()) {
// Run directly on the dispatch thread (should not "redispatch" this again)
runnable.run();
}
else {
final CountDownLatch countDownLatch = new CountDownLatch(1);
dispatch(new Runnable() {
@Override
public
void run() {
try {
runnable.run();
} catch (Exception e) {
SystemTray.logger.error("Error during GTK run loop: ", e);
} finally {
countDownLatch.countDown();
}
}
});
// this is slightly different than how swing does it. We have a timeout here so that we can make sure that updates on the GUI
// thread occur in REASONABLE time-frames, and alert the user if not.
try {
if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error(
"Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT + " seconds " +
"to complete.", new Exception(""));
}
else {
throw new RuntimeException("Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT +
" seconds " + "to complete.");
}
}
} catch (InterruptedException e) {
SystemTray.logger.error("Error waiting for dispatch to complete.", new Exception(""));
}
}
}
/**
* required to properly setup the dispatch flag when using native menus
*
* @param callback will never be null.
*/
public static
void proxyClick(final Entry menuEntry, final ActionListener callback) {
isDispatch.set(true);
try {
if (menuEntry != null) {
callback.actionPerformed(new ActionEvent(menuEntry, ActionEvent.ACTION_PERFORMED, ""));
}
else {
// checkbox entries will not pass the menuEntry in, because they redispatch the click event so that the checkbox state is
// toggled
callback.actionPerformed(null);
}
} finally {
isDispatch.set(false);
}
}
}

View File

@ -114,7 +114,7 @@ class GtkTheme {
int getMenuEntryImageSize() {
final AtomicReference<Integer> imageHeight = new AtomicReference<Integer>();
Gtk.dispatchAndWait(new Runnable() {
GtkEventDispatch.dispatchAndWait(new Runnable() {
@Override
public
void run() {
@ -162,7 +162,7 @@ class GtkTheme {
final AtomicReference<Double> screenScale = new AtomicReference<Double>();
final AtomicInteger screenDPI = new AtomicInteger();
Gtk.dispatchAndWait(new Runnable() {
GtkEventDispatch.dispatchAndWait(new Runnable() {
@Override
public
void run() {
@ -431,7 +431,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
// try to use GTK to get the tray icon size
final AtomicInteger traySize = new AtomicInteger();
Gtk.dispatchAndWait(new Runnable() {
GtkEventDispatch.dispatchAndWait(new Runnable() {
@Override
public
void run() {
@ -498,7 +498,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
// these methods are from most accurate (but limited in support) to compatible across Linux OSes.. Strangely enough, GTK makes
// this information insanely difficult to get.
final AtomicReference<Color> color = new AtomicReference<Color>(null);
Gtk.dispatchAndWait(new Runnable() {
GtkEventDispatch.dispatchAndWait(new Runnable() {
@SuppressWarnings("UnusedAssignment")
@Override
public
@ -700,7 +700,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
if (Gtk.isLoaded && Gtk.isGtk3) {
final AtomicReference<String> css_ = new AtomicReference<String>(null);
Gtk.dispatchAndWait(new Runnable() {
GtkEventDispatch.dispatchAndWait(new Runnable() {
@Override
public
void run() {
@ -1218,7 +1218,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
String getThemeName() {
final AtomicReference<String> themeName = new AtomicReference<String>(null);
Gtk.dispatchAndWait(new Runnable() {
GtkEventDispatch.dispatchAndWait(new Runnable() {
@Override
public
void run() {

View File

@ -21,6 +21,7 @@ import com.sun.jna.Pointer;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.EntryPeer;
import dorkbox.systemTray.util.ImageResizeUtil;
@ -102,7 +103,7 @@ class GtkBaseMenuItem implements EntryPeer {
@Override
public
void remove() {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {

View File

@ -28,6 +28,7 @@ import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.MenuPeer;
@SuppressWarnings("deprecation")
@ -196,7 +197,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
public
void add(final Menu parentMenu, final Entry entry, final int index) {
// must always be called on the GTK dispatch. This must be dispatchAndWait
Gtk.dispatchAndWait(new Runnable() {
GtkEventDispatch.dispatchAndWait(new Runnable() {
@Override
public
void run() {
@ -250,7 +251,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
// is overridden by system tray
setLegitImage(menuItem.getImage() != null);
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -279,7 +280,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
public
void setEnabled(final MenuItem menuItem) {
// is overridden by system tray
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -318,7 +319,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
textWithMnemonic = menuItem.getText();
}
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -362,7 +363,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
@Override
public
void remove() {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {

View File

@ -24,6 +24,7 @@ import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.jna.linux.GCallback;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.MenuItemPeer;
class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@ -58,7 +59,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
final ActionListener cb = menuItemForActionCallback.getCallback();
if (cb != null) {
try {
Gtk.proxyClick(menuItemForActionCallback, cb);
GtkEventDispatch.proxyClick(menuItemForActionCallback, cb);
} catch (Exception e) {
SystemTray.logger.error("Error calling menu entry {} click event.", menuItemForActionCallback.getText(), e);
}
@ -77,7 +78,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
void setImage(final MenuItem menuItem) {
setLegitImage(menuItem.getImage() != null);
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -104,7 +105,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@Override
public
void setEnabled(final MenuItem menuItem) {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -141,7 +142,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
textWithMnemonic = menuItem.getText();
}
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -169,7 +170,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@Override
public
void remove() {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {

View File

@ -27,6 +27,7 @@ import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.jna.linux.GCallback;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.jna.linux.GtkTheme;
import dorkbox.systemTray.peer.CheckboxPeer;
import dorkbox.systemTray.util.HeavyCheckMark;
@ -127,7 +128,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
int callback(final Pointer instance, final Pointer data) {
if (callback != null) {
// this will redispatch to our created callback via `setCallback`
Gtk.proxyClick(null, callback);
GtkEventDispatch.proxyClick(null, callback);
}
return Gtk.TRUE;
@ -148,7 +149,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
void setEnabled(final Checkbox menuItem) {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -180,7 +181,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
textWithMnemonic = menuItem.getText();
}
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -227,7 +228,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
if (checked != this.isChecked) {
this.isChecked = checked;
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -283,7 +284,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
void remove() {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {

View File

@ -16,6 +16,7 @@
package dorkbox.systemTray.nativeUI;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.EntryPeer;
class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
@ -35,7 +36,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
@Override
public
void remove() {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -46,11 +47,13 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
});
}
@Override
public
boolean hasImage() {
return false;
}
@Override
public
void setSpacerImage(final boolean everyoneElseHasImages) {
// no op

View File

@ -17,6 +17,7 @@ package dorkbox.systemTray.nativeUI;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.StatusPeer;
// you might wonder WHY this extends MenuEntryItem -- the reason is that an AppIndicator "status" will be offset from everyone else,
@ -40,7 +41,7 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
@Override
public
void setText(final Status menuItem) {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -59,7 +60,7 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
@Override
public
void remove() {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {

View File

@ -27,7 +27,7 @@ import dorkbox.systemTray.gnomeShell.Extension;
import dorkbox.systemTray.jna.linux.AppIndicator;
import dorkbox.systemTray.jna.linux.AppIndicatorInstanceStruct;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.util.ImageResizeUtil;
/**
@ -133,7 +133,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
@Override
public
void setEnabled(final MenuItem menuItem) {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -160,7 +160,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
return;
}
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -195,7 +195,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
final AppIndicatorInstanceStruct savedAppIndicator = appIndicator;
appIndicator = null;
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -209,12 +209,12 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
super.remove();
// does not need to be called on the dispatch (it does that)
Gtk.shutdownGui();
GtkEventDispatch.shutdownGui();
}
}
};
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -226,7 +226,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
}
});
Gtk.waitForEventsToComplete();
GtkEventDispatch.waitForEventsToComplete();
bind(gtkMenu, null, systemTray);
}

View File

@ -28,6 +28,7 @@ import dorkbox.systemTray.jna.linux.GEventCallback;
import dorkbox.systemTray.jna.linux.GdkEventButton;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
/**
* Class for handling all system tray interactions via GTK.
@ -68,7 +69,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
@Override
public
void setEnabled(final MenuItem menuItem) {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -94,7 +95,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
return;
}
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -125,7 +126,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
void remove() {
// This is required if we have JavaFX or SWT shutdown hooks (to prevent us from shutting down twice...)
if (!shuttingDown.getAndSet(true)) {
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -142,12 +143,12 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
super.remove();
// does not need to be called on the dispatch (it does that)
Gtk.shutdownGui();
GtkEventDispatch.shutdownGui();
}
}
};
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -169,10 +170,10 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
}
});
Gtk.waitForEventsToComplete();
GtkEventDispatch.waitForEventsToComplete();
// we have to be able to set our title, otherwise the gnome-shell extension WILL NOT work
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
@ -211,7 +212,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
}
this.tooltipText = tooltipText;
Gtk.dispatch(new Runnable() {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {

View File

@ -28,7 +28,7 @@ import javax.swing.JPopupMenu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Tray;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.util.OS;
import dorkbox.util.SwingUtil;
@ -191,7 +191,7 @@ class _SwingTray extends Tray implements SwingUI {
if (OS.isLinux() || OS.isUnix()) {
// does not need to be called on the dispatch (it does that). Startup happens in the SystemTray (in a special block),
// because we MUST startup the system tray BEFORE to access GTK before we create the swing version (to get size info)
Gtk.shutdownGui();
GtkEventDispatch.shutdownGui();
}
}
};