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