src/solaris/classes/sun/awt/X11/XWM.java
Print this page
@@ -28,22 +28,25 @@
* Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
* Author: Denis Mikhalkin
*/
package sun.awt.X11;
-import sun.misc.Unsafe;
import java.awt.Insets;
import java.awt.Frame;
import java.awt.Rectangle;
+import java.security.AccessController;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import sun.misc.Unsafe;
+import sun.security.action.GetIntegerAction;
+
/**
* Class incapsulating knowledge about window managers in general
* Descendants should provide some information about specific window manager.
*/
final class XWM
@@ -134,12 +137,10 @@
}
}
int WMID;
- static final Insets zeroInsets = new Insets(0, 0, 0, 0);
- static final Insets defaultInsets = new Insets(25, 5, 5, 5);
XWM(int WMID) {
this.WMID = WMID;
initializeProtocols();
if (log.isLoggable(Level.FINE)) log.fine("Window manager: " + toString());
@@ -147,25 +148,10 @@
int getID() {
return WMID;
}
- static Insets normalize(Insets insets) {
- if (insets.top > 64 || insets.top < 0) {
- insets.top = 28;
- }
- if (insets.left > 32 || insets.left < 0) {
- insets.left = 6;
- }
- if (insets.right > 32 || insets.right < 0) {
- insets.right = 6;
- }
- if (insets.bottom > 32 || insets.bottom < 0) {
- insets.bottom = 6;
- }
- return insets;
- }
static XNETProtocol g_net_protocol = null;
static XWINProtocol g_win_protocol = null;
static boolean isNetWMName(String name) {
if (g_net_protocol != null) {
@@ -279,10 +265,11 @@
XToolkit.WITH_XERROR_HANDLER(detectWMHandler);
XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
XToolkit.getDefaultRootWindow(),
XConstants.CWEventMask,
substruct.pData);
+ XToolkit.XSync();
XToolkit.RESTORE_XERROR_HANDLER();
/*
* If no WM is running then our selection for SubstructureRedirect
* succeeded and needs to be undone (hey we are *not* a WM ;-).
@@ -558,20 +545,36 @@
/*
* Is Metacity running?
*/
static boolean isMetacity() {
return isNetWMName("Metacity");
-// || (
-// XA_NET_SUPPORTING_WM_CHECK.
-// getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
-// getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
}
- static boolean isNonReparentingWM() {
+ //XXX: Perhaps we need to consider OTHER_WM as non-reparenting also?
+ public static boolean isNonReparentingWM() {
return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
}
+ /**
+ * Indicates if the window manager does not send a synthetic
+ * ConfigureNotify when the user resizes the window using the left or top
+ * border of the window.
+ * In that cases we receive a real event only.
+ * See 6261336.
+ */
+ public static boolean isNoSyntheticConfigureNotifyOnLeftTopResize() {
+ switch (XWM.getWMID()) {
+ case XWM.CDE_WM:
+ case XWM.MOTIF_WM:
+ case XWM.METACITY_WM:
+ case XWM.SAWFISH_WM:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/*
* Prepare IceWM check.
*
* The only way to detect IceWM, seems to be by setting
* _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
@@ -681,28 +684,45 @@
}
};
/*
* Make an educated guess about running window manager.
- * XXX: ideally, we should detect wm restart.
+ * XXX: would be nice to synchronize access to these variables.
*/
- static int awt_wmgr = XWM.UNDETERMINED_WM;
- static XWM wm;
+ private static int awt_wmgr = XWM.UNDETERMINED_WM;
+ private static XWM wm;
static XWM getWM() {
if (wm == null) {
- wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
+ wm = new XWM(awt_wmgr = getWMID());
}
return wm;
}
+
+ /**
+ * Indicates if there's currently a window manager running.
+ */
+ public static boolean isRunning() {
+ return XWM.getWMID() != XWM.NO_WM;
+ }
+
+ /**
+ * Resets the currently detected window manager.
+ * Must be called whenever we detect that the currently running window
+ * manager exits.
+ */
+ public static void reset() {
+ awt_wmgr = XWM.UNDETERMINED_WM;
+ g_net_protocol = null;
+ g_win_protocol = null;
+ wm = null;
+ }
+
+
static int getWMID() {
if (insLog.isLoggable(Level.FINEST)) {
insLog.finest("awt_wmgr = " + awt_wmgr);
}
- /*
- * Ideally, we should support cases when a different WM is started
- * during a Java app lifetime.
- */
if (awt_wmgr != XWM.UNDETERMINED_WM) {
return awt_wmgr;
}
@@ -964,11 +984,12 @@
static void setShellResizable(XDecoratedPeer window) {
if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell resizable " + window);
XToolkit.awtLock();
try {
Rectangle shellBounds = window.getShellBounds();
- shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top);
+ Insets insets = window.getNativeInsets();
+ shellBounds.translate(-insets.left, -insets.top);
window.updateSizeHints(window.getDimensions());
requestWMExtents(window.getWindow());
XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
/* REMINDER: will need to revisit when setExtendedStateBounds is added */
@@ -1046,10 +1067,11 @@
case XWM.OPENLOOK_WM:
case XWM.MOTIF_WM:
case XWM.CDE_WM:
return false;
default:
+ //XXX: so what about compiz?
return false;
}
}
@@ -1262,34 +1284,27 @@
}
}
}
}
- HashMap storedInsets = new HashMap();
- Insets guessInsets(XDecoratedPeer window) {
- Insets res = (Insets)storedInsets.get(window.getClass());
- if (res == null) {
+ /**
+ * Returns the "default" insets for the currently running window manager.
+ */
+ public final Insets getDefaultInsets() {
switch (WMID) {
case ENLIGHTEN_WM:
- res = new Insets(19, 4, 4, 4);
- break;
+ return new Insets(19, 4, 4, 4);
case CDE_WM:
- res = new Insets(28, 6, 6, 6);
- break;
+ return new Insets(28, 6, 6, 6);
case NO_WM:
case LG3D_WM:
- res = zeroInsets;
- break;
- case MOTIF_WM:
- case OPENLOOK_WM:
+ return XDecoratedPeer.ZERO_INSETS;
default:
- res = defaultInsets;
+ return null;
}
}
- if (insLog.isLoggable(Level.FINEST)) insLog.finest("WM guessed insets: " + res);
- return res;
- }
+
/*
* Some buggy WMs ignore window gravity when processing
* ConfigureRequest and position window as if the gravity is Static.
* We work around this in MWindowPeer.pReshape().
*
@@ -1399,15 +1414,17 @@
getter.dispose();
}
}
/**
- * Asks WM to fill Frame Extents (insets) for the window.
+ * Asks the WM to fill Frame Extents (insets) for the window.
+ *
+ * @return true if a request has been actually sent, false otherwise.
*/
- public static void requestWMExtents(long window) {
+ public static boolean requestWMExtents(long window) {
if (window == XConstants.None) { // not initialized
- return;
+ return false;
}
log.fine("Requesting FRAME_EXTENTS");
XClientMessageEvent msg = new XClientMessageEvent();
@@ -1423,215 +1440,111 @@
msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
false,
XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
msg.getPData());
+ return true;
}
if (getWMID() == XWM.KDE2_WM) {
msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom());
XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
false,
XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
msg.getPData());
+ return true;
}
- // XXX: should we wait for response? XIfEvent() would be useful here :)
} finally {
XToolkit.awtUnlock();
msg.dispose();
}
+ return false;
}
- /* syncTopLEvelPos() is necessary to insure that the window manager has in
- * fact moved us to our final position relative to the reParented WM window.
- * We have noted a timing window which our shell has not been moved so we
- * screw up the insets thinking they are 0,0. Wait (for a limited period of
- * time to let the WM hava a chance to move us.
- * @param window window ID of the shell, assuming it is the window
- * which will NOT have zero coordinates after the complete
- * reparenting
+ /**
+ * Indicates if the given atom represents a property containing the extents
+ * of a frame.
*/
- boolean syncTopLevelPos(long window, XWindowAttributes attrs) {
- int tries = 0;
- XToolkit.awtLock();
- try {
- do {
- XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData);
- if (attrs.get_x() != 0 || attrs.get_y() != 0) {
- return true;
+ public static boolean isExtentsPropertyAtom(long atom) {
+ return atom == XA_KDE_NET_WM_FRAME_STRUT.getAtom()
+ || atom == XA_NET_FRAME_EXTENTS.getAtom()
+ || atom == XA_E_FRAME_SIZE.getAtom();
}
- tries++;
- XToolkit.XSync();
- } while (tries < 50);
- }
- finally {
- XToolkit.awtUnlock();
- }
- return false;
- }
-
- Insets getInsets(XDecoratedPeer win, long window, long parent) {
- /*
- * Unfortunately the concept of "insets" borrowed to AWT
- * from Win32 is *absolutely*, *unbelievably* foreign to
- * X11. Few WMs provide the size of frame decor
- * (i.e. insets) in a property they set on the client
- * window, so we check if we can get away with just
- * peeking at it. [Future versions of wm-spec might add a
- * standardized hint for this].
- *
- * Otherwise we do some special casing. Actually the
- * fallback code ("default" case) seems to cover most of
- * the existing WMs (modulo Reparent/Configure order
- * perhaps?).
- *
- * Fallback code tries to account for the two most common cases:
- *
- * . single reparenting
- * parent window is the WM frame
- * [twm, olwm, sawfish]
- *
- * . double reparenting
- * parent is a lining exactly the size of the client
- * grandpa is the WM frame
- * [mwm, e!, kwin, fvwm2 ... ]
- */
- Insets correctWM = XWM.getInsetsFromExtents(window);
- insLog.log(Level.FINER, "Got insets from property: {0}", correctWM);
- if (correctWM == null) {
- correctWM = new Insets(0,0,0,0);
+ // Synchronization: XWM.class
+ private static Integer syncDelay = null;
- correctWM.top = -1;
- correctWM.left = -1;
-
- XWindowAttributes lwinAttr = new XWindowAttributes();
- XWindowAttributes pattr = new XWindowAttributes();
- try {
- switch (XWM.getWMID()) {
- /* should've been done in awt_wm_getInsetsFromProp */
- case XWM.ENLIGHTEN_WM: {
- /* enlightenment does double reparenting */
- syncTopLevelPos(parent, lwinAttr);
- correctWM.left = lwinAttr.get_x();
- correctWM.top = lwinAttr.get_y();
- /*
- * Now get the actual dimensions of the parent window
- * resolve the difference. We can't rely on the left
- * to be equal to right or bottom... Enlightment
- * breaks that assumption.
- */
- XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
- XlibUtil.getParentWindow(parent),
- pattr.pData);
- correctWM.right = pattr.get_width() -
- (lwinAttr.get_width() + correctWM.left);
- correctWM.bottom = pattr.get_height() -
- (lwinAttr.get_height() + correctWM.top);
-
- break;
- }
- case XWM.ICE_WM: // for 1.2.2.
- case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */
- case XWM.CDE_WM:
- case XWM.MOTIF_WM: {
- /* these are double reparenting too */
- if (syncTopLevelPos(parent, lwinAttr)) {
- correctWM.top = lwinAttr.get_y();
- correctWM.left = lwinAttr.get_x();
- correctWM.right = correctWM.left;
- correctWM.bottom = correctWM.left;
- } else {
- return null;
- }
- break;
- }
- case XWM.SAWFISH_WM:
- case XWM.OPENLOOK_WM: {
- /* single reparenting */
- syncTopLevelPos(window, lwinAttr);
- correctWM.top = lwinAttr.get_y();
- correctWM.left = lwinAttr.get_x();
- correctWM.right = correctWM.left;
- correctWM.bottom = correctWM.left;
- break;
- }
- case XWM.OTHER_WM:
- default: { /* this is very similar to the E! case above */
- insLog.log(Level.FINEST, "Getting correct insets for OTHER_WM/default, parent: {0}", parent);
- syncTopLevelPos(parent, lwinAttr);
- int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
- window, lwinAttr.pData);
- status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
- parent, pattr.pData);
- if (lwinAttr.get_root() == parent) {
- insLog.finest("our parent is root so insets should be zero");
- correctWM = new Insets(0, 0, 0, 0);
- break;
+ /**
+ * Retrieves the synchronization delay in milliseconds.
+ *
+ * Sending a request to the window manager and doing XSync(), we do not
+ * immediately get the requested result because the window manager is not
+ * the X server, but a separate process. An example of such request is the
+ * _NET_REQUEST_FRAME_EXTENTS: the _NET_FRAME_EXTENTS window property may
+ * be updated with a delay after the request has been dispatched to the X
+ * server via the XSync() method.
+ *
+ * To wait for the response we need a delay value. This method is supposed
+ * to return a reasonable value for such delay. The default value is
+ * considered fine when running on a local display. However, for
+ * connections over TCP, SSH, or other types of connections, the delay may
+ * have to be increased. For such purposes there's a private system
+ * property: sun.awt.wmsyncdelay that may be set to an integer value
+ * representing the number of milliseconds to wait for a window manager's
+ * reposnse.
+ */
+ public static synchronized long getSyncDelay() {
+ if (syncDelay == null) {
+ syncDelay = AccessController.doPrivileged(
+ new GetIntegerAction("sun.awt.wmsyncdelay", 150));
+ }
+ return syncDelay;
+ }
+
+ private static XlibWrapper.CheckEventPredicate
+ checkExtentsUpdateEventPredicate =
+ new XlibWrapper.CheckEventPredicate() {
+ public boolean doesMatch(XEvent event) {
+ return event.get_type() == XConstants.PropertyNotify
+ && isExtentsPropertyAtom(event.get_xproperty().get_atom());
}
+ };
- /*
- * Check for double-reparenting WM.
+ /**
+ * Waits for a PropertyNotify containing the requested frame extents.
*
- * If the parent is exactly the same size as the
- * top-level assume taht it's the "lining" window and
- * that the grandparent is the actual frame (NB: we
- * have already handled undecorated windows).
- *
- * XXX: what about timing issues that syncTopLevelPos
- * is supposed to work around?
- */
- if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
- && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
- && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
- {
- insLog.log(Level.FINEST, "Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
- new Object[] {lwinAttr, pattr, parent, window});
- lwinAttr.set_x(pattr.get_x());
- lwinAttr.set_y(pattr.get_y());
- lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
-
- final long grand_parent = XlibUtil.getParentWindow(parent);
-
- if (grand_parent == lwinAttr.get_root()) {
- // This is not double-reparenting in a
- // general sense - we simply don't have
- // correct insets - return null to try to
- // get insets later
- return null;
- } else {
- parent = grand_parent;
- XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
- parent,
- pattr.pData);
- }
+ * @return the PropertyNotify event, or null if nothing got dispatched
+ */
+ public static XEvent waitForExtentsUpdateEvent() {
+ final long TRIES = 5;
+ long timeout = getSyncDelay();
+ if (timeout < TRIES) {
+ timeout = TRIES;
}
- /*
- * XXX: To be absolutely correct, we'd need to take
- * parent's border-width into account too, but the
- * rest of the code is happily unaware about border
- * widths and inner/outer distinction, so for the time
- * being, just ignore it.
- */
- insLog.log(Level.FINEST, "Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
- new Object[] {lwinAttr, pattr, parent, window});
- correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
- lwinAttr.get_x() + lwinAttr.get_border_width(),
- pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
- pattr.get_width() - (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
- break;
- } /* default */
- } /* switch (runningWM) */
+ final long delay = timeout / TRIES;
+
+ XEvent event = null;
+ do {
+ XToolkit.awtLock();
+ try {
+ XToolkit.XSync();
+ event = XlibWrapper.CheckIfEvent(XToolkit.getDisplay(),
+ checkExtentsUpdateEventPredicate);
} finally {
- lwinAttr.dispose();
- pattr.dispose();
+ XToolkit.awtUnlock();
}
+ if (event != null) {
+ break;
}
- if (storedInsets.get(win.getClass()) == null) {
- storedInsets.put(win.getClass(), correctWM);
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException ex) {
}
- return correctWM;
+ } while ((timeout -= delay) > 0);
+
+ return event;
}
+
boolean isDesktopWindow( long w ) {
if (g_net_protocol != null) {
XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
} else {