1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 #include <X11/Xlib.h>
  26 #include <X11/Xatom.h>
  27 #include <gdk/gdk.h>
  28 #include <gdk/gdkx.h>
  29 #include <gtk/gtk.h>
  30 #include <glib.h>
  31 
  32 #include <cstdlib>
  33 #include <iostream>
  34 #include <com_sun_glass_ui_Application.h>
  35 #include <com_sun_glass_ui_gtk_GtkApplication.h>
  36 #include <com_sun_glass_events_WindowEvent.h>
  37 #include <com_sun_glass_events_MouseEvent.h>
  38 #include <com_sun_glass_events_ViewEvent.h>
  39 #include <com_sun_glass_events_KeyEvent.h>
  40 #include <jni.h>
  41 
  42 #include "glass_general.h"
  43 #include "glass_evloop.h"
  44 #include "glass_dnd.h"
  45 #include "glass_window.h"
  46 #include "glass_screen.h"
  47 
  48 GdkEventFunc process_events_prev;
  49 static void process_events(GdkEvent*, gpointer);
  50 
  51 JNIEnv* mainEnv; // Use only with main loop thread!!!
  52 
  53 extern gboolean disableGrab;
  54 
  55 static gboolean call_runnable (gpointer data)
  56 {
  57     RunnableContext* context = reinterpret_cast<RunnableContext*>(data);
  58 
  59     mainEnv->CallVoidMethod(context->runnable, jRunnableRun, NULL);
  60     LOG_EXCEPTION(mainEnv);
  61     mainEnv->DeleteGlobalRef(context->runnable);
  62     free(context);
  63     return FALSE;
  64 }
  65 
  66 extern "C" {
  67 
  68 /*
  69  * Class:     com_sun_glass_ui_gtk_GtkApplication
  70  * Method:    _isDisplayValid
  71  * Signature: ()Z
  72  */
  73 JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1isDisplayValid
  74   (JNIEnv * env, jclass clazz)
  75 {
  76     (void)env;
  77     (void)clazz;
  78 
  79     return is_display_valid();
  80 }
  81 
  82 /*
  83  * Class:     com_sun_glass_ui_gtk_GtkApplication
  84  * Method:    _init
  85  * Signature: ()V
  86  */
  87 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1init
  88   (JNIEnv * env, jobject obj, jlong handler, jboolean _disableGrab)
  89 {
  90     (void)obj;
  91 
  92     mainEnv = env;
  93     process_events_prev = (GdkEventFunc) handler;
  94     disableGrab = (gboolean) _disableGrab;
  95 
  96     gdk_event_handler_set(process_events, NULL, NULL);
  97 
  98     GdkScreen *default_gdk_screen = gdk_screen_get_default();
  99     if (default_gdk_screen != NULL) {
 100         g_signal_connect(G_OBJECT(default_gdk_screen), "monitors-changed",
 101                          G_CALLBACK(screen_settings_changed), NULL);
 102         g_signal_connect(G_OBJECT(default_gdk_screen), "size-changed",
 103                          G_CALLBACK(screen_settings_changed), NULL);
 104     }
 105 
 106     GdkWindow *root = gdk_screen_get_root_window(default_gdk_screen);
 107     gdk_window_set_events(root, static_cast<GdkEventMask>(gdk_window_get_events(root) | GDK_PROPERTY_CHANGE_MASK));
 108 }
 109 
 110 /*
 111  * Class:     com_sun_glass_ui_gtk_GtkApplication
 112  * Method:    _runLoop
 113  * Signature: (Ljava/lang/Runnable;Z)V
 114  */
 115 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1runLoop
 116   (JNIEnv * env, jobject obj, jobject launchable, jboolean noErrorTrap)
 117 {
 118     (void)obj;
 119     (void)noErrorTrap;
 120 
 121     env->CallVoidMethod(launchable, jRunnableRun);
 122     CHECK_JNI_EXCEPTION(env);
 123 
 124     // GTK installs its own X error handler that conflicts with AWT.
 125     // During drag and drop, AWT hides errors so we need to hide them
 126     // to avoid exit()'ing.  It's not clear that we don't want to hide
 127     // X error all the time, otherwise FX will exit().
 128     //
 129     // A better solution would be to coordinate with AWT and save and
 130     // restore the X handler.
 131 
 132     // Disable X error handling
 133 #ifndef VERBOSE
 134     if (!noErrorTrap) {
 135         gdk_error_trap_push();
 136     }
 137 #endif
 138 
 139     gtk_main();
 140 
 141     // When the last JFrame closes and DISPOSE_ON_CLOSE is specified,
 142     // Java exits with an X error. X error are hidden during the FX
 143     // event loop and should be restored when the event loop exits. Unfortunately,
 144     // this is too early. The fix is to never restore X errors.
 145     //
 146     // See RT-21408 & RT-20756
 147 
 148     // Restore X error handling
 149     // #ifndef VERBOSE
 150     //     if (!noErrorTrap) {
 151     //         gdk_error_trap_pop();
 152     //     }
 153     // #endif
 154 
 155     gdk_threads_leave();
 156 
 157 }
 158 
 159 /*
 160  * Class:     com_sun_glass_ui_gtk_GtkApplication
 161  * Method:    _terminateLoop
 162  * Signature: ()V
 163  */
 164 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1terminateLoop
 165   (JNIEnv * env, jobject obj)
 166 {
 167     (void)env;
 168     (void)obj;
 169 
 170     gtk_main_quit();
 171 }
 172 
 173 /*
 174  * Class:     com_sun_glass_ui_gtk_GtkApplication
 175  * Method:    _submitForLaterInvocation
 176  * Signature: (Ljava/lang/Runnable;)V
 177  */
 178 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1submitForLaterInvocation
 179   (JNIEnv * env, jobject obj, jobject runnable)
 180 {
 181     (void)obj;
 182 
 183     RunnableContext* context = (RunnableContext*)malloc(sizeof(RunnableContext));
 184     context->runnable = env->NewGlobalRef(runnable);
 185     gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE + 30, call_runnable, context, NULL);
 186 }
 187 
 188 /*
 189  * Class:     com_sun_glass_ui_gtk_GtkApplication
 190  * Method:    enterNestedEventLoopImpl
 191  * Signature: ()V
 192  */
 193 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_enterNestedEventLoopImpl
 194   (JNIEnv * env, jobject obj)
 195 {
 196     (void)env;
 197     (void)obj;
 198 
 199     gtk_main();
 200 }
 201 
 202 /*
 203  * Class:     com_sun_glass_ui_gtk_GtkApplication
 204  * Method:    leaveNestedEventLoopImpl
 205  * Signature: ()V
 206  */
 207 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_leaveNestedEventLoopImpl
 208   (JNIEnv * env, jobject obj)
 209 {
 210     (void)env;
 211     (void)obj;
 212 
 213     gtk_main_quit();
 214 }
 215 
 216 /*
 217  * Class:     com_sun_glass_ui_gtk_GtkApplication
 218  * Method:    staticScreen_getScreens
 219  * Signature: ()[Lcom/sun/glass/ui/Screen;
 220  */
 221 JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticScreen_1getScreens
 222   (JNIEnv * env, jobject obj)
 223 {
 224     (void)obj;
 225 
 226     try {
 227         return rebuild_screens(env);
 228     } catch (jni_exception&) {
 229         return NULL;
 230     }
 231 }
 232 
 233 /*
 234  * Class:     com_sun_glass_ui_gtk_GtkApplication
 235  * Method:    staticTimer_getMinPeriod
 236  * Signature: ()I
 237  */
 238 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticTimer_1getMinPeriod
 239   (JNIEnv * env, jobject obj)
 240 {
 241     (void)env;
 242     (void)obj;
 243 
 244     return 0; // There are no restrictions on period in g_threads
 245 }
 246 
 247 /*
 248  * Class:     com_sun_glass_ui_gtk_GtkApplication
 249  * Method:    staticTimer_getMaxPeriod
 250  * Signature: ()I
 251  */
 252 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticTimer_1getMaxPeriod
 253   (JNIEnv * env, jobject obj)
 254 {
 255     (void)env;
 256     (void)obj;
 257 
 258     return 10000; // There are no restrictions on period in g_threads
 259 }
 260 
 261 /*
 262  * Class:     com_sun_glass_ui_gtk_GtkApplication
 263  * Method:    staticView_getMultiClickTime
 264  * Signature: ()J
 265  */
 266 JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickTime
 267   (JNIEnv * env, jobject obj)
 268 {
 269     (void)env;
 270     (void)obj;
 271 
 272     static gint multi_click_time = -1;
 273     if (multi_click_time == -1) {
 274         g_object_get(gtk_settings_get_default(), "gtk-double-click-time", &multi_click_time, NULL);
 275     }
 276     return (jlong)multi_click_time;
 277 }
 278 
 279 /*
 280  * Class:     com_sun_glass_ui_gtk_GtkApplication
 281  * Method:    staticView_getMultiClickMaxX
 282  * Signature: ()I
 283  */
 284 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxX
 285   (JNIEnv * env, jobject obj)
 286 {
 287     (void)env;
 288     (void)obj;
 289 
 290     static gint multi_click_dist = -1;
 291 
 292     if (multi_click_dist == -1) {
 293         g_object_get(gtk_settings_get_default(), "gtk-double-click-distance", &multi_click_dist, NULL);
 294     }
 295     return multi_click_dist;
 296 }
 297 
 298 /*
 299  * Class:     com_sun_glass_ui_gtk_GtkApplication
 300  * Method:    staticView_getMultiClickMaxY
 301  * Signature: ()I
 302  */
 303 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxY
 304   (JNIEnv * env, jobject obj)
 305 {
 306     return Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxX(env, obj);
 307 }
 308 
 309 /*
 310  * Class:     com_sun_glass_ui_gtk_GtkApplication
 311  * Method:    _supportsTransparentWindows
 312  * Signature: ()Z
 313  */
 314 JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1supportsTransparentWindows
 315   (JNIEnv * env, jobject obj) {
 316     (void)env;
 317     (void)obj;
 318 
 319     return gdk_display_supports_composite(gdk_display_get_default())
 320             && gdk_screen_is_composited(gdk_screen_get_default());
 321 }
 322 
 323 } // extern "C"
 324 
 325 bool is_window_enabled_for_event(GdkWindow * window, WindowContext *ctx, gint event_type) {
 326 
 327 
 328     if (gdk_window_is_destroyed(window)) {
 329         return FALSE;
 330     }
 331 
 332     /*
 333      * GDK_DELETE can be blocked for disabled window e.q. parent window
 334      * which prevents from closing it
 335      */
 336     switch (event_type) {
 337         case GDK_CONFIGURE:
 338         case GDK_DESTROY:
 339         case GDK_EXPOSE:
 340         case GDK_DAMAGE:
 341         case GDK_WINDOW_STATE:
 342         case GDK_FOCUS_CHANGE:
 343             return TRUE;
 344             break;
 345     }//switch
 346 
 347     if (ctx != NULL ) {
 348         return ctx->isEnabled();
 349     }
 350     return TRUE;
 351 }
 352 
 353 static void process_events(GdkEvent* event, gpointer data)
 354 {
 355     GdkWindow* window = event->any.window;
 356     WindowContext *ctx = window != NULL ? (WindowContext*)
 357         g_object_get_data(G_OBJECT(window), GDK_WINDOW_DATA_CONTEXT) : NULL;
 358 
 359     if ((window != NULL)
 360             && !is_window_enabled_for_event(window, ctx, event->type)) {
 361         return;
 362     }
 363 
 364     if (ctx != NULL && ctx->hasIME() && ctx->filterIME(event)) {
 365         return;
 366     }
 367 
 368     glass_evloop_call_hooks(event);
 369 
 370     if (ctx != NULL && dynamic_cast<WindowContextPlug*>(ctx) && ctx->get_gtk_window()) {
 371         WindowContextPlug* ctx_plug = dynamic_cast<WindowContextPlug*>(ctx);
 372         if (!ctx_plug->embedded_children.empty()) {
 373             // forward to child
 374             ctx = (WindowContext*) ctx_plug->embedded_children.back();
 375             window = ctx->get_gdk_window();
 376         }
 377     }
 378 
 379     if (is_in_drag()) {
 380         process_dnd_source(window, event);
 381     }
 382 
 383     if (ctx != NULL) {
 384         EventsCounterHelper helper(ctx);
 385         try {
 386             switch (event->type) {
 387                 case GDK_PROPERTY_NOTIFY:
 388                     ctx->process_property_notify(&event->property);
 389                     gtk_main_do_event(event);
 390                     break;
 391                 case GDK_CONFIGURE:
 392                     ctx->process_configure(&event->configure);
 393                     gtk_main_do_event(event);
 394                     break;
 395                 case GDK_FOCUS_CHANGE:
 396                     ctx->process_focus(&event->focus_change);
 397                     gtk_main_do_event(event);
 398                     break;
 399                 case GDK_DESTROY:
 400                     destroy_and_delete_ctx(ctx);
 401                     gtk_main_do_event(event);
 402                     break;
 403                 case GDK_DELETE:
 404                     ctx->process_delete();
 405                     break;
 406                 case GDK_EXPOSE:
 407                 case GDK_DAMAGE:
 408                     ctx->process_expose(&event->expose);
 409                     break;
 410                 case GDK_WINDOW_STATE:
 411                     ctx->process_state(&event->window_state);
 412                     gtk_main_do_event(event);
 413                     break;
 414                 case GDK_BUTTON_PRESS:
 415                 case GDK_BUTTON_RELEASE:
 416                     ctx->process_mouse_button(&event->button);
 417                     break;
 418                 case GDK_MOTION_NOTIFY:
 419                     ctx->process_mouse_motion(&event->motion);
 420                     gdk_event_request_motions(&event->motion);
 421                     break;
 422                 case GDK_SCROLL:
 423                     ctx->process_mouse_scroll(&event->scroll);
 424                     break;
 425                 case GDK_ENTER_NOTIFY:
 426                 case GDK_LEAVE_NOTIFY:
 427                     ctx->process_mouse_cross(&event->crossing);
 428                     break;
 429                 case GDK_KEY_PRESS:
 430                 case GDK_KEY_RELEASE:
 431                     ctx->process_key(&event->key);
 432                     break;
 433                 case GDK_DROP_START:
 434                 case GDK_DRAG_ENTER:
 435                 case GDK_DRAG_LEAVE:
 436                 case GDK_DRAG_MOTION:
 437                     process_dnd_target(ctx, &event->dnd);
 438                     break;
 439                 case GDK_MAP:
 440                     ctx->process_map();
 441                     // fall-through
 442                 case GDK_UNMAP:
 443                 case GDK_CLIENT_EVENT:
 444                 case GDK_VISIBILITY_NOTIFY:
 445                 case GDK_SETTING:
 446                 case GDK_OWNER_CHANGE:
 447                     gtk_main_do_event(event);
 448                     break;
 449                 default:
 450                     break;
 451             }
 452         } catch (jni_exception&) {
 453         }
 454     } else {
 455 
 456         if (window == gdk_screen_get_root_window(gdk_screen_get_default())) {
 457             if (event->any.type == GDK_PROPERTY_NOTIFY) {
 458                 if (event->property.atom == gdk_atom_intern_static_string("_NET_WORKAREA")
 459                         || event->property.atom == gdk_atom_intern_static_string("_NET_CURRENT_DESKTOP")) {
 460                     screen_settings_changed(gdk_screen_get_default(), NULL);
 461                 }
 462             }
 463         }
 464 
 465         //process only for non-FX windows
 466         if (process_events_prev != NULL) {
 467             (*process_events_prev)(event, data);
 468         } else {
 469             gtk_main_do_event(event);
 470         }
 471     }
 472 }