1 /*
   2  * Copyright (c) 2002, 2010, 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 package sun.awt.X11;
  26 
  27 import java.awt.*;
  28 
  29 import java.awt.event.ComponentEvent;
  30 import java.awt.event.InvocationEvent;
  31 import java.awt.event.WindowEvent;
  32 
  33 import sun.util.logging.PlatformLogger;
  34 
  35 import sun.awt.AWTAccessor;
  36 import sun.awt.SunToolkit;
  37 
  38 abstract class XDecoratedPeer extends XWindowPeer {
  39     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XDecoratedPeer");
  40     private static final PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XDecoratedPeer");
  41     private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.X11.focus.XDecoratedPeer");
  42     private static final PlatformLogger iconLog = PlatformLogger.getLogger("sun.awt.X11.icon.XDecoratedPeer");
  43 
  44     // Set to true when we get the first ConfigureNotify after being
  45     // reparented - indicates that WM has adopted the top-level.
  46     boolean configure_seen;
  47     boolean insets_corrected;
  48 
  49     XIconWindow iconWindow;
  50     WindowDimensions dimensions;
  51     XContentWindow content;
  52     Insets currentInsets;
  53     XFocusProxyWindow focusProxy;
  54 
  55     XDecoratedPeer(Window target) {
  56         super(target);
  57     }
  58 
  59     XDecoratedPeer(XCreateWindowParams params) {
  60         super(params);
  61     }
  62 
  63     public long getShell() {
  64         return window;
  65     }
  66 
  67     public long getContentWindow() {
  68         return (content == null) ? window : content.getWindow();
  69     }
  70 
  71     void preInit(XCreateWindowParams params) {
  72         super.preInit(params);
  73         winAttr.initialFocus = true;
  74 
  75         currentInsets = new Insets(0,0,0,0);
  76         applyGuessedInsets();
  77 
  78         Rectangle bounds = (Rectangle)params.get(BOUNDS);
  79         dimensions = new WindowDimensions(bounds, getRealInsets(), false);
  80         params.put(BOUNDS, dimensions.getClientRect());
  81         insLog.fine("Initial dimensions {0}", dimensions);
  82 
  83         // Deny default processing of these events on the shell - proxy will take care of
  84         // them instead
  85         Long eventMask = (Long)params.get(EVENT_MASK);
  86         params.add(EVENT_MASK, Long.valueOf(eventMask.longValue() & ~(XConstants.FocusChangeMask | XConstants.KeyPressMask | XConstants.KeyReleaseMask)));
  87     }
  88 
  89     void postInit(XCreateWindowParams params) {
  90         // The size hints must be set BEFORE mapping the window (see 6895647)
  91         updateSizeHints(dimensions);
  92 
  93         // The super method maps the window if it's visible on the shared level
  94         super.postInit(params);
  95 
  96         // The lines that follow need to be in a postInit, so they
  97         // happen after the X window is created.
  98         initResizability();
  99         XWM.requestWMExtents(getWindow());
 100 
 101         content = XContentWindow.createContent(this);
 102 
 103         if (warningWindow != null) {
 104             warningWindow.toFront();
 105         }
 106         focusProxy = createFocusProxy();
 107     }
 108 
 109     void setIconHints(java.util.List<XIconInfo> icons) {
 110         if (!XWM.getWM().setNetWMIcon(this, icons)) {
 111             if (icons.size() > 0) {
 112                 if (iconWindow == null) {
 113                     iconWindow = new XIconWindow(this);
 114                 }
 115                 iconWindow.setIconImages(icons);
 116             }
 117         }
 118     }
 119 
 120     public void updateMinimumSize() {
 121         super.updateMinimumSize();
 122         updateMinSizeHints();
 123     }
 124 
 125     private void updateMinSizeHints() {
 126         if (isResizable()) {
 127             Dimension minimumSize = getTargetMinimumSize();
 128             if (minimumSize != null) {
 129                 Insets insets = getRealInsets();
 130                 int minWidth = minimumSize.width - insets.left - insets.right;
 131                 int minHeight = minimumSize.height - insets.top - insets.bottom;
 132                 if (minWidth < 0) minWidth = 0;
 133                 if (minHeight < 0) minHeight = 0;
 134                 setSizeHints(XUtilConstants.PMinSize | (isLocationByPlatform()?0:(XUtilConstants.PPosition | XUtilConstants.USPosition)),
 135                              getX(), getY(), minWidth, minHeight);
 136                 if (isVisible()) {
 137                     Rectangle bounds = getShellBounds();
 138                     int nw = (bounds.width < minWidth) ? minWidth : bounds.width;
 139                     int nh = (bounds.height < minHeight) ? minHeight : bounds.height;
 140                     if (nw != bounds.width || nh != bounds.height) {
 141                         setShellSize(new Rectangle(0, 0, nw, nh));
 142                     }
 143                 }
 144             } else {
 145                 boolean isMinSizeSet = isMinSizeSet();
 146                 XWM.removeSizeHints(this, XUtilConstants.PMinSize);
 147                 /* Some WMs need remap to redecorate the window */
 148                 if (isMinSizeSet && isShowing() && XWM.needRemap(this)) {
 149                     /*
 150                      * Do the re/mapping at the Xlib level.  Since we essentially
 151                      * work around a WM bug we don't want this hack to be exposed
 152                      * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
 153                      */
 154                     xSetVisible(false);
 155                     XToolkit.XSync();
 156                     xSetVisible(true);
 157                 }
 158             }
 159         }
 160     }
 161 
 162     XFocusProxyWindow createFocusProxy() {
 163         return new XFocusProxyWindow(this);
 164     }
 165 
 166     protected XAtomList getWMProtocols() {
 167         XAtomList protocols = super.getWMProtocols();
 168         protocols.add(wm_delete_window);
 169         protocols.add(wm_take_focus);
 170         return protocols;
 171     }
 172 
 173     public Graphics getGraphics() {
 174         AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
 175         return getGraphics(content.surfaceData,
 176                            compAccessor.getForeground(target),
 177                            compAccessor.getBackground(target),
 178                            compAccessor.getFont(target));
 179     }
 180 
 181     public void setTitle(String title) {
 182         if (log.isLoggable(PlatformLogger.FINE)) log.fine("Title is " + title);
 183         winAttr.title = title;
 184         updateWMName();
 185     }
 186 
 187     protected String getWMName() {
 188         if (winAttr.title == null || winAttr.title.trim().equals("")) {
 189             return " ";
 190         } else {
 191             return winAttr.title;
 192         }
 193     }
 194 
 195     void updateWMName() {
 196         super.updateWMName();
 197         String name = getWMName();
 198         XToolkit.awtLock();
 199         try {
 200             if (name == null || name.trim().equals("")) {
 201                 name = "Java";
 202             }
 203             XAtom iconNameAtom = XAtom.get(XAtom.XA_WM_ICON_NAME);
 204             iconNameAtom.setProperty(getWindow(), name);
 205             XAtom netIconNameAtom = XAtom.get("_NET_WM_ICON_NAME");
 206             netIconNameAtom.setPropertyUTF8(getWindow(), name);
 207         } finally {
 208             XToolkit.awtUnlock();
 209         }
 210     }
 211 
 212     // NOTE: This method may be called by privileged threads.
 213     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 214     public void handleIconify() {
 215         postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_ICONIFIED));
 216     }
 217 
 218     // NOTE: This method may be called by privileged threads.
 219     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 220     public void handleDeiconify() {
 221         postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED));
 222     }
 223 
 224     public void handleFocusEvent(XEvent xev) {
 225         super.handleFocusEvent(xev);
 226         XFocusChangeEvent xfe = xev.get_xfocus();
 227 
 228         // If we somehow received focus events forward it instead to proxy
 229         // FIXME: Shouldn't we instead check for inferrior?
 230         focusLog.finer("Received focus event on shell: " + xfe);
 231 //         focusProxy.xRequestFocus();
 232    }
 233 
 234 /***************************************************************************************
 235  *                             I N S E T S   C O D E
 236  **************************************************************************************/
 237 
 238     protected boolean isInitialReshape() {
 239         return false;
 240     }
 241 
 242     private static Insets difference(Insets i1, Insets i2) {
 243         return new Insets(i1.top-i2.top, i1.left - i2.left, i1.bottom-i2.bottom, i1.right-i2.right);
 244     }
 245 
 246     private static boolean isNull(Insets i) {
 247         return (i == null) || ((i.left | i.top | i.right | i.bottom) == 0);
 248     }
 249 
 250     private static Insets copy(Insets i) {
 251         return new Insets(i.top, i.left, i.bottom, i.right);
 252     }
 253 
 254     // insets which we get from WM (e.g from _NET_FRAME_EXTENTS)
 255     private Insets wm_set_insets;
 256 
 257     private Insets getWMSetInsets(XAtom changedAtom) {
 258         if (isEmbedded()) {
 259             return null;
 260         }
 261 
 262         if (wm_set_insets != null) {
 263             return wm_set_insets;
 264         }
 265 
 266         if (changedAtom == null) {
 267             wm_set_insets = XWM.getInsetsFromExtents(getWindow());
 268         } else {
 269             wm_set_insets = XWM.getInsetsFromProp(getWindow(), changedAtom);
 270         }
 271 
 272         insLog.finer("FRAME_EXTENTS: {0}", wm_set_insets);
 273 
 274         if (wm_set_insets != null) {
 275             wm_set_insets = copy(wm_set_insets);
 276         }
 277         return wm_set_insets;
 278     }
 279 
 280     private void resetWMSetInsets() {
 281         wm_set_insets = null;
 282     }
 283 
 284     public void handlePropertyNotify(XEvent xev) {
 285         super.handlePropertyNotify(xev);
 286 
 287         XPropertyEvent ev = xev.get_xproperty();
 288         if (ev.get_atom() == XWM.XA_KDE_NET_WM_FRAME_STRUT.getAtom()
 289             || ev.get_atom() == XWM.XA_NET_FRAME_EXTENTS.getAtom())
 290         {
 291             getWMSetInsets(XAtom.get(ev.get_atom()));
 292         }
 293     }
 294 
 295     long reparent_serial = 0;
 296 
 297     public void handleReparentNotifyEvent(XEvent xev) {
 298         XReparentEvent  xe = xev.get_xreparent();
 299         if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine(xe.toString());
 300         reparent_serial = xe.get_serial();
 301         XToolkit.awtLock();
 302         try {
 303             long root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
 304 
 305             if (isEmbedded()) {
 306                 setReparented(true);
 307                 insets_corrected = true;
 308                 return;
 309             }
 310             Component t = (Component)target;
 311             if (getDecorations() == XWindowAttributesData.AWT_DECOR_NONE) {
 312                 setReparented(true);
 313                 insets_corrected = true;
 314                 reshape(dimensions, SET_SIZE, false);
 315             } else if (xe.get_parent() == root) {
 316                 configure_seen = false;
 317                 insets_corrected = false;
 318 
 319                 /*
 320                  * We can be repareted to root for two reasons:
 321                  *   . setVisible(false)
 322                  *   . WM exited
 323                  */
 324                 if (isVisible()) { /* WM exited */
 325                     /* Work around 4775545 */
 326                     XWM.getWM().unshadeKludge(this);
 327                     insLog.fine("- WM exited");
 328                 } else {
 329                     insLog.fine(" - reparent due to hide");
 330                 }
 331             } else { /* reparented to WM frame, figure out our insets */
 332                 setReparented(true);
 333                 insets_corrected = false;
 334 
 335                 // Check if we have insets provided by the WM
 336                 Insets correctWM = getWMSetInsets(null);
 337                 if (correctWM != null) {
 338                     insLog.finer("wm-provided insets {0}", correctWM);
 339                     // If these insets are equal to our current insets - no actions are necessary
 340                     Insets dimInsets = dimensions.getInsets();
 341                     if (correctWM.equals(dimInsets)) {
 342                         insLog.finer("Insets are the same as estimated - no additional reshapes necessary");
 343                         no_reparent_artifacts = true;
 344                         insets_corrected = true;
 345                         applyGuessedInsets();
 346                         return;
 347                     }
 348                 } else {
 349                     correctWM = XWM.getWM().getInsets(this, xe.get_window(), xe.get_parent());
 350 
 351                     if (correctWM != null) {
 352                         insLog.finer("correctWM {0}", correctWM);
 353                     } else {
 354                         insLog.finer("correctWM insets are not available, waiting for configureNotify");
 355                     }
 356                 }
 357 
 358                 if (correctWM != null) {
 359                     handleCorrectInsets(correctWM);
 360                 }
 361             }
 362         } finally {
 363             XToolkit.awtUnlock();
 364         }
 365     }
 366 
 367     protected void handleCorrectInsets(Insets correctWM) {
 368         XToolkit.awtLock();
 369         try {
 370             /*
 371              * Ok, now see if we need adjust window size because
 372              * initial insets were wrong (most likely they were).
 373              */
 374             Insets correction = difference(correctWM, currentInsets);
 375             insLog.finest("Corrention {0}", correction);
 376             if (!isNull(correction)) {
 377                 currentInsets = copy(correctWM);
 378                 applyGuessedInsets();
 379 
 380                 //Fix for 6318109: PIT: Min Size is not honored properly when a
 381                 //smaller size is specified in setSize(), XToolkit
 382                 //update minimum size hints
 383                 updateMinSizeHints();
 384             }
 385             if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Dimensions before reparent: " + dimensions);
 386 
 387             dimensions.setInsets(getRealInsets());
 388             insets_corrected = true;
 389 
 390             if (isMaximized()) {
 391                 return;
 392             }
 393 
 394             /*
 395              * If this window has been sized by a pack() we need
 396              * to keep the interior geometry intact.  Since pack()
 397              * computed width and height with wrong insets, we
 398              * must adjust the target dimensions appropriately.
 399              */
 400             if ((getHints().get_flags() & (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0) {
 401                 reshape(dimensions, SET_BOUNDS, false);
 402             } else {
 403                 reshape(dimensions, SET_SIZE, false);
 404             }
 405         } finally {
 406             XToolkit.awtUnlock();
 407         }
 408     }
 409 
 410     public void handleMoved(WindowDimensions dims) {
 411         Point loc = dims.getLocation();
 412         AWTAccessor.getComponentAccessor().setLocation((Component)target, loc.x, loc.y);
 413         postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
 414     }
 415 
 416 
 417     protected Insets guessInsets() {
 418         if (isEmbedded() || isTargetUndecorated()) {
 419             return new Insets(0, 0, 0, 0);
 420         } else {
 421             if (!isNull(currentInsets)) {
 422                 /* insets were set on wdata by System Properties */
 423                 return copy(currentInsets);
 424             } else {
 425                 Insets res = getWMSetInsets(null);
 426                 if (res == null) {
 427                     res = XWM.getWM().guessInsets(this);
 428                 }
 429                 return res;
 430             }
 431         }
 432     }
 433 
 434     private void applyGuessedInsets() {
 435         Insets guessed = guessInsets();
 436         currentInsets = copy(guessed);
 437     }
 438 
 439     public void revalidate() {
 440         XToolkit.executeOnEventHandlerThread(target, new Runnable() {
 441                 public void run() {
 442                     target.invalidate();
 443                     target.validate();
 444                 }
 445             });
 446     }
 447 
 448     Insets getRealInsets() {
 449         if (isNull(currentInsets)) {
 450             applyGuessedInsets();
 451         }
 452         return currentInsets;
 453     }
 454 
 455     public Insets getInsets() {
 456         Insets in = copy(getRealInsets());
 457         in.top += getMenuBarHeight();
 458         if (insLog.isLoggable(PlatformLogger.FINEST)) {
 459             insLog.finest("Get insets returns {0}", in);
 460         }
 461         return in;
 462     }
 463 
 464     boolean gravityBug() {
 465         return XWM.configureGravityBuggy();
 466     }
 467 
 468     // The height of area used to display current active input method
 469     int getInputMethodHeight() {
 470         return 0;
 471     }
 472 
 473     void updateSizeHints(WindowDimensions dims) {
 474         Rectangle rec = dims.getClientRect();
 475         checkShellRect(rec);
 476         updateSizeHints(rec.x, rec.y, rec.width, rec.height);
 477     }
 478 
 479     void updateSizeHints() {
 480         updateSizeHints(dimensions);
 481     }
 482 
 483     // Coordinates are that of the target
 484     // Called only on Toolkit thread
 485     public void reshape(WindowDimensions newDimensions, int op,
 486                         boolean userReshape)
 487     {
 488         if (insLog.isLoggable(PlatformLogger.FINE)) {
 489             insLog.fine("Reshaping " + this + " to " + newDimensions + " op " + op + " user reshape " + userReshape);
 490         }
 491         if (userReshape) {
 492             // We handle only userReshape == true cases. It means that
 493             // if the window manager or any other part of the windowing
 494             // system sets inappropriate size for this window, we can
 495             // do nothing but accept it.
 496             Rectangle newBounds = newDimensions.getBounds();
 497             Insets insets = newDimensions.getInsets();
 498             // Inherit isClientSizeSet from newDimensions
 499             if (newDimensions.isClientSizeSet()) {
 500                 newBounds = new Rectangle(newBounds.x, newBounds.y,
 501                                           newBounds.width - insets.left - insets.right,
 502                                           newBounds.height - insets.top - insets.bottom);
 503             }
 504             newDimensions = new WindowDimensions(newBounds, insets, newDimensions.isClientSizeSet());
 505         }
 506         XToolkit.awtLock();
 507         try {
 508             if (!isReparented() || !isVisible()) {
 509                 insLog.fine("- not reparented({0}) or not visible({1}), default reshape",
 510                            Boolean.valueOf(isReparented()), Boolean.valueOf(visible));
 511 
 512                 // Fix for 6323293.
 513                 // This actually is needed to preserve compatibility with previous releases -
 514                 // some of licensees are expecting componentMoved event on invisible one while
 515                 // its location changes.
 516                 Point oldLocation = getLocation();
 517 
 518                 Point newLocation = new Point(AWTAccessor.getComponentAccessor().getX((Component)target),
 519                                               AWTAccessor.getComponentAccessor().getY((Component)target));
 520 
 521                 if (!newLocation.equals(oldLocation)) {
 522                     handleMoved(newDimensions);
 523                 }
 524 
 525                 dimensions = new WindowDimensions(newDimensions);
 526                 updateSizeHints(dimensions);
 527                 Rectangle client = dimensions.getClientRect();
 528                 checkShellRect(client);
 529                 setShellBounds(client);
 530                 if (content != null &&
 531                     !content.getSize().equals(newDimensions.getSize()))
 532                 {
 533                     reconfigureContentWindow(newDimensions);
 534                 }
 535                 return;
 536             }
 537 
 538             int wm = XWM.getWMID();
 539             updateChildrenSizes();
 540             applyGuessedInsets();
 541 
 542             Rectangle shellRect = newDimensions.getClientRect();
 543 
 544             if (gravityBug()) {
 545                 Insets in = newDimensions.getInsets();
 546                 shellRect.translate(in.left, in.top);
 547             }
 548 
 549             if ((op & NO_EMBEDDED_CHECK) == 0 && isEmbedded()) {
 550                 shellRect.setLocation(0, 0);
 551             }
 552 
 553             checkShellRectSize(shellRect);
 554             if (!isEmbedded()) {
 555                 checkShellRectPos(shellRect);
 556             }
 557 
 558             op = op & ~NO_EMBEDDED_CHECK;
 559 
 560             if (op == SET_LOCATION) {
 561                 setShellPosition(shellRect);
 562             } else if (isResizable()) {
 563                 if (op == SET_BOUNDS) {
 564                     setShellBounds(shellRect);
 565                 } else {
 566                     setShellSize(shellRect);
 567                 }
 568             } else {
 569                 XWM.setShellNotResizable(this, newDimensions, shellRect, true);
 570                 if (op == SET_BOUNDS) {
 571                     setShellPosition(shellRect);
 572                 }
 573             }
 574 
 575             reconfigureContentWindow(newDimensions);
 576         } finally {
 577             XToolkit.awtUnlock();
 578         }
 579     }
 580 
 581     /**
 582      * @param x, y, width, heith - dimensions of the window with insets
 583      */
 584     private void reshape(int x, int y, int width, int height, int operation,
 585                          boolean userReshape)
 586     {
 587         Rectangle newRec;
 588         boolean setClient = false;
 589         WindowDimensions dims = new WindowDimensions(dimensions);
 590         switch (operation & (~NO_EMBEDDED_CHECK)) {
 591           case SET_LOCATION:
 592               // Set location always sets bounds location. However, until the window is mapped we
 593               // should use client coordinates
 594               dims.setLocation(x, y);
 595               break;
 596           case SET_SIZE:
 597               // Set size sets bounds size. However, until the window is mapped we
 598               // should use client coordinates
 599               dims.setSize(width, height);
 600               break;
 601           case SET_CLIENT_SIZE: {
 602               // Sets client rect size. Width and height contain insets.
 603               Insets in = currentInsets;
 604               width -= in.left+in.right;
 605               height -= in.top+in.bottom;
 606               dims.setClientSize(width, height);
 607               break;
 608           }
 609           case SET_BOUNDS:
 610           default:
 611               dims.setLocation(x, y);
 612               dims.setSize(width, height);
 613               break;
 614         }
 615         if (insLog.isLoggable(PlatformLogger.FINE))
 616             insLog.fine("For the operation {0} new dimensions are {1}",
 617                         operationToString(operation), dims);
 618 
 619         reshape(dims, operation, userReshape);
 620     }
 621 
 622     // This method gets overriden in XFramePeer & XDialogPeer.
 623     abstract boolean isTargetUndecorated();
 624 
 625     /**
 626      * @see java.awt.peer.ComponentPeer#setBounds
 627      */
 628     public void setBounds(int x, int y, int width, int height, int op) {
 629         // TODO: Rewrite with WindowDimensions
 630         reshape(x, y, width, height, op, true);
 631         validateSurface();
 632     }
 633 
 634     // Coordinates are that of the shell
 635     void reconfigureContentWindow(WindowDimensions dims) {
 636         if (content == null) {
 637             insLog.fine("WARNING: Content window is null");
 638             return;
 639         }
 640         content.setContentBounds(dims);
 641     }
 642 
 643     boolean no_reparent_artifacts = false;
 644     public void handleConfigureNotifyEvent(XEvent xev) {
 645         assert (SunToolkit.isAWTLockHeldByCurrentThread());
 646         XConfigureEvent xe = xev.get_xconfigure();
 647         insLog.fine("Configure notify {0}", xe);
 648 
 649         // XXX: should really only consider synthetic events, but
 650         if (isReparented()) {
 651             configure_seen = true;
 652         }
 653 
 654         if (!isMaximized()
 655             && (xe.get_serial() == reparent_serial || xe.get_window() != getShell())
 656             && !no_reparent_artifacts)
 657         {
 658             insLog.fine("- reparent artifact, skipping");
 659             return;
 660         }
 661         no_reparent_artifacts = false;
 662 
 663         /**
 664          * When there is a WM we receive some CN before being visible and after.
 665          * We should skip all CN which are before being visible, because we assume
 666          * the gravity is in action while it is not yet.
 667          *
 668          * When there is no WM we receive CN only _before_ being visible.
 669          * We should process these CNs.
 670          */
 671         if (!isVisible() && XWM.getWMID() != XWM.NO_WM) {
 672             insLog.fine(" - not visible, skipping");
 673             return;
 674         }
 675 
 676         /*
 677          * Some window managers configure before we are reparented and
 678          * the send event flag is set! ugh... (Enlighetenment for one,
 679          * possibly MWM as well).  If we haven't been reparented yet
 680          * this is just the WM shuffling us into position.  Ignore
 681          * it!!!! or we wind up in a bogus location.
 682          */
 683         int runningWM = XWM.getWMID();
 684         if (insLog.isLoggable(PlatformLogger.FINE)) {
 685             insLog.fine("reparented={0}, visible={1}, WM={2}, decorations={3}",
 686                         isReparented(), isVisible(), runningWM, getDecorations());
 687         }
 688         if (!isReparented() && isVisible() && runningWM != XWM.NO_WM
 689                 &&  !XWM.isNonReparentingWM()
 690                 && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
 691             insLog.fine("- visible but not reparented, skipping");
 692             return;
 693         }
 694         //Last chance to correct insets
 695         if (!insets_corrected && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
 696             long parent = XlibUtil.getParentWindow(window);
 697             Insets correctWM = (parent != -1) ? XWM.getWM().getInsets(this, window, parent) : null;
 698             if (insLog.isLoggable(PlatformLogger.FINER)) {
 699                 if (correctWM != null) {
 700                     insLog.finer("Configure notify - insets : " + correctWM);
 701                 } else {
 702                     insLog.finer("Configure notify - insets are still not available");
 703                 }
 704             }
 705             if (correctWM != null) {
 706                 handleCorrectInsets(correctWM);
 707             } else {
 708                 //Only one attempt to correct insets is made (to lower risk)
 709                 //if insets are still not available we simply set the flag
 710                 insets_corrected = true;
 711             }
 712         }
 713 
 714         updateChildrenSizes();
 715 
 716         // Bounds of the window
 717         Rectangle targetBounds = AWTAccessor.getComponentAccessor().getBounds((Component)target);
 718 
 719         Point newLocation = targetBounds.getLocation();
 720         if (xe.get_send_event() || runningWM == XWM.NO_WM || XWM.isNonReparentingWM()) {
 721             // Location, Client size + insets
 722             newLocation = new Point(xe.get_x() - currentInsets.left, xe.get_y() - currentInsets.top);
 723         } else {
 724             // ICCCM 4.1.5 states that a real ConfigureNotify will be sent when
 725             // a window is resized but the client can not tell if the window was
 726             // moved or not. The client should consider the position as unkown
 727             // and use TranslateCoordinates to find the actual position.
 728             //
 729             // TODO this should be the default for every case.
 730             switch (XWM.getWMID()) {
 731                 case XWM.CDE_WM:
 732                 case XWM.MOTIF_WM:
 733                 case XWM.METACITY_WM:
 734                 case XWM.MUTTER_WM:
 735                 case XWM.SAWFISH_WM:
 736                 {
 737                     Point xlocation = queryXLocation();
 738                     if (log.isLoggable(PlatformLogger.FINE)) log.fine("New X location: {0}", xlocation);
 739                     if (xlocation != null) {
 740                         newLocation = xlocation;
 741                     }
 742                     break;
 743                 }
 744                 default:
 745                     break;
 746             }
 747         }
 748 
 749         WindowDimensions newDimensions =
 750                 new WindowDimensions(newLocation,
 751                 new Dimension(xe.get_width(), xe.get_height()),
 752                 copy(currentInsets),
 753                 true);
 754 
 755         insLog.finer("Insets are {0}, new dimensions {1}",
 756                      currentInsets, newDimensions);
 757 
 758         checkIfOnNewScreen(newDimensions.getBounds());
 759 
 760         Point oldLocation = getLocation();
 761         dimensions = newDimensions;
 762         if (!newLocation.equals(oldLocation)) {
 763             handleMoved(newDimensions);
 764         }
 765         reconfigureContentWindow(newDimensions);
 766         updateChildrenSizes();
 767 
 768         repositionSecurityWarning();
 769     }
 770 
 771     private void checkShellRectSize(Rectangle shellRect) {
 772         shellRect.width = Math.max(MIN_SIZE, shellRect.width);
 773         shellRect.height = Math.max(MIN_SIZE, shellRect.height);
 774     }
 775 
 776     private void checkShellRectPos(Rectangle shellRect) {
 777         int wm = XWM.getWMID();
 778         if (wm == XWM.MOTIF_WM || wm == XWM.CDE_WM) {
 779             if (shellRect.x == 0 && shellRect.y == 0) {
 780                 shellRect.x = shellRect.y = 1;
 781             }
 782         }
 783     }
 784 
 785     private void checkShellRect(Rectangle shellRect) {
 786         checkShellRectSize(shellRect);
 787         checkShellRectPos(shellRect);
 788     }
 789 
 790     public void setShellBounds(Rectangle rec) {
 791         if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting shell bounds on " +
 792                                                                 this + " to " + rec);
 793         XToolkit.awtLock();
 794         try {
 795             updateSizeHints(rec.x, rec.y, rec.width, rec.height);
 796             XlibWrapper.XResizeWindow(XToolkit.getDisplay(), getShell(), rec.width, rec.height);
 797             XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), rec.x, rec.y);
 798         }
 799         finally {
 800             XToolkit.awtUnlock();
 801         }
 802     }
 803     public void setShellSize(Rectangle rec) {
 804         if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting shell size on " +
 805                                                                 this + " to " + rec);
 806         XToolkit.awtLock();
 807         try {
 808             updateSizeHints(rec.x, rec.y, rec.width, rec.height);
 809             XlibWrapper.XResizeWindow(XToolkit.getDisplay(), getShell(), rec.width, rec.height);
 810         }
 811         finally {
 812             XToolkit.awtUnlock();
 813         }
 814     }
 815     public void setShellPosition(Rectangle rec) {
 816         if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting shell position on " +
 817                                                                 this + " to " + rec);
 818         XToolkit.awtLock();
 819         try {
 820             updateSizeHints(rec.x, rec.y, rec.width, rec.height);
 821             XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), rec.x, rec.y);
 822         }
 823         finally {
 824             XToolkit.awtUnlock();
 825         }
 826     }
 827 
 828     void initResizability() {
 829         setResizable(winAttr.initialResizability);
 830     }
 831     public void setResizable(boolean resizable) {
 832         int fs = winAttr.functions;
 833         if (!isResizable() && resizable) {
 834             currentInsets = new Insets(0, 0, 0, 0);
 835             resetWMSetInsets();
 836             if (!isEmbedded()) {
 837                 setReparented(false);
 838             }
 839             winAttr.isResizable = resizable;
 840             if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
 841                 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
 842             } else {
 843                 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
 844             }
 845             winAttr.functions = fs;
 846             XWM.setShellResizable(this);
 847         } else if (isResizable() && !resizable) {
 848             currentInsets = new Insets(0, 0, 0, 0);
 849             resetWMSetInsets();
 850             if (!isEmbedded()) {
 851                 setReparented(false);
 852             }
 853             winAttr.isResizable = resizable;
 854             if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
 855                 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
 856             } else {
 857                 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
 858             }
 859             winAttr.functions = fs;
 860             XWM.setShellNotResizable(this, dimensions, dimensions.getBounds(), false);
 861         }
 862     }
 863 
 864     Rectangle getShellBounds() {
 865         return dimensions.getClientRect();
 866     }
 867 
 868     public Rectangle getBounds() {
 869         return dimensions.getBounds();
 870     }
 871 
 872     public Dimension getSize() {
 873         return dimensions.getSize();
 874     }
 875 
 876     public int getX() {
 877         return dimensions.getLocation().x;
 878     }
 879 
 880     public int getY() {
 881         return dimensions.getLocation().y;
 882     }
 883 
 884     public Point getLocation() {
 885         return dimensions.getLocation();
 886     }
 887 
 888     public int getAbsoluteX() {
 889         // NOTE: returning this peer's location which is shell location
 890         return dimensions.getScreenBounds().x;
 891     }
 892 
 893     public int getAbsoluteY() {
 894         // NOTE: returning this peer's location which is shell location
 895         return dimensions.getScreenBounds().y;
 896     }
 897 
 898     public int getWidth() {
 899         return getSize().width;
 900     }
 901 
 902     public int getHeight() {
 903         return getSize().height;
 904     }
 905 
 906     final public WindowDimensions getDimensions() {
 907         return dimensions;
 908     }
 909 
 910     public Point getLocationOnScreen() {
 911         XToolkit.awtLock();
 912         try {
 913             if (configure_seen) {
 914                 return toGlobal(0,0);
 915             } else {
 916                 Point location = target.getLocation();
 917                 if (insLog.isLoggable(PlatformLogger.FINE))
 918                     insLog.fine("getLocationOnScreen {0} not reparented: {1} ",
 919                                 this, location);
 920                 return location;
 921             }
 922         } finally {
 923             XToolkit.awtUnlock();
 924         }
 925     }
 926 
 927 
 928 /***************************************************************************************
 929  *              END            OF             I N S E T S   C O D E
 930  **************************************************************************************/
 931 
 932     protected boolean isEventDisabled(XEvent e) {
 933         switch (e.get_type()) {
 934             // Do not generate MOVED/RESIZED events since we generate them by ourselves
 935           case XConstants.ConfigureNotify:
 936               return true;
 937           case XConstants.EnterNotify:
 938           case XConstants.LeaveNotify:
 939               // Disable crossing event on outer borders of Frame so
 940               // we receive only one set of cross notifications(first set is from content window)
 941               return true;
 942           default:
 943               return super.isEventDisabled(e);
 944         }
 945     }
 946 
 947     int getDecorations() {
 948         return winAttr.decorations;
 949     }
 950 
 951     int getFunctions() {
 952         return winAttr.functions;
 953     }
 954 
 955     public void setVisible(boolean vis) {
 956         log.finer("Setting {0} to visible {1}", this, Boolean.valueOf(vis));
 957         if (vis && !isVisible()) {
 958             XWM.setShellDecor(this);
 959             super.setVisible(vis);
 960             if (winAttr.isResizable) {
 961                 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
 962                 //We need to update frame's minimum size, not to reset it
 963                 XWM.removeSizeHints(this, XUtilConstants.PMaxSize);
 964                 updateMinimumSize();
 965             }
 966         } else {
 967             super.setVisible(vis);
 968         }
 969     }
 970 
 971     protected void suppressWmTakeFocus(boolean doSuppress) {
 972         XAtomList protocols = getWMProtocols();
 973         if (doSuppress) {
 974             protocols.remove(wm_take_focus);
 975         } else {
 976             protocols.add(wm_take_focus);
 977         }
 978         wm_protocols.setAtomListProperty(this, protocols);
 979     }
 980 
 981     public void dispose() {
 982         if (content != null) {
 983             content.destroy();
 984         }
 985         focusProxy.destroy();
 986 
 987         if (iconWindow != null) {
 988             iconWindow.destroy();
 989         }
 990 
 991         super.dispose();
 992     }
 993 
 994     public void handleClientMessage(XEvent xev) {
 995         super.handleClientMessage(xev);
 996         XClientMessageEvent cl = xev.get_xclient();
 997         if ((wm_protocols != null) && (cl.get_message_type() == wm_protocols.getAtom())) {
 998             if (cl.get_data(0) == wm_delete_window.getAtom()) {
 999                 handleQuit();
1000             } else if (cl.get_data(0) == wm_take_focus.getAtom()) {
1001                 handleWmTakeFocus(cl);
1002             }
1003         }
1004     }
1005 
1006     private void handleWmTakeFocus(XClientMessageEvent cl) {
1007         focusLog.fine("WM_TAKE_FOCUS on {0}", this);
1008         requestWindowFocus(cl.get_data(1), true);
1009     }
1010 
1011     /**
1012      * Requests focus to this decorated top-level by requesting X input focus
1013      * to the shell window.
1014      */
1015     protected void requestXFocus(long time, boolean timeProvided) {
1016         // We have proxied focus mechanism - instead of shell the focus is held
1017         // by "proxy" - invisible mapped window. When we want to set X input focus to
1018         // toplevel set it on proxy instead.
1019         if (focusProxy == null) {
1020             if (focusLog.isLoggable(PlatformLogger.FINE)) focusLog.warning("Focus proxy is null for " + this);
1021         } else {
1022             if (focusLog.isLoggable(PlatformLogger.FINE)) focusLog.fine("Requesting focus to proxy: " + focusProxy);
1023             if (timeProvided) {
1024                 focusProxy.xRequestFocus(time);
1025             } else {
1026                 focusProxy.xRequestFocus();
1027             }
1028         }
1029     }
1030 
1031     XFocusProxyWindow getFocusProxy() {
1032         return focusProxy;
1033     }
1034 
1035     public void handleQuit() {
1036         postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_CLOSING));
1037     }
1038 
1039     final void dumpMe() {
1040         System.err.println(">>> Peer: " + x + ", " + y + ", " + width + ", " + height);
1041     }
1042 
1043     final void dumpTarget() {
1044         AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
1045         int getWidth = compAccessor.getWidth((Component)target);
1046         int getHeight = compAccessor.getHeight((Component)target);
1047         int getTargetX = compAccessor.getX((Component)target);
1048         int getTargetY = compAccessor.getY((Component)target);
1049         System.err.println(">>> Target: " + getTargetX + ", " + getTargetY + ", " + getWidth + ", " + getHeight);
1050     }
1051 
1052     final void dumpShell() {
1053         dumpWindow("Shell", getShell());
1054     }
1055     final void dumpContent() {
1056         dumpWindow("Content", getContentWindow());
1057     }
1058     final void dumpParent() {
1059         long parent = XlibUtil.getParentWindow(getShell());
1060         if (parent != 0)
1061         {
1062             dumpWindow("Parent", parent);
1063         }
1064         else
1065         {
1066             System.err.println(">>> NO PARENT");
1067         }
1068     }
1069 
1070     final void dumpWindow(String id, long window) {
1071         XWindowAttributes pattr = new XWindowAttributes();
1072         try {
1073             XToolkit.awtLock();
1074             try {
1075                 int status =
1076                     XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1077                                                      window, pattr.pData);
1078             }
1079             finally {
1080                 XToolkit.awtUnlock();
1081             }
1082             System.err.println(">>>> " + id + ": " + pattr.get_x()
1083                                + ", " + pattr.get_y() + ", " + pattr.get_width()
1084                                + ", " + pattr.get_height());
1085         } finally {
1086             pattr.dispose();
1087         }
1088     }
1089 
1090     final void dumpAll() {
1091         dumpTarget();
1092         dumpMe();
1093         dumpParent();
1094         dumpShell();
1095         dumpContent();
1096     }
1097 
1098     boolean isMaximized() {
1099         return false;
1100     }
1101 
1102     @Override
1103     boolean isOverrideRedirect() {
1104         return Window.Type.POPUP.equals(getWindowType());
1105     }
1106 
1107     public boolean requestWindowFocus(long time, boolean timeProvided) {
1108         focusLog.fine("Request for decorated window focus");
1109         // If this is Frame or Dialog we can't assure focus request success - but we still can try
1110         // If this is Window and its owner Frame is active we can be sure request succedded.
1111         Window focusedWindow = XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow();
1112         Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow);
1113 
1114         focusLog.finer("Current window is: active={0}, focused={1}",
1115                        Boolean.valueOf(target == activeWindow),
1116                        Boolean.valueOf(target == focusedWindow));
1117 
1118         XWindowPeer toFocus = this;
1119         while (toFocus.nextTransientFor != null) {
1120             toFocus = toFocus.nextTransientFor;
1121         }
1122         if (toFocus == null || !toFocus.focusAllowedFor()) {
1123             // This might change when WM will have property to determine focus policy.
1124             // Right now, because policy is unknown we can't be sure we succedded
1125             return false;
1126         }
1127         if (this == toFocus) {
1128             if (isWMStateNetHidden()) {
1129                 focusLog.fine("The window is unmapped, so rejecting the request");
1130                 return false;
1131             }
1132             if (target == activeWindow && target != focusedWindow) {
1133                 // Happens when an owned window is currently focused
1134                 focusLog.fine("Focus is on child window - transfering it back to the owner");
1135                 handleWindowFocusInSync(-1);
1136                 return true;
1137             }
1138             Window realNativeFocusedWindow = XWindowPeer.getNativeFocusedWindow();
1139             focusLog.finest("Real native focused window: " + realNativeFocusedWindow +
1140                             "\nKFM's focused window: " + focusedWindow);
1141 
1142             // See 6522725, 6613426.
1143             if (target == realNativeFocusedWindow) {
1144                 focusLog.fine("The window is already natively focused.");
1145                 return true;
1146             }
1147         }
1148         focusLog.fine("Requesting focus to " + (this == toFocus ? "this window" : toFocus));
1149 
1150         if (timeProvided) {
1151             toFocus.requestXFocus(time);
1152         } else {
1153             toFocus.requestXFocus();
1154         }
1155         return (this == toFocus);
1156     }
1157 
1158     XWindowPeer actualFocusedWindow = null;
1159     void setActualFocusedWindow(XWindowPeer actualFocusedWindow) {
1160         synchronized(getStateLock()) {
1161             this.actualFocusedWindow = actualFocusedWindow;
1162         }
1163     }
1164 
1165     boolean requestWindowFocus(XWindowPeer actualFocusedWindow,
1166                                long time, boolean timeProvided)
1167     {
1168         setActualFocusedWindow(actualFocusedWindow);
1169         return requestWindowFocus(time, timeProvided);
1170     }
1171     public void handleWindowFocusIn(long serial) {
1172         if (null == actualFocusedWindow) {
1173             super.handleWindowFocusIn(serial);
1174         } else {
1175             /*
1176              * Fix for 6314575.
1177              * If this is a result of clicking on one of the Frame's component
1178              * then 'actualFocusedWindow' shouldn't be focused. A decision of focusing
1179              * it or not should be made after the appropriate Java mouse event (if any)
1180              * is handled by the component where 'actualFocusedWindow' value may be reset.
1181              *
1182              * The fix is based on the empiric fact consisting in that the component
1183              * receives native mouse event nearly at the same time the Frame receives
1184              * WM_TAKE_FOCUS (when FocusIn is generated via XSetInputFocus call) but
1185              * definetely before the Frame gets FocusIn event (when this method is called).
1186              */
1187             postEvent(new InvocationEvent(target, new Runnable() {
1188                 public void run() {
1189                     XWindowPeer fw = null;
1190                     synchronized (getStateLock()) {
1191                         fw = actualFocusedWindow;
1192                         actualFocusedWindow = null;
1193                         if (null == fw || !fw.isVisible() || !fw.isFocusableWindow()) {
1194                             fw = XDecoratedPeer.this;
1195                         }
1196                     }
1197                     fw.handleWindowFocusIn_Dispatch();
1198                 }
1199             }));
1200         }
1201     }
1202 
1203     public void handleWindowFocusOut(Window oppositeWindow, long serial) {
1204         Window actualFocusedWindow = XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow();
1205 
1206         // If the actual focused window is not this decorated window then retain it.
1207         if (actualFocusedWindow != null && actualFocusedWindow != target) {
1208             Window owner = XWindowPeer.getDecoratedOwner(actualFocusedWindow);
1209 
1210             if (owner != null && owner == target) {
1211                 setActualFocusedWindow((XWindowPeer) AWTAccessor.getComponentAccessor().getPeer(actualFocusedWindow));
1212             }
1213         }
1214         super.handleWindowFocusOut(oppositeWindow, serial);
1215     }
1216 
1217     private Point queryXLocation()
1218     {
1219         return XlibUtil.translateCoordinates(
1220             getContentWindow(),
1221             XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber()),
1222             new Point(0, 0));
1223     }
1224 }