1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 #include "glass_window.h" 26 #include "glass_general.h" 27 #include "glass_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 44 #ifdef GLASS_GTK3 45 #include <gtk/gtkx.h> 46 #endif 47 48 #include <string.h> 49 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 #ifdef GLASS_GTK3 488 cairo_region_t *region = gdk_window_get_clip_region(gdk_window); 489 gdk_window_begin_paint_region(gdk_window, region); 490 #endif 491 492 cairo_t* context; 493 context = gdk_cairo_create(gdk_window); 494 495 cairo_surface_t* cairo_surface; 496 cairo_surface = cairo_image_surface_create_for_data( 497 (unsigned char*)data, 498 CAIRO_FORMAT_ARGB32, 499 width, height, width * 4); 500 501 applyShapeMask(data, width, height); 502 503 cairo_set_source_surface(context, cairo_surface, 0, 0); 504 505 cairo_set_operator (context, CAIRO_OPERATOR_SOURCE); 506 cairo_paint(context); 507 #ifdef GLASS_GTK3 508 gdk_window_end_paint(gdk_window); 509 cairo_region_destroy(region); 510 #endif 511 512 cairo_destroy(context); 513 cairo_surface_destroy(cairo_surface); 514 } 515 516 void WindowContextBase::add_child(WindowContextTop* child) { 517 children.insert(child); 518 gtk_window_set_transient_for(child->get_gtk_window(), this->get_gtk_window()); 519 } 520 521 void WindowContextBase::remove_child(WindowContextTop* child) { 522 children.erase(child); 523 gtk_window_set_transient_for(child->get_gtk_window(), NULL); 524 } 525 526 void WindowContextBase::show_or_hide_children(bool show) { 527 std::set<WindowContextTop*>::iterator it; 528 for (it = children.begin(); it != children.end(); ++it) { 529 (*it)->set_minimized(!show); 530 (*it)->show_or_hide_children(show); 531 } 532 } 533 534 void WindowContextBase::reparent_children(WindowContext* parent) { 535 std::set<WindowContextTop*>::iterator it; 536 for (it = children.begin(); it != children.end(); ++it) { 537 (*it)->set_owner(parent); 538 parent->add_child(*it); 539 } 540 children.clear(); 541 } 542 543 void WindowContextBase::set_visible(bool visible) { 544 if (visible) { 545 gtk_widget_show_all(gtk_widget); 546 } else { 547 gtk_widget_hide(gtk_widget); 548 if (jview && is_mouse_entered) { 549 is_mouse_entered = false; 550 mainEnv->CallVoidMethod(jview, jViewNotifyMouse, 551 com_sun_glass_events_MouseEvent_EXIT, 552 com_sun_glass_events_MouseEvent_BUTTON_NONE, 553 0, 0, 554 0, 0, 555 0, 556 JNI_FALSE, 557 JNI_FALSE); 558 CHECK_JNI_EXCEPTION(mainEnv) 559 } 560 } 561 } 562 563 bool WindowContextBase::is_visible() { 564 return gtk_widget_get_visible(gtk_widget); 565 } 566 567 bool WindowContextBase::set_view(jobject view) { 568 569 if (jview) { 570 mainEnv->DeleteGlobalRef(jview); 571 } 572 573 if (view) { 574 gint width, height; 575 jview = mainEnv->NewGlobalRef(view); 576 gtk_window_get_size(GTK_WINDOW(gtk_widget), &width, &height); 577 mainEnv->CallVoidMethod(view, jViewNotifyResize, width, height); 578 CHECK_JNI_EXCEPTION_RET(mainEnv, FALSE) 579 } else { 580 jview = NULL; 581 } 582 return TRUE; 583 } 584 585 bool WindowContextBase::grab_mouse_drag_focus() { 586 if (glass_gdk_mouse_devices_grab_with_cursor( 587 gdk_window, gdk_window_get_cursor(gdk_window), FALSE)) { 588 WindowContextBase::sm_mouse_drag_window = this; 589 return true; 590 } else { 591 return false; 592 } 593 } 594 595 void WindowContextBase::ungrab_mouse_drag_focus() { 596 WindowContextBase::sm_mouse_drag_window = NULL; 597 glass_gdk_mouse_devices_ungrab(); 598 if (WindowContextBase::sm_grab_window) { 599 WindowContextBase::sm_grab_window->grab_focus(); 600 } 601 } 602 603 bool WindowContextBase::grab_focus() { 604 if (WindowContextBase::sm_mouse_drag_window 605 || glass_gdk_mouse_devices_grab(gdk_window)) { 606 WindowContextBase::sm_grab_window = this; 607 return true; 608 } else { 609 return false; 610 } 611 } 612 613 void WindowContextBase::ungrab_focus() { 614 if (!WindowContextBase::sm_mouse_drag_window) { 615 glass_gdk_mouse_devices_ungrab(); 616 } 617 WindowContextBase::sm_grab_window = NULL; 618 619 if (jwindow) { 620 mainEnv->CallVoidMethod(jwindow, jWindowNotifyFocusUngrab); 621 CHECK_JNI_EXCEPTION(mainEnv) 622 } 623 } 624 625 void WindowContextBase::set_cursor(GdkCursor* cursor) { 626 if (!is_in_drag()) { 627 if (WindowContextBase::sm_mouse_drag_window) { 628 glass_gdk_mouse_devices_grab_with_cursor( 629 WindowContextBase::sm_mouse_drag_window->get_gdk_window(), cursor, FALSE); 630 } else if (WindowContextBase::sm_grab_window) { 631 glass_gdk_mouse_devices_grab_with_cursor( 632 WindowContextBase::sm_grab_window->get_gdk_window(), cursor, TRUE); 633 } 634 } 635 gdk_window_set_cursor(gdk_window, cursor); 636 } 637 638 void WindowContextBase::set_background(float r, float g, float b) { 639 #ifdef GLASS_GTK3 640 GdkRGBA rgba = {0, 0, 0, 1.}; 641 rgba.red = r; 642 rgba.green = g; 643 rgba.blue = b; 644 gdk_window_set_background_rgba(gdk_window, &rgba); 645 #else 646 GdkColor color; 647 color.red = (guint16) (r * 65535); 648 color.green = (guint16) (g * 65535); 649 color.blue = (guint16) (b * 65535); 650 gtk_widget_modify_bg(gtk_widget, GTK_STATE_NORMAL, &color); 651 #endif 652 } 653 654 WindowContextBase::~WindowContextBase() { 655 if (xim.ic) { 656 XDestroyIC(xim.ic); 657 xim.ic = NULL; 658 } 659 if (xim.im) { 660 XCloseIM(xim.im); 661 xim.im = NULL; 662 } 663 664 gtk_widget_destroy(gtk_widget); 665 } 666 667 ////////////////////////////// WindowContextTop ///////////////////////////////// 668 669 670 WindowContextTop::WindowContextTop(jobject _jwindow, WindowContext* _owner, long _screen, 671 WindowFrameType _frame_type, WindowType type, GdkWMFunction wmf) : 672 WindowContextBase(), 673 screen(_screen), 674 frame_type(_frame_type), 675 owner(_owner), 676 geometry(), 677 resizable(), 678 frame_extents_initialized(), 679 map_received(false), 680 location_assigned(false), 681 size_assigned(false), 682 on_top(false) 683 { 684 jwindow = mainEnv->NewGlobalRef(_jwindow); 685 686 gtk_widget = gtk_window_new(type == POPUP ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); 687 688 if (gchar* app_name = get_application_name()) { 689 gtk_window_set_wmclass(GTK_WINDOW(gtk_widget), app_name, app_name); 690 g_free(app_name); 691 } 692 693 if (owner) { 694 owner->add_child(this); 695 if (on_top_inherited()) { 696 gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE); 697 } 698 } 699 700 if (type == UTILITY) { 701 gtk_window_set_type_hint(GTK_WINDOW(gtk_widget), GDK_WINDOW_TYPE_HINT_UTILITY); 702 } 703 704 // glong xdisplay = (glong)mainEnv->GetStaticLongField(jApplicationCls, jApplicationDisplay); 705 // gint xscreenID = (gint)mainEnv->GetStaticIntField(jApplicationCls, jApplicationScreen); 706 glong xvisualID = (glong)mainEnv->GetStaticLongField(jApplicationCls, jApplicationVisualID); 707 708 if (xvisualID != 0) { 709 GdkVisual *visual = gdk_x11_screen_lookup_visual(gdk_screen_get_default(), xvisualID); 710 glass_gtk_window_configure_from_visual(gtk_widget, visual); 711 } 712 713 gtk_widget_set_size_request(gtk_widget, 0, 0); 714 gtk_widget_set_events(gtk_widget, GDK_ALL_EVENTS_MASK); 715 gtk_widget_set_app_paintable(gtk_widget, TRUE); 716 if (frame_type != TITLED) { 717 gtk_window_set_decorated(GTK_WINDOW(gtk_widget), FALSE); 718 } 719 720 glass_gtk_configure_transparency_and_realize(gtk_widget, frame_type == TRANSPARENT); 721 gtk_window_set_title(GTK_WINDOW(gtk_widget), ""); 722 723 gdk_window = gtk_widget_get_window(gtk_widget); 724 725 g_object_set_data_full(G_OBJECT(gdk_window), GDK_WINDOW_DATA_CONTEXT, this, NULL); 726 727 gdk_window_register_dnd(gdk_window); 728 729 gdk_windowManagerFunctions = wmf; 730 if (wmf) { 731 gdk_window_set_functions(gdk_window, wmf); 732 } 733 734 if (frame_type == TITLED) { 735 request_frame_extents(); 736 } 737 } 738 739 // Applied to a temporary full screen window to prevent sending events to Java 740 void WindowContextTop::detach_from_java() { 741 if (jview) { 742 mainEnv->DeleteGlobalRef(jview); 743 jview = NULL; 744 } 745 if (jwindow) { 746 mainEnv->DeleteGlobalRef(jwindow); 747 jwindow = NULL; 748 } 749 } 750 751 static GdkAtom 752 get_net_frame_extents_atom() { 753 static const char * extents_str = "_NET_FRAME_EXTENTS"; 754 return gdk_atom_intern(extents_str, TRUE); 755 } 756 757 void 758 WindowContextTop::request_frame_extents() { 759 Display *display = GDK_DISPLAY_XDISPLAY(gdk_window_get_display(gdk_window)); 760 Atom rfeAtom = XInternAtom(display, "_NET_REQUEST_FRAME_EXTENTS", True); 761 if (rfeAtom != None) { 762 XClientMessageEvent clientMessage; 763 memset(&clientMessage, 0, sizeof(clientMessage)); 764 765 clientMessage.type = ClientMessage; 766 clientMessage.window = GDK_WINDOW_XID(gdk_window); 767 clientMessage.message_type = rfeAtom; 768 clientMessage.format = 32; 769 770 XSendEvent(display, XDefaultRootWindow(display), False, 771 SubstructureRedirectMask | SubstructureNotifyMask, 772 (XEvent *) &clientMessage); 773 XFlush(display); 774 } 775 } 776 777 void WindowContextTop::activate_window() { 778 Display *display = GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdk_window)); 779 Atom navAtom = XInternAtom(display, "_NET_ACTIVE_WINDOW", True); 780 if (navAtom != None) { 781 XClientMessageEvent clientMessage; 782 memset(&clientMessage, 0, sizeof(clientMessage)); 783 784 clientMessage.type = ClientMessage; 785 clientMessage.window = GDK_WINDOW_XID(gdk_window); 786 clientMessage.message_type = navAtom; 787 clientMessage.format = 32; 788 clientMessage.data.l[0] = 1; 789 clientMessage.data.l[1] = gdk_x11_get_server_time(gdk_window); 790 clientMessage.data.l[2] = 0; 791 792 XSendEvent(display, XDefaultRootWindow(display), False, 793 SubstructureRedirectMask | SubstructureNotifyMask, 794 (XEvent *) &clientMessage); 795 XFlush(display); 796 } 797 } 798 799 void 800 WindowContextTop::initialize_frame_extents() { 801 int top, left, bottom, right; 802 if (get_frame_extents_property(&top, &left, &bottom, &right)) { 803 geometry.extents.top = top; 804 geometry.extents.left = left; 805 geometry.extents.bottom = bottom; 806 geometry.extents.right = right; 807 } 808 } 809 810 bool 811 WindowContextTop::get_frame_extents_property(int *top, int *left, 812 int *bottom, int *right) { 813 unsigned long *extents; 814 815 if (gdk_property_get(gdk_window, 816 get_net_frame_extents_atom(), 817 gdk_atom_intern("CARDINAL", FALSE), 818 0, 819 sizeof (unsigned long) * 4, 820 FALSE, 821 NULL, 822 NULL, 823 NULL, 824 (guchar**) & extents)) { 825 *left = extents [0]; 826 *right = extents [1]; 827 *top = extents [2]; 828 *bottom = extents [3]; 829 830 g_free(extents); 831 return true; 832 } 833 834 return false; 835 } 836 837 static int geometry_get_window_width(const WindowGeometry *windowGeometry) { 838 return (windowGeometry->final_width.type != BOUNDSTYPE_WINDOW) 839 ? windowGeometry->final_width.value 840 + windowGeometry->extents.left 841 + windowGeometry->extents.right 842 : windowGeometry->final_width.value; 843 } 844 845 static int geometry_get_window_height(const WindowGeometry *windowGeometry) { 846 return (windowGeometry->final_height.type != BOUNDSTYPE_WINDOW) 847 ? windowGeometry->final_height.value 848 + windowGeometry->extents.top 849 + windowGeometry->extents.bottom 850 : windowGeometry->final_height.value; 851 } 852 853 static int geometry_get_content_width(WindowGeometry *windowGeometry) { 854 return (windowGeometry->final_width.type != BOUNDSTYPE_CONTENT) 855 ? windowGeometry->final_width.value 856 - windowGeometry->extents.left 857 - windowGeometry->extents.right 858 : windowGeometry->final_width.value; 859 } 860 static int geometry_get_content_height(WindowGeometry *windowGeometry) { 861 return (windowGeometry->final_height.type != BOUNDSTYPE_CONTENT) 862 ? windowGeometry->final_height.value 863 - windowGeometry->extents.top 864 - windowGeometry->extents.bottom 865 : windowGeometry->final_height.value; 866 } 867 868 static int geometry_get_window_x(const WindowGeometry *windowGeometry) { 869 float value = windowGeometry->refx; 870 if (windowGeometry->gravity_x != 0) { 871 value -= geometry_get_window_width(windowGeometry) 872 * windowGeometry->gravity_x; 873 } 874 return (int) value; 875 } 876 877 static int geometry_get_window_y(const WindowGeometry *windowGeometry) { 878 float value = windowGeometry->refy; 879 if (windowGeometry->gravity_y != 0) { 880 value -= geometry_get_window_height(windowGeometry) 881 * windowGeometry->gravity_y; 882 } 883 return (int) value; 884 } 885 886 static void geometry_set_window_x(WindowGeometry *windowGeometry, int value) { 887 float newValue = value; 888 if (windowGeometry->gravity_x != 0) { 889 newValue += geometry_get_window_width(windowGeometry) 890 * windowGeometry->gravity_x; 891 } 892 windowGeometry->refx = newValue; 893 } 894 895 static void geometry_set_window_y(WindowGeometry *windowGeometry, int value) { 896 float newValue = value; 897 if (windowGeometry->gravity_y != 0) { 898 newValue += geometry_get_window_height(windowGeometry) 899 * windowGeometry->gravity_y; 900 } 901 windowGeometry->refy = newValue; 902 } 903 904 void WindowContextTop::process_net_wm_property() { 905 // Workaround for https://bugs.launchpad.net/unity/+bug/998073 906 907 static GdkAtom atom_atom = gdk_atom_intern_static_string("ATOM"); 908 static GdkAtom atom_net_wm_state = gdk_atom_intern_static_string("_NET_WM_STATE"); 909 static GdkAtom atom_net_wm_state_hidden = gdk_atom_intern_static_string("_NET_WM_STATE_HIDDEN"); 910 static GdkAtom atom_net_wm_state_above = gdk_atom_intern_static_string("_NET_WM_STATE_ABOVE"); 911 912 gint length; 913 914 glong* atoms = NULL; 915 916 if (gdk_property_get(gdk_window, atom_net_wm_state, atom_atom, 917 0, G_MAXLONG, FALSE, NULL, NULL, &length, (guchar**) &atoms)) { 918 919 bool is_hidden = false; 920 bool is_above = false; 921 for (gint i = 0; i < (gint)(length / sizeof(glong)); i++) { 922 if (atom_net_wm_state_hidden == (GdkAtom)atoms[i]) { 923 is_hidden = true; 924 } else if (atom_net_wm_state_above == (GdkAtom)atoms[i]) { 925 is_above = true; 926 } 927 } 928 929 g_free(atoms); 930 931 if (is_iconified != is_hidden) { 932 is_iconified = is_hidden; 933 934 notify_state((is_hidden) 935 ? com_sun_glass_events_WindowEvent_MINIMIZE 936 : com_sun_glass_events_WindowEvent_RESTORE); 937 } 938 939 notify_on_top(is_above); 940 } 941 } 942 943 void WindowContextTop::process_property_notify(GdkEventProperty* event) { 944 static GdkAtom atom_net_wm_state = gdk_atom_intern_static_string("_NET_WM_STATE"); 945 946 if (event->atom == atom_net_wm_state && event->window == gdk_window) { 947 process_net_wm_property(); 948 } 949 } 950 951 void WindowContextTop::process_configure(GdkEventConfigure* event) { 952 gint x, y, w, h; 953 bool updateWindowConstraints = false; 954 if (gtk_window_get_decorated(GTK_WINDOW(gtk_widget))) { 955 GdkRectangle frame; 956 gint top, left, bottom, right; 957 958 gdk_window_get_frame_extents(gdk_window, &frame); 959 gint contentX, contentY; 960 gdk_window_get_origin(gdk_window, &contentX, &contentY); 961 #ifdef GLASS_GTK3 962 gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h); 963 #else 964 gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h, NULL); 965 #endif 966 x = frame.x; 967 y = frame.y; 968 geometry.current_width = frame.width; 969 geometry.current_height = frame.height; 970 971 top = contentY - frame.y; 972 left = contentX - frame.x; 973 bottom = frame.y + frame.height - (contentY + h); 974 right = frame.x + frame.width - (contentX + w); 975 if (geometry.extents.top != top 976 || geometry.extents.left != left 977 || geometry.extents.bottom != bottom 978 || geometry.extents.right != right) { 979 updateWindowConstraints = true; 980 geometry.extents.top = top; 981 geometry.extents.left = left; 982 geometry.extents.bottom = bottom; 983 geometry.extents.right = right; 984 } 985 } else { 986 x = event->x; 987 y = event->y; 988 w = event->width; 989 h = event->height; 990 } 991 992 if (size_assigned && w <= 1 && h <= 1 && (geometry.final_width.value > 1 || 993 geometry.final_height.value > 1)) { 994 // skip artifact 995 return; 996 } 997 998 geometry.final_width.value = w; 999 geometry.final_width.type = BOUNDSTYPE_CONTENT; 1000 geometry.final_height.value = h; 1001 geometry.final_height.type = BOUNDSTYPE_CONTENT; 1002 geometry_set_window_x(&geometry, x); 1003 geometry_set_window_y(&geometry, y); 1004 1005 if (jview) { 1006 mainEnv->CallVoidMethod(jview, jViewNotifyResize, 1007 event->width, 1008 event->height); 1009 CHECK_JNI_EXCEPTION(mainEnv) 1010 mainEnv->CallVoidMethod(jview, jViewNotifyView, 1011 com_sun_glass_events_ViewEvent_MOVE); 1012 CHECK_JNI_EXCEPTION(mainEnv) 1013 } 1014 1015 if (jwindow) { 1016 mainEnv->CallVoidMethod(jwindow, jWindowNotifyResize, 1017 (is_maximized) 1018 ? com_sun_glass_events_WindowEvent_MAXIMIZE 1019 : com_sun_glass_events_WindowEvent_RESIZE, 1020 geometry.current_width, 1021 geometry.current_height); 1022 CHECK_JNI_EXCEPTION(mainEnv) 1023 1024 mainEnv->CallVoidMethod(jwindow, jWindowNotifyMove, x, y); 1025 CHECK_JNI_EXCEPTION(mainEnv) 1026 } 1027 1028 glong to_screen = getScreenPtrForLocation(x, y); 1029 if (to_screen != -1) { 1030 if (to_screen != screen) { 1031 if (jwindow) { 1032 //notify screen changed 1033 jobject jScreen = createJavaScreen(mainEnv, to_screen); 1034 mainEnv->CallVoidMethod(jwindow, jWindowNotifyMoveToAnotherScreen, jScreen); 1035 CHECK_JNI_EXCEPTION(mainEnv) 1036 } 1037 screen = to_screen; 1038 } 1039 } 1040 1041 if (resizable.request != REQUEST_NONE) { 1042 set_window_resizable(resizable.request == REQUEST_RESIZABLE); 1043 resizable.request = REQUEST_NONE; 1044 } else if (!resizable.value) { 1045 set_window_resizable(false); 1046 } else if (updateWindowConstraints) { 1047 update_window_constraints(); 1048 } 1049 } 1050 1051 void WindowContextTop::update_window_constraints() { 1052 if (resizable.value) { 1053 GdkGeometry geom = { 1054 (resizable.minw == -1) ? 1 1055 : resizable.minw - geometry.extents.left - geometry.extents.right, 1056 (resizable.minh == -1) ? 1 1057 : resizable.minh - geometry.extents.top - geometry.extents.bottom, 1058 (resizable.maxw == -1) ? 100000 1059 : resizable.maxw - geometry.extents.left - geometry.extents.right, 1060 (resizable.maxh == -1) ? 100000 1061 : resizable.maxh - geometry.extents.top - geometry.extents.bottom, 1062 0, 0, 0, 0, 0.0, 0.0, GDK_GRAVITY_NORTH_WEST 1063 }; 1064 gtk_window_set_geometry_hints(GTK_WINDOW(gtk_widget), NULL, &geom, 1065 static_cast<GdkWindowHints> (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); 1066 } 1067 } 1068 1069 void WindowContextTop::set_window_resizable(bool res) { 1070 if(!res) { 1071 int w = geometry_get_content_width(&geometry); 1072 int h = geometry_get_content_height(&geometry); 1073 if (w == -1 && h == -1) { 1074 gtk_window_get_size(GTK_WINDOW(gtk_widget), &w, &h); 1075 } 1076 GdkGeometry geom = {w, h, w, h, 0, 0, 0, 0, 0.0, 0.0, GDK_GRAVITY_NORTH_WEST}; 1077 gtk_window_set_geometry_hints(GTK_WINDOW(gtk_widget), NULL, &geom, 1078 static_cast<GdkWindowHints>(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); 1079 resizable.value = false; 1080 } else { 1081 resizable.value = true; 1082 update_window_constraints(); 1083 } 1084 } 1085 1086 void WindowContextTop::set_resizable(bool res) { 1087 resizable.prev = false; 1088 gint w, h; 1089 gtk_window_get_size(GTK_WINDOW(gtk_widget), &w, &h); 1090 if (map_received || w > 1 || h > 1) { 1091 set_window_resizable(res); 1092 } else { 1093 //Since window is not ready yet set only request for change of resizable. 1094 resizable.request = res ? REQUEST_RESIZABLE : REQUEST_NOT_RESIZABLE; 1095 } 1096 } 1097 1098 void WindowContextTop::set_visible(bool visible) 1099 { 1100 if (visible) { 1101 if (!size_assigned) { 1102 set_bounds(0, 0, false, false, 320, 200, -1, -1); 1103 } 1104 if (!location_assigned) { 1105 set_bounds(0, 0, true, true, -1, -1, -1, -1); 1106 } 1107 } 1108 WindowContextBase::set_visible(visible); 1109 } 1110 1111 void WindowContextTop::set_bounds(int x, int y, bool xSet, bool ySet, int w, int h, int cw, int ch) { 1112 if (!frame_extents_initialized && frame_type == TITLED) { 1113 initialize_frame_extents(); 1114 frame_extents_initialized = true; 1115 } 1116 1117 XWindowChanges windowChanges; 1118 unsigned int windowChangesMask = 0; 1119 if (w > 0) { 1120 geometry.final_width.value = w; 1121 geometry.final_width.type = BOUNDSTYPE_WINDOW; 1122 geometry.current_width = geometry_get_window_width(&geometry); 1123 windowChanges.width = geometry_get_content_width(&geometry); 1124 windowChangesMask |= CWWidth; 1125 } else if (cw > 0) { 1126 geometry.final_width.value = cw; 1127 geometry.final_width.type = BOUNDSTYPE_CONTENT; 1128 geometry.current_width = geometry_get_window_width(&geometry); 1129 windowChanges.width = geometry_get_content_width(&geometry); 1130 windowChangesMask |= CWWidth; 1131 } 1132 1133 if (h > 0) { 1134 geometry.final_height.value = h; 1135 geometry.final_height.type = BOUNDSTYPE_WINDOW; 1136 geometry.current_height = geometry_get_window_height(&geometry); 1137 windowChanges.height = geometry_get_content_height(&geometry); 1138 windowChangesMask |= CWHeight; 1139 } else if (ch > 0) { 1140 geometry.final_height.value = ch; 1141 geometry.final_height.type = BOUNDSTYPE_CONTENT; 1142 geometry.current_height = geometry_get_window_height(&geometry); 1143 windowChanges.height = geometry_get_content_height(&geometry); 1144 windowChangesMask |= CWHeight; 1145 } 1146 1147 if (xSet) { 1148 geometry.refx = x + geometry.current_width * geometry.gravity_x; 1149 windowChanges.x = geometry_get_window_x(&geometry); 1150 windowChangesMask |= CWX; 1151 1152 } else if ((geometry.gravity_x != 0) && (windowChangesMask & CWWidth)) { 1153 windowChanges.x = geometry_get_window_x(&geometry); 1154 windowChangesMask |= CWX; 1155 } 1156 1157 if (ySet) { 1158 geometry.refy = y + geometry.current_height * geometry.gravity_y; 1159 windowChanges.y = geometry_get_window_y(&geometry); 1160 windowChangesMask |= CWY; 1161 1162 } else if ((geometry.gravity_y != 0) && (windowChangesMask & CWHeight)) { 1163 windowChanges.y = geometry_get_window_y(&geometry); 1164 windowChangesMask |= CWY; 1165 } 1166 1167 if (xSet || ySet) location_assigned = true; 1168 if (w > 0 || h > 0 || cw > 0 || ch > 0) size_assigned = true; 1169 1170 window_configure(&windowChanges, windowChangesMask); 1171 1172 } 1173 1174 void WindowContextTop::process_map() { 1175 map_received = true; 1176 } 1177 1178 void WindowContextTop::window_configure(XWindowChanges *windowChanges, 1179 unsigned int windowChangesMask) { 1180 if (windowChangesMask == 0) { 1181 return; 1182 } 1183 1184 if (windowChangesMask & (CWX | CWY)) { 1185 gint newX, newY; 1186 gtk_window_get_position(GTK_WINDOW(gtk_widget), &newX, &newY); 1187 if (windowChangesMask & CWX) { 1188 newX = windowChanges->x; 1189 } 1190 if (windowChangesMask & CWY) { 1191 newY = windowChanges->y; 1192 } 1193 gtk_window_move(GTK_WINDOW(gtk_widget), newX, newY); 1194 } 1195 1196 if (windowChangesMask & (CWWidth | CWHeight)) { 1197 gint newWidth, newHeight; 1198 gtk_window_get_size(GTK_WINDOW(gtk_widget), &newWidth, &newHeight); 1199 1200 if (windowChangesMask & CWWidth) { 1201 newWidth = windowChanges->width; 1202 } 1203 if (windowChangesMask & CWHeight) { 1204 newHeight = windowChanges->height; 1205 } 1206 if (!resizable.value) { 1207 GdkGeometry geom; 1208 GdkWindowHints hints = (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); 1209 geom.min_width = geom.max_width = newWidth; 1210 geom.min_height = geom.max_height = newHeight; 1211 gtk_window_set_geometry_hints(GTK_WINDOW(gtk_widget), NULL, &geom, hints); 1212 } 1213 gtk_window_resize(GTK_WINDOW(gtk_widget), newWidth, newHeight); 1214 } 1215 } 1216 1217 void WindowContextTop::applyShapeMask(void* data, uint width, uint height) 1218 { 1219 if (frame_type != TRANSPARENT) { 1220 return; 1221 } 1222 1223 glass_window_apply_shape_mask(gtk_widget_get_window(gtk_widget), data, width, height); 1224 } 1225 1226 void WindowContextTop::ensure_window_size() { 1227 gint w, h; 1228 #ifdef GLASS_GTK3 1229 gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h); 1230 #else 1231 gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h, NULL); 1232 #endif 1233 if (size_assigned && (geometry.final_width.value != w 1234 || geometry.final_height.value != h)) { 1235 1236 gdk_window_resize(gdk_window, geometry.final_width.value, 1237 geometry.final_height.value); 1238 } 1239 } 1240 1241 void WindowContextTop::set_minimized(bool minimize) { 1242 is_iconified = minimize; 1243 if (minimize) { 1244 if (frame_type == TRANSPARENT) { 1245 // https://bugs.launchpad.net/ubuntu/+source/unity/+bug/1245571 1246 glass_window_reset_input_shape_mask(gtk_widget_get_window(gtk_widget)); 1247 } 1248 1249 if ((gdk_windowManagerFunctions & GDK_FUNC_MINIMIZE) == 0) { 1250 // in this case - the window manager will not support the programatic 1251 // request to iconify - so we need to disable this until we are restored. 1252 GdkWMFunction wmf = (GdkWMFunction)(gdk_windowManagerFunctions | GDK_FUNC_MINIMIZE); 1253 gdk_window_set_functions(gdk_window, wmf); 1254 } 1255 gtk_window_iconify(GTK_WINDOW(gtk_widget)); 1256 } else { 1257 gtk_window_deiconify(GTK_WINDOW(gtk_widget)); 1258 activate_window(); 1259 } 1260 } 1261 void WindowContextTop::set_maximized(bool maximize) { 1262 is_maximized = maximize; 1263 if (maximize) { 1264 ensure_window_size(); 1265 gtk_window_maximize(GTK_WINDOW(gtk_widget)); 1266 } else { 1267 gtk_window_unmaximize(GTK_WINDOW(gtk_widget)); 1268 } 1269 } 1270 1271 void WindowContextTop::enter_fullscreen() { 1272 ensure_window_size(); 1273 gtk_window_fullscreen(GTK_WINDOW(gtk_widget)); 1274 } 1275 1276 void WindowContextTop::exit_fullscreen() { 1277 gtk_window_unfullscreen(GTK_WINDOW(gtk_widget)); 1278 } 1279 1280 void WindowContextTop::request_focus() { 1281 gtk_window_present(GTK_WINDOW(gtk_widget)); 1282 } 1283 1284 void WindowContextTop::set_focusable(bool focusable) { 1285 gtk_window_set_accept_focus(GTK_WINDOW(gtk_widget), focusable ? TRUE : FALSE); 1286 } 1287 1288 void WindowContextTop::set_title(const char* title) { 1289 gtk_window_set_title(GTK_WINDOW(gtk_widget),title); 1290 } 1291 1292 void WindowContextTop::set_alpha(double alpha) { 1293 gtk_window_set_opacity(GTK_WINDOW(gtk_widget), (gdouble)alpha); 1294 } 1295 1296 void WindowContextTop::set_enabled(bool enabled) { 1297 if (enabled) { 1298 if (resizable.prev) { 1299 set_window_resizable(true); 1300 } 1301 } else { 1302 if (resizable.value) { 1303 set_window_resizable(false); 1304 resizable.prev = true; 1305 } else if (resizable.request == REQUEST_RESIZABLE) { 1306 resizable.request = REQUEST_NOT_RESIZABLE; 1307 resizable.prev = true; 1308 } 1309 } 1310 } 1311 1312 void WindowContextTop::set_minimum_size(int w, int h) { 1313 resizable.minw = w; 1314 resizable.minh = h; 1315 update_window_constraints(); 1316 } 1317 1318 void WindowContextTop::set_maximum_size(int w, int h) { 1319 resizable.maxw = w; 1320 resizable.maxh = h; 1321 update_window_constraints(); 1322 } 1323 1324 void WindowContextTop::set_icon(GdkPixbuf* pixbuf) { 1325 gtk_window_set_icon(GTK_WINDOW(gtk_widget), pixbuf); 1326 } 1327 1328 void WindowContextTop::restack(bool restack) { 1329 gdk_window_restack(gdk_window, NULL, restack ? TRUE : FALSE); 1330 } 1331 1332 void WindowContextTop::set_modal(bool modal, WindowContext* parent) { 1333 if (modal) { 1334 //gtk_window_set_type_hint(GTK_WINDOW(gtk_widget), GDK_WINDOW_TYPE_HINT_DIALOG); 1335 if (parent) { 1336 gtk_window_set_transient_for(GTK_WINDOW(gtk_widget), parent->get_gtk_window()); 1337 } 1338 } 1339 gtk_window_set_modal(GTK_WINDOW(gtk_widget), modal ? TRUE : FALSE); 1340 } 1341 1342 GtkWindow *WindowContextTop::get_gtk_window() { 1343 return GTK_WINDOW(gtk_widget); 1344 } 1345 1346 WindowFrameExtents WindowContextTop::get_frame_extents() { 1347 return geometry.extents; 1348 } 1349 1350 void WindowContextTop::set_gravity(float x, float y) { 1351 int oldX = geometry_get_window_x(&geometry); 1352 int oldY = geometry_get_window_y(&geometry); 1353 geometry.gravity_x = x; 1354 geometry.gravity_y = y; 1355 geometry_set_window_x(&geometry, oldX); 1356 geometry_set_window_y(&geometry, oldY); 1357 } 1358 1359 void WindowContextTop::update_ontop_tree(bool on_top) { 1360 bool effective_on_top = on_top || this->on_top; 1361 gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), effective_on_top ? TRUE : FALSE); 1362 for (std::set<WindowContextTop*>::iterator it = children.begin(); it != children.end(); ++it) { 1363 (*it)->update_ontop_tree(effective_on_top); 1364 } 1365 } 1366 1367 bool WindowContextTop::on_top_inherited() { 1368 WindowContext* o = owner; 1369 while (o) { 1370 WindowContextTop* topO = dynamic_cast<WindowContextTop*>(o); 1371 if (!topO) break; 1372 if (topO->on_top) { 1373 return true; 1374 } 1375 o = topO->owner; 1376 } 1377 return false; 1378 } 1379 1380 bool WindowContextTop::effective_on_top() { 1381 if (owner) { 1382 WindowContextTop* topO = dynamic_cast<WindowContextTop*>(owner); 1383 return (topO && topO->effective_on_top()) || on_top; 1384 } 1385 return on_top; 1386 } 1387 1388 void WindowContextTop::notify_on_top(bool top) { 1389 // Do not report effective (i.e. native) values to the FX, only if the user sets it manually 1390 if (top != effective_on_top() && jwindow) { 1391 if (on_top_inherited() && !top) { 1392 // Disallow user's "on top" handling on windows that inherited the property 1393 gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE); 1394 } else { 1395 on_top = top; 1396 update_ontop_tree(top); 1397 mainEnv->CallVoidMethod(jwindow, 1398 jWindowNotifyLevelChanged, 1399 top ? com_sun_glass_ui_Window_Level_FLOATING : com_sun_glass_ui_Window_Level_NORMAL); 1400 CHECK_JNI_EXCEPTION(mainEnv); 1401 } 1402 } 1403 } 1404 1405 void WindowContextTop::set_level(int level) { 1406 if (level == com_sun_glass_ui_Window_Level_NORMAL) { 1407 on_top = false; 1408 } else if (level == com_sun_glass_ui_Window_Level_FLOATING 1409 || level == com_sun_glass_ui_Window_Level_TOPMOST) { 1410 on_top = true; 1411 } 1412 // We need to emulate always on top behaviour on child windows 1413 1414 if (!on_top_inherited()) { 1415 update_ontop_tree(on_top); 1416 } 1417 } 1418 1419 void WindowContextTop::set_owner(WindowContext * owner_ctx) { 1420 owner = owner_ctx; 1421 } 1422 1423 void WindowContextTop::process_destroy() { 1424 if (owner) { 1425 owner->remove_child(this); 1426 } 1427 1428 WindowContextBase::process_destroy(); 1429 } 1430 1431 ////////////////////////////// WindowContextPlug //////////////////////////////// 1432 1433 static gboolean plug_configure(GtkWidget *widget, GdkEvent *event, gpointer user_data) { 1434 (void)widget; 1435 1436 if (event->type == GDK_CONFIGURE) { 1437 ((WindowContextPlug*)user_data)->process_gtk_configure(&event->configure); 1438 } 1439 return FALSE; 1440 } 1441 1442 WindowContextPlug::WindowContextPlug(jobject _jwindow, void* _owner) : 1443 WindowContextBase(), 1444 parent() 1445 { 1446 jwindow = mainEnv->NewGlobalRef(_jwindow); 1447 1448 WindowContext* parent = ((WindowContext*)JLONG_TO_PTR(_owner)); 1449 Window win = GDK_WINDOW_XID(parent->get_gdk_window()); 1450 gtk_widget = gtk_plug_new(win); 1451 1452 g_signal_connect(G_OBJECT(gtk_widget), "configure-event", G_CALLBACK(plug_configure), this); 1453 1454 gtk_widget_set_size_request(gtk_widget, 0, 0); 1455 gtk_widget_set_events(gtk_widget, GDK_ALL_EVENTS_MASK); 1456 gtk_widget_set_can_focus(GTK_WIDGET(gtk_widget), TRUE); 1457 gtk_widget_set_app_paintable(gtk_widget, TRUE); 1458 1459 gtk_widget_realize(gtk_widget); 1460 gdk_window = gtk_widget_get_window(gtk_widget); 1461 1462 g_object_set_data_full(G_OBJECT(gdk_window), GDK_WINDOW_DATA_CONTEXT, this, NULL); 1463 gdk_window_register_dnd(gdk_window); 1464 1465 gtk_container = gtk_fixed_new(); 1466 gtk_container_add (GTK_CONTAINER(gtk_widget), gtk_container); 1467 gtk_widget_realize(gtk_container); 1468 } 1469 1470 GtkWindow *WindowContextPlug::get_gtk_window() { 1471 return GTK_WINDOW(gtk_widget); 1472 } 1473 1474 void WindowContextPlug::process_configure(GdkEventConfigure* event) { 1475 (void)event; 1476 1477 //Note: process_gtk_configure is used, so there's no need to handle GDK events 1478 } 1479 1480 void WindowContextPlug::process_gtk_configure(GdkEventConfigure* event) { 1481 if (jview) { 1482 mainEnv->CallVoidMethod(jview, jViewNotifyResize, 1483 event->width, 1484 event->height); 1485 CHECK_JNI_EXCEPTION(mainEnv) 1486 } 1487 1488 mainEnv->CallVoidMethod(jwindow, jWindowNotifyResize, 1489 com_sun_glass_events_WindowEvent_RESIZE, 1490 event->width, 1491 event->height); 1492 CHECK_JNI_EXCEPTION(mainEnv) 1493 1494 if (!embedded_children.empty()) { 1495 WindowContextChild* child = embedded_children.back(); 1496 child->process_configure(event); 1497 } 1498 } 1499 1500 bool WindowContextPlug::set_view(jobject view) { 1501 // probably never called for applet window 1502 if (jview) { 1503 mainEnv->DeleteGlobalRef(jview); 1504 } 1505 1506 if (view) { 1507 gint width, height; 1508 jview = mainEnv->NewGlobalRef(view); 1509 gtk_window_get_size(GTK_WINDOW(gtk_widget), &width, &height); 1510 mainEnv->CallVoidMethod(view, jViewNotifyResize, width, height); 1511 CHECK_JNI_EXCEPTION_RET(mainEnv, FALSE) 1512 } else { 1513 jview = NULL; 1514 } 1515 return TRUE; 1516 } 1517 1518 void WindowContextPlug::window_configure(XWindowChanges *windowChanges, 1519 unsigned int windowChangesMask) { 1520 if (windowChangesMask == 0) { 1521 return; 1522 } 1523 1524 if (windowChangesMask & (CWX | CWY)) { 1525 gint newX, newY; 1526 gtk_window_get_position(GTK_WINDOW(gtk_widget), &newX, &newY); 1527 1528 if (windowChangesMask & CWX) { 1529 newX = windowChanges->x; 1530 } 1531 if (windowChangesMask & CWY) { 1532 newY = windowChanges->y; 1533 } 1534 gtk_window_move(GTK_WINDOW(gtk_widget), newX, newY); 1535 } 1536 1537 if (windowChangesMask & (CWWidth | CWHeight)) { 1538 gint newWidth, newHeight; 1539 gtk_window_get_size(GTK_WINDOW(gtk_widget), 1540 &newWidth, &newHeight); 1541 1542 if (windowChangesMask & CWWidth) { 1543 newWidth = windowChanges->width; 1544 } 1545 if (windowChangesMask & CWHeight) { 1546 newHeight = windowChanges->height; 1547 }; 1548 gtk_widget_set_size_request(gtk_widget, newWidth, newHeight); 1549 } 1550 } 1551 1552 void WindowContextPlug::set_bounds(int x, int y, bool xSet, bool ySet, int w, int h, int cw, int ch) { 1553 XWindowChanges windowChanges; 1554 unsigned int windowChangesMask = 0; 1555 1556 if (xSet) { 1557 windowChanges.x = x; 1558 windowChangesMask |= CWX; 1559 } 1560 1561 if (ySet) { 1562 windowChanges.y = y; 1563 windowChangesMask |= CWY; 1564 } 1565 1566 if (w > 0) { 1567 windowChanges.width = w; 1568 windowChangesMask |= CWWidth; 1569 } else if (cw > 0) { 1570 windowChanges.width = cw; 1571 windowChangesMask |= CWWidth; 1572 } 1573 1574 if (h > 0) { 1575 windowChanges.height = h; 1576 windowChangesMask |= CWHeight; 1577 } else if (ch > 0) { 1578 windowChanges.height = ch; 1579 windowChangesMask |= CWHeight; 1580 } 1581 1582 window_configure(&windowChanges, windowChangesMask); 1583 } 1584 ////////////////////////////// WindowContextChild //////////////////////////////// 1585 1586 void WindowContextChild::process_mouse_button(GdkEventButton* event) { 1587 WindowContextBase::process_mouse_button(event); 1588 // gtk_window_set_focus (GTK_WINDOW (gtk_widget_get_ancestor(gtk_widget, GTK_TYPE_WINDOW)), NULL); 1589 gtk_widget_grab_focus(gtk_widget); 1590 } 1591 1592 static gboolean child_focus_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data) 1593 { 1594 (void)widget; 1595 1596 WindowContext *ctx = (WindowContext *)user_data; 1597 ctx->process_focus(&event->focus_change); 1598 return TRUE; 1599 } 1600 1601 WindowContextChild::WindowContextChild(jobject _jwindow, 1602 void* _owner, 1603 GtkWidget *parent_widget, 1604 WindowContextPlug *parent_ctx) : 1605 WindowContextBase(), 1606 parent(), 1607 full_screen_window(), 1608 view() 1609 { 1610 (void)_owner; 1611 1612 jwindow = mainEnv->NewGlobalRef(_jwindow); 1613 gtk_widget = gtk_drawing_area_new(); 1614 parent = parent_ctx; 1615 1616 glong xvisualID = (glong) mainEnv->GetStaticLongField(jApplicationCls, jApplicationVisualID); 1617 1618 if (xvisualID != 0) { 1619 GdkVisual *visual = gdk_x11_screen_lookup_visual(gdk_screen_get_default(), xvisualID); 1620 glass_gtk_window_configure_from_visual(gtk_widget, visual); 1621 } 1622 1623 gtk_widget_set_events(gtk_widget, GDK_ALL_EVENTS_MASK); 1624 gtk_widget_set_can_focus(GTK_WIDGET(gtk_widget), TRUE); 1625 gtk_widget_set_app_paintable(gtk_widget, TRUE); 1626 gtk_container_add (GTK_CONTAINER(parent_widget), gtk_widget); 1627 gtk_widget_realize(gtk_widget); 1628 gdk_window = gtk_widget_get_window(gtk_widget); 1629 g_object_set_data_full(G_OBJECT(gdk_window), GDK_WINDOW_DATA_CONTEXT, this, NULL); 1630 gdk_window_register_dnd(gdk_window); 1631 g_signal_connect(gtk_widget, "focus-in-event", G_CALLBACK(child_focus_callback), this); 1632 g_signal_connect(gtk_widget, "focus-out-event", G_CALLBACK(child_focus_callback), this); 1633 } 1634 1635 void WindowContextChild::set_visible(bool visible) { 1636 std::vector<WindowContextChild*> &embedded_children = 1637 dynamic_cast<WindowContextPlug*>(parent)->embedded_children; 1638 1639 if (visible) { 1640 embedded_children.push_back(this); 1641 } else { 1642 std::vector<WindowContextChild*>::iterator pos 1643 = std::find(embedded_children.begin(), embedded_children.end(), this); 1644 if (pos != embedded_children.end()) { 1645 embedded_children.erase((pos)); 1646 } 1647 } 1648 1649 WindowContextBase::set_visible(visible); 1650 } 1651 1652 GtkWindow *WindowContextChild::get_gtk_window() { 1653 return GTK_WINDOW(gtk_widget_get_ancestor(gtk_widget, GTK_TYPE_WINDOW)); 1654 } 1655 1656 void WindowContextChild::process_configure(GdkEventConfigure* event) { 1657 if (jview) { 1658 mainEnv->CallVoidMethod(jview, jViewNotifyResize, 1659 event->width, 1660 event->height); 1661 CHECK_JNI_EXCEPTION(mainEnv) 1662 } 1663 1664 gtk_widget_set_size_request(gtk_widget, event->width, event->height); 1665 1666 if (jwindow) { 1667 mainEnv->CallVoidMethod(jwindow, jWindowNotifyResize, 1668 com_sun_glass_events_WindowEvent_RESIZE, 1669 event->width, 1670 event->height); 1671 CHECK_JNI_EXCEPTION(mainEnv) 1672 } 1673 } 1674 1675 bool WindowContextChild::set_view(jobject view) { 1676 if (jview) { 1677 mainEnv->DeleteGlobalRef(jview); 1678 } 1679 1680 if (view) { 1681 gint width, height; 1682 jview = mainEnv->NewGlobalRef(view); 1683 GtkAllocation ws; 1684 gtk_widget_get_allocation(gtk_widget, &ws); 1685 width = ws.width; 1686 height = ws.height; 1687 mainEnv->CallVoidMethod(view, jViewNotifyResize, width, height); 1688 CHECK_JNI_EXCEPTION_RET(mainEnv, FALSE) 1689 } else { 1690 jview = NULL; 1691 } 1692 return TRUE; 1693 } 1694 1695 void WindowContextChild::set_bounds(int x, int y, bool xSet, bool ySet, int w, int h, int cw, int ch) { 1696 1697 if (x > 0 || y > 0 || xSet || ySet) { 1698 gint newX, newY; 1699 gdk_window_get_origin(gdk_window, &newX, &newY); 1700 if (jwindow) { 1701 mainEnv->CallVoidMethod(jwindow, 1702 jWindowNotifyMove, 1703 newX, newY); 1704 CHECK_JNI_EXCEPTION(mainEnv) 1705 } 1706 } 1707 1708 // As we have no frames, there's no difference between the calls 1709 if ((cw | ch) > 0) { 1710 w = cw; h = ch; 1711 } 1712 1713 if (w > 0 || h > 0) { 1714 gint newWidth, newHeight; 1715 GtkAllocation ws; 1716 gtk_widget_get_allocation(gtk_widget, &ws); 1717 newWidth = ws.width; 1718 newHeight = ws.height; 1719 1720 if (w > 0) { 1721 newWidth = w; 1722 } 1723 if (h > 0) { 1724 newHeight = h; 1725 } 1726 gtk_widget_set_size_request(gtk_widget, newWidth, newHeight); 1727 // FIXME: hack to set correct size to view 1728 if (jview) { 1729 mainEnv->CallVoidMethod(jview, 1730 jViewNotifyResize, 1731 newWidth, newHeight); 1732 CHECK_JNI_EXCEPTION(mainEnv) 1733 } 1734 } 1735 } 1736 1737 int WindowContextChild::getEmbeddedX() { 1738 int x; 1739 gdk_window_get_origin(gdk_window, &x, NULL); 1740 return x; 1741 } 1742 1743 int WindowContextChild::getEmbeddedY() { 1744 int y; 1745 gdk_window_get_origin(gdk_window, NULL, &y); 1746 return y; 1747 1748 } 1749 1750 void WindowContextChild::restack(bool toFront) { 1751 std::vector<WindowContextChild*> &embedded_children = 1752 dynamic_cast<WindowContextPlug*>(parent)->embedded_children; 1753 1754 std::vector<WindowContextChild*>::iterator pos 1755 = std::find(embedded_children.begin(), embedded_children.end(), this); 1756 1757 embedded_children.erase(pos); 1758 if (toFront) { 1759 embedded_children.push_back(this); 1760 } else { 1761 embedded_children.insert(embedded_children.begin(), this); 1762 } 1763 1764 gdk_window_restack(gdk_window, NULL, toFront ? TRUE : FALSE); 1765 } 1766 1767 void WindowContextChild::enter_fullscreen() { 1768 if (full_screen_window) { 1769 return; 1770 } 1771 1772 full_screen_window = new WindowContextTop(jwindow, NULL, 0L, UNTITLED, 1773 NORMAL, (GdkWMFunction) 0); 1774 int x, y, w, h; 1775 gdk_window_get_origin(gdk_window, &x, &y); 1776 #ifdef GLASS_GTK3 1777 gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h); 1778 #else 1779 gdk_window_get_geometry(gdk_window, NULL, NULL, &w, &h, NULL); 1780 #endif 1781 full_screen_window->set_bounds(x, y, true, true, w, h, -1, -1); 1782 1783 if (WindowContextBase::sm_grab_window == this) { 1784 ungrab_focus(); 1785 } 1786 1787 reparent_children(full_screen_window); 1788 1789 full_screen_window->set_visible(true); 1790 full_screen_window->enter_fullscreen(); 1791 1792 if (jwindow) { 1793 mainEnv->CallVoidMethod(jwindow, jWindowNotifyDelegatePtr, (jlong)full_screen_window); 1794 CHECK_JNI_EXCEPTION(mainEnv) 1795 } 1796 1797 if (jview) { 1798 this->view = (GlassView*)mainEnv->GetLongField(jview, jViewPtr); 1799 1800 this->view->current_window = full_screen_window; 1801 this->view->embedded_window = this; 1802 full_screen_window->set_view(jview); 1803 this->set_view(NULL); 1804 } 1805 } 1806 1807 void WindowContextChild::exit_fullscreen() { 1808 if (!full_screen_window) { 1809 return; 1810 } 1811 1812 if (WindowContextBase::sm_grab_window == this) { 1813 ungrab_focus(); 1814 } 1815 1816 full_screen_window->reparent_children(this); 1817 1818 mainEnv->CallVoidMethod(jwindow, jWindowNotifyDelegatePtr, (jlong)NULL); 1819 CHECK_JNI_EXCEPTION(mainEnv) 1820 1821 if (this->view) { 1822 this->view->current_window = this; 1823 this->view->embedded_window = NULL; 1824 } 1825 this->set_view(full_screen_window->get_jview()); 1826 1827 full_screen_window->detach_from_java(); 1828 1829 full_screen_window->set_view(NULL); 1830 1831 full_screen_window->set_visible(false); 1832 1833 destroy_and_delete_ctx(full_screen_window); 1834 full_screen_window = NULL; 1835 this->view = NULL; 1836 } 1837 1838 void WindowContextChild::process_destroy() { 1839 if (full_screen_window) { 1840 destroy_and_delete_ctx(full_screen_window); 1841 } 1842 1843 std::vector<WindowContextChild*> &embedded_children = 1844 dynamic_cast<WindowContextPlug*>(parent)->embedded_children; 1845 1846 std::vector<WindowContextChild*>::iterator pos 1847 = std::find(embedded_children.begin(), embedded_children.end(), this); 1848 if (pos != embedded_children.end()) { 1849 embedded_children.erase((pos)); 1850 } 1851 1852 WindowContextBase::process_destroy(); 1853 }