1 /* 2 * Copyright (c) 2011, 2014, 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 "glass_window.h" 26 #include "glass_general.h" 27 #include "glass_gtkcompat.h" 28 #include "glass_key.h" 29 #include "glass_screen.h" 30 #include "glass_dnd.h" 31 32 #include <com_sun_glass_events_WindowEvent.h> 33 #include <com_sun_glass_events_ViewEvent.h> 34 #include <com_sun_glass_events_MouseEvent.h> 35 #include <com_sun_glass_events_KeyEvent.h> 36 37 #include <com_sun_glass_ui_Window_Level.h> 38 39 #include <X11/extensions/shape.h> 40 #include <cairo.h> 41 #include <cairo-xlib.h> 42 #include <gdk/gdkx.h> 43 #include <gdk/gdk.h> 44 45 #include <string.h> 46 47 #include <iostream> 48 #include <algorithm> 49 50 WindowContext * WindowContextBase::sm_grab_window = NULL; 51 WindowContext * WindowContextBase::sm_mouse_drag_window = NULL; 52 53 GdkAtom atom_net_wm_state = gdk_atom_intern_static_string("_NET_WM_STATE"); 54 55 GdkWindow* WindowContextBase::get_gdk_window(){ 56 return gdk_window; 57 } 58 59 jobject WindowContextBase::get_jview() { 60 return jview; 61 } 62 63 jobject WindowContextBase::get_jwindow() { 64 return jwindow; 65 } 66 67 bool WindowContextBase::isEnabled() { 68 if (jwindow) { 69 bool result = (JNI_TRUE == mainEnv->CallBooleanMethod(jwindow, jWindowIsEnabled)); 70 LOG_EXCEPTION(mainEnv) 71 return result; 72 } else { 73 return false; 74 } 75 } 76 77 void WindowContextBase::notify_state(jint glass_state) { 78 if (glass_state == com_sun_glass_events_WindowEvent_RESTORE) { 79 if (is_maximized) { 80 glass_state = com_sun_glass_events_WindowEvent_MAXIMIZE; 81 } 82 83 int w, h; 84 glass_gdk_window_get_size(gdk_window, &w, &h); 85 if (jview) { 86 mainEnv->CallVoidMethod(jview, 87 jViewNotifyRepaint, 88 0, 0, w, h); 89 CHECK_JNI_EXCEPTION(mainEnv); 90 } 91 } 92 93 if (jwindow) { 94 mainEnv->CallVoidMethod(jwindow, 95 jGtkWindowNotifyStateChanged, 96 glass_state); 97 CHECK_JNI_EXCEPTION(mainEnv); 98 } 99 } 100 101 void WindowContextBase::process_state(GdkEventWindowState* event) { 102 if (event->changed_mask & 103 (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_MAXIMIZED)) { 104 105 if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) { 106 is_iconified = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED; 107 } 108 if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { 109 is_maximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; 110 } 111 112 jint stateChangeEvent; 113 114 if (is_iconified) { 115 stateChangeEvent = com_sun_glass_events_WindowEvent_MINIMIZE; 116 } else if (is_maximized) { 117 stateChangeEvent = com_sun_glass_events_WindowEvent_MAXIMIZE; 118 } else { 119 stateChangeEvent = com_sun_glass_events_WindowEvent_RESTORE; 120 } 121 122 notify_state(stateChangeEvent); 123 } else if (event->changed_mask & GDK_WINDOW_STATE_ABOVE) { 124 notify_on_top( event->new_window_state & GDK_WINDOW_STATE_ABOVE); 125 } 126 } 127 128 void WindowContextBase::process_focus(GdkEventFocus* event) { 129 if (!event->in && WindowContextBase::sm_mouse_drag_window == this) { 130 ungrab_mouse_drag_focus(); 131 } 132 if (!event->in && WindowContextBase::sm_grab_window == this) { 133 ungrab_focus(); 134 } 135 136 if (xim.enabled && xim.ic) { 137 if (event->in) { 138 XSetICFocus(xim.ic); 139 } else { 140 XUnsetICFocus(xim.ic); 141 } 142 } 143 144 if (jwindow) { 145 if (!event->in || isEnabled()) { 146 mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocus, 147 event->in ? com_sun_glass_events_WindowEvent_FOCUS_GAINED : com_sun_glass_events_WindowEvent_FOCUS_LOST); 148 CHECK_JNI_EXCEPTION(mainEnv) 149 } else { 150 mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocusDisabled); 151 CHECK_JNI_EXCEPTION(mainEnv) 152 } 153 } 154 } 155 156 void WindowContextBase::increment_events_counter() { 157 ++events_processing_cnt; 158 } 159 160 void WindowContextBase::decrement_events_counter() { 161 --events_processing_cnt; 162 } 163 164 size_t WindowContextBase::get_events_count() { 165 return events_processing_cnt; 166 } 167 168 bool WindowContextBase::is_dead() { 169 return can_be_deleted; 170 } 171 172 void destroy_and_delete_ctx(WindowContext* ctx) { 173 if (ctx) { 174 ctx->process_destroy(); 175 176 if (!ctx->get_events_count()) { 177 delete ctx; 178 } 179 // else: ctx will be deleted in EventsCounterHelper after completing 180 // an event processing 181 } 182 } 183 184 void WindowContextBase::process_destroy() { 185 if (WindowContextBase::sm_mouse_drag_window == this) { 186 ungrab_mouse_drag_focus(); 187 } 188 189 if (WindowContextBase::sm_grab_window == this) { 190 ungrab_focus(); 191 } 192 193 std::set<WindowContextTop*>::iterator it; 194 for (it = children.begin(); it != children.end(); ++it) { 195 (*it)->set_owner(NULL); 196 destroy_and_delete_ctx(*it); 197 } 198 children.clear(); 199 200 if (jwindow) { 201 mainEnv->CallVoidMethod(jwindow, jWindowNotifyDestroy); 202 EXCEPTION_OCCURED(mainEnv); 203 } 204 205 if (jview) { 206 mainEnv->DeleteGlobalRef(jview); 207 jview = NULL; 208 } 209 210 if (jwindow) { 211 mainEnv->DeleteGlobalRef(jwindow); 212 jwindow = NULL; 213 } 214 215 can_be_deleted = true; 216 } 217 218 void WindowContextBase::process_delete() { 219 if (jwindow && isEnabled()) { 220 mainEnv->CallVoidMethod(jwindow, jWindowNotifyClose); 221 CHECK_JNI_EXCEPTION(mainEnv) 222 } 223 } 224 225 void WindowContextBase::process_expose(GdkEventExpose* event) { 226 if (jview) { 227 mainEnv->CallVoidMethod(jview, jViewNotifyRepaint, event->area.x, event->area.y, event->area.width, event->area.height); 228 CHECK_JNI_EXCEPTION(mainEnv) 229 } 230 } 231 232 static inline jint gtk_button_number_to_mouse_button(guint button) { 233 switch (button) { 234 case 1: 235 return com_sun_glass_events_MouseEvent_BUTTON_LEFT; 236 case 2: 237 return com_sun_glass_events_MouseEvent_BUTTON_OTHER; 238 case 3: 239 return com_sun_glass_events_MouseEvent_BUTTON_RIGHT; 240 default: 241 // Other buttons are not supported by quantum and are not reported by other platforms 242 return com_sun_glass_events_MouseEvent_BUTTON_NONE; 243 } 244 } 245 246 void WindowContextBase::process_mouse_button(GdkEventButton* event) { 247 bool press = event->type == GDK_BUTTON_PRESS; 248 guint state = event->state; 249 guint mask = 0; 250 251 // We need to add/remove current mouse button from the modifier flags 252 // as X lib state represents the state just prior to the event and 253 // glass needs the state just after the event 254 switch (event->button) { 255 case 1: 256 mask = GDK_BUTTON1_MASK; 257 break; 258 case 2: 259 mask = GDK_BUTTON2_MASK; 260 break; 261 case 3: 262 mask = GDK_BUTTON3_MASK; 263 break; 264 } 265 266 if (press) { 267 state |= mask; 268 } else { 269 state &= ~mask; 270 } 271 272 if (press) { 273 GdkDevice* device = event->device; 274 275 if (glass_gdk_device_is_grabbed(device) 276 && (glass_gdk_device_get_window_at_position(device, NULL, NULL) 277 == NULL)) { 278 ungrab_focus(); 279 return; 280 } 281 } 282 283 // Upper layers expects from us Windows behavior: 284 // all mouse events should be delivered to window where drag begins 285 // and no exit/enter event should be reported during this drag. 286 // We can grab mouse pointer for these needs. 287 if (press) { 288 grab_mouse_drag_focus(); 289 } else if ((event->state & MOUSE_BUTTONS_MASK) 290 && !(state & MOUSE_BUTTONS_MASK)) { // all buttons released 291 ungrab_mouse_drag_focus(); 292 } 293 294 jint button = gtk_button_number_to_mouse_button(event->button); 295 296 if (jview && button != com_sun_glass_events_MouseEvent_BUTTON_NONE) { 297 mainEnv->CallVoidMethod(jview, jViewNotifyMouse, 298 press ? com_sun_glass_events_MouseEvent_DOWN : com_sun_glass_events_MouseEvent_UP, 299 button, 300 (jint) event->x, (jint) event->y, 301 (jint) event->x_root, (jint) event->y_root, 302 gdk_modifier_mask_to_glass(state), 303 (event->button == 3 && press) ? JNI_TRUE : JNI_FALSE, 304 JNI_FALSE); 305 CHECK_JNI_EXCEPTION(mainEnv) 306 307 if (jview && event->button == 3 && press) { 308 mainEnv->CallVoidMethod(jview, jViewNotifyMenu, 309 (jint)event->x, (jint)event->y, 310 (jint)event->x_root, (jint)event->y_root, 311 JNI_FALSE); 312 CHECK_JNI_EXCEPTION(mainEnv) 313 } 314 } 315 } 316 317 void WindowContextBase::process_mouse_motion(GdkEventMotion* event) { 318 jint glass_modifier = gdk_modifier_mask_to_glass(event->state); 319 jint isDrag = glass_modifier & ( 320 com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY | 321 com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE | 322 com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY); 323 jint button = com_sun_glass_events_MouseEvent_BUTTON_NONE; 324 325 if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY) { 326 button = com_sun_glass_events_MouseEvent_BUTTON_LEFT; 327 } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE) { 328 button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; 329 } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY) { 330 button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT; 331 } 332 333 if (jview) { 334 mainEnv->CallVoidMethod(jview, jViewNotifyMouse, 335 isDrag ? com_sun_glass_events_MouseEvent_DRAG : com_sun_glass_events_MouseEvent_MOVE, 336 button, 337 (jint) event->x, (jint) event->y, 338 (jint) event->x_root, (jint) event->y_root, 339 glass_modifier, 340 JNI_FALSE, 341 JNI_FALSE); 342 CHECK_JNI_EXCEPTION(mainEnv) 343 } 344 } 345 346 void WindowContextBase::process_mouse_scroll(GdkEventScroll* event) { 347 jdouble dx = 0; 348 jdouble dy = 0; 349 350 // converting direction to change in pixels 351 switch (event->direction) { 352 case GDK_SCROLL_UP: 353 dy = 1; 354 break; 355 case GDK_SCROLL_DOWN: 356 dy = -1; 357 break; 358 case GDK_SCROLL_LEFT: 359 dx = 1; 360 break; 361 case GDK_SCROLL_RIGHT: 362 dx = -1; 363 break; 364 } 365 366 if (jview) { 367 mainEnv->CallVoidMethod(jview, jViewNotifyScroll, 368 (jint) event->x, (jint) event->y, 369 (jint) event->x_root, (jint) event->y_root, 370 dx, dy, 371 gdk_modifier_mask_to_glass(event->state), 372 (jint) 0, (jint) 0, 373 (jint) 0, (jint) 0, 374 (jdouble) 40.0, (jdouble) 40.0); 375 CHECK_JNI_EXCEPTION(mainEnv) 376 } 377 378 } 379 380 void WindowContextBase::process_mouse_cross(GdkEventCrossing* event) { 381 bool enter = event->type == GDK_ENTER_NOTIFY; 382 if (jview) { 383 guint state = event->state; 384 if (enter) { // workaround for RT-21590 385 state &= ~MOUSE_BUTTONS_MASK; 386 } 387 388 if (enter != is_mouse_entered) { 389 is_mouse_entered = enter; 390 mainEnv->CallVoidMethod(jview, jViewNotifyMouse, 391 enter ? com_sun_glass_events_MouseEvent_ENTER : com_sun_glass_events_MouseEvent_EXIT, 392 com_sun_glass_events_MouseEvent_BUTTON_NONE, 393 (jint) event->x, (jint) event->y, 394 (jint) event->x_root, (jint) event->y_root, 395 gdk_modifier_mask_to_glass(state), 396 JNI_FALSE, 397 JNI_FALSE); 398 CHECK_JNI_EXCEPTION(mainEnv) 399 } 400 } 401 } 402 403 void WindowContextBase::process_key(GdkEventKey* event) { 404 bool press = event->type == GDK_KEY_PRESS; 405 jint glassKey = get_glass_key(event); 406 jint glassModifier = gdk_modifier_mask_to_glass(event->state); 407 if (press) { 408 glassModifier |= glass_key_to_modifier(glassKey); 409 } else { 410 glassModifier &= ~glass_key_to_modifier(glassKey); 411 } 412 jcharArray jChars = NULL; 413 jchar key = gdk_keyval_to_unicode(event->keyval); 414 if (key >= 'a' && key <= 'z' && (event->state & GDK_CONTROL_MASK)) { 415 key = key - 'a' + 1; // map 'a' to ctrl-a, and so on. 416 } else { 417 key = glass_gtk_fixup_typed_key(key, event->keyval); 418 } 419 420 if (key > 0) { 421 jChars = mainEnv->NewCharArray(1); 422 if (jChars) { 423 mainEnv->SetCharArrayRegion(jChars, 0, 1, &key); 424 CHECK_JNI_EXCEPTION(mainEnv) 425 } 426 } else { 427 jChars = mainEnv->NewCharArray(0); 428 } 429 if (jview) { 430 if (press) { 431 mainEnv->CallVoidMethod(jview, jViewNotifyKey, 432 com_sun_glass_events_KeyEvent_PRESS, 433 glassKey, 434 jChars, 435 glassModifier); 436 CHECK_JNI_EXCEPTION(mainEnv) 437 438 if (jview && key > 0) { // TYPED events should only be sent for printable characters. 439 mainEnv->CallVoidMethod(jview, jViewNotifyKey, 440 com_sun_glass_events_KeyEvent_TYPED, 441 com_sun_glass_events_KeyEvent_VK_UNDEFINED, 442 jChars, 443 glassModifier); 444 CHECK_JNI_EXCEPTION(mainEnv) 445 } 446 } else { 447 mainEnv->CallVoidMethod(jview, jViewNotifyKey, 448 com_sun_glass_events_KeyEvent_RELEASE, 449 glassKey, 450 jChars, 451 glassModifier); 452 CHECK_JNI_EXCEPTION(mainEnv) 453 } 454 } 455 } 456 457 void WindowContextBase::paint(void* data, jint width, jint height) 458 { 459 if (!is_visible()) { 460 return; 461 } 462 463 cairo_t* context; 464 context = gdk_cairo_create(GDK_DRAWABLE(gdk_window)); 465 466 cairo_surface_t* cairo_surface; 467 cairo_surface = cairo_image_surface_create_for_data( 468 (unsigned char*)data, 469 CAIRO_FORMAT_ARGB32, 470 width, height, width * 4); 471 472 applyShapeMask(data, width, height); 473 474 cairo_set_source_surface(context, cairo_surface, 0, 0); 475 cairo_set_operator (context, CAIRO_OPERATOR_SOURCE); 476 cairo_paint(context); 477 478 cairo_destroy(context); 479 cairo_surface_destroy(cairo_surface); 480 } 481 482 void WindowContextBase::add_child(WindowContextTop* child) { 483 children.insert(child); 484 gtk_window_set_transient_for(child->get_gtk_window(), this->get_gtk_window()); 485 } 486 487 void WindowContextBase::remove_child(WindowContextTop* child) { 488 children.erase(child); 489 gtk_window_set_transient_for(child->get_gtk_window(), NULL); 490 } 491 492 void WindowContextBase::show_or_hide_children(bool show) { 493 std::set<WindowContextTop*>::iterator it; 494 for (it = children.begin(); it != children.end(); ++it) { 495 (*it)->set_visible(show); 496 (*it)->show_or_hide_children(show); 497 } 498 } 499 500 void WindowContextBase::reparent_children(WindowContext* parent) { 501 std::set<WindowContextTop*>::iterator it; 502 for (it = children.begin(); it != children.end(); ++it) { 503 (*it)->set_owner(parent); 504 parent->add_child(*it); 505 } 506 children.clear(); 507 } 508 509 void WindowContextBase::set_visible(bool visible) { 510 if (visible) { 511 gtk_widget_show_all(gtk_widget); 512 } else { 513 gtk_widget_hide(gtk_widget); 514 if (jview && is_mouse_entered) { 515 is_mouse_entered = false; 516 mainEnv->CallVoidMethod(jview, jViewNotifyMouse, 517 com_sun_glass_events_MouseEvent_EXIT, 518 com_sun_glass_events_MouseEvent_BUTTON_NONE, 519 0, 0, 520 0, 0, 521 0, 522 JNI_FALSE, 523 JNI_FALSE); 524 CHECK_JNI_EXCEPTION(mainEnv) 525 } 526 } 527 } 528 529 bool WindowContextBase::is_visible() { 530 return gtk_widget_get_visible(gtk_widget); 531 } 532 533 bool WindowContextBase::set_view(jobject view) { 534 535 if (jview) { 536 mainEnv->DeleteGlobalRef(jview); 537 } 538 539 if (view) { 540 gint width, height; 541 jview = mainEnv->NewGlobalRef(view); 542 gtk_window_get_size(GTK_WINDOW(gtk_widget), &width, &height); 543 mainEnv->CallVoidMethod(view, jViewNotifyResize, width, height); 544 CHECK_JNI_EXCEPTION_RET(mainEnv, FALSE) 545 } else { 546 jview = NULL; 547 } 548 return TRUE; 549 } 550 551 bool WindowContextBase::grab_mouse_drag_focus() { 552 if (glass_gdk_mouse_devices_grab_with_cursor( 553 gdk_window, gdk_window_get_cursor(gdk_window), FALSE)) { 554 WindowContextBase::sm_mouse_drag_window = this; 555 return true; 556 } else { 557 return false; 558 } 559 } 560 561 void WindowContextBase::ungrab_mouse_drag_focus() { 562 WindowContextBase::sm_mouse_drag_window = NULL; 563 glass_gdk_mouse_devices_ungrab(); 564 if (WindowContextBase::sm_grab_window) { 565 WindowContextBase::sm_grab_window->grab_focus(); 566 } 567 } 568 569 bool WindowContextBase::grab_focus() { 570 if (WindowContextBase::sm_mouse_drag_window 571 || glass_gdk_mouse_devices_grab(gdk_window)) { 572 WindowContextBase::sm_grab_window = this; 573 return true; 574 } else { 575 return false; 576 } 577 } 578 579 void WindowContextBase::ungrab_focus() { 580 if (!WindowContextBase::sm_mouse_drag_window) { 581 glass_gdk_mouse_devices_ungrab(); 582 } 583 WindowContextBase::sm_grab_window = NULL; 584 585 if (jwindow) { 586 mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocusUngrab); 587 CHECK_JNI_EXCEPTION(mainEnv) 588 } 589 } 590 591 void WindowContextBase::set_cursor(GdkCursor* cursor) { 592 if (!is_in_drag()) { 593 if (WindowContextBase::sm_mouse_drag_window) { 594 glass_gdk_mouse_devices_grab_with_cursor( 595 WindowContextBase::sm_mouse_drag_window->get_gdk_window(), cursor, FALSE); 596 } else if (WindowContextBase::sm_grab_window) { 597 glass_gdk_mouse_devices_grab_with_cursor( 598 WindowContextBase::sm_grab_window->get_gdk_window(), cursor); 599 } 600 } 601 gdk_window_set_cursor(gdk_window, cursor); 602 } 603 604 void WindowContextBase::set_background(float r, float g, float b) { 605 GdkColor color; 606 color.red = (guint16) (r * 65535); 607 color.green = (guint16) (g * 65535); 608 color.blue = (guint16) (b * 65535); 609 gtk_widget_modify_bg(gtk_widget, GTK_STATE_NORMAL, &color); 610 } 611 612 WindowContextBase::~WindowContextBase() { 613 if (xim.ic) { 614 XDestroyIC(xim.ic); 615 xim.ic = NULL; 616 } 617 if (xim.im) { 618 XCloseIM(xim.im); 619 xim.im = NULL; 620 } 621 622 gtk_widget_destroy(gtk_widget); 623 } 624 625 ////////////////////////////// WindowContextTop ///////////////////////////////// 626 627 628 WindowContextTop::WindowContextTop(jobject _jwindow, WindowContext* _owner, long _screen, 629 WindowFrameType _frame_type, WindowType type, GdkWMFunction wmf) : 630 WindowContextBase(), 631 screen(_screen), 632 frame_type(_frame_type), 633 owner(_owner), 634 geometry(), 635 stale_config_notifications(), 636 resizable(), 637 frame_extents_initialized(), 638 map_received(false), 639 location_assigned(false), 640 size_assigned(false), 641 on_top(false) 642 { 643 jwindow = mainEnv->NewGlobalRef(_jwindow); 644 645 gtk_widget = gtk_window_new(type == POPUP ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); 646 647 if (gchar* app_name = get_application_name()) { 648 gtk_window_set_wmclass(GTK_WINDOW(gtk_widget), app_name, app_name); 649 g_free(app_name); 650 } 651 652 if (owner) { 653 owner->add_child(this); 654 if (on_top_inherited()) { 655 gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE); 656 } 657 } 658 659 if (type == UTILITY) { 660 gtk_window_set_type_hint(GTK_WINDOW(gtk_widget), GDK_WINDOW_TYPE_HINT_UTILITY); 661 } 662 663 // glong xdisplay = (glong)mainEnv->GetStaticLongField(jApplicationCls, jApplicationDisplay); 664 // gint xscreenID = (gint)mainEnv->GetStaticIntField(jApplicationCls, jApplicationScreen); 665 glong xvisualID = (glong)mainEnv->GetStaticLongField(jApplicationCls, jApplicationVisualID); 666 667 if (xvisualID != 0) { 668 GdkVisual *visual = gdk_x11_screen_lookup_visual(gdk_screen_get_default(), xvisualID); 669 glass_gtk_window_configure_from_visual(gtk_widget, visual); 670 } 671 672 gtk_widget_set_size_request(gtk_widget, 0, 0); 673 gtk_widget_set_events(gtk_widget, GDK_ALL_EVENTS_MASK); 674 gtk_widget_set_app_paintable(gtk_widget, TRUE); 675 if (frame_type != TITLED) { 676 gtk_window_set_decorated(GTK_WINDOW(gtk_widget), FALSE); 677 } 678 679 glass_gtk_configure_transparency_and_realize(gtk_widget, frame_type == TRANSPARENT); 680 gtk_window_set_title(GTK_WINDOW(gtk_widget), ""); 681 682 gdk_window = gtk_widget_get_window(gtk_widget); 683 684 g_object_set_data_full(G_OBJECT(gdk_window), GDK_WINDOW_DATA_CONTEXT, this, NULL); 685 686 gdk_window_register_dnd(gdk_window); 687 688 if (wmf) { 689 gdk_window_set_functions(gdk_window, wmf); 690 } 691 692 if (frame_type == TITLED) { 693 request_frame_extents(); 694 } 695 } 696 697 // Applied to a temporary full screen window to prevent sending events to Java 698 void WindowContextTop::detach_from_java() { 699 if (jview) { 700 mainEnv->DeleteGlobalRef(jview); 701 jview = NULL; 702 } 703 if (jwindow) { 704 mainEnv->DeleteGlobalRef(jwindow); 705 jwindow = NULL; 706 } 707 } 708 709 static GdkAtom 710 get_net_frame_extents_atom() { 711 static const char * extents_str = "_NET_FRAME_EXTENTS"; 712 return gdk_atom_intern(extents_str, TRUE); 713 } 714 715 void 716 WindowContextTop::request_frame_extents() { 717 Display *display = GDK_WINDOW_XDISPLAY(gdk_window); 718 Atom rfeAtom = XInternAtom(display, "_NET_REQUEST_FRAME_EXTENTS", True); 719 if (rfeAtom != None) { 720 XClientMessageEvent clientMessage; 721 memset(&clientMessage, 0, sizeof(clientMessage)); 722 723 clientMessage.type = ClientMessage; 724 clientMessage.window = GDK_WINDOW_XID(gdk_window); 725 clientMessage.message_type = rfeAtom; 726 clientMessage.format = 32; 727 728 XSendEvent(display, XDefaultRootWindow(display), False, 729 SubstructureRedirectMask | SubstructureNotifyMask, 730 (XEvent *) &clientMessage); 731 XFlush(display); 732 } 733 } 734 735 void 736 WindowContextTop::initialize_frame_extents() { 737 int top, left, bottom, right; 738 if (get_frame_extents_property(&top, &left, &bottom, &right)) { 739 geometry.extents.top = top; 740 geometry.extents.left = left; 741 geometry.extents.bottom = bottom; 742 geometry.extents.right = right; 743 } 744 } 745 746 bool 747 WindowContextTop::get_frame_extents_property(int *top, int *left, 748 int *bottom, int *right) { 749 unsigned long *extents; 750 751 if (gdk_property_get(gdk_window, 752 get_net_frame_extents_atom(), 753 gdk_atom_intern("CARDINAL", FALSE), 754 0, 755 sizeof (unsigned long) * 4, 756 FALSE, 757 NULL, 758 NULL, 759 NULL, 760 (guchar**) & extents)) { 761 *left = extents [0]; 762 *right = extents [1]; 763 *top = extents [2]; 764 *bottom = extents [3]; 765 766 g_free(extents); 767 return true; 768 } 769 770 return false; 771 } 772 773 774 static int geometry_get_window_width(const WindowGeometry *windowGeometry) { 775 return (windowGeometry->final_width.type != BOUNDSTYPE_WINDOW) 776 ? windowGeometry->final_width.value 777 + windowGeometry->extents.left 778 + windowGeometry->extents.right 779 : windowGeometry->final_width.value; 780 } 781 782 static int geometry_get_window_height(const WindowGeometry *windowGeometry) { 783 return (windowGeometry->final_height.type != BOUNDSTYPE_WINDOW) 784 ? windowGeometry->final_height.value 785 + windowGeometry->extents.top 786 + windowGeometry->extents.bottom 787 : windowGeometry->final_height.value; 788 } 789 790 static int geometry_get_content_width(WindowGeometry *windowGeometry) { 791 return (windowGeometry->final_width.type != BOUNDSTYPE_CONTENT) 792 ? windowGeometry->final_width.value 793 - windowGeometry->extents.left 794 - windowGeometry->extents.right 795 : windowGeometry->final_width.value; 796 } 797 static int geometry_get_content_height(WindowGeometry *windowGeometry) { 798 return (windowGeometry->final_height.type != BOUNDSTYPE_CONTENT) 799 ? windowGeometry->final_height.value 800 - windowGeometry->extents.top 801 - windowGeometry->extents.bottom 802 : windowGeometry->final_height.value; 803 } 804 805 static int geometry_get_window_x(const WindowGeometry *windowGeometry) { 806 float value = windowGeometry->refx; 807 if (windowGeometry->gravity_x != 0) { 808 value -= geometry_get_window_width(windowGeometry) 809 * windowGeometry->gravity_x; 810 } 811 return (int) value; 812 } 813 814 static int geometry_get_window_y(const WindowGeometry *windowGeometry) { 815 float value = windowGeometry->refy; 816 if (windowGeometry->gravity_y != 0) { 817 value -= geometry_get_window_height(windowGeometry) 818 * windowGeometry->gravity_y; 819 } 820 return (int) value; 821 } 822 823 static void geometry_set_window_x(WindowGeometry *windowGeometry, int value) { 824 float newValue = value; 825 if (windowGeometry->gravity_x != 0) { 826 newValue += geometry_get_window_width(windowGeometry) 827 * windowGeometry->gravity_x; 828 } 829 windowGeometry->refx = newValue; 830 } 831 832 static void geometry_set_window_y(WindowGeometry *windowGeometry, int value) { 833 float newValue = value; 834 if (windowGeometry->gravity_y != 0) { 835 newValue += geometry_get_window_height(windowGeometry) 836 * windowGeometry->gravity_y; 837 } 838 windowGeometry->refy = newValue; 839 } 840 841 void WindowContextTop::process_net_wm_property() { 842 // Workaround for https://bugs.launchpad.net/unity/+bug/998073 843 844 static GdkAtom atom_atom = gdk_atom_intern_static_string("ATOM"); 845 static GdkAtom atom_net_wm_state_hidden = gdk_atom_intern_static_string("_NET_WM_STATE_HIDDEN"); 846 static GdkAtom atom_net_wm_state_above = gdk_atom_intern_static_string("_NET_WM_STATE_ABOVE"); 847 848 gint length; 849 850 glong* atoms = NULL; 851 852 if (gdk_property_get(gdk_window, atom_net_wm_state, atom_atom, 853 0, G_MAXLONG, FALSE, NULL, NULL, &length, (guchar**) &atoms)) { 854 855 bool is_hidden = false; 856 bool is_above = false; 857 for (gint i = 0; i < (gint)(length / sizeof(glong)); i++) { 858 if (atom_net_wm_state_hidden == (GdkAtom)atoms[i]) { 859 is_hidden = true; 860 } else if (atom_net_wm_state_above == (GdkAtom)atoms[i]) { 861 is_above = true; 862 } 863 } 864 865 g_free(atoms); 866 867 if (is_iconified != is_hidden) { 868 is_iconified = is_hidden; 869 870 notify_state((is_hidden) 871 ? com_sun_glass_events_WindowEvent_MINIMIZE 872 : com_sun_glass_events_WindowEvent_RESTORE); 873 } 874 875 notify_on_top(is_above); 876 } 877 } 878 879 void WindowContextTop::process_property_notify(GdkEventProperty* event) { 880 if (event->atom == atom_net_wm_state && event->window == gdk_window) { 881 process_net_wm_property(); 882 } else if (event->atom == get_net_frame_extents_atom() && 883 event->window == gdk_window) { 884 int top, left, bottom, right; 885 if (get_frame_extents_property(&top, &left, &bottom, &right)) { 886 int oldX = geometry_get_window_x(&geometry); 887 int oldY = geometry_get_window_y(&geometry); 888 int oldWidth = geometry_get_content_width(&geometry); 889 int oldHeight = geometry_get_content_height(&geometry); 890 891 bool updateWindowConstraints = geometry.extents.top != top 892 || geometry.extents.left != left 893 || geometry.extents.bottom != bottom 894 || geometry.extents.right != right; 895 896 geometry.extents.top = top; 897 geometry.extents.left = left; 898 geometry.extents.bottom = bottom; 899 geometry.extents.right = right; 900 901 if (updateWindowConstraints) { 902 update_window_constraints(); 903 } 904 905 XWindowChanges windowChanges; 906 unsigned int windowChangesMask = 0; 907 908 int newX = geometry_get_window_x(&geometry); 909 int newY = geometry_get_window_y(&geometry); 910 int newWidth = geometry_get_content_width(&geometry); 911 int newHeight = geometry_get_content_height(&geometry); 912 913 if (oldX != newX) { 914 windowChanges.x = newX; 915 windowChangesMask |= CWX; 916 } 917 918 if (oldY != newY) { 919 windowChanges.y = newY; 920 windowChangesMask |= CWY; 921 } 922 923 if (oldWidth != newWidth) { 924 windowChanges.width = newWidth; 925 windowChangesMask |= CWWidth; 926 } 927 928 if (oldHeight != newHeight) { 929 windowChanges.height = newHeight; 930 windowChangesMask |= CWHeight; 931 } 932 933 window_configure(&windowChanges, windowChangesMask); 934 935 if (jview) { 936 mainEnv->CallVoidMethod(jview, jViewNotifyView, com_sun_glass_events_ViewEvent_MOVE); 937 CHECK_JNI_EXCEPTION(mainEnv) 938 } 939 } 940 } 941 } 942 943 void WindowContextTop::process_configure(GdkEventConfigure* event) { 944 945 geometry.current_width = event->width + geometry.extents.left 946 + geometry.extents.right; 947 geometry.current_height = event->height + geometry.extents.top 948 + geometry.extents.bottom; 949 gint x, y; 950 if (gtk_window_get_decorated(GTK_WINDOW(gtk_widget))) { 951 gtk_window_get_position(GTK_WINDOW(gtk_widget), &x, &y); 952 } else { 953 x = event->x; 954 y = event->y; 955 } 956 957 if (stale_config_notifications == 0) { 958 if ((geometry_get_content_width(&geometry) != event->width) 959 || (geometry_get_content_height(&geometry) != event->height)) { 960 geometry.final_width.value = event->width; 961 geometry.final_width.type = BOUNDSTYPE_CONTENT; 962 geometry.final_height.value = event->height; 963 geometry.final_height.type = BOUNDSTYPE_CONTENT; 964 } 965 geometry_set_window_x(&geometry, x); 966 geometry_set_window_y(&geometry, y); 967 } else { 968 --stale_config_notifications; 969 } 970 if (jview) { 971 mainEnv->CallVoidMethod(jview, jViewNotifyResize, 972 event->width, 973 event->height); 974 CHECK_JNI_EXCEPTION(mainEnv) 975 } 976 if (jwindow) { 977 mainEnv->CallVoidMethod(jwindow, jWindowNotifyResize, 978 (is_maximized) 979 ? com_sun_glass_events_WindowEvent_MAXIMIZE 980 : com_sun_glass_events_WindowEvent_RESIZE, 981 geometry.current_width, 982 geometry.current_height); 983 CHECK_JNI_EXCEPTION(mainEnv) 984 985 mainEnv->CallVoidMethod(jwindow, jWindowNotifyMove, x, y); 986 CHECK_JNI_EXCEPTION(mainEnv) 987 } 988 989 glong to_screen = getScreenPtrForLocation(x, y); 990 if (to_screen != -1) { 991 if (to_screen != screen) { 992 if (jwindow) { 993 //notify screen changed 994 jobject jScreen = createJavaScreen(mainEnv, to_screen); 995 mainEnv->CallVoidMethod(jwindow, jWindowNotifyMoveToAnotherScreen, jScreen); 996 CHECK_JNI_EXCEPTION(mainEnv) 997 } 998 screen = to_screen; 999 } 1000 } 1001 1002 if (resizable.request != REQUEST_NONE) { 1003 set_window_resizable(resizable.request == REQUEST_RESIZABLE, true); 1004 resizable.request = REQUEST_NONE; 1005 } 1006 } 1007 1008 void WindowContextTop::update_window_constraints() { 1009 if (resizable.value) { 1010 GdkGeometry geom = { 1011 (resizable.minw == -1) ? 1 1012 : resizable.minw - geometry.extents.left - geometry.extents.right, 1013 (resizable.minh == -1) ? 1 1014 : resizable.minh - geometry.extents.top - geometry.extents.bottom, 1015 (resizable.maxw == -1) ? 100000 1016 : resizable.maxw - geometry.extents.left - geometry.extents.right, 1017 (resizable.maxh == -1) ? 100000 1018 : resizable.maxh - geometry.extents.top - geometry.extents.bottom, 1019 0, 0, 0, 0, 0.0, 0.0, GDK_GRAVITY_NORTH_WEST 1020 }; 1021 gtk_window_set_geometry_hints(GTK_WINDOW(gtk_widget), NULL, &geom, 1022 static_cast<GdkWindowHints> (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); 1023 } 1024 } 1025 1026 void WindowContextTop::set_window_resizable(bool res, bool grip) { 1027 if(!res) { 1028 int w = geometry_get_content_width(&geometry); 1029 int h = geometry_get_content_height(&geometry); 1030 if (w == -1 && h == -1) { 1031 gtk_window_get_size(GTK_WINDOW(gtk_widget), &w, &h); 1032 } 1033 GdkGeometry geom = {w, h, w, h, 0, 0, 0, 0, 0.0, 0.0, GDK_GRAVITY_NORTH_WEST}; 1034 gtk_window_set_geometry_hints(GTK_WINDOW(gtk_widget), NULL, &geom, 1035 static_cast<GdkWindowHints>(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); 1036 GLASS_GTK_WINDOW_SET_HAS_RESIZE_GRIP(gdk_window, FALSE); 1037 resizable.prev = resizable.value; 1038 resizable.value = false; 1039 } else { 1040 resizable.prev = resizable.value; 1041 resizable.value = true; 1042 update_window_constraints(); 1043 if (grip) { 1044 GLASS_GTK_WINDOW_SET_HAS_RESIZE_GRIP(gdk_window, TRUE); 1045 } 1046 } 1047 } 1048 1049 void WindowContextTop::set_resizable(bool res) { 1050 gint w, h; 1051 gtk_window_get_size(GTK_WINDOW(gtk_widget), &w, &h); 1052 if (map_received || w > 1 || h > 1) { 1053 set_window_resizable(res, true); 1054 } else { 1055 //Since window is not ready yet set only request for change of resizable. 1056 resizable.request = res ? REQUEST_RESIZABLE : REQUEST_NOT_RESIZABLE; 1057 } 1058 } 1059 1060 void WindowContextTop::set_visible(bool visible) 1061 { 1062 if (visible) { 1063 if (!size_assigned) { 1064 set_bounds(0, 0, false, false, 320, 200, -1, -1); 1065 } 1066 if (!location_assigned) { 1067 set_bounds(0, 0, true, true, -1, -1, -1, -1); 1068 } 1069 } 1070 WindowContextBase::set_visible(visible); 1071 } 1072 1073 void WindowContextTop::set_bounds(int x, int y, bool xSet, bool ySet, int w, int h, int cw, int ch) { 1074 if (!frame_extents_initialized && frame_type == TITLED) { 1075 initialize_frame_extents(); 1076 frame_extents_initialized = true; 1077 } 1078 1079 XWindowChanges windowChanges; 1080 unsigned int windowChangesMask = 0; 1081 if (w > 0) { 1082 geometry.final_width.value = w; 1083 geometry.final_width.type = BOUNDSTYPE_WINDOW; 1084 geometry.current_width = geometry_get_window_width(&geometry); 1085 windowChanges.width = geometry_get_content_width(&geometry); 1086 windowChangesMask |= CWWidth; 1087 } else if (cw > 0) { 1088 geometry.final_width.value = cw; 1089 geometry.final_width.type = BOUNDSTYPE_CONTENT; 1090 geometry.current_width = geometry_get_window_width(&geometry); 1091 windowChanges.width = geometry_get_content_width(&geometry); 1092 windowChangesMask |= CWWidth; 1093 } 1094 1095 if (h > 0) { 1096 geometry.final_height.value = h; 1097 geometry.final_height.type = BOUNDSTYPE_WINDOW; 1098 geometry.current_height = geometry_get_window_height(&geometry); 1099 windowChanges.height = geometry_get_content_height(&geometry); 1100 windowChangesMask |= CWHeight; 1101 } else if (ch > 0) { 1102 geometry.final_height.value = ch; 1103 geometry.final_height.type = BOUNDSTYPE_CONTENT; 1104 geometry.current_height = geometry_get_window_height(&geometry); 1105 windowChanges.height = geometry_get_content_height(&geometry); 1106 windowChangesMask |= CWHeight; 1107 } 1108 1109 if (xSet) { 1110 geometry.refx = x + geometry.current_width * geometry.gravity_x; 1111 windowChanges.x = geometry_get_window_x(&geometry); 1112 windowChangesMask |= CWX; 1113 1114 } else if ((geometry.gravity_x != 0) && (windowChangesMask & CWWidth)) { 1115 windowChanges.x = geometry_get_window_x(&geometry); 1116 windowChangesMask |= CWX; 1117 } 1118 1119 if (ySet) { 1120 geometry.refy = y + geometry.current_height * geometry.gravity_y; 1121 windowChanges.y = geometry_get_window_y(&geometry); 1122 windowChangesMask |= CWY; 1123 1124 } else if ((geometry.gravity_y != 0) && (windowChangesMask & CWHeight)) { 1125 windowChanges.y = geometry_get_window_y(&geometry); 1126 windowChangesMask |= CWY; 1127 } 1128 1129 if (xSet || ySet) location_assigned = true; 1130 if (w > 0 || h > 0 || cw > 0 || ch > 0) size_assigned = true; 1131 1132 window_configure(&windowChanges, windowChangesMask); 1133 1134 } 1135 1136 void WindowContextTop::process_map() { 1137 map_received = true; 1138 } 1139 1140 void WindowContextTop::window_configure(XWindowChanges *windowChanges, 1141 unsigned int windowChangesMask) { 1142 if (windowChangesMask == 0) { 1143 return; 1144 } 1145 1146 if (!gtk_widget_get_visible(gtk_widget)) { 1147 // not visible yet, synchronize with gtk only 1148 if (windowChangesMask & (CWX | CWY)) { 1149 gint newX, newY; 1150 gtk_window_get_position(GTK_WINDOW(gtk_widget), &newX, &newY); 1151 1152 if (windowChangesMask & CWX) { 1153 newX = windowChanges->x; 1154 } 1155 if (windowChangesMask & CWY) { 1156 newY = windowChanges->y; 1157 } 1158 1159 gtk_window_move(GTK_WINDOW(gtk_widget), newX, newY); 1160 } 1161 1162 if (windowChangesMask & (CWWidth | CWHeight)) { 1163 gint newWidth, newHeight; 1164 gtk_window_get_size(GTK_WINDOW(gtk_widget), 1165 &newWidth, &newHeight); 1166 1167 if (windowChangesMask & CWWidth) { 1168 newWidth = windowChanges->width; 1169 } 1170 if (windowChangesMask & CWHeight) { 1171 newHeight = windowChanges->height; 1172 } 1173 1174 gtk_window_resize(GTK_WINDOW(gtk_widget), newWidth, newHeight); 1175 } 1176 stale_config_notifications = 1; 1177 return; 1178 } 1179 1180 ++stale_config_notifications; 1181 1182 if (!resizable.value && (windowChangesMask & (CWWidth | CWHeight))) { 1183 XSizeHints *sizeHints = XAllocSizeHints(); 1184 if (sizeHints != NULL) { 1185 int fixedWidth = (windowChangesMask & CWWidth) 1186 ? windowChanges->width 1187 : geometry_get_content_width(&geometry); 1188 int fixedHeight = (windowChangesMask & CWHeight) 1189 ? windowChanges->height 1190 : geometry_get_content_height(&geometry); 1191 1192 sizeHints->flags = PMinSize | PMaxSize; 1193 1194 sizeHints->min_width = 1; 1195 sizeHints->min_height = 1; 1196 sizeHints->max_width = INT_MAX; 1197 sizeHints->max_height = INT_MAX; 1198 XSetWMNormalHints(GDK_WINDOW_XDISPLAY(gdk_window), 1199 GDK_WINDOW_XID(gdk_window), 1200 sizeHints); 1201 1202 XConfigureWindow(GDK_WINDOW_XDISPLAY(gdk_window), 1203 GDK_WINDOW_XID(gdk_window), 1204 windowChangesMask, 1205 windowChanges); 1206 1207 sizeHints->min_width = fixedWidth; 1208 sizeHints->min_height = fixedHeight; 1209 sizeHints->max_width = fixedWidth; 1210 sizeHints->max_height = fixedHeight; 1211 XSetWMNormalHints(GDK_WINDOW_XDISPLAY(gdk_window), 1212 GDK_WINDOW_XID(gdk_window), 1213 sizeHints); 1214 1215 XFree(sizeHints); 1216 return; 1217 } 1218 } 1219 1220 XConfigureWindow(GDK_WINDOW_XDISPLAY(gdk_window), 1221 GDK_WINDOW_XID(gdk_window), 1222 windowChangesMask, 1223 windowChanges); 1224 } 1225 1226 void WindowContextTop::applyShapeMask(void* data, uint width, uint height) 1227 { 1228 if (frame_type != TRANSPARENT) { 1229 return; 1230 } 1231 1232 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data((guchar *) data, 1233 GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * 4, NULL, NULL); 1234 1235 if (GDK_IS_PIXBUF(pixbuf)) { 1236 GdkBitmap* mask = NULL; 1237 gdk_pixbuf_render_pixmap_and_mask(pixbuf, NULL, &mask, 128); 1238 1239 gdk_window_input_shape_combine_mask(gdk_window, mask, 0, 0); 1240 1241 g_object_unref(pixbuf); 1242 if (mask) { 1243 g_object_unref(mask); 1244 } 1245 } 1246 } 1247 1248 void WindowContextTop::set_minimized(bool minimize) { 1249 is_iconified = minimize; 1250 if (minimize) { 1251 if (frame_type == TRANSPARENT) { 1252 // https://bugs.launchpad.net/ubuntu/+source/unity/+bug/1245571 1253 gdk_window_input_shape_combine_mask(gdk_window, NULL, 0, 0); 1254 } 1255 gtk_window_iconify(GTK_WINDOW(gtk_widget)); 1256 } else { 1257 gtk_window_deiconify(GTK_WINDOW(gtk_widget)); 1258 } 1259 } 1260 void WindowContextTop::set_maximized(bool maximize) { 1261 is_maximized = maximize; 1262 if (maximize) { 1263 gtk_window_maximize(GTK_WINDOW(gtk_widget)); 1264 } else { 1265 gtk_window_unmaximize(GTK_WINDOW(gtk_widget)); 1266 } 1267 } 1268 1269 void WindowContextTop::enter_fullscreen() { 1270 gtk_window_fullscreen(GTK_WINDOW(gtk_widget)); 1271 } 1272 1273 void WindowContextTop::exit_fullscreen() { 1274 gtk_window_unfullscreen(GTK_WINDOW(gtk_widget)); 1275 } 1276 1277 void WindowContextTop::request_focus() { 1278 gtk_window_present(GTK_WINDOW(gtk_widget)); 1279 } 1280 1281 void WindowContextTop::set_focusable(bool focusable) { 1282 gtk_window_set_accept_focus(GTK_WINDOW(gtk_widget), focusable ? TRUE : FALSE); 1283 } 1284 1285 void WindowContextTop::set_title(const char* title) { 1286 gtk_window_set_title(GTK_WINDOW(gtk_widget),title); 1287 } 1288 1289 void WindowContextTop::set_alpha(double alpha) { 1290 gtk_window_set_opacity(GTK_WINDOW(gtk_widget), (gdouble)alpha); 1291 } 1292 1293 void WindowContextTop::set_enabled(bool enabled) { 1294 if (enabled) { 1295 //set back proper resizable value. 1296 set_window_resizable(resizable.prev, true); 1297 } else { 1298 //disabled window can't be resizable. 1299 set_window_resizable(false, false); 1300 } 1301 } 1302 1303 void WindowContextTop::set_minimum_size(int w, int h) { 1304 resizable.minw = w; 1305 resizable.minh = h; 1306 update_window_constraints(); 1307 } 1308 1309 void WindowContextTop::set_maximum_size(int w, int h) { 1310 resizable.maxw = w; 1311 resizable.maxh = h; 1312 update_window_constraints(); 1313 } 1314 1315 void WindowContextTop::set_icon(GdkPixbuf* pixbuf) { 1316 gtk_window_set_icon(GTK_WINDOW(gtk_widget), pixbuf); 1317 } 1318 1319 void WindowContextTop::restack(bool restack) { 1320 gdk_window_restack(gdk_window, NULL, restack ? TRUE : FALSE); 1321 } 1322 1323 void WindowContextTop::set_modal(bool modal, WindowContext* parent) { 1324 if (modal) { 1325 //gtk_window_set_type_hint(GTK_WINDOW(gtk_widget), GDK_WINDOW_TYPE_HINT_DIALOG); 1326 if (parent) { 1327 gtk_window_set_transient_for(GTK_WINDOW(gtk_widget), parent->get_gtk_window()); 1328 } 1329 } 1330 gtk_window_set_modal(GTK_WINDOW(gtk_widget), modal ? TRUE : FALSE); 1331 } 1332 1333 GtkWindow *WindowContextTop::get_gtk_window() { 1334 return GTK_WINDOW(gtk_widget); 1335 } 1336 1337 WindowFrameExtents WindowContextTop::get_frame_extents() { 1338 return geometry.extents; 1339 } 1340 1341 void WindowContextTop::set_gravity(float x, float y) { 1342 int oldX = geometry_get_window_x(&geometry); 1343 int oldY = geometry_get_window_y(&geometry); 1344 geometry.gravity_x = x; 1345 geometry.gravity_y = y; 1346 geometry_set_window_x(&geometry, oldX); 1347 geometry_set_window_y(&geometry, oldY); 1348 } 1349 1350 void WindowContextTop::update_ontop_tree(bool on_top) { 1351 bool effective_on_top = on_top || this->on_top; 1352 gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), effective_on_top ? TRUE : FALSE); 1353 for (std::set<WindowContextTop*>::iterator it = children.begin(); it != children.end(); ++it) { 1354 (*it)->update_ontop_tree(effective_on_top); 1355 } 1356 } 1357 1358 bool WindowContextTop::on_top_inherited() { 1359 WindowContext* o = owner; 1360 while (o) { 1361 WindowContextTop* topO = dynamic_cast<WindowContextTop*>(o); 1362 if (!topO) break; 1363 if (topO->on_top) { 1364 return true; 1365 } 1366 o = topO->owner; 1367 } 1368 return false; 1369 } 1370 1371 bool WindowContextTop::effective_on_top() { 1372 if (owner) { 1373 WindowContextTop* topO = dynamic_cast<WindowContextTop*>(owner); 1374 return (topO && topO->effective_on_top()) || on_top; 1375 } 1376 return on_top; 1377 } 1378 1379 void WindowContextTop::notify_on_top(bool top) { 1380 // Do not report effective (i.e. native) values to the FX, only if the user sets it manually 1381 if (top != effective_on_top() && jwindow) { 1382 if (on_top_inherited() && !top) { 1383 // Disallow user's "on top" handling on windows that inherited the property 1384 gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE); 1385 } else { 1386 on_top = top; 1387 update_ontop_tree(top); 1388 mainEnv->CallVoidMethod(jwindow, 1389 jWindowNotifyLevelChanged, 1390 top ? com_sun_glass_ui_Window_Level_FLOATING : com_sun_glass_ui_Window_Level_NORMAL); 1391 CHECK_JNI_EXCEPTION(mainEnv); 1392 } 1393 } 1394 } 1395 1396 void WindowContextTop::set_level(int level) { 1397 if (level == com_sun_glass_ui_Window_Level_NORMAL) { 1398 on_top = false; 1399 } else if (level == com_sun_glass_ui_Window_Level_FLOATING 1400 || level == com_sun_glass_ui_Window_Level_TOPMOST) { 1401 on_top = true; 1402 } 1403 // We need to emulate always on top behaviour on child windows 1404 1405 if (!on_top_inherited()) { 1406 update_ontop_tree(on_top); 1407 } 1408 } 1409 1410 void WindowContextTop::set_owner(WindowContext * owner_ctx) { 1411 owner = owner_ctx; 1412 } 1413 1414 void WindowContextTop::process_destroy() { 1415 if (owner) { 1416 owner->remove_child(this); 1417 } 1418 1419 WindowContextBase::process_destroy(); 1420 } 1421 1422 ////////////////////////////// WindowContextPlug //////////////////////////////// 1423 1424 static gboolean plug_configure(GtkWidget *widget, GdkEvent *event, gpointer user_data) { 1425 (void)widget; 1426 1427 if (event->type == GDK_CONFIGURE) { 1428 ((WindowContextPlug*)user_data)->process_gtk_configure(&event->configure); 1429 } 1430 return FALSE; 1431 } 1432 1433 WindowContextPlug::WindowContextPlug(jobject _jwindow, void* _owner) : 1434 WindowContextBase(), 1435 parent() 1436 { 1437 jwindow = mainEnv->NewGlobalRef(_jwindow); 1438 1439 gtk_widget = gtk_plug_new((GdkNativeWindow)PTR_TO_JLONG(_owner)); 1440 1441 g_signal_connect(G_OBJECT(gtk_widget), "configure-event", G_CALLBACK(plug_configure), this); 1442 1443 gtk_widget_set_size_request(gtk_widget, 0, 0); 1444 gtk_widget_set_events(gtk_widget, GDK_ALL_EVENTS_MASK); 1445 gtk_widget_set_can_focus(GTK_WIDGET(gtk_widget), TRUE); 1446 gtk_widget_set_app_paintable(gtk_widget, TRUE); 1447 1448 gtk_widget_realize(gtk_widget); 1449 gdk_window = gtk_widget_get_window(gtk_widget); 1450 1451 g_object_set_data_full(G_OBJECT(gdk_window), GDK_WINDOW_DATA_CONTEXT, this, NULL); 1452 gdk_window_register_dnd(gdk_window); 1453 1454 gtk_container = gtk_fixed_new(); 1455 gtk_container_add (GTK_CONTAINER(gtk_widget), gtk_container); 1456 gtk_widget_realize(gtk_container); 1457 } 1458 1459 GtkWindow *WindowContextPlug::get_gtk_window() { 1460 return GTK_WINDOW(gtk_widget); 1461 } 1462 1463 void WindowContextPlug::process_configure(GdkEventConfigure* event) { 1464 (void)event; 1465 1466 //Note: process_gtk_configure is used, so there's no need to handle GDK events 1467 } 1468 1469 void WindowContextPlug::process_gtk_configure(GdkEventConfigure* event) { 1470 if (jview) { 1471 mainEnv->CallVoidMethod(jview, jViewNotifyResize, 1472 event->width, 1473 event->height); 1474 CHECK_JNI_EXCEPTION(mainEnv) 1475 } 1476 1477 mainEnv->CallVoidMethod(jwindow, jWindowNotifyResize, 1478 com_sun_glass_events_WindowEvent_RESIZE, 1479 event->width, 1480 event->height); 1481 CHECK_JNI_EXCEPTION(mainEnv) 1482 1483 if (!embedded_children.empty()) { 1484 WindowContextChild* child = embedded_children.back(); 1485 child->process_configure(event); 1486 } 1487 } 1488 1489 bool WindowContextPlug::set_view(jobject view) { 1490 // probably never called for applet window 1491 if (jview) { 1492 mainEnv->DeleteGlobalRef(jview); 1493 } 1494 1495 if (view) { 1496 gint width, height; 1497 jview = mainEnv->NewGlobalRef(view); 1498 gtk_window_get_size(GTK_WINDOW(gtk_widget), &width, &height); 1499 mainEnv->CallVoidMethod(view, jViewNotifyResize, width, height); 1500 CHECK_JNI_EXCEPTION_RET(mainEnv, FALSE) 1501 } else { 1502 jview = NULL; 1503 } 1504 return TRUE; 1505 } 1506 1507 void WindowContextPlug::window_configure(XWindowChanges *windowChanges, 1508 unsigned int windowChangesMask) { 1509 if (windowChangesMask == 0) { 1510 return; 1511 } 1512 1513 if (windowChangesMask & (CWX | CWY)) { 1514 gint newX, newY; 1515 gtk_window_get_position(GTK_WINDOW(gtk_widget), &newX, &newY); 1516 1517 if (windowChangesMask & CWX) { 1518 newX = windowChanges->x; 1519 } 1520 if (windowChangesMask & CWY) { 1521 newY = windowChanges->y; 1522 } 1523 gtk_window_move(GTK_WINDOW(gtk_widget), newX, newY); 1524 } 1525 1526 if (windowChangesMask & (CWWidth | CWHeight)) { 1527 gint newWidth, newHeight; 1528 gtk_window_get_size(GTK_WINDOW(gtk_widget), 1529 &newWidth, &newHeight); 1530 1531 if (windowChangesMask & CWWidth) { 1532 newWidth = windowChanges->width; 1533 } 1534 if (windowChangesMask & CWHeight) { 1535 newHeight = windowChanges->height; 1536 }; 1537 gtk_widget_set_size_request(gtk_widget, newWidth, newHeight); 1538 } 1539 } 1540 1541 void WindowContextPlug::set_bounds(int x, int y, bool xSet, bool ySet, int w, int h, int cw, int ch) { 1542 XWindowChanges windowChanges; 1543 unsigned int windowChangesMask = 0; 1544 1545 if (xSet) { 1546 windowChanges.x = x; 1547 windowChangesMask |= CWX; 1548 } 1549 1550 if (ySet) { 1551 windowChanges.y = y; 1552 windowChangesMask |= CWY; 1553 } 1554 1555 if (w > 0) { 1556 windowChanges.width = w; 1557 windowChangesMask |= CWWidth; 1558 } else if (cw > 0) { 1559 windowChanges.width = cw; 1560 windowChangesMask |= CWWidth; 1561 } 1562 1563 if (h > 0) { 1564 windowChanges.height = h; 1565 windowChangesMask |= CWHeight; 1566 } else if (ch > 0) { 1567 windowChanges.height = ch; 1568 windowChangesMask |= CWHeight; 1569 } 1570 1571 window_configure(&windowChanges, windowChangesMask); 1572 } 1573 ////////////////////////////// WindowContextChild //////////////////////////////// 1574 1575 void WindowContextChild::process_mouse_button(GdkEventButton* event) { 1576 WindowContextBase::process_mouse_button(event); 1577 // gtk_window_set_focus (GTK_WINDOW (gtk_widget_get_ancestor(gtk_widget, GTK_TYPE_WINDOW)), NULL); 1578 gtk_widget_grab_focus(gtk_widget); 1579 } 1580 1581 static gboolean child_focus_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) 1582 { 1583 (void)widget; 1584 1585 WindowContext *ctx = (WindowContext *)user_data; 1586 ctx->process_focus(&event->focus_change); 1587 return TRUE; 1588 } 1589 1590 WindowContextChild::WindowContextChild(jobject _jwindow, 1591 void* _owner, 1592 GtkWidget *parent_widget, 1593 WindowContextPlug *parent_ctx) : 1594 WindowContextBase(), 1595 parent(), 1596 full_screen_window(), 1597 view() 1598 { 1599 (void)_owner; 1600 1601 jwindow = mainEnv->NewGlobalRef(_jwindow); 1602 gtk_widget = gtk_drawing_area_new(); 1603 parent = parent_ctx; 1604 1605 glong xvisualID = (glong) mainEnv->GetStaticLongField(jApplicationCls, jApplicationVisualID); 1606 1607 if (xvisualID != 0) { 1608 GdkVisual *visual = gdk_x11_screen_lookup_visual(gdk_screen_get_default(), xvisualID); 1609 glass_gtk_window_configure_from_visual(gtk_widget, visual); 1610 } 1611 1612 gtk_widget_set_events(gtk_widget, GDK_ALL_EVENTS_MASK); 1613 gtk_widget_set_can_focus(GTK_WIDGET(gtk_widget), TRUE); 1614 gtk_widget_set_app_paintable(gtk_widget, TRUE); 1615 gtk_container_add (GTK_CONTAINER(parent_widget), gtk_widget); 1616 gtk_widget_realize(gtk_widget); 1617 gdk_window = gtk_widget_get_window(gtk_widget); 1618 g_object_set_data_full(G_OBJECT(gdk_window), GDK_WINDOW_DATA_CONTEXT, this, NULL); 1619 gdk_window_register_dnd(gdk_window); 1620 g_signal_connect(gtk_widget, "focus-in-event", G_CALLBACK(child_focus_callback), this); 1621 g_signal_connect(gtk_widget, "focus-out-event", G_CALLBACK(child_focus_callback), this); 1622 } 1623 1624 void WindowContextChild::set_visible(bool visible) { 1625 std::vector<WindowContextChild*> &embedded_children = 1626 dynamic_cast<WindowContextPlug*>(parent)->embedded_children; 1627 1628 if (visible) { 1629 embedded_children.push_back(this); 1630 } else { 1631 std::vector<WindowContextChild*>::iterator pos 1632 = std::find(embedded_children.begin(), embedded_children.end(), this); 1633 if (pos != embedded_children.end()) { 1634 embedded_children.erase((pos)); 1635 } 1636 } 1637 1638 WindowContextBase::set_visible(visible); 1639 } 1640 1641 GtkWindow *WindowContextChild::get_gtk_window() { 1642 return GTK_WINDOW(gtk_widget_get_ancestor(gtk_widget, GTK_TYPE_WINDOW)); 1643 } 1644 1645 void WindowContextChild::process_configure(GdkEventConfigure* event) { 1646 if (jview) { 1647 mainEnv->CallVoidMethod(jview, jViewNotifyResize, 1648 event->width, 1649 event->height); 1650 CHECK_JNI_EXCEPTION(mainEnv) 1651 } 1652 1653 gtk_widget_set_size_request(gtk_widget, event->width, event->height); 1654 1655 if (jwindow) { 1656 mainEnv->CallVoidMethod(jwindow, jWindowNotifyResize, 1657 com_sun_glass_events_WindowEvent_RESIZE, 1658 event->width, 1659 event->height); 1660 CHECK_JNI_EXCEPTION(mainEnv) 1661 } 1662 } 1663 1664 bool WindowContextChild::set_view(jobject view) { 1665 if (jview) { 1666 mainEnv->DeleteGlobalRef(jview); 1667 } 1668 1669 if (view) { 1670 gint width, height; 1671 jview = mainEnv->NewGlobalRef(view); 1672 width = gtk_widget->allocation.width; 1673 height = gtk_widget->allocation.height; 1674 mainEnv->CallVoidMethod(view, jViewNotifyResize, width, height); 1675 CHECK_JNI_EXCEPTION_RET(mainEnv, FALSE) 1676 } else { 1677 jview = NULL; 1678 } 1679 return TRUE; 1680 } 1681 1682 void WindowContextChild::set_bounds(int x, int y, bool xSet, bool ySet, int w, int h, int cw, int ch) { 1683 1684 if (x > 0 || y > 0 || xSet || ySet) { 1685 gint newX, newY; 1686 gdk_window_get_origin(gdk_window, &newX, &newY); 1687 if (jwindow) { 1688 mainEnv->CallVoidMethod(jwindow, 1689 jWindowNotifyMove, 1690 newX, newY); 1691 CHECK_JNI_EXCEPTION(mainEnv) 1692 } 1693 } 1694 1695 // As we have no frames, there's no difference between the calls 1696 if ((cw | ch) > 0) { 1697 w = cw; h = ch; 1698 } 1699 1700 if (w > 0 || h > 0) { 1701 gint newWidth, newHeight; 1702 newWidth = gtk_widget->allocation.width; 1703 newHeight = gtk_widget->allocation.height; 1704 1705 if (w > 0) { 1706 newWidth = w; 1707 } 1708 if (h > 0) { 1709 newHeight = h; 1710 } 1711 gtk_widget_set_size_request(gtk_widget, newWidth, newHeight); 1712 // FIXME: hack to set correct size to view 1713 if (jview) { 1714 mainEnv->CallVoidMethod(jview, 1715 jViewNotifyResize, 1716 newWidth, newHeight); 1717 CHECK_JNI_EXCEPTION(mainEnv) 1718 } 1719 } 1720 } 1721 1722 int WindowContextChild::getEmbeddedX() { 1723 int x; 1724 gdk_window_get_origin(gdk_window, &x, NULL); 1725 return x; 1726 } 1727 1728 int WindowContextChild::getEmbeddedY() { 1729 int y; 1730 gdk_window_get_origin(gdk_window, NULL, &y); 1731 return y; 1732 1733 } 1734 1735 void WindowContextChild::restack(bool toFront) { 1736 std::vector<WindowContextChild*> &embedded_children = 1737 dynamic_cast<WindowContextPlug*>(parent)->embedded_children; 1738 1739 std::vector<WindowContextChild*>::iterator pos 1740 = std::find(embedded_children.begin(), embedded_children.end(), this); 1741 1742 embedded_children.erase(pos); 1743 if (toFront) { 1744 embedded_children.push_back(this); 1745 } else { 1746 embedded_children.insert(embedded_children.begin(), this); 1747 } 1748 1749 gdk_window_restack(gdk_window, NULL, toFront ? TRUE : FALSE); 1750 } 1751 1752 void WindowContextChild::enter_fullscreen() { 1753 if (full_screen_window) { 1754 return; 1755 } 1756 1757 full_screen_window = new WindowContextTop(jwindow, NULL, 0L, UNTITLED, 1758 NORMAL, (GdkWMFunction) 0); 1759 int x, y, w, h; 1760 gdk_window_get_origin(gdk_window, &x, &y); 1761 gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h, NULL); 1762 full_screen_window->set_bounds(x, y, true, true, w, h, -1, -1); 1763 1764 if (WindowContextBase::sm_grab_window == this) { 1765 ungrab_focus(); 1766 } 1767 1768 reparent_children(full_screen_window); 1769 1770 full_screen_window->set_visible(true); 1771 full_screen_window->enter_fullscreen(); 1772 1773 if (jwindow) { 1774 mainEnv->CallVoidMethod(jwindow, jWindowNotifyDelegatePtr, (jlong)full_screen_window); 1775 CHECK_JNI_EXCEPTION(mainEnv) 1776 } 1777 1778 if (jview) { 1779 this->view = (GlassView*)mainEnv->GetLongField(jview, jViewPtr); 1780 1781 this->view->current_window = full_screen_window; 1782 this->view->embedded_window = this; 1783 full_screen_window->set_view(jview); 1784 this->set_view(NULL); 1785 } 1786 } 1787 1788 void WindowContextChild::exit_fullscreen() { 1789 if (!full_screen_window) { 1790 return; 1791 } 1792 1793 if (WindowContextBase::sm_grab_window == this) { 1794 ungrab_focus(); 1795 } 1796 1797 full_screen_window->reparent_children(this); 1798 1799 mainEnv->CallVoidMethod(jwindow, jWindowNotifyDelegatePtr, (jlong)NULL); 1800 CHECK_JNI_EXCEPTION(mainEnv) 1801 1802 if (this->view) { 1803 this->view->current_window = this; 1804 this->view->embedded_window = NULL; 1805 } 1806 this->set_view(full_screen_window->get_jview()); 1807 1808 full_screen_window->detach_from_java(); 1809 1810 full_screen_window->set_view(NULL); 1811 1812 full_screen_window->set_visible(false); 1813 1814 destroy_and_delete_ctx(full_screen_window); 1815 full_screen_window = NULL; 1816 this->view = NULL; 1817 } 1818 1819 void WindowContextChild::process_destroy() { 1820 if (full_screen_window) { 1821 destroy_and_delete_ctx(full_screen_window); 1822 } 1823 1824 std::vector<WindowContextChild*> &embedded_children = 1825 dynamic_cast<WindowContextPlug*>(parent)->embedded_children; 1826 1827 std::vector<WindowContextChild*>::iterator pos 1828 = std::find(embedded_children.begin(), embedded_children.end(), this); 1829 if (pos != embedded_children.end()) { 1830 embedded_children.erase((pos)); 1831 } 1832 1833 WindowContextBase::process_destroy(); 1834 }