1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <stdio.h>
  27 #include <stdlib.h>
  28 #include <linux/fb.h>
  29 #include <fcntl.h>
  30 #ifndef __USE_GNU       // required for dladdr() & Dl_info
  31 #define __USE_GNU
  32 #endif
  33 #include <dlfcn.h>
  34 #include <sys/ioctl.h>
  35 
  36 #include <string.h>
  37 #include <strings.h>
  38 #include <stdlib.h>
  39 
  40 #include <X11/Xlib.h>
  41 
  42 #include <assert.h>
  43 
  44 #include <jni.h>
  45 
  46 #include <com_sun_glass_ui_gtk_GtkApplication.h>
  47 
  48 static jboolean gtk_versionDebug = JNI_FALSE;
  49 
  50 static const char * gtk2_chain[] = {
  51    "libglassgtk2.so", "libglassgtk3.so", 0
  52 };
  53 
  54 static const char * gtk3_chain[] = {
  55    "libglassgtk3.so", "libglassgtk2.so", 0
  56 };
  57 
  58 static JavaVM* javaVM;
  59 
  60 JNIEXPORT jint JNICALL
  61 JNI_OnLoad(JavaVM *jvm, void *reserved)
  62 {
  63     (void) reserved;
  64 
  65     javaVM = jvm;
  66 
  67     return JNI_VERSION_1_6;
  68 }
  69 
  70 // our library combinations defined
  71 // "version" "libgtk", "libdgdk", "libpixbuf"
  72 // note that currently only the first char of the version is used
  73 static char * gtk2_versioned[] = {
  74    "2", "libgtk-x11-2.0.so.0"
  75 };
  76 
  77 static char * gtk2_not_versioned[] = {
  78    "2", "libgtk-x11-2.0.so"
  79 };
  80 
  81 static char * gtk3_versioned[] = {
  82    "3", "libgtk-3.so.0"
  83 };
  84 
  85 static char * gtk3_not_versioned[] = {
  86    "3", "libgtk-3.so"
  87 };
  88 
  89 // our library set orders defined, null terminated
  90 static char ** two_to_three[] = {
  91     gtk2_versioned, gtk2_not_versioned,
  92     gtk3_versioned, gtk3_not_versioned,
  93     0
  94 };
  95 
  96 static char ** three_to_two[] = {
  97     gtk3_versioned, gtk3_not_versioned,
  98     gtk2_versioned, gtk2_not_versioned,
  99     0
 100 };
 101 
 102 static int try_opening_libraries(char *names[3])
 103 {
 104     void * gtk;
 105 
 106     gtk = dlopen (names[1], RTLD_LAZY | RTLD_GLOBAL);
 107     if (!gtk) {
 108         return 0;
 109     }
 110 
 111     return 1;
 112 }
 113 
 114 static int try_libraries_noload(char *names[3])
 115 {
 116 #ifdef RTLD_NOLOAD
 117     void *gtk;
 118     gtk = dlopen(names[1], RTLD_LAZY | RTLD_NOLOAD);
 119     return gtk ? 1 : 0;
 120 #else
 121     return 0;
 122 #endif
 123 }
 124 
 125 static int sniffLibs(int wantVersion) {
 126 
 127      if (gtk_versionDebug) {
 128          printf("checking GTK version %d\n",wantVersion);
 129      }
 130 
 131      int success = 1;
 132      char *** use_chain = two_to_three;
 133      int i, found = 0;
 134 
 135      //at first try to detect already loaded GTK version
 136      for (i = 0; use_chain[i] && !found; i++) {
 137         found = try_libraries_noload(use_chain[i]);
 138         if (found && gtk_versionDebug) {
 139             printf("found already loaded GTK library %s\n", use_chain[i][1]);
 140         }
 141      }
 142 
 143      if (!found) {
 144          if (wantVersion == 0 || wantVersion == 2) {
 145              use_chain = two_to_three;
 146          } else if (wantVersion == 3) {
 147              use_chain = three_to_two;
 148          } else {
 149              // Note, this should never happen, java should be protecting us
 150              if (gtk_versionDebug) {
 151                  printf("bad GTK version specified, assuming 2\n");
 152              }
 153              wantVersion = 2;
 154              use_chain = two_to_three;
 155          }
 156 
 157          for (i = 0; use_chain[i] && !found; i++) {
 158              if (gtk_versionDebug) {
 159                  printf("trying GTK library %s\n", use_chain[i][1]);
 160              }
 161              found = try_opening_libraries(use_chain[i]);
 162          }
 163      }
 164 
 165     if (found) {
 166         if (gtk_versionDebug) {
 167             i--;
 168             printf("using GTK library version %s set %s\n",
 169                  use_chain[i][0],
 170                  use_chain[i][1]);
 171             fflush(stdout);
 172         }
 173         return use_chain[i][0][0];
 174     }
 175     if (gtk_versionDebug) {
 176         fflush(stdout);
 177     }
 178     return -1;
 179 }
 180 
 181 /*
 182  * Class:     com_sun_glass_ui_gtk_GtkApplication
 183  * Method:    _queryLibrary
 184  * Signature: Signature: (IZ)I
 185  */
 186 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1queryLibrary
 187   (JNIEnv *env, jclass clazz, jint suggestedVersion, jboolean verbose)
 188 {
 189     (void) env;
 190     (void) clazz;
 191 
 192     gtk_versionDebug = verbose;
 193 
 194     // Before doing anything with GTK we validate that the DISPLAY can be opened
 195     Display *display = XOpenDisplay(NULL);
 196     if (display == NULL) {
 197         return com_sun_glass_ui_gtk_GtkApplication_QUERY_NO_DISPLAY;
 198     }
 199     XCloseDisplay(display);
 200 
 201     // now check the the presence of the libraries
 202 
 203     char version = sniffLibs(suggestedVersion);
 204 
 205     if (version == '2') {
 206         return com_sun_glass_ui_gtk_GtkApplication_QUERY_LOAD_GTK2;
 207     } else if (version == '3') {
 208         return com_sun_glass_ui_gtk_GtkApplication_QUERY_LOAD_GTK3;
 209     }
 210 
 211     return com_sun_glass_ui_gtk_GtkApplication_QUERY_ERROR;
 212 }
 213