Fixed issues with reading structure data from JNA. Now gets text size

and padding VIA pango. Fixed issues with getting colors on linux.
Abstracted Gtk2 from Gtk once Gtk2 deprecated methods are removed.
This commit is contained in:
nathan 2017-07-03 15:18:33 +02:00
parent 0caf22de16
commit a6711dacb4
9 changed files with 1132 additions and 392 deletions

View File

@ -47,6 +47,7 @@ class GdkColor extends Structure {
public
Color getColor() {
read(); // have to read the struct members first!
return new Color(red(), green(), blue());
}

View File

@ -1,5 +1,6 @@
package dorkbox.systemTray.jna.linux;
import java.awt.Color;
import java.util.Arrays;
import java.util.List;
@ -17,6 +18,25 @@ class GdkRGBAColor extends Structure {
public double blue;
public double alpha;
public float red() {
return (float) red;
}
public float green() {
return (float) green;
}
public float blue() {
return (float) blue;
}
public
Color getColor() {
read(); // have to read the struct members first!
return new Color(red(), green(), blue());
}
@Override
protected
List<String> getFieldOrder() {

View File

@ -18,7 +18,6 @@ package dorkbox.systemTray.jna.linux;
import com.sun.jna.Callback;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.PointerByReference;
import dorkbox.systemTray.SystemTray;
import dorkbox.util.jna.JnaHelper;
@ -55,7 +54,7 @@ class Gobject {
public static native void g_signal_handler_block(Pointer instance, long handlerId);
public static native void g_signal_handler_unblock(Pointer instance, long handlerId);
public static native void g_object_get(Pointer instance, String property_name, PointerByReference value, Pointer terminator);
public static native void g_object_get(Pointer instance, String property_name, Pointer value, Pointer terminator);

View File

@ -35,17 +35,21 @@ import dorkbox.util.OS;
import dorkbox.util.jna.JnaHelper;
/**
* bindings for GTK+ 2. Bindings that are exclusively for GTK+ 3 are in that respective class
* Bindings for GTK+ 2. Bindings that are exclusively for GTK+ 3 are in that respective class
* <p>
* Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
*/
@SuppressWarnings({"Duplicates", "SameParameterValue", "DeprecatedIsStillUsed"})
@SuppressWarnings({"Duplicates", "SameParameterValue", "DeprecatedIsStillUsed", "WeakerAccess", "deprecation"})
public
class Gtk {
// 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
// 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
@SuppressWarnings({"unused", "PointlessBitwiseExpression"})
public static class State {
public static final int NORMAL = 0x0; // normal state.
public static final int ACTIVE = 0x1; // pressed-in or activated; e.g. buttons while the mouse button is held down.
@ -71,11 +75,6 @@ class Gtk {
public static final boolean isLoaded;
// 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
public static final int FALSE = 0;
public static final int TRUE = 1;
private static final boolean alreadyRunningGTK;
@ -102,6 +101,7 @@ class Gtk {
private static volatile boolean started = false;
@SuppressWarnings("FieldCanBeLocal")
private static Thread gtkUpdateThread = null;
public static final int MAJOR;
@ -139,13 +139,13 @@ class Gtk {
if (!_isLoaded && shouldUseGtk2) {
try {
NativeLibrary library = JnaHelper.register(gtk2LibName, Gtk.class);
NativeLibrary library = JnaHelper.register(gtk2LibName, Gtk2.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;
_alreadyRunningGTK = Gtk2.gtk_main_level() != 0;
_isLoaded = true;
major = library.getGlobalVariableAddress("gtk_major_version").getInt(0);
@ -167,7 +167,8 @@ class Gtk {
// start with version 3
if (!_isLoaded) {
try {
JnaHelper.register(gtk3LibName, Gtk.class);
// ALSO map Gtk2.java to GTK3 library. Cannot have Gtk3 extend Gtk2, it won't work.
JnaHelper.register(gtk3LibName, Gtk2.class);
gtk_status_icon_position_menu = Function.getFunction(gtk3LibName, "gtk_status_icon_position_menu");
// ALSO have to load the SPECIFIC Gtk+ 3 methods. We cannot subclass because JNA doesn't like it.
@ -177,7 +178,7 @@ class Gtk {
// 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;
_alreadyRunningGTK = Gtk2.gtk_main_level() != 0;
_isLoaded = true;
major = Gtk3.gtk_get_major_version();
@ -197,13 +198,13 @@ class Gtk {
// now version 2
if (!_isLoaded) {
try {
NativeLibrary library = JnaHelper.register(gtk2LibName, Gtk.class);
NativeLibrary library = JnaHelper.register(gtk2LibName, Gtk2.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;
_alreadyRunningGTK = Gtk2.gtk_main_level() != 0;
_isLoaded = true;
major = library.getGlobalVariableAddress("gtk_major_version").getInt(0);
@ -292,7 +293,7 @@ class Gtk {
// prep for the event loop.
// GThread.g_thread_init(null); would be needed for g_idle_add()
if (!gtk_init_check(0)) {
if (!Gtk2.gtk_init_check(0)) {
if (SystemTray.DEBUG) {
logger.error("Error starting GTK");
}
@ -306,7 +307,7 @@ class Gtk {
}
// blocks unit quit
gtk_main();
Gtk2.gtk_main();
// clean up threads
// gdk_threads_leave(); would be needed for g_idle_add()
@ -319,20 +320,6 @@ class Gtk {
}
}
/**
* This would NORMALLY have a 2nd argument that is a String[] -- however JNA direct-mapping DOES NOT support this. We are lucky
* enough that we just pass 'null' as the second argument, therefore, we don't have to define that parameter here.
*/
private static native
boolean gtk_init_check(int argc);
/**
* 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.
*/
private static native
void gtk_main();
/**
* Waits for the all posted events to GTK to finish loading
*/
@ -484,21 +471,10 @@ class Gtk {
}
// the correct way to do it. Add with a slightly higher value
gdk_threads_add_idle_full(100, callback, null, null);
Gtk2.gdk_threads_add_idle_full(100, callback, null, null);
}
}
/**
* Adds a function to be called whenever there are no higher priority events pending. If the function returns FALSE it is automatically
* removed from the list of event sources and will not be called again.
* <p>
* This variant of g_idle_add_full() calls function with the GDK lock held. It can be thought of a MT-safe version for GTK+ widgets
* for the following use case, where you have to worry about idle_callback() running in thread A and accessing self after it has
* been finalized in thread B.
*/
public static native
int gdk_threads_add_idle_full(int priority, FuncCallback function, Pointer data, Pointer notify);
public static
void shutdownGui() {
dispatchAndWait(new Runnable() {
@ -507,7 +483,7 @@ class Gtk {
void run() {
// If JavaFX/SWT is used, this is UNNECESSARY (and will break SWT/JavaFX shutdown)
if (!alreadyRunningGTK) {
gtk_main_quit();
Gtk2.gtk_main_quit();
}
started = false;
@ -558,13 +534,6 @@ class Gtk {
}
}
/**
* 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!
*/
private static native
void gtk_main_quit();
/**
* required to properly setup the dispatch flag when using native menus
*
@ -588,29 +557,29 @@ class Gtk {
}
}
/**
* aks for the current nesting level of the main loop. Useful to determine (at startup) if GTK is already running
*/
private static native
int gtk_main_level();
/**
* Creates a new GtkMenu
*/
public static native
Pointer gtk_menu_new();
public static
Pointer gtk_menu_new() {
return Gtk2.gtk_menu_new();
}
/**
* Sets or replaces the menu items submenu, or removes it when a NULL submenu is passed.
*/
public static native
Pointer gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu);
public static
void gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu) {
Gtk2.gtk_menu_item_set_submenu(menuEntry, menu);
}
/**
* Creates a new GtkSeparatorMenuItem.
*/
public static native
Pointer gtk_separator_menu_item_new();
public static
Pointer gtk_separator_menu_item_new() {
return Gtk2.gtk_separator_menu_item_new();
}
/**
* Creates a new GtkImage displaying the file filename . If the file isnt found or cant be loaded, the resulting GtkImage will
@ -618,17 +587,18 @@ class Gtk {
* <p>
* If the file contains an animation, the image will contain an animation.
*/
public static native
Pointer gtk_image_new_from_file(String iconPath);
public static
Pointer gtk_image_new_from_file(String iconPath) {
return Gtk2.gtk_image_new_from_file(iconPath);
}
/**
* Sets the active state of the menu items check box.
*/
public static native
void gtk_check_menu_item_set_active(Pointer check_menu_item, boolean isChecked);
public static
void gtk_check_menu_item_set_active(Pointer check_menu_item, boolean isChecked) {
Gtk2.gtk_check_menu_item_set_active(check_menu_item, isChecked);
}
/**
* Creates a new GtkImageMenuItem containing a label. The label will be created using gtk_label_new_with_mnemonic(), so underscores
@ -639,12 +609,15 @@ class Gtk {
* gtk_image_menu_item_new_with_mnemonic has been deprecated since version 3.10 and should not be used in newly-written code.
* NOTE: Use gtk_menu_item_new_with_mnemonic() instead.
*/
@Deprecated
public static native
Pointer gtk_image_menu_item_new_with_mnemonic(String label);
public static
Pointer gtk_image_menu_item_new_with_mnemonic(String label) {
return Gtk2.gtk_image_menu_item_new_with_mnemonic(label);
}
public static native
Pointer gtk_check_menu_item_new_with_mnemonic(String label);
public static
Pointer gtk_check_menu_item_new_with_mnemonic(String label) {
return Gtk2.gtk_check_menu_item_new_with_mnemonic(label);
}
/**
* Sets the image of image_menu_item to the given widget. Note that it depends on the show-menu-images setting whether the image
@ -652,9 +625,10 @@ class Gtk {
* <p>
* gtk_image_menu_item_set_image has been deprecated since version 3.10 and should not be used in newly-written code.
*/
@Deprecated
public static native
void gtk_image_menu_item_set_image(Pointer image_menu_item, Pointer image);
public static
void gtk_image_menu_item_set_image(Pointer image_menu_item, Pointer image) {
Gtk2.gtk_image_menu_item_set_image(image_menu_item, image);
}
/**
* If TRUE, the menu item will ignore the gtk-menu-images setting and always show the image, if available.
@ -662,9 +636,10 @@ class Gtk {
* <p>
* gtk_image_menu_item_set_always_show_image has been deprecated since version 3.10 and should not be used in newly-written code.
*/
@Deprecated
public static native
void gtk_image_menu_item_set_always_show_image(Pointer menu_item, boolean forceShow);
public static
void gtk_image_menu_item_set_always_show_image(Pointer menu_item, boolean forceShow) {
Gtk2.gtk_image_menu_item_set_always_show_image(menu_item, forceShow);
}
/**
* Creates an empty status icon object.
@ -672,17 +647,20 @@ class Gtk {
* gtk_status_icon_new has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
Pointer gtk_status_icon_new();
public static
Pointer gtk_status_icon_new() {
return Gtk2.gtk_status_icon_new();
}
/**
* Obtains the root window (parent all other windows are inside) for the default display and screen.
*
* @return the default root window
*/
public static native
Pointer gdk_get_default_root_window();
public static
Pointer gdk_get_default_root_window() {
return Gtk2.gdk_get_default_root_window();
}
/**
* Gets the default screen for the default display. (See gdk_display_get_default()).
@ -691,8 +669,10 @@ class Gtk {
*
* @since 2.2
*/
public static native
Pointer gdk_screen_get_default();
public static
Pointer gdk_screen_get_default() {
return Gtk2.gdk_screen_get_default();
}
/**
* Gets the resolution for font handling on the screen; see gdk_screen_set_resolution() for full details.
@ -706,8 +686,10 @@ class Gtk {
*
* @since Since: 2.10
*/
public static native
double gdk_screen_get_resolution(Pointer screen);
public static
double gdk_screen_get_resolution(Pointer screen) {
return Gtk2.gdk_screen_get_resolution(screen);
}
/**
* Makes status_icon display the file filename . See gtk_status_icon_new_from_file() for details.
@ -715,9 +697,10 @@ class Gtk {
* gtk_status_icon_set_from_file has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_from_file(Pointer widget, String label);
public static
void gtk_status_icon_set_from_file(Pointer widget, String label) {
Gtk2.gtk_status_icon_set_from_file(widget, label);
}
/**
* Shows or hides a status icon.
@ -725,9 +708,10 @@ class Gtk {
* gtk_status_icon_set_visible has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_visible(Pointer widget, boolean visible);
public static
void gtk_status_icon_set_visible(Pointer widget, boolean visible) {
Gtk2.gtk_status_icon_set_visible(widget, visible);
}
/**
@ -739,9 +723,10 @@ class Gtk {
* gtk_status_icon_set_tooltip_text has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_tooltip_text(Pointer widget, String tooltipText);
public static
void gtk_status_icon_set_tooltip_text(Pointer widget, String tooltipText) {
Gtk2.gtk_status_icon_set_tooltip_text(widget, tooltipText);
}
/**
* Sets the title of this tray icon. This should be a short, human-readable, localized string describing the tray icon. It may be used
@ -750,9 +735,10 @@ class Gtk {
* gtk_status_icon_set_title has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_title(Pointer widget, String titleText);
public static
void gtk_status_icon_set_title(Pointer widget, String titleText) {
Gtk2.gtk_status_icon_set_title(widget, titleText);
}
/**
* Sets the name of this tray icon. This should be a string identifying this icon. It is may be used for sorting the icons in the
@ -761,9 +747,10 @@ class Gtk {
* gtk_status_icon_set_name has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_name(Pointer widget, String name);
public static
void gtk_status_icon_set_name(Pointer widget, String name) {
Gtk2.gtk_status_icon_set_name(widget, name);
}
/**
* Displays a menu and makes it available for selection.
@ -771,34 +758,43 @@ class Gtk {
* gtk_menu_popup has been deprecated since version 3.22 and should not be used in newly-written code.
* NOTE: Please use gtk_menu_popup_at_widget(), gtk_menu_popup_at_pointer(). or gtk_menu_popup_at_rect() instead
*/
@Deprecated
public static native
void gtk_menu_popup(Pointer menu, Pointer widget, Pointer bla, Function func, Pointer data, int button, int time);
public static
void gtk_menu_popup(Pointer menu, Pointer widget, Pointer bla, Function func, Pointer data, int button, int time) {
Gtk2.gtk_menu_popup(menu, widget, bla, func, data, button, time);
}
/**
* Sets text on the menu_item label
*/
public static native
void gtk_menu_item_set_label(Pointer menu_item, String label);
public static
void gtk_menu_item_set_label(Pointer menu_item, String label) {
Gtk2.gtk_menu_item_set_label(menu_item, label);
}
/**
* Adds a new GtkMenuItem to the end of the menu shell's item list.
*/
public static native
void gtk_menu_shell_append(Pointer menu_shell, Pointer child);
public static
void gtk_menu_shell_append(Pointer menu_shell, Pointer child) {
Gtk2.gtk_menu_shell_append(menu_shell, child);
}
/**
* Sets the sensitivity of a widget. A widget is sensitive if the user can interact with it. Insensitive widgets are grayed out
* and the user cant interact with them. Insensitive widgets are known as inactive, disabled, or ghosted in some other toolkits.
*/
public static native
void gtk_widget_set_sensitive(Pointer widget, boolean sensitive);
public static
void gtk_widget_set_sensitive(Pointer widget, boolean sensitive) {
Gtk2.gtk_widget_set_sensitive(widget, sensitive);
}
/**
* Recursively shows a widget, and any child widgets (if the widget is a container)
*/
public static native
void gtk_widget_show_all(Pointer widget);
public static
void gtk_widget_show_all(Pointer widget) {
Gtk2.gtk_widget_show_all(widget);
}
/**
* Removes widget from container . widget must be inside container . Note that container will own a reference to widget , and that
@ -808,8 +804,10 @@ class Gtk {
* If you dont want to use widget again its usually more efficient to simply destroy it directly using gtk_widget_destroy()
* since this will remove it from the container and help break any circular reference count cycles.
*/
public static native
void gtk_container_remove(Pointer parentWidget, Pointer widget);
public static
void gtk_container_remove(Pointer parentWidget, Pointer widget) {
Gtk2.gtk_container_remove(parentWidget, widget);
}
/**
* Destroys a widget.
@ -829,29 +827,37 @@ class Gtk {
* <p>
* NOTE You should typically call this function on top level widgets, and rarely on child widgets.
*/
public static native
void gtk_widget_destroy(Pointer widget);
public static
void gtk_widget_destroy(Pointer widget) {
Gtk2.gtk_widget_destroy(widget);
}
/**
* Gets the GtkSettings object for screen , creating it if necessary.
*
* @since 2.2
*/
public static native
Pointer gtk_settings_get_for_screen(Pointer screen);
public static
Pointer gtk_settings_get_for_screen(Pointer screen) {
return Gtk2.gtk_settings_get_for_screen(screen);
}
/**
* Simply an accessor function that returns @widget->style.
*/
public static native
GtkStyle.ByReference gtk_widget_get_style(Pointer widget);
public static
GtkStyle.ByReference gtk_widget_get_style(Pointer widget) {
return Gtk2.gtk_widget_get_style(widget);
}
/**
* Finds all matching RC styles for a given widget, composites them together, and then creates a GtkStyle representing the composite
* appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
*/
public static native
Pointer gtk_rc_get_style(Pointer widget);
public static
Pointer gtk_rc_get_style(Pointer widget) {
return Gtk2.gtk_rc_get_style(widget);
}
/**
* Looks up color_name in the styles logical color mappings, filling in color and returning TRUE if found, otherwise returning
@ -859,8 +865,10 @@ class Gtk {
*
* @since 2.10
*/
public static native
boolean gtk_style_lookup_color(Pointer widgetStyle, String color_name, Pointer color);
public static
boolean gtk_style_lookup_color(Pointer widgetStyle, String color_name, Pointer color) {
return Gtk2.gtk_style_lookup_color(widgetStyle, color_name, color);
}
/**
* Adds widget to container . Typically used for simple containers such as GtkWindow, GtkFrame, or GtkButton; for more complicated
@ -868,7 +876,60 @@ class Gtk {
* consider functions such as gtk_box_pack_start() and gtk_table_attach() as an alternative to gtk_container_add() in those cases.
* A widget may be added to only one container at a time; you can't place the same widget inside two different containers.
*/
public static native
void gtk_container_add(Pointer offscreen, Pointer widget);
public static
void gtk_container_add(Pointer offscreen, Pointer widget) {
Gtk2.gtk_container_add(offscreen, widget);
}
/**
* Get's the child from a GTK Bin object
*/
public static
Pointer gtk_bin_get_child(Pointer bin) {
return Gtk2.gtk_bin_get_child(bin);
}
/**
* Gets the PangoLayout used to display the label. The layout is useful to e.g. convert text positions to pixel positions, in
* combination with gtk_label_get_layout_offsets(). The returned layout is owned by the label so need not be freed by the caller.
*
* The label is free to recreate its layout at any time, so it should be considered read-only.
*/
public static
Pointer gtk_label_get_layout(Pointer label) {
return Gtk2.gtk_label_get_layout(label);
}
/**
* Computes the logical and ink extents of layout in device units. This function just calls pango_layout_get_extents() followed
* by two pango_extents_to_pixels() calls, rounding ink_rect and logical_rect such that the rounded rectangles fully contain the
* unrounded one (that is, passes them as first argument to pango_extents_to_pixels()).
*
* @param layout a PangoLayout
* @param ink_rect rectangle used to store the extents of the layout as drawn or NULL to indicate that the result is not needed.
* @param logical_rect rectangle used to store the logical extents of the layout or NULL to indicate that the result is not needed.
*/
public static
void pango_layout_get_pixel_extents(Pointer layout, Pointer ink_rect, Pointer logical_rect) {
Gtk2.pango_layout_get_pixel_extents(layout, ink_rect, logical_rect);
}
/**
* Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen.
*
* @since 2.20
*/
public static
Pointer gtk_offscreen_window_new() {
return Gtk2.gtk_offscreen_window_new();
}
/**
* Retrieves the border width of the container. See gtk_container_set_border_width().
*/
public static
int gtk_container_get_border_width(Pointer container) {
return Gtk2.gtk_container_get_border_width(container);
}
}

View File

@ -0,0 +1,388 @@
/*
* Copyright 2017 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 com.sun.jna.Function;
import com.sun.jna.Pointer;
/**
* Bindings for GTK+ 2. Bindings that are exclusively for GTK+ 3 are in that respective class
*
* Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
*/
public
class Gtk2 {
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk
/**
* Adds a function to be called whenever there are no higher priority events pending. If the function returns FALSE it is automatically
* removed from the list of event sources and will not be called again.
* <p>
* This variant of g_idle_add_full() calls function with the GDK lock held. It can be thought of a MT-safe version for GTK+ widgets
* for the following use case, where you have to worry about idle_callback() running in thread A and accessing self after it has
* been finalized in thread B.
*/
static native
int gdk_threads_add_idle_full(int priority, FuncCallback function, Pointer data, Pointer notify);
/**
* This would NORMALLY have a 2nd argument that is a String[] -- however JNA direct-mapping DOES NOT support this. We are lucky
* enough that we just pass 'null' as the second argument, therefore, we don't have to define that parameter here.
*/
static native
boolean gtk_init_check(int argc);
/**
* 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.
*/
static native
void gtk_main();
/**
* aks for the current nesting level of the main loop. Useful to determine (at startup) if GTK is already running
*/
static native
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!
*/
static native
void gtk_main_quit();
/**
* Creates a new GtkMenu
*/
public static native
Pointer gtk_menu_new();
/**
* Sets or replaces the menu items submenu, or removes it when a NULL submenu is passed.
*/
public static native
void gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu);
/**
* Creates a new GtkSeparatorMenuItem.
*/
public static native
Pointer gtk_separator_menu_item_new();
/**
* Creates a new GtkImage displaying the file filename . If the file isnt found or cant be loaded, the resulting GtkImage will
* display a broken image icon. This function never returns NULL, it always returns a valid GtkImage widget.
* <p>
* If the file contains an animation, the image will contain an animation.
*/
public static native
Pointer gtk_image_new_from_file(String iconPath);
/**
* Sets the active state of the menu items check box.
*/
public static native
void gtk_check_menu_item_set_active(Pointer check_menu_item, boolean isChecked);
/**
* Creates a new GtkImageMenuItem containing a label. The label will be created using gtk_label_new_with_mnemonic(), so underscores
* in label indicate the mnemonic for the menu item.
* <p>
* uses '_' to define which key is the mnemonic
* <p>
* gtk_image_menu_item_new_with_mnemonic has been deprecated since version 3.10 and should not be used in newly-written code.
* NOTE: Use gtk_menu_item_new_with_mnemonic() instead.
*/
@Deprecated
public static native
Pointer gtk_image_menu_item_new_with_mnemonic(String label);
public static native
Pointer gtk_check_menu_item_new_with_mnemonic(String label);
/**
* Sets the image of image_menu_item to the given widget. Note that it depends on the show-menu-images setting whether the image
* will be displayed or not.
* <p>
* gtk_image_menu_item_set_image has been deprecated since version 3.10 and should not be used in newly-written code.
*/
@Deprecated
public static native
void gtk_image_menu_item_set_image(Pointer image_menu_item, Pointer image);
/**
* If TRUE, the menu item will ignore the gtk-menu-images setting and always show the image, if available.
* Use this property if the menuitem would be useless or hard to use without the image
* <p>
* gtk_image_menu_item_set_always_show_image has been deprecated since version 3.10 and should not be used in newly-written code.
*/
@Deprecated
public static native
void gtk_image_menu_item_set_always_show_image(Pointer menu_item, boolean forceShow);
/**
* Creates an empty status icon object.
* <p>
* gtk_status_icon_new has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
Pointer gtk_status_icon_new();
/**
* Obtains the root window (parent all other windows are inside) for the default display and screen.
*
* @return the default root window
*/
public static native
Pointer gdk_get_default_root_window();
/**
* Gets the default screen for the default display. (See gdk_display_get_default()).
*
* @return a GdkScreen, or NULL if there is no default display.
*
* @since 2.2
*/
public static native
Pointer gdk_screen_get_default();
/**
* Gets the resolution for font handling on the screen; see gdk_screen_set_resolution() for full details.
*
* IE:
*
* The resolution for font handling on the screen. This is a scale factor between points specified in a PangoFontDescription and
* cairo units. The default value is 96, meaning that a 10 point font will be 13 units high. (10 * 96. / 72. = 13.3).
*
* @return the current resolution, or -1 if no resolution has been set.
*
* @since Since: 2.10
*/
public static native
double gdk_screen_get_resolution(Pointer screen);
/**
* Makes status_icon display the file filename . See gtk_status_icon_new_from_file() for details.
* <p>
* gtk_status_icon_set_from_file has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_from_file(Pointer widget, String label);
/**
* Shows or hides a status icon.
* <p>
* gtk_status_icon_set_visible has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_visible(Pointer widget, boolean visible);
/**
* Sets text as the contents of the tooltip.
* This function will take care of setting has-tooltip to TRUE and of the default handler for the query-tooltip signal.
*
* app indicators don't support this
*
* gtk_status_icon_set_tooltip_text has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_tooltip_text(Pointer widget, String tooltipText);
/**
* Sets the title of this tray icon. This should be a short, human-readable, localized string describing the tray icon. It may be used
* by tools like screen readers to render the tray icon.
* <p>
* gtk_status_icon_set_title has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_title(Pointer widget, String titleText);
/**
* Sets the name of this tray icon. This should be a string identifying this icon. It is may be used for sorting the icons in the
* tray and will not be shown to the user.
* <p>
* gtk_status_icon_set_name has been deprecated since version 3.14 and should not be used in newly-written code.
* Use notifications
*/
@Deprecated
public static native
void gtk_status_icon_set_name(Pointer widget, String name);
/**
* Displays a menu and makes it available for selection.
* <p>
* gtk_menu_popup has been deprecated since version 3.22 and should not be used in newly-written code.
* NOTE: Please use gtk_menu_popup_at_widget(), gtk_menu_popup_at_pointer(). or gtk_menu_popup_at_rect() instead
*/
@Deprecated
public static native
void gtk_menu_popup(Pointer menu, Pointer widget, Pointer bla, Function func, Pointer data, int button, int time);
/**
* Sets text on the menu_item label
*/
public static native
void gtk_menu_item_set_label(Pointer menu_item, String label);
/**
* Adds a new GtkMenuItem to the end of the menu shell's item list.
*/
public static native
void gtk_menu_shell_append(Pointer menu_shell, Pointer child);
/**
* Sets the sensitivity of a widget. A widget is sensitive if the user can interact with it. Insensitive widgets are grayed out
* and the user cant interact with them. Insensitive widgets are known as inactive, disabled, or ghosted in some other toolkits.
*/
public static native
void gtk_widget_set_sensitive(Pointer widget, boolean sensitive);
/**
* Recursively shows a widget, and any child widgets (if the widget is a container)
*/
public static native
void gtk_widget_show_all(Pointer widget);
/**
* Removes widget from container . widget must be inside container . Note that container will own a reference to widget , and that
* this may be the last reference held; so removing a widget from its container can destroy that widget.
* <p>
* If you want to use widget again, you need to add a reference to it before removing it from a container, using g_object_ref().
* If you dont want to use widget again its usually more efficient to simply destroy it directly using gtk_widget_destroy()
* since this will remove it from the container and help break any circular reference count cycles.
*/
public static native
void gtk_container_remove(Pointer parentWidget, Pointer widget);
/**
* Destroys a widget.
* When a widget is destroyed all references it holds on other objects will be released:
* - if the widget is inside a container, it will be removed from its parent
* - if the widget is a container, all its children will be destroyed, recursively
* - if the widget is a top level, it will be removed from the list of top level widgets that GTK+ maintains internally
* <p>
* It's expected that all references held on the widget will also be released; you should connect to the destroy signal if you
* hold a reference to widget and you wish to remove it when this function is called. It is not necessary to do so if you are
* implementing a GtkContainer, as you'll be able to use the GtkContainerClass.remove() virtual function for that.
* <p>
* It's important to notice that gtk_widget_destroy() will only cause the widget to be finalized if no additional references,
* acquired using g_object_ref(), are held on it. In case additional references are in place, the widget will be in an "inert" state
* after calling this function; widget will still point to valid memory, allowing you to release the references you hold, but you
* may not query the widget's own state.
* <p>
* NOTE You should typically call this function on top level widgets, and rarely on child widgets.
*/
public static native
void gtk_widget_destroy(Pointer widget);
/**
* Gets the GtkSettings object for screen , creating it if necessary.
*
* @since 2.2
*/
public static native
Pointer gtk_settings_get_for_screen(Pointer screen);
/**
* Simply an accessor function that returns @widget->style.
*/
public static native
GtkStyle.ByReference gtk_widget_get_style(Pointer widget);
/**
* Finds all matching RC styles for a given widget, composites them together, and then creates a GtkStyle representing the composite
* appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
*/
public static native
Pointer gtk_rc_get_style(Pointer widget);
/**
* Looks up color_name in the styles logical color mappings, filling in color and returning TRUE if found, otherwise returning
* FALSE. Do not cache the found mapping, because it depends on the GtkStyle and might change when a theme switch occurs.
*
* @since 2.10
*/
public static native
boolean gtk_style_lookup_color(Pointer widgetStyle, String color_name, Pointer color);
/**
* Adds widget to container . Typically used for simple containers such as GtkWindow, GtkFrame, or GtkButton; for more complicated
* layout containers such as GtkBox or GtkTable, this function will pick default packing parameters that may not be correct. So
* consider functions such as gtk_box_pack_start() and gtk_table_attach() as an alternative to gtk_container_add() in those cases.
* A widget may be added to only one container at a time; you can't place the same widget inside two different containers.
*/
public static native
void gtk_container_add(Pointer offscreen, Pointer widget);
/**
* Get's the child from a GTK Bin object
*/
public static native
Pointer gtk_bin_get_child(Pointer bin);
/**
* Gets the PangoLayout used to display the label. The layout is useful to e.g. convert text positions to pixel positions, in
* combination with gtk_label_get_layout_offsets(). The returned layout is owned by the label so need not be freed by the caller.
*
* The label is free to recreate its layout at any time, so it should be considered read-only.
*/
public static native
Pointer gtk_label_get_layout(Pointer label);
/**
* Computes the logical and ink extents of layout in device units. This function just calls pango_layout_get_extents() followed
* by two pango_extents_to_pixels() calls, rounding ink_rect and logical_rect such that the rounded rectangles fully contain the
* unrounded one (that is, passes them as first argument to pango_extents_to_pixels()).
*
*
* @param layout a PangoLayout
* @param ink_rect rectangle used to store the extents of the layout as drawn or NULL to indicate that the result is not needed.
* @param logical_rect rectangle used to store the logical extents of the layout or NULL to indicate that the result is not needed.
*
*/
public static native
void pango_layout_get_pixel_extents (Pointer layout, Pointer ink_rect, Pointer logical_rect);
/**
* Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen.
*
* @since 2.20
*/
public static native
Pointer gtk_offscreen_window_new ();
/**
* Retrieves the border width of the container. See gtk_container_set_border_width().
*/
public static native
int gtk_container_get_border_width(Pointer container);
}

View File

@ -89,6 +89,49 @@ class Gtk3 {
*/
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().
*
* 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);
/**
* Restores context state to a previous stage. See gtk_style_context_save().
*
* @since 3.0
*/
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
* use of this new class for styling.
*
* @since 3.0
*/
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);
/**
* Gets the border for a given state as a GtkBorder.
*
* 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);
/**
* 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).

View File

@ -0,0 +1,20 @@
package dorkbox.systemTray.jna.linux;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
public
class GtkBorder extends Structure {
public short left;
public short right;
public short top;
public short bottom;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("left", "right", "top", "bottom");
}
}

View File

@ -1,22 +1,32 @@
package dorkbox.systemTray.jna.linux;
import static dorkbox.systemTray.util.CssParser.CssNode;
import static dorkbox.systemTray.util.CssParser.getAttributeFromSections;
import static dorkbox.systemTray.util.CssParser.getSections;
import static dorkbox.systemTray.util.CssParser.injectAdditionalCss;
import static dorkbox.systemTray.util.CssParser.removeComments;
import java.awt.Color;
import java.awt.Insets;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.Tray;
import dorkbox.systemTray.util.CssParser;
import dorkbox.systemTray.util.CssParser.Css;
import dorkbox.systemTray.util.CssParser.CssNode;
import dorkbox.systemTray.util.CssParser.Entry;
import dorkbox.util.FileUtil;
import dorkbox.util.MathUtil;
import dorkbox.util.OS;
import dorkbox.util.OSUtil;
/**
* Class to contain all of the methods needed to get the text color from the AppIndicator/GtkStatusIcon menu entry. This is primarily
@ -28,7 +38,7 @@ import dorkbox.util.FileUtil;
*
* Also note: not all themes have CSS or Theme files!!!
*/
@SuppressWarnings("deprecation")
@SuppressWarnings({"deprecation", "WeakerAccess"})
public
class GtkTheme {
private static final boolean DEBUG = false;
@ -36,173 +46,350 @@ class GtkTheme {
private static final boolean DEBUG_VERBOSE = false;
// CSS nodes that we care about, in oder of preference from left to right.
// GtkPopover is a bubble-like context window, primarily meant to provide context-dependent information or options.
private static final
String[] cssNodes = new String[] {"GtkPopover", "unity-panel", ".unity-panel", "gnome-panel-menu-bar", ".gnome-panel-menu-bar",
"PanelMenuBar", ".menuitem", ".entry", "*"};
String[] cssNodes = new String[] {".menuitem", ".entry", "*"};
/**
* Gets the text height of menu items (in the system tray menu), as specified by CSS.
* NOTE: not all themes have CSS
*/
public static
int getTextHeight() {
String css = getCss();
if (css != null) {
System.err.println(css);
// collect a list of all of the sections that have what we are interested in.
List<CssNode> sections = getSections(css, cssNodes, null);
String size = getAttributeFromSections(sections, cssNodes, "MenuItem-indicator-size", false);
// CheckButton-indicator-size
int i = stripNonDigits(size);
if (i != 0) {
return i;
}
}
Rectangle getPixelTextHeight(String text) {
// have to use pango to get the size of text (for the checkmark size)
Pointer offscreen = Gtk.gtk_offscreen_window_new();
// sane default
return 14;
// we use the size of "X" as the checkmark
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic(text);
Gtk.gtk_container_add(offscreen, item);
// get the text widget (GtkAccelLabel) from inside the GtkMenuItem
Pointer textLabel = Gtk.gtk_bin_get_child(item);
Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel);
// ink pixel size is how much exact space it takes on the screen
PangoRectangle ink = new PangoRectangle();
// logical pixel size (ascent + descent)
PangoRectangle logical = new PangoRectangle();
Gtk.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), logical.getPointer());
ink.read();
// logical.read();
Rectangle size = new Rectangle(ink.width, ink.height);
Gtk.gtk_widget_destroy(item);
Gtk.gtk_widget_destroy(offscreen);
return size;
}
/**
* Gets the text padding of menu items (in the system tray menu), as specified by CSS. This is the padding value for all sides!
* NOTE: not all themes have CSS
*/
@SuppressWarnings("Duplicates")
public static
int getTextPadding() {
/*
Maybe he best way is to get the element size, then subtract the text size.
Insets getTextPadding(String text) {
// have to use pango to get the size of text (for the checkmark size)
Pointer offscreen = Gtk.gtk_offscreen_window_new();
The margin properties set the size of the white space outside the border.
// we use the size of "X" as the checkmark
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic(text);
we care about top and bottom padding
Gtk.gtk_container_add(offscreen, item);
padding:10px 5px 15px 20px;
top padding is 10px
right padding is 5px
bottom padding is 15px
left padding is 20px
// get the text widget (GtkAccelLabel) from inside the GtkMenuItem
Pointer textLabel = Gtk.gtk_bin_get_child(item);
padding:10px 5px 15px;
top padding is 10px
right and left padding are 5px
bottom padding is 15px
int top = 0;
int bottom = 0;
int right = 0;
int left = 0;
padding:10px 5px;
top and bottom padding are 10px
right and left padding are 5px
IntByReference pointer = new IntByReference();
Gobject.g_object_get(textLabel, "ypad", pointer.getPointer(), null);
int value = pointer.getValue();
padding:10px;
all four paddings are 10px
top += value;
bottom += value;
Gobject.g_object_get(textLabel, "xpad", pointer.getPointer(), null);
value = pointer.getValue();
left += value;
right += value;
GtkColorButton.button {
padding: 2px;
value = Gtk.gtk_container_get_border_width(item);
top += value;
bottom += value;
left += value;
right += value;
if (Gtk.isGtk3) {
Pointer context = Gtk3.gtk_widget_get_style_context(item);
GtkBorder tmp = new GtkBorder();
Gtk3.gtk_style_context_save(context);
Gtk3.gtk_style_context_add_class(context, "frame");
Gtk3.gtk_style_context_get_padding(context, Gtk.State.NORMAL, tmp.getPointer());
tmp.read();
top += tmp.top;
bottom += tmp.bottom;
left += tmp.left;
right += tmp.right;
Gtk3.gtk_style_context_get_border(context, Gtk.State.NORMAL, tmp.getPointer());
tmp.read();
top += tmp.top;
bottom += tmp.bottom;
left += tmp.left;
right += tmp.right;
Gtk3.gtk_style_context_restore (context);
}
GtkPopover {
margin: 10px;
padding: 2px;
border-radius: 3px;
border-color: shade(@menu_bg_color, 0.8);
border-width: 1px;
border-style: solid;
background-clip: border-box;
background-image: none;
background-color: @menu_bg_color;
color: @menu_fg_color;
box-shadow: 0 2px 3px alpha(black, 0.5);
}
.menubar.menuitem,
.menubar .menuitem {
padding: 3px 8px;
border-width: 1px;
border-style: solid;
border-color: transparent;
background-color: transparent;
background-image: none;
color: @menubar_fg_color;
}
GtkStyle.ByReference style = Gtk.gtk_widget_get_style(item);
top += style.ythickness;
bottom += style.ythickness;
left += style.xthickness;
right += style.xthickness;
.entry {
padding: 3px;
border-width: 1px;
border-style: solid;
border-top-color: shade(@theme_bg_color, 0.6);
border-right-color: shade(@theme_bg_color, 0.7);
border-left-color: shade(@theme_bg_color, 0.7);
border-bottom-color: shade(@theme_bg_color, 0.72);
border-radius: 3px;
background-color: @theme_base_color;
background-image: linear-gradient(to bottom,
shade(@theme_base_color, 0.99),
@theme_base_color
);
Gtk.gtk_widget_destroy(item);
Gtk.gtk_widget_destroy(offscreen);
color: @theme_text_color;
}
.button {
-GtkWidget-focus-padding: 1;
-GtkWidget-focus-line-width: 0;
padding: 2px 4px;
border-width: 1px;
border-radius: 3px;
border-style: solid;
border-top-color: shade(@theme_bg_color, 0.8);
border-right-color: shade(@theme_bg_color, 0.72);
border-left-color: shade(@theme_bg_color, 0.72);
border-bottom-color: shade(@theme_bg_color, 0.7);
background-image: linear-gradient(to bottom,
shade(shade(@theme_bg_color, 1.02), 1.05),
shade(shade(@theme_bg_color, 1.02), 0.97)
);
color: @theme_fg_color;
}
*/
String css = getCss();
if (css != null) {
// collect a list of all of the sections that have what we are interested in.
List<CssNode> sections = getSections(css, cssNodes, null);
String padding = getAttributeFromSections(sections, cssNodes, "padding", true);
String border = getAttributeFromSections(sections, cssNodes, "border-width", true);
return stripNonDigits(padding) + stripNonDigits(border);
}
return 0;
return new Insets(top, left, bottom, right);
}
/**
* Gets the system tray indicator size as specified by CSS.
* Gets the system tray indicator size.
* - AppIndicator: will properly scale the image if it's not the correct size
* - GtkStatusIndicator: ??
*/
public static
int getIndicatorSize() {
String css = getCss();
if (css != null) {
String[] cssNodes = new String[] {"GdMainIconView", ".content-view"};
int getIndicatorSize(final Class<? extends Tray> trayType) {
if (OSUtil.DesktopEnv.isKDE()) {
// KDE
/*
* Tray icons with fixed size
* If the tray icons are not scaled with the rest of the desktop, the size can be set editing the default value for iconSize
* in /usr/share/plasma/plasmoids/org.kde.plasma.private.systemtray/contents/config/main.xml
* (e.g. the value 2 may be fine):
*
* /usr/share/plasma/plasmoids/org.kde.plasma.private.systemtray/contents/config/main.xml
* <entry name="iconSize" type="Int">
* <label>Default icon size for the systray icons, it's an enum which values mean,
* Small, SmallMedium, Medium, Large, Huge, Enormous respectively. On low DPI systems they correspond to
* 16, 22, 32, 48, 64, 128 pixels. On high DPI systems those values would be scaled up, depending on the DPI.
* </label>
* <default>2</default>
* </entry>
*/
}
else {
final AtomicInteger screenScale = new AtomicInteger();
final AtomicReference<Double> screenDPI = new AtomicReference<Double>();
// collect a list of all of the sections that have what we are interested in.
List<CssNode> sections = getSections(css, cssNodes, null);
String indicatorSize = getAttributeFromSections(sections, cssNodes, "-GdMainIconView-icon-size", true);
Gtk.dispatchAndWait(new Runnable() {
@Override
public
void run() {
// screen DPI
Pointer screen = Gtk.gdk_screen_get_default();
if (screen != null) {
// this call makes NO SENSE, but reading the documentation shows it is the CORRECT call.
screenDPI.set(Gtk.gdk_screen_get_resolution(screen));
}
int i = stripNonDigits(indicatorSize);
if (i != 0) {
return i;
if (Gtk.isGtk3) {
Pointer window = Gtk.gdk_get_default_root_window();
if (window != null) {
screenScale.set(Gtk3.gdk_window_get_scale_factor(window));
}
}
}
});
// TODO: what to do with screen DPI? 72 is default? 96 is default (yes, i think)?
OSUtil.DesktopEnv.Env env = OSUtil.DesktopEnv.get();
if (OSUtil.Linux.isUbuntu() && env == OSUtil.DesktopEnv.Env.Unity) {
// if we measure on ubuntu unity using a screen shot (using swing, so....) , the max size was 24, HOWEVER this goes from
// the top->bottom of the indicator bar -- and since it was swing, it uses a different rendering method and it (honestly)
// looks weird, because there is no padding on the icon. The official AppIndicator size is hardcoded...
// http://bazaar.launchpad.net/~indicator-applet-developers/libindicator/trunk.16.10/view/head:/libindicator/indicator-image-helper.c
return 22;
}
else {
// Swing or AWT. While not "normal", this is absolutely a possible combination.
// NOTE: On linux, if using Swing -- the icon will look HORRID! The background is not rendered correctly!
// xfce is easy, because it's not a GTK setting for the size (xfce notification area maximum icon size)
if (env == OSUtil.DesktopEnv.Env.XFCE) {
String properties = OSUtil.DesktopEnv.queryXfce("xfce4-panel", null);
List<String> propertiesAsList = Arrays.asList(properties.split(OS.LINE_SEPARATOR));
for (String prop : propertiesAsList) {
if (prop.startsWith("/plugins/") && prop.endsWith("/size-max")) {
// this is the property we are looking for (we just don't know which panel it's on)
String size = OSUtil.DesktopEnv.queryXfce("xfce4-panel", prop);
try {
return Integer.parseInt(size);
} catch (Exception e) {
SystemTray.logger.error("Unable to get XFCE notification panel size for channel '{}', property '{}'",
"xfce4-panel", prop, e);
}
}
}
// default...
return 22;
}
// try to use GTK to get the tray icon size
final AtomicInteger traySize = new AtomicInteger();
Gtk.dispatchAndWait(new Runnable() {
@Override
public
void run() {
Pointer screen = Gtk.gdk_screen_get_default();
Pointer settings = null;
if (screen != null) {
settings = Gtk.gtk_settings_get_for_screen(screen);
}
if (settings != null) {
PointerByReference pointer = new PointerByReference();
// https://wiki.archlinux.org/index.php/GTK%2B
// To use smaller icons, use a line like this:
// gtk-icon-sizes = "panel-menu=16,16:panel=16,16:gtk-menu=16,16:gtk-large-toolbar=16,16:gtk-small-toolbar=16,16:gtk-button=16,16"
// this gets icon sizes. On XFCE, ubuntu, it returns "panel-menu-bar=24,24"
// NOTE: gtk-icon-sizes is deprecated and ignored since GTK+ 3.10.
// A list of icon sizes. The list is separated by colons, and item has the form: size-name = width , height
Gobject.g_object_get(settings, "gtk-icon-sizes", pointer.getPointer(), null);
Pointer value = pointer.getValue();
if (value != null) {
String iconSizes = value.getString(0);
String[] strings = new String[] {"panel-menu-bar=", "panel=", "gtk-large-toolbar=", "gtk-small-toolbar="};
for (String var : strings) {
int i = iconSizes.indexOf(var);
if (i >= 0) {
String size = iconSizes.substring(i + var.length(), iconSizes.indexOf(",", i));
if (MathUtil.isInteger(size)) {
traySize.set(Integer.parseInt(size));
return;
}
}
}
}
}
}
});
int i = traySize.get();
System.err.println("TRAY SIZE: " + traySize.get());
System.err.println("SCREEN DPI: " + screenDPI.get());
System.err.println("SCREEN SCALE: " + screenScale.get());
if (i != 0) {
// xfce gtk status icon is also size 22, measured manually
return i;
}
}
}
// https://wiki.gnome.org/HowDoI/HiDpi
// String css = getCss();
// System.err.println(css);
//
// -GtkCheckButton-indicator-size: 16;
// -GtkCheckMenuItem-indicator-size: 16;
//
// xdpyinfo | grep dots reported 96x96 dots
// else it's an app indicator
// if we can get the theme path, the theme path will (almost always) have size info as part of the path name!
// app_indicator_get_icon_theme_path ()
//
//const gchar * app_indicator_get_icon_theme_path (AppIndicator *self);
// Wrapper function for property "icon-theme-path".
//
// self :
//
// The AppIndicator object to use
// Returns :
//
// The current icon theme path.
// g_param_spec_int ("check-icon-size",
// "Check icon size",
// "Check icon size",
// -1, G_MAXINT, 40,
// G_PARAM_READWRITE));
// -GtkCheckButton-indicator-size: 15;
// -GtkCheckMenuItem-indicator-size: 14;
// GdMainIconView.content-view {
// -GdMainIconView-icon-size: 40;
//}
// no idea? check scaling? base it off of CSS values????
// what about KDE or elementary OS or unix? or arch?
// return 32;
// if we are an AppIndicator -- the size is hardcoded to 22
// http://bazaar.launchpad.net/~indicator-applet-developers/libindicator/trunk.16.10/view/head:/libindicator/indicator-image-helper.c
// sane default
return 40;
return 22;
}
/**
@ -218,12 +405,16 @@ all four paddings are 10px
// this information insanely difficult to get.
final AtomicReference<Color> color = new AtomicReference<Color>(null);
Gtk.dispatchAndWait(new Runnable() {
@SuppressWarnings("UnusedAssignment")
@Override
public
void run() {
// see if we can get the info via CSS properties (> GTK+ 3.2 uses an API, GTK2 gets it from disk)
Color c = getFromCss();
Color c;
// see if we can get the info via CSS properties (> GTK+ 3.2 uses an API, GTK2 gets it from disk).
// This is often the BEST way to get information, since GTK **DOES NOT** make it easy to get widget information BEFORE
// the widget is realized -- which in our case, we must do.
c = getFromCss();
if (c != null) {
if (DEBUG) {
System.err.println("Got from CSS");
@ -232,9 +423,8 @@ all four paddings are 10px
return;
}
// we got here because it's not possible to get the info via raw-CSS
// try to get via the color scheme. A bit more accurate than parsing the raw theme file
// try to get via the color scheme.
c = getFromColorScheme();
if (c != null) {
if (DEBUG) {
@ -260,15 +450,13 @@ all four paddings are 10px
}
// the following methods all require an offscreen widget to get the style information from. This rarely is correct for
// some bizzare reason.
// the following methods all require an offscreen widget to get the style information from.
// create an off-screen widget (don't forget to destroy everything!)
Pointer menu = Gtk.gtk_menu_new();
Pointer offscreen = Gtk.gtk_offscreen_window_new();
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic("a");
Gtk.gtk_container_add(menu, item);
Gtk.gtk_container_add(offscreen, item);
Gtk.gtk_widget_show_all(item);
// Try to get via RC style... Sometimes this works (sometimes it does not...)
@ -276,29 +464,44 @@ all four paddings are 10px
Pointer style = Gtk.gtk_rc_get_style(item);
GdkColor gdkColor = new GdkColor();
boolean success;
boolean success = false;
success = Gtk.gtk_style_lookup_color(style, "menu_fg_color", gdkColor.getPointer());
if (!success) {
success = Gtk.gtk_style_lookup_color(style, "text_color", gdkColor.getPointer());
if (success) {
System.err.println("a");
}
}
if (!success) {
success = Gtk.gtk_style_lookup_color(style, "theme_text_color", gdkColor.getPointer());
if (success) {
System.err.println("a");
}
}
if (success) {
color.set(gdkColor.getColor());
Gtk.gtk_widget_destroy(item);
return;
c = gdkColor.getColor();
}
}
if (c != null) {
if (DEBUG) {
System.err.println("Got from gtk offscreen gtk_style_lookup_color");
}
color.set(c);
Gtk.gtk_widget_destroy(item);
return;
}
if (Gtk.isGtk3) {
Pointer context = Gtk3.gtk_widget_get_style_context(item);
int state = Gtk3.gtk_style_context_get_state(context);
GdkRGBAColor gdkColor = new GdkRGBAColor();
boolean success = Gtk3.gtk_style_context_lookup_color(context, "fg_color", gdkColor.getPointer());
boolean success = false;
success = Gtk3.gtk_style_context_lookup_color(context, "fg_color", gdkColor.getPointer());
if (!success) {
success = Gtk3.gtk_style_context_lookup_color(context, "text_color", gdkColor.getPointer());
}
@ -311,42 +514,38 @@ all four paddings are 10px
}
if (success) {
color.set(new Color((float) gdkColor.red, (float) gdkColor.green, (float) gdkColor.blue, (float) gdkColor.alpha));
}
else {
// fall back in case nothing else works
Gtk3.gtk_style_context_get_color(context, state, gdkColor.getPointer());
if (gdkColor.red == 0.0 && gdkColor.green == 0.0 && gdkColor.blue == 0.0 && gdkColor.alpha == 0.0) {
// have nothing here, check something else...
if (DEBUG) {
System.err.println("No valid output from gtk_style_context_get_color");
}
}
else {
// if we have something that is not all 0's
color.set(new Color((float) gdkColor.red,
(float) gdkColor.green,
(float) gdkColor.blue,
(float) gdkColor.alpha));
}
c = gdkColor.getColor();
}
}
// this also doesn't always work...
if (c != null) {
color.set(c);
if (DEBUG) {
System.err.println("Got from gtk3 offscreen gtk_widget_get_style_context");
}
Gtk.gtk_widget_destroy(item);
return;
}
// this doesn't always work...
GtkStyle.ByReference style = Gtk.gtk_widget_get_style(item);
color.set(style.text[Gtk.State.NORMAL].getColor());
if (DEBUG) {
System.err.println("Got from gtk gtk_widget_get_style");
}
Gtk.gtk_widget_destroy(item);
}
});
Color color1 = color.get();
if (color1 != null) {
Color c = color.get();
if (c != null) {
if (DEBUG) {
System.err.println("COLOR FOUND: " + color1);
System.err.println("COLOR FOUND: " + c);
}
return color1;
return c;
}
SystemTray.logger.error("Unable to determine the text color in use by your system. Please create an issue and include your " +
@ -363,91 +562,49 @@ all four paddings are 10px
* <p>
* > GTK+ 3.2 uses an API, GTK2 gets it from disk
*
* @return the color string, parsed from CSS/
* @return the color string, parsed from CSS,
*/
private static
Color getFromCss() {
String css = getCss();
Css css = getCss();
if (css != null) {
if (DEBUG_SHOW_CSS) {
System.err.println(css);
}
// collect a list of all of the sections that have what we are interested in.
List<CssNode> sections = getSections(css, cssNodes, null);
String colorString = getAttributeFromSections(sections, cssNodes, "color", true);
try {
// collect a list of all of the sections that have what we are interested in.
List<CssNode> sections = CssParser.getSections(css, cssNodes, null);
List<Entry> colorStrings = CssParser.getAttributeFromSections(sections, "color", true);
// hopefully we found it.
if (colorString != null) {
if (colorString.startsWith("@")) {
// it's a color definition
String colorSubString = getColorDefinition(css, colorString.substring(1));
String colorString = CssParser.selectMostRelevantAttribute(cssNodes, colorStrings);
return parseColor(colorSubString);
}
else {
return parseColor(colorString);
}
}
}
return null;
}
private static
String getColorDefinition(final String css, final String colorString) {
// have to setup the "define color" section
String colorDefine = "@define-color";
int start = css.indexOf(colorDefine);
int end = css.lastIndexOf(colorDefine);
end = css.lastIndexOf(";", end) + 1; // include the ;
String colorDefines = css.substring(start, end);
if (DEBUG_VERBOSE) {
System.err.println("+++++++++++++++++++++++");
System.err.println(colorDefines);
System.err.println("+++++++++++++++++++++++");
}
// since it's a color definition, it will start a very specific way.
String newColorString = colorDefine + " " + colorString;
int i = 0;
while (i != -1) {
i = colorDefines.indexOf(newColorString);
if (i >= 0) {
try {
int startIndex = i + newColorString.length();
int endIndex = colorDefines.indexOf(";", i);
String colorSubString = colorDefines.substring(startIndex, endIndex)
.trim();
if (colorSubString.startsWith("@")) {
// have to recursively get the defined color
newColorString = colorDefine + " " + colorSubString.substring(1);
i = 0;
continue;
if (colorString != null) {
if (colorString.startsWith("@")) {
// it's a color definition
String colorSubString = css.getColorDefinition(colorString.substring(1));
return parseColor(colorSubString);
}
else {
return parseColor(colorString);
}
return colorSubString;
} catch (Exception ignored) {
}
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* @return the CSS for the current theme or null. It is important that this is called AFTER GTK has been initialized.
*/
public static
String getCss() {
if (Gtk.isGtk3) {
final AtomicReference<String> css = new AtomicReference<String>(null);
Css getCss() {
String css;
if (Gtk.isLoaded && Gtk.isGtk3) {
final AtomicReference<String> css_ = new AtomicReference<String>(null);
Gtk.dispatchAndWait(new Runnable() {
@Override
@ -463,7 +620,7 @@ all four paddings are 10px
// NOTE: This can output warnings if the theme doesn't parse correctly by GTK, so we suppress them
Glib.GLogFunc orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
css.set(Gtk3.gtk_css_provider_to_string(value));
css_.set(Gtk3.gtk_css_provider_to_string(value));
Glib.g_log_set_default_handler(orig, null);
}
@ -476,7 +633,7 @@ all four paddings are 10px
// NOTE: This can output warnings if the theme doesn't parse correctly by GTK, so we suppress them
Glib.GLogFunc orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
css.set(Gtk3.gtk_css_provider_to_string(value));
css_.set(Gtk3.gtk_css_provider_to_string(value));
Glib.g_log_set_default_handler(orig, null);
}
@ -485,14 +642,17 @@ all four paddings are 10px
});
// will be either the string, or null.
return css.get();
css = css_.get();
}
else {
// GTK2 has to get the GTK3 theme text a different way (parsing it from disk). SOMETIMES, the app must be GTK2, even though
// the system is GTK3. This works around the API restriction if we are an APP in GTK2 mode. This is not done ALL the time,
// because this is not as accurate as using the GTK3 API.
return getGtk3ThemeCssViaFile();
// This can also be a requirement if GTK is not loaded
css = getGtk3ThemeCssViaFile();
}
return CssParser.parse(css);
}
/**
@ -502,13 +662,19 @@ all four paddings are 10px
*/
public static
Color getFromColorScheme() {
Pointer settings = Gtk.gtk_settings_get_default();
Pointer screen = Gtk.gdk_screen_get_default();
Pointer settings = null;
if (screen != null) {
settings = Gtk.gtk_settings_get_for_screen(screen);
}
if (settings != null) {
// see if we can get the info we want the EASY way (likely only when GTK+ 2 is used, but can be < GTK+ 3.2)...
// been deprecated since version 3.8
PointerByReference pointer = new PointerByReference();
Gobject.g_object_get(settings, "gtk-color-scheme", pointer, null);
Gobject.g_object_get(settings, "gtk-color-scheme", pointer.getPointer(), null);
// A palette of named colors for use in themes. The format of the string is
@ -577,7 +743,7 @@ all four paddings are 10px
String colorString = s.substring(startIndex, endIndex)
.trim();
if (DEBUG) {
if (DEBUG_VERBOSE) {
System.out.println("Color string: " + colorString);
}
return parseColor(colorString);
@ -608,7 +774,6 @@ all four paddings are 10px
}
File gtkFile = new File(themeDirectory, "gtk.css");
try {
StringBuilder stringBuilder = new StringBuilder((int) (gtkFile.length()));
FileUtil.read(gtkFile, stringBuilder);
@ -623,8 +788,11 @@ all four paddings are 10px
injectAdditionalCss(themeDirectory, stringBuilder);
return stringBuilder.toString();
} catch (IOException ignored) {
} catch (IOException e) {
// cant read the file or something else.
if (SystemTray.DEBUG) {
SystemTray.logger.error("Error getting RAW GTK3 theme file.", e);
}
}
return null;
@ -800,6 +968,10 @@ all four paddings are 10px
@SuppressWarnings("Duplicates")
private static
Color parseColor(String colorString) {
if (colorString == null) {
return null;
}
int red = 0;
int green = 0;
int blue = 0;
@ -950,26 +1122,38 @@ all four paddings are 10px
*/
public static
String getThemeName() {
String themeName = null;
final AtomicReference<String> themeName = new AtomicReference<String>(null);
Pointer settings = Gtk.gtk_settings_get_default();
if (settings != null) {
Gtk.dispatchAndWait(new Runnable() {
@Override
public
void run() {
Pointer screen = Gtk.gdk_screen_get_default();
Pointer settings = null;
if (screen != null) {
settings = Gtk.gtk_settings_get_for_screen(screen);
}
PointerByReference pointer = new PointerByReference();
Gobject.g_object_get(settings, "gtk-theme-name", pointer, null);
if (settings != null) {
PointerByReference pointer = new PointerByReference();
Gobject.g_object_get(settings, "gtk-theme-name", pointer.getPointer(), null);
Pointer value = pointer.getValue();
if (value != null) {
themeName = value.getString(0);
Pointer value = pointer.getValue();
if (value != null) {
themeName.set(value.getString(0));
}
}
if (DEBUG) {
System.err.println("Theme name: " + themeName);
}
}
}
});
if (DEBUG) {
System.err.println("Theme name: " + themeName);
}
// will be either the string, or null.
return themeName.get();
return themeName;
}
// have to strip anything that is not a number.

View File

@ -0,0 +1,24 @@
package dorkbox.systemTray.jna.linux;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
/**
* https://developer.gnome.org/pango/stable/pango-Glyph-Storage.html#PangoRectangle
*/
public
class PangoRectangle extends Structure {
public int x;
public int y;
public int width;
public int height;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("x", "y", "width", "height");
}
}