1 /*
   2  * Copyright (c) 2016, 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 #define LOAD_SYMBOL(s, x) \
  49     if (s == NULL) { \
  50         s = dlsym(libraryHandle, #x); \
  51         if (s == NULL) { \
  52             fprintf(stderr,"failed loading %s\n", #x); \
  53             exit(-1); \
  54         } else { \
  55             fprintf(stderr,"loading %s\n", #x); \
  56         } \
  57     } else { \
  58         fprintf(stderr,"have %s\n", #x); \
  59     }
  60 
  61 static const char * gtk2_chain[] = {
  62    "libglassgtk2.so", "libglassgtk3.so", 0
  63 };
  64 
  65 static const char * gtk3_chain[] = {
  66    "libglassgtk3.so", "libglassgtk2.so", 0
  67 };
  68 
  69 static JavaVM* javaVM;
  70 
  71 JNIEXPORT jint JNICALL
  72 JNI_OnLoad(JavaVM *jvm, void *reserved)
  73 {
  74     (void) reserved;
  75 
  76     javaVM = jvm;
  77     // better way to get/save this?
  78     return JNI_VERSION_1_6;
  79 }
  80 
  81 // our library combinations defined
  82 // "version" "libgtk", "libdgdk", "libpixbuf"
  83 // note that currently only the first char of the version is used
  84 static char * gtk2_versioned[] = {
  85    "2", "libgtk-x11-2.0.so.0", "libgdk-x11-2.0.so.0", "libgdk_pixbuf-2.0.so"
  86 };
  87 
  88 static char * gtk2_not_versioned[] = {
  89    "2", "libgtk-x11-2.0.so", "libgdk-x11-2.0.so", "libgdk_pixbuf-2.0.so"
  90 };
  91 
  92 static char * gtk3_versioned[] = {
  93    "3", "libgtk-3.so.0", "libgdk-3.so.0", "libgdk_pixbuf-2.0.so.0"
  94 };
  95 
  96 static char * gtk3_not_versioned[] = {
  97    "3", "libgtk-3.so", "libgdk-3.so", "libgdk_pixbuf-2.0.so"
  98 };
  99 
 100 // our library set orders defined, null terminated
 101 static char ** two_to_three[] = {
 102     gtk2_versioned, gtk2_not_versioned,
 103     gtk3_versioned, gtk3_not_versioned,
 104     0
 105 };
 106 
 107 static char ** three_to_two[] = {
 108     gtk3_versioned, gtk3_not_versioned,
 109     gtk2_versioned, gtk2_not_versioned,
 110     0
 111 };
 112 
 113 static int try_opening_libraries(char *names[3])
 114 {
 115     void * gtk;
 116     void * gdk;
 117     void * pix;
 118 
 119     gtk = dlopen (names[1], RTLD_LAZY | RTLD_GLOBAL);
 120     if (!gtk) {
 121         return 0;
 122     }
 123 
 124     gdk = dlopen (names[2], RTLD_LAZY | RTLD_GLOBAL);
 125     if (!gdk) {
 126         dlclose(gtk);
 127         return 0;
 128     }
 129 
 130     pix = dlopen (names[3], RTLD_LAZY | RTLD_GLOBAL);
 131     if (!pix) {
 132         dlclose(gtk);
 133         dlclose(gdk);
 134         gtk = gdk = 0;
 135         return 0;
 136     }
 137 
 138     dlclose(gtk);
 139     dlclose(gdk);
 140     dlclose(pix);
 141 
 142     return 1;
 143 }
 144 
 145 static int sniffLibs(int wantVersion) {
 146 
 147      printf("trying for version %d\n",wantVersion);
 148 
 149      int success = 1;
 150      char *** use_chain;
 151      int wrapper_gtk_version;
 152      int wrapper_gtk_versionDebug = 1;
 153 
 154      if (wantVersion == 0 || wantVersion == 2) {
 155          use_chain = two_to_three;
 156          wrapper_gtk_version = 2;
 157      } else if (wantVersion == 3) {
 158          use_chain = three_to_two;
 159          wrapper_gtk_version = 3;
 160      } else {
 161         printf(" Huh ? what version is %d\n", wantVersion);
 162         exit (-3);
 163      }
 164 
 165     int i, found = 0;
 166     for(i = 0; use_chain[i] && !found; i++) {
 167         if (wrapper_gtk_versionDebug) {
 168             printf("trying GTK library set %s, %s, %s\n", use_chain[i][1], use_chain[i][2], use_chain[i][3]);
 169         }
 170         found = try_opening_libraries(use_chain[i]);
 171 
 172         if (found) {
 173             if (use_chain[i][0][0] == '2') {
 174                 wrapper_gtk_version = 2;
 175             } else {
 176                 wrapper_gtk_version = 3;
 177             }
 178         }
 179     }
 180 
 181     if (found) {
 182         if (wrapper_gtk_versionDebug) {
 183             i--;
 184             printf("using GTK library version %s set %s, %s, %s\n",
 185                  use_chain[i][0],
 186                  use_chain[i][1],
 187                  use_chain[i][2],
 188                  use_chain[i][3]);
 189         }
 190         return use_chain[i][0][0];
 191     }
 192 
 193      return -1;
 194 }
 195 /*
 196  * Class:     com_sun_glass_ui_gtk_GtkApplication
 197  * Method:    _queryLibrary
 198  * Signature: Signature: (IZ)I
 199  */
 200 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1queryLibrary
 201   (JNIEnv *env, jclass clazz, jint suggestedVersion, jboolean verbose)
 202 {
 203     (void) env;
 204     (void) clazz;
 205     (void) verbose;
 206 
 207     // Before doing anything with GTK we validate that the DISPLAY can be opened
 208     Display *display = XOpenDisplay(NULL);
 209     if (display == NULL) {
 210         return com_sun_glass_ui_gtk_GtkApplication_QUERY_NO_DISPLAY;
 211     }
 212     XCloseDisplay(display);
 213 
 214     // now check the the presence of the libraries
 215 
 216     char version = sniffLibs(suggestedVersion);
 217 
 218     if (version == '2') {
 219         return com_sun_glass_ui_gtk_GtkApplication_QUERY_LOAD_GTK2;
 220     } else if (version == '3') {
 221         return com_sun_glass_ui_gtk_GtkApplication_QUERY_LOAD_GTK3;
 222     }
 223 
 224     return com_sun_glass_ui_gtk_GtkApplication_QUERY_ERROR;
 225 }
 226