1 /*
   2  * Copyright (c) 2011, 2015, 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 #pragma GCC diagnostic push
 115 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 116 void init_threads() {
 117     gboolean is_g_thread_get_initialized = FALSE;
 118     if (glib_check_version(2, 32, 0)) { // < 2.32
 119         if (!glib_check_version(2, 20, 0)) {
 120             is_g_thread_get_initialized = g_thread_get_initialized();
 121         }
 122         if (!is_g_thread_get_initialized) {
 123             // Calling g_thread_init() multiple times leads to crash on GLib < 2.24
 124             // We can use g_thread_get_initialized () but it is available only for
 125             // GLib >= 2.20. We rely on GThreadHelper for GLib < 2.20.
 126             // g_thread_init is no longer necessary for GLib >=2.32
 127             g_thread_init(NULL);
 128         }
 129     }
 130     gdk_threads_init();
 131 }
 132 #pragma GCC diagnostic pop
 133 
 134 static jboolean displayValid = JNI_FALSE;
 135 
 136 jboolean
 137 is_display_valid() {
 138     return displayValid;
 139 }
 140 
 141 JNIEXPORT jint JNICALL
 142 JNI_OnLoad(JavaVM *jvm, void *reserved)
 143 {
 144     (void)reserved;
 145 
 146     JNIEnv *env;
 147     jclass clazz;
 148     Display* display;
 149 
 150     if (jvm->GetEnv((void **)&env, JNI_VERSION_1_6)) {
 151          return JNI_ERR; /* JNI version not supported */
 152      }
 153 
 154     clazz = env->FindClass("java/lang/String");
 155     if (env->ExceptionCheck()) return JNI_ERR;
 156     jStringCls = (jclass) env->NewGlobalRef(clazz);
 157 
 158     clazz = env->FindClass("java/nio/ByteBuffer");
 159     if (env->ExceptionCheck()) return JNI_ERR;
 160     jByteBufferCls = (jclass) env->NewGlobalRef(clazz);
 161     jByteBufferArray = env->GetMethodID(jByteBufferCls, "array", "()[B");
 162     if (env->ExceptionCheck()) return JNI_ERR;
 163     jByteBufferWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
 164     if (env->ExceptionCheck()) return JNI_ERR;
 165 
 166     clazz = env->FindClass("java/lang/Runnable");
 167     if (env->ExceptionCheck()) return JNI_ERR;
 168 
 169     jRunnableRun = env->GetMethodID(clazz, "run", "()V");
 170     if (env->ExceptionCheck()) return JNI_ERR;
 171 
 172     clazz = env->FindClass("java/util/ArrayList");
 173     if (env->ExceptionCheck()) return JNI_ERR;
 174     jArrayListCls = (jclass) env->NewGlobalRef(clazz);
 175     jArrayListInit = env->GetMethodID(jArrayListCls, "<init>", "()V");
 176     if (env->ExceptionCheck()) return JNI_ERR;
 177     jArrayListAdd = env->GetMethodID(jArrayListCls, "add", "(Ljava/lang/Object;)Z");
 178     if (env->ExceptionCheck()) return JNI_ERR;
 179     jArrayListGetIdx = env->GetMethodID(jArrayListCls, "get", "(I)Ljava/lang/Object;");
 180     if (env->ExceptionCheck()) return JNI_ERR;
 181     clazz = env->FindClass("com/sun/glass/ui/Pixels");
 182     if (env->ExceptionCheck()) return JNI_ERR;
 183     jPixelsAttachData = env->GetMethodID(clazz, "attachData", "(J)V");
 184     if (env->ExceptionCheck()) return JNI_ERR;
 185 
 186     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkPixels");
 187     if (env->ExceptionCheck()) return JNI_ERR;
 188 
 189     jGtkPixelsCls = (jclass) env->NewGlobalRef(clazz);
 190     jGtkPixelsInit = env->GetMethodID(jGtkPixelsCls, "<init>", "(IILjava/nio/ByteBuffer;)V");
 191     if (env->ExceptionCheck()) return JNI_ERR;
 192 
 193     clazz = env->FindClass("com/sun/glass/ui/Screen");
 194     if (env->ExceptionCheck()) return JNI_ERR;
 195     jScreenCls = (jclass) env->NewGlobalRef(clazz);
 196     jScreenInit = env->GetMethodID(jScreenCls, "<init>", "(JIIIIIIIIIIIF)V");
 197     if (env->ExceptionCheck()) return JNI_ERR;
 198     jScreenNotifySettingsChanged = env->GetStaticMethodID(jScreenCls, "notifySettingsChanged", "()V");
 199     if (env->ExceptionCheck()) return JNI_ERR;
 200 
 201     clazz = env->FindClass("com/sun/glass/ui/View");
 202     if (env->ExceptionCheck()) return JNI_ERR;
 203     jViewNotifyResize = env->GetMethodID(clazz, "notifyResize", "(II)V");
 204     if (env->ExceptionCheck()) return JNI_ERR;
 205     jViewNotifyMouse = env->GetMethodID(clazz, "notifyMouse", "(IIIIIIIZZ)V");
 206     if (env->ExceptionCheck()) return JNI_ERR;
 207     jViewNotifyRepaint = env->GetMethodID(clazz, "notifyRepaint", "(IIII)V");
 208     if (env->ExceptionCheck()) return JNI_ERR;
 209     jViewNotifyKey = env->GetMethodID(clazz, "notifyKey", "(II[CI)V");
 210     if (env->ExceptionCheck()) return JNI_ERR;
 211     jViewNotifyView = env->GetMethodID(clazz, "notifyView", "(I)V");
 212     if (env->ExceptionCheck()) return JNI_ERR;
 213     jViewNotifyDragEnter = env->GetMethodID(clazz, "notifyDragEnter", "(IIIII)I");
 214     if (env->ExceptionCheck()) return JNI_ERR;
 215     jViewNotifyDragOver = env->GetMethodID(clazz, "notifyDragOver", "(IIIII)I");
 216     if (env->ExceptionCheck()) return JNI_ERR;
 217     jViewNotifyDragDrop = env->GetMethodID(clazz, "notifyDragDrop", "(IIIII)I");
 218     if (env->ExceptionCheck()) return JNI_ERR;
 219     jViewNotifyDragLeave = env->GetMethodID(clazz, "notifyDragLeave", "()V");
 220     if (env->ExceptionCheck()) return JNI_ERR;
 221     jViewNotifyScroll = env->GetMethodID(clazz, "notifyScroll", "(IIIIDDIIIIIDD)V");
 222     if (env->ExceptionCheck()) return JNI_ERR;
 223     jViewNotifyInputMethod = env->GetMethodID(clazz, "notifyInputMethod", "(Ljava/lang/String;[I[I[BIII)V");
 224     if (env->ExceptionCheck()) return JNI_ERR;
 225     jViewNotifyMenu = env->GetMethodID(clazz, "notifyMenu", "(IIIIZ)V");
 226     if (env->ExceptionCheck()) return JNI_ERR;
 227     jViewPtr = env->GetFieldID(clazz, "ptr", "J");
 228     if (env->ExceptionCheck()) return JNI_ERR;
 229 
 230     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkView");
 231     if (env->ExceptionCheck()) return JNI_ERR;
 232     jViewNotifyInputMethodDraw = env->GetMethodID(clazz, "notifyInputMethodDraw", "(Ljava/lang/String;III[B)V");
 233     if (env->ExceptionCheck()) return JNI_ERR;
 234     jViewNotifyInputMethodCaret = env->GetMethodID(clazz, "notifyInputMethodCaret", "(III)V");
 235     if (env->ExceptionCheck()) return JNI_ERR;
 236     jViewNotifyPreeditMode = env->GetMethodID(clazz, "notifyPreeditMode", "(Z)V");
 237     if (env->ExceptionCheck()) return JNI_ERR;
 238 
 239     clazz = env->FindClass("com/sun/glass/ui/Window");
 240     if (env->ExceptionCheck()) return JNI_ERR;
 241     jWindowNotifyResize = env->GetMethodID(clazz, "notifyResize", "(III)V");
 242     if (env->ExceptionCheck()) return JNI_ERR;
 243     jWindowNotifyMove = env->GetMethodID(clazz, "notifyMove", "(II)V");
 244     if (env->ExceptionCheck()) return JNI_ERR;
 245     jWindowNotifyDestroy = env->GetMethodID(clazz, "notifyDestroy", "()V");
 246     if (env->ExceptionCheck()) return JNI_ERR;
 247     jWindowNotifyClose = env->GetMethodID(clazz, "notifyClose", "()V");
 248     if (env->ExceptionCheck()) return JNI_ERR;
 249     jWindowNotifyFocus = env->GetMethodID(clazz, "notifyFocus", "(I)V");
 250     if (env->ExceptionCheck()) return JNI_ERR;
 251     jWindowNotifyFocusDisabled = env->GetMethodID(clazz, "notifyFocusDisabled", "()V");
 252     if (env->ExceptionCheck()) return JNI_ERR;
 253     jWindowNotifyFocusUngrab = env->GetMethodID(clazz, "notifyFocusUngrab", "()V");
 254     if (env->ExceptionCheck()) return JNI_ERR;
 255     jWindowNotifyMoveToAnotherScreen = env->GetMethodID(clazz, "notifyMoveToAnotherScreen", "(Lcom/sun/glass/ui/Screen;)V");
 256     if (env->ExceptionCheck()) return JNI_ERR;
 257     jWindowNotifyLevelChanged = env->GetMethodID(clazz, "notifyLevelChanged", "(I)V");
 258     if (env->ExceptionCheck()) return JNI_ERR;
 259     jWindowIsEnabled = env->GetMethodID(clazz, "isEnabled", "()Z");
 260     if (env->ExceptionCheck()) return JNI_ERR;
 261     jWindowNotifyDelegatePtr = env->GetMethodID(clazz, "notifyDelegatePtr", "(J)V");
 262     if (env->ExceptionCheck()) return JNI_ERR;
 263     jWindowPtr = env->GetFieldID(clazz, "ptr", "J");
 264     if (env->ExceptionCheck()) return JNI_ERR;
 265 
 266     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkWindow");
 267     if (env->ExceptionCheck()) return JNI_ERR;
 268     jGtkWindowNotifyStateChanged =
 269             env->GetMethodID(clazz, "notifyStateChanged", "(I)V");
 270     if (env->ExceptionCheck()) return JNI_ERR;
 271 
 272     clazz = env->FindClass("com/sun/glass/ui/Clipboard");
 273     if (env->ExceptionCheck()) return JNI_ERR;
 274     jClipboardContentChanged = env->GetMethodID(clazz, "contentChanged", "()V");
 275     if (env->ExceptionCheck()) return JNI_ERR;
 276 
 277     clazz = env->FindClass("com/sun/glass/ui/Cursor");
 278     if (env->ExceptionCheck()) return JNI_ERR;
 279     jCursorPtr = env->GetFieldID(clazz, "ptr", "J");
 280     if (env->ExceptionCheck()) return JNI_ERR;
 281 
 282     clazz = env->FindClass("com/sun/glass/ui/Size");
 283     if (env->ExceptionCheck()) return JNI_ERR;
 284     jSizeInit = env->GetMethodID(clazz, "<init>", "(II)V");
 285     if (env->ExceptionCheck()) return JNI_ERR;
 286 
 287     clazz = env->FindClass("java/util/Map");
 288     if (env->ExceptionCheck()) return JNI_ERR;
 289     jMapGet = env->GetMethodID(clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
 290     if (env->ExceptionCheck()) return JNI_ERR;
 291     jMapKeySet = env->GetMethodID(clazz, "keySet", "()Ljava/util/Set;");
 292     if (env->ExceptionCheck()) return JNI_ERR;
 293     jMapContainsKey = env->GetMethodID(clazz, "containsKey", "(Ljava/lang/Object;)Z");
 294     if (env->ExceptionCheck()) return JNI_ERR;
 295 
 296     clazz = env->FindClass("java/util/HashSet");
 297     if (env->ExceptionCheck()) return JNI_ERR;
 298     jHashSetCls = (jclass) env->NewGlobalRef(clazz);
 299     jHashSetInit = env->GetMethodID(jHashSetCls, "<init>", "()V");
 300     if (env->ExceptionCheck()) return JNI_ERR;
 301 
 302     clazz = env->FindClass("java/util/Set");
 303     if (env->ExceptionCheck()) return JNI_ERR;
 304     jSetAdd = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");
 305     if (env->ExceptionCheck()) return JNI_ERR;
 306     jSetSize = env->GetMethodID(clazz, "size", "()I");
 307     if (env->ExceptionCheck()) return JNI_ERR;
 308     jSetToArray = env->GetMethodID(clazz, "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;");
 309     if (env->ExceptionCheck()) return JNI_ERR;
 310 
 311     clazz = env->FindClass("java/lang/Iterable");
 312     if (env->ExceptionCheck()) return JNI_ERR;
 313     jIterableIterator = env->GetMethodID(clazz, "iterator", "()Ljava/util/Iterator;");
 314     if (env->ExceptionCheck()) return JNI_ERR;
 315 
 316     clazz = env->FindClass("java/util/Iterator");
 317     if (env->ExceptionCheck()) return JNI_ERR;
 318     jIteratorHasNext = env->GetMethodID(clazz, "hasNext", "()Z");
 319     if (env->ExceptionCheck()) return JNI_ERR;
 320     jIteratorNext = env->GetMethodID(clazz, "next", "()Ljava/lang/Object;");
 321     if (env->ExceptionCheck()) return JNI_ERR;
 322 
 323     clazz = env->FindClass("com/sun/glass/ui/gtk/GtkApplication");
 324     if (env->ExceptionCheck()) return JNI_ERR;
 325     jApplicationCls = (jclass) env->NewGlobalRef(clazz);
 326     jApplicationDisplay = env->GetStaticFieldID(jApplicationCls, "display", "J");
 327     if (env->ExceptionCheck()) return JNI_ERR;
 328     jApplicationScreen = env->GetStaticFieldID(jApplicationCls, "screen", "I");
 329     if (env->ExceptionCheck()) return JNI_ERR;
 330     jApplicationVisualID = env->GetStaticFieldID(jApplicationCls, "visualID", "J");
 331     if (env->ExceptionCheck()) return JNI_ERR;
 332     jApplicationReportException = env->GetStaticMethodID(
 333         jApplicationCls, "reportException", "(Ljava/lang/Throwable;)V");
 334     if (env->ExceptionCheck()) return JNI_ERR;
 335     jApplicationGetApplication = env->GetStaticMethodID(
 336         jApplicationCls, "GetApplication", "()Lcom/sun/glass/ui/Application;");
 337     if (env->ExceptionCheck()) return JNI_ERR;
 338     jApplicationGetName = env->GetMethodID(jApplicationCls, "getName", "()Ljava/lang/String;");
 339     if (env->ExceptionCheck()) return JNI_ERR;
 340 
 341     // Before doing anything with GTK we validate that the DISPLAY can be opened
 342     display = XOpenDisplay(NULL);
 343     if (display != NULL) {
 344         XCloseDisplay(display);
 345         displayValid = JNI_TRUE;
 346     } else {
 347         // Invalid DISPLAY, skip initialization
 348         return JNI_VERSION_1_6;
 349     }
 350 
 351     clazz = env->FindClass("sun/misc/GThreadHelper");
 352     if (env->ExceptionCheck()) return JNI_ERR;
 353     if (clazz) {
 354         jmethodID mid_getAndSetInitializationNeededFlag = env->GetStaticMethodID(clazz, "getAndSetInitializationNeededFlag", "()Z");
 355         if (env->ExceptionCheck()) return JNI_ERR;
 356         jmethodID mid_lock = env->GetStaticMethodID(clazz, "lock", "()V");
 357         if (env->ExceptionCheck()) return JNI_ERR;
 358         jmethodID mid_unlock = env->GetStaticMethodID(clazz, "unlock", "()V");
 359         if (env->ExceptionCheck()) return JNI_ERR;
 360 
 361         env->CallStaticVoidMethod(clazz, mid_lock);
 362 
 363         if (!env->CallStaticBooleanMethod(clazz, mid_getAndSetInitializationNeededFlag)) {
 364             init_threads();
 365         }
 366 
 367         env->CallStaticVoidMethod(clazz, mid_unlock);
 368     } else {
 369         env->ExceptionClear();
 370         init_threads();
 371     }
 372 
 373     gdk_threads_enter();
 374     gtk_init(NULL, NULL);
 375 
 376     return JNI_VERSION_1_6;
 377 }
 378 
 379 void
 380 glass_throw_exception(JNIEnv * env,
 381                       const char * exceptionClass,
 382                       const char * exceptionMessage) {
 383     jclass throwableClass = env->FindClass(exceptionClass);
 384     if (check_and_clear_exception(env)) return;
 385     env->ThrowNew(throwableClass, exceptionMessage);
 386     check_and_clear_exception(env);
 387 }
 388 
 389 int
 390 glass_throw_oom(JNIEnv * env, const char * message) {
 391     glass_throw_exception(env, "java/lang/OutOfMemoryError", message);
 392     // must return a non-zero value, see HANDLE_MEM_ALLOC_ERROR
 393     return 1;
 394 }
 395 
 396 
 397 guint8* convert_BGRA_to_RGBA(const int* pixels, int stride, int height) {
 398   guint8* new_pixels = (guint8*) g_malloc(height * stride);
 399   int i = 0;
 400 
 401   for (i = 0; i < height * stride; i += 4) {
 402       new_pixels[i] = (guint8)(*pixels >> 16);
 403       new_pixels[i + 1] = (guint8)(*pixels >> 8);
 404       new_pixels[i + 2] = (guint8)(*pixels);
 405       new_pixels[i + 3] = (guint8)(*pixels >> 24);
 406       pixels++;
 407   }
 408 
 409   return new_pixels;
 410 }
 411 
 412 
 413 void dump_jstring_array(JNIEnv* env, jobjectArray arr) {
 414     if (arr == NULL) {
 415         LOG0("dump: Array is null\n")
 416         return;
 417     }
 418     jsize len = env->GetArrayLength(arr);
 419     LOG1("dump: length = %d\n", len)
 420     int i = 0;
 421     jboolean isCopy;
 422     for(i = 0; i < len; i++) {
 423         jstring jstr = (jstring) env->GetObjectArrayElement(arr, i);
 424         check_and_clear_exception(env);
 425         const char* str = env->GetStringUTFChars(jstr, &isCopy);
 426         LOG2("dump: s[%d]: %s\n", i, str)
 427     }
 428 }
 429 
 430 gboolean check_and_clear_exception(JNIEnv *env) {
 431     jthrowable t = env->ExceptionOccurred();
 432     if (t) {
 433         env->ExceptionClear();
 434         env->CallStaticVoidMethod(jApplicationCls, jApplicationReportException, t);
 435         return TRUE;
 436     }
 437     return FALSE;
 438 }
 439 
 440 // The returned string should be freed with g_free().
 441 gchar* get_application_name() {
 442     gchar* ret = NULL;
 443 
 444     jobject japp = mainEnv->CallStaticObjectMethod(jApplicationCls, jApplicationGetApplication);
 445     CHECK_JNI_EXCEPTION_RET(mainEnv, NULL);
 446     jstring jname = (jstring) mainEnv->CallObjectMethod(japp, jApplicationGetName);
 447     CHECK_JNI_EXCEPTION_RET(mainEnv, NULL);
 448     if (const gchar *name = mainEnv->GetStringUTFChars(jname, NULL)) {
 449         ret = g_strdup(name);
 450         mainEnv->ReleaseStringUTFChars(jname, name);
 451     }
 452     return ret;
 453 }
 454 
 455 gpointer glass_try_malloc_n(gsize m, gsize n,
 456         gboolean zer0 /* initialized to 0 if true*/) {
 457     if (n > 0 && m > G_MAXSIZE / n) {
 458         return NULL;
 459     }
 460     return (zer0)
 461             ? g_try_malloc0(m * n)
 462             : g_try_malloc(m * n);
 463 }
 464 
 465 /*
 466  * Since we support glib 2.18 we can't use g_try_malloc_n and g_try_malloc0_n
 467  * which was introduced in 2.24.
 468  * glass_try_malloc_n and glass_try_malloc0_n is replacement for those functions
 469  */
 470 gpointer glass_try_malloc0_n(gsize m, gsize n) {
 471     return glass_try_malloc_n(m, n, TRUE);
 472 }
 473 
 474 gpointer glass_try_malloc_n(gsize m, gsize n) {
 475     return glass_try_malloc_n(m, n, FALSE);
 476 }
 477 
 478 gsize get_files_count(gchar **uris) {
 479     if (!uris) {
 480         return 0;
 481     }
 482 
 483     guint size = g_strv_length(uris);
 484     guint files_cnt = 0;
 485 
 486     for (guint i = 0; i < size; ++i) {
 487         if (g_str_has_prefix(uris[i], FILE_PREFIX)) {
 488             files_cnt++;
 489         }
 490     }
 491     return files_cnt;
 492 }
 493 
 494 // Note: passed uris will be freed by this function
 495 jobject uris_to_java(JNIEnv *env, gchar **uris, gboolean files) {
 496     if (uris == NULL) {
 497         return NULL;
 498     }
 499 
 500     jobject result = NULL;
 501 
 502     guint size = g_strv_length(uris);
 503     guint files_cnt = get_files_count(uris);
 504 
 505     if (files) {
 506         if (files_cnt) {
 507             result = env->NewObjectArray(files_cnt, jStringCls, NULL);
 508             check_and_clear_exception(env);
 509 
 510             for (gsize i = 0; i < size; ++i) {
 511                 if (g_str_has_prefix(uris[i], FILE_PREFIX)) {
 512                     gchar* path = g_filename_from_uri(uris[i], NULL, NULL);
 513                     jstring str = env->NewStringUTF(path);
 514                     check_and_clear_exception(env);
 515                     env->SetObjectArrayElement((jobjectArray) result, i, str);
 516                     check_and_clear_exception(env);
 517                     g_free(path);
 518                 }
 519             }
 520         }
 521     } else if (size - files_cnt) {
 522         GString* str = g_string_new(NULL); //http://www.ietf.org/rfc/rfc2483.txt
 523 
 524         for (guint i = 0; i < size; ++i) {
 525             if (!g_str_has_prefix(uris[i], FILE_PREFIX)
 526                     && !g_str_has_prefix(uris[i], URI_LIST_COMMENT_PREFIX)) {
 527                 g_string_append(str, uris[i]);
 528                 g_string_append(str, URI_LIST_LINE_BREAK);
 529             }
 530         }
 531 
 532         if (str->len > 2) {
 533             g_string_erase(str, str->len - 2, 2);
 534         }
 535 
 536         result = env->NewStringUTF(str->str);
 537         check_and_clear_exception(env);
 538 
 539         g_string_free(str, TRUE);
 540     }
 541     g_strfreev(uris);
 542     return result;
 543 }