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 }