Moved JNA libraries into SystemTray project (it made no sense to have them inside the utils project)

This commit is contained in:
nathan 2016-02-14 20:23:46 +01:00
parent e38071475a
commit 60b12d4036
5 changed files with 0 additions and 768 deletions

View File

@ -1,71 +0,0 @@
/*
* 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.util.jna.linux;
import com.sun.jna.Library;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import dorkbox.util.Keep;
import dorkbox.util.jna.linux.Gobject.GObjectStruct;
import java.util.Arrays;
import java.util.List;
/* bindings for libappindicator */
public
interface AppIndicator extends Library {
// effing retarded. There are DIFFERENT versions, of which they all share the same basic compatibility (of the methods that
// we use), however -- we cannot just LOAD via the 'base-name', we actually have to try each one. There are bash commands that
// will tell us the linked library name, however - I'd rather not run bash commands to determine this. This is so hacky it makes me
// sick.
AppIndicator INSTANCE = AppIndicatorQuery.get();
/** Necessary to provide warnings, because libappindicator3 is a piece of shit. */
boolean IS_VERSION_3 = AppIndicatorQuery.isVersion3;
int CATEGORY_APPLICATION_STATUS = 0;
int CATEGORY_COMMUNICATIONS = 1;
int CATEGORY_SYSTEM_SERVICES = 2;
int CATEGORY_HARDWARE = 3;
int CATEGORY_OTHER = 4;
int STATUS_PASSIVE = 0;
int STATUS_ACTIVE = 1;
int STATUS_ATTENTION = 2;
@Keep
class AppIndicatorInstanceStruct extends Structure {
public GObjectStruct parent;
public Pointer priv;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("parent", "priv");
}
}
// Note: AppIndicators DO NOT support tooltips, as per mark shuttleworth. Rather stupid IMHO.
// See: https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12
AppIndicatorInstanceStruct app_indicator_new(String id, String icon_name, int category);
void app_indicator_set_status(AppIndicatorInstanceStruct self, int status);
void app_indicator_set_menu(AppIndicatorInstanceStruct self, Pointer menu);
void app_indicator_set_icon(AppIndicatorInstanceStruct self, String icon_name);
}

View File

@ -1,120 +0,0 @@
/*
* 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.util.jna.linux;
import com.sun.jna.Native;
/**
* Helper for AppIndicator, because it is absolutely mindboggling how those whom maintain the standard, can't agree to what that standard
* library naming convention or features set is. We just try until we find one that work, and are able to map the symbols we need.
*/
class AppIndicatorQuery {
/**
* must call get() before accessing this! Only "AppIndicator" interface should access this!
*/
static volatile boolean isVersion3 = false;
/**
* Is AppIndicator loaded yet?
*/
static volatile boolean isLoaded = false;
public static
AppIndicator get() {
Object library;
// NOTE: GtkSupport uses this info to figure out WHAT VERSION OF GTK to use: appindiactor1 -> GTk2, appindicator3 -> GTK3.
if (GtkSupport.FORCE_GTK2) {
// try loading appindicator1 first, maybe it's there?
try {
library = Native.loadLibrary("appindicator1", AppIndicator.class);
if (library != null) {
return (AppIndicator) library;
}
} catch (Throwable ignored) {
}
}
// start with base version
try {
library = Native.loadLibrary("appindicator", AppIndicator.class);
if (library != null) {
String s = library.toString();
if (s.indexOf("appindicator3") > 0) {
isVersion3 = true;
}
isLoaded = true;
return (AppIndicator) library;
}
} catch (Throwable ignored) {
}
// whoops. Symbolic links are bugged out. Look manually for it...
try {
library = Native.loadLibrary("appindicator1", AppIndicator.class);
if (library != null) {
return (AppIndicator) library;
}
} catch (Throwable ignored) {
}
// now check all others. super hacky way to do this.
for (int i = 10; i >= 0; i--) {
try {
library = Native.loadLibrary("appindicator" + i, AppIndicator.class);
} catch (Throwable ignored) {
library = null;
}
if (library != null) {
String s = library.toString();
// version 3 WILL NOT work with icons in the menu. This allows us to show a warning (in the System tray initialization)
if (i == 3 || s.indexOf("appindicator3") > 0) {
isVersion3 = true;
}
return (AppIndicator) library;
}
}
// another type. who knows...
try {
library = Native.loadLibrary("appindicator-gtk", AppIndicator.class);
if (library != null) {
return (AppIndicator) library;
}
} catch (Throwable ignored) {
}
// this is HORRID. such a PITA
try {
library = Native.loadLibrary("appindicator-gtk3", AppIndicator.class);
if (library != null) {
return (AppIndicator) library;
}
} catch (Throwable ignored) {
}
throw new RuntimeException("We apologize for this, but we are unable to determine the appIndicator library is in use, if " +
"or even if it is in use... Please create an issue for this and include your OS type and configuration.");
}
}

View File

@ -1,174 +0,0 @@
/*
* 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.util.jna.linux;
import com.sun.jna.*;
import dorkbox.util.Keep;
import dorkbox.util.jna.linux.Gtk.GdkEventButton;
import java.util.Arrays;
import java.util.List;
public
interface Gobject extends Library {
Gobject INSTANCE = (Gobject) Native.loadLibrary("gobject-2.0", Gobject.class);
@Keep
class GTypeClassStruct extends Structure {
public
class ByValue extends GTypeClassStruct implements Structure.ByValue {}
public
class ByReference extends GTypeClassStruct implements Structure.ByReference {}
public NativeLong g_type;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("g_type");
}
}
@Keep
class GTypeInstanceStruct extends Structure {
public
class ByValue extends GTypeInstanceStruct implements Structure.ByValue {}
public
class ByReference extends GTypeInstanceStruct implements Structure.ByReference {}
public Pointer g_class;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("g_class");
}
}
@Keep
class GObjectStruct extends Structure {
public
class ByValue extends GObjectStruct implements Structure.ByValue {}
public
class ByReference extends GObjectStruct implements Structure.ByReference {}
public GTypeInstanceStruct g_type_instance;
public int ref_count;
public Pointer qdata;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("g_type_instance", "ref_count", "qdata");
}
}
@Keep
class GObjectClassStruct extends Structure {
public
class ByValue extends GObjectClassStruct implements Structure.ByValue {}
public
class ByReference extends GObjectClassStruct implements Structure.ByReference {}
public GTypeClassStruct g_type_class;
public Pointer construct_properties;
public Pointer constructor;
public Pointer set_property;
public Pointer get_property;
public Pointer dispose;
public Pointer finalize;
public Pointer dispatch_properties_changed;
public Pointer notify;
public Pointer constructed;
public NativeLong flags;
public Pointer dummy1;
public Pointer dummy2;
public Pointer dummy3;
public Pointer dummy4;
public Pointer dummy5;
public Pointer dummy6;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("g_type_class", "construct_properties", "constructor", "set_property", "get_property", "dispose",
"finalize", "dispatch_properties_changed", "notify", "constructed", "flags", "dummy1", "dummy2", "dummy3",
"dummy4", "dummy5", "dummy6");
}
}
@Keep
interface GCallback extends Callback {
/**
* @return Gtk.TRUE if we handled this event
*/
int callback(Pointer instance, Pointer data);
}
@Keep
interface GEventCallback extends Callback {
void callback(Pointer instance, GdkEventButton event);
}
@Keep
class xyPointer extends Structure {
public int value;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("value");
}
}
@Keep
interface GPositionCallback extends Callback {
void callback(Pointer menu, xyPointer x, xyPointer y, Pointer push_in_bool, Pointer user_data);
}
void g_free(Pointer object);
void g_object_ref(Pointer object);
void g_object_unref(Pointer object);
void g_object_ref_sink(Pointer object);
NativeLong g_signal_connect_data(Pointer instance, String detailed_signal, Callback c_handler, Pointer data, Pointer destroy_data,
int connect_flags);
void g_signal_handler_disconnect(Pointer instance, NativeLong longAddress);
Pointer g_markup_printf_escaped(String pattern, String inputString);
}

View File

@ -1,140 +0,0 @@
/*
* 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.util.jna.linux;
import com.sun.jna.Function;
import com.sun.jna.Library;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import dorkbox.util.Keep;
import java.util.Arrays;
import java.util.List;
public
interface Gtk extends Library {
// 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
Gtk INSTANCE = GtkSupport.get();
Function gtk_status_icon_position_menu = GtkSupport.gtk_status_icon_position_menu;
int FALSE = 0;
int TRUE = 1;
@Keep
class GdkEventButton extends Structure {
public int type;
public Pointer window;
public int send_event;
public int time;
public double x;
public double y;
public Pointer axes;
public int state;
public int button;
public Pointer device;
public double x_root;
public double y_root;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("type", "window", "send_event", "time", "x", "y", "axes", "state", "button", "device", "x_root", "y_root");
}
}
boolean gtk_init_check(int argc, String[] argv);
/**
* Runs the main loop until gtk_main_quit() is called. You can nest calls to gtk_main(). In that case gtk_main_quit() will make the
* innermost invocation of the main loop return.
*/
void gtk_main();
/** sks for the current nesting level of the main loop. Useful to determine (at startup) if GTK is already runnign */
int gtk_main_level();
/**
* Makes the innermost invocation of the main loop return when it regains control. ONLY CALL FROM THE GtkSupport class, UNLESS you know
* what you're doing!
*/
void gtk_main_quit();
void gdk_threads_init();
// tricky business. This should only be in the dispatch thread
void gdk_threads_enter();
void gdk_threads_leave();
Pointer gtk_menu_new();
Pointer gtk_menu_item_new();
Pointer gtk_menu_item_new_with_label(String label);
// to create a menu entry WITH an icon.
Pointer gtk_image_new_from_file(String iconPath);
Pointer gtk_image_menu_item_new_with_label(String label);
void gtk_image_menu_item_set_image(Pointer image_menu_item, Pointer image);
void gtk_image_menu_item_set_always_show_image(Pointer menu_item, int forceShow);
Pointer gtk_bin_get_child(Pointer parent);
void gtk_label_set_text(Pointer label, String text);
void gtk_label_set_markup(Pointer label, Pointer markup);
void gtk_label_set_use_markup(Pointer label, int gboolean);
Pointer gtk_status_icon_new();
void gtk_status_icon_set_from_file(Pointer widget, String lablel);
void gtk_status_icon_set_visible(Pointer widget, boolean visible);
// app indicators don't support this, and we cater to the lowest common denominator
// void gtk_status_icon_set_tooltip(Pointer widget, String tooltipText);
void gtk_status_icon_set_title(Pointer widget, String titleText);
void gtk_status_icon_set_name(Pointer widget, String name);
void gtk_menu_popup(Pointer menu, Pointer widget, Pointer bla, Function func, Pointer data, int button, int time);
void gtk_menu_item_set_label(Pointer menu_item, String label);
void gtk_menu_shell_append(Pointer menu_shell, Pointer child);
void gtk_menu_shell_deactivate(Pointer menu_shell, Pointer child);
void gtk_widget_set_sensitive(Pointer widget, int sensitive);
void gtk_container_remove(Pointer menu, Pointer subItem);
void gtk_widget_show(Pointer widget);
void gtk_widget_show_all(Pointer widget);
void gtk_widget_destroy(Pointer widget);
}

View File

@ -1,263 +0,0 @@
/*
* 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.util.jna.linux;
import com.sun.jna.Function;
import com.sun.jna.Native;
import dorkbox.util.Property;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
public
class GtkSupport {
// RE: SWT
// https://developer.gnome.org/glib/stable/glib-Deprecated-Thread-APIs.html#g-thread-init
// Since version >= 2.24, threads can only init once. Multiple calls do nothing, and we can nest gtk_main()
// in a nested loop.
private static volatile boolean started = false;
private static final ArrayBlockingQueue<Runnable> dispatchEvents = new ArrayBlockingQueue<Runnable>(256);
private static volatile Thread gtkDispatchThread;
@Property
/** Forces the system to always choose GTK2 (even when GTK3 might be available). JavaFX uses GTK2! */
public static boolean FORCE_GTK2 = false;
@Property
/**
* Forces the system to enter into JavaFX compatibility mode, where it will use GTK2 AND will not start/stop the GTK main loop.
* This is only necessary if autodetection fails
*/
public static boolean JAVAFX_COMPATIBILITY_MODE = false;
/**
* must call get() before accessing this! Only "Gtk" interface should access this!
*/
static volatile Function gtk_status_icon_position_menu = null;
public static volatile boolean isGtk2 = false;
private static volatile boolean alreadyRunningGTK = false;
/**
* Helper for GTK, because we could have v3 or v2.
*
* Observations: JavaFX uses GTK2, and we can't load GTK3 if GTK2 symbols are loaded
*/
@SuppressWarnings("Duplicates")
public static
Gtk get() {
Gtk library;
boolean shouldUseGtk2 = GtkSupport.FORCE_GTK2 || JAVAFX_COMPATIBILITY_MODE;
alreadyRunningGTK = JAVAFX_COMPATIBILITY_MODE;
// for more info on JavaFX: https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
// from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
// in some cases, we ALWAYS want to try GTK2 first
if (shouldUseGtk2) {
try {
gtk_status_icon_position_menu = Function.getFunction("gtk-x11-2.0", "gtk_status_icon_position_menu");
library = (Gtk) Native.loadLibrary("gtk-x11-2.0", Gtk.class);
if (library != null) {
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 stared GTK -- so we DO NOT NEED TO.
alreadyRunningGTK |= library.gtk_main_level() != 0;
return library;
}
} catch (Throwable ignored) {
}
}
if (AppIndicatorQuery.isLoaded) {
if (AppIndicatorQuery.isVersion3) {
// appindicator3 requires GTK3
try {
gtk_status_icon_position_menu = Function.getFunction("libgtk-3.so.0", "gtk_status_icon_position_menu");
library = (Gtk) Native.loadLibrary("libgtk-3.so.0", Gtk.class);
if (library != null) {
// 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 stared GTK -- so we DO NOT NEED TO.
alreadyRunningGTK |= library.gtk_main_level() != 0;
return library;
}
} catch (Throwable ignored) {
}
} else {
// appindicator1 requires GTK2
try {
gtk_status_icon_position_menu = Function.getFunction("gtk-x11-2.0", "gtk_status_icon_position_menu");
library = (Gtk) Native.loadLibrary("gtk-x11-2.0", Gtk.class);
if (library != null) {
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 stared GTK -- so we DO NOT NEED TO.
alreadyRunningGTK |= library.gtk_main_level() != 0;
return library;
}
} catch (Throwable ignored) {
}
}
}
// now for the defaults...
// start with version 3
try {
gtk_status_icon_position_menu = Function.getFunction("libgtk-3.so.0", "gtk_status_icon_position_menu");
library = (Gtk) Native.loadLibrary("libgtk-3.so.0", Gtk.class);
if (library != null) {
// 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 stared GTK -- so we DO NOT NEED TO.
alreadyRunningGTK |= library.gtk_main_level() != 0;
return library;
}
} catch (Throwable ignored) {
}
// now version 2
try {
gtk_status_icon_position_menu = Function.getFunction("gtk-x11-2.0", "gtk_status_icon_position_menu");
library = (Gtk) Native.loadLibrary("gtk-x11-2.0", Gtk.class);
if (library != null) {
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 stared GTK -- so we DO NOT NEED TO.
alreadyRunningGTK |= library.gtk_main_level() != 0;
return library;
}
} catch (Throwable ignored) {
}
throw new RuntimeException("We apologize for this, but we are unable to determine the GTK library is in use, if " +
"or even if it is in use... Please create an issue for this and include your OS type and configuration.");
}
public static
void startGui() {
// only permit one startup per JVM instance
if (!started) {
started = true;
// GTK specifies that we ONLY run from a single thread. This guarantees that.
gtkDispatchThread = new Thread() {
@Override
public
void run() {
final Gtk gtk = Gtk.INSTANCE;
while (started) {
try {
final Runnable take = dispatchEvents.take();
gtk.gdk_threads_enter();
take.run();
gtk.gdk_threads_leave();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
gtkDispatchThread.setName("GTK Event Loop");
gtkDispatchThread.start();
// startup the GTK GUI event loop. There can be multiple/nested loops.
// If JavaFX/SWT is used, this is UNNECESSARY
if (!alreadyRunningGTK) {
// only necessary if we are the only GTK instance running...
final CountDownLatch blockUntilStarted = new CountDownLatch(1);
Thread gtkUpdateThread = new Thread() {
@Override
public
void run() {
Gtk gtk = Gtk.INSTANCE;
// prep for the event loop.
gtk.gdk_threads_init();
gtk.gdk_threads_enter();
gtk.gtk_init_check(0, null);
// notify our main thread to continue
blockUntilStarted.countDown();
// blocks unit quit
gtk.gtk_main();
gtk.gdk_threads_leave();
}
};
gtkUpdateThread.setName("GTK Event Loop (Native)");
gtkUpdateThread.start();
try {
// we CANNOT continue until the GTK thread has started!
blockUntilStarted.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* Best practices for GTK, is to call EVERYTHING for it on a SINGLE THREAD. This accomplishes that.
*/
public static
void dispatch(Runnable runnable) {
try {
dispatchEvents.put(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static
void shutdownGui() {
// If JavaFX/SWT is used, this is UNNECESSARY (an will break SWT/JavaFX shutdown)
if (!alreadyRunningGTK) {
Gtk.INSTANCE.gtk_main_quit();
}
started = false;
// put it in a NEW dispatch event (so that we cleanup AFTER this one is finished)
dispatch(new Runnable() {
@Override
public
void run() {
new Thread(new Runnable() {
@Override
public
void run() {
// this should happen in a new thread
gtkDispatchThread.interrupt();
}
}).run();
}
});
}
}