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