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