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