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");