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