diff --git a/src/dorkbox/systemTray/jna/linux/GdkColor.java b/src/dorkbox/systemTray/jna/linux/GdkColor.java index 7dfd475..4343631 100644 --- a/src/dorkbox/systemTray/jna/linux/GdkColor.java +++ b/src/dorkbox/systemTray/jna/linux/GdkColor.java @@ -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()); } diff --git a/src/dorkbox/systemTray/jna/linux/GdkRGBAColor.java b/src/dorkbox/systemTray/jna/linux/GdkRGBAColor.java index 787769b..8ce452e 100644 --- a/src/dorkbox/systemTray/jna/linux/GdkRGBAColor.java +++ b/src/dorkbox/systemTray/jna/linux/GdkRGBAColor.java @@ -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 getFieldOrder() { diff --git a/src/dorkbox/systemTray/jna/linux/Gobject.java b/src/dorkbox/systemTray/jna/linux/Gobject.java index 36c3248..5d0ed43 100644 --- a/src/dorkbox/systemTray/jna/linux/Gobject.java +++ b/src/dorkbox/systemTray/jna/linux/Gobject.java @@ -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); diff --git a/src/dorkbox/systemTray/jna/linux/Gtk.java b/src/dorkbox/systemTray/jna/linux/Gtk.java index 579947d..745d725 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk.java @@ -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 *

* 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. - *

- * 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 item’s 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 isn’t found or can’t be loaded, the resulting GtkImage will @@ -618,17 +587,18 @@ class Gtk { *

* 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 item’s 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 { *

* 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 { *

* 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 can’t 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 don’t want to use widget again it’s 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 { *

* 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 style’s 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); + } } diff --git a/src/dorkbox/systemTray/jna/linux/Gtk2.java b/src/dorkbox/systemTray/jna/linux/Gtk2.java new file mode 100644 index 0000000..6607fad --- /dev/null +++ b/src/dorkbox/systemTray/jna/linux/Gtk2.java @@ -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. + *

+ * 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 item’s 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 isn’t found or can’t be loaded, the resulting GtkImage will + * display a “broken image” icon. This function never returns NULL, it always returns a valid GtkImage widget. + *

+ * 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 item’s 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. + *

+ * uses '_' to define which key is the mnemonic + *

+ * 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. + *

+ * 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 + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 can’t 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. + *

+ * 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 don’t want to use widget again it’s 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 + *

+ * 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. + *

+ * 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. + *

+ * 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 style’s 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); +} diff --git a/src/dorkbox/systemTray/jna/linux/Gtk3.java b/src/dorkbox/systemTray/jna/linux/Gtk3.java index 39fe5c8..48f8256 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk3.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk3.java @@ -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). diff --git a/src/dorkbox/systemTray/jna/linux/GtkBorder.java b/src/dorkbox/systemTray/jna/linux/GtkBorder.java new file mode 100644 index 0000000..d454729 --- /dev/null +++ b/src/dorkbox/systemTray/jna/linux/GtkBorder.java @@ -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 getFieldOrder() { + return Arrays.asList("left", "right", "top", "bottom"); + } +} diff --git a/src/dorkbox/systemTray/jna/linux/GtkTheme.java b/src/dorkbox/systemTray/jna/linux/GtkTheme.java index a999d0c..187bba0 100644 --- a/src/dorkbox/systemTray/jna/linux/GtkTheme.java +++ b/src/dorkbox/systemTray/jna/linux/GtkTheme.java @@ -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 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 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 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 + * + * + * 2 + * + */ + } + else { + final AtomicInteger screenScale = new AtomicInteger(); + final AtomicReference screenDPI = new AtomicReference(); - // collect a list of all of the sections that have what we are interested in. - List 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 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 = new AtomicReference(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 *

* > 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 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 sections = CssParser.getSections(css, cssNodes, null); + List 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 css = new AtomicReference(null); + Css getCss() { + String css; + if (Gtk.isLoaded && Gtk.isGtk3) { + final AtomicReference css_ = new AtomicReference(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 themeName = new AtomicReference(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. diff --git a/src/dorkbox/systemTray/jna/linux/PangoRectangle.java b/src/dorkbox/systemTray/jna/linux/PangoRectangle.java new file mode 100644 index 0000000..2c72540 --- /dev/null +++ b/src/dorkbox/systemTray/jna/linux/PangoRectangle.java @@ -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 getFieldOrder() { + return Arrays.asList("x", "y", "width", "height"); + } +}