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