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