src/solaris/classes/sun/awt/X11/XDecoratedPeer.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright 2002-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2002-2009 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Sun designates this

@@ -31,28 +31,33 @@
 import java.awt.event.WindowEvent;
 
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import sun.awt.AWTAccessor;
 import sun.awt.ComponentAccessor;
 import sun.awt.SunToolkit;
 
 abstract class XDecoratedPeer extends XWindowPeer {
     private static final Logger log = Logger.getLogger("sun.awt.X11.XDecoratedPeer");
     private static final Logger insLog = Logger.getLogger("sun.awt.X11.insets.XDecoratedPeer");
     private static final Logger focusLog = Logger.getLogger("sun.awt.X11.focus.XDecoratedPeer");
     private static final Logger iconLog = Logger.getLogger("sun.awt.X11.icon.XDecoratedPeer");
 
-    // Set to true when we get the first ConfigureNotify after being
-    // reparented - indicates that WM has adopted the top-level.
-    boolean configure_seen;
-    boolean insets_corrected;
-
     XIconWindow iconWindow;
-    WindowDimensions dimensions;
+
+    /**
+     * The dimensions of the window.
+     *
+     * The entity encapsulates information about the bounds and the insets of
+     * the window. The value is initialized in the preInit() method with the
+     * bounds of the window as known on the shared level. Further updates
+     * should be performed with the reportReshape() method only.
+     */
+    protected WindowDimensions dimensions;
+
     XContentWindow content;
-    Insets currentInsets;
     XFocusProxyWindow focusProxy;
 
     XDecoratedPeer(Window target) {
         super(target);
     }

@@ -71,15 +76,12 @@
 
     void preInit(XCreateWindowParams params) {
         super.preInit(params);
         winAttr.initialFocus = true;
 
-        currentInsets = new Insets(0,0,0,0);
-        applyGuessedInsets();
-
         Rectangle bounds = (Rectangle)params.get(BOUNDS);
-        dimensions = new WindowDimensions(bounds, getRealInsets(), false);
+        dimensions = new WindowDimensions(bounds, getNativeInsets(), false);
         params.put(BOUNDS, dimensions.getClientRect());
         insLog.log(Level.FINE, "Initial dimensions {0}", new Object[] { dimensions });
 
         // Deny default processing of these events on the shell - proxy will take care of
         // them instead

@@ -91,11 +93,10 @@
         super.postInit(params);
         // The lines that follow need to be in a postInit, so they
         // happen after the X window is created.
         initResizability();
         updateSizeHints(dimensions);
-        XWM.requestWMExtents(getWindow());
 
         content = XContentWindow.createContent(this);
 
         if (warningWindow != null) {
             warningWindow.toFront();

@@ -114,18 +115,17 @@
         }
     }
 
     public void updateMinimumSize() {
         super.updateMinimumSize();
-        updateMinSizeHints();
+        updateMinSizeHints(getNativeInsets());
     }
 
-    private void updateMinSizeHints() {
+    private void updateMinSizeHints(Insets insets) {
         if (isResizable()) {
             Dimension minimumSize = getTargetMinimumSize();
             if (minimumSize != null) {
-                Insets insets = getRealInsets();
                 int minWidth = minimumSize.width - insets.left - insets.right;
                 int minHeight = minimumSize.height - insets.top - insets.bottom;
                 if (minWidth < 0) minWidth = 0;
                 if (minHeight < 0) minHeight = 0;
                 setSizeHints(XUtilConstants.PMinSize | (isLocationByPlatform()?0:(XUtilConstants.PPosition | XUtilConstants.USPosition)),

@@ -229,237 +229,289 @@
 
 /***************************************************************************************
  *                             I N S E T S   C O D E
  **************************************************************************************/
 
-    protected boolean isInitialReshape() {
-        return false;
-    }
 
-    private static Insets difference(Insets i1, Insets i2) {
-        return new Insets(i1.top-i2.top, i1.left - i2.left, i1.bottom-i2.bottom, i1.right-i2.right);
-    }
+    /**
+    * Current native insets of the window on the desktop.
+    * When it's noticed the insets might have been changed (like when we
+    * receive a ReparentNotify), the value must be reset to null by the
+    * setNativeInsets(null) call.
+    *
+    * Synchronization: the awtLock is used. This should have been the state
+    * lock instead, but the retrieveNativeInsets() uses the awtLock, and the
+    * later must be always taken before the state lock.
+    *
+    * The field MUST NOT be used directly. Use get/setNativeInsets() instead.
+    */
+    private Insets nativeInsets = null;
 
-    private static boolean isNull(Insets i) {
-        return (i == null) || ((i.left | i.top | i.right | i.bottom) == 0);
+    /**
+    * Gets the current native insets.
+    * This method may only return null if the fallBackToDefault is false.
+    */
+    private Insets getNativeInsets(boolean retrieve, boolean fallBackToDefault) {
+        if (isTargetUndecorated() || isEmbedded() || !XWM.isRunning())
+        {
+            return (Insets)ZERO_INSETS.clone();
     }
-
-    private static Insets copy(Insets i) {
-        return new Insets(i.top, i.left, i.bottom, i.right);
+        if (getWindow() == XConstants.None) {
+            return getDefaultInsets();
     }
-
-    // insets which we get from WM (e.g from _NET_FRAME_EXTENTS)
-    private Insets wm_set_insets;
-
-    private Insets getWMSetInsets(XAtom changedAtom) {
-        if (isEmbedded()) {
-            return null;
+        XToolkit.awtLock();
+        try {
+            if (nativeInsets == null && retrieve) {
+                retrieveNativeInsets();
         }
-
-        if (wm_set_insets != null) {
-            return wm_set_insets;
+            return nativeInsets == null ?
+                (fallBackToDefault ? getDefaultInsets() : null) :
+                (Insets)nativeInsets.clone();
+        } finally {
+            XToolkit.awtUnlock();
         }
-
-        if (changedAtom == null) {
-            wm_set_insets = XWM.getInsetsFromExtents(getWindow());
-        } else {
-            wm_set_insets = XWM.getInsetsFromProp(getWindow(), changedAtom);
         }
 
-        insLog.log(Level.FINER, "FRAME_EXTENTS: {0}", new Object[]{wm_set_insets});
+    /**
+    * Gets the current native insets.
+    */
+    public Insets getNativeInsets() {
+        return getNativeInsets(true, true);
+    }
 
-        if (wm_set_insets != null) {
-            wm_set_insets = copy(wm_set_insets);
+    @Override
+    public Insets getInsets() {
+        Insets insets = getNativeInsets();
+        insets.top += getMenuBarHeight();
+        if (insLog.isLoggable(Level.FINEST)) {
+            insLog.log(Level.FINEST, "Get insets returns {0}",
+                    new Object[] {insets});
         }
-        return wm_set_insets;
+        return insets;
     }
 
-    private void resetWMSetInsets() {
-        wm_set_insets = null;
+    /**
+     * Returns the insets that the window probably will get.
+     */
+    private Insets getDefaultInsets() {
+        Insets insets = XWM.getWM().getDefaultInsets();
+        return (Insets)(insets == null ? DEFAULT_INSETS : insets).clone();
     }
 
-    public void handlePropertyNotify(XEvent xev) {
-        super.handlePropertyNotify(xev);
-
-        XPropertyEvent ev = xev.get_xproperty();
-        if (ev.get_atom() == XWM.XA_KDE_NET_WM_FRAME_STRUT.getAtom()
-            || ev.get_atom() == XWM.XA_NET_FRAME_EXTENTS.getAtom())
-        {
-            getWMSetInsets(XAtom.get(ev.get_atom()));
+    /**
+     * Get rid of insane insets.
+     * The operation is performed in place - the given object gets modified.
+     */
+    private static Insets sanitize(Insets insets) {
+        //XXX: Perhaps using the marginal values instead of the default would
+        //     make more sense?
+        if (insets.top > 64 || insets.top < 0) {
+            insets.top = DEFAULT_INSETS.top;
         }
+        if (insets.left > 32 || insets.left < 0) {
+            insets.left = DEFAULT_INSETS.left;
+        }
+        if (insets.right > 32 || insets.right < 0) {
+            insets.right = DEFAULT_INSETS.right;
+        }
+        if (insets.bottom > 32 || insets.bottom < 0) {
+            insets.bottom = DEFAULT_INSETS.bottom;
+        }
+        return insets;
     }
 
-    long reparent_serial = 0;
-
-    public void handleReparentNotifyEvent(XEvent xev) {
-        XReparentEvent  xe = xev.get_xreparent();
-        if (insLog.isLoggable(Level.FINE)) insLog.fine(xe.toString());
-        reparent_serial = xe.get_serial();
+    private void setNativeInsets(Insets insets) {
         XToolkit.awtLock();
         try {
-            long root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
-
-            if (isEmbedded()) {
-                setReparented(true);
-                insets_corrected = true;
-                return;
+            nativeInsets = insets == null ? null :
+                sanitize((Insets)insets.clone());
+        } finally {
+            XToolkit.awtUnlock();
+        }
             }
-            Component t = (Component)target;
-            if (getDecorations() == XWindowAttributesData.AWT_DECOR_NONE) {
-                setReparented(true);
-                insets_corrected = true;
-                reshape(dimensions, SET_SIZE, false);
-            } else if (xe.get_parent() == root) {
-                configure_seen = false;
-                insets_corrected = false;
 
-                /*
-                 * We can be repareted to root for two reasons:
-                 *   . setVisible(false)
-                 *   . WM exited
+    public static final Insets DEFAULT_INSETS = new Insets(25, 5, 5, 5);
+    public static final Insets ZERO_INSETS = new Insets(0, 0, 0, 0);
+
+    /**
+     * Retrieve the current insets of the window.
+     *
+     * This method must only be called by the getNativeInsets() method. DO NOT
+     * call this method directly. If the insets need to be updated, nullify
+     * them using the setNativeInsets(null) method.
                  */
-                if (isVisible()) { /* WM exited */
-                    /* Work around 4775545 */
-                    XWM.getWM().unshadeKludge(this);
-                    insLog.fine("- WM exited");
-                } else {
-                    insLog.fine(" - reparent due to hide");
+    private void retrieveNativeInsets() {
+        long window = getWindow();
+
+        if (XWM.requestWMExtents(window)) {
+            XWM.waitForExtentsUpdateEvent();
                 }
-            } else { /* reparented to WM frame, figure out our insets */
-                setReparented(true);
-                insets_corrected = false;
-
-                // Check if we have insets provided by the WM
-                Insets correctWM = getWMSetInsets(null);
-                if (correctWM != null) {
-                    insLog.log(Level.FINER, "wm-provided insets {0}", new Object[]{correctWM});
-                    // If these insets are equal to our current insets - no actions are necessary
-                    Insets dimInsets = dimensions.getInsets();
-                    if (correctWM.equals(dimInsets)) {
-                        insLog.finer("Insets are the same as estimated - no additional reshapes necessary");
-                        no_reparent_artifacts = true;
-                        insets_corrected = true;
-                        applyGuessedInsets();
+
+        // Some WMs may provide the extents via a property, but do not require
+        // a request to update them. Hence try getting them unconditionally.
+        // We could use the XEvent returned by the waitForExtentsUpdateEvent()
+        // above, though that doesn't seem to make much sense.
+        Insets insets = XWM.getInsetsFromExtents(window);
+        if (insets != null) {
+            setNativeInsets(insets);
                         return;
                     }
-                } else {
-                    correctWM = XWM.getWM().getInsets(this, xe.get_window(), xe.get_parent());
 
-                    if (correctWM != null) {
-                        insLog.log(Level.FINER, "correctWM {0}", new Object[] {correctWM});
-                    } else {
-                        insLog.log(Level.FINER, "correctWM insets are not available, waiting for configureNotify");
+        // The window manager is unable to report any extents, so we need to
+        // calculate them ourselves.
+        Rectangle winRect = XlibUtil.getWindowGeometry(
+                window, XlibWrapper.larg1);
+        if (winRect == null) {
+            // Some error occured
+            return;
                     }
+        // The root window must be got immediately after the previous
+        // getWindowGeometry() call.
+        long root = Native.getWindow(XlibWrapper.larg1); 
+
+        long parent = getNativeParent();
+        if (parent == XConstants.None || parent == root) {
+            // Non-reparenting WM. Assume the insets are zero.
+            setNativeInsets(ZERO_INSETS);
+            return;
                 }
 
-                if (correctWM != null) {
-                    handleCorrectInsets(correctWM);
+        Rectangle parentRect = XlibUtil.getWindowGeometry(parent);
+        if (parentRect == null) {
+            // Some error again
+            return;
                 }
+
+        long grand_parent = XlibUtil.getParentWindow(parent);
+        if (grand_parent == XConstants.None || grand_parent == root ||
+                (winRect.x != 0 || winRect.y != 0 ||
+                 winRect.width != parentRect.width ||
+                 winRect.height != parentRect.height))
+        {
+            // Single-reparenting WM
+            // Either there's no a valid grand-parent, or the direct parent
+            // has greater bounds than the window itself.
+            setNativeInsets(calculateInsets(winRect, parentRect));
+        } else {
+            // Double-reparenting WM
+            // There's a valid grand-parent. The bounds of the direct
+            // parent are equal to the bounds of the window itself.
+            Rectangle grandParentRect = XlibUtil.getWindowGeometry(
+                    grand_parent);
+            if (grandParentRect == null) {
+                // One more error condition. This time we fall back to the
+                // single-reparenting case, though this will produce
+                // ZERO_INSETS actually...
+                setNativeInsets(calculateInsets(winRect, parentRect));
+            } else {
+                setNativeInsets(calculateInsets(parentRect, grandParentRect));
             }
-        } finally {
-            XToolkit.awtUnlock();
         }
     }
 
-    protected void handleCorrectInsets(Insets correctWM) {
-        XToolkit.awtLock();
-        try {
-            /*
-             * Ok, now see if we need adjust window size because
-             * initial insets were wrong (most likely they were).
+    /**
+     * Calculates the insets for the given interior and exterior rectangles.
+     * Only positive insets are allowed. If the interior rectangle is bigger
+     * than the exterior one, the negative inset values will be ignored,
+     * and zero value used instead.
              */
-            Insets correction = difference(correctWM, currentInsets);
-            insLog.log(Level.FINEST, "Corrention {0}", new Object[] {correction});
-            if (!isNull(correction)) {
-                currentInsets = copy(correctWM);
-                applyGuessedInsets();
-
-                //Fix for 6318109: PIT: Min Size is not honored properly when a
-                //smaller size is specified in setSize(), XToolkit
-                //update minimum size hints
-                updateMinSizeHints();
+    private static Insets calculateInsets(Rectangle interior, Rectangle exterior) {
+        return new Insets(
+                Math.max(interior.y, 0),
+                Math.max(interior.x, 0),
+                Math.max(exterior.height - interior.height - interior.y, 0),
+                Math.max(exterior.width - interior.width - interior.x, 0));
             }
-            if (insLog.isLoggable(Level.FINER)) insLog.finer("Dimensions before reparent: " + dimensions);
 
-            dimensions.setInsets(getRealInsets());
-            insets_corrected = true;
+    /**
+     * Applies the new insets.
+     */
+    private void setInsets(Insets insets) {
+        setNativeInsets(insets);
 
-            if (isMaximized()) {
-                return;
+        WindowDimensions dims = new WindowDimensions(dimensions);
+        dims.setInsets(insets);
+        reportOrAdjust(dims, false);
             }
 
-            /*
-             * If this window has been sized by a pack() we need
-             * to keep the interior geometry intact.  Since pack()
-             * computed width and height with wrong insets, we
-             * must adjust the target dimensions appropriately.
-             */
-            if ((getHints().get_flags() & (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0) {
-                reshape(dimensions, SET_BOUNDS, false);
-            } else {
-                reshape(dimensions, SET_SIZE, false);
+    @Override
+    public void handlePropertyNotify(XEvent xev) {
+        super.handlePropertyNotify(xev);
+
+        XPropertyEvent ev = xev.get_xproperty();
+        if (XWM.isExtentsPropertyAtom(ev.get_atom())) {
+            Insets insets = XWM.getInsetsFromProp(getWindow(),
+                    XAtom.get(ev.get_atom()));
+            if (insLog.isLoggable(Level.FINE)) {
+                insLog.fine("" + insets);
+            }
+            if (insets != null) {
+                setInsets(insets);
             }
-        } finally {
-            XToolkit.awtUnlock();
         }
     }
 
-    public void handleMoved(WindowDimensions dims) {
-        Point loc = dims.getLocation();
-        ComponentAccessor.setX((Component)target, loc.x);
-        ComponentAccessor.setY((Component)target, loc.y);
-        postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
+    // The serial of the last received ReparentNotify event
+    private long reparentNotifySerial = 0;
+
+    // The number of processed ConfigureNotify events with the same serial
+    // as reparentNotifySerial.
+    private int numOfConfigureNotifyJustAfterReparentNotify = 0;
+
+    @Override
+    public void handleReparentNotifyEvent(XEvent xev) {
+        super.handleReparentNotifyEvent(xev);
+
+        XReparentEvent  xe = xev.get_xreparent();
+        if (insLog.isLoggable(Level.FINE)) {
+            insLog.fine(xe.toString());
     }
 
+        if (xe.get_window() != getWindow()) {
+            return;
+        }
 
-    protected Insets guessInsets() {
-        if (isEmbedded() || isTargetUndecorated()) {
-            return new Insets(0, 0, 0, 0);
-        } else {
-            if (!isNull(currentInsets)) {
-                /* insets were set on wdata by System Properties */
-                return copy(currentInsets);
-            } else {
-                Insets res = getWMSetInsets(null);
-                if (res == null) {
-                    res = XWM.getWM().guessInsets(this);
+        if (!isParented()) {
+            if (isVisible()) {
+                // Either the WM or the embedder exited
+                XWM.getWM().unshadeKludge(this);
+                if (!isEmbedded()) {
+                    XWM.reset();
                 }
-                return res;
+                setInsets(null);
+            } //else: The window just got hidden
+        } else {
+            // We just got parented: prepare stuff to recalculate the insets,
+            // and to readjust the bounds if needed
+            reparentNotifySerial = xe.get_serial();
+            numOfConfigureNotifyJustAfterReparentNotify = 0;
+
+            setNativeInsets(null);
+            needToAdjustBounds();
             }
         }
+
+    protected boolean isInitialReshape() {
+        return false;
     }
 
-    private void applyGuessedInsets() {
-        Insets guessed = guessInsets();
-        currentInsets = copy(guessed);
+    public void handleMoved(Point loc) {
+        ComponentAccessor.setX((Component)target, loc.x);
+        ComponentAccessor.setY((Component)target, loc.y);
+        postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
     }
 
+
     public void revalidate() {
         XToolkit.executeOnEventHandlerThread(target, new Runnable() {
                 public void run() {
                     target.invalidate();
                     target.validate();
                 }
             });
     }
 
-    Insets getRealInsets() {
-        if (isNull(currentInsets)) {
-            applyGuessedInsets();
-        }
-        return currentInsets;
-    }
-
-    public Insets getInsets() {
-        Insets in = copy(getRealInsets());
-        in.top += getMenuBarHeight();
-        if (insLog.isLoggable(Level.FINEST)) {
-            insLog.log(Level.FINEST, "Get insets returns {0}", new Object[] {in});
-        }
-        return in;
-    }
-
     boolean gravityBug() {
         return XWM.configureGravityBuggy();
     }
 
     // The height of area used to display current active input method

@@ -477,66 +529,99 @@
         updateSizeHints(dimensions);
     }
 
     // Coordinates are that of the target
     // Called only on Toolkit thread
-    public void reshape(WindowDimensions newDimensions, int op,
-                        boolean userReshape)
+    private void reshape(WindowDimensions newDimensions, int op)
     {
         if (insLog.isLoggable(Level.FINE)) {
-            insLog.fine("Reshaping " + this + " to " + newDimensions + " op " + op + " user reshape " + userReshape);
-        }
-        if (userReshape) {
-            // We handle only userReshape == true cases. It means that
-            // if the window manager or any other part of the windowing
-            // system sets inappropriate size for this window, we can
-            // do nothing but accept it.
-            Rectangle newBounds = newDimensions.getBounds();
-            Insets insets = newDimensions.getInsets();
-            // Inherit isClientSizeSet from newDimensions
-            if (newDimensions.isClientSizeSet()) {
-                newBounds = new Rectangle(newBounds.x, newBounds.y,
-                                          newBounds.width - insets.left - insets.right,
-                                          newBounds.height - insets.top - insets.bottom);
-            }
-            newDimensions = new WindowDimensions(newBounds, insets, newDimensions.isClientSizeSet());
+            insLog.fine("Reshaping " + this + " to " + newDimensions +
+                    "; op " + operationToString(op));
         }
         XToolkit.awtLock();
         try {
-            if (!isReparented() || !isVisible()) {
-                insLog.log(Level.FINE, "- not reparented({0}) or not visible({1}), default reshape",
-                           new Object[] {Boolean.valueOf(isReparented()), Boolean.valueOf(visible)});
-
-                // Fix for 6323293.
-                // This actually is needed to preserve compatibility with previous releases -
-                // some of licensees are expecting componentMoved event on invisible one while
-                // its location changes.
+            if (!isVisible()) {
+                Rectangle client = newDimensions.getClientRect();
+                checkShellRect(client);
+                setShellBounds(client);
+
+                reportReshape(newDimensions);
+            } else {
+                requestReshape(newDimensions, op);
+            }
+        } finally {
+            XToolkit.awtUnlock();
+        }
+    }
+
+    /**
+     * Sets window dimensions and propagates the changes to the target sending
+     * corresponding events if needed.
+     * 
+     * MUST be invoked under the AWTLock.
+     */
+    private void reportReshape(WindowDimensions newDims) {
+        if (insLog.isLoggable(Level.FINE)) {
+            insLog.fine("" + newDims);
+        }
+
+        final Insets insets = newDims.getInsets();
+        if (!insets.equals(dimensions.getInsets())) {
+            // Recalculate the minimum size of the client area
+            updateMinSizeHints(insets);
+        }
+
+        // If the client area size changes, we need to revalidate the target.
+        // This may happen in the XContentWindow.setContentBounds(). If this
+        // does not happen there, we need to dispatch the operation here.
+        final boolean needRevalidate =
+            !dimensions.getClientSize().equals(newDims.getClientSize());
+
+        checkIfOnNewScreen(newDims.getBounds());
+
                 Point oldLocation = getLocation();
+        dimensions = newDims;
+        if (!getLocation().equals(oldLocation)) {
+            handleMoved(getLocation());
+        }
 
-                Point newLocation = new Point(ComponentAccessor.getX((Component)target),
-                                              ComponentAccessor.getY((Component)target));
+        if (!reconfigureContentWindow(dimensions) && needRevalidate) {
+            revalidate();
+        }
 
-                if (!newLocation.equals(oldLocation)) {
-                    handleMoved(newDimensions);
+        updateChildrenSizes();
+        repositionSecurityWarning();
                 }
 
-                dimensions = new WindowDimensions(newDimensions);
-                updateSizeHints(dimensions);
-                Rectangle client = dimensions.getClientRect();
-                checkShellRect(client);
-                setShellBounds(client);
-                if (content != null &&
-                    !content.getSize().equals(newDimensions.getSize()))
+    private void reportOrAdjust(WindowDimensions newDims,
+            boolean handlingConfigureNotify)
                 {
-                    reconfigureContentWindow(newDimensions);
+        if (dimensions.equals(newDims) && !areBoundsAdjusting()) {
+            // If nothing has changed and we're already adjusted, return
+            if (insLog.isLoggable(Level.FINE)) {
+                insLog.fine("Ignored: nothing changed: " + newDims);
+            }
+            return;
                 }
+        if (adjustBounds(newDims, handlingConfigureNotify)) {
+            // We expect another ConfigureNotify
                 return;
             }
 
-            int wm = XWM.getWMID();
-            updateChildrenSizes();
-            applyGuessedInsets();
+        reportReshape(newDims);
+    }
+
+    /**
+     * Requests the system to reshape the window.
+     * 
+     * MUST be invoked under the AWTLock.
+     */
+    private void requestReshape(WindowDimensions newDimensions, int op) {
+        if (insLog.isLoggable(Level.FINE)) {
+            insLog.fine("Request reshape: " + newDimensions + "; op: " +
+                    operationToString(op));
+        }
 
             Rectangle shellRect = newDimensions.getClientRect();
 
             if (gravityBug()) {
                 Insets in = newDimensions.getInsets();

@@ -566,25 +651,19 @@
                 XWM.setShellNotResizable(this, newDimensions, shellRect, true);
                 if (op == SET_BOUNDS) {
                     setShellPosition(shellRect);
                 }
             }
-
-            reconfigureContentWindow(newDimensions);
-        } finally {
-            XToolkit.awtUnlock();
-        }
     }
 
+    // This method gets overriden in XFramePeer & XDialogPeer.
+    abstract boolean isTargetUndecorated();
+
     /**
-     * @param x, y, width, heith - dimensions of the window with insets
+     * @see java.awt.peer.ComponentPeer#setBounds
      */
-    private void reshape(int x, int y, int width, int height, int operation,
-                         boolean userReshape)
-    {
-        Rectangle newRec;
-        boolean setClient = false;
+    public void setBounds(int x, int y, int width, int height, int operation) {
         WindowDimensions dims = new WindowDimensions(dimensions);
         switch (operation & (~NO_EMBEDDED_CHECK)) {
           case SET_LOCATION:
               // Set location always sets bounds location. However, until the window is mapped we
               // should use client coordinates

@@ -595,176 +674,290 @@
               // should use client coordinates
               dims.setSize(width, height);
               break;
           case SET_CLIENT_SIZE: {
               // Sets client rect size. Width and height contain insets.
-              Insets in = currentInsets;
+              // Also update the insets to the latest known value to decrease
+              // (or even eliminate) the bounds adjustment after showing
+              // the window.
+              Insets in = getNativeInsets();
               width -= in.left+in.right;
               height -= in.top+in.bottom;
               dims.setClientSize(width, height);
+              dims.setInsets(in);
               break;
           }
           case SET_BOUNDS:
           default:
               dims.setLocation(x, y);
               dims.setSize(width, height);
               break;
         }
-        if (insLog.isLoggable(Level.FINE)) insLog.log(Level.FINE, "For the operation {0} new dimensions are {1}",
-                                                      new Object[] {operationToString(operation), dims});
-
-        reshape(dims, operation, userReshape);
+        reshape(dims, operation);
+        validateSurface();
     }
 
-    // This method gets overriden in XFramePeer & XDialogPeer.
-    abstract boolean isTargetUndecorated();
-
     /**
-     * @see java.awt.peer.ComponentPeer#setBounds
+     * Sets the content window bounds.
+     *
+     * @return whether a COMPONENT_RESIZED has been sent
      */
-    public void setBounds(int x, int y, int width, int height, int op) {
-        // TODO: Rewrite with WindowDimensions
-        reshape(x, y, width, height, op, true);
-        validateSurface();
-    }
-
-    // Coordinates are that of the shell
-    void reconfigureContentWindow(WindowDimensions dims) {
+    boolean reconfigureContentWindow(WindowDimensions dims) {
         if (content == null) {
             insLog.fine("WARNING: Content window is null");
-            return;
+            return false;
         }
-        content.setContentBounds(dims);
+        return content.setContentBounds(dims);
     }
 
-    boolean no_reparent_artifacts = false;
-    public void handleConfigureNotifyEvent(XEvent xev) {
-        assert (SunToolkit.isAWTLockHeldByCurrentThread());
-        XConfigureEvent xe = xev.get_xconfigure();
-        insLog.log(Level.FINE, "Configure notify {0}", new Object[] {xe});
+    /**
+     * Indicates if the adjustBounds() needs to be invoked.
+     * Synchronization: state lock.
+     */
+    private boolean areBoundsAdjusted = false;
 
-        // XXX: should really only consider synthetic events, but
-        if (isReparented()) {
-            configure_seen = true;
+    /**
+     * Forces the system to re-adjust the bounds of the window after it has
+     * been finally adopted by the windowing system.
+     */
+    private void needToAdjustBounds() {
+        synchronized (getStateLock()) {
+            areBoundsAdjusted = false;
         }
-
-        if (!isMaximized()
-            && (xe.get_serial() == reparent_serial || xe.get_window() != getShell())
-            && !no_reparent_artifacts)
-        {
-            insLog.fine("- reparent artifact, skipping");
-            return;
         }
-        no_reparent_artifacts = false;
 
         /**
-         * When there is a WM we receive some CN before being visible and after.
-         * We should skip all CN which are before being visible, because we assume
-         * the gravity is in action while it is not yet.
-         *
-         * When there is no WM we receive CN only _before_ being visible.
-         * We should process these CNs.
+     * Indicates if the window bounds has not yet been finally configured by
+     * the X server/window manager.
          */
-        if (!isVisible() && XWM.getWMID() != XWM.NO_WM) {
-            insLog.fine(" - not visible, skipping");
-            return;
+    public boolean areBoundsAdjusting() {
+        synchronized (getStateLock()) {
+            return !areBoundsAdjusted;
+        }
         }
 
-        /*
-         * Some window managers configure before we are reparented and
-         * the send event flag is set! ugh... (Enlighetenment for one,
-         * possibly MWM as well).  If we haven't been reparented yet
-         * this is just the WM shuffling us into position.  Ignore
-         * it!!!! or we wind up in a bogus location.
+    // XXX: Perhaps the following two might be replaced with Window.isSizeSet,
+    //      isLocationSet? Just like we have Window.isPacked...
+    /**
+     * Indicates if the size hints of the window specify a position requested
+     * by the user's code.
          */
-        int runningWM = XWM.getWMID();
-        if (insLog.isLoggable(Level.FINE)) {
-            insLog.log(Level.FINE, "reparented={0}, visible={1}, WM={2}, decorations={3}",
-                    new Object[] {isReparented(), isVisible(), runningWM, getDecorations()});
+    private boolean isUserSpecifiedPositionSet() {
+        return (getHints().get_flags() &
+                (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0;
         }
-        if (!isReparented() && isVisible() && runningWM != XWM.NO_WM
-                &&  !XWM.isNonReparentingWM()
-                && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
-            insLog.fine("- visible but not reparented, skipping");
-            return;
+
+    /**
+     * Indicates if the size hints of the window specify a position requested
+     * by the user's code.
+     */
+    private boolean isUserSpecifiedSizeSet() {
+        return (getHints().get_flags() &
+                (XUtilConstants.USSize | XUtilConstants.PSize)) != 0;
         }
-        //Last chance to correct insets
-        if (!insets_corrected && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) {
-            long parent = XlibUtil.getParentWindow(window);
-            Insets correctWM = (parent != -1) ? XWM.getWM().getInsets(this, window, parent) : null;
-            if (insLog.isLoggable(Level.FINER)) {
-                if (correctWM != null) {
-                    insLog.finer("Configure notify - insets : " + correctWM);
-                } else {
-                    insLog.finer("Configure notify - insets are still not available");
+
+    /**
+     * Adjust the bounds of the window.
+     *
+     * @return true if an adjustment has taken place. false if the new bounds
+     *         are OK.
+     */
+    private boolean adjustBounds(WindowDimensions newDims,
+            boolean handlingConfigureNotify)
+    {
+        synchronized (getStateLock()) {
+            if (areBoundsAdjusted) {
+                return false;
                 }
+            // We have to adjust bounds upon showing once.
+            // We also may adjust them several times before showing - e.g.
+            // when we receive the PropertyNotify event with the extents.
+            if (handlingConfigureNotify && isVisible()) {
+                insLog.log(Level.FINE, "Final adjustment");
+                areBoundsAdjusted = true;
             }
-            if (correctWM != null) {
-                handleCorrectInsets(correctWM);
-            } else {
-                //Only one attempt to correct insets is made (to lower risk)
-                //if insets are still not available we simply set the flag
-                insets_corrected = true;
             }
+
+        if (isMaximized()) {
+            return false;
         }
 
-        updateChildrenSizes();
+        int operation = 0;
+        boolean isPacked = AWTAccessor.getWindowAccessor().
+            isPacked((Window)target);
 
-        // Bounds of the window
-        Rectangle targetBounds = new Rectangle(ComponentAccessor.getX((Component)target),
-                ComponentAccessor.getY((Component)target),
-                ComponentAccessor.getWidth((Component)target),
-                ComponentAccessor.getHeight((Component)target));
-
-        Point newLocation = targetBounds.getLocation();
-        if (xe.get_send_event() || runningWM == XWM.NO_WM || XWM.isNonReparentingWM()) {
-            // Location, Client size + insets
-            newLocation = new Point(xe.get_x() - currentInsets.left, xe.get_y() - currentInsets.top);
-        } else {
-            // CDE/MWM/Metacity/Sawfish bug: if shell is resized using
-            // top or left border, we don't receive synthetic
-            // ConfigureNotify, only the one from X with zero
-            // coordinates.  This is the workaround to get real
-            // location, 6261336
-            switch (XWM.getWMID()) {
-                case XWM.CDE_WM:
-                case XWM.MOTIF_WM:
-                case XWM.METACITY_WM:
-                case XWM.SAWFISH_WM:
+        if (!newDims.getClientSize().equals(dimensions.getClientSize())
+                && isPacked)
                 {
-                    Point xlocation = queryXLocation();
-                    if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "New X location: {0}", new Object[]{xlocation});
-                    if (xlocation != null) {
-                        newLocation = xlocation;
+            // The client area size calculated via pack() needs to be preserved
+            operation = SET_SIZE;
+        } else
+        if (!newDims.getSize().equals(dimensions.getSize())
+                && isUserSpecifiedSizeSet())
+        {
+            // The size set via setSize()/setBounds() needs to be preserved
+            operation = SET_SIZE;
                     }
-                    break;
+
+        if (isUserSpecifiedPositionSet() &&
+                !newDims.getLocation().equals(dimensions.getLocation()))
+        {
+            // The user also requested a specific position
+            if (operation == 0) {
+                operation = SET_LOCATION;
+            } else {
+                operation = SET_BOUNDS;
                 }
-                default:
-                    break;
             }
+
+        if (operation == 0) {
+            insLog.fine("No adjustment needed");
+            return false;
         }
 
-        WindowDimensions newDimensions =
-                new WindowDimensions(newLocation,
-                new Dimension(xe.get_width(), xe.get_height()),
-                copy(currentInsets),
-                true);
+        WindowDimensions dims = new WindowDimensions(
+                dimensions.getLocation(),
+                isPacked ? dimensions.getClientSize() : dimensions.getSize(),
+                newDims.getInsets(),
+                isPacked);
 
-        insLog.log(Level.FINER, "Insets are {0}, new dimensions {1}",
-                new Object[] {currentInsets, newDimensions});
+        if (insLog.isLoggable(Level.FINE)) {
+            insLog.fine("Request: " + dims + "; operation=" +
+                    operationToString(operation));
+        }
+        requestReshape(dims, operation);
+        return true;
+    }
+
+    @Override
+    public void handleConfigureNotifyEvent(XEvent xev) {
+        // Note: we don't call super because the XWindowPeer immediately calls
+        //       the checkIfOnNewScreen() assuming the bounds are correct. This
+        //       is not always correct for decorated peers.
 
-        checkIfOnNewScreen(newDimensions.getBounds());
+        XConfigureEvent xe = xev.get_xconfigure();
 
-        Point oldLocation = getLocation();
-        dimensions = newDimensions;
-        if (!newLocation.equals(oldLocation)) {
-            handleMoved(newDimensions);
+        // Due to the SubstructureNotifyMask we should process only those
+        // events that belong to us.
+        if (xe.get_window() != getWindow()) {
+            return;
         }
-        reconfigureContentWindow(newDimensions);
-        updateChildrenSizes();
 
-        repositionSecurityWarning();
+        if (insLog.isLoggable(Level.FINE)) {
+            insLog.fine("" + xe);
+            insLog.fine("XWM.isRunning: " + XWM.isRunning());
+        }
+
+        // If there's a WM we have to ignore some events
+        if (XWM.isRunning()) {
+            // Ignore any events until after we become visible
+            if (!isVisible()) {
+                insLog.fine("Ignored: Not visible yet");
+                needToAdjustBounds();
+                return;
+            }
+
+            // We have not yet been parented by the WM
+            if (mayBeReparented() && !isTargetUndecorated()) {
+                insLog.fine("Ignored: Not parented yet");
+                needToAdjustBounds();
+                return;
+            }
+
+            // Just after reparenting we can receive a synthetic and/or real
+            // event(s).  Since sometimes we need to adjust the bounds of the
+            // frame, we need to process only one of the events. Justification:
+            //    1. Both events carry the same information: one from the X
+            //    server, the other from the WM.
+            //    2. The adjustment operation is performed only once upon
+            //    processing the first ConfigureNotify event that is not
+            //    ignored.
+            //    3. If the first event causes the adjustment to happen, the
+            //    second event will be considered as a normal, not ignored
+            //    event that may generate Java events and set incorrect
+            //    (not-yet-adjusted) bounds to the frame.
+            // So generally, if the adjustment is needed, the first
+            // ConfigureNotify that we should really process is the one caused
+            // by the adjustment operation. So we process only the first
+            // ConfigureNotify event having the same serial that the preceeding
+            // ReparentNotify.
+            //
+            // There's one exception however: we do not ignore the events for
+            // maximized frames. The real ConfigureNotify that we would like to
+            // act upon usually is the third one (it carries the correct
+            // maximized bounds). However, it has the same serial (which is
+            // obvious), and what's worse - we can't reliably determine if the
+            // window manager/X server send us exactly two events before that,
+            // or maybe one of them might be not sent at all - and in this case
+            // we would need to process the second event, not the third. So we
+            // just process everything for a maximized frame. This does produce
+            // absolutely meaningless bounds dancing (with events sent to the
+            // user space), but at least this is backward-compatible and quite
+            // reliable. Should we find a way to detect and process only the
+            // very last of ConfigureNotify events sent with the same serial,
+            // we would like to use this approach instead of the current
+            // processing of the very first event only.
+            if (xe.get_serial() == reparentNotifySerial && !isMaximized())
+            {
+                if (++numOfConfigureNotifyJustAfterReparentNotify > 1) {
+                    insLog.fine("Ignored: serial matches the ReparentNotify");
+                    return;
+                }
+            }
+        }
+
+        // At this point we can calculate some reliable insets
+        Insets insets = getNativeInsets();
+
+        // Note that in some cases this kind of event may be caused by the
+        // changed insets (like if the theme changes, or something).  However
+        // it's difficult if at all possible to identify this situation, and
+        // not confuse it with a regular reconfiguration w/o introducing some
+        // serious performance degradation. So we only support changing the
+        // insets:
+        //    a. when a reparenting WM exits/gets replaced.
+        //    b. when the window gets hidden/shown.
+        //    c. if the WM is smart enough to send the PropertyNotify
+        //       events with the updated extents.
+
+        // x, y, width, height hold the coordinates of the whole frame
+        // including the decorations.
+        int x;
+        int y;
+
+        if (xe.get_send_event() || !isParented()) {
+            // If the event is synthetic or we're not parented, the coordinates
+            // are relative to the root window.
+            x = xe.get_x() - insets.left;
+            y = xe.get_y() - insets.top;
+        } else {
+            // The event is real and we're parented - the coordinates are
+            // relative to the parent
+            Point loc = null;
+            if (XWM.isNoSyntheticConfigureNotifyOnLeftTopResize()) {
+                // there's a bug in the WM, so ask the X server
+                loc = queryXLocation();
+            }
+            if (loc == null) {
+                // consider the current location unchanged
+                // TODO: this may not be true... perhaps querying X in all cases is worthwhile?
+                //       after all: we already do this for Metacity which is quite common,
+                //       so we should not worry much about the performance.
+                loc = getLocation();
+            }
+
+            x = loc.x;
+            y = loc.y;
+        }
+
+        int width = xe.get_width() + insets.left + insets.right;
+        int height = xe.get_height() + insets.top + insets.bottom;
+
+        // Now apply the new bounds
+        WindowDimensions d = new WindowDimensions(
+                    new Rectangle(x, y, width, height), insets, false);
+        reportOrAdjust(d, true);
     }
 
     private void checkShellRectSize(Rectangle shellRect) {
         if (shellRect.width < 0) {
             shellRect.width = 1;

@@ -830,29 +1023,21 @@
         setResizable(winAttr.initialResizability);
     }
     public void setResizable(boolean resizable) {
         int fs = winAttr.functions;
         if (!isResizable() && resizable) {
-            currentInsets = new Insets(0, 0, 0, 0);
-            resetWMSetInsets();
-            if (!isEmbedded()) {
-                setReparented(false);
-            }
+            setNativeInsets(null);
             winAttr.isResizable = resizable;
             if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
                 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
             } else {
                 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
             }
             winAttr.functions = fs;
             XWM.setShellResizable(this);
         } else if (isResizable() && !resizable) {
-            currentInsets = new Insets(0, 0, 0, 0);
-            resetWMSetInsets();
-            if (!isEmbedded()) {
-                setReparented(false);
-            }
+            setNativeInsets(null);
             winAttr.isResizable = resizable;
             if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) {
                 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);
             } else {
                 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE);

@@ -909,17 +1094,18 @@
     }
 
     public Point getLocationOnScreen() {
         XToolkit.awtLock();
         try {
-            if (configure_seen) {
+            if (!areBoundsAdjusting()) {
                 return toGlobal(0,0);
             } else {
                 Point location = target.getLocation();
-                if (insLog.isLoggable(Level.FINE))
+                if (insLog.isLoggable(Level.FINE)) {
                     insLog.log(Level.FINE, "getLocationOnScreen {0} not reparented: {1} ",
                                new Object[] {this, location});
+                }
                 return location;
             }
         } finally {
             XToolkit.awtUnlock();
         }

@@ -1055,11 +1241,11 @@
     final void dumpContent() {
         dumpWindow("Content", getContentWindow());
     }
     final void dumpParent() {
         long parent = XlibUtil.getParentWindow(getShell());
-        if (parent != 0)
+        if (parent != XConstants.None)
         {
             dumpWindow("Parent", parent);
         }
         else
         {

@@ -1098,11 +1284,10 @@
     boolean isMaximized() {
         return false;
     }
 
     boolean isOverrideRedirect() {
-//        return false;
         return ((XToolkit)Toolkit.getDefaultToolkit()).isOverrideRedirect((Window)target);
     }
 
     public boolean requestWindowFocus(long time, boolean timeProvided) {
         focusLog.fine("Request for decorated window focus");