1 /*
   2  * Copyright (c) 2011, 2017, 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 #include "glass_general.h"
  26 
  27 #include <jni.h>
  28 #include <gtk/gtk.h>
  29 
  30 char const * const GDK_WINDOW_DATA_CONTEXT = "glass_window_context";
  31 
  32 jclass jStringCls;
  33 jclass jByteBufferCls;
  34 jmethodID jByteBufferArray;
  35 jmethodID jByteBufferWrap;
  36 
  37 jclass jRunnableCls;
  38 jmethodID jRunnableRun;
  39 
  40 jclass jArrayListCls;
  41 jmethodID jArrayListInit;
  42 jmethodID jArrayListAdd;
  43 jmethodID jArrayListGetIdx;
  44 
  45 jmethodID jPixelsAttachData;
  46 
  47 jclass jGtkPixelsCls;
  48 jmethodID jGtkPixelsInit;
  49 
  50 jclass jScreenCls;
  51 jmethodID jScreenInit;
  52 jmethodID jScreenNotifySettingsChanged;
  53 
  54 jmethodID jViewNotifyResize;
  55 jmethodID jViewNotifyMouse;
  56 jmethodID jViewNotifyRepaint;
  57 jmethodID jViewNotifyKey;
  58 jmethodID jViewNotifyView;
  59 jmethodID jViewNotifyDragEnter;
  60 jmethodID jViewNotifyDragOver;
  61 jmethodID jViewNotifyDragDrop;
  62 jmethodID jViewNotifyDragLeave;
  63 jmethodID jViewNotifyScroll;
  64 jmethodID jViewNotifyInputMethod;
  65 jmethodID jViewNotifyInputMethodDraw;
  66 jmethodID jViewNotifyInputMethodCaret;
  67 jmethodID jViewNotifyPreeditMode;
  68 jmethodID jViewNotifyMenu;
  69 jfieldID  jViewPtr;
  70 
  71 jmethodID jWindowNotifyResize;
  72 jmethodID jWindowNotifyMove;
  73 jmethodID jWindowNotifyDestroy;
  74 jmethodID jWindowNotifyClose;
  75 jmethodID jWindowNotifyFocus;
  76 jmethodID jWindowNotifyFocusDisabled;
  77 jmethodID jWindowNotifyFocusUngrab;
  78 jmethodID jWindowNotifyMoveToAnotherScreen;
  79 jmethodID jWindowNotifyLevelChanged;
  80 jmethodID jWindowIsEnabled;
  81 jmethodID jWindowNotifyDelegatePtr;
  82 jfieldID jWindowPtr;
  83 jfieldID jCursorPtr;
  84 
  85 jmethodID jGtkWindowNotifyStateChanged;
  86 
  87 jmethodID jClipboardContentChanged;
  88 
  89 jmethodID jSizeInit;
  90 
  91 jmethodID jMapGet;
  92 jmethodID jMapKeySet;
  93 jmethodID jMapContainsKey;
  94 
  95 jclass jHashSetCls;
  96 jmethodID jHashSetInit;
  97 
  98 jmethodID jSetAdd;
  99 jmethodID jSetSize;
 100 jmethodID jSetToArray;
 101 
 102 jmethodID jIterableIterator;
 103 jmethodID jIteratorHasNext;
 104 jmethodID jIteratorNext;
 105 
 106 jclass jApplicationCls;
 107 jfieldID jApplicationDisplay;
 108 jfieldID jApplicationScreen;
 109 jfieldID jApplicationVisualID;
 110 jmethodID jApplicationReportException;
 111 jmethodID jApplicationGetApplication;
 112 jmethodID jApplicationGetName;
 113 
 114 static jboolean displayValid = JNI_FALSE;
 115 
 116 jboolean
 117 is_display_valid() {
 118     return displayValid;
 119 }
 120 
 121 JavaVM* javaVM;
 122 
 123 JNIEXPORT jint JNICALL
 124 JNI_OnLoad(JavaVM *jvm, void *reserved)
 125 {
 126     (void)reserved;
 127 
 128     JNIEnv *env;
 129     jclass clazz;
 130     Display* display;
 131 
 132     javaVM = jvm;
 133     if (jvm->GetEnv((void **)&env, JNI_VERSION_1_6)) {
 134          return JNI_ERR; /* JNI version not supported */
 135     }
 136 
 137     clazz = env->FindClass("java/lang/String");
 138     if (env->ExceptionCheck()) return JNI_ERR;
 139     jStringCls = (jclass) env->NewGlobalRef(clazz);
 140 
 141     clazz = env->FindClass("java/nio/ByteBuffer");
 142     if (env->ExceptionCheck()) return JNI_ERR;
 143     jByteBufferCls = (jclass) env->NewGlobalRef(clazz);
 144     jByteBufferArray = env->GetMethodID(jByteBufferCls, "array", "()[B");
 145     if (env->ExceptionCheck()) return JNI_ERR;
 146     jByteBufferWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
 147     if (env->ExceptionCheck()) return JNI_ERR;
 148 
 149     clazz = env->FindClass("java/lang/Runnable");
 150     if (env->ExceptionCheck()) return JNI_ERR;
 151 
 152     jRunnableRun = env->GetMethodID(clazz, "run", "()V");
 153     if (env->ExceptionCheck()) return JNI_ERR;
 154 
 155     clazz = env->FindClass("java/util/ArrayList");
 156     if (env->ExceptionCheck()) return JNI_ERR;
 157     jArrayListCls = (jclass) env->NewGlobalRef(clazz);
 158     jArrayListInit = env->GetMethodID(jArrayListCls, "<init>", "()V");
 159     if (env->ExceptionCheck()) return JNI_ERR;
 160     jArrayListAdd = env->GetMethodID(jArrayListCls, "add", "(Ljava/lang/Object;)Z");
 161     if (env->ExceptionCheck()) return JNI_ERR;
 162     jArrayListGetIdx = env->GetMethodID(jArrayListCls, "get", "(I)Ljava/lang/Object;");
 163     if (env->ExceptionCheck()) return JNI_ERR;
 164     clazz = env->FindClass("com/sun/glass/ui/Pixels");
 165     if (env->ExceptionCheck()) return JNI_ERR;
 166     jPixelsAttachData = env->GetMethodID(clazz, "attachData", "(J)V");
 167     if (env->ExceptionCheck()) return JNI_ERR;
 168 
 169     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkPixels");
 170     if (env->ExceptionCheck()) return JNI_ERR;
 171 
 172     jGtkPixelsCls = (jclass) env->NewGlobalRef(clazz);
 173     jGtkPixelsInit = env->GetMethodID(jGtkPixelsCls, "<init>", "(IILjava/nio/ByteBuffer;)V");
 174     if (env->ExceptionCheck()) return JNI_ERR;
 175 
 176     clazz = env->FindClass("com/sun/glass/ui/Screen");
 177     if (env->ExceptionCheck()) return JNI_ERR;
 178     jScreenCls = (jclass) env->NewGlobalRef(clazz);
 179     jScreenInit = env->GetMethodID(jScreenCls, "<init>", "(JIIIIIIIIIIIIIIIFFFF)V");
 180     if (env->ExceptionCheck()) return JNI_ERR;
 181     jScreenNotifySettingsChanged = env->GetStaticMethodID(jScreenCls, "notifySettingsChanged", "()V");
 182     if (env->ExceptionCheck()) return JNI_ERR;
 183 
 184     clazz = env->FindClass("com/sun/glass/ui/View");
 185     if (env->ExceptionCheck()) return JNI_ERR;
 186     jViewNotifyResize = env->GetMethodID(clazz, "notifyResize", "(II)V");
 187     if (env->ExceptionCheck()) return JNI_ERR;
 188     jViewNotifyMouse = env->GetMethodID(clazz, "notifyMouse", "(IIIIIIIZZ)V");
 189     if (env->ExceptionCheck()) return JNI_ERR;
 190     jViewNotifyRepaint = env->GetMethodID(clazz, "notifyRepaint", "(IIII)V");
 191     if (env->ExceptionCheck()) return JNI_ERR;
 192     jViewNotifyKey = env->GetMethodID(clazz, "notifyKey", "(II[CI)V");
 193     if (env->ExceptionCheck()) return JNI_ERR;
 194     jViewNotifyView = env->GetMethodID(clazz, "notifyView", "(I)V");
 195     if (env->ExceptionCheck()) return JNI_ERR;
 196     jViewNotifyDragEnter = env->GetMethodID(clazz, "notifyDragEnter", "(IIIII)I");
 197     if (env->ExceptionCheck()) return JNI_ERR;
 198     jViewNotifyDragOver = env->GetMethodID(clazz, "notifyDragOver", "(IIIII)I");
 199     if (env->ExceptionCheck()) return JNI_ERR;
 200     jViewNotifyDragDrop = env->GetMethodID(clazz, "notifyDragDrop", "(IIIII)I");
 201     if (env->ExceptionCheck()) return JNI_ERR;
 202     jViewNotifyDragLeave = env->GetMethodID(clazz, "notifyDragLeave", "()V");
 203     if (env->ExceptionCheck()) return JNI_ERR;
 204     jViewNotifyScroll = env->GetMethodID(clazz, "notifyScroll", "(IIIIDDIIIIIDD)V");
 205     if (env->ExceptionCheck()) return JNI_ERR;
 206     jViewNotifyInputMethod = env->GetMethodID(clazz, "notifyInputMethod", "(Ljava/lang/String;[I[I[BIII)V");
 207     if (env->ExceptionCheck()) return JNI_ERR;
 208     jViewNotifyMenu = env->GetMethodID(clazz, "notifyMenu", "(IIIIZ)V");
 209     if (env->ExceptionCheck()) return JNI_ERR;
 210     jViewPtr = env->GetFieldID(clazz, "ptr", "J");
 211     if (env->ExceptionCheck()) return JNI_ERR;
 212 
 213     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkView");
 214     if (env->ExceptionCheck()) return JNI_ERR;
 215     jViewNotifyInputMethodDraw = env->GetMethodID(clazz, "notifyInputMethodDraw", "(Ljava/lang/String;III[B)V");
 216     if (env->ExceptionCheck()) return JNI_ERR;
 217     jViewNotifyInputMethodCaret = env->GetMethodID(clazz, "notifyInputMethodCaret", "(III)V");
 218     if (env->ExceptionCheck()) return JNI_ERR;
 219     jViewNotifyPreeditMode = env->GetMethodID(clazz, "notifyPreeditMode", "(Z)V");
 220     if (env->ExceptionCheck()) return JNI_ERR;
 221 
 222     clazz = env->FindClass("com/sun/glass/ui/Window");
 223     if (env->ExceptionCheck()) return JNI_ERR;
 224     jWindowNotifyResize = env->GetMethodID(clazz, "notifyResize", "(III)V");
 225     if (env->ExceptionCheck()) return JNI_ERR;
 226     jWindowNotifyMove = env->GetMethodID(clazz, "notifyMove", "(II)V");
 227     if (env->ExceptionCheck()) return JNI_ERR;
 228     jWindowNotifyDestroy = env->GetMethodID(clazz, "notifyDestroy", "()V");
 229     if (env->ExceptionCheck()) return JNI_ERR;
 230     jWindowNotifyClose = env->GetMethodID(clazz, "notifyClose", "()V");
 231     if (env->ExceptionCheck()) return JNI_ERR;
 232     jWindowNotifyFocus = env->GetMethodID(clazz, "notifyFocus", "(I)V");
 233     if (env->ExceptionCheck()) return JNI_ERR;
 234     jWindowNotifyFocusDisabled = env->GetMethodID(clazz, "notifyFocusDisabled", "()V");
 235     if (env->ExceptionCheck()) return JNI_ERR;
 236     jWindowNotifyFocusUngrab = env->GetMethodID(clazz, "notifyFocusUngrab", "()V");
 237     if (env->ExceptionCheck()) return JNI_ERR;
 238     jWindowNotifyMoveToAnotherScreen = env->GetMethodID(clazz, "notifyMoveToAnotherScreen", "(Lcom/sun/glass/ui/Screen;)V");
 239     if (env->ExceptionCheck()) return JNI_ERR;
 240     jWindowNotifyLevelChanged = env->GetMethodID(clazz, "notifyLevelChanged", "(I)V");
 241     if (env->ExceptionCheck()) return JNI_ERR;
 242     jWindowIsEnabled = env->GetMethodID(clazz, "isEnabled", "()Z");
 243     if (env->ExceptionCheck()) return JNI_ERR;
 244     jWindowNotifyDelegatePtr = env->GetMethodID(clazz, "notifyDelegatePtr", "(J)V");
 245     if (env->ExceptionCheck()) return JNI_ERR;
 246     jWindowPtr = env->GetFieldID(clazz, "ptr", "J");
 247     if (env->ExceptionCheck()) return JNI_ERR;
 248 
 249     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkWindow");
 250     if (env->ExceptionCheck()) return JNI_ERR;
 251     jGtkWindowNotifyStateChanged =
 252             env->GetMethodID(clazz, "notifyStateChanged", "(I)V");
 253     if (env->ExceptionCheck()) return JNI_ERR;
 254 
 255     clazz = env->FindClass("com/sun/glass/ui/Clipboard");
 256     if (env->ExceptionCheck()) return JNI_ERR;
 257     jClipboardContentChanged = env->GetMethodID(clazz, "contentChanged", "()V");
 258     if (env->ExceptionCheck()) return JNI_ERR;
 259 
 260     clazz = env->FindClass("com/sun/glass/ui/Cursor");
 261     if (env->ExceptionCheck()) return JNI_ERR;
 262     jCursorPtr = env->GetFieldID(clazz, "ptr", "J");
 263     if (env->ExceptionCheck()) return JNI_ERR;
 264 
 265     clazz = env->FindClass("com/sun/glass/ui/Size");
 266     if (env->ExceptionCheck()) return JNI_ERR;
 267     jSizeInit = env->GetMethodID(clazz, "<init>", "(II)V");
 268     if (env->ExceptionCheck()) return JNI_ERR;
 269 
 270     clazz = env->FindClass("java/util/Map");
 271     if (env->ExceptionCheck()) return JNI_ERR;
 272     jMapGet = env->GetMethodID(clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
 273     if (env->ExceptionCheck()) return JNI_ERR;
 274     jMapKeySet = env->GetMethodID(clazz, "keySet", "()Ljava/util/Set;");
 275     if (env->ExceptionCheck()) return JNI_ERR;
 276     jMapContainsKey = env->GetMethodID(clazz, "containsKey", "(Ljava/lang/Object;)Z");
 277     if (env->ExceptionCheck()) return JNI_ERR;
 278 
 279     clazz = env->FindClass("java/util/HashSet");
 280     if (env->ExceptionCheck()) return JNI_ERR;
 281     jHashSetCls = (jclass) env->NewGlobalRef(clazz);
 282     jHashSetInit = env->GetMethodID(jHashSetCls, "<init>", "()V");
 283     if (env->ExceptionCheck()) return JNI_ERR;
 284 
 285     clazz = env->FindClass("java/util/Set");
 286     if (env->ExceptionCheck()) return JNI_ERR;
 287     jSetAdd = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");
 288     if (env->ExceptionCheck()) return JNI_ERR;
 289     jSetSize = env->GetMethodID(clazz, "size", "()I");
 290     if (env->ExceptionCheck()) return JNI_ERR;
 291     jSetToArray = env->GetMethodID(clazz, "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;");
 292     if (env->ExceptionCheck()) return JNI_ERR;
 293 
 294     clazz = env->FindClass("java/lang/Iterable");
 295     if (env->ExceptionCheck()) return JNI_ERR;
 296     jIterableIterator = env->GetMethodID(clazz, "iterator", "()Ljava/util/Iterator;");
 297     if (env->ExceptionCheck()) return JNI_ERR;
 298 
 299     clazz = env->FindClass("java/util/Iterator");
 300     if (env->ExceptionCheck()) return JNI_ERR;
 301     jIteratorHasNext = env->GetMethodID(clazz, "hasNext", "()Z");
 302     if (env->ExceptionCheck()) return JNI_ERR;
 303     jIteratorNext = env->GetMethodID(clazz, "next", "()Ljava/lang/Object;");
 304     if (env->ExceptionCheck()) return JNI_ERR;
 305 
 306     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkApplication");
 307     if (env->ExceptionCheck()) return JNI_ERR;
 308     jApplicationCls = (jclass) env->NewGlobalRef(clazz);
 309     jApplicationDisplay = env->GetStaticFieldID(jApplicationCls, "display", "J");
 310     if (env->ExceptionCheck()) return JNI_ERR;
 311     jApplicationScreen = env->GetStaticFieldID(jApplicationCls, "screen", "I");
 312     if (env->ExceptionCheck()) return JNI_ERR;
 313     jApplicationVisualID = env->GetStaticFieldID(jApplicationCls, "visualID", "J");
 314     if (env->ExceptionCheck()) return JNI_ERR;
 315     jApplicationReportException = env->GetStaticMethodID(
 316         jApplicationCls, "reportException", "(Ljava/lang/Throwable;)V");
 317     if (env->ExceptionCheck()) return JNI_ERR;
 318     jApplicationGetApplication = env->GetStaticMethodID(
 319         jApplicationCls, "GetApplication", "()Lcom/sun/glass/ui/Application;");
 320     if (env->ExceptionCheck()) return JNI_ERR;
 321     jApplicationGetName = env->GetMethodID(jApplicationCls, "getName", "()Ljava/lang/String;");
 322     if (env->ExceptionCheck()) return JNI_ERR;
 323 
 324     return JNI_VERSION_1_6;
 325 }
 326 
 327 void
 328 glass_throw_exception(JNIEnv * env,
 329                       const char * exceptionClass,
 330                       const char * exceptionMessage) {
 331     jclass throwableClass = env->FindClass(exceptionClass);
 332     if (check_and_clear_exception(env)) return;
 333     env->ThrowNew(throwableClass, exceptionMessage);
 334     check_and_clear_exception(env);
 335 }
 336 
 337 int
 338 glass_throw_oom(JNIEnv * env, const char * message) {
 339     glass_throw_exception(env, "java/lang/OutOfMemoryError", message);
 340     // must return a non-zero value, see HANDLE_MEM_ALLOC_ERROR
 341     return 1;
 342 }
 343 
 344 
 345 guint8* convert_BGRA_to_RGBA(const int* pixels, int stride, int height) {
 346   guint8* new_pixels = (guint8*) g_malloc(height * stride);
 347   int i = 0;
 348 
 349   for (i = 0; i < height * stride; i += 4) {
 350       new_pixels[i] = (guint8)(*pixels >> 16);
 351       new_pixels[i + 1] = (guint8)(*pixels >> 8);
 352       new_pixels[i + 2] = (guint8)(*pixels);
 353       new_pixels[i + 3] = (guint8)(*pixels >> 24);
 354       pixels++;
 355   }
 356 
 357   return new_pixels;
 358 }
 359 
 360 
 361 void dump_jstring_array(JNIEnv* env, jobjectArray arr) {
 362     if (arr == NULL) {
 363         LOG0("dump: Array is null\n")
 364         return;
 365     }
 366     jsize len = env->GetArrayLength(arr);
 367     LOG1("dump: length = %d\n", len)
 368     int i = 0;
 369     jboolean isCopy;
 370     for(i = 0; i < len; i++) {
 371         jstring jstr = (jstring) env->GetObjectArrayElement(arr, i);
 372         check_and_clear_exception(env);
 373         const char* str = env->GetStringUTFChars(jstr, &isCopy);
 374         LOG2("dump: s[%d]: %s\n", i, str)
 375     }
 376 }
 377 
 378 gboolean check_and_clear_exception(JNIEnv *env) {
 379     jthrowable t = env->ExceptionOccurred();
 380     if (t) {
 381         env->ExceptionClear();
 382         env->CallStaticVoidMethod(jApplicationCls, jApplicationReportException, t);
 383         //Clear in case our reporting upcall failed too!
 384         env->ExceptionClear();
 385         return TRUE;
 386     }
 387     return FALSE;
 388 }
 389 
 390 // The returned string should be freed with g_free().
 391 gchar* get_application_name() {
 392     gchar* ret = NULL;
 393 
 394     jobject japp = mainEnv->CallStaticObjectMethod(jApplicationCls, jApplicationGetApplication);
 395     CHECK_JNI_EXCEPTION_RET(mainEnv, NULL);
 396     jstring jname = (jstring) mainEnv->CallObjectMethod(japp, jApplicationGetName);
 397     CHECK_JNI_EXCEPTION_RET(mainEnv, NULL);
 398     if (const gchar *name = mainEnv->GetStringUTFChars(jname, NULL)) {
 399         ret = g_strdup(name);
 400         mainEnv->ReleaseStringUTFChars(jname, name);
 401     }
 402     return ret;
 403 }
 404 
 405 gpointer glass_try_malloc_n(gsize m, gsize n,
 406         gboolean zer0 /* initialized to 0 if true*/) {
 407     if (n > 0 && m > G_MAXSIZE / n) {
 408         return NULL;
 409     }
 410     return (zer0)
 411             ? g_try_malloc0(m * n)
 412             : g_try_malloc(m * n);
 413 }
 414 
 415 /*
 416  * Since we support glib 2.18 we can't use g_try_malloc_n and g_try_malloc0_n
 417  * which was introduced in 2.24.
 418  * glass_try_malloc_n and glass_try_malloc0_n is replacement for those functions
 419  */
 420 gpointer glass_try_malloc0_n(gsize m, gsize n) {
 421     return glass_try_malloc_n(m, n, TRUE);
 422 }
 423 
 424 gpointer glass_try_malloc_n(gsize m, gsize n) {
 425     return glass_try_malloc_n(m, n, FALSE);
 426 }
 427 
 428 gsize get_files_count(gchar **uris) {
 429     if (!uris) {
 430         return 0;
 431     }
 432 
 433     guint size = g_strv_length(uris);
 434     guint files_cnt = 0;
 435 
 436     for (guint i = 0; i < size; ++i) {
 437         if (g_str_has_prefix(uris[i], FILE_PREFIX)) {
 438             files_cnt++;
 439         }
 440     }
 441     return files_cnt;
 442 }
 443 
 444 // Note: passed uris will be freed by this function
 445 jobject uris_to_java(JNIEnv *env, gchar **uris, gboolean files) {
 446     if (uris == NULL) {
 447         return NULL;
 448     }
 449 
 450     jobject result = NULL;
 451 
 452     guint size = g_strv_length(uris);
 453     guint files_cnt = get_files_count(uris);
 454 
 455     if (files) {
 456         if (files_cnt) {
 457             result = env->NewObjectArray(files_cnt, jStringCls, NULL);
 458             check_and_clear_exception(env);
 459 
 460             for (gsize i = 0; i < size; ++i) {
 461                 if (g_str_has_prefix(uris[i], FILE_PREFIX)) {
 462                     gchar* path = g_filename_from_uri(uris[i], NULL, NULL);
 463                     jstring str = env->NewStringUTF(path);
 464                     check_and_clear_exception(env);
 465                     env->SetObjectArrayElement((jobjectArray) result, i, str);
 466                     check_and_clear_exception(env);
 467                     g_free(path);
 468                 }
 469             }
 470         }
 471     } else if (size - files_cnt) {
 472         GString* str = g_string_new(NULL); //http://www.ietf.org/rfc/rfc2483.txt
 473 
 474         for (guint i = 0; i < size; ++i) {
 475             if (!g_str_has_prefix(uris[i], FILE_PREFIX)
 476                     && !g_str_has_prefix(uris[i], URI_LIST_COMMENT_PREFIX)) {
 477                 g_string_append(str, uris[i]);
 478                 g_string_append(str, URI_LIST_LINE_BREAK);
 479             }
 480         }
 481 
 482         if (str->len > 2) {
 483             g_string_erase(str, str->len - 2, 2);
 484         }
 485 
 486         result = env->NewStringUTF(str->str);
 487         check_and_clear_exception(env);
 488 
 489         g_string_free(str, TRUE);
 490     }
 491     g_strfreev(uris);
 492     return result;
 493 }
 494 
 495 //***************************************************************************
 496 
 497 typedef struct _DeviceGrabContext {
 498     GdkWindow * window;
 499     gboolean grabbed;
 500 } DeviceGrabContext;
 501 
 502 gboolean disableGrab = FALSE;
 503 static gboolean configure_transparent_window(GtkWidget *window);
 504 static void configure_opaque_window(GtkWidget *window);
 505 
 506 static void grab_mouse_device(GdkDevice *device, DeviceGrabContext *context);
 507 static void ungrab_mouse_device(GdkDevice *device);
 508 
 509 gint glass_gdk_visual_get_depth (GdkVisual * visual)
 510 {
 511     // gdk_visual_get_depth is GTK 2.2 +
 512     return gdk_visual_get_depth(visual);
 513 }
 514 
 515 GdkScreen * glass_gdk_window_get_screen(GdkWindow * gdkWindow)
 516 {
 517 #ifdef GLASS_GTK3
 518         GdkVisual * gdkVisual = gdk_window_get_visual(gdkWindow);
 519         return gdk_visual_get_screen(gdkVisual);
 520 #else
 521         return gdk_window_get_screen(gdkWindow);
 522 #endif
 523 }
 524 
 525 gboolean
 526 glass_gdk_mouse_devices_grab(GdkWindow *gdkWindow) {
 527 #ifdef GLASS_GTK3
 528         if (disableGrab) {
 529             return TRUE;
 530         }
 531         DeviceGrabContext context;
 532         GList *devices = gdk_device_manager_list_devices (
 533                              gdk_display_get_device_manager(
 534                                  gdk_display_get_default()),
 535                                  GDK_DEVICE_TYPE_MASTER);
 536 
 537         context.window = gdkWindow;
 538         context.grabbed = FALSE;
 539         g_list_foreach(devices, (GFunc) grab_mouse_device, &context);
 540 
 541         return context.grabbed;
 542 #else
 543     return glass_gdk_mouse_devices_grab_with_cursor(gdkWindow, NULL, TRUE);
 544 #endif
 545 }
 546 
 547 gboolean
 548 glass_gdk_mouse_devices_grab_with_cursor(GdkWindow *gdkWindow, GdkCursor *cursor, gboolean owner_events) {
 549     if (disableGrab) {
 550         return TRUE;
 551     }
 552     GdkGrabStatus status = gdk_pointer_grab(gdkWindow, owner_events, (GdkEventMask)
 553                                             (GDK_POINTER_MOTION_MASK
 554                                                 | GDK_POINTER_MOTION_HINT_MASK
 555                                                 | GDK_BUTTON_MOTION_MASK
 556                                                 | GDK_BUTTON1_MOTION_MASK
 557                                                 | GDK_BUTTON2_MOTION_MASK
 558                                                 | GDK_BUTTON3_MOTION_MASK
 559                                                 | GDK_BUTTON_PRESS_MASK
 560                                                 | GDK_BUTTON_RELEASE_MASK),
 561                                             NULL, cursor, GDK_CURRENT_TIME);
 562 
 563     return (status == GDK_GRAB_SUCCESS) ? TRUE : FALSE;
 564 }
 565 
 566 void
 567 glass_gdk_mouse_devices_ungrab() {
 568 #ifdef GLASS_GTK3
 569         GList *devices = gdk_device_manager_list_devices(
 570                              gdk_display_get_device_manager(
 571                                  gdk_display_get_default()),
 572                                  GDK_DEVICE_TYPE_MASTER);
 573         g_list_foreach(devices, (GFunc) ungrab_mouse_device, NULL);
 574 #else
 575         gdk_pointer_ungrab(GDK_CURRENT_TIME);
 576 #endif
 577 }
 578 
 579 void
 580 glass_gdk_master_pointer_grab(GdkWindow *window, GdkCursor *cursor) {
 581     if (disableGrab) {
 582         gdk_window_set_cursor(window, cursor);
 583         return;
 584     }
 585 #ifdef GLASS_GTK3
 586         gdk_device_grab(gdk_device_manager_get_client_pointer(
 587                     gdk_display_get_device_manager(
 588                         gdk_display_get_default())),
 589                     window, GDK_OWNERSHIP_NONE, FALSE, GDK_ALL_EVENTS_MASK,
 590                     cursor, GDK_CURRENT_TIME);
 591 #else
 592         gdk_pointer_grab(window, FALSE, (GdkEventMask)
 593                          (GDK_POINTER_MOTION_MASK
 594                              | GDK_BUTTON_MOTION_MASK
 595                              | GDK_BUTTON1_MOTION_MASK
 596                              | GDK_BUTTON2_MOTION_MASK
 597                              | GDK_BUTTON3_MOTION_MASK
 598                              | GDK_BUTTON_RELEASE_MASK),
 599                          NULL, cursor, GDK_CURRENT_TIME);
 600 #endif
 601 }
 602 
 603 void
 604 glass_gdk_master_pointer_ungrab() {
 605 #ifdef GLASS_GTK3
 606         gdk_device_ungrab(gdk_device_manager_get_client_pointer(
 607                               gdk_display_get_device_manager(
 608                                   gdk_display_get_default())),
 609                           GDK_CURRENT_TIME);
 610 #else
 611         gdk_pointer_ungrab(GDK_CURRENT_TIME);
 612 #endif
 613 }
 614 
 615 void
 616 glass_gdk_master_pointer_get_position(gint *x, gint *y) {
 617 #ifdef GLASS_GTK3
 618         gdk_device_get_position(gdk_device_manager_get_client_pointer(
 619                                     gdk_display_get_device_manager(
 620                                         gdk_display_get_default())),
 621                                 NULL, x, y);
 622 #else
 623         gdk_display_get_pointer(gdk_display_get_default(), NULL, x, y, NULL);
 624 #endif
 625 }
 626 
 627 gboolean
 628 glass_gdk_device_is_grabbed(GdkDevice *device) {
 629 #ifdef GLASS_GTK3
 630         return gdk_display_device_is_grabbed(gdk_display_get_default(), device);
 631 #else
 632         (void) device;
 633         return gdk_display_pointer_is_grabbed(gdk_display_get_default());
 634 #endif
 635 }
 636 
 637 void
 638 glass_gdk_device_ungrab(GdkDevice *device) {
 639 #ifdef GLASS_GTK3
 640         gdk_device_ungrab(device, GDK_CURRENT_TIME);
 641 #else
 642         (void) device;
 643         gdk_pointer_ungrab(GDK_CURRENT_TIME);
 644 #endif
 645 }
 646 
 647 GdkWindow *
 648 glass_gdk_device_get_window_at_position(GdkDevice *device, gint *x, gint *y) {
 649 #ifdef GLASS_GTK3
 650         return gdk_device_get_window_at_position(device, x, y);
 651 #else
 652         (void) device;
 653         return gdk_display_get_window_at_pointer(gdk_display_get_default(), x, y);
 654 #endif
 655 }
 656 
 657 void
 658 glass_gtk_configure_transparency_and_realize(GtkWidget *window,
 659                                              gboolean transparent) {
 660         gboolean isTransparent = glass_configure_window_transparency(window, transparent);
 661         gtk_widget_realize(window);
 662 }
 663 
 664 void
 665 glass_gtk_window_configure_from_visual(GtkWidget *widget, GdkVisual *visual) {
 666     glass_widget_set_visual(widget, visual);
 667 }
 668 
 669 static gboolean
 670 configure_transparent_window(GtkWidget *window) {
 671     GdkScreen *default_screen = gdk_screen_get_default();
 672     GdkDisplay *default_display = gdk_display_get_default();
 673 
 674 #ifdef GLASS_GTK3
 675         GdkVisual *visual = gdk_screen_get_rgba_visual(default_screen);
 676         if (visual
 677                 && gdk_display_supports_composite(default_display)
 678                 && gdk_screen_is_composited(default_screen)) {
 679             glass_widget_set_visual(window, visual);
 680             return TRUE;
 681         }
 682 #else
 683         GdkColormap *colormap = gdk_screen_get_rgba_colormap(default_screen);
 684         if (colormap
 685                 && gdk_display_supports_composite(default_display)
 686                 && gdk_screen_is_composited(default_screen)) {
 687             gtk_widget_set_colormap(window, colormap);
 688             return TRUE;
 689         }
 690 #endif
 691 
 692     return FALSE;
 693 }
 694 
 695 void
 696 glass_gdk_window_get_size(GdkWindow *window, gint *w, gint *h) {
 697     *w = gdk_window_get_width(window);
 698     *h = gdk_window_get_height(window);
 699 }
 700 
 701 void
 702 glass_gdk_display_get_pointer(GdkDisplay* display, gint* x, gint *y) {
 703 #ifdef GLASS_GTK3
 704         gdk_device_get_position(
 705             gdk_device_manager_get_client_pointer(
 706                 gdk_display_get_device_manager(display)), NULL , x, y);
 707 #else
 708         gdk_display_get_pointer(display, NULL, x, y, NULL);
 709 #endif
 710 }
 711 
 712 
 713 const guchar*
 714 glass_gtk_selection_data_get_data_with_length(
 715         GtkSelectionData * selectionData,
 716         gint * length) {
 717     if (selectionData == NULL) {
 718         return NULL;
 719     }
 720 
 721     *length = gtk_selection_data_get_length(selectionData);
 722     return gtk_selection_data_get_data(selectionData);
 723 }
 724 
 725 static void
 726 configure_opaque_window(GtkWidget *window) {
 727     (void) window;
 728 /* We need to pick a visual that really is glx compatible
 729  * instead of using the default visual
 730  */
 731  /* see: JDK-8087516 for why this is commented out
 732     glass_widget_set_visual(window,
 733                           gdk_screen_get_system_visual(
 734                               gdk_screen_get_default()));
 735   */
 736 }
 737 
 738 gboolean
 739 glass_configure_window_transparency(GtkWidget *window, gboolean transparent) {
 740     if (transparent) {
 741         if (configure_transparent_window(window)) {
 742             return TRUE;
 743         }
 744 
 745         fprintf(stderr,"Can't create transparent stage, because your screen doesn't"
 746                " support alpha channel."
 747                " You need to enable XComposite extension.\n");
 748         fflush(stderr);
 749     }
 750 
 751     configure_opaque_window(window);
 752     return FALSE;
 753 }
 754 
 755 static void
 756 grab_mouse_device(GdkDevice *device, DeviceGrabContext *context) {
 757     GdkInputSource source = gdk_device_get_source(device);
 758     if (source == GDK_SOURCE_MOUSE) {
 759 #ifdef GLASS_GTK3
 760         GdkGrabStatus status = gdk_device_grab(device,
 761                                                context->window,
 762                                                GDK_OWNERSHIP_NONE,
 763                                                TRUE,
 764                                                GDK_ALL_EVENTS_MASK,
 765                                                NULL,
 766                                                GDK_CURRENT_TIME);
 767 #else
 768         GdkGrabStatus status = GDK_GRAB_SUCCESS;
 769 /* FIXME reachable by 2?
 770         GdkGrabStatus status = gdk_device_grab(device,
 771                                                context->window,
 772                                                GDK_OWNERSHIP_NONE,
 773                                                TRUE,
 774                                                GDK_ALL_EVENTS_MASK,
 775                                                NULL,
 776                                                GDK_CURRENT_TIME);
 777                                        */
 778 #endif
 779         if (status == GDK_GRAB_SUCCESS) {
 780             context->grabbed = TRUE;
 781         }
 782     }
 783 }
 784 
 785 static void
 786 ungrab_mouse_device(GdkDevice *device) {
 787 #ifdef GLASS_GTK3
 788     GdkInputSource source = gdk_device_get_source(device);
 789     if (source == GDK_SOURCE_MOUSE) {
 790         gdk_device_ungrab(device, GDK_CURRENT_TIME);
 791     }
 792 #else
 793     (void) device;
 794     // not used on the GTK2 path
 795 #endif
 796 }
 797 
 798 GdkPixbuf *
 799 glass_pixbuf_from_window(GdkWindow *window,
 800     gint srcx, gint srcy,
 801     gint width, gint height)
 802 {
 803     GdkPixbuf * ret = NULL;
 804 
 805 #ifdef GLASS_GTK3
 806         ret = gdk_pixbuf_get_from_window (window, srcx, srcy, width, height);
 807 #else
 808         ret = gdk_pixbuf_get_from_drawable (NULL,
 809             window,
 810             NULL,
 811             srcx, srcy,
 812             0, 0,
 813             width, height);
 814 #endif
 815     return ret;
 816 }
 817 
 818 void
 819 glass_window_apply_shape_mask(GdkWindow *window,
 820     void* data, uint width, uint height)
 821 {
 822 #ifdef GLASS_GTK3
 823     (void) window;
 824     (void) data;
 825     (void) width;
 826     (void) height;
 827 #else
 828         GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data((guchar *) data,
 829                 GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * 4, NULL, NULL);
 830 
 831         if (GDK_IS_PIXBUF(pixbuf)) {
 832             GdkBitmap* mask = NULL;
 833             gdk_pixbuf_render_pixmap_and_mask(pixbuf, NULL, &mask, 128);
 834 
 835             gdk_window_input_shape_combine_mask(window, mask, 0, 0);
 836 
 837             g_object_unref(pixbuf);
 838             if (mask) {
 839                 g_object_unref(mask);
 840             }
 841         }
 842 #endif
 843 }
 844 
 845 void
 846 glass_window_reset_input_shape_mask(GdkWindow *window)
 847 {
 848 #ifdef GLASS_GTK3
 849         gdk_window_input_shape_combine_region(window, NULL, 0, 0);
 850 #else
 851         gdk_window_input_shape_combine_mask(window, NULL, 0, 0);
 852 #endif
 853 }
 854 
 855 GdkWindow *
 856 glass_gdk_drag_context_get_dest_window (GdkDragContext * context)
 857 {
 858     return ((context != NULL) ? gdk_drag_context_get_dest_window(context) : NULL);
 859 }
 860 
 861 
 862 void glass_gdk_x11_display_set_window_scale (GdkDisplay *display,
 863                           gint scale)
 864 {
 865 #ifdef GLASS_GTK3
 866     // Optional call, if it does not exist then GTK3 is not yet
 867     // doing automatic scaling of coordinates so we do not need
 868     // to override it.
 869     wrapped_gdk_x11_display_set_window_scale(display, scale);
 870 #else
 871     (void) display;
 872     (void) scale;
 873 #endif
 874 }
 875 
 876 //-------- Glass utility ----------------------------------------
 877 
 878 void
 879 glass_widget_set_visual(GtkWidget *widget, GdkVisual *visual)
 880 {
 881 #ifdef GLASS_GTK3
 882         gtk_widget_set_visual (widget, visual);
 883 #else
 884         GdkColormap *colormap = gdk_colormap_new(visual, TRUE);
 885         gtk_widget_set_colormap (widget, colormap);
 886 #endif
 887 }
 888 
 889 guint glass_settings_get_guint_opt (const gchar *schema_name,
 890                     const gchar *key_name,
 891                     int defval)
 892 {
 893     GSettingsSchemaSource *default_schema_source =
 894             wrapped_g_settings_schema_source_get_default();
 895     if (default_schema_source == NULL) {
 896         if (gtk_verbose) {
 897             fprintf(stderr, "No schema source dir found!\n");
 898         }
 899         return defval;
 900     }
 901     GSettingsSchema *the_schema =
 902             wrapped_g_settings_schema_source_lookup(default_schema_source, schema_name, TRUE);
 903     if (the_schema == NULL) {
 904         if (gtk_verbose) {
 905             fprintf(stderr, "schema '%s' not found!\n", schema_name);
 906         }
 907         return defval;
 908     }
 909     if (!wrapped_g_settings_schema_has_key(the_schema, key_name)) {
 910         if (gtk_verbose) {
 911             fprintf(stderr, "key '%s' not found in schema '%s'!\n", key_name, schema_name);
 912         }
 913         return defval;
 914     }
 915     if (gtk_verbose) {
 916         fprintf(stderr, "found schema '%s' and key '%s'\n", schema_name, key_name);
 917     }
 918 
 919     GSettings *gset = g_settings_new(schema_name);
 920 
 921     wrapped_g_settings_schema_unref(the_schema);
 922 
 923     return g_settings_get_uint(gset, key_name);
 924 }