/* * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "glass_general.h" #include #include char const * const GDK_WINDOW_DATA_CONTEXT = "glass_window_context"; jclass jStringCls; jclass jByteBufferCls; jmethodID jByteBufferArray; jmethodID jByteBufferWrap; jclass jRunnableCls; jmethodID jRunnableRun; jclass jArrayListCls; jmethodID jArrayListInit; jmethodID jArrayListAdd; jmethodID jArrayListGetIdx; jmethodID jPixelsAttachData; jclass jGtkPixelsCls; jmethodID jGtkPixelsInit; jclass jScreenCls; jmethodID jScreenInit; jmethodID jScreenNotifySettingsChanged; jmethodID jViewNotifyResize; jmethodID jViewNotifyMouse; jmethodID jViewNotifyRepaint; jmethodID jViewNotifyKey; jmethodID jViewNotifyView; jmethodID jViewNotifyDragEnter; jmethodID jViewNotifyDragOver; jmethodID jViewNotifyDragDrop; jmethodID jViewNotifyDragLeave; jmethodID jViewNotifyScroll; jmethodID jViewNotifyInputMethod; jmethodID jViewNotifyInputMethodDraw; jmethodID jViewNotifyInputMethodCaret; jmethodID jViewNotifyPreeditMode; jmethodID jViewNotifyMenu; jfieldID jViewPtr; jmethodID jWindowNotifyResize; jmethodID jWindowNotifyMove; jmethodID jWindowNotifyDestroy; jmethodID jWindowNotifyClose; jmethodID jWindowNotifyFocus; jmethodID jWindowNotifyFocusDisabled; jmethodID jWindowNotifyFocusUngrab; jmethodID jWindowNotifyMoveToAnotherScreen; jmethodID jWindowNotifyLevelChanged; jmethodID jWindowIsEnabled; jmethodID jWindowNotifyDelegatePtr; jfieldID jWindowPtr; jfieldID jCursorPtr; jmethodID jGtkWindowNotifyStateChanged; jmethodID jClipboardContentChanged; jmethodID jSizeInit; jmethodID jMapGet; jmethodID jMapKeySet; jmethodID jMapContainsKey; jclass jHashSetCls; jmethodID jHashSetInit; jmethodID jSetAdd; jmethodID jSetSize; jmethodID jSetToArray; jmethodID jIterableIterator; jmethodID jIteratorHasNext; jmethodID jIteratorNext; jclass jApplicationCls; jfieldID jApplicationDisplay; jfieldID jApplicationScreen; jfieldID jApplicationVisualID; jmethodID jApplicationReportException; jmethodID jApplicationGetApplication; jmethodID jApplicationGetName; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" void init_threads() { gboolean is_g_thread_get_initialized = FALSE; if (glib_check_version(2, 32, 0)) { // < 2.32 if (!glib_check_version(2, 20, 0)) { is_g_thread_get_initialized = g_thread_get_initialized(); } if (!is_g_thread_get_initialized) { // Calling g_thread_init() multiple times leads to crash on GLib < 2.24 // We can use g_thread_get_initialized () but it is available only for // GLib >= 2.20. We rely on GThreadHelper for GLib < 2.20. // g_thread_init is no longer necessary for GLib >=2.32 g_thread_init(NULL); } } gdk_threads_init(); } #pragma GCC diagnostic pop static jboolean displayValid = JNI_FALSE; jboolean is_display_valid() { return displayValid; } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { (void)reserved; JNIEnv *env; jclass clazz; Display* display; if (jvm->GetEnv((void **)&env, JNI_VERSION_1_6)) { return JNI_ERR; /* JNI version not supported */ } clazz = env->FindClass("java/lang/String"); if (env->ExceptionCheck()) return JNI_ERR; jStringCls = (jclass) env->NewGlobalRef(clazz); clazz = env->FindClass("java/nio/ByteBuffer"); if (env->ExceptionCheck()) return JNI_ERR; jByteBufferCls = (jclass) env->NewGlobalRef(clazz); jByteBufferArray = env->GetMethodID(jByteBufferCls, "array", "()[B"); if (env->ExceptionCheck()) return JNI_ERR; jByteBufferWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("java/lang/Runnable"); if (env->ExceptionCheck()) return JNI_ERR; jRunnableRun = env->GetMethodID(clazz, "run", "()V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("java/util/ArrayList"); if (env->ExceptionCheck()) return JNI_ERR; jArrayListCls = (jclass) env->NewGlobalRef(clazz); jArrayListInit = env->GetMethodID(jArrayListCls, "", "()V"); if (env->ExceptionCheck()) return JNI_ERR; jArrayListAdd = env->GetMethodID(jArrayListCls, "add", "(Ljava/lang/Object;)Z"); if (env->ExceptionCheck()) return JNI_ERR; jArrayListGetIdx = env->GetMethodID(jArrayListCls, "get", "(I)Ljava/lang/Object;"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/Pixels"); if (env->ExceptionCheck()) return JNI_ERR; jPixelsAttachData = env->GetMethodID(clazz, "attachData", "(J)V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/gtk/GtkPixels"); if (env->ExceptionCheck()) return JNI_ERR; jGtkPixelsCls = (jclass) env->NewGlobalRef(clazz); jGtkPixelsInit = env->GetMethodID(jGtkPixelsCls, "", "(IILjava/nio/ByteBuffer;)V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/Screen"); if (env->ExceptionCheck()) return JNI_ERR; jScreenCls = (jclass) env->NewGlobalRef(clazz); jScreenInit = env->GetMethodID(jScreenCls, "", "(JIIIIIIIIIIIIIIIFFFF)V"); if (env->ExceptionCheck()) return JNI_ERR; jScreenNotifySettingsChanged = env->GetStaticMethodID(jScreenCls, "notifySettingsChanged", "()V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/View"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyResize = env->GetMethodID(clazz, "notifyResize", "(II)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyMouse = env->GetMethodID(clazz, "notifyMouse", "(IIIIIIIZZ)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyRepaint = env->GetMethodID(clazz, "notifyRepaint", "(IIII)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyKey = env->GetMethodID(clazz, "notifyKey", "(II[CI)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyView = env->GetMethodID(clazz, "notifyView", "(I)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyDragEnter = env->GetMethodID(clazz, "notifyDragEnter", "(IIIII)I"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyDragOver = env->GetMethodID(clazz, "notifyDragOver", "(IIIII)I"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyDragDrop = env->GetMethodID(clazz, "notifyDragDrop", "(IIIII)I"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyDragLeave = env->GetMethodID(clazz, "notifyDragLeave", "()V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyScroll = env->GetMethodID(clazz, "notifyScroll", "(IIIIDDIIIIIDD)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyInputMethod = env->GetMethodID(clazz, "notifyInputMethod", "(Ljava/lang/String;[I[I[BIII)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyMenu = env->GetMethodID(clazz, "notifyMenu", "(IIIIZ)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewPtr = env->GetFieldID(clazz, "ptr", "J"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/gtk/GtkView"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyInputMethodDraw = env->GetMethodID(clazz, "notifyInputMethodDraw", "(Ljava/lang/String;III[B)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyInputMethodCaret = env->GetMethodID(clazz, "notifyInputMethodCaret", "(III)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyPreeditMode = env->GetMethodID(clazz, "notifyPreeditMode", "(Z)V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/Window"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyResize = env->GetMethodID(clazz, "notifyResize", "(III)V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyMove = env->GetMethodID(clazz, "notifyMove", "(II)V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyDestroy = env->GetMethodID(clazz, "notifyDestroy", "()V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyClose = env->GetMethodID(clazz, "notifyClose", "()V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyFocus = env->GetMethodID(clazz, "notifyFocus", "(I)V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyFocusDisabled = env->GetMethodID(clazz, "notifyFocusDisabled", "()V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyFocusUngrab = env->GetMethodID(clazz, "notifyFocusUngrab", "()V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyMoveToAnotherScreen = env->GetMethodID(clazz, "notifyMoveToAnotherScreen", "(Lcom/sun/glass/ui/Screen;)V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyLevelChanged = env->GetMethodID(clazz, "notifyLevelChanged", "(I)V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowIsEnabled = env->GetMethodID(clazz, "isEnabled", "()Z"); if (env->ExceptionCheck()) return JNI_ERR; jWindowNotifyDelegatePtr = env->GetMethodID(clazz, "notifyDelegatePtr", "(J)V"); if (env->ExceptionCheck()) return JNI_ERR; jWindowPtr = env->GetFieldID(clazz, "ptr", "J"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/gtk/GtkWindow"); if (env->ExceptionCheck()) return JNI_ERR; jGtkWindowNotifyStateChanged = env->GetMethodID(clazz, "notifyStateChanged", "(I)V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/Clipboard"); if (env->ExceptionCheck()) return JNI_ERR; jClipboardContentChanged = env->GetMethodID(clazz, "contentChanged", "()V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/Cursor"); if (env->ExceptionCheck()) return JNI_ERR; jCursorPtr = env->GetFieldID(clazz, "ptr", "J"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/Size"); if (env->ExceptionCheck()) return JNI_ERR; jSizeInit = env->GetMethodID(clazz, "", "(II)V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("java/util/Map"); if (env->ExceptionCheck()) return JNI_ERR; jMapGet = env->GetMethodID(clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); if (env->ExceptionCheck()) return JNI_ERR; jMapKeySet = env->GetMethodID(clazz, "keySet", "()Ljava/util/Set;"); if (env->ExceptionCheck()) return JNI_ERR; jMapContainsKey = env->GetMethodID(clazz, "containsKey", "(Ljava/lang/Object;)Z"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("java/util/HashSet"); if (env->ExceptionCheck()) return JNI_ERR; jHashSetCls = (jclass) env->NewGlobalRef(clazz); jHashSetInit = env->GetMethodID(jHashSetCls, "", "()V"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("java/util/Set"); if (env->ExceptionCheck()) return JNI_ERR; jSetAdd = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"); if (env->ExceptionCheck()) return JNI_ERR; jSetSize = env->GetMethodID(clazz, "size", "()I"); if (env->ExceptionCheck()) return JNI_ERR; jSetToArray = env->GetMethodID(clazz, "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("java/lang/Iterable"); if (env->ExceptionCheck()) return JNI_ERR; jIterableIterator = env->GetMethodID(clazz, "iterator", "()Ljava/util/Iterator;"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("java/util/Iterator"); if (env->ExceptionCheck()) return JNI_ERR; jIteratorHasNext = env->GetMethodID(clazz, "hasNext", "()Z"); if (env->ExceptionCheck()) return JNI_ERR; jIteratorNext = env->GetMethodID(clazz, "next", "()Ljava/lang/Object;"); if (env->ExceptionCheck()) return JNI_ERR; clazz = env->FindClass("com/sun/glass/ui/gtk/GtkApplication"); if (env->ExceptionCheck()) return JNI_ERR; jApplicationCls = (jclass) env->NewGlobalRef(clazz); jApplicationDisplay = env->GetStaticFieldID(jApplicationCls, "display", "J"); if (env->ExceptionCheck()) return JNI_ERR; jApplicationScreen = env->GetStaticFieldID(jApplicationCls, "screen", "I"); if (env->ExceptionCheck()) return JNI_ERR; jApplicationVisualID = env->GetStaticFieldID(jApplicationCls, "visualID", "J"); if (env->ExceptionCheck()) return JNI_ERR; jApplicationReportException = env->GetStaticMethodID( jApplicationCls, "reportException", "(Ljava/lang/Throwable;)V"); if (env->ExceptionCheck()) return JNI_ERR; jApplicationGetApplication = env->GetStaticMethodID( jApplicationCls, "GetApplication", "()Lcom/sun/glass/ui/Application;"); if (env->ExceptionCheck()) return JNI_ERR; jApplicationGetName = env->GetMethodID(jApplicationCls, "getName", "()Ljava/lang/String;"); if (env->ExceptionCheck()) return JNI_ERR; // Before doing anything with GTK we validate that the DISPLAY can be opened display = XOpenDisplay(NULL); if (display != NULL) { XCloseDisplay(display); displayValid = JNI_TRUE; } else { // Invalid DISPLAY, skip initialization return JNI_VERSION_1_6; } clazz = env->FindClass("sun/misc/GThreadHelper"); if (env->ExceptionCheck()) return JNI_ERR; if (clazz) { jmethodID mid_getAndSetInitializationNeededFlag = env->GetStaticMethodID(clazz, "getAndSetInitializationNeededFlag", "()Z"); if (env->ExceptionCheck()) return JNI_ERR; jmethodID mid_lock = env->GetStaticMethodID(clazz, "lock", "()V"); if (env->ExceptionCheck()) return JNI_ERR; jmethodID mid_unlock = env->GetStaticMethodID(clazz, "unlock", "()V"); if (env->ExceptionCheck()) return JNI_ERR; env->CallStaticVoidMethod(clazz, mid_lock); if (!env->CallStaticBooleanMethod(clazz, mid_getAndSetInitializationNeededFlag)) { init_threads(); } env->CallStaticVoidMethod(clazz, mid_unlock); } else { env->ExceptionClear(); init_threads(); } gdk_threads_enter(); gtk_init(NULL, NULL); return JNI_VERSION_1_6; } void glass_throw_exception(JNIEnv * env, const char * exceptionClass, const char * exceptionMessage) { jclass throwableClass = env->FindClass(exceptionClass); if (check_and_clear_exception(env)) return; env->ThrowNew(throwableClass, exceptionMessage); check_and_clear_exception(env); } int glass_throw_oom(JNIEnv * env, const char * message) { glass_throw_exception(env, "java/lang/OutOfMemoryError", message); // must return a non-zero value, see HANDLE_MEM_ALLOC_ERROR return 1; } guint8* convert_BGRA_to_RGBA(const int* pixels, int stride, int height) { guint8* new_pixels = (guint8*) g_malloc(height * stride); int i = 0; for (i = 0; i < height * stride; i += 4) { new_pixels[i] = (guint8)(*pixels >> 16); new_pixels[i + 1] = (guint8)(*pixels >> 8); new_pixels[i + 2] = (guint8)(*pixels); new_pixels[i + 3] = (guint8)(*pixels >> 24); pixels++; } return new_pixels; } void dump_jstring_array(JNIEnv* env, jobjectArray arr) { if (arr == NULL) { LOG0("dump: Array is null\n") return; } jsize len = env->GetArrayLength(arr); LOG1("dump: length = %d\n", len) int i = 0; jboolean isCopy; for(i = 0; i < len; i++) { jstring jstr = (jstring) env->GetObjectArrayElement(arr, i); check_and_clear_exception(env); const char* str = env->GetStringUTFChars(jstr, &isCopy); LOG2("dump: s[%d]: %s\n", i, str) } } gboolean check_and_clear_exception(JNIEnv *env) { jthrowable t = env->ExceptionOccurred(); if (t) { env->ExceptionClear(); env->CallStaticVoidMethod(jApplicationCls, jApplicationReportException, t); return TRUE; } return FALSE; } // The returned string should be freed with g_free(). gchar* get_application_name() { gchar* ret = NULL; jobject japp = mainEnv->CallStaticObjectMethod(jApplicationCls, jApplicationGetApplication); CHECK_JNI_EXCEPTION_RET(mainEnv, NULL); jstring jname = (jstring) mainEnv->CallObjectMethod(japp, jApplicationGetName); CHECK_JNI_EXCEPTION_RET(mainEnv, NULL); if (const gchar *name = mainEnv->GetStringUTFChars(jname, NULL)) { ret = g_strdup(name); mainEnv->ReleaseStringUTFChars(jname, name); } return ret; } gpointer glass_try_malloc_n(gsize m, gsize n, gboolean zer0 /* initialized to 0 if true*/) { if (n > 0 && m > G_MAXSIZE / n) { return NULL; } return (zer0) ? g_try_malloc0(m * n) : g_try_malloc(m * n); } /* * Since we support glib 2.18 we can't use g_try_malloc_n and g_try_malloc0_n * which was introduced in 2.24. * glass_try_malloc_n and glass_try_malloc0_n is replacement for those functions */ gpointer glass_try_malloc0_n(gsize m, gsize n) { return glass_try_malloc_n(m, n, TRUE); } gpointer glass_try_malloc_n(gsize m, gsize n) { return glass_try_malloc_n(m, n, FALSE); } gsize get_files_count(gchar **uris) { if (!uris) { return 0; } guint size = g_strv_length(uris); guint files_cnt = 0; for (guint i = 0; i < size; ++i) { if (g_str_has_prefix(uris[i], FILE_PREFIX)) { files_cnt++; } } return files_cnt; } // Note: passed uris will be freed by this function jobject uris_to_java(JNIEnv *env, gchar **uris, gboolean files) { if (uris == NULL) { return NULL; } jobject result = NULL; guint size = g_strv_length(uris); guint files_cnt = get_files_count(uris); if (files) { if (files_cnt) { result = env->NewObjectArray(files_cnt, jStringCls, NULL); check_and_clear_exception(env); for (gsize i = 0; i < size; ++i) { if (g_str_has_prefix(uris[i], FILE_PREFIX)) { gchar* path = g_filename_from_uri(uris[i], NULL, NULL); jstring str = env->NewStringUTF(path); check_and_clear_exception(env); env->SetObjectArrayElement((jobjectArray) result, i, str); check_and_clear_exception(env); g_free(path); } } } } else if (size - files_cnt) { GString* str = g_string_new(NULL); //http://www.ietf.org/rfc/rfc2483.txt for (guint i = 0; i < size; ++i) { if (!g_str_has_prefix(uris[i], FILE_PREFIX) && !g_str_has_prefix(uris[i], URI_LIST_COMMENT_PREFIX)) { g_string_append(str, uris[i]); g_string_append(str, URI_LIST_LINE_BREAK); } } if (str->len > 2) { g_string_erase(str, str->len - 2, 2); } result = env->NewStringUTF(str->str); check_and_clear_exception(env); g_string_free(str, TRUE); } g_strfreev(uris); return result; }