JDK-8087516 : [JavaFX] Conditional support for GTK 3 on Linux
  • Type: Enhancement
  • Component: javafx
  • Sub-Component: window-toolkit
  • Affected Version: 8,9
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2015-02-12
  • Updated: 2018-10-12
  • Resolved: 2016-05-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
9 b118Fixed
Related Reports
Blocks :  
Blocks :  
Blocks :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Currently JFX uses Gtk 2.0 for Glass/GTK.

Gtk 3.0 has been around for a while (since ~2011), so we should consider migrating to it.
Gtk 4.0 is not scheduled for shipment until late 2015 so should not be considered.

AWT and Swing use Gtk 2.0 and are not likely to be updated in JDK9 time frame. 

Currently it is possible to encounter difficulties with the GTK version when using other 
technologies with JFX that depend on GTK3 - like SWT (JDK-8089584). 

Over the life of JDK9, it may become likely that GTK2 is no longer installed by default. 
(Ubuntu 14, 15, OEL 7 seem to install GTK2 and GTK3 runtime by default)

There are a limited number of ways to launch a JFX app:

* FXCanvas - Used to support interop with SWT, fails on newer systems where SWT uses GTK3.0

* JFXPanel - Swing Interop, requires GTK2

* Application subclass - The "normal" way to use JFX

* Platform.startup - A JDK9 added API to initiate the JFX toolkit.

* Application, then JFXPanel - JFX is started prior to JFXPanel being used. Requires GTK2.

   Gtk 3.0 is the currently supported code stream
   Swt 4.4 uses Gtk 3.0 by default (can fall back to Gtk 2.0 though, with ENV setting )
   Swt 3.x uses Gtk 2.0 by default and linking JFX with Gtk 3.0 would cause issues.
   Unknown amount of porting effort
   Coverage testing of the port.

There is a GTK migration guide at:

Proposed solution for this bug:

Create and use a dynamic wrapper using dlopen/dlsym. This mechanism
is a common pattern used in JFX for other purposes, and so is well
understood. In short, a dynamic GTK function table  will be used
to access the basic functionality of GTK. At startup time, dlopen/dlsym
will be used to populate the function table. JFX code in Glass/GTK
will be modified to call through the function table. As needed
"shim" functions will be created as needed to isolate and handle
API changes. 

A system property will be created that will contain the desired
order for GTK discovery.  It will be possible for a user to specify
the property to force GTK2 or GTK3 usage. 

Documentation will need to be added for this property.

In some cases we will be able to set the default order to hide what
is happening, and just work. For example in the case of JFXPanel -
we can set the system property to indicate that only GTK2 may be
used, and so, if JFXPanel then causes the JFX runtime to be started,
the correct version will be selected. Note that in the case of
Application then JFXPanel, the FX runtime will already have been
started so if for some reason GTK3 has been selected, JFXPanel will
fail, unless the user specified the property.

Normally we will default the property to load GTK2, falling forward
to GTK3, if GTK2 is not present.
Note that the 8u backport for this fix, along with the follow-on fixes for various GTK 3 issues, is tracked by JDK-8206246 and will be pushed using that bug ID and not this one.

Changeset: 8c6d0386d3f5 Author: ddhill Date: 2016-05-06 19:50 -0400 URL: http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/8c6d0386d3f5 8087516: Conditional support for GTK 3 on Linux Reviewed-by: kcr ! buildSrc/linux.gradle ! modules/graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java ! modules/graphics/src/main/native-glass/gtk/GlassApplication.cpp ! modules/graphics/src/main/native-glass/gtk/GlassCommonDialogs.cpp ! modules/graphics/src/main/native-glass/gtk/GlassRobot.cpp ! modules/graphics/src/main/native-glass/gtk/GlassSystemClipboard.cpp ! modules/graphics/src/main/native-glass/gtk/GlassView.cpp ! modules/graphics/src/main/native-glass/gtk/GlassWindow.cpp ! modules/graphics/src/main/native-glass/gtk/glass_dnd.cpp ! modules/graphics/src/main/native-glass/gtk/glass_general.cpp ! modules/graphics/src/main/native-glass/gtk/glass_general.h - modules/graphics/src/main/native-glass/gtk/glass_gtkcompat.cpp - modules/graphics/src/main/native-glass/gtk/glass_gtkcompat.h ! modules/graphics/src/main/native-glass/gtk/glass_key.cpp ! modules/graphics/src/main/native-glass/gtk/glass_screen.cpp ! modules/graphics/src/main/native-glass/gtk/glass_window.cpp ! modules/graphics/src/main/native-glass/gtk/glass_window.h ! modules/graphics/src/main/native-glass/gtk/glass_window_ime.cpp + modules/graphics/src/main/native-glass/gtk/glass_wrapper.h + modules/graphics/src/main/native-glass/gtk/wrapper_gdk.c + modules/graphics/src/main/native-glass/gtk/wrapper_gtk.c + modules/graphics/src/main/native-glass/gtk/wrapper_main.c + modules/graphics/src/main/native-glass/gtk/wrapper_pix.c

wrapper_main.c 1. You have an off by one error here as 'i' will already have been incremented. This is a functional error that needs to be fixed. 180 if (found) { 181 if (wrapper_gtk_versionDebug) { 182 printf("using GTK library set %s, %s, %s\n", 183 use_chain[i][1], 184 use_chain[i][2], 185 use_chain[i][3]); 186 } 2. A few minor spacing nits remain: 88 fprintf(stderr,"failed to load %s\n",names[1]); 96 fprintf(stderr,"failed to load %s\n",names[2]); 106 fprintf(stderr,"failed to load %s\n",names[3]); 137 fprintf(stderr,"Unrecognized GTK version requested, falling back to v 2.0\n"); (add space after commas separating arguments) 148 for(i=0; use_chain[i] && ! found; i ++) { (add space around '='; remove space after '!' and before '++') etc. Other wrapper files also have an extra space after the macro name, but may not be worth fixing. 3. Check the copyright dates of the new files (OK for the ones that got the bulk of the code from glass_gtkcompat.cpp to retain that start date). +1 pending a fix for at least #1

The :systemTests:test failures reported above are spurious (almost certainly a result of my use of the system during the test run). A clean run today shows no failure.

version 4: http://cr.openjdk.java.net/~ddhill/8087516.4/ changes from 3: http://cr.openjdk.java.net/~ddhill/8087516.4d/

I did some testing today (I'll review the latest webrev tomorrow), and didn't find anything too broken. 1. I ran a full set of tests using: gradle --continue --info -PFULL_TEST=true -PUSE_ROBOT=true -PEXTRA_TEST_ARGS="-Djdk.gtk.verbose=true -Djdk.gtk.version=3" test I got the following failures (although I'm not sure the RegionBackgroundFillUITest failures are real, since I did use the system at the time which is a bad idea for robot tests) USKeyboardTest.testCapsLock USKeyboardTest.testShift RegionBackgroundFillUITest. linearFill_Radius4 RegionBackgroundFillUITest. testScenario1 RegionBackgroundFillUITest. testScenario2 RegionBackgroundFillUITest. testScenario3 RegionBackgroundFillUITest. testScenario4 2. I was able to run an FXCanvas application (in legacy mode using jdk-9+109 and the jfxswt.jar file from our build) with gtk2 (using swt 3.7.1) and with gtk3 (using swt 4.4). As long as you force gtk3 by using "-Djdk.gtk.version=3" when using the newer swt library it works. Similarly, using gtk2 (either by default or by specifying "-Djdk.gtk.version=2") with the older SWT works. What doesn't work is detecting the use of GTK3 even when FXCanvas is used. This can be handled with a follow-up JIRA. To summarize: With the older GTK2-based SWT ----------------------------------------- WORKS: java ... WORKS: java -Djdk.gtk.version=2 ... FAILS: java -Djdk.gtk.version=3 ... The failure case is as follows: (SWT:18932): GLib-GObject-WARNING **: cannot register existing type 'GdkDisplayManager' (SWT:18932): GLib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed (SWT:18932): GLib-GObject-CRITICAL **: g_object_new: assertion 'G_TYPE_IS_OBJECT (object_type)' failed With the newer GTK3-based SWT ------------------------------------------ CRASHES: java ... CRASHES: java -Djdk.gtk.version=2 ... WORKS: java -Djdk.gtk.version=3 ... 3. Running any FX application that uses SwingNode in GTK 3 mode produces the following warning, but otherwise seems to run OK: ----- EmbeddedSwing ----- java -ea -Djdk.gtk.verbose=true -Djdk.gtk.version=3 -DproxyHost=www-proxy.us.oracle.com -DproxyPort=80 -cp /localhome/kcr/javafx/9-kcr/jfx/artifacts/sdk/lib/jfxrt.jar:dist/EmbeddedSwing.jar embeddedswing.EmbeddedSwing Loading GTK libraries version 3 trying GTK library set libgtk-3.so.0, libgdk-3.so.0, libgdk_pixbuf-2.0.so.0 using GTK library set libgtk-3.so, libgdk-3.so, libgdk_pixbuf-2.0.so (java:5773): Gdk-WARNING **: XSetErrorHandler() called with a GDK error trap pushed. Don't do that. I saw this warning in the full test in #1, too.

Phil, folded in your comments, thank you. Except, > Since its the same pixbuf library why do we need to load this by dlopen instead of direct linkage ? I have done back and forth on the issue of ibgdk_pixbuf-2.0.so, and finally left it in, partly because I already had it, partly because it is part of the GTK set, partly because it felt better to have it in place when I looked through the symbol table. Certainly if pushed I would reconsider dropping the pixbuf wrapper, at least after some testing confirmed it is not needed. I had wrapped then disposed of my Cairo wrapping.

147 //LINUX.glass.linkFlags = [linkFlags, "-lgio-2.0", "-lgobject-2.0", "-lglib-2.0" ].flatten() remove 93 "We rely on GThreadHelper for GLib < 2.20.: remove 453 // PRELOAD_SYMBOL_GDK (gdk_drawable_get_type); remove ?? 370 exit(-10); \ 371 assert(_##x); \ Not sure there's a point to an assert after you exited but should you exit ? If it is bound to crash afterwards perhaps that is best and it may be a lot of work to know which ones you could try to survive and probably not worth it since it should not happen anyway. 85 *gtk = dlopen (names[1], RTLD_LAZY); shouldn't we be specifying RTLD_GLOBAL ? A comment in one email (maybe off-line) referenced someone getting advice from "gtk devs" that expects to be loaded with GLOBAL 55 "2", "libgtk-x11-2.0.so.0", "libgdk-x11-2.0.so.0", "libgdk_pixbuf-2.0.so" 59 "2", "libgtk-x11-2.0.so", "libgdk-x11-2.0.so", "libgdk_pixbuf-2.0.so" Since its the same pixbuf library why do we need to load this by dlopen instead of direct linkage ?

version 3: http://cr.openjdk.java.net/~ddhill/8087516.3/ This reworks the loading of the gtk symbols, providing for trying .so.0 first, and falling forward or back between 2 & 3. The meat of the change in the rework in wrapper_main.c I also removed the block of code mentioned by Serge in GlassApplication.cpp

Although I am not sure of this without testing, I thought Semyon's approach is to try what you asked but fall back if it was not possible. We should probably be consistent. The "verbose" option can always tell you what you got.

Phil, working now on implementing the load of the versioned library. > Sorry, one more suggestion. In the event that your load of GTK3 fails as it does above, can we not then revert to GTK 2 instead of blowing up ? As to this - my thought was that if you asked for a specific version, you get that or nothing. If you don't ask for a version, then I will try 2 and fall forward to 3 (and hopefully in 10, the reverse). I am open for discussion however.

PS .. in the "JDK" GTK 3 fix the code looks for both with and without the extension:- See the following in gtk_interface.c 55 { 56 GTK_3, 57 JNI_LIB_NAME("gtk-3"), 58 VERSIONED_JNI_LIB_NAME("gtk-3", "0"), 59 &gtk3_load, 60 &gtk3_check 61 }, The macros are defined in jvm_md.h PPS. Ubuntu 16.04 "out of the box" defines just gtk-3.so.0 so if you only have one that one is the more important one. And it means there is still no "later" version of gtk3 likely to be found on current systems. Sorry, one more suggestion. In the event that your load of GTK3 fails as it does above, can we not then revert to GTK 2 instead of blowing up ?

https://bugs.openjdk.java.net/browse/JDK-8154546 will move GThreadHelper. JDK might still need it but FX does not so I suggest you remove it as part of this change. That will facilitate JDK moving it into the desktop module and we then won't need to ensure it is a qualified export .. I see Sergey's comment above. I am not sure but I think the JDK might still need it since it must run on older Linuxes. When I try -Djdk.gtk.version=3 I get an exception :- -------- Loading GTK libraries version 3 Did not find libgtk-3.so: libgtk-3.so: cannot open shared object file: No such file or directory Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ------- On my system I do not have GTK3 *developer* packages, just runtime, so I do not have libgtk-3.so, instead I have libgtk-3.so.0 If I manually add a symlink it then just makes the same complaint about libgdk3 When I added that one *then* I was able to start up and Ensemble8 runs. ---- Loading GTK libraries version 3 loaded libgtk-3.so loaded libgdk-3.so loading gdk we have symbol_load_errors = 0 loaded libgdk_pixbuf-2.0.so

It seems that sun/misc/GThreadHelper is not used any more [1] by FX, and can be removed from jdk? [1] http://cr.openjdk.java.net/~ddhill/8087516.2/modules/graphics/src/main/native-glass/gtk/glass_general.cpp.sdiff.html Note that it is possible to change the "jdk.gtk.version" after awt was started but before FX(and vice versa), can this lead to some issues?

Here is the latest version of the changes. This addresses the issues raised by Kevin, as well as some other minor cleanups. This passes a FULL_TEST and Robot tests with GTK 2 & 3 for me. http://cr.openjdk.java.net/~ddhill/8087516.2/

Here is the patch to fix the visual problem (the one causing the glXMakeCurrent to fail). diff --git a/modules/graphics/src/main/native-glass/gtk/wrapper_gdk.c b/modules/graphics/src/main/native-glass/gtk/wrapper_gdk.c --- a/modules/graphics/src/main/native-glass/gtk/wrapper_gdk.c +++ b/modules/graphics/src/main/native-glass/gtk/wrapper_gdk.c @@ -1590,11 +1590,13 @@ static void configure_opaque_window(GtkWidget *window) { +/* CHECK_LOAD_SYMBOL_GDK (gdk_screen_get_system_visual); CHECK_LOAD_SYMBOL_GDK (gdk_screen_get_default); glass_widget_set_visual(window, gdk_screen_get_system_visual( gdk_screen_get_default())); +*/ } gboolean

The above function, which used to live in glass_gtkcompat.cpp, is a no-op in the current code (it calls gtk_widget_set_visual() which is a no-op in GTK2). So by actually implementing this method, the previously-set visual is overwritten with the screen default.

Here is my feedback on webrev.1 General comments: * For reasons as yet unknown, glXMakeCurrent fails on my Ubuntu 14.04 system with this patch when running the default GTK 2.0 library * "wrapper.h" seems a too-common name; maybe gtk_wrapper.h or glass_wrapper.h would be better? * All other files in the gtk/ dir are .cpp files. Do the wrapper_xxxx files really need to be .c or can they also be .cpp? * I didn't look at the glass utility functions in wrapper_gtk.c (I can do that next time) buildSrc/linux.gradle * I see that you removed "gtk+-2.0" from pkgconfig --libs, and also removed "-lgobject-2.0" and "-lglib-2.0" from the glass.linkFlags. How will the right version of these libraries be loaded? * Possibly related to the above, have you built and tested both 32-bit and 64-bit versions of glass? GtkApplication.java * The following needs to be moved ahead of the call to _setGTKversion or you will cause a regression (it will reintroduce a bug in headless mode). This block should remain the first thing that the constructor does: 84 // Check whether the Display is valid and throw an exception if not. 85 // We use UnsupportedOperationException rather than HeadlessException 86 // so as not to introduce a dependency on AWT. 87 if (!isDisplayValid()) { 88 throw new UnsupportedOperationException("Unable to open DISPLAY"); 89 } * In the following: + if ("3".equals(v)) { Is it worth adding v.startsWith("3.") as you do for 2? * Minor: indentation for continue lines following, e.g., "int gtkVersion =" is not right (should be indented) * I think "_initGTK" (or similar) might be a better name that "_setGTKversion" to highlight that this is where the initialization of the wrappers and gtk goes. glass_general.cpp * I see that you moved initThreads() method. Was this the only call into gdk (or gtk) that was done prior to when you now call the native setGTKversion method? glass_window.cpp * Minor: Extra space before the open parens in a few places. For example, the following has two extra spaces: GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdk_window)); wrapper_main.c * I think the following will be ineffective given that you initialize the version to 2 rather than 0 in the absence of the system property in GtkApplication.java: 75 if (version == 0) { 76 // specific version not set, try the other 77 wrapper_gtk_version = wrapper_gtk_version == 2 ? 3 : 2; * I also recommend testing for a return code of -1 (library failed to load) in addition to version == 0, since falling over to the other library (2 or 3) only has a chance of working if it failed to load the library. There will only be pain in trying to load gtk3 if gtk2 has already been loaded, but one of the dlsym calls failed. * You are missing a "success = 0" in the case where a version was specified but could not be loaded (basically you need an else case for the above if check). 83 if (wrapper_load_symbols_gtk(wrapper_gtk_version) != 0 ) { 84 success = 0; 85 } } else { success = 0 } 87 } * The following is OK for debugging, but not for production code. You need to change it to return an error status (or throw an exception) back to the calling Java code so it can properly throw an exception to the application. 97 if (!success) { 98 fprintf (stderr, "Fatal Error: Could not resolve symbols in GTK libraries %d \n",version); 99 fflush(stderr); 100 exit(-10); 101 } * Minor: extra space before some of the closing paren. For example: if (wrapper_load_symbols_gtk(wrapper_gtk_version) != 0 ) { wrapper_gtk.c * Are the following library names going to work across different Linux distros and versions? Also, as a follow-up to my earlier question, will it correctly load the library of the right architecture -- 32-bit versus 64-bit? 252 if (version == 2) { 253 libname = "libgtk-x11-2.0.so"; 254 } else if (version == 3) { 255 libname = "libgtk-3.so"; 256 } else { * same comment as in previous file about not calling exit (or assert unless compiling a DEBUG build (CONF=DebugNative)) : 256 } else { 257 fprintf (stderr, "Internal error - wrong GTK version specified\n"); 258 exit(-1); 259 } ... 389 if (!_##x) { \ 390 if (wrapper_debug) fprintf(stderr,"missing %s\n",#x); \ 391 exit(-10); \ 392 assert(_##x); \ * I don't understand the following comment: 261 // not using | RTLD_GLOBAL as that overlays our entries 262 libgtk = dlopen (libname, (wrapper_debug ? RTLD_NOW : RTLD_LAZY ) | RTLD_GLOBAL); You are using "| RTLD_GLOBAL" and yet your comment says you are not. * There should be a space after the commas in the PRELOAD_SYMBOL_GTK macro: 240 _##x = dlsym(libgtk,#x); \ 243 fprintf(stderr,"failed loading %s\n",#x); \ * The following should either be an "else if", or the "if" should be moved to the subsequent line: 370 } if (version == 3) { * Minor: extra space before the paren here: 246 int wrapper_load_symbols_gtk (int version) wrapper_gdk.c * Same comment about exit and assert: 362 } else { 363 fprintf (stderr, "Internal error - wrong GTK version specified\n"); 364 exit(-1); 365 } ... 514 if (!_##x) { \ 515 if (wrapper_debug) fprintf(stderr,"missing %s\n",#x); \ 516 exit(-10); \ 517 assert(_##x); \ * Other similar spacing issues as with wrapper_gtk wrapper_pix.c * Same comments about exit, spacing, etc.

Here is an early draft of [JavaFX] Conditional support for GTK 3 on Linux http://cr.openjdk.java.net/~ddhill/8087516.1/ There are some rough edges left, particularly with the GTK 3 side of shaped windows. Given my time frame, I am interested in feedback on what is there because the core of it is complete. I have done limited testing on this, some with GTK3 and have not found any problems so far. I have more unit testing planned for next week. The default GTK version will be GTK2. To use GTK3, use the following: -Djdk.gtk.version=3 To enable some really verbose output: -Djdk.gtk.verbose=true This verbosity will be reduced before I commit this - and only a message about the version actually used will be output. Known issues: Marked with a DAVE for easy cleanup later. * shaped windows with GTK3 - need to verify the new region methods * debug code that will be used to check that all of the code paths are tested. * move the disableGrab back into the main code logic.

We are blocked by AWT moving to GTK3.

Adding this request for enhancement for awt/swing.

We probably want to consider if AWT/Swing is moving to GTK 3. We support Swing interop, and that could be challenged if we are not careful.