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