1 /* 2 * Copyright (c) 2011, 2016, 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_gtk_GtkApplication.h> 35 #include <com_sun_glass_events_WindowEvent.h> 36 #include <com_sun_glass_events_MouseEvent.h> 37 #include <com_sun_glass_events_ViewEvent.h> 38 #include <com_sun_glass_events_KeyEvent.h> 39 #include <jni.h> 40 41 #include "glass_general.h" 42 #include "glass_evloop.h" 43 #include "glass_dnd.h" 44 #include "glass_window.h" 45 #include "glass_screen.h" 46 47 GdkEventFunc process_events_prev; 48 static void process_events(GdkEvent*, gpointer); 49 50 JNIEnv* mainEnv; // Use only with main loop thread!!! 51 52 extern gboolean disableGrab; 53 54 static gboolean call_runnable (gpointer data) 55 { 56 RunnableContext* context = reinterpret_cast<RunnableContext*>(data); 57 58 JNIEnv *env; 59 int envStatus = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6); 60 if (envStatus == JNI_EDETACHED) { 61 javaVM->AttachCurrentThread((void **)&env, NULL); 62 } 63 64 env->CallVoidMethod(context->runnable, jRunnableRun, NULL); 65 LOG_EXCEPTION(env); 66 env->DeleteGlobalRef(context->runnable); 67 free(context); 68 69 if (envStatus == JNI_EDETACHED) { 70 javaVM->DetachCurrentThread(); 71 } 72 73 return FALSE; 74 } 75 76 extern "C" { 77 78 #pragma GCC diagnostic push 79 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 80 static void init_threads() { 81 gboolean is_g_thread_get_initialized = FALSE; 82 if (glib_check_version(2, 32, 0)) { // < 2.32 83 if (!glib_check_version(2, 20, 0)) { 84 is_g_thread_get_initialized = g_thread_get_initialized(); 85 } 86 if (!is_g_thread_get_initialized) { 87 g_thread_init(NULL); 88 } 89 } 90 gdk_threads_init(); 91 } 92 #pragma GCC diagnostic pop 93 94 jboolean gtk_verbose = JNI_FALSE; 95 96 /* 97 * Class: com_sun_glass_ui_gtk_GtkApplication 98 * Method: _initGTK 99 * Signature: (IZ)I 100 */ 101 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1initGTK 102 (JNIEnv *env, jclass clazz, jint version, jboolean verbose, jfloat uiScale) 103 { 104 (void) clazz; 105 (void) version; 106 107 OverrideUIScale = uiScale; 108 gtk_verbose = verbose; 109 110 env->ExceptionClear(); 111 init_threads(); 112 113 gdk_threads_enter(); 114 gtk_init(NULL, NULL); 115 116 return JNI_TRUE; 117 } 118 119 /* 120 * Class: com_sun_glass_ui_gtk_GtkApplication 121 * Method: _queryLibrary 122 * Signature: Signature: (IZ)I 123 */ 124 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1queryLibrary 125 (JNIEnv *env, jclass clazz, jint suggestedVersion, jboolean verbose) 126 { 127 // If we are being called, then the launcher is 128 // not in use, and we are in the proper glass library already. 129 // This can be done by renaming the gtk versioned native 130 // libraries to be libglass.so 131 // Note: we will make no effort to complain if the suggestedVersion 132 // is out of phase. 133 134 (void)env; 135 (void)clazz; 136 (void)suggestedVersion; 137 (void)verbose; 138 139 Display *display = XOpenDisplay(NULL); 140 if (display == NULL) { 141 return com_sun_glass_ui_gtk_GtkApplication_QUERY_NO_DISPLAY; 142 } 143 XCloseDisplay(display); 144 145 return com_sun_glass_ui_gtk_GtkApplication_QUERY_USE_CURRENT; 146 } 147 148 /* 149 * Class: com_sun_glass_ui_gtk_GtkApplication 150 * Method: _init 151 * Signature: ()V 152 */ 153 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1init 154 (JNIEnv * env, jobject obj, jlong handler, jboolean _disableGrab) 155 { 156 (void)obj; 157 158 mainEnv = env; 159 process_events_prev = (GdkEventFunc) handler; 160 disableGrab = (gboolean) _disableGrab; 161 162 glass_gdk_x11_display_set_window_scale(gdk_display_get_default(), 1); 163 gdk_event_handler_set(process_events, NULL, NULL); 164 165 GdkScreen *default_gdk_screen = gdk_screen_get_default(); 166 if (default_gdk_screen != NULL) { 167 g_signal_connect(G_OBJECT(default_gdk_screen), "monitors-changed", 168 G_CALLBACK(screen_settings_changed), NULL); 169 g_signal_connect(G_OBJECT(default_gdk_screen), "size-changed", 170 G_CALLBACK(screen_settings_changed), NULL); 171 } 172 173 GdkWindow *root = gdk_screen_get_root_window(default_gdk_screen); 174 gdk_window_set_events(root, static_cast<GdkEventMask>(gdk_window_get_events(root) | GDK_PROPERTY_CHANGE_MASK)); 175 } 176 177 /* 178 * Class: com_sun_glass_ui_gtk_GtkApplication 179 * Method: _runLoop 180 * Signature: (Ljava/lang/Runnable;Z)V 181 */ 182 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1runLoop 183 (JNIEnv * env, jobject obj, jobject launchable, jboolean noErrorTrap) 184 { 185 (void)obj; 186 (void)noErrorTrap; 187 188 env->CallVoidMethod(launchable, jRunnableRun); 189 CHECK_JNI_EXCEPTION(env); 190 191 // GTK installs its own X error handler that conflicts with AWT. 192 // During drag and drop, AWT hides errors so we need to hide them 193 // to avoid exit()'ing. It's not clear that we don't want to hide 194 // X error all the time, otherwise FX will exit(). 195 // 196 // A better solution would be to coordinate with AWT and save and 197 // restore the X handler. 198 199 // Disable X error handling 200 #ifndef VERBOSE 201 if (!noErrorTrap) { 202 gdk_error_trap_push(); 203 } 204 #endif 205 206 gtk_main(); 207 208 // When the last JFrame closes and DISPOSE_ON_CLOSE is specified, 209 // Java exits with an X error. X error are hidden during the FX 210 // event loop and should be restored when the event loop exits. Unfortunately, 211 // this is too early. The fix is to never restore X errors. 212 // 213 // See RT-21408 & RT-20756 214 215 // Restore X error handling 216 // #ifndef VERBOSE 217 // if (!noErrorTrap) { 218 // gdk_error_trap_pop(); 219 // } 220 // #endif 221 222 gdk_threads_leave(); 223 224 } 225 226 /* 227 * Class: com_sun_glass_ui_gtk_GtkApplication 228 * Method: _terminateLoop 229 * Signature: ()V 230 */ 231 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1terminateLoop 232 (JNIEnv * env, jobject obj) 233 { 234 (void)env; 235 (void)obj; 236 237 gtk_main_quit(); 238 } 239 240 /* 241 * Class: com_sun_glass_ui_gtk_GtkApplication 242 * Method: _submitForLaterInvocation 243 * Signature: (Ljava/lang/Runnable;)V 244 */ 245 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1submitForLaterInvocation 246 (JNIEnv * env, jobject obj, jobject runnable) 247 { 248 (void)obj; 249 250 RunnableContext* context = (RunnableContext*)malloc(sizeof(RunnableContext)); 251 context->runnable = env->NewGlobalRef(runnable); 252 gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE + 30, call_runnable, context, NULL); 253 } 254 255 /* 256 * Class: com_sun_glass_ui_gtk_GtkApplication 257 * Method: enterNestedEventLoopImpl 258 * Signature: ()V 259 */ 260 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_enterNestedEventLoopImpl 261 (JNIEnv * env, jobject obj) 262 { 263 (void)env; 264 (void)obj; 265 266 gtk_main(); 267 } 268 269 /* 270 * Class: com_sun_glass_ui_gtk_GtkApplication 271 * Method: leaveNestedEventLoopImpl 272 * Signature: ()V 273 */ 274 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_leaveNestedEventLoopImpl 275 (JNIEnv * env, jobject obj) 276 { 277 (void)env; 278 (void)obj; 279 280 gtk_main_quit(); 281 } 282 283 /* 284 * Class: com_sun_glass_ui_gtk_GtkApplication 285 * Method: staticScreen_getScreens 286 * Signature: ()[Lcom/sun/glass/ui/Screen; 287 */ 288 JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticScreen_1getScreens 289 (JNIEnv * env, jobject obj) 290 { 291 (void)obj; 292 293 try { 294 return rebuild_screens(env); 295 } catch (jni_exception&) { 296 return NULL; 297 } 298 } 299 300 /* 301 * Class: com_sun_glass_ui_gtk_GtkApplication 302 * Method: staticTimer_getMinPeriod 303 * Signature: ()I 304 */ 305 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticTimer_1getMinPeriod 306 (JNIEnv * env, jobject obj) 307 { 308 (void)env; 309 (void)obj; 310 311 return 0; // There are no restrictions on period in g_threads 312 } 313 314 /* 315 * Class: com_sun_glass_ui_gtk_GtkApplication 316 * Method: staticTimer_getMaxPeriod 317 * Signature: ()I 318 */ 319 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticTimer_1getMaxPeriod 320 (JNIEnv * env, jobject obj) 321 { 322 (void)env; 323 (void)obj; 324 325 return 10000; // There are no restrictions on period in g_threads 326 } 327 328 /* 329 * Class: com_sun_glass_ui_gtk_GtkApplication 330 * Method: staticView_getMultiClickTime 331 * Signature: ()J 332 */ 333 JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickTime 334 (JNIEnv * env, jobject obj) 335 { 336 (void)env; 337 (void)obj; 338 339 static gint multi_click_time = -1; 340 if (multi_click_time == -1) { 341 g_object_get(gtk_settings_get_default(), "gtk-double-click-time", &multi_click_time, NULL); 342 } 343 return (jlong)multi_click_time; 344 } 345 346 /* 347 * Class: com_sun_glass_ui_gtk_GtkApplication 348 * Method: staticView_getMultiClickMaxX 349 * Signature: ()I 350 */ 351 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxX 352 (JNIEnv * env, jobject obj) 353 { 354 (void)env; 355 (void)obj; 356 357 static gint multi_click_dist = -1; 358 359 if (multi_click_dist == -1) { 360 g_object_get(gtk_settings_get_default(), "gtk-double-click-distance", &multi_click_dist, NULL); 361 } 362 return multi_click_dist; 363 } 364 365 /* 366 * Class: com_sun_glass_ui_gtk_GtkApplication 367 * Method: staticView_getMultiClickMaxY 368 * Signature: ()I 369 */ 370 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxY 371 (JNIEnv * env, jobject obj) 372 { 373 return Java_com_sun_glass_ui_gtk_GtkApplication_staticView_1getMultiClickMaxX(env, obj); 374 } 375 376 /* 377 * Class: com_sun_glass_ui_gtk_GtkApplication 378 * Method: _supportsTransparentWindows 379 * Signature: ()Z 380 */ 381 JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1supportsTransparentWindows 382 (JNIEnv * env, jobject obj) { 383 (void)env; 384 (void)obj; 385 386 return gdk_display_supports_composite(gdk_display_get_default()) 387 && gdk_screen_is_composited(gdk_screen_get_default()); 388 } 389 390 } // extern "C" 391 392 bool is_window_enabled_for_event(GdkWindow * window, WindowContext *ctx, gint event_type) { 393 394 395 if (gdk_window_is_destroyed(window)) { 396 return FALSE; 397 } 398 399 /* 400 * GDK_DELETE can be blocked for disabled window e.q. parent window 401 * which prevents from closing it 402 */ 403 switch (event_type) { 404 case GDK_CONFIGURE: 405 case GDK_DESTROY: 406 case GDK_EXPOSE: 407 case GDK_DAMAGE: 408 case GDK_WINDOW_STATE: 409 case GDK_FOCUS_CHANGE: 410 return TRUE; 411 break; 412 }//switch 413 414 if (ctx != NULL ) { 415 return ctx->isEnabled(); 416 } 417 return TRUE; 418 } 419 420 static void process_events(GdkEvent* event, gpointer data) 421 { 422 GdkWindow* window = event->any.window; 423 WindowContext *ctx = window != NULL ? (WindowContext*) 424 g_object_get_data(G_OBJECT(window), GDK_WINDOW_DATA_CONTEXT) : NULL; 425 426 if ((window != NULL) 427 && !is_window_enabled_for_event(window, ctx, event->type)) { 428 return; 429 } 430 431 if (ctx != NULL && ctx->hasIME() && ctx->filterIME(event)) { 432 return; 433 } 434 435 glass_evloop_call_hooks(event); 436 437 if (ctx != NULL && dynamic_cast<WindowContextPlug*>(ctx) && ctx->get_gtk_window()) { 438 WindowContextPlug* ctx_plug = dynamic_cast<WindowContextPlug*>(ctx); 439 if (!ctx_plug->embedded_children.empty()) { 440 // forward to child 441 ctx = (WindowContext*) ctx_plug->embedded_children.back(); 442 window = ctx->get_gdk_window(); 443 } 444 } 445 446 if (is_in_drag()) { 447 process_dnd_source(window, event); 448 } 449 450 if (ctx != NULL) { 451 EventsCounterHelper helper(ctx); 452 try { 453 switch (event->type) { 454 case GDK_PROPERTY_NOTIFY: 455 ctx->process_property_notify(&event->property); 456 gtk_main_do_event(event); 457 break; 458 case GDK_CONFIGURE: 459 ctx->process_configure(&event->configure); 460 gtk_main_do_event(event); 461 break; 462 case GDK_FOCUS_CHANGE: 463 ctx->process_focus(&event->focus_change); 464 gtk_main_do_event(event); 465 break; 466 case GDK_DESTROY: 467 destroy_and_delete_ctx(ctx); 468 gtk_main_do_event(event); 469 break; 470 case GDK_DELETE: 471 ctx->process_delete(); 472 break; 473 case GDK_EXPOSE: 474 case GDK_DAMAGE: 475 ctx->process_expose(&event->expose); 476 break; 477 case GDK_WINDOW_STATE: 478 ctx->process_state(&event->window_state); 479 gtk_main_do_event(event); 480 break; 481 case GDK_BUTTON_PRESS: 482 case GDK_BUTTON_RELEASE: 483 ctx->process_mouse_button(&event->button); 484 break; 485 case GDK_MOTION_NOTIFY: 486 ctx->process_mouse_motion(&event->motion); 487 gdk_event_request_motions(&event->motion); 488 break; 489 case GDK_SCROLL: 490 ctx->process_mouse_scroll(&event->scroll); 491 break; 492 case GDK_ENTER_NOTIFY: 493 case GDK_LEAVE_NOTIFY: 494 ctx->process_mouse_cross(&event->crossing); 495 break; 496 case GDK_KEY_PRESS: 497 case GDK_KEY_RELEASE: 498 ctx->process_key(&event->key); 499 break; 500 case GDK_DROP_START: 501 case GDK_DRAG_ENTER: 502 case GDK_DRAG_LEAVE: 503 case GDK_DRAG_MOTION: 504 process_dnd_target(ctx, &event->dnd); 505 break; 506 case GDK_MAP: 507 ctx->process_map(); 508 // fall-through 509 case GDK_UNMAP: 510 case GDK_CLIENT_EVENT: 511 case GDK_VISIBILITY_NOTIFY: 512 case GDK_SETTING: 513 case GDK_OWNER_CHANGE: 514 gtk_main_do_event(event); 515 break; 516 default: 517 break; 518 } 519 } catch (jni_exception&) { 520 } 521 } else { 522 523 if (window == gdk_screen_get_root_window(gdk_screen_get_default())) { 524 if (event->any.type == GDK_PROPERTY_NOTIFY) { 525 if (event->property.atom == gdk_atom_intern_static_string("_NET_WORKAREA") 526 || event->property.atom == gdk_atom_intern_static_string("_NET_CURRENT_DESKTOP")) { 527 screen_settings_changed(gdk_screen_get_default(), NULL); 528 } 529 } 530 } 531 532 //process only for non-FX windows 533 if (process_events_prev != NULL) { 534 (*process_events_prev)(event, data); 535 } else { 536 gtk_main_do_event(event); 537 } 538 } 539 }