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