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