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 {