diff --git a/src/dorkbox/util/jna/linux/Gtk.java b/src/dorkbox/util/jna/linux/Gtk.java index aefbc9f..4f27d41 100644 --- a/src/dorkbox/util/jna/linux/Gtk.java +++ b/src/dorkbox/util/jna/linux/Gtk.java @@ -25,7 +25,7 @@ import dorkbox.util.jna.linux.structs.GtkStyle; *

* Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md */ -@SuppressWarnings({"Duplicates", "SameParameterValue", "DeprecatedIsStillUsed", "WeakerAccess"}) +@SuppressWarnings({"Duplicates", "SameParameterValue", "DeprecatedIsStillUsed", "WeakerAccess", "UnusedReturnValue"}) public interface Gtk { // objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk @@ -35,6 +35,12 @@ interface 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 + int FALSE = 0; + int TRUE = 1; + + int MAJOR = GtkLoader.MAJOR; + int MINOR = GtkLoader.MINOR; + int MICRO = GtkLoader.MICRO; // make specific versions of GTK2 vs GTK3 APIs // ALSO, GTK must be loaded via .init() @@ -49,12 +55,7 @@ interface Gtk { Function gtk_status_icon_position_menu = GtkLoader.gtk_status_icon_position_menu; - int FALSE = 0; - int TRUE = 1; - int MAJOR = GtkLoader.MAJOR; - int MINOR = GtkLoader.MINOR; - int MICRO = GtkLoader.MICRO; /** * Adds a function to be called whenever there are no higher priority events pending. If the function returns FALSE it is automatically @@ -78,11 +79,6 @@ interface Gtk { */ void gtk_main(); - /** - * aks for the current nesting level of the main loop. Useful to determine (at startup) if GTK is already running - */ - 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! diff --git a/src/dorkbox/util/jna/linux/Gtk2.java b/src/dorkbox/util/jna/linux/Gtk2.java index 60f1942..6a19299 100644 --- a/src/dorkbox/util/jna/linux/Gtk2.java +++ b/src/dorkbox/util/jna/linux/Gtk2.java @@ -41,10 +41,6 @@ class Gtk2 implements Gtk { public native void gtk_main(); - @Override - public native - int gtk_main_level(); - @Override public native void gtk_main_quit(); diff --git a/src/dorkbox/util/jna/linux/Gtk3.java b/src/dorkbox/util/jna/linux/Gtk3.java index 9952051..a689338 100644 --- a/src/dorkbox/util/jna/linux/Gtk3.java +++ b/src/dorkbox/util/jna/linux/Gtk3.java @@ -27,24 +27,19 @@ import dorkbox.util.jna.linux.structs.GtkStyle; */ public class Gtk3 implements Gtk { + static Function gdk_window_get_scale_factor = null; + // 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 - /** - * This function is typically used when implementing a GtkContainer subclass. Obtains the preferred size of a widget. The - * container uses this information to arrange its child widgets and decide what size allocations to give them with - * gtk_widget_size_allocate(). - * - * You can also call this function from an application, with some caveats. Most notably, getting a size request requires the - * widget to be associated with a screen, because font information may be needed. Multihead-aware applications should keep this in mind. - * - * Also remember that the size request is not necessarily the size a widget will actually be allocated. - */ - @Override - public - void gtk_widget_size_request(final Pointer widget, final Pointer requisition) { - this.gtk_widget_get_preferred_size(widget, requisition, null); - } + public native + int gtk_get_major_version(); + + public native + int gtk_get_minor_version(); + + public native + int gtk_get_micro_version(); /** * Retrieves the minimum and natural size of a widget, taking into account the widget’s preference for height-for-width management. @@ -64,15 +59,6 @@ class Gtk3 implements Gtk { public native void gtk_widget_get_preferred_size(final Pointer widget, final Pointer minimum_size, final Pointer natural_size); - public native - int gtk_get_major_version(); - - public native - int gtk_get_minor_version(); - - public native - int gtk_get_micro_version(); - /** * 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). @@ -87,11 +73,34 @@ class Gtk3 implements Gtk { * * @since 3.10 */ - public native - int gdk_window_get_scale_factor(Pointer window); - + public + int gdk_window_get_scale_factor(Pointer window) { + if (gdk_window_get_scale_factor != null) { + return gdk_window_get_scale_factor.invokeInt(new Object[]{window}); + } else { + return 0; + } + } + /////////////////////////// + //// GTK2 methods + /////////////////////////// + /** + * This function is typically used when implementing a GtkContainer subclass. Obtains the preferred size of a widget. The + * container uses this information to arrange its child widgets and decide what size allocations to give them with + * gtk_widget_size_allocate(). + * + * You can also call this function from an application, with some caveats. Most notably, getting a size request requires the + * widget to be associated with a screen, because font information may be needed. Multihead-aware applications should keep this in mind. + * + * Also remember that the size request is not necessarily the size a widget will actually be allocated. + */ + @Override + public + void gtk_widget_size_request(final Pointer widget, final Pointer requisition) { + this.gtk_widget_get_preferred_size(widget, requisition, null); + } @Override public native @@ -105,10 +114,6 @@ class Gtk3 implements Gtk { public native void gtk_main(); - @Override - public native - int gtk_main_level(); - @Override public native void gtk_main_quit(); diff --git a/src/dorkbox/util/jna/linux/Gtk3VersionInfo.java b/src/dorkbox/util/jna/linux/Gtk3VersionInfo.java new file mode 100644 index 0000000..2b61810 --- /dev/null +++ b/src/dorkbox/util/jna/linux/Gtk3VersionInfo.java @@ -0,0 +1,36 @@ +/* + * 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.util.jna.linux; + +/** + * bindings for GTK+ 3 version info. + *

+ * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md + */ +public +class Gtk3VersionInfo { + // 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 native + int gtk_get_major_version(); + + public native + int gtk_get_minor_version(); + + public native + int gtk_get_micro_version(); +} diff --git a/src/dorkbox/util/jna/linux/GtkEventDispatch.java b/src/dorkbox/util/jna/linux/GtkEventDispatch.java index 0c53360..59cbcec 100644 --- a/src/dorkbox/util/jna/linux/GtkEventDispatch.java +++ b/src/dorkbox/util/jna/linux/GtkEventDispatch.java @@ -81,6 +81,7 @@ class GtkEventDispatch { void run() { Glib.GLogFunc orig = null; if (DEBUG) { + // don't suppress GTK warnings in debug mode LoggerFactory.getLogger(GtkEventDispatch.class).debug("Running GTK Native Event Loop"); } else { // NOTE: This can output warnings, so we suppress them diff --git a/src/dorkbox/util/jna/linux/GtkLoader.java b/src/dorkbox/util/jna/linux/GtkLoader.java index e69b579..d855e4c 100644 --- a/src/dorkbox/util/jna/linux/GtkLoader.java +++ b/src/dorkbox/util/jna/linux/GtkLoader.java @@ -15,8 +15,6 @@ */ package dorkbox.util.jna.linux; -import static dorkbox.util.jna.linux.Gtk.Gtk2; - import org.slf4j.LoggerFactory; import com.sun.jna.Function; @@ -84,23 +82,23 @@ class GtkLoader { String gtk2LibName = "gtk-x11-2.0"; String gtk3LibName = "libgtk-3.so.0"; - if (!_isLoaded && shouldUseGtk2) { try { NativeLibrary library = JnaHelper.register(gtk2LibName, Gtk2.class); - gtk_status_icon_position_menu = Function.getFunction(gtk2LibName, "gtk_status_icon_position_menu"); - _isGtk2 = true; - Gtk gtk = new Gtk2(); - // 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.gtk_main_level() != 0; - _isLoaded = true; + _isGtk2 = true; major = library.getGlobalVariableAddress("gtk_major_version").getInt(0); minor = library.getGlobalVariableAddress("gtk_minor_version").getInt(0); micro = library.getGlobalVariableAddress("gtk_micro_version").getInt(0); + gtk_status_icon_position_menu = library.getFunction( "gtk_status_icon_position_menu"); + Function gtk_main_level = library.getFunction("gtk_main_level"); + + // 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.invokeInt(null) != 0; + _isLoaded = true; if (GtkEventDispatch.DEBUG) { LoggerFactory.getLogger(GtkLoader.class).debug("GTK: {}", gtk2LibName); } @@ -116,26 +114,35 @@ class GtkLoader { // start with version 3 if (!_isLoaded) { try { - // ALSO map Gtk2.java to GTK3 library. - JnaHelper.register(gtk3LibName, Gtk3.class); - gtk_status_icon_position_menu = Function.getFunction(gtk3LibName, "gtk_status_icon_position_menu"); - Gtk3 gtk = new Gtk3(); + // have to get the version information FIRST, because there are some really old GTK3 libraries out there. + NativeLibrary library = JnaHelper.register(gtk3LibName, Gtk3VersionInfo.class); + + Gtk3VersionInfo version = new Gtk3VersionInfo(); + major = version.gtk_get_major_version(); + minor = version.gtk_get_minor_version(); + micro = version.gtk_get_micro_version(); + library.dispose(); + + library = JnaHelper.register(gtk3LibName, Gtk3.class); + if (major >= 3 && minor >= 10) { + // Abusing static fields this way is not proper, but it gets the job done nicely. + Gtk3.gdk_window_get_scale_factor = library.getFunction("gdk_window_get_scale_factor"); + } + + gtk_status_icon_position_menu = library.getFunction( "gtk_status_icon_position_menu"); + Function gtk_main_level = library.getFunction("gtk_main_level"); // 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.gtk_main_level() != 0; + _alreadyRunningGTK = gtk_main_level.invokeInt(null) != 0; _isLoaded = true; - major = gtk.gtk_get_major_version(); - minor = gtk.gtk_get_minor_version(); - micro = gtk.gtk_get_micro_version(); - if (GtkEventDispatch.DEBUG) { LoggerFactory.getLogger(GtkLoader.class).debug("GTK: {}", gtk3LibName); } } catch (Throwable e) { if (GtkEventDispatch.DEBUG) { - LoggerFactory.getLogger(GtkLoader.class).error("Error loading library", e); + LoggerFactory.getLogger(GtkLoader.class).error("Error loading library.", e); } } } @@ -144,18 +151,19 @@ class GtkLoader { if (!_isLoaded) { try { 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 = Gtk2.gtk_main_level() != 0; - _isLoaded = true; - major = library.getGlobalVariableAddress("gtk_major_version").getInt(0); minor = library.getGlobalVariableAddress("gtk_minor_version").getInt(0); micro = library.getGlobalVariableAddress("gtk_micro_version").getInt(0); + gtk_status_icon_position_menu = Function.getFunction(gtk2LibName, "gtk_status_icon_position_menu"); + Function gtk_main_level = library.getFunction("gtk_main_level"); + + // 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.invokeInt(null) != 0; + _isLoaded = true; if (GtkEventDispatch.DEBUG) { LoggerFactory.getLogger(GtkLoader.class).debug("GTK: {}", gtk2LibName); }