![JAR search and dependency download from the Maven repository](/logo.png)
dorkbox.systemTray.jna.linux.Gtk Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of SystemTray Show documentation
Show all versions of SystemTray Show documentation
Cross-platform SystemTray support for Swing/AWT, GtkStatusIcon, and AppIndicator on Java 6+
/*
* 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.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.Pointer;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.jna.JnaHelper;
import dorkbox.systemTray.util.JavaFX;
import dorkbox.systemTray.util.Swt;
import dorkbox.util.OS;
/**
* bindings for gtk 2 or 3
*
* note: gtk2/3 loading is SENSITIVE, and which AppIndicator symbols are loaded depends on this being loaded first
* Additionally, gtk3 has deprecated some methods -- which, fortunately for us, means it will be another 25 years before they are
* removed; forcing us to have separate gtk2/3 bindings (we can then only have gtk3 bindings)
*
* Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
*/
@SuppressWarnings({"Duplicates", "SameParameterValue", "DanglingJavadoc"})
public
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 -> appindicator1, GTK3 -> appindicator3
public static final boolean isGtk2;
public static final boolean isGtk3;
public static final boolean isLoaded;
public static Function gtk_status_icon_position_menu = null;
private static final boolean alreadyRunningGTK;
// when debugging the EDT, we need a longer timeout.
private static final boolean debugEDT = true;
// 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 isDispatch = new ThreadLocal() {
@Override
protected
Boolean initialValue() {
return false;
}
};
// timeout is in seconds
private static final int TIMEOUT = debugEDT ? 10000000 : 2;
// 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
// objdump -T /usr/local/lib/libgtk-3.so.0 | grep gtk
/**
* 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.
*/
static {
boolean shouldUseGtk2 = SystemTray.FORCE_GTK2;
boolean _isGtk2 = false;
boolean _isLoaded = false;
boolean _alreadyRunningGTK = false;
boolean shouldLoadGtk = !(OS.isWindows() || OS.isMacOsX());
if (!shouldLoadGtk) {
_isLoaded = true;
}
// we can force the system to use the swing indicator, which WORKS, but doesn't support transparency in the icon.
if (!_isLoaded &&
(SystemTray.FORCE_TRAY_TYPE == SystemTray.TrayType.Swing || SystemTray.FORCE_TRAY_TYPE == SystemTray.TrayType.AWT)) {
if (SystemTray.DEBUG) {
logger.debug("Not loading GTK for Swing or AWT");
}
_isLoaded = true;
}
// in some cases, we ALWAYS want to try GTK2 first
String gtk2LibName = "gtk-x11-2.0";
String gtk3LibName = "libgtk-3.so.0";
if (!_isLoaded && shouldUseGtk2) {
try {
JnaHelper.register(gtk2LibName, Gtk.class);
gtk_status_icon_position_menu = Function.getFunction(gtk2LibName, "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 started GTK -- so we DO NOT NEED TO.
_alreadyRunningGTK = gtk_main_level() != 0;
_isLoaded = true;
if (SystemTray.DEBUG) {
logger.debug("GTK: {}", gtk2LibName);
}
} catch (Throwable e) {
if (SystemTray.DEBUG) {
logger.error("Error loading library", e);
}
}
}
// now for the defaults...
// start with version 3
if (!_isLoaded) {
try {
JnaHelper.register(gtk3LibName, Gtk.class);
gtk_status_icon_position_menu = Function.getFunction(gtk3LibName, "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 started GTK -- so we DO NOT NEED TO.
_alreadyRunningGTK = gtk_main_level() != 0;
_isLoaded = true;
if (SystemTray.DEBUG) {
logger.debug("GTK: {}", gtk3LibName);
}
} catch (Throwable e) {
if (SystemTray.DEBUG) {
logger.error("Error loading library", e);
}
}
}
// now version 2
if (!_isLoaded) {
try {
JnaHelper.register(gtk2LibName, Gtk.class);
gtk_status_icon_position_menu = Function.getFunction(gtk2LibName, "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 started GTK -- so we DO NOT NEED TO.
_alreadyRunningGTK = gtk_main_level() != 0;
_isLoaded = true;
if (SystemTray.DEBUG) {
logger.debug("GTK: {}", gtk2LibName);
}
} catch (Throwable e) {
if (SystemTray.DEBUG) {
logger.error("Error loading library", e);
}
}
}
if (shouldLoadGtk && _isLoaded) {
isLoaded = true;
// depending on how the system is initialized, SWT may, or may not, have the gtk_main loop running. It will EVENTUALLY run, so we
// do not want to run our own GTK event loop.
_alreadyRunningGTK |= SystemTray.isSwtLoaded;
if (SystemTray.DEBUG) {
logger.debug("Is the system already running GTK? {}", _alreadyRunningGTK);
}
alreadyRunningGTK = _alreadyRunningGTK;
isGtk2 = _isGtk2;
isGtk3 = !_isGtk2;
} else {
isLoaded = false;
alreadyRunningGTK = false;
isGtk2 = false;
isGtk3 = false;
}
if (shouldLoadGtk && !_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.");
}
}
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)
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private static final LinkedList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy