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