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