diff --git a/buildSrc/linux.gradle b/buildSrc/linux.gradle index a8e9fb19a5..fdfc2bdb39 100644 --- a/buildSrc/linux.gradle +++ b/buildSrc/linux.gradle @@ -84,6 +84,7 @@ if (hasProperty('toolchainDir')) { def gtk2CCFlags = [ "-Wno-deprecated-declarations" ]; def gtk3CCFlags = [ "-Wno-deprecated-declarations" ]; +def gtk3expCCFlags = [ "-Wno-deprecated-declarations" ]; def gtk2LinkFlags = [ ]; def gtk3LinkFlags = [ ]; @@ -138,6 +139,7 @@ setupTools("linux_gtk3", def libsGTK3 = properties.getProperty("libsGTK3") if (cflagsGTK3 && libsGTK3) { gtk3CCFlags.addAll(cflagsGTK3.split(" ")) + gtk3expCCFlags.addAll(cflagsGTK3.split(" ")) gtk3LinkFlags.addAll(libsGTK3.split(" ")) } else { throw new IllegalStateException("GTK3 development packages not found. If GTK3 packages are installed, please remove the build directory and try again.") @@ -212,7 +214,7 @@ def compiler = IS_COMPILE_PARFAIT ? "parfait-gcc" : "${toolchainDir}gcc"; def linker = IS_STATIC_BUILD ? "ar" : IS_COMPILE_PARFAIT ? "parfait-g++" : "${toolchainDir}g++"; LINUX.glass = [:] -LINUX.glass.variants = ["glass", "glassgtk2", "glassgtk3"] +LINUX.glass.variants = ["glass", "glassgtk2", "glassgtk3", "glassgtk2_exp", "glassgtk3_exp"] FileTree ft_gtk_launcher = fileTree("${project(":graphics").projectDir}/src/main/native-glass/gtk/") { include("**/launcher.c") @@ -222,6 +224,11 @@ FileTree ft_gtk = fileTree("${project(":graphics").projectDir}/src/main/native-g exclude("**/launcher.c") } +FileTree ft_gtkexp = fileTree("${project(":graphics").projectDir}/src/main/native-glass/gtk_experimental/") { + exclude("**/launcher.c") +} + + LINUX.glass.glass = [:] LINUX.glass.glass.nativeSource = ft_gtk_launcher.getFiles() LINUX.glass.glass.compiler = compiler @@ -250,6 +257,26 @@ LINUX.glass.glassgtk3.linker = linker LINUX.glass.glassgtk3.linkFlags = IS_STATIC_BUILD ? linkFlags : [linkFlags, gtk3LinkFlags].flatten() LINUX.glass.glassgtk3.lib = "glassgtk3" +LINUX.glass.glassgtk2_exp = [:] +LINUX.glass.glassgtk2_exp.nativeSource = ft_gtkexp.getFiles() +LINUX.glass.glassgtk2_exp.compiler = compiler +LINUX.glass.glassgtk2_exp.ccFlags = IS_STATIC_BUILD ? + ["-fno-threadsafe-statics", ccFlags, gtk2CCFlags].flatten() : + [cppFlags, gtk2CCFlags, "-Werror"].flatten() +LINUX.glass.glassgtk2_exp.linker = linker +LINUX.glass.glassgtk2_exp.linkFlags = IS_STATIC_BUILD ? linkFlags : [linkFlags, gtk2LinkFlags].flatten() +LINUX.glass.glassgtk2_exp.lib = "glassgtk2_exp" + +LINUX.glass.glassgtk3_exp = [:] +LINUX.glass.glassgtk3_exp.nativeSource = ft_gtkexp.getFiles() +LINUX.glass.glassgtk3_exp.compiler = compiler +LINUX.glass.glassgtk3_exp.ccFlags = IS_STATIC_BUILD ? + ["-fno-threadsafe-statics", ccFlags, gtk3expCCFlags].flatten() : + [cppFlags, gtk3expCCFlags, "-Werror"].flatten() +LINUX.glass.glassgtk3_exp.linker = linker +LINUX.glass.glassgtk3_exp.linkFlags = IS_STATIC_BUILD ? linkFlags : [linkFlags, gtk3LinkFlags].flatten() +LINUX.glass.glassgtk3_exp.lib = "glassgtk3_exp" + LINUX.decora = [:] LINUX.decora.compiler = compiler LINUX.decora.ccFlags = [cppFlags, "-ffast-math"].flatten() diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java index 0526b62ba4..05b226d598 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java @@ -155,6 +155,10 @@ private static float getFloat(String propname, float defval, String description) } return ret; }) : forcedGtkVersion; + + boolean gtkExperimental = AccessController.doPrivileged((PrivilegedAction) () -> + Boolean.getBoolean("javafx.gtk.experimental")); + boolean gtkVersionVerbose = AccessController.doPrivileged((PrivilegedAction) () -> { return Boolean.getBoolean("jdk.gtk.verbose"); @@ -176,15 +180,19 @@ private static float getFloat(String propname, float defval, String description) System.out.println("Glass GTK library to load is already loaded"); } } else if (libraryToLoad == QUERY_LOAD_GTK2) { + String libName = (gtkExperimental) ? "glassgtk2_exp" : "glassgtk2"; if (gtkVersionVerbose) { - System.out.println("Glass GTK library to load is glassgtk2"); + System.out.println(String.format("Glass GTK library to load is %s", libName)); } - NativeLibLoader.loadLibrary("glassgtk2"); + + NativeLibLoader.loadLibrary(libName); } else if (libraryToLoad == QUERY_LOAD_GTK3) { + String libName = (gtkExperimental) ? "glassgtk3_exp" : "glassgtk3"; if (gtkVersionVerbose) { - System.out.println("Glass GTK library to load is glassgtk3"); + System.out.println(String.format("Glass GTK library to load is %s", libName)); } - NativeLibLoader.loadLibrary("glassgtk3"); + + NativeLibLoader.loadLibrary(libName); } else { throw new UnsupportedOperationException("Internal Error"); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java index fc770274de..80f86afdfc 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2020, 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 @@ -32,6 +32,7 @@ import com.sun.glass.ui.Window; class GtkWindow extends Window { + private static boolean USE_EXPERIMENTAL_GTK_IMPL = Boolean.getBoolean("javafx.gtk.experimental"); public GtkWindow(Window owner, Screen screen, int styleMask) { super(owner, screen, styleMask); @@ -201,16 +202,18 @@ protected void _setBounds(long ptr, int x, int y, boolean xSet, boolean ySet, in _setGravity(ptr, xGravity, yGravity); setBoundsImpl(ptr, x, y, xSet, ySet, w, h, cw, ch); - if ((w <= 0) && (cw > 0) || (h <= 0) && (ch > 0)) { - final int[] extarr = new int[4]; - getFrameExtents(ptr, extarr); - - // TODO: ((w <= 0) && (cw <= 0)) || ((h <= 0) && (ch <= 0)) - notifyResize(WindowEvent.RESIZE, - ((w <= 0) && (cw > 0)) ? cw + extarr[0] + extarr[1] - : w, - ((h <= 0) && (ch > 0)) ? ch + extarr[2] + extarr[3] - : h); + if (!USE_EXPERIMENTAL_GTK_IMPL) { + if ((w <= 0) && (cw > 0) || (h <= 0) && (ch > 0)) { + final int[] extarr = new int[4]; + getFrameExtents(ptr, extarr); + + // TODO: ((w <= 0) && (cw <= 0)) || ((h <= 0) && (ch <= 0)) + notifyResize(WindowEvent.RESIZE, + ((w <= 0) && (cw > 0)) ? cw + extarr[0] + extarr[1] + : w, + ((h <= 0) && (ch > 0)) ? ch + extarr[2] + extarr[3] + : h); + } } } diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassApplication.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassApplication.cpp new file mode 100644 index 0000000000..a5afb948d8 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassApplication.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2011, 2020, 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "glass_general.h" +#include "glass_evloop.h" +#include "glass_dnd.h" +#include "glass_window.h" +#include "glass_screen.h" + +GdkEventFunc process_events_prev; +static void process_events(GdkEvent*, gpointer); + +JNIEnv* mainEnv; // Use only with main loop thread!!! + +extern gboolean disableGrab; + +static gboolean call_runnable (gpointer data) +{ + RunnableContext* context = reinterpret_cast(data); + + JNIEnv *env; + int envStatus = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6); + if (envStatus == JNI_EDETACHED) { + javaVM->AttachCurrentThread((void **)&env, NULL); + } + + env->CallVoidMethod(context->runnable, jRunnableRun, NULL); + LOG_EXCEPTION(env); + env->DeleteGlobalRef(context->runnable); + free(context); + + if (envStatus == JNI_EDETACHED) { + javaVM->DetachCurrentThread(); + } + + return FALSE; +} + +extern "C" { + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +static 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) { + g_thread_init(NULL); + } + } + gdk_threads_init(); +} +#pragma GCC diagnostic pop + +jboolean gtk_verbose = JNI_FALSE; + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _initGTK + * Signature: (IZ)I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1initGTK + (JNIEnv *env, jclass clazz, jint version, jboolean verbose, jfloat uiScale) +{ + (void) clazz; + (void) version; + + OverrideUIScale = uiScale; + gtk_verbose = verbose; + + env->ExceptionClear(); + +#if !GTK_CHECK_VERSION(3, 6, 0) + init_threads(); + gdk_threads_enter(); +#endif + + gtk_init(NULL, NULL); + + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _queryLibrary + * Signature: Signature: (IZ)I + */ +#ifndef STATIC_BUILD +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1queryLibrary + (JNIEnv *env, jclass clazz, jint suggestedVersion, jboolean verbose) +{ + // If we are being called, then the launcher is + // not in use, and we are in the proper glass library already. + // This can be done by renaming the gtk versioned native + // libraries to be libglass.so + // Note: we will make no effort to complain if the suggestedVersion + // is out of phase. + + (void)env; + (void)clazz; + (void)suggestedVersion; + (void)verbose; + + Display *display = XOpenDisplay(NULL); + if (display == NULL) { + return com_sun_glass_ui_gtk_GtkApplication_QUERY_NO_DISPLAY; + } + XCloseDisplay(display); + + return com_sun_glass_ui_gtk_GtkApplication_QUERY_USE_CURRENT; +} +#endif + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _init + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1init + (JNIEnv * env, jobject obj, jlong handler, jboolean _disableGrab) +{ + (void)obj; + + mainEnv = env; + process_events_prev = (GdkEventFunc) handler; + disableGrab = (gboolean) _disableGrab; + + glass_gdk_x11_display_set_window_scale(gdk_display_get_default(), 1); + gdk_event_handler_set(process_events, NULL, NULL); + + GdkScreen *default_gdk_screen = gdk_screen_get_default(); + if (default_gdk_screen != NULL) { + g_signal_connect(G_OBJECT(default_gdk_screen), "monitors-changed", + G_CALLBACK(screen_settings_changed), NULL); + g_signal_connect(G_OBJECT(default_gdk_screen), "size-changed", + G_CALLBACK(screen_settings_changed), NULL); + } + + GdkWindow *root = gdk_screen_get_root_window(default_gdk_screen); + gdk_window_set_events(root, static_cast(gdk_window_get_events(root) | GDK_PROPERTY_CHANGE_MASK)); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _runLoop + * Signature: (Ljava/lang/Runnable;Z)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1runLoop + (JNIEnv * env, jobject obj, jobject launchable, jboolean noErrorTrap) +{ + (void)obj; + (void)noErrorTrap; + + env->CallVoidMethod(launchable, jRunnableRun); + CHECK_JNI_EXCEPTION(env); + + // GTK installs its own X error handler that conflicts with AWT. + // During drag and drop, AWT hides errors so we need to hide them + // to avoid exit()'ing. It's not clear that we don't want to hide + // X error all the time, otherwise FX will exit(). + // + // A better solution would be to coordinate with AWT and save and + // restore the X handler. + + // Disable X error handling +#ifndef VERBOSE + if (!noErrorTrap) { +#if GTK_CHECK_VERSION(3, 0, 0) + gdk_x11_display_error_trap_push(gdk_display_get_default()); +#else + gdk_error_trap_push(); +#endif + } +#endif + + gtk_main(); + + // When the last JFrame closes and DISPOSE_ON_CLOSE is specified, + // Java exits with an X error. X error are hidden during the FX + // event loop and should be restored when the event loop exits. Unfortunately, + // this is too early. The fix is to never restore X errors. + // + // See RT-21408 & RT-20756 + + // Restore X error handling + // #ifndef VERBOSE + // if (!noErrorTrap) { + // gdk_error_trap_pop(); + // } + // #endif + +#if !GTK_CHECK_VERSION(3, 6, 0) + gdk_threads_leave(); +#endif + +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _terminateLoop + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1terminateLoop + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + gtk_main_quit(); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _submitForLaterInvocation + * Signature: (Ljava/lang/Runnable;)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1submitForLaterInvocation + (JNIEnv * env, jobject obj, jobject runnable) +{ + (void)obj; + + RunnableContext* context = (RunnableContext*)malloc(sizeof(RunnableContext)); + context->runnable = env->NewGlobalRef(runnable); + gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE + 30, call_runnable, context, NULL); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: enterNestedEventLoopImpl + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_enterNestedEventLoopImpl + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + gtk_main(); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: leaveNestedEventLoopImpl + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_leaveNestedEventLoopImpl + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + gtk_main_quit(); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: staticScreen_getScreens + * Signature: ()[Lcom/sun/glass/ui/Screen; + */ +JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticScreen_1getScreens + (JNIEnv * env, jobject obj) +{ + (void)obj; + + try { + return rebuild_screens(env); + } catch (jni_exception&) { + return NULL; + } +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: staticTimer_getMinPeriod + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticTimer_1getMinPeriod + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + return 0; // There are no restrictions on period in g_threads +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: staticTimer_getMaxPeriod + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticTimer_1getMaxPeriod + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + return 10000; // There are no restrictions on period in g_threads +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: staticView_getMultiClickTime + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickTime + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + static gint multi_click_time = -1; + if (multi_click_time == -1) { + g_object_get(gtk_settings_get_default(), "gtk-double-click-time", &multi_click_time, NULL); + } + return (jlong)multi_click_time; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: staticView_getMultiClickMaxX + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxX + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + static gint multi_click_dist = -1; + + if (multi_click_dist == -1) { + g_object_get(gtk_settings_get_default(), "gtk-double-click-distance", &multi_click_dist, NULL); + } + return multi_click_dist; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: staticView_getMultiClickMaxY + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxY + (JNIEnv * env, jobject obj) +{ + return Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxX(env, obj); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _supportsTransparentWindows + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1supportsTransparentWindows + (JNIEnv * env, jobject obj) { + (void)env; + (void)obj; + + return gdk_screen_is_composited(gdk_screen_get_default()); +} + +} // extern "C" + +bool is_window_enabled_for_event(GdkWindow * window, WindowContext *ctx, gint event_type) { + + + if (gdk_window_is_destroyed(window)) { + return FALSE; + } + + /* + * GDK_DELETE can be blocked for disabled window e.q. parent window + * which prevents from closing it + */ + switch (event_type) { + case GDK_CONFIGURE: + case GDK_DESTROY: + case GDK_EXPOSE: + case GDK_DAMAGE: + case GDK_WINDOW_STATE: + case GDK_FOCUS_CHANGE: + return TRUE; + break; + }//switch + + if (ctx != NULL) { + return ctx->isEnabled(); + } + return TRUE; +} + +static void process_events(GdkEvent* event, gpointer data) +{ + GdkWindow* window = event->any.window; + WindowContext *ctx = window != NULL ? (WindowContext*) + g_object_get_data(G_OBJECT(window), GDK_WINDOW_DATA_CONTEXT) : NULL; + + if ((window != NULL) + && !is_window_enabled_for_event(window, ctx, event->type)) { + return; + } + + if (ctx != NULL && ctx->hasIME() && ctx->filterIME(event)) { + return; + } + + glass_evloop_call_hooks(event); + + if (ctx != NULL) { + EventsCounterHelper helper(ctx); + + if (event->type == GDK_EXPOSE) { + ctx->process_expose(&event->expose); + } else if (event->type == GDK_DRAG_LEAVE) { + dnd_drag_leave_callback(ctx); + } else { + gtk_main_do_event(event); + } + } else { + if (window == gdk_screen_get_root_window(gdk_screen_get_default())) { + if (event->any.type == GDK_PROPERTY_NOTIFY) { + if (event->property.atom == gdk_atom_intern_static_string("_NET_WORKAREA") + || event->property.atom == gdk_atom_intern_static_string("_NET_CURRENT_DESKTOP")) { + screen_settings_changed(gdk_screen_get_default(), NULL); + } + } + } + + //process only for non-FX windows + if (process_events_prev != NULL) { + (*process_events_prev)(event, data); + } else { + gtk_main_do_event(event); + } + } +} diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassCommonDialogs.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassCommonDialogs.cpp new file mode 100644 index 0000000000..b0c058fd1f --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassCommonDialogs.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2011, 2016, 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 +#include "glass_general.h" +#include "glass_window.h" + +#include +#include + +#include +#include + +static GSList* setup_GtkFileFilters(GtkFileChooser*, JNIEnv*, jobjectArray, int default_filter_index); + +static void free_fname(char* fname, gpointer unused) { + (void)unused; + + g_free(fname); +} + +static gboolean jstring_to_utf_get(JNIEnv *env, jstring jstr, + const char **cstr) { + const char *newstr; + + if (jstr == NULL) { + *cstr = NULL; + return TRUE; + } + + newstr = env->GetStringUTFChars(jstr, NULL); + if (newstr != NULL) { + *cstr = newstr; + return TRUE; + } + + return FALSE; +} + +static void jstring_to_utf_release(JNIEnv *env, jstring jstr, + const char *cstr) { + if (cstr != NULL) { + env->ReleaseStringUTFChars(jstr, cstr); + } +} + +static GtkWindow *gdk_window_handle_to_gtk(jlong handle) { + return (handle != 0) + ? ((WindowContext*)JLONG_TO_PTR(handle))->get_gtk_window() + : NULL; +} + +static jobject create_empty_result() { + jclass jFileChooserResult = (jclass) mainEnv->FindClass("com/sun/glass/ui/CommonDialogs$FileChooserResult"); + if (EXCEPTION_OCCURED(mainEnv)) return NULL; + jmethodID jFileChooserResultInit = mainEnv->GetMethodID(jFileChooserResult, "", "()V"); + if (EXCEPTION_OCCURED(mainEnv)) return NULL; + jobject jResult = mainEnv->NewObject(jFileChooserResult, jFileChooserResultInit); + if (EXCEPTION_OCCURED(mainEnv)) return NULL; + return jResult; +} + +extern "C" { + +JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_gtk_GtkCommonDialogs__1showFileChooser + (JNIEnv *env, jclass clazz, jlong parent, jstring folder, jstring name, jstring title, + jint type, jboolean multiple, jobjectArray jFilters, jint default_filter_index) { + (void)clazz; + + jobjectArray jFileNames = NULL; + char* filename; + jstring jfilename; + + const char* chooser_folder; + const char* chooser_filename; + const char* chooser_title; + const int chooser_type = type == 0 ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE; + + if (!jstring_to_utf_get(env, folder, &chooser_folder)) { + return create_empty_result(); + } + + if (!jstring_to_utf_get(env, title, &chooser_title)) { + jstring_to_utf_release(env, folder, chooser_folder); + return create_empty_result(); + } + + if (!jstring_to_utf_get(env, name, &chooser_filename)) { + jstring_to_utf_release(env, folder, chooser_folder); + jstring_to_utf_release(env, title, chooser_title); + return create_empty_result(); + } + + GtkWidget* chooser = gtk_file_chooser_dialog_new(chooser_title, gdk_window_handle_to_gtk(parent), + static_cast(chooser_type), + "_Cancel", + GTK_RESPONSE_CANCEL, + (chooser_type == GTK_FILE_CHOOSER_ACTION_OPEN ? "_Open" : "_Save"), + GTK_RESPONSE_ACCEPT, + NULL); + + if (chooser_type == GTK_FILE_CHOOSER_ACTION_SAVE) { + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser), chooser_filename); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (chooser), TRUE); + } + + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), (JNI_TRUE == multiple)); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), chooser_folder); + GSList* filters = setup_GtkFileFilters(GTK_FILE_CHOOSER(chooser), env, jFilters, default_filter_index); + + if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) { + GSList* fnames_gslist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chooser)); + guint fnames_list_len = g_slist_length(fnames_gslist); + LOG1("FileChooser selected files: %d\n", fnames_list_len) + + if (fnames_list_len > 0) { + jFileNames = env->NewObjectArray((jsize)fnames_list_len, jStringCls, NULL); + EXCEPTION_OCCURED(env); + for (guint i = 0; i < fnames_list_len; i++) { + filename = (char*)g_slist_nth(fnames_gslist, i)->data; + LOG1("Add [%s] into returned filenames\n", filename) + jfilename = env->NewStringUTF(filename); + EXCEPTION_OCCURED(env); + env->SetObjectArrayElement(jFileNames, (jsize)i, jfilename); + EXCEPTION_OCCURED(env); + } + g_slist_foreach(fnames_gslist, (GFunc) free_fname, NULL); + g_slist_free(fnames_gslist); + } + } + + if (!jFileNames) { + jFileNames = env->NewObjectArray(0, jStringCls, NULL); + EXCEPTION_OCCURED(env); + } + + int index = g_slist_index(filters, gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(chooser))); + + jclass jCommonDialogs = (jclass) env->FindClass("com/sun/glass/ui/CommonDialogs"); + EXCEPTION_OCCURED(env); + jmethodID jCreateFileChooserResult = env->GetStaticMethodID(jCommonDialogs, + "createFileChooserResult", + "([Ljava/lang/String;[Lcom/sun/glass/ui/CommonDialogs$ExtensionFilter;I)Lcom/sun/glass/ui/CommonDialogs$FileChooserResult;"); + + EXCEPTION_OCCURED(env); + + jobject result = + env->CallStaticObjectMethod(jCommonDialogs, jCreateFileChooserResult, jFileNames, jFilters, index); + LOG_EXCEPTION(env) + + g_slist_free(filters); + gtk_widget_destroy(chooser); + + jstring_to_utf_release(env, folder, chooser_folder); + jstring_to_utf_release(env, title, chooser_title); + jstring_to_utf_release(env, name, chooser_filename); + + LOG_STRING_ARRAY(env, jFileNames); + return result; +} + +JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_gtk_GtkCommonDialogs__1showFolderChooser + (JNIEnv *env, jclass clazz, jlong parent, jstring folder, jstring title) { + (void)clazz; + + jstring jfilename = NULL; + const char *chooser_folder; + const char *chooser_title; + + if (!jstring_to_utf_get(env, folder, &chooser_folder)) { + return NULL; + } + + if (!jstring_to_utf_get(env, title, &chooser_title)) { + jstring_to_utf_release(env, folder, chooser_folder); + return NULL; + } + + GtkWidget* chooser = gtk_file_chooser_dialog_new( + chooser_title, + gdk_window_handle_to_gtk(parent), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Open", + GTK_RESPONSE_ACCEPT, + NULL); + + if (chooser_folder != NULL) { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), + chooser_folder); + } + + if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) { + gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser)); + jfilename = env->NewStringUTF(filename); + LOG1("Selected folder: %s\n", filename); + g_free(filename); + } + + jstring_to_utf_release(env, folder, chooser_folder); + jstring_to_utf_release(env, title, chooser_title); + + gtk_widget_destroy(chooser); + return jfilename; +} + +} // extern "C" + +/** + * + * @param env + * @param extFilters ExtensionFilter[] + * @return + */ +static GSList* setup_GtkFileFilters(GtkFileChooser* chooser, JNIEnv* env, jobjectArray extFilters, int default_filter_index) { + int i; + LOG0("Setup filters\n") + //setup methodIDs + jclass jcls = env->FindClass("com/sun/glass/ui/CommonDialogs$ExtensionFilter"); + if (EXCEPTION_OCCURED(env)) return NULL; + jmethodID jgetDescription = env->GetMethodID(jcls, + "getDescription", "()Ljava/lang/String;"); + if (EXCEPTION_OCCURED(env)) return NULL; + jmethodID jextensionsToArray = env->GetMethodID(jcls, + "extensionsToArray", "()[Ljava/lang/String;"); + if (EXCEPTION_OCCURED(env)) return NULL; + + jsize jfilters_size = env->GetArrayLength(extFilters); + LOG1("Filters: %d\n", jfilters_size) + if (jfilters_size == 0) return NULL; + + GSList* filter_list = NULL; + + for(i = 0; iGetObjectArrayElement(extFilters, i); + EXCEPTION_OCCURED(env); + + //setup description + jstring jdesc = (jstring)env->CallObjectMethod(jfilter, jgetDescription); + const char * description = env->GetStringUTFChars(jdesc, NULL); + LOG2("description[%d]: %s\n", i, description) + gtk_file_filter_set_name(ffilter, (gchar*)const_cast(description)); + env->ReleaseStringUTFChars(jdesc, description); + + //add patterns + jobjectArray jextensions = (jobjectArray)env->CallObjectMethod(jfilter, jextensionsToArray); + jsize jextarray_size = env->GetArrayLength(jextensions); + LOG1("Patterns: %d\n", jextarray_size) + int ext_idx; + for(ext_idx = 0; ext_idx < jextarray_size; ext_idx++) { + jstring jext = (jstring)env->GetObjectArrayElement(jextensions, ext_idx); + EXCEPTION_OCCURED(env); + const char * ext = env->GetStringUTFChars(jext, NULL); + LOG2("pattern[%d]: %s\n", ext_idx, ext) + gtk_file_filter_add_pattern(ffilter, (gchar*)const_cast(ext)); + env->ReleaseStringUTFChars(jext, ext); + } + LOG0("Filter ready\n") + gtk_file_chooser_add_filter(chooser, ffilter); + + if (default_filter_index == i) { + gtk_file_chooser_set_filter(chooser, ffilter); + } + + filter_list = g_slist_append(filter_list, ffilter); + } + return filter_list; +} diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassCursor.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassCursor.cpp new file mode 100644 index 0000000000..8e6498a5b7 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassCursor.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2011, 2020, 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 + +#include +#include +#include + +#include "com_sun_glass_ui_Cursor.h" +#include "glass_general.h" + +#ifndef GLASS_GTK3 +static GdkCursor* find_best_cursor(const gchar* options, GdkCursorType type) { + gchar **opts = g_strsplit(options, ",", -1); + gint size = g_strv_length(opts); + + GdkCursor *cursor = NULL; + + for (int i = 0; i < size; i++) { + cursor = gdk_cursor_new_from_name(gdk_display_get_default(), opts[i]); + + if (cursor != NULL) { + break; + } + } + + g_strfreev(opts); + + if (cursor != NULL) { + return cursor; + } + + return gdk_cursor_new_for_display(gdk_display_get_default(), type); +} + +GdkCursor* get_native_cursor(int type) +{ + GdkCursor *cursor = NULL; + switch (type) { + case com_sun_glass_ui_Cursor_CURSOR_DEFAULT: + cursor = find_best_cursor("default", GDK_LEFT_PTR); + break; + case com_sun_glass_ui_Cursor_CURSOR_TEXT: + cursor = find_best_cursor("text", GDK_XTERM); + break; + case com_sun_glass_ui_Cursor_CURSOR_CROSSHAIR: + cursor = find_best_cursor("cross,crosshair", GDK_CROSSHAIR); + break; + case com_sun_glass_ui_Cursor_CURSOR_CLOSED_HAND: + cursor = find_best_cursor("closedhand", GDK_HAND2); + break; + case com_sun_glass_ui_Cursor_CURSOR_OPEN_HAND: + cursor = find_best_cursor("openhand", GDK_HAND2); + break; + case com_sun_glass_ui_Cursor_CURSOR_POINTING_HAND: + cursor = gdk_cursor_new(GDK_HAND2); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_UP: + cursor = find_best_cursor("n-resize,ns-resize,size_ver", GDK_TOP_SIDE); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_DOWN: + cursor = find_best_cursor("s-resize,ns-resize,size_ver", GDK_BOTTOM_SIDE); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_UPDOWN: + cursor = find_best_cursor("ns-resize,ew-resize,size_ver", GDK_SB_V_DOUBLE_ARROW); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_LEFT: + cursor = find_best_cursor("w-resize,ew-resize,size_hor", GDK_LEFT_SIDE); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_RIGHT: + cursor = find_best_cursor("e-resize,ew-resize,size_hor", GDK_RIGHT_SIDE); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_LEFTRIGHT: + cursor = find_best_cursor("ew-resize,size_hor", GDK_SB_H_DOUBLE_ARROW); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_SOUTHWEST: + cursor = find_best_cursor("sw-resize,nesw-resize,size_bdiag", GDK_BOTTOM_LEFT_CORNER); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_NORTHEAST: + cursor = find_best_cursor("ne-resize,nesw-resize,size_bdiag", GDK_TOP_RIGHT_CORNER); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_SOUTHEAST: + cursor = find_best_cursor("se-resize,nwse-resize,size_fdiag", GDK_BOTTOM_RIGHT_CORNER); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_NORTHWEST: + cursor = find_best_cursor("nw-resize,nwse-resize,size_fdiag", GDK_TOP_LEFT_CORNER); + break; + case com_sun_glass_ui_Cursor_CURSOR_MOVE: + cursor = find_best_cursor("fleur,move,alt-scroll", GDK_SIZING); + break; + case com_sun_glass_ui_Cursor_CURSOR_WAIT: + cursor = find_best_cursor("wait", GDK_WATCH); + break; + case com_sun_glass_ui_Cursor_CURSOR_DISAPPEAR: + case com_sun_glass_ui_Cursor_CURSOR_NONE: + cursor = find_best_cursor("none", GDK_BLANK_CURSOR); + break; + default: + cursor = find_best_cursor("default", GDK_LEFT_PTR); + break; + } + + if (cursor == NULL) { + cursor = find_best_cursor("default", GDK_LEFT_PTR); + } + + return cursor; +} +#else +GdkCursor* get_native_cursor(int type) +{ + gchar* cursor_name = NULL; + + switch (type) { + case com_sun_glass_ui_Cursor_CURSOR_DEFAULT: + cursor_name = g_strdup("default"); + break; + case com_sun_glass_ui_Cursor_CURSOR_TEXT: + cursor_name = g_strdup("text"); + break; + case com_sun_glass_ui_Cursor_CURSOR_CROSSHAIR: + cursor_name = g_strdup("crosshair"); + break; + case com_sun_glass_ui_Cursor_CURSOR_CLOSED_HAND: + cursor_name = g_strdup("grabbing"); + break; + case com_sun_glass_ui_Cursor_CURSOR_OPEN_HAND: + cursor_name = g_strdup("grab"); + break; + case com_sun_glass_ui_Cursor_CURSOR_POINTING_HAND: + cursor_name = g_strdup("pointer"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_UP: + cursor_name = g_strdup("n-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_DOWN: + cursor_name = g_strdup("s-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_UPDOWN: + cursor_name = g_strdup("ns-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_LEFT: + cursor_name = g_strdup("w-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_RIGHT: + cursor_name = g_strdup("e-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_LEFTRIGHT: + cursor_name = g_strdup("ew-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_SOUTHWEST: + cursor_name = g_strdup("sw-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_NORTHEAST: + cursor_name = g_strdup("ne-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_SOUTHEAST: + cursor_name = g_strdup("se-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_RESIZE_NORTHWEST: + cursor_name = g_strdup("nw-resize"); + break; + case com_sun_glass_ui_Cursor_CURSOR_MOVE: + cursor_name = g_strdup("move"); + break; + case com_sun_glass_ui_Cursor_CURSOR_WAIT: + cursor_name = g_strdup("wait"); + break; + case com_sun_glass_ui_Cursor_CURSOR_DISAPPEAR: + case com_sun_glass_ui_Cursor_CURSOR_NONE: + cursor_name = g_strdup("none"); + break; + default: + cursor_name = g_strdup("default"); + break; + } + + GdkCursor* cursor = gdk_cursor_new_from_name(gdk_display_get_default(), cursor_name); + + if (cursor == NULL) { + cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "default"); + } + + g_free(cursor_name); + + return cursor; +} +#endif + +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkCursor + * Method: _createCursor + * Signature: (IILcom/sun/glass/ui/Pixels;)J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkCursor__1createCursor + (JNIEnv * env, jobject obj, jint x, jint y, jobject pixels) +{ + (void)obj; + + GdkPixbuf *pixbuf = NULL; + GdkCursor *cursor = NULL; + env->CallVoidMethod(pixels, jPixelsAttachData, PTR_TO_JLONG(&pixbuf)); + if (!EXCEPTION_OCCURED(env)) { + cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf, x, y); + } + g_object_unref(pixbuf); + + return PTR_TO_JLONG(cursor); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkCursor + * Method: _getBestSize + * Signature: (II)Lcom.sun.glass.ui.Size + */ +JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_gtk_GtkCursor__1getBestSize + (JNIEnv *env, jclass jCursorClass, jint width, jint height) +{ + (void)jCursorClass; + (void)width; + (void)height; + + int size = gdk_display_get_default_cursor_size(gdk_display_get_default()); + + jclass jc = env->FindClass("com/sun/glass/ui/Size"); + if (env->ExceptionCheck()) return NULL; + jobject jo = env->NewObject( + jc, + jSizeInit, + size, + size); + EXCEPTION_OCCURED(env); + return jo; +} + +} // extern "C" diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassDnDClipboard.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassDnDClipboard.cpp new file mode 100644 index 0000000000..319862f1bd --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassDnDClipboard.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011, 2014, 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 +#include "glass_general.h" +#include "glass_dnd.h" + +extern gboolean is_dnd_owner; +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkDnDClipboard + * Method: isOwner + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkDnDClipboard_isOwner + (JNIEnv *env , jobject obj) +{ + (void)env; + (void)obj; + + return (is_dnd_owner) ? JNI_TRUE : JNI_FALSE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkDnDClipboard + * Method: pushToSystemImpl + * Signature: (Ljava/util/HashMap;I)I + */ +JNIEXPORT jint JNICALL +Java_com_sun_glass_ui_gtk_GtkDnDClipboard_pushToSystemImpl + (JNIEnv * env, jobject obj, jobject data, jint supported) +{ + (void)obj; + + return execute_dnd(env, data, supported); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkDnDClipboard + * Method: pushTargetActionToSystem + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkDnDClipboard_pushTargetActionToSystem + (JNIEnv * env, jobject obj, jint action) +{ + (void)env; + (void)obj; + (void)action; + + // Never called. +} + +/* + * Class: com_sun_glass_ui_gtk_GtkDnDClipboard + * Method: popFromSystem + * Signature: (Ljava/lang/String;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_gtk_GtkDnDClipboard_popFromSystem + (JNIEnv * env, jobject obj, jstring mime) +{ + (void)obj; + + return dnd_target_get_data(env, mime); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkDnDClipboard + * Method: supportedSourceActionsFromSystem + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkDnDClipboard_supportedSourceActionsFromSystem + (JNIEnv *env, jobject obj) +{ + (void)obj; + + return dnd_target_get_supported_actions(env); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkDnDClipboard + * Method: mimesFromSystem + * Signature: ()[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_gtk_GtkDnDClipboard_mimesFromSystem + (JNIEnv * env, jobject obj) +{ + (void)obj; + + return dnd_target_get_mimes(env); +} + +} // extern "C" diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassPixels.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassPixels.cpp new file mode 100644 index 0000000000..354f3c60ce --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassPixels.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2011, 2014, 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 +#include +#include +#include +#include +#include +#include + +#include "glass_general.h" + +static void my_free(guchar *pixels, gpointer data) { + (void)data; + + g_free(pixels); +} + +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkPixels + * Method: _copyPixels + * Signature: (Ljava/nio/Buffer;Ljava/nio/Buffer;I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkPixels__1copyPixels + (JNIEnv *env, jobject obj, jobject jDst, jobject jSrc, jint jSize) +{ + (void)obj; + + //Taken from MacPixels (and fixed) + void *src = env->GetDirectBufferAddress(jSrc); + void *dst = env->GetDirectBufferAddress(jDst); + if ((src != NULL) && (dst != NULL) && (jSize > 0)) + { + memcpy(dst, src, jSize * 4); + } +} + +/* + * Class: com_sun_glass_ui_gtk_GtkPixels + * Method: _attachInt + * Signature: (JIILjava/nio/IntBuffer;[II)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkPixels__1attachInt + (JNIEnv * env, jobject obj, jlong ptr, jint w, jint h, jobject ints, jintArray array, jint offset) +{ + (void)obj; + + jint *data; + GdkPixbuf **pixbuf; + guint8 *dataRGBA; + + if (array == NULL) { + data = (jint*) env->GetDirectBufferAddress(ints); + assert((w*h*4 + offset * 4) == env->GetDirectBufferCapacity(ints)); + } else { + assert((w*h + offset) == env->GetArrayLength(array)); + data = (jint*) env->GetPrimitiveArrayCritical(array, 0); + } + + pixbuf = (GdkPixbuf**)JLONG_TO_PTR(ptr); + dataRGBA = convert_BGRA_to_RGBA(data + offset, w*4, h); + *pixbuf = gdk_pixbuf_new_from_data(dataRGBA, GDK_COLORSPACE_RGB, TRUE, 8, + w, h, w * 4, (GdkPixbufDestroyNotify) my_free, NULL); + if (array != NULL) { + env->ReleasePrimitiveArrayCritical(array, data, 0); + } +} + + +/* + * Class: com_sun_glass_ui_gtk_GtkPixels + * Method: _attachByte + * Signature: (JIILjava/nio/ByteBuffer;[BI)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkPixels__1attachByte + (JNIEnv * env, jobject obj, jlong ptr, jint w, jint h, jobject bytes, jbyteArray array, jint offset) +{ + (void)obj; + + jbyte *data; + GdkPixbuf **pixbuf; + guint8 *dataRGBA; + + if (array == NULL) { + data = (jbyte*) env->GetDirectBufferAddress(bytes); + assert((w*h*4 + offset) == env->GetDirectBufferCapacity(bytes)); + } else { + assert((w*h*4 + offset) == env->GetArrayLength(array)); + data = (jbyte*) env->GetPrimitiveArrayCritical(array, 0); + } + + pixbuf = (GdkPixbuf**)JLONG_TO_PTR(ptr); + dataRGBA = convert_BGRA_to_RGBA((const int*)(data + offset), w*4, h); + *pixbuf = gdk_pixbuf_new_from_data(dataRGBA, GDK_COLORSPACE_RGB, TRUE, 8, + w, h, w * 4, (GdkPixbufDestroyNotify) my_free, NULL); + if (array != NULL) { + env->ReleasePrimitiveArrayCritical(array, data, 0); + } +} + +} // extern "C" diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassRobot.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassRobot.cpp new file mode 100644 index 0000000000..1ac32749f2 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassRobot.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2011, 2018, 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "glass_general.h" +#include "glass_key.h" +#include "glass_screen.h" + +#define MOUSE_BACK_BTN 8 +#define MOUSE_FORWARD_BTN 9 + +static void checkXTest(JNIEnv* env) { + int32_t major_opcode, first_event, first_error; + int32_t event_basep, error_basep, majorp, minorp; + static int32_t isXTestAvailable; + static gboolean checkDone = FALSE; + if (!checkDone) { + /* check if XTest is available */ + isXTestAvailable = XQueryExtension(gdk_x11_get_default_xdisplay(), XTestExtensionName, &major_opcode, &first_event, &first_error); + if (isXTestAvailable) { + /* check if XTest version is OK */ + XTestQueryExtension(gdk_x11_get_default_xdisplay(), &event_basep, &error_basep, &majorp, &minorp); + if (majorp < 2 || (majorp == 2 && minorp < 2)) { + isXTestAvailable = False; + } else { + XTestGrabControl(gdk_x11_get_default_xdisplay(), True); + } + } + checkDone = TRUE; + } + if (!isXTestAvailable) { + jclass cls = env->FindClass("java/lang/UnsupportedOperationException"); + if (env->ExceptionCheck()) return; + env->ThrowNew(cls, "Glass Robot needs XTest extension to work"); + } +} + +static void keyButton(jint code, gboolean press) +{ + Display *xdisplay = gdk_x11_get_default_xdisplay(); + gint gdk_keyval = find_gdk_keyval_for_glass_keycode(code); + GdkKeymapKey *keys; + gint n_keys; + if (gdk_keyval == -1) { + return; + } + gdk_keymap_get_entries_for_keyval(gdk_keymap_get_for_display(gdk_x11_lookup_xdisplay(xdisplay)), + gdk_keyval, &keys, &n_keys); + if (n_keys < 1) { + return; + } + + XTestFakeKeyEvent(xdisplay, + keys[0].keycode, + press ? True : False, + CurrentTime); + g_free(keys); + XSync(xdisplay, False); +} + +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _keyPress + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1keyPress + (JNIEnv *env, jobject obj, jint code) +{ + (void)obj; + + checkXTest(env); + keyButton(code, TRUE); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _keyRelease + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1keyRelease + (JNIEnv *env, jobject obj, jint code) +{ + (void)obj; + + checkXTest(env); + keyButton(code, FALSE); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _mouseMove + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mouseMove + (JNIEnv *env, jobject obj, jint x, jint y) +{ + (void)obj; + + Display *xdisplay = gdk_x11_get_default_xdisplay(); + checkXTest(env); + jfloat uiScale = getUIScale(); + x = rint(x * uiScale); + y = rint(y * uiScale); + + //-1 means current is current screen + XTestFakeMotionEvent(xdisplay, -1, x, y, CurrentTime); + XSync(xdisplay, False); +} + +static void mouseButtons(jint buttons, gboolean press) +{ + Display *xdisplay = gdk_x11_get_default_xdisplay(); + if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_LEFT_BTN) { + XTestFakeButtonEvent(xdisplay, 1, press, CurrentTime); + } + if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_MIDDLE_BTN) { + XTestFakeButtonEvent(xdisplay, 2, press, CurrentTime); + } + if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_RIGHT_BTN) { + XTestFakeButtonEvent(xdisplay, 3, press, CurrentTime); + } + if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_BACK_BTN) { + XTestFakeButtonEvent(xdisplay, MOUSE_BACK_BTN, press, CurrentTime); + } + if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_FORWARD_BTN) { + XTestFakeButtonEvent(xdisplay, MOUSE_FORWARD_BTN, press, CurrentTime); + } + + XSync(xdisplay, False); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _mousePress + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mousePress + (JNIEnv *env, jobject obj, jint buttons) +{ + (void)obj; + + checkXTest(env); + mouseButtons(buttons, TRUE); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _mouseRelease + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mouseRelease + (JNIEnv *env, jobject obj, jint buttons) +{ + (void)obj; + + checkXTest(env); + mouseButtons(buttons, FALSE); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _mouseWheel + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mouseWheel + (JNIEnv *env, jobject obj, jint amt) +{ + (void)obj; + + Display *xdisplay = gdk_x11_get_default_xdisplay(); + int repeat = abs(amt); + int button = amt < 0 ? 4 : 5; + int i; + + checkXTest(env); + for (i = 0; i < repeat; i++) { + XTestFakeButtonEvent(xdisplay, button, True, CurrentTime); + XTestFakeButtonEvent(xdisplay, button, False, CurrentTime); + } + XSync(xdisplay, False); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _getMouseX + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1getMouseX + (JNIEnv *env, jobject obj) +{ + (void)env; + (void)obj; + + jint x; + glass_gdk_display_get_pointer(gdk_display_get_default(), &x, NULL); + x = rint(x / getUIScale()); + return x; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _getMouseY + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1getMouseY + (JNIEnv *env, jobject obj) +{ + (void)env; + (void)obj; + + jint y; + glass_gdk_display_get_pointer(gdk_display_get_default(), NULL, &y); + y = rint(y / getUIScale()); + return y; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkRobot + * Method: _getScreenCapture + * Signature: (IIII[I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1getScreenCapture + (JNIEnv * env, jobject obj, jint x, jint y, jint width, jint height, jintArray data) +{ + (void)obj; + + GdkPixbuf *screenshot, *tmp; + GdkWindow *root_window = gdk_get_default_root_window(); + + tmp = glass_pixbuf_from_window(root_window, x, y, width, height); + screenshot = gdk_pixbuf_add_alpha(tmp, FALSE, 0, 0, 0); + g_object_unref(tmp); + + jint *pixels = (jint *)convert_BGRA_to_RGBA((int*)gdk_pixbuf_get_pixels(screenshot), width * 4, height); + env->SetIntArrayRegion(data, 0, height * width, pixels); + g_free(pixels); + + g_object_unref(screenshot); +} + +} // extern "C" diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassSystemClipboard.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassSystemClipboard.cpp new file mode 100644 index 0000000000..b3793cda91 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassSystemClipboard.cpp @@ -0,0 +1,664 @@ +/* + * Copyright (c) 2011, 2017, 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 "com_sun_glass_ui_gtk_GtkSystemClipboard.h" +#include "glass_general.h" + +#include +#include +#include + +static GdkAtom MIME_TEXT_PLAIN_TARGET; + +static GdkAtom MIME_TEXT_URI_LIST_TARGET; + +static GdkAtom MIME_JAVA_IMAGE; + +static GdkAtom MIME_FILES_TARGET; + +static jmethodID String_init_ID; + +static jmethodID String_getBytes_ID; + +static jstring charset; + +static void init_atoms() +{ + static int initialized = 0; + + if (!initialized) { + MIME_TEXT_PLAIN_TARGET = gdk_atom_intern_static_string("text/plain"); + + MIME_TEXT_URI_LIST_TARGET = gdk_atom_intern_static_string("text/uri-list"); + + MIME_JAVA_IMAGE = gdk_atom_intern_static_string("application/x-java-rawimage"); + + MIME_FILES_TARGET = gdk_atom_intern_static_string("application/x-java-file-list"); + + String_init_ID = mainEnv->GetMethodID(jStringCls, + "", "([BLjava/lang/String;)V"); + + String_getBytes_ID = mainEnv->GetMethodID(jStringCls, + "getBytes", "(Ljava/lang/String;)[B"); + + jstring set = mainEnv->NewStringUTF("UTF-8"); + CHECK_JNI_EXCEPTION(mainEnv); + charset = (jstring)mainEnv->NewGlobalRef(set); + mainEnv->DeleteLocalRef(set); + + initialized = 1; + } +} + + +static GtkClipboard* clipboard = NULL; +static gboolean is_clipboard_owner = FALSE; +static gboolean is_clipboard_updated_by_glass = FALSE; + +static GtkClipboard *get_clipboard() { + if (clipboard == NULL) { + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + } + return clipboard; +} + +static jobject createUTF(JNIEnv *env, char *data) { + int len; + jbyteArray ba; + jobject jdata; + len = strlen(data); + ba = env->NewByteArray(len); + EXCEPTION_OCCURED(env); + env->SetByteArrayRegion(ba, 0, len, (jbyte *)data); + EXCEPTION_OCCURED(env); + jdata = env->NewObject(jStringCls, String_init_ID, ba, charset); + env->DeleteLocalRef(ba); + EXCEPTION_OCCURED(env); + return jdata; +} + +static char *getUTF(JNIEnv *env, jstring str) { + jbyteArray ba; + jsize len; + char *data; + ba = (jbyteArray) env->CallObjectMethod(str, String_getBytes_ID, charset); + EXCEPTION_OCCURED(env); + len = env->GetArrayLength(ba); + data = (char *)g_malloc(len + 1); + env->GetByteArrayRegion(ba, 0, len, (jbyte *)data); + env->DeleteLocalRef(ba); + EXCEPTION_OCCURED(env); + data[len] = 0; + return data; +} + +static void add_target_from_jstring(JNIEnv *env, GtkTargetList *list, jstring string) +{ + const char *gstring = getUTF(env, string); + if (g_strcmp0(gstring, "text/plain") == 0) { + gtk_target_list_add_text_targets(list, 0); + } else if (g_strcmp0(gstring, "application/x-java-rawimage") == 0) { + gtk_target_list_add_image_targets(list, 0, TRUE); + } else if (g_strcmp0(gstring, "application/x-java-file-list") == 0) { + gtk_target_list_add(list, MIME_TEXT_URI_LIST_TARGET, 0, 0); + } else { + gtk_target_list_add(list, gdk_atom_intern(gstring, FALSE), 0, 0); + } + + g_free((gpointer)gstring); +} + +static void data_to_targets(JNIEnv *env, jobject data, GtkTargetEntry **targets, gint *ntargets) +{ + jobject keys; + jobject keysIterator; + jstring next; + + GtkTargetList *list = gtk_target_list_new(NULL, 0); + + keys = env->CallObjectMethod(data, jMapKeySet, NULL); + CHECK_JNI_EXCEPTION(env) + keysIterator = env->CallObjectMethod(keys, jIterableIterator, NULL); + CHECK_JNI_EXCEPTION(env) + + while (env->CallBooleanMethod(keysIterator, jIteratorHasNext) == JNI_TRUE) { + next = (jstring) env->CallObjectMethod(keysIterator, jIteratorNext, NULL); + add_target_from_jstring(env, list, next); + } + *targets = gtk_target_table_new_from_list(list, ntargets); + gtk_target_list_unref(list); + +} + +static void set_text_data(GtkSelectionData *selection_data, jstring data) +{ + const char *text_data = getUTF(mainEnv, data); + guint ntext_data = strlen(text_data); + + gtk_selection_data_set_text(selection_data, text_data, ntext_data); + g_free((gpointer)text_data); +} + +static void set_jstring_data(GtkSelectionData *selection_data, GdkAtom target, jstring data) +{ + const char *text_data = getUTF(mainEnv, data); + guint ntext_data = strlen(text_data); + + //XXX is target == type ?? + gtk_selection_data_set(selection_data, target, 8, (const guchar *)text_data, ntext_data); + g_free((gpointer)text_data); +} + +static void set_bytebuffer_data(GtkSelectionData *selection_data, GdkAtom target, jobject data) +{ + jbyteArray byteArray = (jbyteArray) mainEnv->CallObjectMethod(data, jByteBufferArray); + CHECK_JNI_EXCEPTION(mainEnv) + jbyte* raw = mainEnv->GetByteArrayElements(byteArray, NULL); + jsize nraw = mainEnv->GetArrayLength(byteArray); + + //XXX is target == type ?? + gtk_selection_data_set(selection_data, target, 8, (guchar *)raw, (gint)nraw); + + mainEnv->ReleaseByteArrayElements(byteArray, raw, JNI_ABORT); +} + +static void set_uri_data(GtkSelectionData *selection_data, jobject data) { + const gchar* url = NULL; + jstring jurl = NULL; + + jobjectArray files_array = NULL; + gsize files_cnt = 0; + + jstring typeString; + + typeString = mainEnv->NewStringUTF("text/uri-list"); + if (mainEnv->ExceptionCheck()) return; + if (mainEnv->CallBooleanMethod(data, jMapContainsKey, typeString, NULL)) { + jurl = (jstring) mainEnv->CallObjectMethod(data, jMapGet, typeString, NULL); + CHECK_JNI_EXCEPTION(mainEnv); + url = getUTF(mainEnv, jurl); + } + + typeString = mainEnv->NewStringUTF("application/x-java-file-list"); + if (mainEnv->ExceptionCheck()) return; + if (mainEnv->CallBooleanMethod(data, jMapContainsKey, typeString, NULL)) { + files_array = (jobjectArray) mainEnv->CallObjectMethod(data, jMapGet, typeString, NULL); + CHECK_JNI_EXCEPTION(mainEnv); + if (files_array) { + files_cnt = mainEnv->GetArrayLength(files_array); + } + } + + if (!url && !files_cnt) { + return; + } + + gsize uri_cnt = files_cnt + (url ? 1 : 0); + + gchar **uris = + (gchar**) glass_try_malloc0_n(uri_cnt + 1, // uris must be a NULL-terminated array of strings + sizeof(gchar*)); + if (!uris) { + if (url) { + g_free((gpointer)url); + } + glass_throw_oom(mainEnv, "Failed to allocate uri data"); + return; + } + + gsize i = 0; + if (files_cnt > 0) { + for (; i < files_cnt; ++i) { + jstring string = (jstring) mainEnv->GetObjectArrayElement(files_array, i); + const gchar* file = getUTF(mainEnv, string); + uris[i] = g_filename_to_uri(file, NULL, NULL); + g_free((gpointer)file); + } + } + + if (url) { + uris[i] = (gchar*) url; + } + //http://www.ietf.org/rfc/rfc2483.txt + gtk_selection_data_set_uris(selection_data, uris); + + for (i = 0; i < uri_cnt; ++i) { + if (uris[i] != url) { + g_free(uris[i]); + } + } + + if (url) { + g_free((gpointer)url); + } + g_free(uris); +} + +static void set_image_data(GtkSelectionData *selection_data, jobject pixels) +{ + GdkPixbuf *pixbuf = NULL; + + mainEnv->CallVoidMethod(pixels, jPixelsAttachData, PTR_TO_JLONG(&pixbuf)); + if (!EXCEPTION_OCCURED(mainEnv)) { + gtk_selection_data_set_pixbuf(selection_data, pixbuf); + } + + g_object_unref(pixbuf); +} + +static void set_data(GdkAtom target, GtkSelectionData *selection_data, jobject data) +{ + gchar *name = gdk_atom_name(target); + jstring typeString; + jobject result; + + if (gtk_targets_include_text(&target, 1)) { + typeString = mainEnv->NewStringUTF("text/plain"); + EXCEPTION_OCCURED(mainEnv); + result = mainEnv->CallObjectMethod(data, jMapGet, typeString, NULL); + if (!EXCEPTION_OCCURED(mainEnv) && result != NULL) { + set_text_data(selection_data, (jstring)result); + } + } else if (gtk_targets_include_image(&target, 1, TRUE)) { + typeString = mainEnv->NewStringUTF("application/x-java-rawimage"); + EXCEPTION_OCCURED(mainEnv); + result = mainEnv->CallObjectMethod(data, jMapGet, typeString, NULL); + if (!EXCEPTION_OCCURED(mainEnv) && result != NULL) { + set_image_data(selection_data, result); + } + } else if (target == MIME_TEXT_URI_LIST_TARGET) { + set_uri_data(selection_data, data); + } else { + typeString = mainEnv->NewStringUTF(name); + EXCEPTION_OCCURED(mainEnv); + result = mainEnv->CallObjectMethod(data, jMapGet, typeString, NULL); + if (!EXCEPTION_OCCURED(mainEnv) && result != NULL) { + if (mainEnv->IsInstanceOf(result, jStringCls)) { + set_jstring_data(selection_data, target, (jstring)result); + } else if (mainEnv->IsInstanceOf(result, jByteBufferCls)) { + set_bytebuffer_data(selection_data, target, result); + } + } + } + + g_free(name); +} + +static void set_data_func(GtkClipboard *clipboard, GtkSelectionData *selection_data, + guint info, gpointer user_data) +{ + (void)clipboard; + (void)info; + + jobject data = (jobject) user_data; //HashMap + GdkAtom target; + target = gtk_selection_data_get_target(selection_data); + + set_data(target, selection_data, data); + CHECK_JNI_EXCEPTION(mainEnv); +} + +static void clear_data_func(GtkClipboard *clipboard, gpointer user_data) +{ + (void)clipboard; + + jobject data =(jobject) user_data; + mainEnv->DeleteGlobalRef(data); +} + +static jobject get_data_text(JNIEnv *env) +{ + gchar *data = gtk_clipboard_wait_for_text(get_clipboard()); + if (data == NULL) { + return NULL; + } + jobject jdata = createUTF(env, data); + EXCEPTION_OCCURED(env); + g_free(data); + return jdata; +} + +static jobject get_data_uri_list(JNIEnv *env, gboolean files) +{ + return uris_to_java(env, gtk_clipboard_wait_for_uris(get_clipboard()), files); +} + +static jobject get_data_image(JNIEnv* env) { + GdkPixbuf* pixbuf; + guchar *data; + jbyteArray data_array; + jobject buffer, result; + int w,h,stride; + + pixbuf = gtk_clipboard_wait_for_image(get_clipboard()); + if (pixbuf == NULL) { + return NULL; + } + + if (!gdk_pixbuf_get_has_alpha(pixbuf)) { + GdkPixbuf *tmp_buf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); + g_object_unref(pixbuf); + pixbuf = tmp_buf; + } + w = gdk_pixbuf_get_width(pixbuf); + h = gdk_pixbuf_get_height(pixbuf); + stride = gdk_pixbuf_get_rowstride(pixbuf); + + data = gdk_pixbuf_get_pixels(pixbuf); + + //Actually, we are converting RGBA to BGRA, but that's the same operation + data = (guchar*) convert_BGRA_to_RGBA((int*)data, stride, h); + + data_array = env->NewByteArray(stride*h); + EXCEPTION_OCCURED(env); + env->SetByteArrayRegion(data_array, 0, stride*h, (jbyte*)data); + EXCEPTION_OCCURED(env); + + buffer = env->CallStaticObjectMethod(jByteBufferCls, jByteBufferWrap, data_array); + EXCEPTION_OCCURED(env); + result = env->NewObject(jGtkPixelsCls, jGtkPixelsInit, w, h, buffer); + EXCEPTION_OCCURED(env); + + g_free(data); + g_object_unref(pixbuf); + + return result; + +} + +static jobject get_data_raw(JNIEnv *env, const char* mime, gboolean string_data) +{ + GtkSelectionData *data; + const guchar *raw_data; + jsize length; + jbyteArray array; + jobject result = NULL; + data = gtk_clipboard_wait_for_contents(get_clipboard(), gdk_atom_intern(mime, FALSE)); + if (data != NULL) { + raw_data = glass_gtk_selection_data_get_data_with_length(data, &length); + if (string_data) { + result = createUTF(env, (char*)raw_data); + EXCEPTION_OCCURED(env); + } else { + array = env->NewByteArray(length); + EXCEPTION_OCCURED(env); + env->SetByteArrayRegion(array, 0, length, (const jbyte*)raw_data); + EXCEPTION_OCCURED(env); + result = env->CallStaticObjectMethod(jByteBufferCls, jByteBufferWrap, array); + EXCEPTION_OCCURED(env); + } + gtk_selection_data_free(data); + } + return result; +} + +static jobject jclipboard = NULL; +static gulong owner_change_handler_id = 0; + +static void clipboard_owner_changed_callback(GtkClipboard *clipboard, GdkEventOwnerChange *event, jobject obj) +{ + (void)clipboard; + (void)event; + (void)obj; + + is_clipboard_owner = is_clipboard_updated_by_glass; + is_clipboard_updated_by_glass = FALSE; + mainEnv->CallVoidMethod(obj, jClipboardContentChanged); + CHECK_JNI_EXCEPTION(mainEnv) +} + +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: init + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_init + (JNIEnv *env, jobject obj) +{ + if (jclipboard) { + ERROR0("GtkSystemClipboard already initiated"); + } + + jclipboard = env->NewGlobalRef(obj); + owner_change_handler_id = g_signal_connect(G_OBJECT(get_clipboard()), + "owner-change", G_CALLBACK(clipboard_owner_changed_callback), jclipboard); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: dispose + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_dispose + (JNIEnv *env, jobject obj) +{ + (void)obj; + + g_signal_handler_disconnect(G_OBJECT(get_clipboard()), owner_change_handler_id); + env->DeleteGlobalRef(jclipboard); + + owner_change_handler_id = 0; + jclipboard = NULL; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: isOwner + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_isOwner + (JNIEnv *env, jobject obj) +{ + (void)env; + (void)obj; + + return is_clipboard_owner ? JNI_TRUE : JNI_FALSE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: pushToSystem + * Signature: (Ljava/util/HashMap;I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_pushToSystem + (JNIEnv * env, jobject obj, jobject data, jint supported) +{ + (void)obj; + (void)supported; + + GtkTargetEntry* targets = NULL; + gint ntargets; + data = env->NewGlobalRef(data); + init_atoms(); + data_to_targets(env, data, &targets, &ntargets); + CHECK_JNI_EXCEPTION(env) + if (targets) { + gtk_clipboard_set_with_data(get_clipboard(), targets, ntargets, set_data_func, clear_data_func, data); + gtk_target_table_free(targets, ntargets); + } else { + // targets == NULL means that we want to clear clipboard. + // Passing NULL as targets parameter to gtk_clipboard_set_with_data will produce Gtk-CRITICAL assertion + // but passing 0 as n_targets parameter allows to set empty list of available mime types + GtkTargetEntry dummy_targets = {(gchar*) "MIME_DUMMY_TARGET", 0, 0}; + gtk_clipboard_set_with_data(get_clipboard(), &dummy_targets, 0, set_data_func, clear_data_func, data); + } + + is_clipboard_updated_by_glass = TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: pushTargetActionToSystem + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_pushTargetActionToSystem + (JNIEnv * env, jobject obj, jint action) +{ + //Not used for clipboard. DnD only + (void)env; + (void)obj; + (void)action; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: popFromSystem + * Signature: (Ljava/lang/String;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_popFromSystem + (JNIEnv * env, jobject obj, jstring mime) +{ + (void)env; + (void)obj; + + const char* cmime = env->GetStringUTFChars(mime, NULL); + jobject result; + + init_atoms(); + if (g_strcmp0(cmime, "text/plain") == 0) { + result = get_data_text(env); + } else if (g_strcmp0(cmime, "text/uri-list") == 0) { + result = get_data_uri_list(env, FALSE); + } else if (g_str_has_prefix(cmime, "text/")) { + result = get_data_raw(env, cmime, TRUE); + } else if (g_strcmp0(cmime, "application/x-java-file-list") == 0) { + result = get_data_uri_list(env, TRUE); + } else if (g_strcmp0(cmime, "application/x-java-rawimage") == 0 ) { + result = get_data_image(env); + } else { + result = get_data_raw(env, cmime, FALSE); + } + LOG_EXCEPTION(env) + env->ReleaseStringUTFChars(mime, cmime); + + return result; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: supportedSourceActionsFromSystem + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_supportedSourceActionsFromSystem + (JNIEnv *env, jobject obj) +{ + //Not used for clipboard. DnD only + (void)env; + (void)obj; + return 0; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkSystemClipboard + * Method: mimesFromSystem + * Signature: ()[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_gtk_GtkSystemClipboard_mimesFromSystem + (JNIEnv * env, jobject obj) +{ + (void)obj; + + GdkAtom *targets; + gint ntargets; + gint i; + GdkAtom *convertible; + GdkAtom *convertible_ptr; + gchar *name; + jobjectArray result; + jstring tmpString; + + init_atoms(); + + gtk_clipboard_wait_for_targets(get_clipboard(), &targets, &ntargets); + + convertible = (GdkAtom*) glass_try_malloc0_n(ntargets * 2, sizeof(GdkAtom)); //theoretically, the number can double + if (!convertible) { + if (ntargets > 0) { + glass_throw_oom(env, "Failed to allocate mimes"); + } + g_free(targets); + return NULL; + } + + convertible_ptr = convertible; + + bool uri_list_added = false; + bool text_added = false; + bool image_added = false; + + for (i = 0; i < ntargets; ++i) { + //handle text targets + //if (targets[i] == TEXT_TARGET || targets[i] == STRING_TARGET || targets[i] == UTF8_STRING_TARGET) { + + if (gtk_targets_include_text(targets + i, 1) && !text_added) { + *(convertible_ptr++) = MIME_TEXT_PLAIN_TARGET; + text_added = true; + } else if (gtk_targets_include_image(targets + i, 1, TRUE) && !image_added) { + *(convertible_ptr++) = MIME_JAVA_IMAGE; + image_added = true; + } + //TODO text/x-moz-url ? RT-17802 + + if (targets[i] == MIME_TEXT_URI_LIST_TARGET) { + if (uri_list_added) { + continue; + } + + gchar** uris = gtk_clipboard_wait_for_uris(get_clipboard()); + if (uris) { + guint size = g_strv_length(uris); + guint files_cnt = get_files_count(uris); + if (files_cnt) { + *(convertible_ptr++) = MIME_FILES_TARGET; + } + if (size - files_cnt) { + *(convertible_ptr++) = MIME_TEXT_URI_LIST_TARGET; + } + g_strfreev(uris); + } + uri_list_added = true; + } else { + *(convertible_ptr++) = targets[i]; + } + } + + result = env->NewObjectArray(convertible_ptr - convertible, jStringCls, NULL); + EXCEPTION_OCCURED(env); + for (i = 0; convertible + i < convertible_ptr; ++i) { + name = gdk_atom_name(convertible[i]); + tmpString = env->NewStringUTF(name); + EXCEPTION_OCCURED(env); + env->SetObjectArrayElement(result, (jsize)i, tmpString); + EXCEPTION_OCCURED(env); + g_free(name); + } + + g_free(targets); + g_free(convertible); + return result; +} + +} // extern "C" { diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassTimer.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassTimer.cpp new file mode 100644 index 0000000000..d5154ae6ef --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassTimer.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011, 2016, 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 +#include "glass_general.h" + +#include +#include +#include + +static gboolean call_runnable_in_timer + (gpointer); + +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkTimer + * Method: _start + * Signature: (Ljava/lang/Runnable;I)J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1start + (JNIEnv * env, jobject obj, jobject runnable, jint period) +{ + (void)obj; + + RunnableContext* context = (RunnableContext*) malloc(sizeof(RunnableContext)); + context->runnable = env->NewGlobalRef(runnable); + context->flag = 0; + gdk_threads_add_timeout_full(G_PRIORITY_HIGH_IDLE, period, call_runnable_in_timer, context, NULL); + return PTR_TO_JLONG(context); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkTimer + * Method: _stop + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1stop + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)obj; + + RunnableContext* context = (RunnableContext*) JLONG_TO_PTR(ptr); + context->flag = 1; + env->DeleteGlobalRef(context->runnable); + context->runnable = NULL; +} + +} // extern "C" + + +static gboolean call_runnable_in_timer + (gpointer data) +{ + RunnableContext* context = (RunnableContext*) data; + if (context->flag) { + free(context); + return FALSE; + } + else if (context->runnable) { + JNIEnv *env; + int envStatus = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6); + if (envStatus == JNI_EDETACHED) { + javaVM->AttachCurrentThread((void **)&env, NULL); + } + + env->CallVoidMethod(context->runnable, jRunnableRun, NULL); + LOG_EXCEPTION(env); + + if (envStatus == JNI_EDETACHED) { + javaVM->DetachCurrentThread(); + } + } + return TRUE; +} + diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassView.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassView.cpp new file mode 100644 index 0000000000..8f19fca3ce --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassView.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2011, 2020, 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 +#include + +#include +#include +#include + +#include "glass_general.h" +#include "glass_view.h" +#include "glass_window.h" + +#define JLONG_TO_GLASSVIEW(value) ((GlassView *) JLONG_TO_PTR(value)) + +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _enableInputMethodEvents + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkView_enableInputMethodEventsImpl + (JNIEnv * env, jobject obj, jlong ptr, jboolean enable) +{ + (void)env; + (void)obj; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view->current_window) { + if (enable) { + view->current_window->enableOrResetIME(); + } else { + view->current_window->disableIME(); + } + } +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _create + * Signature: (Ljava/util/Map;)J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkView__1create + (JNIEnv * env, jobject obj, jobject caps) +{ + (void)env; + (void)obj; + (void)caps; + + GlassView *view = new GlassView(); + return PTR_TO_JLONG(view); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _getNativeView + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkView__1getNativeView + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + (void)ptr; + + return 0; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _getX + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkView__1getX + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view && view->current_window) { + return view->current_window->get_geometry().view_x; + } + return 0; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _getY + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkView__1getY + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view && view->current_window) { + return view->current_window->get_geometry().view_y; + } + return 0; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _setParent + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkView__1setParent + (JNIEnv * env, jobject obj, jlong ptr, jlong parent) +{ + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + bool is_removing = view->current_window && !parent; + + view->current_window = (WindowContext*)JLONG_TO_PTR(parent); + + if (is_removing) { + env->CallVoidMethod(obj, jViewNotifyView, com_sun_glass_events_ViewEvent_REMOVE); + } else { + env->CallVoidMethod(obj, jViewNotifyView, com_sun_glass_events_ViewEvent_ADD); + } + CHECK_JNI_EXCEPTION(env); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _close + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkView__1close + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + delete JLONG_TO_GLASSVIEW(ptr); + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _scheduleRepaint + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkView__1scheduleRepaint + (JNIEnv * env, jobject obj, jlong ptr) +{ + // Seems to be unused + (void)env; + (void)obj; + (void)ptr; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _uploadPixelsDirect + * Signature: (JLjava/nio/Buffer;II)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkView__1uploadPixelsDirect +(JNIEnv *env, jobject jView, jlong ptr, jobject buffer, jint width, jint height) +{ + (void)jView; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view->current_window) { + void *data = env->GetDirectBufferAddress(buffer); + + view->current_window->paint(data, width, height); + } +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _uploadPixelsIntArray + * Signature: (J[IIII)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkView__1uploadPixelsIntArray + (JNIEnv * env, jobject obj, jlong ptr, jintArray array, jint offset, jint width, jint height) +{ + (void)obj; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view->current_window) { + int *data = NULL; + assert((width*height + offset) == env->GetArrayLength(array)); + data = (int*)env->GetPrimitiveArrayCritical(array, 0); + + view->current_window->paint(data + offset, width, height); + + env->ReleasePrimitiveArrayCritical(array, data, JNI_ABORT); + } +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _uploadPixelsByteArray + * Signature: (J[BIII)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkView__1uploadPixelsByteArray + (JNIEnv * env, jobject obj, jlong ptr, jbyteArray array, jint offset, jint width, jint height) +{ + (void)obj; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view->current_window) { + unsigned char *data = NULL; + + assert((4*width*height + offset) == env->GetArrayLength(array)); + data = (unsigned char*)env->GetPrimitiveArrayCritical(array, 0); + + view->current_window->paint(data + offset, width, height); + + env->ReleasePrimitiveArrayCritical(array, data, JNI_ABORT); + } +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _enterFullscreen + * Signature: (JZZZ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkView__1enterFullscreen + (JNIEnv * env, jobject obj, jlong ptr, jboolean animate, jboolean keepRation, jboolean hideCursor) +{ + (void)animate; + (void)keepRation; + (void)hideCursor; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view->current_window) { + view->current_window->enter_fullscreen(); + env->CallVoidMethod(obj, jViewNotifyView, com_sun_glass_events_ViewEvent_FULLSCREEN_ENTER); + CHECK_JNI_EXCEPTION_RET(env, JNI_FALSE) + } + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkView + * Method: _exitFullscreen + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkView__1exitFullscreen + (JNIEnv * env, jobject obj, jlong ptr, jboolean animate) +{ + (void)animate; + + GlassView* view = JLONG_TO_GLASSVIEW(ptr); + if (view->current_window) { + if (view->embedded_window) { + view->embedded_window->exit_fullscreen(); + } else { + view->current_window->exit_fullscreen(); + } + env->CallVoidMethod(obj, jViewNotifyView, com_sun_glass_events_ViewEvent_FULLSCREEN_EXIT); + CHECK_JNI_EXCEPTION(env) + } + +} + +} // extern "C" diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassWindow.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassWindow.cpp new file mode 100644 index 0000000000..407bf54c1e --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/GlassWindow.cpp @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2011, 2020, 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 +#include +#include + +#include +#include +#include "glass_general.h" +#include "glass_evloop.h" +#include "glass_window.h" + +#define JLONG_TO_WINDOW_CTX(ptr) ((WindowContext*)JLONG_TO_PTR(ptr)) + +static WindowFrameType glass_mask_to_window_frame_type(jint mask) { + if (mask & com_sun_glass_ui_gtk_GtkWindow_TRANSPARENT) { + return TRANSPARENT; + } + if (mask & com_sun_glass_ui_gtk_GtkWindow_TITLED) { + return TITLED; + } + return UNTITLED; +} + +static WindowType glass_mask_to_window_type(jint mask) { + if (mask & com_sun_glass_ui_gtk_GtkWindow_POPUP) { + return POPUP; + } + if (mask & com_sun_glass_ui_gtk_GtkWindow_UTILITY) { + return UTILITY; + } + return NORMAL; +} + +static GdkWMFunction glass_mask_to_wm_function(jint mask) { + int func = GDK_FUNC_RESIZE | GDK_FUNC_MOVE; + + if (mask & com_sun_glass_ui_gtk_GtkWindow_CLOSABLE) { + func |= GDK_FUNC_CLOSE; + } + if (mask & com_sun_glass_ui_gtk_GtkWindow_MAXIMIZABLE) { + func |= GDK_FUNC_MAXIMIZE; + } + if (mask & com_sun_glass_ui_gtk_GtkWindow_MINIMIZABLE) { + func |= GDK_FUNC_MINIMIZE; + } + + return (GdkWMFunction) func; +} + +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _createWindow + * Signature: (JJI)J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1createWindow + (JNIEnv * env, jobject obj, jlong owner, jlong screen, jint mask) +{ + (void)env; + + WindowContext* parent = JLONG_TO_WINDOW_CTX(owner); + + WindowContext* ctx = new WindowContext(obj, + parent, + screen, + glass_mask_to_window_frame_type(mask), + glass_mask_to_window_type(mask), + glass_mask_to_wm_function(mask) + ); + + return PTR_TO_JLONG(ctx); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _createChildWindow + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1createChildWindow + (JNIEnv * env, jobject obj , jlong owner) +{ + (void)env; + + return 0; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _close + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1close + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + destroy_and_delete_ctx(ctx); + return JNI_TRUE; // return value not used +} +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setView + * Signature: (JLcom/sun/glass/ui/View;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setView + (JNIEnv * env, jobject obj, jlong ptr, jobject view) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + return (ctx->set_view(view)) ? JNI_TRUE : JNI_FALSE; +} +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _showOrHideChildren + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1showOrHideChildren + (JNIEnv *env, jobject obj, jlong ptr, jboolean show) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->show_or_hide_children(show); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: minimizeImpl + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow_minimizeImpl + (JNIEnv * env, jobject obj, jlong ptr, jboolean minimize) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_minimized(minimize); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: maximizeImpl + * Signature: (JZZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow_maximizeImpl + (JNIEnv * env, jobject obj, jlong ptr, jboolean maximize, jboolean wasMaximized) +{ + (void)env; + (void)obj; + (void)wasMaximized; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_maximized(maximize); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: setBoundsImpl + * Signature: (JIIZZIIII)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow_setBoundsImpl + (JNIEnv * env, jobject obj, jlong ptr, jint x, jint y, jboolean xSet, jboolean ySet, jint w, jint h, jint cw, jint ch) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_bounds(x, y, xSet, ySet, w, h, cw, ch); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: setVisibleImpl + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow_setVisibleImpl + (JNIEnv * env, jobject obj, jlong ptr, jboolean visible) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_visible(visible); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setResizable + * Signature: (JZ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setResizable + (JNIEnv * env, jobject obj, jlong ptr, jboolean resizable) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_resizable(resizable); + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _requestFocus + * Signature: (JI)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1requestFocus + (JNIEnv * env, jobject obj, jlong ptr, jint focus) +{ + (void)env; + (void)obj; + (void)focus; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->request_focus(); + return JNI_TRUE; //not used +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setFocusable + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setFocusable + (JNIEnv * env, jobject obj, jlong ptr, jboolean focusable) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_focusable(focusable); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _grabFocus + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1grabFocus + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + return ctx->grab_focus(); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _ungrabFocus + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1ungrabFocus + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->ungrab_focus(); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setTitle + * Signature: (JLjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setTitle + (JNIEnv * env, jobject obj, jlong ptr, jstring title) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + const char* ctitle = mainEnv->GetStringUTFChars(title, NULL); + ctx->set_title(ctitle); + mainEnv->ReleaseStringUTFChars(title, ctitle); + + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setLevel + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setLevel + (JNIEnv * env, jobject obj, jlong ptr, jint level) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_level(level); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setAlpha + * Signature: (JF)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setAlpha + (JNIEnv * env, jobject obj, jlong ptr, jfloat alpha) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_alpha(alpha); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setBackground + * Signature: (JFFF)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setBackground + (JNIEnv * env, jobject obj, jlong ptr, jfloat r, jfloat g, jfloat b) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_background(r, g, b); + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setEnabled + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setEnabled + (JNIEnv * env, jobject obj, jlong ptr, jboolean enabled) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_enabled(enabled); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setMinimumSize + * Signature: (JII)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setMinimumSize + (JNIEnv * env, jobject obj, jlong ptr, jint w, jint h) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + if (w < 0 || h < 0) return JNI_FALSE; + ctx->set_minimum_size(w, h); + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setMaximumSize + * Signature: (JII)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setMaximumSize + (JNIEnv * env, jobject obj, jlong ptr, jint w, jint h) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + if (w == 0 || h == 0) return JNI_FALSE; + + ctx->set_maximum_size(w, h); + return JNI_TRUE; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setIcon + * Signature: (JLcom/sun/glass/ui/Pixels;)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setIcon + (JNIEnv * env, jobject obj, jlong ptr, jobject pixels) +{ + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + GdkPixbuf *pixbuf = NULL; + if (pixels != NULL) { + env->CallVoidMethod(pixels, jPixelsAttachData, PTR_TO_JLONG(&pixbuf)); + } + if (!EXCEPTION_OCCURED(env)) { + ctx->set_icon(pixbuf); + } + if (pixbuf != NULL) g_object_unref(pixbuf); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _toFront + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1toFront + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->restack(true); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _toBack + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1toBack + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->restack(false); + +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _enterModal + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1enterModal + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_modal(true); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _enterModalWithWindow + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1enterModalWithWindow + (JNIEnv * env, jobject obj, jlong ptrDialog, jlong ptrWindow) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptrDialog); + WindowContext* parent_ctx = JLONG_TO_WINDOW_CTX(ptrWindow); + ctx->set_modal(true, parent_ctx); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _exitModal + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1exitModal + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_modal(false); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkCursor + * Method: _setCursorType + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setCursorType + (JNIEnv * env, jobject obj, jlong ptr, jint type) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + GdkCursor *cursor = get_native_cursor(type); + ctx->set_cursor(cursor); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkCursor + * Method: _setCustomCursor + * Signature: (JLcom/sun/glass/ui/Cursor;)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setCustomCursor + (JNIEnv * env, jobject obj, jlong ptr, jobject jCursor) +{ + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + GdkCursor *cursor = (GdkCursor*)JLONG_TO_PTR(env->GetLongField(jCursor, jCursorPtr)); + + ctx->set_cursor(cursor); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: isVisible + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkWindow_isVisible + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + return ctx->is_visible() ? JNI_TRUE : JNI_FALSE; +} +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1getNativeWindowImpl + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + return GDK_WINDOW_XID(ctx->get_gdk_window()); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _setGravity + * Signature: (JFF)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1setGravity + (JNIEnv * env, jobject obj, jlong ptr, jfloat xGravity, jfloat yGravity) +{ + (void)env; + (void)obj; + + WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); + ctx->set_gravity(xGravity, yGravity); + +} + + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _getEmbeddedX + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1getEmbeddedX + (JNIEnv *env, jobject obj, jlong ptr) { + (void)env; + (void)obj; + + return 0; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: _getEmbeddedY + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1getEmbeddedY + (JNIEnv *env, jobject obj, jlong ptr) { + (void)env; + (void)obj; + + return 0; +} + +/* + * Class: com_sun_glass_ui_gtk_GtkWindow + * Method: getFrameExtents + * Signature: (J[I)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow_getFrameExtents + (JNIEnv * env, jobject obj, jlong ptr, jintArray extarr) +{ + (void)obj; +} + +} // extern "C" diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_dnd.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_dnd.cpp new file mode 100644 index 0000000000..77b12c4a01 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_dnd.cpp @@ -0,0 +1,953 @@ +/* + * Copyright (c) 2011, 2020, 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_dnd.h" +#include "glass_general.h" +#include "glass_evloop.h" + +#include "com_sun_glass_events_DndEvent.h" +#include "com_sun_glass_ui_gtk_GtkDnDClipboard.h" + +#include +#include + +#include +#include +#include + +/************************* COMMON *********************************************/ +static jint translate_gdk_action_to_glass(GdkDragAction action) { + jint result = 0; + result |= (action & GDK_ACTION_COPY) ? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_COPY : 0; + result |= (action & GDK_ACTION_MOVE) ? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_MOVE : 0; + result |= (action & GDK_ACTION_LINK) ? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_REFERENCE : 0; + return result; +} + +static GdkDragAction translate_glass_action_to_gdk(jint action) { + int result = 0; + result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_COPY) ? GDK_ACTION_COPY : 0; + result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_MOVE) ? GDK_ACTION_MOVE : 0; + result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_REFERENCE) ? GDK_ACTION_LINK : 0; + return static_cast(result); +} + +static void clear_global_ref(gpointer data) { + mainEnv->DeleteGlobalRef((jobject) data); +} + +static void dnd_set_performed_action(jint performed_action); + +static jint dnd_get_performed_action(); + +enum { + TARGET_TEXT, + TARGET_IMAGE, + TARGET_URI, + TARGET_RAW +}; + +/************************* TARGET *********************************************/ + +static struct { + GdkDragContext *ctx; + GtkSelectionData *data; + gboolean just_entered; + jobjectArray mimes; +} target_ctx = {NULL, NULL, FALSE, NULL}; + +gboolean is_dnd_owner = FALSE; +GtkWidget *drag_widget = NULL; + +gboolean is_in_drag() { + return drag_widget != NULL; +} + +static void reset_target_ctx() { + if (target_ctx.mimes != NULL) { + mainEnv->DeleteGlobalRef(target_ctx.mimes); + } + + memset(&target_ctx, 0, sizeof(target_ctx)); +} + +static gboolean dnd_drag_motion_callback(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + gpointer user_data) { + + WindowContext *ctx = (WindowContext *) user_data; + + if (target_ctx.ctx == NULL || (target_ctx.ctx != context && !target_ctx.just_entered)) { + reset_target_ctx(); + is_dnd_owner = is_in_drag(); + target_ctx.ctx = context; + target_ctx.just_entered = TRUE; + } + + gint x_abs, y_abs; + gdk_window_get_origin(gdk_drag_context_get_dest_window(context), &x_abs, &y_abs); + + jmethodID method = target_ctx.just_entered ? jViewNotifyDragEnter : jViewNotifyDragOver; + + GdkDragAction suggested = gdk_drag_context_get_suggested_action(context); + GdkDragAction result = translate_glass_action_to_gdk(mainEnv->CallIntMethod(ctx->get_jview(), method, + (jint) x, (jint) y, + (jint) x_abs, (jint) y_abs, + translate_gdk_action_to_glass( + suggested))); + CHECK_JNI_EXCEPTION_RET(mainEnv, FALSE) + + if (target_ctx.just_entered) { + target_ctx.just_entered = FALSE; + } + + gdk_drag_status(context, result, GDK_CURRENT_TIME); + + return (gboolean) result; +} + +static gboolean dnd_drag_drop_callback(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + gpointer user_data) { + if (target_ctx.ctx == NULL || target_ctx.just_entered) { + return FALSE; // Do not process drop events if no enter event and subsequent motion event were received + } + + GdkAtom target = gtk_drag_dest_find_target(widget, context, NULL); + + if (target == GDK_NONE) { + // used for RAW + target = gdk_atom_intern_static_string(""); + } + + gtk_drag_get_data(widget, context, target, GDK_CURRENT_TIME); + + return TRUE; +} + +static void dnd_on_drag_data_received_callback(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) { + WindowContext *ctx = (WindowContext *) user_data; + + if (gtk_selection_data_get_length(data) == 0) { + gtk_drag_finish(context, FALSE, FALSE, GDK_CURRENT_TIME); + reset_target_ctx(); + return; + } + + gint x_abs, y_abs; + gdk_window_get_origin(gdk_drag_context_get_dest_window(context), &x_abs, &y_abs); + GdkDragAction selected = gdk_drag_context_get_selected_action(context); + target_ctx.data = data; + + // Delay the notify for when we have the data + mainEnv->CallIntMethod(ctx->get_jview(), jViewNotifyDragDrop, + (jint) x, (jint) y, + (jint) x_abs, (jint) y_abs, + translate_gdk_action_to_glass(selected)); + LOG_EXCEPTION(mainEnv) + + gtk_drag_finish(context, selected, selected == GDK_ACTION_MOVE, GDK_CURRENT_TIME); +} + +void dnd_drag_leave_callback(WindowContext *ctx) { + mainEnv->CallVoidMethod(ctx->get_jview(), jViewNotifyDragLeave, NULL); + CHECK_JNI_EXCEPTION(mainEnv) + + reset_target_ctx(); +} + +void glass_dnd_attach_context(WindowContext *ctx) { + gtk_drag_dest_set(ctx->get_gtk_widget(), (GtkDestDefaults) 0, NULL, 0, + (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)); + + GtkTargetList *target_list = gtk_target_list_new(NULL, 0); + gtk_target_list_add_image_targets(target_list, TARGET_IMAGE, TRUE); + gtk_target_list_add_uri_targets(target_list, TARGET_URI); + gtk_target_list_add_text_targets(target_list, TARGET_TEXT); + gtk_target_list_add(target_list, gdk_atom_intern_static_string(""), 0, TARGET_RAW); + + gtk_drag_dest_set_target_list(ctx->get_gtk_widget(), target_list); + + g_signal_connect(ctx->get_gtk_widget(), "drag-motion", G_CALLBACK(dnd_drag_motion_callback), ctx); + g_signal_connect(ctx->get_gtk_widget(), "drag-drop", G_CALLBACK(dnd_drag_drop_callback), ctx); + g_signal_connect(ctx->get_gtk_widget(), "drag-data-received", G_CALLBACK(dnd_on_drag_data_received_callback), ctx); +} + +static gboolean check_state_in_drag(JNIEnv *env) { + if (!target_ctx.ctx) { + jclass jc = env->FindClass("java/lang/IllegalStateException"); + if (!env->ExceptionCheck()) { + env->ThrowNew(jc, + "Cannot get supported actions. Drag pointer haven't entered the application window"); + } + return TRUE; + } + return FALSE; +} + +static GdkAtom *get_target_ctx_target_atoms(gint *size) { + GList *targets = gdk_drag_context_list_targets(target_ctx.ctx); + gint s = (gint) g_list_length(targets); + GdkAtom *atoms = (GdkAtom *) g_try_malloc0(sizeof(GdkAtom) * s); + + int i = 0; + for (; targets != NULL; targets = targets->next) { + atoms[i++] = (GdkAtom) targets->data; + } + + *size = s; + + g_list_free(targets); + return atoms; +} + +jobjectArray dnd_target_get_mimes(JNIEnv *env) { + if (check_state_in_drag(env)) { + return NULL; + } + + if (!target_ctx.mimes) { + jobject set = env->NewObject(jHashSetCls, jHashSetInit, NULL); + EXCEPTION_OCCURED(env); + + gboolean was_set = FALSE; + gint size; + GdkAtom *targets = get_target_ctx_target_atoms(&size); + + if (gtk_targets_include_image(targets, size, TRUE)) { + jstring jStr = env->NewStringUTF("application/x-java-rawimage"); + EXCEPTION_OCCURED(env); + env->CallBooleanMethod(set, jSetAdd, jStr, NULL); + EXCEPTION_OCCURED(env); + was_set = TRUE; + } + if (gtk_targets_include_uri(targets, size)) { + // it's a possibility + jstring jStr = env->NewStringUTF("application/x-java-file-list"); + EXCEPTION_OCCURED(env); + env->CallBooleanMethod(set, jSetAdd, jStr, NULL); + EXCEPTION_OCCURED(env); + + jstring jStr2 = env->NewStringUTF("text/uri-list"); + EXCEPTION_OCCURED(env); + env->CallBooleanMethod(set, jSetAdd, jStr2, NULL); + EXCEPTION_OCCURED(env); + was_set = TRUE; + } else if (gtk_targets_include_text(targets, size)) { + jstring jStr = env->NewStringUTF("text/plain"); + EXCEPTION_OCCURED(env); + env->CallBooleanMethod(set, jSetAdd, jStr, NULL); + EXCEPTION_OCCURED(env); + was_set = TRUE; + } + + g_free(targets); + + if (!was_set) { + GdkAtom target = gtk_selection_data_get_target(target_ctx.data); + gchar *name = gdk_atom_name(target); + + jstring jStr = env->NewStringUTF(name); + EXCEPTION_OCCURED(env); + env->CallBooleanMethod(set, jSetAdd, jStr, NULL); + EXCEPTION_OCCURED(env); + g_free(name); + } + + target_ctx.mimes = env->NewObjectArray(env->CallIntMethod(set, jSetSize, NULL), + jStringCls, NULL); + EXCEPTION_OCCURED(env); + target_ctx.mimes = (jobjectArray) env->CallObjectMethod(set, jSetToArray, target_ctx.mimes, NULL); + target_ctx.mimes = (jobjectArray) env->NewGlobalRef(target_ctx.mimes); + } + + return target_ctx.mimes; +} + +jint dnd_target_get_supported_actions(JNIEnv *env) { + if (check_state_in_drag(env)) { + return 0; + } + return translate_gdk_action_to_glass(gdk_drag_context_get_actions(target_ctx.ctx)); +} + +static jobject dnd_target_get_string(JNIEnv *env) { + jobject result = NULL; + + GdkAtom atom = gtk_selection_data_get_data_type(target_ctx.data); + guchar *data = gtk_selection_data_get_text(target_ctx.data); + + if (data) { + result = env->NewStringUTF((char *) data); + EXCEPTION_OCCURED(env); + + g_free(data); + } + + return result; +} + +static jobject dnd_target_get_list(JNIEnv *env, gboolean files) { + jobject result = NULL; + GdkAtom atom = gtk_selection_data_get_selection(target_ctx.data); + gchar **data = gtk_selection_data_get_uris(target_ctx.data); + + if (data) { + result = uris_to_java(env, data, files); + // uris_to_java frees it + //g_strfreev(data); + } + + return result; +} + +static jobject dnd_target_get_image(JNIEnv *env) { + jobject result = NULL; + + GdkAtom atom = gtk_selection_data_get_selection(target_ctx.data); + GdkPixbuf *buf = gtk_selection_data_get_pixbuf(target_ctx.data); + + if (buf == NULL) { + return NULL; + } + + gint length = gtk_selection_data_get_length(target_ctx.data); + + if (!gdk_pixbuf_get_has_alpha(buf)) { + GdkPixbuf *tmp_buf = gdk_pixbuf_add_alpha(buf, FALSE, 0, 0, 0); + g_object_unref(buf); + buf = tmp_buf; + } + + gint w, h, stride; + guchar *cdata; + jbyteArray data_array; + jobject buffer; + + w = gdk_pixbuf_get_width(buf); + h = gdk_pixbuf_get_height(buf); + stride = gdk_pixbuf_get_rowstride(buf); + + cdata = gdk_pixbuf_get_pixels(buf); + + //Actually, we are converting RGBA to BGRA, but that's the same operation + cdata = (guchar *) convert_BGRA_to_RGBA((int *) cdata, stride, h); + data_array = env->NewByteArray(stride * h); + EXCEPTION_OCCURED(env); + env->SetByteArrayRegion(data_array, 0, stride * h, (jbyte *) cdata); + EXCEPTION_OCCURED(env); + + buffer = env->CallStaticObjectMethod(jByteBufferCls, jByteBufferWrap, data_array); + EXCEPTION_OCCURED(env); + result = env->NewObject(jGtkPixelsCls, jGtkPixelsInit, w, h, buffer); + EXCEPTION_OCCURED(env); + + g_object_unref(buf); + g_free(cdata); + + return result; +} + +static jobject dnd_target_get_raw(JNIEnv *env, GdkAtom target, gboolean string_data) { + jobject result = NULL; + GdkAtom atom = gtk_selection_data_get_selection(target_ctx.data); + const guchar *data = gtk_selection_data_get_data(target_ctx.data); + + if (string_data) { + result = env->NewStringUTF((char *) data); + EXCEPTION_OCCURED(env); + } else { + gint length = gtk_selection_data_get_length(target_ctx.data); + + jbyteArray array = env->NewByteArray((jsize) length); + EXCEPTION_OCCURED(env); + env->SetByteArrayRegion(array, 0, length, (const jbyte *) data); + EXCEPTION_OCCURED(env); + result = env->CallStaticObjectMethod(jByteBufferCls, jByteBufferWrap, array); + EXCEPTION_OCCURED(env); + } + + return result; +} + +jobject dnd_target_get_data(JNIEnv *env, jstring mime) { + jobject ret = NULL; + + if (check_state_in_drag(env)) { + return NULL; + } + + const char *cmime = env->GetStringUTFChars(mime, NULL); + + if (g_strcmp0(cmime, "text/plain") == 0) { + ret = dnd_target_get_string(env); + } else if (g_strcmp0(cmime, "text/uri-list") == 0) { + ret = dnd_target_get_list(env, FALSE); + } else if (g_str_has_prefix(cmime, "text/")) { + ret = dnd_target_get_raw(env, gdk_atom_intern(cmime, FALSE), TRUE); + } else if (g_strcmp0(cmime, "application/x-java-file-list") == 0) { + ret = dnd_target_get_list(env, TRUE); + } else if (g_strcmp0(cmime, "application/x-java-rawimage") == 0) { + ret = dnd_target_get_image(env); + } else { + ret = dnd_target_get_raw(env, gdk_atom_intern(cmime, FALSE), FALSE); + } + + LOG_EXCEPTION(env) + env->ReleaseStringUTFChars(mime, cmime); + + return ret; +} + +/************************* SOURCE *********************************************/ + +static jint dnd_performed_action; + +const char *const SOURCE_DND_DATA = "fx-dnd-data"; + +static void dnd_set_performed_action(jint performed_action) { + dnd_performed_action = performed_action; +} + +static jint dnd_get_performed_action() { + return dnd_performed_action; +} + +static void pixbufDestroyNotifyFunc(guchar *pixels, gpointer) { + if (pixels != NULL) { + g_free(pixels); + } +} + +static jobject dnd_source_get_data(GtkWidget *widget, const char *key) { + jobject data = (jobject) g_object_get_data(G_OBJECT(widget), SOURCE_DND_DATA); + jstring string = mainEnv->NewStringUTF(key); + EXCEPTION_OCCURED(mainEnv); + jobject result = mainEnv->CallObjectMethod(data, jMapGet, string, NULL); + + return (EXCEPTION_OCCURED(mainEnv)) ? NULL : result; +} + +static void add_gtk_target_from_jstring(JNIEnv *env, GtkTargetList **list, jstring string, guint flags) { + const char *gstring = env->GetStringUTFChars(string, NULL); + + if (g_strcmp0(gstring, "text/plain") == 0) { + gtk_target_list_add_text_targets(*list, TARGET_TEXT); + } else if (g_strcmp0(gstring, "application/x-java-rawimage") == 0) { + gtk_target_list_add_image_targets(*list, TARGET_IMAGE, TRUE); + } else if (g_strcmp0(gstring, "application/x-java-file-list") == 0) { + gtk_target_list_add_uri_targets(*list, TARGET_URI); + } else if (g_strcmp0(gstring, "application/x-java-drag-image") == 0 + || g_strcmp0(gstring, "application/x-java-drag-image-offset") == 0) { + // do nothing - those are DragView information + } else { + GdkAtom atom = gdk_atom_intern(gstring, FALSE); + gtk_target_list_add(*list, atom, flags, TARGET_RAW); + } + + env->ReleaseStringUTFChars(string, gstring); +} + +static GtkTargetList *data_to_gtk_target_list(JNIEnv *env, jobject data) { + guint flags = GTK_TARGET_OTHER_APP | GTK_TARGET_SAME_APP; + + jobject keys; + jobject keysIterator; + jstring next; + + GtkTargetList *tlist = gtk_target_list_new(NULL, 0); + + gint added_count = 0; + + keys = env->CallObjectMethod(data, jMapKeySet, NULL); + JNI_EXCEPTION_TO_CPP(env) + keysIterator = env->CallObjectMethod(keys, jIterableIterator, NULL); + JNI_EXCEPTION_TO_CPP(env) + while (env->CallBooleanMethod(keysIterator, jIteratorHasNext) == JNI_TRUE) { + next = (jstring) env->CallObjectMethod(keysIterator, jIteratorNext, NULL); + JNI_EXCEPTION_TO_CPP(env) + add_gtk_target_from_jstring(env, &tlist, next, flags); + } + + return tlist; +} + +static gboolean dnd_source_set_string(GtkWidget *widget, GtkSelectionData *data, GdkAtom atom) { + gboolean is_data_set; + + jstring string = (jstring) dnd_source_get_data(widget, "text/plain"); + if (!string) { + return FALSE; + } + + const char *cstring = mainEnv->GetStringUTFChars(string, NULL); + gint size = strlen(cstring); + is_data_set = gtk_selection_data_set_text(data, (gchar *) cstring, size); + + mainEnv->ReleaseStringUTFChars(string, cstring); + + return is_data_set; +} + +static gboolean dnd_source_set_image(GtkWidget *widget, GtkSelectionData *data, GdkAtom atom) { + jobject pixels = dnd_source_get_data(widget, "application/x-java-rawimage"); + if (!pixels) { + g_warning("DND source failed to set image\n"); + return FALSE; + } + + gchar *buffer; + gsize size; + const char *type; + GdkPixbuf *pixbuf = NULL; + gboolean is_data_set; + + mainEnv->CallVoidMethod(pixels, jPixelsAttachData, PTR_TO_JLONG(&pixbuf)); + + if (!EXCEPTION_OCCURED(mainEnv)) { + is_data_set = gtk_selection_data_set_pixbuf(data, pixbuf); + } + + g_object_unref(pixbuf); + + return is_data_set; +} + +static gboolean dnd_source_set_uri(GtkWidget *widget, GtkSelectionData *data, GdkAtom atom) { + const gchar *url = NULL; + jstring jurl = NULL; + + jobjectArray files_array = NULL; + gsize files_cnt = 0; + + if (jurl = (jstring) dnd_source_get_data(widget, "text/uri-list")) { + url = mainEnv->GetStringUTFChars(jurl, NULL); + } + + if (files_array = (jobjectArray) dnd_source_get_data(widget, "application/x-java-file-list")) { + files_cnt = mainEnv->GetArrayLength(files_array); + } + + if (!url && !files_cnt) { + return FALSE; + } + + gboolean is_data_set; + GString *res = g_string_new(NULL); //http://www.ietf.org/rfc/rfc2483.txt + + if (files_cnt > 0) { + for (gsize i = 0; i < files_cnt; ++i) { + jstring string = (jstring) mainEnv->GetObjectArrayElement(files_array, i); + EXCEPTION_OCCURED(mainEnv); + const gchar *file = mainEnv->GetStringUTFChars(string, NULL); + gchar *uri = g_filename_to_uri(file, NULL, NULL); + + g_string_append(res, uri); + g_string_append(res, URI_LIST_LINE_BREAK); + + g_free(uri); + mainEnv->ReleaseStringUTFChars(string, file); + } + } + if (url) { + g_string_append(res, url); + g_string_append(res, URI_LIST_LINE_BREAK); + mainEnv->ReleaseStringUTFChars(jurl, url); + } + + gchar *uri[2]; + uri[0] = g_string_free(res, FALSE); + uri[1] = NULL; + + is_data_set = gtk_selection_data_set_uris(data, uri); + + g_free(uri[0]); + + return is_data_set; +} + +static gboolean dnd_source_set_raw(GtkWidget *widget, GtkSelectionData *sel_data, GdkAtom atom) { + gchar *target_name = gdk_atom_name(atom); + jobject data = dnd_source_get_data(widget, target_name); + gboolean is_data_set = FALSE; + if (data) { + if (mainEnv->IsInstanceOf(data, jStringCls)) { + const char *cstring = mainEnv->GetStringUTFChars((jstring) data, NULL); + if (cstring) { + is_data_set = gtk_selection_data_set_text(sel_data, (gchar *) cstring, strlen(cstring)); + mainEnv->ReleaseStringUTFChars((jstring) data, cstring); + } + } else if (mainEnv->IsInstanceOf(data, jByteBufferCls)) { + jbyteArray byteArray = (jbyteArray) mainEnv->CallObjectMethod(data, jByteBufferArray); + if (!EXCEPTION_OCCURED(mainEnv)) { + jbyte *raw = mainEnv->GetByteArrayElements(byteArray, NULL); + if (raw) { + jsize nraw = mainEnv->GetArrayLength(byteArray); + gtk_selection_data_set(sel_data, atom, 8, (guchar *) raw, nraw); + mainEnv->ReleaseByteArrayElements(byteArray, raw, JNI_ABORT); + is_data_set = TRUE; + } + } + } + } + + g_free(target_name); + return is_data_set; +} + +static gboolean dnd_destroy_drag_widget_callback(gpointer) { + if (drag_widget) { + gtk_widget_destroy(drag_widget); + drag_widget = NULL; + } + + return FALSE; +} + +static void dnd_end_callback(GtkWidget *widget, + GdkDragContext *context, + gpointer user_data) { + if (drag_widget) { + GdkDragAction action = gdk_drag_context_get_selected_action(context); + dnd_set_performed_action(translate_gdk_action_to_glass(action)); + } + gdk_threads_add_idle((GSourceFunc) dnd_destroy_drag_widget_callback, NULL); +} + +static gboolean dnd_drag_failed_callback(GtkWidget *widget, + GdkDragContext *context, + GtkDragResult result, + gpointer user_data) { + dnd_set_performed_action(com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_NONE); + gdk_threads_add_idle((GSourceFunc) dnd_destroy_drag_widget_callback, NULL); + + return FALSE; +} + +static void dnd_data_get_callback(GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) { + GdkAtom atom = gtk_selection_data_get_target(data); + + switch (info) { + case TARGET_TEXT: + dnd_source_set_string(widget, data, atom); + break; + case TARGET_IMAGE: + dnd_source_set_image(widget, data, atom); + break; + case TARGET_URI: + dnd_source_set_uri(widget, data, atom); + break; + default: + dnd_source_set_raw(widget, data, atom); + } +} + +static void dnd_drag_begin_callback(GtkWidget *widget, + GdkDragContext *context, + gpointer user_data) { + DragView::set_drag_view(widget, context); +} + +static void dnd_source_push_data(JNIEnv *env, jobject data, jint supported) { + if (supported == 0) { + return; // No supported actions, do nothing + } + + data = env->NewGlobalRef(data); + + GdkDragAction actions = translate_glass_action_to_gdk(supported); + + // this widget is used only to pass events and will + // be destroyed on drag end + drag_widget = gtk_invisible_new(); + gtk_widget_show(drag_widget); + + g_object_set_data_full(G_OBJECT(drag_widget), SOURCE_DND_DATA, data, clear_global_ref); + + g_signal_connect(drag_widget, "drag-begin", + G_CALLBACK(dnd_drag_begin_callback), NULL); + + g_signal_connect(drag_widget, "drag-failed", + G_CALLBACK(dnd_drag_failed_callback), NULL); + + g_signal_connect(drag_widget, "drag-data-get", + G_CALLBACK(dnd_data_get_callback), NULL); + + g_signal_connect(drag_widget, "drag-end", + G_CALLBACK(dnd_end_callback), NULL); + + GtkTargetList *tlist = data_to_gtk_target_list(env, data); + + GdkDragContext *context; + + gint x, y; + glass_gdk_master_pointer_get_position(&x, &y); + + is_dnd_owner = TRUE; + + context = gtk_drag_begin(drag_widget, tlist, actions, 1, NULL); + + gtk_target_list_unref(tlist); +} + +jint execute_dnd(JNIEnv *env, jobject data, jint supported) { + try { + dnd_source_push_data(env, data, supported); + } catch (jni_exception &) { + gdk_threads_add_idle((GSourceFunc) dnd_destroy_drag_widget_callback, NULL); + return com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_NONE; + } + + while (is_in_drag()) { + gtk_main_iteration(); + } + + return dnd_get_performed_action(); +} + +/******************** DRAG VIEW ***************************/ +DragView::View *DragView::view = NULL; + +gboolean DragView::get_drag_image_offset(GtkWidget *widget, int *x, int *y) { + gboolean offset_set = FALSE; + jobject bb = dnd_source_get_data(widget, "application/x-java-drag-image-offset"); + if (bb) { + jbyteArray byteArray = (jbyteArray) mainEnv->CallObjectMethod(bb, jByteBufferArray); + if (!EXCEPTION_OCCURED(mainEnv)) { + jbyte *raw = mainEnv->GetByteArrayElements(byteArray, NULL); + jsize nraw = mainEnv->GetArrayLength(byteArray); + + if ((size_t) nraw >= sizeof(jint) * 2) { + jint *r = (jint *) raw; + *x = BSWAP_32(r[0]); + *y = BSWAP_32(r[1]); + offset_set = TRUE; + } + + mainEnv->ReleaseByteArrayElements(byteArray, raw, JNI_ABORT); + } + } + return offset_set; +} + +GdkPixbuf *DragView::get_drag_image(GtkWidget *widget, gboolean *is_raw_image, gint *width, gint *height) { + GdkPixbuf *pixbuf = NULL; + gboolean is_raw = FALSE; + + jobject drag_image = dnd_source_get_data(widget, "application/x-java-drag-image"); + + if (drag_image) { + jbyteArray byteArray = (jbyteArray) mainEnv->CallObjectMethod(drag_image, jByteBufferArray); + if (!EXCEPTION_OCCURED(mainEnv)) { + + jbyte *raw = mainEnv->GetByteArrayElements(byteArray, NULL); + jsize nraw = mainEnv->GetArrayLength(byteArray); + + int w = 0, h = 0; + int whsz = sizeof(jint) * 2; // Pixels are stored right after two ints + // in this byteArray: width and height + if (nraw > whsz) { + jint *int_raw = (jint *) raw; + w = BSWAP_32(int_raw[0]); + h = BSWAP_32(int_raw[1]); + + // We should have enough pixels for requested width and height + if ((nraw - whsz) / 4 - w * h >= 0) { + guchar *data = (guchar *) g_try_malloc0(nraw - whsz); + if (data) { + memcpy(data, (raw + whsz), nraw - whsz); + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, 8, + w, h, w * 4, pixbufDestroyNotifyFunc, NULL); + } + } + } + mainEnv->ReleaseByteArrayElements(byteArray, raw, JNI_ABORT); + } + } + + if (!GDK_IS_PIXBUF(pixbuf)) { + jobject pixels = dnd_source_get_data(widget, "application/x-java-rawimage"); + if (pixels) { + is_raw = TRUE; + mainEnv->CallVoidMethod(pixels, jPixelsAttachData, PTR_TO_JLONG(&pixbuf)); + CHECK_JNI_EXCEPTION_RET(mainEnv, NULL) + } + } + + if (!GDK_IS_PIXBUF(pixbuf)) { + return NULL; + } + + int w = gdk_pixbuf_get_width(pixbuf); + int h = gdk_pixbuf_get_height(pixbuf); + + if (w > DRAG_IMAGE_MAX_WIDTH || h > DRAG_IMAGE_MAX_HEIGH) { + double rw = DRAG_IMAGE_MAX_WIDTH / (double) w; + double rh = DRAG_IMAGE_MAX_HEIGH / (double) h; + double r = MIN(rw, rh); + + int new_w = w * r; + int new_h = h * r; + + w = new_w; + h = new_h; + + GdkPixbuf *tmp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, new_w, new_h, GDK_INTERP_TILES); + g_object_unref(pixbuf); + if (!GDK_IS_PIXBUF(tmp_pixbuf)) { + return NULL; + } + pixbuf = tmp_pixbuf; + } + + *is_raw_image = is_raw; + *width = w; + *height = h; + + return pixbuf; +} + +void DragView::set_drag_view(GtkWidget *widget, GdkDragContext *context) { + gboolean is_raw_image = FALSE; + gint w = 0, h = 0; + GdkPixbuf *pixbuf = get_drag_image(widget, &is_raw_image, &w, &h); + + if (GDK_IS_PIXBUF(pixbuf)) { + gint offset_x = w / 2; + gint offset_y = h / 2; + + gboolean is_offset_set = get_drag_image_offset(widget, &offset_x, &offset_y); + + DragView::view = new DragView::View(context, pixbuf, w, h, is_raw_image, + is_offset_set, offset_x, offset_y); + } +} + +static void on_screen_changed(GtkWidget *widget, GdkScreen *previous_screen, gpointer view) { + (void) widget; + (void) previous_screen; + + ((DragView::View *) view)->screen_changed(); +} + +static gboolean on_expose(GtkWidget *widget, GdkEventExpose *event, gpointer view) { + (void) widget; + (void) event; + + ((DragView::View *) view)->expose(); + return FALSE; +} + +DragView::View::View(GdkDragContext *_context, GdkPixbuf *_pixbuf, gint _width, gint _height, + gboolean _is_raw_image, gboolean _is_offset_set, gint _offset_x, gint _offset_y) : + context(_context), + pixbuf(_pixbuf), + width(_width), + height(_height), + is_raw_image(_is_raw_image), + is_offset_set(_is_offset_set), + offset_x(_offset_x), + offset_y(_offset_y) { +#ifdef GLASS_GTK3 + gtk_drag_set_icon_pixbuf(context, pixbuf, offset_x, offset_y); +#else + widget = gtk_window_new(GTK_WINDOW_POPUP); + gtk_window_set_type_hint(GTK_WINDOW(widget), GDK_WINDOW_TYPE_HINT_DND); + gtk_widget_set_events(widget, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + screen_changed(); + + gtk_widget_realize(widget); + + gtk_widget_set_app_paintable(widget, TRUE); + g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(on_expose), this); + g_signal_connect(G_OBJECT(widget), "screen-changed", G_CALLBACK(on_screen_changed), this); + gtk_widget_set_size_request(widget, width, height); + gtk_window_set_decorated(GTK_WINDOW(widget), FALSE); + + gtk_widget_show_all(widget); + gtk_drag_set_icon_widget(context, widget, offset_x, offset_y); +#endif +} + +void DragView::View::screen_changed() { + GdkScreen *screen = gtk_widget_get_screen(widget); + + glass_configure_window_transparency(widget, true); + + if (!gdk_screen_is_composited(screen)) { + if (!is_offset_set) { + offset_x = 1; + offset_y = 1; + } + } +} + +void DragView::View::expose() { +#ifdef GLASS_GTK2 + cairo_t *context = gdk_cairo_create(gtk_widget_get_window(widget)); + + cairo_surface_t *cairo_surface; + + guchar *pixels = is_raw_image + ? (guchar *) convert_BGRA_to_RGBA((const int *) gdk_pixbuf_get_pixels(pixbuf), + gdk_pixbuf_get_rowstride(pixbuf), + height) + : gdk_pixbuf_get_pixels(pixbuf); + + cairo_surface = cairo_image_surface_create_for_data( + pixels, + CAIRO_FORMAT_ARGB32, + width, height, width * 4); + + cairo_set_source_surface(context, cairo_surface, 0, 0); + cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); + cairo_paint(context); + + if (is_raw_image) { + g_free(pixels); + } + cairo_destroy(context); + cairo_surface_destroy(cairo_surface); +#endif +} diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_dnd.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_dnd.h new file mode 100644 index 0000000000..0ab64d81a2 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_dnd.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011, 2020, 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. + */ +#ifndef GLASS_DND_H +#define GLASS_DND_H + +#include "glass_general.h" +#include "glass_window.h" +#include + +#include + +void process_dnd_target(WindowContext *, GdkEventDND *); +void glass_dnd_attach_context(WindowContext *ctx); +void dnd_drag_leave_callback(WindowContext *ctx); +jint dnd_target_get_supported_actions(JNIEnv *); +jobjectArray dnd_target_get_mimes(JNIEnv *); +jobject dnd_target_get_data(JNIEnv *, jstring); + +void process_dnd_source(GdkWindow *, GdkEvent *); +jint execute_dnd(JNIEnv *, jobject, jint); + +gboolean is_in_drag(); + +#define DRAG_IMAGE_MAX_WIDTH 320 +#define DRAG_IMAGE_MAX_HEIGH 240 + +#define BSWAP_32(x) (((uint)(x) << 24) | \ + (((uint)(x) << 8) & 0xff0000) | \ + (((uint)(x) >> 8) & 0xff00) | \ + ((uint)(x) >> 24)) + +class DragView { +public: + class View { + GdkDragContext* context; + GtkWidget* widget; + GdkPixbuf* pixbuf; + gint width, height; + gboolean is_raw_image; + gboolean is_offset_set; + gint offset_x, offset_y; + public: + View(GdkDragContext* context, GdkPixbuf* pixbuf, gint width, gint height, + gboolean is_raw_image, gboolean is_offset_set, gint offset_x, gint offset_y); + void screen_changed(); + void expose(); + void move(gint x, gint y); + ~View(); + private: + View(View&); + View& operator=(const View&); + }; + + static void reset_drag_view(); + static void set_drag_view(GtkWidget* widget, GdkDragContext* context); + static void move(gint x, gint y); + +private: + static View* view; + static gboolean get_drag_image_offset(GtkWidget *widget, int* x, int* y); + static GdkPixbuf* get_drag_image(GtkWidget* widget, gboolean* is_raw_image, gint* width, gint* height); + + DragView() {} + DragView(DragView&); + DragView& operator=(const DragView&); +}; + +#endif /* GLASS_DND_H */ diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_evloop.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_evloop.cpp new file mode 100644 index 0000000000..53fb4833d1 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_evloop.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011, 2013, 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_evloop.h" + +#include +#include + +static GSList * evloopHookList; + +#define GEVL_HOOK_REGISTRATION_IMPL(ptr) ((GevlHookRegistrationImpl *) ptr) + +typedef struct _GevlHookRegistrationImpl { + GevlHookFunction hookFn; + void * data; +} GevlHookRegistrationImpl; + +void +glass_evloop_initialize() { +} + +void +glass_evloop_finalize() { + GSList * ptr = evloopHookList; + while (ptr != NULL) { + free(ptr->data); + ptr = g_slist_next(ptr); + } + + g_slist_free(evloopHookList); + evloopHookList = NULL; +} + +void +glass_evloop_call_hooks(GdkEvent * event) { + GSList * ptr = evloopHookList; + while (ptr != NULL) { + GevlHookRegistrationImpl * hookReg = + GEVL_HOOK_REGISTRATION_IMPL(ptr->data); + hookReg->hookFn(event, hookReg->data); + + ptr = g_slist_next(ptr); + } +} + +GevlHookRegistration +glass_evloop_hook_add(GevlHookFunction hookFn, void * data) { + GevlHookRegistrationImpl * hookReg = + (GevlHookRegistrationImpl *) + malloc(sizeof(GevlHookRegistrationImpl)); + + if (hookReg != NULL) { + hookReg->hookFn = hookFn; + hookReg->data = data; + + evloopHookList = g_slist_prepend(evloopHookList, hookReg); + } + + return hookReg; +} + +void +glass_evloop_hook_remove(GevlHookRegistration hookReg) { + evloopHookList = g_slist_remove(evloopHookList, hookReg); + free(hookReg); +} + diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_evloop.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_evloop.h new file mode 100644 index 0000000000..96aa7328f8 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_evloop.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011, 2013, 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. + */ +#ifndef GLASS_EVENT_LOOP_H +#define GLASS_EVENT_LOOP_H + +#include + +typedef void (* GevlHookFunction)(GdkEvent * event, void * data); +typedef void * GevlHookRegistration; + +void glass_evloop_initialize(); +void glass_evloop_finalize(); +void glass_evloop_call_hooks(GdkEvent * event); + +GevlHookRegistration glass_evloop_hook_add(GevlHookFunction hookFn, + void * data); +void glass_evloop_hook_remove(GevlHookRegistration hookReg); + +#endif diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_general.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_general.cpp new file mode 100644 index 0000000000..6a0bfb8d30 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_general.cpp @@ -0,0 +1,787 @@ +/* + * Copyright (c) 2011, 2020, 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; + +static jboolean displayValid = JNI_FALSE; + +jboolean +is_display_valid() { + return displayValid; +} + +JavaVM* javaVM; + +#ifdef STATIC_BUILD +extern "C" { +#endif +JNIEXPORT jint JNICALL +#ifdef STATIC_BUILD +JNI_OnLoad_glassgtk3(JavaVM *jvm, void *reserved) +#else +JNI_OnLoad(JavaVM *jvm, void *reserved) +#endif +{ + (void)reserved; + + JNIEnv *env; + jclass clazz; + Display* display; + + javaVM = jvm; + 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; + + return JNI_VERSION_1_6; +} + +#ifdef STATIC_BUILD +} +#endif + +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); + //Clear in case our reporting upcall failed too! + env->ExceptionClear(); + 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; +} + +//*************************************************************************** + + +gboolean disableGrab = FALSE; +static gboolean configure_transparent_window(GtkWidget *window); +static void configure_opaque_window(GtkWidget *window); + +gboolean is_grab_disabled() { + return disableGrab; +} + +gint glass_gdk_visual_get_depth (GdkVisual * visual) +{ + // gdk_visual_get_depth is GTK 2.2 + + return gdk_visual_get_depth(visual); +} + +GdkScreen * glass_gdk_window_get_screen(GdkWindow * gdkWindow) +{ +#ifdef GLASS_GTK3 + GdkVisual * gdkVisual = gdk_window_get_visual(gdkWindow); + return gdk_visual_get_screen(gdkVisual); +#else + return gdk_window_get_screen(gdkWindow); +#endif +} + +void +glass_gdk_master_pointer_get_position(gint *x, gint *y) { +#ifdef GLASS_GTK3 + gdk_device_get_position( + gdk_device_manager_get_client_pointer( + gdk_display_get_device_manager(gdk_display_get_default())), NULL , x, y); +#else + gdk_display_get_pointer(gdk_display_get_default(), NULL, x, y, NULL); +#endif +} + +gboolean +glass_gdk_device_is_grabbed(GdkDevice *device) { +#ifdef GLASS_GTK3 + return gdk_display_device_is_grabbed(gdk_display_get_default(), device); +#else + (void) device; + return gdk_display_pointer_is_grabbed(gdk_display_get_default()); +#endif +} + +GdkWindow * +glass_gdk_device_get_window_at_position(GdkDevice *device, gint *x, gint *y) { +#ifdef GLASS_GTK3 + return gdk_device_get_window_at_position(device, x, y); +#else + (void) device; + return gdk_display_get_window_at_pointer(gdk_display_get_default(), x, y); +#endif +} + +void +glass_gtk_configure_transparency_and_realize(GtkWidget *window, + gboolean transparent) { + gboolean isTransparent = glass_configure_window_transparency(window, transparent); + gtk_widget_realize(window); +} + +void +glass_gtk_window_configure_from_visual(GtkWidget *widget, GdkVisual *visual) { + glass_widget_set_visual(widget, visual); +} + +static gboolean +configure_transparent_window(GtkWidget *window) { + GdkScreen *default_screen = gdk_screen_get_default(); + +#ifdef GLASS_GTK3 + GdkVisual *visual = gdk_screen_get_rgba_visual(default_screen); + // visual will be NULL if rgba not supported + if (visual) { + glass_widget_set_visual(window, visual); + return TRUE; + } +#else + GdkDisplay *default_display = gdk_display_get_default(); + GdkColormap *colormap = gdk_screen_get_rgba_colormap(default_screen); + if (colormap + && gdk_display_supports_composite(default_display) + && gdk_screen_is_composited(default_screen)) { + gtk_widget_set_colormap(window, colormap); + return TRUE; + } +#endif + + return FALSE; +} + +void +glass_gdk_window_get_size(GdkWindow *window, gint *w, gint *h) { + *w = gdk_window_get_width(window); + *h = gdk_window_get_height(window); +} + +void +glass_gdk_display_get_pointer(GdkDisplay* display, gint* x, gint *y) { +#ifdef GLASS_GTK3 + gdk_device_get_position( + gdk_device_manager_get_client_pointer( + gdk_display_get_device_manager(display)), NULL , x, y); +#else + gdk_display_get_pointer(display, NULL, x, y, NULL); +#endif +} + + +const guchar* +glass_gtk_selection_data_get_data_with_length( + GtkSelectionData * selectionData, + gint * length) { + if (selectionData == NULL) { + return NULL; + } + + *length = gtk_selection_data_get_length(selectionData); + return gtk_selection_data_get_data(selectionData); +} + +static void +configure_opaque_window(GtkWidget *window) { + (void) window; +/* We need to pick a visual that really is glx compatible + * instead of using the default visual + */ + /* see: JDK-8087516 for why this is commented out + glass_widget_set_visual(window, + gdk_screen_get_system_visual( + gdk_screen_get_default())); + */ +} + +gboolean +glass_configure_window_transparency(GtkWidget *window, gboolean transparent) { + if (transparent) { + if (configure_transparent_window(window)) { + return TRUE; + } + + fprintf(stderr,"Can't create transparent stage, because your screen doesn't" + " support alpha channel." + " You need to enable XComposite extension.\n"); + fflush(stderr); + } + + configure_opaque_window(window); + return FALSE; +} + +GdkPixbuf * +glass_pixbuf_from_window(GdkWindow *window, + gint srcx, gint srcy, + gint width, gint height) +{ + GdkPixbuf * ret = NULL; + +#ifdef GLASS_GTK3 + ret = gdk_pixbuf_get_from_window (window, srcx, srcy, width, height); +#else + ret = gdk_pixbuf_get_from_drawable (NULL, + window, + NULL, + srcx, srcy, + 0, 0, + width, height); +#endif + return ret; +} + +void +glass_window_apply_shape_mask(GdkWindow *window, + void* data, uint width, uint height) +{ +#ifdef GLASS_GTK3 + (void) window; + (void) data; + (void) width; + (void) height; +#else + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data((guchar *) data, + GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * 4, NULL, NULL); + + if (GDK_IS_PIXBUF(pixbuf)) { + GdkBitmap* mask = NULL; + gdk_pixbuf_render_pixmap_and_mask(pixbuf, NULL, &mask, 128); + + gdk_window_input_shape_combine_mask(window, mask, 0, 0); + + g_object_unref(pixbuf); + if (mask) { + g_object_unref(mask); + } + } +#endif +} + +void +glass_window_reset_input_shape_mask(GdkWindow *window) +{ +#ifdef GLASS_GTK3 + gdk_window_input_shape_combine_region(window, NULL, 0, 0); +#else + gdk_window_input_shape_combine_mask(window, NULL, 0, 0); +#endif +} + +GdkWindow * +glass_gdk_drag_context_get_dest_window (GdkDragContext * context) +{ + return ((context != NULL) ? gdk_drag_context_get_dest_window(context) : NULL); +} + + +void glass_gdk_x11_display_set_window_scale (GdkDisplay *display, + gint scale) +{ +#ifdef GLASS_GTK3 + // Optional call, if it does not exist then GTK3 is not yet + // doing automatic scaling of coordinates so we do not need + // to override it. + wrapped_gdk_x11_display_set_window_scale(display, scale); +#else + (void) display; + (void) scale; +#endif +} + +//-------- Glass utility ---------------------------------------- + +void +glass_widget_set_visual(GtkWidget *widget, GdkVisual *visual) +{ +#ifdef GLASS_GTK3 + gtk_widget_set_visual (widget, visual); +#else + GdkColormap *colormap = gdk_colormap_new(visual, TRUE); + gtk_widget_set_colormap (widget, colormap); +#endif +} + +guint glass_settings_get_guint_opt (const gchar *schema_name, + const gchar *key_name, + int defval) +{ + GSettingsSchemaSource *default_schema_source = + wrapped_g_settings_schema_source_get_default(); + if (default_schema_source == NULL) { + if (gtk_verbose) { + fprintf(stderr, "No schema source dir found!\n"); + } + return defval; + } + GSettingsSchema *the_schema = + wrapped_g_settings_schema_source_lookup(default_schema_source, schema_name, TRUE); + if (the_schema == NULL) { + if (gtk_verbose) { + fprintf(stderr, "schema '%s' not found!\n", schema_name); + } + return defval; + } + if (!wrapped_g_settings_schema_has_key(the_schema, key_name)) { + if (gtk_verbose) { + fprintf(stderr, "key '%s' not found in schema '%s'!\n", key_name, schema_name); + } + return defval; + } + if (gtk_verbose) { + fprintf(stderr, "found schema '%s' and key '%s'\n", schema_name, key_name); + } + + GSettings *gset = g_settings_new(schema_name); + + wrapped_g_settings_schema_unref(the_schema); + + return g_settings_get_uint(gset, key_name); +} diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_general.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_general.h new file mode 100644 index 0000000000..76e71acffc --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_general.h @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2011, 2019, 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. + */ +#ifndef GLASS_GENERAL_H +#define GLASS_GENERAL_H + +#include + +#include +#include +#include +#include +#include + +#include "wrapped.h" + +#if GTK_CHECK_VERSION(3, 0, 0) +#if ! GTK_CHECK_VERSION(3, 8, 0) +#error GTK development version is not the minimum 3.8 +#endif +#define GLASS_GTK3 +#endif + +#define JLONG_TO_PTR(value) ((void*)(intptr_t)(value)) +#define PTR_TO_JLONG(value) ((jlong)(intptr_t)(value)) + +#define FILE_PREFIX "file://" +#define URI_LIST_COMMENT_PREFIX "#" +#define URI_LIST_LINE_BREAK "\r\n" + +extern JNIEnv* mainEnv; // Use only with main loop thread!!! +extern JavaVM* javaVM; + +#define GLASS_GDK_KEY_CONSTANT(key) (GDK_KEY_ ## key) + +#include + +struct jni_exception: public std::exception { + jni_exception(jthrowable _th): throwable(_th), message() { + jclass jc = mainEnv->FindClass("java/lang/Throwable"); + if (mainEnv->ExceptionOccurred()) { + mainEnv->ExceptionDescribe(); + mainEnv->ExceptionClear(); + } + jmethodID jmid = mainEnv->GetMethodID(jc, "getMessage", "()Ljava/lang/String;"); + if (mainEnv->ExceptionOccurred()) { + mainEnv->ExceptionDescribe(); + mainEnv->ExceptionClear(); + } + jmessage = (jstring)mainEnv->CallObjectMethod(throwable, jmid); + message = jmessage == NULL ? "" : mainEnv->GetStringUTFChars(jmessage, NULL); + } + const char *what() const throw() + { + return message; + } + ~jni_exception() throw(){ + if (jmessage && message) { + mainEnv->ReleaseStringUTFChars(jmessage, message); + } + } +private: + jthrowable throwable; + const char *message; + jstring jmessage; +}; + +#define EXCEPTION_OCCURED(env) (check_and_clear_exception(env)) + +#define CHECK_JNI_EXCEPTION(env) \ + if (env->ExceptionCheck()) {\ + check_and_clear_exception(env);\ + return;\ + } + +#define CHECK_JNI_EXCEPTION_RET(env, ret) \ + if (env->ExceptionCheck()) {\ + check_and_clear_exception(env);\ + return ret;\ + } + +#define JNI_EXCEPTION_TO_CPP(env) \ + if (env->ExceptionCheck()) {\ + check_and_clear_exception(env);\ + throw jni_exception(env->ExceptionOccurred());\ + } + +#define HANDLE_MEM_ALLOC_ERROR(env, nativePtr, message) \ + ((nativePtr == NULL) && glass_throw_oom(env, message)) + + gpointer glass_try_malloc0_n(gsize m, gsize n); + + gpointer glass_try_malloc_n(gsize m, gsize n); + + typedef struct { + jobject runnable; + int flag; + } RunnableContext; + + extern char const * const GDK_WINDOW_DATA_CONTEXT; + + GdkCursor* get_native_cursor(int type); + gboolean is_grab_disabled(); + + // JNI global references + extern jclass jStringCls; // java.lang.String + + extern jclass jByteBufferCls; //java.nio.ByteBuffer + extern jmethodID jByteBufferArray; //java.nio.ByteBuffer#array()[B + extern jmethodID jByteBufferWrap; //java.nio.ByteBuffer#wrap([B)Ljava/nio/ByteBuffer; + + extern jclass jRunnableCls; // java.lang.Runnable + extern jmethodID jRunnableRun; // java.lang.Runnable#run ()V + + extern jclass jArrayListCls; // java.util.ArrayList + extern jmethodID jArrayListInit; // java.util.ArrayList# ()V + extern jmethodID jArrayListAdd; // java.util.ArrayList#add (Ljava/lang/Object;)Z + extern jmethodID jArrayListGetIdx; //java.util.ArryList#get (I)Ljava/lang/Object; + + extern jmethodID jPixelsAttachData; // com.sun.class.ui.Pixels#attachData (J)V + extern jclass jGtkPixelsCls; // com.sun.class.ui.gtk.GtkPixels + extern jmethodID jGtkPixelsInit; // com.sun.class.ui.gtk.GtkPixels# (IILjava/nio/ByteBuffer;)V + + extern jclass jScreenCls; // com.sun.glass.ui.Screen + extern jmethodID jScreenInit; // com.sun.glass.ui.Screen# ()V + extern jmethodID jScreenNotifySettingsChanged; // com.sun.glass.ui.Screen#notifySettingsChanged ()V + extern jmethodID jScreenGetScreenForLocation; //com.sun.glass.ui.Screen#getScreenForLocation(JJ)Lcom.sun.glass.ui.Screen; + extern jmethodID jScreenGetNativeScreen; //com.sun.glass.ui.Screen#getNativeScreen()J + + extern jmethodID jViewNotifyResize; // com.sun.glass.ui.View#notifyResize (II)V + extern jmethodID jViewNotifyMouse; // com.sun.glass.ui.View#notifyMouse (IIIIIIIZZ)V + extern jmethodID jViewNotifyRepaint; // com.sun.glass.ui.View#notifyRepaint (IIII)V + extern jmethodID jViewNotifyKey; // com.sun.glass.ui.View#notifyKey (II[CI)V + extern jmethodID jViewNotifyView; //com.sun.glass.ui.View#notifyView (I)V + extern jmethodID jViewNotifyDragEnter; //com.sun.glass.ui.View#notifyDragEnter (IIIII)I + extern jmethodID jViewNotifyDragOver; //com.sun.glass.ui.View#notifyDragOver (IIIII)I + extern jmethodID jViewNotifyDragDrop; //com.sun.glass.ui.View#notifyDragDrop (IIIII)I + extern jmethodID jViewNotifyDragLeave; //com.sun.glass.ui.View#notifyDragLeave ()V + extern jmethodID jViewNotifyScroll; //com.sun.glass.ui.View#notifyScroll (IIIIDDIIIIIDD)V + extern jmethodID jViewNotifyInputMethod; //com.sun.glass.ui.View#notifyInputMethod (Ljava/lang/String;[I[I[BIII)V + extern jmethodID jViewNotifyInputMethodDraw; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodDraw (Ljava/lang/String;III[B)V + extern jmethodID jViewNotifyInputMethodCaret; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodCaret (III)V + extern jmethodID jViewNotifyPreeditMode; //com.sun.glass.ui.gtk.GtkView#notifyPreeditMode (Z)V + extern jmethodID jViewNotifyMenu; //com.sun.glass.ui.View#notifyMenu (IIIIZ)V + extern jfieldID jViewPtr; //com.sun.glass.ui.View.ptr + + extern jmethodID jWindowNotifyResize; // com.sun.glass.ui.Window#notifyResize (III)V + extern jmethodID jWindowNotifyMove; // com.sun.glass.ui.Window#notifyMove (II)V + extern jmethodID jWindowNotifyDestroy; // com.sun.glass.ui.Window#notifyDestroy ()V + extern jmethodID jWindowNotifyClose; // com.sun.glass.ui.Window#notifyClose ()V + extern jmethodID jWindowNotifyFocus; // com.sun.glass.ui.Window#notifyFocus (I)V + extern jmethodID jWindowNotifyFocusDisabled; // com.sun.glass.ui.Window#notifyFocusDisabled ()V + extern jmethodID jWindowNotifyFocusUngrab; // com.sun.glass.ui.Window#notifyFocusUngrab ()V + extern jmethodID jWindowNotifyMoveToAnotherScreen; // com.sun.glass.ui.Window#notifyMoveToAnotherScreen (Lcom/sun/glass/ui/Screen;)V + extern jmethodID jWindowNotifyDelegatePtr; //com.sun.glass.ui.Window#notifyDelegatePtr (J)V + extern jmethodID jWindowNotifyLevelChanged; //com.sun.glass.ui.Window#notifyLevelChanged (I)V + + extern jmethodID jWindowIsEnabled; // com.sun.glass.ui.Window#isEnabled ()Z + extern jfieldID jWindowPtr; // com.sun.glass.ui.Window#ptr + extern jfieldID jCursorPtr; // com.sun.glass.ui.Cursor#ptr + + extern jmethodID jGtkWindowNotifyStateChanged; // com.sun.glass.ui.GtkWindow#notifyStateChanged (I)V + + extern jmethodID jClipboardContentChanged; // com.sun.glass.ui.Clipboard#contentChanged ()V + + extern jmethodID jSizeInit; // com.sun.class.ui.Size# ()V + + extern jmethodID jMapGet; // java.util.Map#get(Ljava/lang/Object;)Ljava/lang/Object; + extern jmethodID jMapKeySet; // java.util.Map#keySet()Ljava/util/Set; + extern jmethodID jMapContainsKey; // java.util.Map#containsKey(Ljava/lang/Object;)Z + + extern jclass jHashSetCls; // java.util.HashSet + extern jmethodID jHashSetInit; // java.util.HashSet# ()V + + extern jmethodID jSetAdd; //java.util.Set#add (Ljava/lang/Object;)Z + extern jmethodID jSetSize; //java.util.Set#size ()I + extern jmethodID jSetToArray; //java.util.Set#toArray ([Ljava/lang/Object;)[Ljava/lang/Object; + + extern jmethodID jIterableIterator; // java.lang.Iterable#iterator()Ljava/util/Iterator; + extern jmethodID jIteratorHasNext; // java.util.Iterator#hasNext()Z; + extern jmethodID jIteratorNext; // java.util.Iterator#next()Ljava/lang/Object; + + extern jclass jApplicationCls; //com.sun.glass.ui.gtk.GtkApplication + extern jfieldID jApplicationDisplay; //com.sun.glass.ui.gtk.GtkApplication#display + extern jfieldID jApplicationScreen; //com.sun.glass.ui.gtk.GtkApplication#screen + extern jfieldID jApplicationVisualID; //com.sun.glass.ui.gtk.GtkApplication#visualID + extern jmethodID jApplicationReportException; // reportException(Ljava/lang/Throwable;)V + extern jmethodID jApplicationGetApplication; // GetApplication()()Lcom/sun/glass/ui/Application; + extern jmethodID jApplicationGetName; // getName()Ljava/lang/String; + +#ifdef VERBOSE +#define LOG0(msg) {printf(msg);fflush(stdout);} +#define LOG1(msg, param) {printf(msg, param);fflush(stdout);} +#define LOG2(msg, param1, param2) {printf(msg, param1, param2);fflush(stdout);} +#define LOG3(msg, param1, param2, param3) {printf(msg, param1, param2, param3);fflush(stdout);} +#define LOG4(msg, param1, param2, param3, param4) {printf(msg, param1, param2, param3, param4);fflush(stdout);} +#define LOG5(msg, param1, param2, param3, param4, param5) {printf(msg, param1, param2, param3, param4, param5);fflush(stdout);} + +#define LOG_STRING_ARRAY(env, array) dump_jstring_array(env, array); + +#define ERROR0(msg) {fprintf(stderr, msg);fflush(stderr);} +#define ERROR1(msg, param) {fprintf(stderr, msg, param);fflush(stderr);} +#define ERROR2(msg, param1, param2) {fprintf(stderr, msg, param1, param2);fflush(stderr);} +#define ERROR3(msg, param1, param2, param3) {fprintf(stderr, msg, param1, param2, param3);fflush(stderr);} +#define ERROR4(msg, param1, param2, param3, param4) {fprintf(stderr, msg, param1, param2, param3, param4);fflush(stderr);} +#else +#define LOG0(msg) +#define LOG1(msg, param) +#define LOG2(msg, param1, param2) +#define LOG3(msg, param1, param2, param3) +#define LOG4(msg, param1, param2, param3, param4) +#define LOG5(msg, param1, param2, param3, param4, param5) + +#define LOG_STRING_ARRAY(env, array) + +#define ERROR0(msg) +#define ERROR1(msg, param) +#define ERROR2(msg, param1, param2) +#define ERROR3(msg, param1, param2, param3) +#define ERROR4(msg, param1, param2, param3, param4) +#endif + +#define LOG_EXCEPTION(env) check_and_clear_exception(env); + + gchar* get_application_name(); + void glass_throw_exception(JNIEnv * env, + const char * exceptionClass, + const char * exceptionMessage); + int glass_throw_oom(JNIEnv * env, const char * exceptionMessage); + void dump_jstring_array(JNIEnv*, jobjectArray); + + guint8* convert_BGRA_to_RGBA(const int* pixels, int stride, int height); + + gboolean check_and_clear_exception(JNIEnv *env); + + jboolean is_display_valid(); + + gsize get_files_count(gchar **uris); + + jobject uris_to_java(JNIEnv *env, gchar **uris, gboolean files); + + +#ifdef __cplusplus +extern "C" { +#endif + +extern jboolean gtk_verbose; + +void +glass_widget_set_visual (GtkWidget *widget, GdkVisual *visual); + +gint +glass_gdk_visual_get_depth (GdkVisual * visual); + +GdkScreen * +glass_gdk_window_get_screen(GdkWindow * gdkWindow); + +void +glass_gdk_master_pointer_get_position(gint *x, gint *y); + +gboolean +glass_gdk_device_is_grabbed(GdkDevice *device); + +GdkWindow * +glass_gdk_device_get_window_at_position( + GdkDevice *device, gint *x, gint *y); + +void +glass_gtk_configure_transparency_and_realize(GtkWidget *window, + gboolean transparent); + +const guchar * +glass_gtk_selection_data_get_data_with_length( + GtkSelectionData * selectionData, + gint * length); + +void +glass_gtk_window_configure_from_visual(GtkWidget *widget, GdkVisual *visual); + +void +glass_gdk_window_get_size(GdkWindow *window, gint *w, gint *h); + +void +glass_gdk_display_get_pointer(GdkDisplay* display, gint* x, gint *y); + +void +glass_gdk_x11_display_set_window_scale(GdkDisplay *display, gint scale); + +gboolean +glass_configure_window_transparency(GtkWidget *window, gboolean transparent); + +GdkPixbuf * +glass_pixbuf_from_window(GdkWindow *window, + gint srcx, gint srcy, + gint width, gint height); + +void +glass_window_apply_shape_mask(GdkWindow *window, + void* data, uint width, uint height); + +void +glass_window_reset_input_shape_mask(GdkWindow *window); + +GdkWindow * +glass_gdk_drag_context_get_dest_window (GdkDragContext * context); + +guint +glass_settings_get_guint_opt (const gchar *schema_name, + const gchar *key_name, + int defval); + +#ifdef __cplusplus +} +#endif + +#endif /* GLASS_GENERAL_H */ diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_key.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_key.cpp new file mode 100644 index 0000000000..cb2184c195 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_key.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2011, 2018, 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_key.h" +#include +#include + +#include +#include "glass_general.h" +#include + +static gboolean key_initialized = FALSE; +static GHashTable *keymap; + +static void glass_g_hash_table_insert_int(GHashTable *table, gint key, gint value) +{ + g_hash_table_insert(table, GINT_TO_POINTER(key), GINT_TO_POINTER(value)); +} + +static void initialize_key() +{ + keymap = g_hash_table_new(g_direct_hash, g_direct_equal); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Return), com_sun_glass_events_KeyEvent_VK_ENTER); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(BackSpace), com_sun_glass_events_KeyEvent_VK_BACKSPACE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Tab), com_sun_glass_events_KeyEvent_VK_TAB); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Clear), com_sun_glass_events_KeyEvent_VK_CLEAR); //XXX what is this? + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Pause), com_sun_glass_events_KeyEvent_VK_PAUSE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Escape), com_sun_glass_events_KeyEvent_VK_ESCAPE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(space), com_sun_glass_events_KeyEvent_VK_SPACE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Delete), com_sun_glass_events_KeyEvent_VK_DELETE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Print), com_sun_glass_events_KeyEvent_VK_PRINTSCREEN); //XXX is correct? + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Insert), com_sun_glass_events_KeyEvent_VK_INSERT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Help), com_sun_glass_events_KeyEvent_VK_HELP); //XXX what is this? + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Shift_L), com_sun_glass_events_KeyEvent_VK_SHIFT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Shift_R), com_sun_glass_events_KeyEvent_VK_SHIFT); //XXX is this correct? + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Control_L), com_sun_glass_events_KeyEvent_VK_CONTROL); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Control_R), com_sun_glass_events_KeyEvent_VK_CONTROL); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Alt_L), com_sun_glass_events_KeyEvent_VK_ALT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Alt_R), com_sun_glass_events_KeyEvent_VK_ALT_GRAPH); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Super_L), com_sun_glass_events_KeyEvent_VK_WINDOWS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Super_R), com_sun_glass_events_KeyEvent_VK_WINDOWS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Menu), com_sun_glass_events_KeyEvent_VK_CONTEXT_MENU); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Meta_L), com_sun_glass_events_KeyEvent_VK_WINDOWS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Meta_R), com_sun_glass_events_KeyEvent_VK_CONTEXT_MENU);//XXX is this correct? + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Caps_Lock), com_sun_glass_events_KeyEvent_VK_CAPS_LOCK); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Num_Lock), com_sun_glass_events_KeyEvent_VK_NUM_LOCK); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Scroll_Lock), com_sun_glass_events_KeyEvent_VK_SCROLL_LOCK); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Page_Up), com_sun_glass_events_KeyEvent_VK_PAGE_UP); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Prior), com_sun_glass_events_KeyEvent_VK_PAGE_UP); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Page_Down), com_sun_glass_events_KeyEvent_VK_PAGE_DOWN); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Next), com_sun_glass_events_KeyEvent_VK_PAGE_DOWN); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(End), com_sun_glass_events_KeyEvent_VK_END); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Home), com_sun_glass_events_KeyEvent_VK_HOME); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Left), com_sun_glass_events_KeyEvent_VK_LEFT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Right), com_sun_glass_events_KeyEvent_VK_RIGHT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Up), com_sun_glass_events_KeyEvent_VK_UP); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Down), com_sun_glass_events_KeyEvent_VK_DOWN); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(comma), com_sun_glass_events_KeyEvent_VK_COMMA); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(minus), com_sun_glass_events_KeyEvent_VK_MINUS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(period), com_sun_glass_events_KeyEvent_VK_PERIOD); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(slash), com_sun_glass_events_KeyEvent_VK_SLASH); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(semicolon), com_sun_glass_events_KeyEvent_VK_SEMICOLON); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(equal), com_sun_glass_events_KeyEvent_VK_EQUALS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(bracketleft), com_sun_glass_events_KeyEvent_VK_OPEN_BRACKET); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(bracketright), com_sun_glass_events_KeyEvent_VK_CLOSE_BRACKET); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(backslash), com_sun_glass_events_KeyEvent_VK_BACK_SLASH); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(bar), com_sun_glass_events_KeyEvent_VK_BACK_SLASH); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Multiply), com_sun_glass_events_KeyEvent_VK_MULTIPLY); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Add), com_sun_glass_events_KeyEvent_VK_ADD); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Separator), com_sun_glass_events_KeyEvent_VK_SEPARATOR); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Subtract), com_sun_glass_events_KeyEvent_VK_SUBTRACT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Decimal), com_sun_glass_events_KeyEvent_VK_DECIMAL); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(apostrophe), com_sun_glass_events_KeyEvent_VK_QUOTE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(grave), com_sun_glass_events_KeyEvent_VK_BACK_QUOTE); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(ampersand), com_sun_glass_events_KeyEvent_VK_AMPERSAND); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(asterisk), com_sun_glass_events_KeyEvent_VK_ASTERISK); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(quotedbl), com_sun_glass_events_KeyEvent_VK_DOUBLE_QUOTE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(less), com_sun_glass_events_KeyEvent_VK_LESS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(greater), com_sun_glass_events_KeyEvent_VK_GREATER); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(braceleft), com_sun_glass_events_KeyEvent_VK_BRACELEFT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(braceright), com_sun_glass_events_KeyEvent_VK_BRACERIGHT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(at), com_sun_glass_events_KeyEvent_VK_AT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(colon), com_sun_glass_events_KeyEvent_VK_COLON); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(asciicircum), com_sun_glass_events_KeyEvent_VK_CIRCUMFLEX); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(dollar), com_sun_glass_events_KeyEvent_VK_DOLLAR); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(EuroSign), com_sun_glass_events_KeyEvent_VK_EURO_SIGN); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(exclam), com_sun_glass_events_KeyEvent_VK_EXCLAMATION); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(exclamdown), com_sun_glass_events_KeyEvent_VK_INV_EXCLAMATION); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(parenleft), com_sun_glass_events_KeyEvent_VK_LEFT_PARENTHESIS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(numbersign), com_sun_glass_events_KeyEvent_VK_NUMBER_SIGN); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(plus), com_sun_glass_events_KeyEvent_VK_PLUS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(parenright), com_sun_glass_events_KeyEvent_VK_RIGHT_PARENTHESIS); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(underscore), com_sun_glass_events_KeyEvent_VK_UNDERSCORE); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(0), com_sun_glass_events_KeyEvent_VK_0); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(1), com_sun_glass_events_KeyEvent_VK_1); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(2), com_sun_glass_events_KeyEvent_VK_2); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(3), com_sun_glass_events_KeyEvent_VK_3); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(4), com_sun_glass_events_KeyEvent_VK_4); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(5), com_sun_glass_events_KeyEvent_VK_5); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(6), com_sun_glass_events_KeyEvent_VK_6); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(7), com_sun_glass_events_KeyEvent_VK_7); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(8), com_sun_glass_events_KeyEvent_VK_8); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(9), com_sun_glass_events_KeyEvent_VK_9); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(a), com_sun_glass_events_KeyEvent_VK_A); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(b), com_sun_glass_events_KeyEvent_VK_B); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(c), com_sun_glass_events_KeyEvent_VK_C); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(d), com_sun_glass_events_KeyEvent_VK_D); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(e), com_sun_glass_events_KeyEvent_VK_E); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(f), com_sun_glass_events_KeyEvent_VK_F); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(g), com_sun_glass_events_KeyEvent_VK_G); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(h), com_sun_glass_events_KeyEvent_VK_H); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(i), com_sun_glass_events_KeyEvent_VK_I); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(j), com_sun_glass_events_KeyEvent_VK_J); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(k), com_sun_glass_events_KeyEvent_VK_K); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(l), com_sun_glass_events_KeyEvent_VK_L); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(m), com_sun_glass_events_KeyEvent_VK_M); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(n), com_sun_glass_events_KeyEvent_VK_N); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(o), com_sun_glass_events_KeyEvent_VK_O); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(p), com_sun_glass_events_KeyEvent_VK_P); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(q), com_sun_glass_events_KeyEvent_VK_Q); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(r), com_sun_glass_events_KeyEvent_VK_R); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(s), com_sun_glass_events_KeyEvent_VK_S); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(t), com_sun_glass_events_KeyEvent_VK_T); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(u), com_sun_glass_events_KeyEvent_VK_U); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(v), com_sun_glass_events_KeyEvent_VK_V); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(w), com_sun_glass_events_KeyEvent_VK_W); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(x), com_sun_glass_events_KeyEvent_VK_X); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(y), com_sun_glass_events_KeyEvent_VK_Y); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(z), com_sun_glass_events_KeyEvent_VK_Z); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(A), com_sun_glass_events_KeyEvent_VK_A); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(B), com_sun_glass_events_KeyEvent_VK_B); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(C), com_sun_glass_events_KeyEvent_VK_C); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(D), com_sun_glass_events_KeyEvent_VK_D); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(E), com_sun_glass_events_KeyEvent_VK_E); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F), com_sun_glass_events_KeyEvent_VK_F); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(G), com_sun_glass_events_KeyEvent_VK_G); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(H), com_sun_glass_events_KeyEvent_VK_H); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(I), com_sun_glass_events_KeyEvent_VK_I); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(J), com_sun_glass_events_KeyEvent_VK_J); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(K), com_sun_glass_events_KeyEvent_VK_K); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(L), com_sun_glass_events_KeyEvent_VK_L); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(M), com_sun_glass_events_KeyEvent_VK_M); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(N), com_sun_glass_events_KeyEvent_VK_N); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(O), com_sun_glass_events_KeyEvent_VK_O); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(P), com_sun_glass_events_KeyEvent_VK_P); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Q), com_sun_glass_events_KeyEvent_VK_Q); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(R), com_sun_glass_events_KeyEvent_VK_R); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(S), com_sun_glass_events_KeyEvent_VK_S); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(T), com_sun_glass_events_KeyEvent_VK_T); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(U), com_sun_glass_events_KeyEvent_VK_U); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(V), com_sun_glass_events_KeyEvent_VK_V); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(W), com_sun_glass_events_KeyEvent_VK_W); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(X), com_sun_glass_events_KeyEvent_VK_X); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Y), com_sun_glass_events_KeyEvent_VK_Y); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(Z), com_sun_glass_events_KeyEvent_VK_Z); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_0), com_sun_glass_events_KeyEvent_VK_NUMPAD0); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_1), com_sun_glass_events_KeyEvent_VK_NUMPAD1); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_2), com_sun_glass_events_KeyEvent_VK_NUMPAD2); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_3), com_sun_glass_events_KeyEvent_VK_NUMPAD3); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_4), com_sun_glass_events_KeyEvent_VK_NUMPAD4); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_5), com_sun_glass_events_KeyEvent_VK_NUMPAD5); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_6), com_sun_glass_events_KeyEvent_VK_NUMPAD6); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_7), com_sun_glass_events_KeyEvent_VK_NUMPAD7); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_8), com_sun_glass_events_KeyEvent_VK_NUMPAD8); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_9), com_sun_glass_events_KeyEvent_VK_NUMPAD9); + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Enter), com_sun_glass_events_KeyEvent_VK_ENTER); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Home), com_sun_glass_events_KeyEvent_VK_HOME); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Left), com_sun_glass_events_KeyEvent_VK_LEFT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Up), com_sun_glass_events_KeyEvent_VK_UP); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Right), com_sun_glass_events_KeyEvent_VK_RIGHT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Down), com_sun_glass_events_KeyEvent_VK_DOWN); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Prior), com_sun_glass_events_KeyEvent_VK_PAGE_UP); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Page_Up), com_sun_glass_events_KeyEvent_VK_PAGE_UP); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Next), com_sun_glass_events_KeyEvent_VK_PAGE_DOWN); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Page_Down), com_sun_glass_events_KeyEvent_VK_PAGE_DOWN); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_End), com_sun_glass_events_KeyEvent_VK_END); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Insert), com_sun_glass_events_KeyEvent_VK_INSERT); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Delete), com_sun_glass_events_KeyEvent_VK_DELETE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Divide), com_sun_glass_events_KeyEvent_VK_DIVIDE); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(KP_Begin), + com_sun_glass_events_KeyEvent_VK_CLEAR); // 5 key on keypad with Num Lock turned off + + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F1), com_sun_glass_events_KeyEvent_VK_F1); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F2), com_sun_glass_events_KeyEvent_VK_F2); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F3), com_sun_glass_events_KeyEvent_VK_F3); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F4), com_sun_glass_events_KeyEvent_VK_F4); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F5), com_sun_glass_events_KeyEvent_VK_F5); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F6), com_sun_glass_events_KeyEvent_VK_F6); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F7), com_sun_glass_events_KeyEvent_VK_F7); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F8), com_sun_glass_events_KeyEvent_VK_F8); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F9), com_sun_glass_events_KeyEvent_VK_F9); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F10), com_sun_glass_events_KeyEvent_VK_F10); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F11), com_sun_glass_events_KeyEvent_VK_F11); + glass_g_hash_table_insert_int(keymap, GLASS_GDK_KEY_CONSTANT(F12), com_sun_glass_events_KeyEvent_VK_F12); +} + +static void init_keymap() { + if (!key_initialized) { + initialize_key(); + key_initialized = TRUE; + } +} + +jint gdk_keyval_to_glass(guint keyval) +{ + init_keymap(); + return GPOINTER_TO_INT(g_hash_table_lookup(keymap, GINT_TO_POINTER(keyval))); +} + +jint get_glass_key(GdkEventKey* e) { + init_keymap(); + + guint keyValue; + guint state = e->state & GDK_MOD2_MASK; //NumLock test + + gdk_keymap_translate_keyboard_state(gdk_keymap_get_for_display(gdk_display_get_default()), + e->hardware_keycode, static_cast(state), e->group, + &keyValue, NULL, NULL, NULL); + + jint key = GPOINTER_TO_INT(g_hash_table_lookup(keymap, + GINT_TO_POINTER(keyValue))); + + if (!key) { + // We failed to find a keyval in our keymap, this may happen with + // non-latin layouts(e.g. Cyrillic). So here we try to find a keyval + // from a default layout(we assume that it is a US-like one). + GdkKeymapKey kk; + kk.keycode = e->hardware_keycode; + kk.group = kk.level = 0; + + keyValue = gdk_keymap_lookup_key(gdk_keymap_get_for_display(gdk_display_get_default()), &kk); + + key = GPOINTER_TO_INT(g_hash_table_lookup(keymap, + GINT_TO_POINTER(keyValue))); + } + + return key; +} + +gint find_gdk_keyval_for_glass_keycode(jint code) { + gint result = -1; + GHashTableIter iter; + gpointer key, value; + init_keymap(); + g_hash_table_iter_init(&iter, keymap); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (code == GPOINTER_TO_INT(value)) { + result = GPOINTER_TO_INT(key); + break; + } + } + return result; +} + +jint gdk_modifier_mask_to_glass(guint mask) +{ + jint glass_mask = 0; + glass_mask |= (mask & GDK_SHIFT_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_SHIFT : 0; + glass_mask |= (mask & GDK_CONTROL_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_CONTROL : 0; + glass_mask |= (mask & GDK_MOD1_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_ALT : 0; + glass_mask |= (mask & GDK_META_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_ALT : 0; // XXX: is this OK? + glass_mask |= (mask & GDK_BUTTON1_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY : 0; + glass_mask |= (mask & GDK_BUTTON2_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE : 0; + glass_mask |= (mask & GDK_BUTTON3_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY : 0; + glass_mask |= (mask & GDK_BUTTON4_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK : 0; + glass_mask |= (mask & GDK_BUTTON5_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD : 0; + glass_mask |= (mask & GDK_SUPER_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_WINDOWS : 0; // XXX: is this OK? + + return glass_mask; +} + +jint glass_key_to_modifier(jint glassKey) { + switch (glassKey) { + case com_sun_glass_events_KeyEvent_VK_SHIFT: + return com_sun_glass_events_KeyEvent_MODIFIER_SHIFT; + case com_sun_glass_events_KeyEvent_VK_ALT: + case com_sun_glass_events_KeyEvent_VK_ALT_GRAPH: + return com_sun_glass_events_KeyEvent_MODIFIER_ALT; + case com_sun_glass_events_KeyEvent_VK_CONTROL: + return com_sun_glass_events_KeyEvent_MODIFIER_CONTROL; + case com_sun_glass_events_KeyEvent_VK_WINDOWS: + return com_sun_glass_events_KeyEvent_MODIFIER_WINDOWS; + default: + return 0; + } +} +extern "C" { + +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: _getKeyCodeForChar + * Signature: (C)I + */ +JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1getKeyCodeForChar + (JNIEnv *env, jobject jApplication, jchar character) +{ + (void)env; + (void)jApplication; + + gunichar *ucs_char = g_utf16_to_ucs4(&character, 1, NULL, NULL, NULL); + if (ucs_char == NULL) { + return com_sun_glass_events_KeyEvent_VK_UNDEFINED; + } + + guint keyval = gdk_unicode_to_keyval(*ucs_char); + + if (keyval == (*ucs_char | 0x01000000)) { + g_free(ucs_char); + return com_sun_glass_events_KeyEvent_VK_UNDEFINED; + } + + g_free(ucs_char); + + return gdk_keyval_to_glass(keyval); +} + +} // extern "C" diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_key.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_key.h new file mode 100644 index 0000000000..1de90ebabe --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_key.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011, 2013, 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. + */ +#ifndef GLASS_KEY_H +#define GLASS_KEY_H + +#include + +#include + +jint gdk_keyval_to_glass(guint keyval); +jint get_glass_key(GdkEventKey* e); +jint glass_key_to_modifier(jint glassKey); +jint gdk_modifier_mask_to_glass(guint mask); +gint find_gdk_keyval_for_glass_keycode(jint code); + +#endif /* GLASS_KEY_H */ + diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_screen.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_screen.cpp new file mode 100644 index 0000000000..eab6b51e5e --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_screen.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2013, 2016, 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 + +#include "glass_screen.h" +#include "glass_general.h" + +#include +#include +#include + +jfloat OverrideUIScale = -1.0f; + +static guint get_current_desktop(GdkScreen *screen) { + Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + Atom currentDesktopAtom = XInternAtom(display, "_NET_CURRENT_DESKTOP", True); + guint ret = 0; + + Atom type; + int format; + gulong num, left; + unsigned long *data = NULL; + + if (currentDesktopAtom == None) { + return 0; + } + + int result = XGetWindowProperty(display, + GDK_WINDOW_XID(gdk_screen_get_root_window(screen)), + currentDesktopAtom, 0, G_MAXLONG, False, XA_CARDINAL, + &type, &format, &num, &left, (unsigned char **)&data); + + if ((result == Success) && (data != NULL)) { + if (type == XA_CARDINAL && format == 32) { + ret = data[0]; + } + + XFree(data); + } + + return ret; + +} + +static GdkRectangle get_screen_workarea(GdkScreen *screen) { + GdkDisplay * gdk_display = gdk_screen_get_display(screen); + Display* display = gdk_x11_display_get_xdisplay(gdk_display); + + int w = gdk_screen_get_width(screen); + int h = gdk_screen_get_height(screen); + + GdkRectangle ret = { 0, 0, w, h }; + + Atom workareaAtom = XInternAtom(display, "_NET_WORKAREA", True); + + Atom type; + int format; + gulong num, left; + unsigned long *data = NULL; + + if (workareaAtom == None) { + return ret; + } + + int result = XGetWindowProperty(display, + GDK_WINDOW_XID(gdk_screen_get_root_window(screen)), + workareaAtom, 0, G_MAXLONG, False, AnyPropertyType, + &type, &format, &num, &left, (unsigned char **)&data); + + if ((result == Success) && (data != NULL)) { + if (type != None && format == 32) { + guint current_desktop = get_current_desktop(screen); + if (current_desktop < num / 4) { + ret.x = data[current_desktop * 4]; + ret.y = data[current_desktop * 4 + 1]; + ret.width = data[current_desktop * 4 + 2]; + ret.height = data[current_desktop * 4 + 3]; + } + } + + XFree(data); + } + + return ret; + +} + +jfloat getUIScale() { + jfloat uiScale; + if (OverrideUIScale > 0.0f) { + uiScale = OverrideUIScale; + } else { + char *scale_str = getenv("GDK_SCALE"); + int gdk_scale = (scale_str == NULL) ? -1 : atoi(scale_str); + if (gdk_scale > 0) { + uiScale = (jfloat) gdk_scale; + } else { + uiScale = (jfloat) glass_settings_get_guint_opt("org.gnome.desktop.interface", + "scaling-factor", 0); + if (uiScale < 1) { + uiScale = 1; + } + } + } + return uiScale; +} + +static jobject createJavaScreen(JNIEnv* env, GdkScreen* screen, gint monitor_idx) +{ + GdkRectangle workArea = get_screen_workarea(screen); + LOG4("Work Area: x:%d, y:%d, w:%d, h:%d\n", workArea.x, workArea.y, workArea.width, workArea.height); + + GdkRectangle monitor_geometry; + gdk_screen_get_monitor_geometry(screen, monitor_idx, &monitor_geometry); + + LOG1("convert monitor[%d] -> glass Screen\n", monitor_idx) + LOG4("[x: %d y: %d w: %d h: %d]\n", + monitor_geometry.x, monitor_geometry.y, + monitor_geometry.width, monitor_geometry.height) + + GdkVisual* visual = gdk_screen_get_system_visual(screen); + + GdkRectangle working_monitor_geometry; + gdk_rectangle_intersect(&workArea, &monitor_geometry, &working_monitor_geometry); + + jfloat uiScale = getUIScale(); + + jint mx = monitor_geometry.x / uiScale; + jint my = monitor_geometry.y / uiScale; + jint mw = monitor_geometry.width / uiScale; + jint mh = monitor_geometry.height / uiScale; + jint wx = working_monitor_geometry.x / uiScale; + jint wy = working_monitor_geometry.y / uiScale; + jint ww = working_monitor_geometry.width / uiScale; + jint wh = working_monitor_geometry.height / uiScale; + + gint mmW = gdk_screen_get_monitor_width_mm(screen, monitor_idx); + gint mmH = gdk_screen_get_monitor_height_mm(screen, monitor_idx); + + if (mmW <= 0 || mmH <= 0) { + if (gdk_screen_get_n_monitors(screen) == 1) { + mmW = gdk_screen_get_width_mm(screen); + mmH = gdk_screen_get_height_mm(screen); + } + } + jint dpiX, dpiY; + if (mmW <= 0 || mmH <= 0) { + dpiX = dpiY = 96; + } else { + dpiX = (mw * 254) / (mmW * 10); + dpiY = (mh * 254) / (mmH * 10); + } + + jobject jScreen = env->NewObject(jScreenCls, jScreenInit, + (jlong)monitor_idx, + + (visual ? glass_gdk_visual_get_depth(visual) : 0), + + mx, my, mw, mh, + + monitor_geometry.x, + monitor_geometry.y, + monitor_geometry.width, + monitor_geometry.height, + + wx, wy, ww, wh, + + dpiX, dpiY, + uiScale, uiScale, uiScale, uiScale); + + JNI_EXCEPTION_TO_CPP(env); + return jScreen; +} + +jobject createJavaScreen(JNIEnv* env, gint monitor_idx) { + GdkScreen *default_gdk_screen = gdk_screen_get_default(); + try { + return createJavaScreen(env, default_gdk_screen, monitor_idx); + } catch (jni_exception&) { + return NULL; + } +} + +jobjectArray rebuild_screens(JNIEnv* env) { + GdkScreen *default_gdk_screen = gdk_screen_get_default(); + + gint n_monitors = gdk_screen_get_n_monitors(default_gdk_screen); + + jobjectArray jscreens = env->NewObjectArray(n_monitors, jScreenCls, NULL); + JNI_EXCEPTION_TO_CPP(env) + LOG1("Available monitors: %d\n", n_monitors) + + int i; + for (i=0; i < n_monitors; i++) { + env->SetObjectArrayElement(jscreens, i, createJavaScreen(env, default_gdk_screen, i)); + JNI_EXCEPTION_TO_CPP(env) + } + + return jscreens; +} + + +glong getScreenPtrForLocation(gint x, gint y) { + //Note: we are relying on the fact that javafx_screen_id == gdk_monitor_id + return gdk_screen_get_monitor_at_point(gdk_screen_get_default(), x, y); +} + +void screen_settings_changed(GdkScreen* screen, gpointer user_data) { + (void)screen; + (void)user_data; + + mainEnv->CallStaticVoidMethod(jScreenCls, jScreenNotifySettingsChanged); + LOG_EXCEPTION(mainEnv); +} diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_screen.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_screen.h new file mode 100644 index 0000000000..77573da745 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_screen.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013, 2016, 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. + */ + +#ifndef GLASS_SCREEN_H +#define GLASS_SCREEN_H + +#include + +#include + +extern jfloat OverrideUIScale; +jfloat getUIScale(); +jobject createJavaScreen(JNIEnv* env, gint monitor_idx); +glong getScreenPtrForLocation(gint x, gint y); +jobjectArray rebuild_screens(JNIEnv* env); +void screen_settings_changed(GdkScreen* screen, gpointer user_data); + +#endif diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_view.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_view.h new file mode 100644 index 0000000000..f8dfc3dfcd --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_view.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011, 2013, 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. + */ +#ifndef GLASS_VIEW_H +#define GLASS_VIEW_H + +class WindowContext; + +struct GlassView { + GlassView() : current_window(), embedded_window() {} + + WindowContext* current_window; + WindowContext* embedded_window; // not null while in Full Screen +}; + +#endif /* GLASS_VIEW_H */ + diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window.cpp new file mode 100644 index 0000000000..78fbfd4cbb --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window.cpp @@ -0,0 +1,1492 @@ +/* + * Copyright (c) 2011, 2020, 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_window.h" +#include "glass_key.h" +#include "glass_screen.h" +#include "glass_dnd.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#ifdef GLASS_GTK3 +#include +#endif + +#include + +#include + +#define MOUSE_BACK_BTN 8 +#define MOUSE_FORWARD_BTN 9 + +static gboolean ctx_configure_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_configure(); + return FALSE; +} + +static gboolean ctx_property_notify_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_property_notify(&event->property); + return TRUE; +} + +static gboolean ctx_focus_change_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_focus(&event->focus_change); + return TRUE; +} + +static gboolean ctx_delete_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_delete(); + return TRUE; +} + +static gboolean ctx_window_state_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_state(&event->window_state); + return FALSE; +} + +static gboolean ctx_device_button_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_mouse_button(&event->button); + return TRUE; +} + +static gboolean ctx_device_motion_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_mouse_motion(&event->motion); + return TRUE; +} + +static gboolean ctx_device_scroll_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_mouse_scroll(&event->scroll); + return TRUE; +} + +static gboolean ctx_enter_or_leave_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_mouse_cross(&event->crossing); + return TRUE; +} + +static gboolean ctx_key_press_or_release_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_key(&event->key); + return TRUE; +} + +static gboolean ctx_map_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + ((WindowContext *) user_data)->process_map(); + return TRUE; +} + +static void ctx_screen_changed_callback(GtkWidget *widget, + GdkScreen *previous_screen, + gpointer user_data) { + ((WindowContext *) user_data)->process_screen_changed(); +} + +static void connect_signals(GtkWidget *gtk_widget, WindowContext *ctx) { + g_signal_connect(gtk_widget, "configure-event", G_CALLBACK(ctx_configure_callback), ctx); + g_signal_connect(gtk_widget, "property-notify-event", G_CALLBACK(ctx_property_notify_callback), ctx); + g_signal_connect(gtk_widget, "focus-in-event", G_CALLBACK(ctx_focus_change_callback), ctx); + g_signal_connect(gtk_widget, "focus-out-event", G_CALLBACK(ctx_focus_change_callback), ctx); + g_signal_connect(gtk_widget, "delete-event", G_CALLBACK(ctx_delete_callback), ctx); + g_signal_connect(gtk_widget, "window-state-event", G_CALLBACK(ctx_window_state_callback), ctx); + g_signal_connect(gtk_widget, "button-press-event", G_CALLBACK(ctx_device_button_callback), ctx); + g_signal_connect(gtk_widget, "button-release-event", G_CALLBACK(ctx_device_button_callback), ctx); + g_signal_connect(gtk_widget, "motion-notify-event", G_CALLBACK(ctx_device_motion_callback), ctx); + g_signal_connect(gtk_widget, "scroll-event", G_CALLBACK(ctx_device_scroll_callback), ctx); + g_signal_connect(gtk_widget, "enter-notify-event", G_CALLBACK(ctx_enter_or_leave_callback), ctx); + g_signal_connect(gtk_widget, "leave-notify-event", G_CALLBACK(ctx_enter_or_leave_callback), ctx); + g_signal_connect(gtk_widget, "key-press-event", G_CALLBACK(ctx_key_press_or_release_callback), ctx); + g_signal_connect(gtk_widget, "key-release-event", G_CALLBACK(ctx_key_press_or_release_callback), ctx); + g_signal_connect(gtk_widget, "map-event", G_CALLBACK(ctx_map_callback), ctx); + g_signal_connect(gtk_widget, "screen-changed", G_CALLBACK(ctx_screen_changed_callback), ctx); +} + + +void destroy_and_delete_ctx(WindowContext *ctx) { + if (ctx) { + ctx->process_destroy(); + + if (!ctx->get_events_count()) { + delete ctx; + } + // else: ctx will be deleted in EventsCounterHelper after completing + // an event processing + } +} + +static inline jint gtk_button_number_to_mouse_button(guint button) { + switch (button) { + case 1: + return com_sun_glass_events_MouseEvent_BUTTON_LEFT; + case 2: + return com_sun_glass_events_MouseEvent_BUTTON_OTHER; + case 3: + return com_sun_glass_events_MouseEvent_BUTTON_RIGHT; + case MOUSE_BACK_BTN: + return com_sun_glass_events_MouseEvent_BUTTON_BACK; + case MOUSE_FORWARD_BTN: + return com_sun_glass_events_MouseEvent_BUTTON_FORWARD; + default: + // Other buttons are not supported by quantum and are not reported by other platforms + return com_sun_glass_events_MouseEvent_BUTTON_NONE; + } +} + +////////////////////////////// WindowContext ///////////////////////////////// + +static GdkAtom atom_net_wm_state = gdk_atom_intern_static_string("_NET_WM_STATE"); +static GdkAtom atom_net_wm_frame_extents = gdk_atom_intern_static_string("_NET_FRAME_EXTENTS"); + +WindowContext * WindowContext::sm_mouse_drag_window = NULL; +WindowContext * WindowContext::sm_grab_window = NULL; + +WindowContext::WindowContext(jobject _jwindow, WindowContext *_owner, long _screen, + WindowFrameType _frame_type, WindowType type, GdkWMFunction wmf) : + screen(_screen), + frame_type(_frame_type), + window_type(type), + owner(_owner), + jview(NULL), + map_received(false), + visible_received(false), + on_top(false), + is_fullscreen(false), + is_iconified(false), + is_maximized(false), + is_mouse_entered(false), + can_be_deleted(false), + events_processing_cnt(0), + grab_pointer(NULL) { + + jwindow = mainEnv->NewGlobalRef(_jwindow); + + gtk_widget = gtk_window_new(type == POPUP ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); + + // This allows to group Windows (based on WM_CLASS). Use the fully qualified + // JavaFX Application class as StartupWMClass on the .desktop launcher + if (gchar * app_name = get_application_name()) { + gtk_window_set_wmclass(GTK_WINDOW(gtk_widget), app_name, app_name); + g_free(app_name); + } + + if (owner) { + owner->add_child(this); + if (on_top_inherited()) { + gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE); + } + } + + if (type == UTILITY) { + gtk_window_set_type_hint(GTK_WINDOW(gtk_widget), GDK_WINDOW_TYPE_HINT_UTILITY); + } + + glong xvisualID = (glong) mainEnv->GetStaticLongField(jApplicationCls, jApplicationVisualID); + + if (xvisualID != 0) { + GdkVisual *visual = gdk_x11_screen_lookup_visual(gdk_screen_get_default(), xvisualID); + glass_gtk_window_configure_from_visual(gtk_widget, visual); + } + + gtk_widget_set_events(gtk_widget, GDK_ALL_EVENTS_MASK); + gtk_widget_set_app_paintable(gtk_widget, TRUE); + + glass_gtk_configure_transparency_and_realize(gtk_widget, frame_type == TRANSPARENT); + gtk_window_set_title(GTK_WINDOW(gtk_widget), ""); + + gdk_window = gtk_widget_get_window(gtk_widget); + g_object_set_data_full(G_OBJECT(gdk_window), GDK_WINDOW_DATA_CONTEXT, this, NULL); + + glass_dnd_attach_context(this); + + gdk_windowManagerFunctions = wmf; + if (wmf) { + gdk_window_set_functions(gdk_window, wmf); + } + + if (frame_type != TITLED) { + gtk_window_set_decorated(GTK_WINDOW(gtk_widget), FALSE); + } + + connect_signals(gtk_widget, this); +} + +void WindowContext::paint(void *data, jint width, jint height) { +#ifdef GLASS_GTK3 + cairo_region_t *region = gdk_window_get_clip_region(gdk_window); + gdk_window_begin_paint_region(gdk_window, region); + cairo_t* context = gdk_cairo_create(gdk_window); +#else + cairo_t *context = gdk_cairo_create(gdk_window); +#endif + + if (bg_color.is_set) { + cairo_set_source_rgba(context, bg_color.red, bg_color.green, bg_color.blue, + (frame_type == TRANSPARENT) ? 0 : 1); + cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); + cairo_paint(context); + } + + cairo_surface_t *cairo_surface; + cairo_surface = cairo_image_surface_create_for_data( + (unsigned char *) data, + CAIRO_FORMAT_ARGB32, + width, height, width * 4); + + cairo_set_source_surface(context, cairo_surface, 0, 0); + + applyShapeMask(data, width, height); + cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); + cairo_paint(context); + +#ifdef GLASS_GTK3 + gdk_window_end_paint(gdk_window); + cairo_region_destroy(region); + cairo_destroy(context); +#else + cairo_destroy(context); +#endif + + cairo_surface_destroy(cairo_surface); +} + +bool WindowContext::isEnabled() { + if (jwindow) { + bool result = (JNI_TRUE == mainEnv->CallBooleanMethod(jwindow, jWindowIsEnabled)); + LOG_EXCEPTION(mainEnv) + return result; + } else { + return false; + } +} + +GdkWindow *WindowContext::get_gdk_window() { + return gdk_window; +} + +GtkWidget *WindowContext::get_gtk_widget() { + return gtk_widget; +} + +GtkWindow *WindowContext::get_gtk_window() { + return GTK_WINDOW(gtk_widget); +} + +WindowGeometry WindowContext::get_geometry() { + return geometry; +} + +jobject WindowContext::get_jwindow() { + return jwindow; +} + +jobject WindowContext::get_jview() { + return jview; +} + +void WindowContext::process_map() { + map_received = true; + calculate_adjustments(); + apply_geometry(); +} + +void WindowContext::process_focus(GdkEventFocus *event) { + if (!event->in && WindowContext::sm_mouse_drag_window == this) { + ungrab_mouse_drag_focus(); + } + + if (!event->in && WindowContext::sm_grab_window == this) { + ungrab_focus(); + } + + if (xim.enabled && xim.ic) { + if (event->in) { + XSetICFocus(xim.ic); + } else { + XUnsetICFocus(xim.ic); + } + } + + if (jwindow) { + if (!event->in || isEnabled()) { + mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocus, + event->in ? com_sun_glass_events_WindowEvent_FOCUS_GAINED + : com_sun_glass_events_WindowEvent_FOCUS_LOST); + CHECK_JNI_EXCEPTION(mainEnv) + } else { + mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocusDisabled); + CHECK_JNI_EXCEPTION(mainEnv) + } + } +} + +void WindowContext::process_property_notify(GdkEventProperty *event) { + if (event->window == gdk_window) { + // This work-around is only necessary for Unity + if (event->atom == atom_net_wm_state) { + process_net_wm_property(); + } else if (event->atom == atom_net_wm_frame_extents) { + if (frame_type != TITLED) { + return; + } + + int top, left, bottom, right; + + if (get_frame_extents_property(&top, &left, &bottom, &right)) { + if (top + left + bottom + right > 0) { + geometry.frame_extents_received = true; + geometry.adjust_w = left + right; + geometry.adjust_h = top + bottom; + geometry.view_x = left; + geometry.view_y = top; + + // set bounds again to set to correct window size that must + // be the total width and height accounting extents + // this is ignored if size is "content size" instead of "window size" + if (geometry.needs_ajustment) { + set_bounds(0, 0, false, false, geometry.current_w, geometry.current_h, -1, -1); + } + + // force position notify so java will know about view_y and view_x + size_position_notify(false, true); + } + } + } + } +} + +void WindowContext::process_configure() { + gint x, y, w, h, gtk_w, gtk_h; + + gtk_window_get_position(GTK_WINDOW(gtk_widget), &x, &y); + gtk_window_get_size(GTK_WINDOW(gtk_widget), >k_w, >k_h); + + w = gtk_w + geometry.adjust_w; + h = gtk_h + geometry.adjust_h; + + gboolean pos_changed = geometry.current_x != x || geometry.current_y != y; + gboolean size_changed = geometry.current_w != w || geometry.current_h != h + || geometry.current_cw != gtk_w || geometry.current_ch != gtk_h; + + geometry.current_x = x; + geometry.current_y = y; + geometry.current_w = w; + geometry.current_h = h; + geometry.current_cw = gtk_w; + geometry.current_ch = gtk_h; + + if (!is_fullscreen && !is_maximized) { + geometry.last_cw = gtk_w; + geometry.last_ch = gtk_h; + } + + size_position_notify(size_changed, pos_changed); +} + +void WindowContext::process_destroy() { + if (owner) { + owner->remove_child(this); + } + + if (WindowContext::sm_mouse_drag_window == this) { + ungrab_mouse_drag_focus(); + } + + if (WindowContext::sm_grab_window == this) { + ungrab_focus(); + } + + std::set::iterator it; + for (it = children.begin(); it != children.end(); ++it) { + // FIX JDK-8226537: this method calls set_owner(NULL) which prevents + // WindowContext::process_destroy() to call remove_child() (because children + // is being iterated here) but also prevents gtk_window_set_transient_for from + // being called - this causes the crash on gnome. + gtk_window_set_transient_for((*it)->get_gtk_window(), NULL); + (*it)->set_owner(NULL); + destroy_and_delete_ctx(*it); + } + children.clear(); + + if (jwindow) { + mainEnv->CallVoidMethod(jwindow, jWindowNotifyDestroy); + EXCEPTION_OCCURED(mainEnv); + } + + if (jview) { + mainEnv->DeleteGlobalRef(jview); + jview = NULL; + } + + if (jwindow) { + mainEnv->DeleteGlobalRef(jwindow); + jwindow = NULL; + } + + can_be_deleted = true; +} + +void WindowContext::process_delete() { + if (jwindow && isEnabled()) { + gtk_widget_hide_on_delete(gtk_widget); + mainEnv->CallVoidMethod(jwindow, jWindowNotifyClose); + CHECK_JNI_EXCEPTION(mainEnv) + } +} + +void WindowContext::process_expose(GdkEventExpose *event) { + if (jview && is_visible()) { + mainEnv->CallVoidMethod(jview, jViewNotifyRepaint, event->area.x, event->area.y, + event->area.width, event->area.height); + CHECK_JNI_EXCEPTION(mainEnv) + } +} + +void WindowContext::process_mouse_button(GdkEventButton *event) { + // there are other events like GDK_2BUTTON_PRESS + if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) { + return; + } + + bool press = event->type == GDK_BUTTON_PRESS; + + guint state = event->state; + guint mask = 0; + + // We need to add/remove current mouse button from the modifier flags + // as X lib state represents the state just prior to the event and + // glass needs the state just after the event + switch (event->button) { + case 1: + mask = GDK_BUTTON1_MASK; + break; + case 2: + mask = GDK_BUTTON2_MASK; + break; + case 3: + mask = GDK_BUTTON3_MASK; + break; + case MOUSE_BACK_BTN: + mask = GDK_BUTTON4_MASK; + break; + case MOUSE_FORWARD_BTN: + mask = GDK_BUTTON5_MASK; + break; + } + + if (press) { + state |= mask; + } else { + state &= ~mask; + } + + if (press) { + GdkDevice* device = event->device; + + if (glass_gdk_device_is_grabbed(device) + && (glass_gdk_device_get_window_at_position(device, NULL, NULL) + == NULL)) { + ungrab_focus(); + return; + } + + // Upper layers expects from us Windows behavior: + // all mouse events should be delivered to window where drag begins + // and no exit/enter event should be reported during this drag. + // We can grab mouse pointer for these needs. + grab_mouse_drag_focus(gdk_window, (GdkEvent *) event, NULL, true); + } else { + if ((event->state & MOUSE_BUTTONS_MASK) + && !(state & MOUSE_BUTTONS_MASK)) { // all buttons released + ungrab_mouse_drag_focus(); + } else if (event->button == 8 || event->button == 9) { + // GDK X backend interprets button press events for buttons 4-7 as + // scroll events so GDK_BUTTON4_MASK and GDK_BUTTON5_MASK will never + // be set on the event->state from GDK. Thus we cannot check if all + // buttons have been released in the usual way (as above). + ungrab_mouse_drag_focus(); + } + } + + bool is_popup_trigger = (event->button == 3); + jint button = gtk_button_number_to_mouse_button(event->button); + + if (jview && button != com_sun_glass_events_MouseEvent_BUTTON_NONE) { + mainEnv->CallVoidMethod(jview, jViewNotifyMouse, + press ? com_sun_glass_events_MouseEvent_DOWN : com_sun_glass_events_MouseEvent_UP, + button, + (jint) event->x, (jint) event->y, + (jint) event->x_root, (jint) event->y_root, + gdk_modifier_mask_to_glass(state), + (is_popup_trigger) ? JNI_TRUE : JNI_FALSE, + JNI_FALSE); + CHECK_JNI_EXCEPTION(mainEnv) + + if (jview && is_popup_trigger) { + mainEnv->CallVoidMethod(jview, jViewNotifyMenu, + (jint) event->x, (jint) event->y, + (jint) event->x_root, (jint) event->y_root, + JNI_FALSE); + CHECK_JNI_EXCEPTION(mainEnv) + } + } +} + +void WindowContext::process_mouse_motion(GdkEventMotion *event) { + jint glass_modifier = gdk_modifier_mask_to_glass(event->state); + jint isDrag = glass_modifier & ( + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY | + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE | + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY | + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK | + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD); + jint button = com_sun_glass_events_MouseEvent_BUTTON_NONE; + + if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY) { + button = com_sun_glass_events_MouseEvent_BUTTON_LEFT; + } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE) { + button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY) { + button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT; + } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK) { + button = com_sun_glass_events_MouseEvent_BUTTON_BACK; + } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD) { + button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD; + } + + if (jview) { + mainEnv->CallVoidMethod(jview, jViewNotifyMouse, + isDrag ? com_sun_glass_events_MouseEvent_DRAG : com_sun_glass_events_MouseEvent_MOVE, + button, + (jint) event->x, (jint) event->y, + (jint) event->x_root, (jint) event->y_root, + glass_modifier, + JNI_FALSE, + JNI_FALSE); + CHECK_JNI_EXCEPTION(mainEnv) + } + + gdk_event_request_motions(event); +} + +void WindowContext::process_mouse_scroll(GdkEventScroll *event) { + jdouble dx = 0, dy = 0; + + // converting direction to change in pixels + switch (event->direction) { + case GDK_SCROLL_UP: + dy = 1; + break; + case GDK_SCROLL_DOWN: + dy = -1; + break; + case GDK_SCROLL_LEFT: + dx = 1; + break; + case GDK_SCROLL_RIGHT: + dx = -1; + break; + default: + break; + } + if (event->state & GDK_SHIFT_MASK) { + jdouble t = dy; + dy = dx; + dx = t; + } + if (jview) { + mainEnv->CallVoidMethod(jview, jViewNotifyScroll, + (jint) event->x, (jint) event->y, + (jint) event->x_root, (jint) event->y_root, + dx, dy, + gdk_modifier_mask_to_glass(event->state), + (jint) 0, (jint) 0, + (jint) 0, (jint) 0, + (jdouble) 40.0, (jdouble) 40.0); + CHECK_JNI_EXCEPTION(mainEnv) + } +} + +void WindowContext::process_mouse_cross(GdkEventCrossing *event) { + bool enter = event->type == GDK_ENTER_NOTIFY; + + if (jview) { + guint state = event->state; + if (enter) { // workaround for RT-21590 + state &= ~MOUSE_BUTTONS_MASK; + } + + if (enter != is_mouse_entered) { + is_mouse_entered = enter; + mainEnv->CallVoidMethod(jview, jViewNotifyMouse, + enter ? com_sun_glass_events_MouseEvent_ENTER + : com_sun_glass_events_MouseEvent_EXIT, + com_sun_glass_events_MouseEvent_BUTTON_NONE, + (jint) event->x, (jint) event->y, + (jint) event->x_root, (jint) event->y_root, + gdk_modifier_mask_to_glass(state), + JNI_FALSE, + JNI_FALSE); + CHECK_JNI_EXCEPTION(mainEnv) + } + } +} + +void WindowContext::process_key(GdkEventKey *event) { + bool press = event->type == GDK_KEY_PRESS; + jint glassKey = get_glass_key(event); + jint glassModifier = gdk_modifier_mask_to_glass(event->state); + if (press) { + glassModifier |= glass_key_to_modifier(glassKey); + } else { + glassModifier &= ~glass_key_to_modifier(glassKey); + } + jcharArray jChars = NULL; + jchar key = gdk_keyval_to_unicode(event->keyval); + if (key >= 'a' && key <= 'z' && (event->state & GDK_CONTROL_MASK)) { + key = key - 'a' + 1; // map 'a' to ctrl-a, and so on. + } else { +#ifdef GLASS_GTK2 + if (key == 0) { + // Work around "bug" fixed in gtk-3.0: + // http://mail.gnome.org/archives/commits-list/2011-March/msg06832.html + switch (event->keyval) { + case 0xFF08 /* Backspace */: key = '\b'; + case 0xFF09 /* Tab */: key = '\t'; + case 0xFF0A /* Linefeed */: key = '\n'; + case 0xFF0B /* Vert. Tab */: key = '\v'; + case 0xFF0D /* Return */: key = '\r'; + case 0xFF1B /* Escape */: key = '\033'; + case 0xFFFF /* Delete */: key = '\177'; + } + } +#endif + } + + if (key > 0) { + jChars = mainEnv->NewCharArray(1); + if (jChars) { + mainEnv->SetCharArrayRegion(jChars, 0, 1, &key); + CHECK_JNI_EXCEPTION(mainEnv) + } + } else { + jChars = mainEnv->NewCharArray(0); + } + if (jview) { + if (press) { + mainEnv->CallVoidMethod(jview, jViewNotifyKey, + com_sun_glass_events_KeyEvent_PRESS, + glassKey, + jChars, + glassModifier); + CHECK_JNI_EXCEPTION(mainEnv) + + if (jview && key > 0) { // TYPED events should only be sent for printable characters. + mainEnv->CallVoidMethod(jview, jViewNotifyKey, + com_sun_glass_events_KeyEvent_TYPED, + com_sun_glass_events_KeyEvent_VK_UNDEFINED, + jChars, + glassModifier); + CHECK_JNI_EXCEPTION(mainEnv) + } + } else { + mainEnv->CallVoidMethod(jview, jViewNotifyKey, + com_sun_glass_events_KeyEvent_RELEASE, + glassKey, + jChars, + glassModifier); + CHECK_JNI_EXCEPTION(mainEnv) + } + } +} + +void WindowContext::process_state(GdkEventWindowState *event) { + if (event->changed_mask & + (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_MAXIMIZED)) { + + if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) { + is_iconified = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED; + } + if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { + is_maximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; + } + + jint stateChangeEvent; + + if (is_iconified) { + stateChangeEvent = com_sun_glass_events_WindowEvent_MINIMIZE; + } else if (is_maximized) { + stateChangeEvent = com_sun_glass_events_WindowEvent_MAXIMIZE; + } else { + stateChangeEvent = com_sun_glass_events_WindowEvent_RESTORE; + if ((gdk_windowManagerFunctions & GDK_FUNC_MINIMIZE) == 0) { + // in this case - the window manager will not support the programatic + // request to iconify - so we need to restore it now. + gdk_window_set_functions(gdk_window, gdk_windowManagerFunctions); + } + } + + notify_state(stateChangeEvent); + } else if (event->changed_mask & GDK_WINDOW_STATE_ABOVE) { + notify_on_top(event->new_window_state & GDK_WINDOW_STATE_ABOVE); + } +} + +void WindowContext::process_net_wm_property() { + // Workaround for https://bugs.launchpad.net/unity/+bug/998073 + + // This is a Unity bug + if (!g_strcmp0("Unity", gdk_x11_screen_get_window_manager_name(gdk_screen_get_default()))) { + return; + } + + static GdkAtom atom_atom = gdk_atom_intern_static_string("ATOM"); + static GdkAtom atom_net_wm_state = gdk_atom_intern_static_string("_NET_WM_STATE"); + static GdkAtom atom_net_wm_state_hidden = gdk_atom_intern_static_string("_NET_WM_STATE_HIDDEN"); + static GdkAtom atom_net_wm_state_above = gdk_atom_intern_static_string("_NET_WM_STATE_ABOVE"); + + gint length; + glong *atoms = NULL; + + if (gdk_property_get(gdk_window, atom_net_wm_state, atom_atom, + 0, G_MAXLONG, FALSE, NULL, NULL, &length, (guchar * *) & atoms)) { + + bool is_hidden = false; + bool is_above = false; + for (gint i = 0; i < (gint)(length / sizeof(glong)); i++) { + if (atom_net_wm_state_hidden == (GdkAtom) atoms[i]) { + is_hidden = true; + } else if (atom_net_wm_state_above == (GdkAtom) atoms[i]) { + is_above = true; + } + } + + g_free(atoms); + + if (is_iconified != is_hidden) { + is_iconified = is_hidden; + + notify_state((is_hidden) + ? com_sun_glass_events_WindowEvent_MINIMIZE + : com_sun_glass_events_WindowEvent_RESTORE); + } + + notify_on_top(is_above); + } +} + +void WindowContext::process_screen_changed() { + glong to_screen = getScreenPtrForLocation(geometry.current_x, geometry.current_y); + if (to_screen != -1) { + if (to_screen != screen) { + if (jwindow) { + //notify screen changed + jobject jScreen = createJavaScreen(mainEnv, to_screen); + mainEnv->CallVoidMethod(jwindow, jWindowNotifyMoveToAnotherScreen, jScreen); + CHECK_JNI_EXCEPTION(mainEnv) + } + screen = to_screen; + } + } +} + +void WindowContext::notify_on_top(bool top) { + // Do not report effective (i.e. native) values to the FX, only if the user sets it manually + if (top != effective_on_top() && jwindow) { + if (on_top_inherited() && !top) { + // Disallow user's "on top" handling on windows that inherited the property + gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE); + } else { + on_top = top; + update_ontop_tree(top); + mainEnv->CallVoidMethod(jwindow, + jWindowNotifyLevelChanged, + top ? com_sun_glass_ui_Window_Level_FLOATING + : com_sun_glass_ui_Window_Level_NORMAL); + CHECK_JNI_EXCEPTION(mainEnv); + } + } +} + +void WindowContext::notify_repaint() { + int w, h; + glass_gdk_window_get_size(gdk_window, &w, &h); + if (jview) { + mainEnv->CallVoidMethod(jview, + jViewNotifyRepaint, + 0, 0, w, h); + CHECK_JNI_EXCEPTION(mainEnv); + } +} + +void WindowContext::notify_state(jint glass_state) { + if (glass_state == com_sun_glass_events_WindowEvent_RESTORE) { + if (is_maximized) { + glass_state = com_sun_glass_events_WindowEvent_MAXIMIZE; + } + + notify_repaint(); + } + + if (jwindow) { + mainEnv->CallVoidMethod(jwindow, + jGtkWindowNotifyStateChanged, + glass_state); + CHECK_JNI_EXCEPTION(mainEnv); + } +} + +bool WindowContext::set_view(jobject view) { + if (jview) { + mainEnv->CallVoidMethod(jview, jViewNotifyMouse, + com_sun_glass_events_MouseEvent_EXIT, + com_sun_glass_events_MouseEvent_BUTTON_NONE, + 0, 0, + 0, 0, + 0, + JNI_FALSE, + JNI_FALSE); + mainEnv->DeleteGlobalRef(jview); + } + + if (view) { + jview = mainEnv->NewGlobalRef(view); + } else { + jview = NULL; + } + return TRUE; +} + +void WindowContext::set_visible(bool visible) { + if (visible) { + gtk_widget_show_all(gtk_widget); + } else { + gtk_widget_hide(gtk_widget); + if (jview && is_mouse_entered) { + is_mouse_entered = false; + mainEnv->CallVoidMethod(jview, jViewNotifyMouse, + com_sun_glass_events_MouseEvent_EXIT, + com_sun_glass_events_MouseEvent_BUTTON_NONE, + 0, 0, + 0, 0, + 0, + JNI_FALSE, + JNI_FALSE); + CHECK_JNI_EXCEPTION(mainEnv) + } + } + + if (visible) { + visible_received = TRUE; + } + + //JDK-8220272 - fire event first because GDK_FOCUS_CHANGE is not always in order + if (visible && jwindow && isEnabled()) { + mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocus, com_sun_glass_events_WindowEvent_FOCUS_GAINED); + CHECK_JNI_EXCEPTION(mainEnv); + } +} + +void WindowContext::set_cursor(GdkCursor *cursor) { +// This seems to have no no effect on either Gtk+2 or Gtk+3 +// if (!is_in_drag()) { +// if (WindowContext::sm_mouse_drag_window) { +// grab_mouse_drag_focus(WindowContext::sm_mouse_drag_window->get_gdk_window(), NULL, cursor, false); +// } else if (WindowContext::sm_grab_window) { +// grab_mouse_drag_focus(WindowContext::sm_grab_window->get_gdk_window(), NULL, cursor, true); +// } +// } + + gdk_window_set_cursor(gdk_window, cursor); +} + +void WindowContext::set_level(int level) { + if (level == com_sun_glass_ui_Window_Level_NORMAL) { + on_top = false; + } else if (level == com_sun_glass_ui_Window_Level_FLOATING + || level == com_sun_glass_ui_Window_Level_TOPMOST) { + on_top = true; + } + // We need to emulate always on top behaviour on child windows + + if (!on_top_inherited()) { + update_ontop_tree(on_top); + } +} + +void WindowContext::set_background(float r, float g, float b) { + bg_color.red = r; + bg_color.green = g; + bg_color.blue = b; + bg_color.is_set = true; + notify_repaint(); +} + +void WindowContext::set_minimized(bool minimize) { + is_iconified = minimize; + if (minimize) { + if (frame_type == TRANSPARENT) { + // https://bugs.launchpad.net/ubuntu/+source/unity/+bug/1245571 + glass_window_reset_input_shape_mask(gtk_widget_get_window(gtk_widget)); + } + + if ((gdk_windowManagerFunctions & GDK_FUNC_MINIMIZE) == 0) { + // in this case - the window manager will not support the programatic + // request to iconify - so we need to disable this until we are restored. + GdkWMFunction wmf = (GdkWMFunction)(gdk_windowManagerFunctions | GDK_FUNC_MINIMIZE); + gdk_window_set_functions(gdk_window, wmf); + } + gtk_window_iconify(GTK_WINDOW(gtk_widget)); + } else { + gtk_window_deiconify(GTK_WINDOW(gtk_widget)); + activate_window(); + } +} + +void WindowContext::set_maximized(bool maximize) { + is_maximized = maximize; + + if (maximize) { + // enable the functionality + GdkWMFunction wmf = (GdkWMFunction)(gdk_windowManagerFunctions | GDK_FUNC_MAXIMIZE); + gdk_window_set_functions(gdk_window, wmf); + + ensure_window_size(); + gtk_window_maximize(GTK_WINDOW(gtk_widget)); + } else { + gtk_window_unmaximize(GTK_WINDOW(gtk_widget)); + } +} + +void WindowContext::set_bounds(int x, int y, bool xSet, bool ySet, int w, int h, int cw, int ch) { + // this will tell if adjustments are needed - that's because GTK does not have full window size + // values, just content values. Frame extents (window decorations) are handled by the window manager. + geometry.needs_ajustment = (w > 0 || h > 0) || geometry.needs_ajustment; + + // newW / newH always content sizes compatible with GTK+ + // if window has no decoration, adjustments will be ZERO + int newW, newH; + newW = (w > 0) ? w - geometry.adjust_w : cw; + newH = (h > 0) ? h - geometry.adjust_h : ch; + + geometry.current_w = newW; + geometry.current_h = newH; + + gboolean size_changed = FALSE; + gboolean pos_changed = FALSE; + + if (newW > 0 && newH > 0) { + size_changed = TRUE; + + // content size + geometry.current_cw = newW; + geometry.current_ch = newH; + geometry.last_cw = newW; + geometry.last_ch = newH; + + if (visible_received) { + // call apply_geometry() to let gtk_window_resize succeed, because it's bound to + // geometry constraints + apply_geometry(); + gtk_window_resize(GTK_WINDOW(gtk_widget), newW, newH); + } else { + gtk_window_set_default_size(GTK_WINDOW(gtk_widget), newW, newH); + } + } + + if (xSet || ySet) { + int newX = (xSet) ? x : geometry.current_x; + int newY = (ySet) ? y : geometry.current_y; + + if (newX != geometry.current_x || newY != geometry.current_y) { + pos_changed = TRUE; + geometry.current_x = newX; + geometry.current_y = newY; + + gtk_window_move(GTK_WINDOW(gtk_widget), newX, newY); + } + } + + size_position_notify(size_changed, pos_changed); +} + +void WindowContext::set_resizable(bool res) { + if (res != geometry.resizable) { + geometry.resizable = res; + apply_geometry(); + } +} + +void WindowContext::set_focusable(bool focusable) { + gtk_window_set_accept_focus(GTK_WINDOW(gtk_widget), focusable ? TRUE : FALSE); +} + +void WindowContext::set_title(const char *title) { + gtk_window_set_title(GTK_WINDOW(gtk_widget), title); +} + +void WindowContext::set_alpha(double alpha) { +#ifdef GLASS_GTK3 + gtk_widget_set_opacity(gtk_widget, (gdouble)alpha); +#else + gtk_window_set_opacity(GTK_WINDOW(gtk_widget), (gdouble)alpha); +#endif +} + +void WindowContext::set_enabled(bool enabled) { + if (enabled != geometry.enabled) { + gtk_widget_set_sensitive(gtk_widget, enabled); + geometry.enabled = enabled; + apply_geometry(); + } +} + +void WindowContext::set_minimum_size(int w, int h) { + gboolean changed = geometry.minw != w || geometry.minh != h; + + if (!changed) { + return; + } + + geometry.minw = w; + geometry.minh = h; + + apply_geometry(); +} + +void WindowContext::set_maximum_size(int w, int h) { + gboolean changed = geometry.maxw != w || geometry.maxh != h; + + if (!changed) { + return; + } + + geometry.maxw = w; + geometry.maxh = h; + + apply_geometry(); +} + +void WindowContext::set_icon(GdkPixbuf *pixbuf) { + gtk_window_set_icon(GTK_WINDOW(gtk_widget), pixbuf); +} + +void WindowContext::set_modal(bool modal, WindowContext *parent) { + if (modal) { + //gtk_window_set_type_hint(GTK_WINDOW(gtk_widget), GDK_WINDOW_TYPE_HINT_DIALOG); + if (parent) { + gtk_window_set_transient_for(GTK_WINDOW(gtk_widget), parent->get_gtk_window()); + } + } + gtk_window_set_modal(GTK_WINDOW(gtk_widget), modal ? TRUE : FALSE); +} + +void WindowContext::set_gravity(float x, float y) { + geometry.gravity_x = x; + geometry.gravity_y = y; +} + +void WindowContext::set_owner(WindowContext *owner_ctx) { + owner = owner_ctx; +} + +void WindowContext::add_child(WindowContext *child) { + children.insert(child); + gtk_window_set_transient_for(child->get_gtk_window(), this->get_gtk_window()); +} + +void WindowContext::remove_child(WindowContext *child) { + children.erase(child); + gtk_window_set_transient_for(child->get_gtk_window(), NULL); +} + +void WindowContext::show_or_hide_children(bool show) { + std::set::iterator it; + for (it = children.begin(); it != children.end(); ++it) { + (*it)->set_minimized(!show); + (*it)->show_or_hide_children(show); + } +} + +bool WindowContext::is_visible() { + return gtk_widget_get_visible(gtk_widget); +} + +bool WindowContext::is_dead() { + return can_be_deleted; +} + +bool WindowContext::grab_focus() { + if (WindowContext::sm_mouse_drag_window + || grab_mouse_drag_focus(gdk_window, NULL, NULL, true)) { + WindowContext::sm_grab_window = this; + return true; + } else { + return false; + } +} + +void WindowContext::ungrab_focus() { + if (!WindowContext::sm_mouse_drag_window) { + ungrab_mouse_drag_focus(); + } + + WindowContext::sm_grab_window = NULL; + + if (jwindow) { + mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocusUngrab); + CHECK_JNI_EXCEPTION(mainEnv) + } +} + +void WindowContext::restack(bool restack) { + gdk_window_restack(gdk_window, NULL, restack ? TRUE : FALSE); +} + +void WindowContext::request_focus() { + //JDK-8212060: Window show and then move glitch. + //The WindowContext::set_visible will take care of showing the window. + //The below code will only handle later request_focus. + if (is_visible()) { + gtk_window_present(GTK_WINDOW(gtk_widget)); + } +} + +void WindowContext::enter_fullscreen() { + is_fullscreen = TRUE; + + ensure_window_size(); + gtk_window_fullscreen(GTK_WINDOW(gtk_widget)); +} + +void WindowContext::exit_fullscreen() { + is_fullscreen = FALSE; + gtk_window_unfullscreen(GTK_WINDOW(gtk_widget)); +} + +// Applied to a temporary full screen window to prevent sending events to Java +void WindowContext::detach_from_java() { + if (jview) { + mainEnv->DeleteGlobalRef(jview); + jview = NULL; + } + if (jwindow) { + mainEnv->DeleteGlobalRef(jwindow); + jwindow = NULL; + } +} + +void WindowContext::increment_events_counter() { + ++events_processing_cnt; +} + +void WindowContext::decrement_events_counter() { + --events_processing_cnt; +} + +size_t WindowContext::get_events_count() { + return events_processing_cnt; +} + +///////////////////////// PROTECTED + +void WindowContext::applyShapeMask(void *data, uint width, uint height) { + if (frame_type != TRANSPARENT) { + return; + } + + glass_window_apply_shape_mask(gtk_widget_get_window(gtk_widget), data, width, height); +} + +///////////////////////// PRIVATE + +// this is to work-around past gtk+ bug +void WindowContext::ensure_window_size() { + gint w, h; +#ifdef GLASS_GTK3 + gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h); +#else + gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h, NULL); +#endif + if ((geometry.last_cw > 0 && geometry.last_ch > 0) + && (geometry.last_cw != w || geometry.last_ch != h)) { + gdk_window_resize(gdk_window, geometry.last_cw, geometry.last_ch); + } +} + +// This function calculate the deltas between window and window + decoration (titlebar, borders). +// It's used when the window manager does not support the _NET_FRAME_EXTENTS extension or when +// it's not received on time. +void WindowContext::calculate_adjustments() { + if (frame_type != TITLED || geometry.frame_extents_received) { + return; + } + + gint x, y, rx, ry; + gdk_window_get_origin(gdk_window, &x, &y); + gdk_window_get_root_origin(gdk_window, &rx, &ry); + + if (rx != x || ry != y) { + // the left extends are correct - the right one is guessed to be the same + geometry.adjust_w = (x - rx) * 2; + // guess that bottom size is the same as left and right + geometry.adjust_h = (y - ry) + (x - rx); + + // those will be correct + geometry.view_x = (x - rx); + geometry.view_y = (y - ry); + + if (geometry.needs_ajustment) { + set_bounds(0, 0, false, false, geometry.current_w, geometry.current_h, -1, -1); + } + + // force position notify so java will know about view_y and view_x + size_position_notify(false, true); + } +} + +void WindowContext::apply_geometry() { + if (!map_received) { + return; + } + + GdkGeometry gdk_geometry; + gdk_geometry.win_gravity = GDK_GRAVITY_NORTH_WEST; + + if ((!geometry.resizable || !geometry.enabled) && !(is_maximized || is_fullscreen)) { + // not resizeable + int w = geometry.current_cw > 0 + ? geometry.current_cw + : geometry.current_w - geometry.adjust_w; + + int h = geometry.current_ch > 0 + ? geometry.current_ch + : geometry.current_h - geometry.adjust_h; + + gdk_geometry.min_width = gdk_geometry.max_width = w; + gdk_geometry.min_height = gdk_geometry.max_height = h; + } else { + //min/max width/height always whole window size (with decors) + gdk_geometry.min_width = (geometry.minw - geometry.adjust_w) > 0 + ? geometry.minw - geometry.adjust_w : 1; + gdk_geometry.min_height = (geometry.minh - geometry.adjust_h) > 0 + ? geometry.minh - geometry.adjust_h : 1; + + gdk_geometry.max_width = (geometry.maxw - geometry.adjust_w > 0) + ? geometry.maxw - geometry.adjust_w : G_MAXINT; + gdk_geometry.max_height = (geometry.maxh - geometry.adjust_h > 0) + ? geometry.maxh - geometry.adjust_h : G_MAXINT; + } + + gtk_window_set_geometry_hints(GTK_WINDOW(gtk_widget), NULL, &gdk_geometry, + (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_WIN_GRAVITY)); +} + +bool WindowContext::get_frame_extents_property(int *top, int *left, + int *bottom, int *right) { + unsigned long *extents; + + if (gdk_property_get(gdk_window, + atom_net_wm_frame_extents, + gdk_atom_intern("CARDINAL", FALSE), + 0, + sizeof(unsigned long) * 4, + FALSE, + NULL, + NULL, + NULL, + (guchar * *) & extents)) { + *left = extents[0]; + *right = extents[1]; + *top = extents[2]; + *bottom = extents[3]; + + g_free(extents); + return true; + } + + return false; +} + +void WindowContext::activate_window() { + Display *display = GDK_DISPLAY_XDISPLAY(gdk_window_get_display(gdk_window)); + Atom navAtom = XInternAtom(display, "_NET_ACTIVE_WINDOW", True); + if (navAtom != None) { + XClientMessageEvent clientMessage; + memset(&clientMessage, 0, sizeof(clientMessage)); + + clientMessage.type = ClientMessage; + clientMessage.window = GDK_WINDOW_XID(gdk_window); + clientMessage.message_type = navAtom; + clientMessage.format = 32; + clientMessage.data.l[0] = 1; + clientMessage.data.l[1] = gdk_x11_get_server_time(gdk_window); + clientMessage.data.l[2] = 0; + + XSendEvent(display, XDefaultRootWindow(display), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent * ) & clientMessage); + XFlush(display); + } +} + +void WindowContext::size_position_notify(bool size_changed, bool pos_changed) { + + if (jview) { + if (size_changed) { + mainEnv->CallVoidMethod(jview, jViewNotifyResize, geometry.current_cw, geometry.current_ch); + CHECK_JNI_EXCEPTION(mainEnv); + } + + if (pos_changed) { + mainEnv->CallVoidMethod(jview, jViewNotifyView, com_sun_glass_events_ViewEvent_MOVE); + CHECK_JNI_EXCEPTION(mainEnv) + } + } + + if (jwindow) { + if (size_changed || is_maximized) { + mainEnv->CallVoidMethod(jwindow, jWindowNotifyResize, + (is_maximized) + ? com_sun_glass_events_WindowEvent_MAXIMIZE + : com_sun_glass_events_WindowEvent_RESIZE, + geometry.current_w, geometry.current_h); + CHECK_JNI_EXCEPTION(mainEnv) + } + + if (pos_changed) { + mainEnv->CallVoidMethod(jwindow, jWindowNotifyMove, geometry.current_x, geometry.current_y); + CHECK_JNI_EXCEPTION(mainEnv) + } + } +} + +void WindowContext::update_ontop_tree(bool on_top) { + bool effective_on_top = on_top || this->on_top; + gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), effective_on_top ? TRUE : FALSE); + for (std::set::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->update_ontop_tree(effective_on_top); + } +} + +bool WindowContext::on_top_inherited() { + WindowContext *o = owner; + while (o) { + WindowContext *topO = dynamic_cast(o); + if (!topO) break; + if (topO->on_top) { + return true; + } + o = topO->owner; + } + return false; +} + +bool WindowContext::effective_on_top() { + if (owner) { + WindowContext *topO = dynamic_cast(owner); + return (topO && topO->effective_on_top()) || on_top; + } + return on_top; +} + +bool WindowContext::grab_mouse_drag_focus(GdkWindow * gdk_w, GdkEvent * event, GdkCursor * cursor, bool owner_events) { + if (is_grab_disabled()) { + return true; + } + + ungrab_mouse_drag_focus(); + +#ifdef GLASS_GTK3 + if (event != NULL) { + grab_pointer = gdk_event_get_device (event); + } else { + grab_pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(gtk_widget))); + } + + GdkGrabStatus status = gdk_device_grab((GdkDevice *) grab_pointer, gdk_w, GDK_OWNERSHIP_WINDOW, owner_events, + (GdkEventMask) + (GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON_MOTION_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON2_MOTION_MASK + | GDK_BUTTON3_MOTION_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK), cursor, GDK_CURRENT_TIME); +#else + GdkGrabStatus status = gdk_pointer_grab(gdk_w, owner_events, + (GdkEventMask) + (GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON_MOTION_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON2_MOTION_MASK + | GDK_BUTTON3_MOTION_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK), NULL, cursor, GDK_CURRENT_TIME); +#endif + WindowContext::sm_mouse_drag_window = this; + + return (status == GDK_GRAB_SUCCESS) ? true : false; +} + +void WindowContext::ungrab_mouse_drag_focus() { + if (!grab_pointer) { + return; + } + +#ifdef GLASS_GTK3 + gdk_device_ungrab((GdkDevice *) grab_pointer, GDK_CURRENT_TIME); +#else + gdk_pointer_ungrab(GDK_CURRENT_TIME); +#endif + grab_pointer = NULL; + WindowContext::sm_mouse_drag_window = NULL; + + if (WindowContext::sm_grab_window) { + WindowContext::sm_grab_window->grab_focus(); + } +} + +WindowContext::~WindowContext() { + if (xim.ic) { + XDestroyIC(xim.ic); + xim.ic = NULL; + } + if (xim.im) { + XCloseIM(xim.im); + xim.im = NULL; + } + + gtk_widget_destroy(gtk_widget); +} diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window.h new file mode 100644 index 0000000000..2cda2040e5 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2011, 2020, 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. + */ +#ifndef GLASS_WINDOW_H +#define GLASS_WINDOW_H + +#include +#include + +#include +#include +#include + +#include "glass_view.h" +#include "glass_general.h" + +enum WindowFrameType { + TITLED, + UNTITLED, + TRANSPARENT +}; + +enum WindowType { + NORMAL, + UTILITY, + POPUP +}; + +enum request_type { + REQUEST_NONE, + REQUEST_RESIZABLE, + REQUEST_NOT_RESIZABLE +}; + +static const guint MOUSE_BUTTONS_MASK = (guint)(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK); + +struct BgColor { + BgColor() : red(0), green(0), blue(0), is_set(FALSE) {} + + float red; + float green; + float blue; + bool is_set; +}; + +struct WindowGeometry { + WindowGeometry() : current_x(0), + current_y(0), + current_w(0), + current_h(0), + current_cw(0), + current_ch(0), + last_cw(0), + last_ch(0), + adjust_w(0), + adjust_h(0), + view_x(0), + view_y(0), + frame_extents_received(false), + gravity_x(1.00), + gravity_y(1.00), + enabled(true), + resizable(true), + minw(-1), + minh(-1), + maxw(-1), + maxh(-1), + needs_ajustment(false) {} + + int current_x; // current position X + int current_y; // current position Y + int current_w; // current window width, adjusted + int current_h; // current window height, adjusted + int current_cw; // current content (view) width + int current_ch; // current content (view) height + int last_cw; // not subjected to fullscreen / maximize + int last_ch; + + // Used to ajust window sizes because gtk doest not account frame extents as part + // of the window size and JavaFx does. + int adjust_w; + int adjust_h; + + // The position of the view relative to the window + int view_x; + int view_y; + + // If WM supports _NET_REQUEST_FRAME_EXTENTS and it was received + bool frame_extents_received; + + // Currently not used + float gravity_x; + float gravity_y; + + bool enabled; + bool resizable; + + int minw; + int minh; + + int maxw; + int maxh; + + // if the window size was set (instead of content size) - this is used to + // "fix" the window size accouting extents. + bool needs_ajustment; +}; + +class WindowContext { +private: + jlong screen; + WindowFrameType frame_type; + WindowType window_type; + struct WindowContext *owner; + jobject jwindow; + jobject jview; + + bool map_received; + bool visible_received; + bool on_top; + bool is_fullscreen; + bool is_iconified; + bool is_maximized; + bool is_mouse_entered; + bool can_be_deleted; + + struct _XIM { + _XIM() : im(NULL), ic(NULL), enabled(FALSE) {} + XIM im; + XIC ic; + bool enabled; + } xim; + + size_t events_processing_cnt; + + WindowGeometry geometry; + std::set children; + GdkWMFunction gdk_windowManagerFunctions; + GtkWidget *gtk_widget; + GdkWindow *gdk_window; + BgColor bg_color; + void *grab_pointer; + + static WindowContext* sm_mouse_drag_window; + static WindowContext* sm_grab_window; +public: + WindowContext(jobject, WindowContext *, long, WindowFrameType, WindowType, GdkWMFunction); + + bool hasIME(); + bool filterIME(GdkEvent *); + void enableOrResetIME(); + void disableIME(); + + void paint(void*, jint, jint); + bool isEnabled(); + + GdkWindow *get_gdk_window(); + GtkWidget *get_gtk_widget(); + GtkWindow *get_gtk_window(); + WindowGeometry get_geometry(); + jobject get_jwindow(); + jobject get_jview(); + + void process_map(); + void process_focus(GdkEventFocus*); + void process_property_notify(GdkEventProperty *); + void process_configure(); + void process_destroy(); + void process_delete(); + void process_expose(GdkEventExpose*); + void process_mouse_button(GdkEventButton*); + void process_mouse_motion(GdkEventMotion*); + void process_mouse_scroll(GdkEventScroll*); + void process_mouse_cross(GdkEventCrossing*); + void process_key(GdkEventKey*); + void process_state(GdkEventWindowState*); + void process_net_wm_property(); + void process_screen_changed(); + + void notify_on_top(bool); + void notify_repaint(); + void notify_state(jint); + + bool set_view(jobject); + void set_visible(bool); + void set_cursor(GdkCursor*); + void set_level(int); + void set_background(float, float, float); + void set_minimized(bool); + void set_maximized(bool); + void set_bounds(int, int, bool, bool, int, int, int, int); + void set_resizable(bool); + void set_focusable(bool); + void set_title(const char *); + void set_alpha(double); + void set_enabled(bool); + void set_minimum_size(int, int); + void set_maximum_size(int, int); + void set_icon(GdkPixbuf *); + void set_modal(bool, WindowContext *parent = NULL); + void set_gravity(float, float); + void set_owner(WindowContext *); + void add_child(WindowContext *); + void remove_child(WindowContext *); + void show_or_hide_children(bool); + bool is_visible(); + bool is_dead(); + bool grab_focus(); + void ungrab_focus(); + void restack(bool); + void request_focus(); + void enter_fullscreen(); + void exit_fullscreen(); + void detach_from_java(); + void increment_events_counter(); + void decrement_events_counter(); + size_t get_events_count(); + ~WindowContext(); + +protected: + void applyShapeMask(void *, uint width, uint height); + +private: + bool im_filter_keypress(GdkEventKey*); + void ensure_window_size(); + void calculate_adjustments(); + void apply_geometry(); + bool get_frame_extents_property(int *, int *, int *, int *); + void activate_window(); + void size_position_notify(bool, bool); + void update_ontop_tree(bool); + bool on_top_inherited(); + bool effective_on_top(); + bool grab_mouse_drag_focus(GdkWindow *, GdkEvent *, GdkCursor *, bool); + void ungrab_mouse_drag_focus(); +}; + +void destroy_and_delete_ctx(WindowContext *ctx); + +class EventsCounterHelper { +private: + WindowContext *ctx; +public: + explicit EventsCounterHelper(WindowContext *context) { + ctx = context; + ctx->increment_events_counter(); + } + + ~EventsCounterHelper() { + ctx->decrement_events_counter(); + if (ctx->is_dead() && ctx->get_events_count() == 0) { + delete ctx; + } + ctx = NULL; + } +}; + +#endif /* GLASS_WINDOW_H */ + diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window_ime.cpp b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window_ime.cpp new file mode 100644 index 0000000000..3c50d6a1bd --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/glass_window_ime.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2011, 2020, 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 "com_sun_glass_ui_View.h" +#include "glass_window.h" +#include "glass_general.h" + +#include +#include + +bool WindowContext::hasIME() { + return xim.enabled; +} + +static XKeyPressedEvent convert_event(GdkEventKey *event) { + XKeyPressedEvent result; + memset(&result, 0, sizeof (result)); + + result.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease; + result.send_event = event->send_event; + result.display = gdk_x11_display_get_xdisplay(gdk_window_get_display(event->window)); + result.window = result.subwindow = GDK_WINDOW_XID(event->window); + result.root = GDK_WINDOW_XID(gdk_screen_get_root_window(glass_gdk_window_get_screen(event->window))); + result.time = event->time; + result.state = event->state; + result.keycode = event->hardware_keycode; + result.same_screen = True; + + return result; +} + +bool WindowContext::im_filter_keypress(GdkEventKey* event) { + static size_t buf_len = 12; + static char *buffer = NULL; + + if (buffer == NULL) { + buffer = (char*)malloc(buf_len * sizeof (char)); + } + + KeySym keysym; + Status status; + XKeyPressedEvent xevent = convert_event(event); + if (XFilterEvent((XEvent*) & xevent, GDK_WINDOW_XID(gdk_window))) { + return TRUE; + } + + if (event->type == GDK_KEY_RELEASE) { + process_key(event); + return TRUE; + } + + int len = Xutf8LookupString(xim.ic, &xevent, buffer, buf_len - 1, &keysym, &status); + if (status == XBufferOverflow) { + buf_len = len + 1; + buffer = (char*)realloc(buffer, buf_len * sizeof (char)); + len = Xutf8LookupString(xim.ic, &xevent, buffer, buf_len - 1, + &keysym, &status); + } + switch (status) { + case XLookupKeySym: + case XLookupBoth: + if (xevent.keycode) { + //process it as a normal key + process_key(event); + break; + } + // fall-through + case XLookupChars: + buffer[len] = 0; + jstring str = mainEnv->NewStringUTF(buffer); + EXCEPTION_OCCURED(mainEnv); + jsize slen = mainEnv->GetStringLength(str); + mainEnv->CallVoidMethod(jview, + jViewNotifyInputMethod, + str, + NULL, NULL, NULL, + slen, + slen, + 0); + LOG_EXCEPTION(mainEnv) + + break; + } + + return TRUE; +} + +bool WindowContext::filterIME(GdkEvent * event) { + if (!hasIME()) { + return false; + } + + switch (event->type) { + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + return im_filter_keypress(reinterpret_cast (event)); + default: + return FALSE; + } +} + +//Note: this function must return int, despite the fact it doesn't conform to XIMProc type. +// This is required in documentation of XIM +static int im_preedit_start(XIM im_xim, XPointer client, XPointer call) { + (void)im_xim; + (void)call; + + mainEnv->CallVoidMethod((jobject) client, jViewNotifyPreeditMode, JNI_TRUE); + CHECK_JNI_EXCEPTION_RET(mainEnv, -1); + return -1; // No restrictions +} + +static void im_preedit_done(XIM im_xim, XPointer client, XPointer call) { + (void)im_xim; + (void)call; + + mainEnv->CallVoidMethod((jobject) client, jViewNotifyPreeditMode, JNI_FALSE); + CHECK_JNI_EXCEPTION(mainEnv); +} + +static void im_preedit_draw(XIM im_xim, XPointer client, XPointer call) { + (void)im_xim; + (void)call; + + XIMPreeditDrawCallbackStruct *data = (XIMPreeditDrawCallbackStruct*) call; + jstring text = NULL; + jbyteArray attr = NULL; + + if (data->text != NULL) { + if (data->text->string.multi_byte) { + if (data->text->encoding_is_wchar) { + size_t csize = wcstombs(NULL, data->text->string.wide_char, 0); + char *ctext = new char[csize + 1]; + wcstombs(ctext, data->text->string.wide_char, csize + 1); + text = mainEnv->NewStringUTF(ctext); + delete[] ctext; + CHECK_JNI_EXCEPTION(mainEnv); + } else { + text = mainEnv->NewStringUTF(data->text->string.multi_byte); + CHECK_JNI_EXCEPTION(mainEnv); + } + } + + if (XIMFeedback* fb = data->text->feedback) { + attr = mainEnv->NewByteArray(data->text->length); + CHECK_JNI_EXCEPTION(mainEnv) + jbyte v[data->text->length]; + for (int i = 0; i < data->text->length; i++) { + if (fb[i] & XIMReverse) { + v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_NOTCONVERTED; + } else if (fb[i] & XIMHighlight) { + v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_CONVERTED; + } else if (fb[i] & XIMUnderline) { + v[i] = com_sun_glass_ui_View_IME_ATTR_CONVERTED; + } else { + v[i] = com_sun_glass_ui_View_IME_ATTR_INPUT; + } + } + mainEnv->SetByteArrayRegion(attr, 0, data->text->length, v); + CHECK_JNI_EXCEPTION(mainEnv) + } + } + + mainEnv->CallVoidMethod((jobject)client, jViewNotifyInputMethodDraw, + text, data->chg_first, data->chg_length, data->caret, attr); + CHECK_JNI_EXCEPTION(mainEnv) +} + +static void im_preedit_caret(XIM im_xim, XPointer client, XPointer call) { + (void)im_xim; + + XIMPreeditCaretCallbackStruct *data = (XIMPreeditCaretCallbackStruct*) call; + mainEnv->CallVoidMethod((jobject)client, jViewNotifyInputMethodCaret, + data->position, data->direction, data->style); + CHECK_JNI_EXCEPTION(mainEnv) +} + +static XIMStyle get_best_supported_style(XIM im_xim) +{ + XIMStyles* styles; + int i; + XIMStyle result = 0; + + if (XGetIMValues(im_xim, XNQueryInputStyle, &styles, NULL) != NULL) { // NULL means it's OK + return 0; + } + + for (i = 0; i < styles->count_styles; ++i) { + if (styles->supported_styles[i] == (XIMPreeditCallbacks | XIMStatusNothing) + || styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) { + result = styles->supported_styles[i]; + break; + } + } + + XFree(styles); + + return result; +} + +void WindowContext::enableOrResetIME() { + Display *display = gdk_x11_display_get_xdisplay(gdk_window_get_display(gdk_window)); + if (xim.im == NULL || xim.ic == NULL) { + xim.im = XOpenIM(display, NULL, NULL, NULL); + if (xim.im == NULL) { + return; + } + + XIMStyle styles = get_best_supported_style(xim.im); + if (styles == 0) { + return; + } + + XIMCallback startCallback = {(XPointer) jview, (XIMProc) (void *) im_preedit_start}; + XIMCallback doneCallback = {(XPointer) jview, im_preedit_done}; + XIMCallback drawCallback = {(XPointer) jview, im_preedit_draw}; + XIMCallback caretCallback = {(XPointer) jview, im_preedit_caret}; + + XVaNestedList list = XVaCreateNestedList(0, + XNPreeditStartCallback, &startCallback, + XNPreeditDoneCallback, &doneCallback, + XNPreeditDrawCallback, &drawCallback, + XNPreeditCaretCallback, &caretCallback, + NULL); + + xim.ic = XCreateIC(xim.im, + XNInputStyle, styles, + XNClientWindow, GDK_WINDOW_XID(gdk_window), + XNPreeditAttributes, list, + NULL); + + XFree(list); + + if (xim.ic == NULL) { + return; + } + } + + if (xim.enabled) { //called when changed focus to different input + XmbResetIC(xim.ic); + } + + + XSetICFocus(xim.ic); + + xim.enabled = TRUE; +} + +void WindowContext::disableIME() { + if (xim.ic != NULL) { + XUnsetICFocus(xim.ic); + } +} diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/wrapped.c b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/wrapped.c new file mode 100644 index 0000000000..a4e497fb55 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/wrapped.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2016, 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 +#include +#include +#ifndef __USE_GNU // required for dladdr() & Dl_info +#define __USE_GNU +#endif +#include +#include + +#include +#include +#include + +#include "wrapped.h" + +extern jboolean gtk_verbose; + +/* + * cpp and dlsym don't play nicely together. Do all dynamic loading in C + */ + +// Note added in Glib 2.36 which is >= our OEL 7.0 version of 2.36 +// but does not seem to be in the headers properly +static GSettingsSchemaSource * (*_g_settings_schema_source_get_default) (void); + +GSettingsSchemaSource * wrapped_g_settings_schema_source_get_default (void) +{ + if(_g_settings_schema_source_get_default == NULL) { + _g_settings_schema_source_get_default = dlsym(RTLD_DEFAULT, "g_settings_schema_source_get_default"); + if (gtk_verbose && _g_settings_schema_source_get_default) { + fprintf(stderr, "loaded g_settings_schema_source_get_default\n"); fflush(stderr); + } + } + + if(_g_settings_schema_source_get_default != NULL) { + return (*_g_settings_schema_source_get_default)(); + } + + return NULL; +} + + +// Note added in Glib 2.36 which is >= our OEL 7.0 version of 2.36 +// but does not seem to be in the headers properly +static GSettingsSchema * + (*_g_settings_schema_source_lookup) (GSettingsSchemaSource *source, + const gchar *schema_id, + gboolean recursive); + +GSettingsSchema * +wrapped_g_settings_schema_source_lookup (GSettingsSchemaSource *source, + const gchar *schema_id, + gboolean recursive) +{ + if(_g_settings_schema_source_lookup == NULL) { + _g_settings_schema_source_lookup = dlsym(RTLD_DEFAULT, "g_settings_schema_source_lookup"); + if (gtk_verbose && _g_settings_schema_source_lookup) { + fprintf(stderr, "loaded g_settings_schema_source_lookup\n"); fflush(stderr); + } + } + + if(_g_settings_schema_source_lookup != NULL) { + return (*_g_settings_schema_source_lookup)(source, schema_id, recursive); + } + + return NULL; +} + +// Note added in Glib 2.40 which is > our OEL 7.0 version of 2.36 +static gboolean (*_g_settings_schema_has_key) (GSettingsSchema *schema, const gchar *name); + +gboolean wrapped_g_settings_schema_has_key (GSettingsSchema *schema, + const gchar *name) +{ + if(_g_settings_schema_has_key == NULL) { + _g_settings_schema_has_key = dlsym(RTLD_DEFAULT, "g_settings_schema_has_key"); + if (gtk_verbose && _g_settings_schema_has_key) { + fprintf(stderr, "loaded g_settings_schema_has_key\n"); fflush(stderr); + } + } + + if(_g_settings_schema_has_key != NULL) { + return (*_g_settings_schema_has_key)(schema, name); + } + + return 0; +} + +static void (*_g_settings_schema_unref) (GSettingsSchema *schema); + +void wrapped_g_settings_schema_unref (GSettingsSchema *schema) +{ + if(_g_settings_schema_unref == NULL) { + _g_settings_schema_unref = dlsym(RTLD_DEFAULT, "g_settings_schema_unref"); + if (gtk_verbose && _g_settings_schema_unref) { + fprintf(stderr, "loaded g_settings_schema_unref\n"); fflush(stderr); + } + } + + if(_g_settings_schema_unref != NULL) { + (*_g_settings_schema_unref)(schema); + } + +} + +static void (*_gdk_x11_display_set_window_scale) (GdkDisplay *display, gint scale); + +// Note added in libgdk 3.10 which is > our OEL 7.0 version of 3.8 +void wrapped_gdk_x11_display_set_window_scale (GdkDisplay *display, + gint scale) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + if(_gdk_x11_display_set_window_scale == NULL) { + _gdk_x11_display_set_window_scale = dlsym(RTLD_DEFAULT, "gdk_x11_display_set_window_scale"); + if (gtk_verbose && _gdk_x11_display_set_window_scale) { + fprintf(stderr, "loaded gdk_x11_display_set_window_scale\n"); fflush(stderr); + } + } +#endif + + if(_gdk_x11_display_set_window_scale != NULL) { + (*_gdk_x11_display_set_window_scale)(display, scale); + } +} + diff --git a/modules/javafx.graphics/src/main/native-glass/gtk_experimental/wrapped.h b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/wrapped.h new file mode 100644 index 0000000000..c0e19a6fc0 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/gtk_experimental/wrapped.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, 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. + */ + +#ifndef WRAPPED_H +#define WRAPPED_H + +#ifdef __cplusplus +extern "C" { +#endif + +GSettingsSchemaSource * wrapped_g_settings_schema_source_get_default (void); + +GSettingsSchema * +wrapped_g_settings_schema_source_lookup (GSettingsSchemaSource *source, + const gchar *schema_id, + gboolean recursive); + +gboolean wrapped_g_settings_schema_has_key (GSettingsSchema *schema, const gchar *name); + +void wrapped_g_settings_schema_unref (GSettingsSchema *schema); + +void wrapped_gdk_x11_display_set_window_scale (GdkDisplay *display, gint scale); + +#ifdef __cplusplus +} +#endif + +#endif /* WRAPPED_H */