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

Print this page




  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 
  27 /**
  28  * Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
  29  * Author: Denis Mikhalkin
  30  */
  31 package sun.awt.X11;
  32 
  33 import sun.misc.Unsafe;
  34 import java.awt.Insets;
  35 import java.awt.Frame;
  36 import java.awt.Rectangle;

  37 import java.util.Collection;
  38 import java.util.HashMap;
  39 import java.util.LinkedList;
  40 import java.util.logging.Level;
  41 import java.util.logging.Logger;
  42 import java.util.regex.Matcher;
  43 import java.util.regex.Pattern;
  44 



  45 /**
  46  * Class incapsulating knowledge about window managers in general
  47  * Descendants should provide some information about specific window manager.
  48  */
  49 final class XWM
  50 {
  51 
  52     private final static Logger log = Logger.getLogger("sun.awt.X11.XWM");
  53     private final static Logger insLog = Logger.getLogger("sun.awt.X11.insets.XWM");
  54     private final static Logger stateLog = Logger.getLogger("sun.awt.X11.states.XWM");
  55 
  56     static final XAtom XA_MWM_HINTS = new XAtom();
  57 
  58     private static Unsafe unsafe = XlibWrapper.unsafe;
  59 
  60 
  61 /* Good old ICCCM */
  62     static XAtom XA_WM_STATE = new XAtom();
  63 
  64 


 119           case KDE2_WM:
 120               return "KWM2";
 121           case SAWFISH_WM:
 122               return "Sawfish";
 123           case ICE_WM:
 124               return "IceWM";
 125           case METACITY_WM:
 126               return "Metacity";
 127           case COMPIZ_WM:
 128               return "Compiz";
 129           case LG3D_WM:
 130               return "LookingGlass";
 131           case UNDETERMINED_WM:
 132           default:
 133               return "Undetermined WM";
 134         }
 135     }
 136 
 137 
 138     int WMID;
 139     static final Insets zeroInsets = new Insets(0, 0, 0, 0);
 140     static final Insets defaultInsets = new Insets(25, 5, 5, 5);
 141 
 142     XWM(int WMID) {
 143         this.WMID = WMID;
 144         initializeProtocols();
 145         if (log.isLoggable(Level.FINE)) log.fine("Window manager: " + toString());
 146     }
 147     int getID() {
 148         return WMID;
 149     }
 150 
 151 
 152     static Insets normalize(Insets insets) {
 153         if (insets.top > 64 || insets.top < 0) {
 154             insets.top = 28;
 155         }
 156         if (insets.left > 32 || insets.left < 0) {
 157             insets.left = 6;
 158         }
 159         if (insets.right > 32 || insets.right < 0) {
 160             insets.right = 6;
 161         }
 162         if (insets.bottom > 32 || insets.bottom < 0) {
 163             insets.bottom = 6;
 164         }
 165         return insets;
 166     }
 167 
 168     static XNETProtocol g_net_protocol = null;
 169     static XWINProtocol g_win_protocol = null;
 170     static boolean isNetWMName(String name) {
 171         if (g_net_protocol != null) {
 172             return g_net_protocol.isWMName(name);
 173         } else {
 174             return false;
 175         }
 176     }
 177 
 178     static void initAtoms() {
 179         final Object[][] atomInitList ={
 180             { XA_WM_STATE,                      "WM_STATE"                  },
 181 
 182             { XA_KDE_NET_WM_FRAME_STRUT,    "_KDE_NET_WM_FRAME_STRUT"       },
 183 
 184             { XA_E_FRAME_SIZE,              "_E_FRAME_SIZE"                 },
 185 
 186             { XA_KWM_WIN_ICONIFIED,          "KWM_WIN_ICONIFIED"             },


 264             long selection_owner =
 265                 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
 266                                                XAtom.get(selection_name).getAtom());
 267             if (insLog.isLoggable(Level.FINE)) {
 268                 insLog.finer("selection owner of " + selection_name
 269                              + " is " + selection_owner);
 270             }
 271 
 272             if (selection_owner != XConstants.None) {
 273                 return false;
 274             }
 275 
 276             winmgr_running = false;
 277             substruct.set_event_mask(XConstants.SubstructureRedirectMask);
 278 
 279             XToolkit.WITH_XERROR_HANDLER(detectWMHandler);
 280             XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
 281                                                 XToolkit.getDefaultRootWindow(),
 282                                                 XConstants.CWEventMask,
 283                                                 substruct.pData);

 284             XToolkit.RESTORE_XERROR_HANDLER();
 285 
 286             /*
 287              * If no WM is running then our selection for SubstructureRedirect
 288              * succeeded and needs to be undone (hey we are *not* a WM ;-).
 289              */
 290             if (!winmgr_running) {
 291                 substruct.set_event_mask(0);
 292                 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
 293                                                     XToolkit.getDefaultRootWindow(),
 294                                                     XConstants.CWEventMask,
 295                                                     substruct.pData);
 296                 if (insLog.isLoggable(Level.FINE)) {
 297                     insLog.finer("It looks like there is no WM thus NO_WM");
 298                 }
 299             }
 300 
 301             return !winmgr_running;
 302         } finally {
 303             substruct.dispose();


 543     /*
 544      * Is KDE2 (KWin) running?
 545      */
 546     static boolean isKDE2() {
 547         return isNetWMName("KWin");
 548     }
 549 
 550     static boolean isCompiz() {
 551         return isNetWMName("compiz");
 552     }
 553 
 554     static boolean isLookingGlass() {
 555         return isNetWMName("LG3D");
 556     }
 557 
 558     /*
 559      * Is Metacity running?
 560      */
 561     static boolean isMetacity() {
 562         return isNetWMName("Metacity");
 563 //         || (
 564 //             XA_NET_SUPPORTING_WM_CHECK.
 565 //             getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
 566 //                            getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
 567     }
 568 
 569     static boolean isNonReparentingWM() {

 570         return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
 571     }
 572 



















 573     /*
 574      * Prepare IceWM check.
 575      *
 576      * The only way to detect IceWM, seems to be by setting
 577      * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
 578      * was immediately deleted by IceWM.
 579      *
 580      * But messing with PropertyNotify here is way too much trouble, so
 581      * approximate the check by setting the property in this function and
 582      * checking if it still exists later on.
 583      *
 584      * Gaa, dirty dances...
 585      */
 586     static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
 587     static final char opt[] = {
 588         'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
 589         'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
 590         '0','\0'
 591     };
 592     static boolean prepareIsIceWM() {


 666     /*
 667      * Temporary error handler that checks if selecting for
 668      * SubstructureRedirect failed.
 669      */
 670     private static boolean winmgr_running = false;
 671     private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() {
 672         @Override
 673         public int handleError(long display, XErrorEvent err) {
 674             if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) &&
 675                 (err.get_error_code() == XConstants.BadAccess))
 676             {
 677                 winmgr_running = true;
 678                 return 0;
 679             }
 680             return super.handleError(display, err);
 681         }
 682     };
 683 
 684     /*
 685      * Make an educated guess about running window manager.
 686      * XXX: ideally, we should detect wm restart.
 687      */
 688     static int awt_wmgr = XWM.UNDETERMINED_WM;
 689     static XWM wm;
 690     static XWM getWM() {
 691         if (wm == null) {
 692             wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
 693         }
 694         return wm;
 695     }





















 696     static int getWMID() {
 697         if (insLog.isLoggable(Level.FINEST)) {
 698             insLog.finest("awt_wmgr = " + awt_wmgr);
 699         }
 700         /*
 701          * Ideally, we should support cases when a different WM is started
 702          * during a Java app lifetime.
 703          */
 704 
 705         if (awt_wmgr != XWM.UNDETERMINED_WM) {
 706             return awt_wmgr;
 707         }
 708 
 709         XSetWindowAttributes substruct = new XSetWindowAttributes();
 710         XToolkit.awtLock();
 711         try {
 712             if (isNoWM()) {
 713                 awt_wmgr = XWM.NO_WM;
 714                 return awt_wmgr;
 715             }
 716 
 717             // Initialize _NET protocol - used to detect Window Manager.
 718             // Later, WM will initialize its own version of protocol
 719             XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
 720             l_net_protocol.detect();
 721             if (log.isLoggable(Level.FINE) && l_net_protocol.active()) {
 722                 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
 723             }


 949         if (window.isShowing() && needRemap(window)) {
 950             /*
 951              * Do the re/mapping at the Xlib level.  Since we essentially
 952              * work around a WM bug we don't want this hack to be exposed
 953              * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
 954              */
 955             window.xSetVisible(false);
 956             XToolkit.XSync();
 957             window.xSetVisible(true);
 958         }
 959     }
 960 
 961     /*
 962      * Make specified shell resizable.
 963      */
 964     static void setShellResizable(XDecoratedPeer window) {
 965         if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell resizable " + window);
 966         XToolkit.awtLock();
 967         try {
 968             Rectangle shellBounds = window.getShellBounds();
 969             shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top);

 970             window.updateSizeHints(window.getDimensions());
 971             requestWMExtents(window.getWindow());
 972             XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
 973                                           shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
 974             /* REMINDER: will need to revisit when setExtendedStateBounds is added */
 975             //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
 976             //We need to update frame's minimum size, not to reset it
 977             removeSizeHints(window, XUtilConstants.PMaxSize);
 978             window.updateMinimumSize();
 979 
 980             /* Restore decorations */
 981             setShellDecor(window);
 982         } finally {
 983             XToolkit.awtUnlock();
 984         }
 985     }
 986 
 987     /*
 988      * Make specified shell non-resizable.
 989      * If justChangeSize is false, update decorations as well.


1031     private <T> void addProtocol(Class<T> protocolInterface, T protocol) {
1032         Collection<T> protocols = getProtocols(protocolInterface);
1033         protocols.add(protocol);
1034         protocolsMap.put(protocolInterface, protocols);
1035     }
1036 
1037     boolean supportsDynamicLayout() {
1038         int wm = getWMID();
1039         switch (wm) {
1040           case XWM.ENLIGHTEN_WM:
1041           case XWM.KDE2_WM:
1042           case XWM.SAWFISH_WM:
1043           case XWM.ICE_WM:
1044           case XWM.METACITY_WM:
1045               return true;
1046           case XWM.OPENLOOK_WM:
1047           case XWM.MOTIF_WM:
1048           case XWM.CDE_WM:
1049               return false;
1050           default:

1051               return false;
1052         }
1053     }
1054 
1055 
1056     /**
1057      * Check if state is supported.
1058      * Note that a compound state is always reported as not supported.
1059      * Note also that MAXIMIZED_BOTH is considered not a compound state.
1060      * Therefore, a compound state is just ICONIFIED | anything else.
1061      *
1062      */
1063     boolean supportsExtendedState(int state) {
1064         switch (state) {
1065           case Frame.MAXIMIZED_VERT:
1066           case Frame.MAXIMIZED_HORIZ:
1067               /*
1068                * WMs that talk NET/WIN protocol, but do not support
1069                * unidirectional maximization.
1070                */


1247                 }
1248                 if (net_protocol.doLayerProtocol()) {
1249                     addProtocol(XLayerProtocol.class, net_protocol);
1250                 }
1251             }
1252         }
1253 
1254         XWINProtocol win = g_win_protocol;
1255         if (win != null) {
1256             if (win.active()) {
1257                 if (win.doStateProtocol()) {
1258                     addProtocol(XStateProtocol.class, win);
1259                 }
1260                 if (win.doLayerProtocol()) {
1261                     addProtocol(XLayerProtocol.class, win);
1262                 }
1263             }
1264         }
1265     }
1266 
1267     HashMap storedInsets = new HashMap();
1268     Insets guessInsets(XDecoratedPeer window) {
1269         Insets res = (Insets)storedInsets.get(window.getClass());
1270         if (res == null) {
1271             switch (WMID) {
1272               case ENLIGHTEN_WM:
1273                   res = new Insets(19, 4, 4, 4);
1274                   break;
1275               case CDE_WM:
1276                   res = new Insets(28, 6, 6, 6);
1277                   break;
1278               case NO_WM:
1279               case LG3D_WM:
1280                   res = zeroInsets;
1281                   break;
1282               case MOTIF_WM:
1283               case OPENLOOK_WM:
1284               default:
1285                   res = defaultInsets;
1286             }
1287         }
1288         if (insLog.isLoggable(Level.FINEST)) insLog.finest("WM guessed insets: " + res);
1289         return res;
1290     }
1291     /*
1292      * Some buggy WMs ignore window gravity when processing
1293      * ConfigureRequest and position window as if the gravity is Static.
1294      * We work around this in MWindowPeer.pReshape().
1295      *
1296      * Starting with 1.5 we have introduced an Environment variable
1297      * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1298      * explicitly that the WM has this behaviour, example is FVWM.
1299      */
1300 
1301     static int awtWMStaticGravity = -1;
1302     static boolean configureGravityBuggy() {
1303 
1304         if (awtWMStaticGravity == -1) {
1305             awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1306         }
1307 
1308         if (awtWMStaticGravity == 1) {
1309             return true;
1310         }


1384                                      0, 4, false, XAtom.XA_CARDINAL);
1385         try {
1386             if (getter.execute() != XConstants.Success
1387                 || getter.getData() == 0
1388                 || getter.getActualType() != XAtom.XA_CARDINAL
1389                 || getter.getActualFormat() != 32)
1390             {
1391                 return null;
1392             } else {
1393                 return new Insets((int)Native.getCard32(getter.getData(), 2), // top
1394                                   (int)Native.getCard32(getter.getData(), 0), // left
1395                                   (int)Native.getCard32(getter.getData(), 3), // bottom
1396                                   (int)Native.getCard32(getter.getData(), 1)); // right
1397             }
1398         } finally {
1399             getter.dispose();
1400         }
1401     }
1402 
1403     /**
1404      * Asks WM to fill Frame Extents (insets) for the window.


1405      */
1406     public static void requestWMExtents(long window) {
1407         if (window == XConstants.None) { // not initialized
1408             return;
1409         }
1410 
1411         log.fine("Requesting FRAME_EXTENTS");
1412 
1413         XClientMessageEvent msg = new XClientMessageEvent();
1414         msg.zero();
1415         msg.set_type(XConstants.ClientMessage);
1416         msg.set_display(XToolkit.getDisplay());
1417         msg.set_window(window);
1418         msg.set_format(32);
1419         XToolkit.awtLock();
1420         try {
1421             XNETProtocol net_protocol = getWM().getNETProtocol();
1422             if (net_protocol != null && net_protocol.active()) {
1423                 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
1424                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1425                                        false,
1426                                        XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1427                                        msg.getPData());

1428             }
1429             if (getWMID() == XWM.KDE2_WM) {
1430                 msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom());
1431                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1432                                        false,
1433                                        XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1434                                        msg.getPData());

1435             }
1436             // XXX: should we wait for response? XIfEvent() would be useful here :)
1437         } finally {
1438             XToolkit.awtUnlock();
1439             msg.dispose();
1440         }

1441     }
1442 
1443     /* syncTopLEvelPos() is necessary to insure that the window manager has in
1444      * fact moved us to our final position relative to the reParented WM window.
1445      * We have noted a timing window which our shell has not been moved so we
1446      * screw up the insets thinking they are 0,0.  Wait (for a limited period of
1447      * time to let the WM hava a chance to move us.
1448      * @param window window ID of the shell, assuming it is the window
1449      * which will NOT have zero coordinates after the complete
1450      * reparenting
1451      */
1452     boolean syncTopLevelPos(long window, XWindowAttributes attrs) {
1453         int tries = 0;
1454         XToolkit.awtLock();
1455         try {
1456             do {
1457                 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData);
1458                 if (attrs.get_x() != 0 || attrs.get_y() != 0) {
1459                     return true;
1460                 }
1461                 tries++;
1462                 XToolkit.XSync();
1463             } while (tries < 50);
1464         }
1465         finally {
1466             XToolkit.awtUnlock();
1467         }
1468         return false;
1469     }
1470 
1471     Insets getInsets(XDecoratedPeer win, long window, long parent) {
1472         /*
1473          * Unfortunately the concept of "insets" borrowed to AWT
1474          * from Win32 is *absolutely*, *unbelievably* foreign to
1475          * X11.  Few WMs provide the size of frame decor
1476          * (i.e. insets) in a property they set on the client
1477          * window, so we check if we can get away with just
1478          * peeking at it.  [Future versions of wm-spec might add a
1479          * standardized hint for this].
1480          *
1481          * Otherwise we do some special casing.  Actually the
1482          * fallback code ("default" case) seems to cover most of
1483          * the existing WMs (modulo Reparent/Configure order
1484          * perhaps?).
1485          *
1486          * Fallback code tries to account for the two most common cases:
1487          *
1488          * . single reparenting
1489          *       parent window is the WM frame
1490          *       [twm, olwm, sawfish]
1491          *
1492          * . double reparenting
1493          *       parent is a lining exactly the size of the client
1494          *       grandpa is the WM frame
1495          *       [mwm, e!, kwin, fvwm2 ... ]
1496          */
1497         Insets correctWM = XWM.getInsetsFromExtents(window);
1498         insLog.log(Level.FINER, "Got insets from property: {0}", correctWM);
1499 
1500         if (correctWM == null) {
1501             correctWM = new Insets(0,0,0,0);
1502 
1503             correctWM.top = -1;
1504             correctWM.left = -1;
1505 
1506             XWindowAttributes lwinAttr = new XWindowAttributes();
1507             XWindowAttributes pattr = new XWindowAttributes();
1508             try {
1509                 switch (XWM.getWMID()) {
1510                   /* should've been done in awt_wm_getInsetsFromProp */
1511                   case XWM.ENLIGHTEN_WM: {
1512                       /* enlightenment does double reparenting */
1513                       syncTopLevelPos(parent, lwinAttr);
1514                       correctWM.left = lwinAttr.get_x();
1515                       correctWM.top = lwinAttr.get_y();
1516                       /*
1517                        * Now get the actual dimensions of the parent window
1518                        * resolve the difference.  We can't rely on the left
1519                        * to be equal to right or bottom...  Enlightment
1520                        * breaks that assumption.
1521                        */
1522                       XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1523                                                        XlibUtil.getParentWindow(parent),
1524                                                        pattr.pData);
1525                       correctWM.right = pattr.get_width() -
1526                           (lwinAttr.get_width() + correctWM.left);
1527                       correctWM.bottom = pattr.get_height() -
1528                           (lwinAttr.get_height() + correctWM.top);
1529 
1530                       break;
1531                   }
1532                   case XWM.ICE_WM: // for 1.2.2.
1533                   case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */
1534                   case XWM.CDE_WM:
1535                   case XWM.MOTIF_WM: {
1536                       /* these are double reparenting too */
1537                       if (syncTopLevelPos(parent, lwinAttr)) {
1538                           correctWM.top = lwinAttr.get_y();
1539                           correctWM.left = lwinAttr.get_x();
1540                           correctWM.right = correctWM.left;
1541                           correctWM.bottom = correctWM.left;
1542                       } else {
1543                           return null;
1544                       }
1545                       break;
1546                   }
1547                   case XWM.SAWFISH_WM:
1548                   case XWM.OPENLOOK_WM: {
1549                       /* single reparenting */
1550                       syncTopLevelPos(window, lwinAttr);
1551                       correctWM.top    = lwinAttr.get_y();
1552                       correctWM.left   = lwinAttr.get_x();
1553                       correctWM.right  = correctWM.left;
1554                       correctWM.bottom = correctWM.left;
1555                       break;
1556                   }
1557                   case XWM.OTHER_WM:
1558                   default: {                /* this is very similar to the E! case above */
1559                       insLog.log(Level.FINEST, "Getting correct insets for OTHER_WM/default, parent: {0}", parent);
1560                       syncTopLevelPos(parent, lwinAttr);
1561                       int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1562                                                                     window, lwinAttr.pData);
1563                       status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1564                                                                 parent, pattr.pData);
1565                       if (lwinAttr.get_root() == parent) {
1566                           insLog.finest("our parent is root so insets should be zero");
1567                           correctWM = new Insets(0, 0, 0, 0);
1568                           break;
1569                       }

1570 
1571                       /*
1572                        * Check for double-reparenting WM.
1573                        *
1574                        * If the parent is exactly the same size as the
1575                        * top-level assume taht it's the "lining" window and
1576                        * that the grandparent is the actual frame (NB: we
1577                        * have already handled undecorated windows).
1578                        *
1579                        * XXX: what about timing issues that syncTopLevelPos
1580                        * is supposed to work around?
1581                        */
1582                       if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
1583                           && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
1584                           && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
1585                       {
1586                           insLog.log(Level.FINEST, "Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1587                                      new Object[] {lwinAttr, pattr, parent, window});
1588                           lwinAttr.set_x(pattr.get_x());
1589                           lwinAttr.set_y(pattr.get_y());
1590                           lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
1591 
1592                           final long grand_parent = XlibUtil.getParentWindow(parent);
1593 
1594                           if (grand_parent == lwinAttr.get_root()) {
1595                               // This is not double-reparenting in a
1596                               // general sense - we simply don't have
1597                               // correct insets - return null to try to
1598                               // get insets later
1599                               return null;
1600                           } else {
1601                               parent = grand_parent;
1602                               XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1603                                                                parent,
1604                                                                pattr.pData);
1605                           }
1606                       }
1607                       /*
1608                        * XXX: To be absolutely correct, we'd need to take
1609                        * parent's border-width into account too, but the
1610                        * rest of the code is happily unaware about border
1611                        * widths and inner/outer distinction, so for the time
1612                        * being, just ignore it.
1613                        */
1614                       insLog.log(Level.FINEST, "Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1615                                  new Object[] {lwinAttr, pattr, parent, window});
1616                       correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
1617                                              lwinAttr.get_x() + lwinAttr.get_border_width(),
1618                                              pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
1619                                              pattr.get_width() -  (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
1620                       break;
1621                   } /* default */
1622                 } /* switch (runningWM) */
1623             } finally {
1624                 lwinAttr.dispose();
1625                 pattr.dispose();
1626             }


1627         }
1628         if (storedInsets.get(win.getClass()) == null) {
1629             storedInsets.put(win.getClass(), correctWM);

1630         }
1631         return correctWM;


1632     }

1633     boolean isDesktopWindow( long w ) {
1634         if (g_net_protocol != null) {
1635             XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
1636             return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
1637         } else {
1638             return false;
1639         }
1640     }
1641 
1642     public XNETProtocol getNETProtocol() {
1643         return g_net_protocol;
1644     }
1645 
1646     /**
1647      * Sets _NET_WN_ICON property on the window using the arrays of
1648      * raster-data for icons. If icons is null, removes _NET_WM_ICON
1649      * property.
1650      * This method invokes XNETProtocol.setWMIcon() for WMs that
1651      * support NET protocol.
1652      *


  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 
  27 /**
  28  * Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
  29  * Author: Denis Mikhalkin
  30  */
  31 package sun.awt.X11;
  32 

  33 import java.awt.Insets;
  34 import java.awt.Frame;
  35 import java.awt.Rectangle;
  36 import java.security.AccessController;
  37 import java.util.Collection;
  38 import java.util.HashMap;
  39 import java.util.LinkedList;
  40 import java.util.logging.Level;
  41 import java.util.logging.Logger;
  42 import java.util.regex.Matcher;
  43 import java.util.regex.Pattern;
  44 
  45 import sun.misc.Unsafe;
  46 import sun.security.action.GetIntegerAction;
  47 
  48 /**
  49  * Class incapsulating knowledge about window managers in general
  50  * Descendants should provide some information about specific window manager.
  51  */
  52 final class XWM
  53 {
  54 
  55     private final static Logger log = Logger.getLogger("sun.awt.X11.XWM");
  56     private final static Logger insLog = Logger.getLogger("sun.awt.X11.insets.XWM");
  57     private final static Logger stateLog = Logger.getLogger("sun.awt.X11.states.XWM");
  58 
  59     static final XAtom XA_MWM_HINTS = new XAtom();
  60 
  61     private static Unsafe unsafe = XlibWrapper.unsafe;
  62 
  63 
  64 /* Good old ICCCM */
  65     static XAtom XA_WM_STATE = new XAtom();
  66 
  67 


 122           case KDE2_WM:
 123               return "KWM2";
 124           case SAWFISH_WM:
 125               return "Sawfish";
 126           case ICE_WM:
 127               return "IceWM";
 128           case METACITY_WM:
 129               return "Metacity";
 130           case COMPIZ_WM:
 131               return "Compiz";
 132           case LG3D_WM:
 133               return "LookingGlass";
 134           case UNDETERMINED_WM:
 135           default:
 136               return "Undetermined WM";
 137         }
 138     }
 139 
 140 
 141     int WMID;


 142 
 143     XWM(int WMID) {
 144         this.WMID = WMID;
 145         initializeProtocols();
 146         if (log.isLoggable(Level.FINE)) log.fine("Window manager: " + toString());
 147     }
 148     int getID() {
 149         return WMID;
 150     }
 151 
 152 















 153 
 154     static XNETProtocol g_net_protocol = null;
 155     static XWINProtocol g_win_protocol = null;
 156     static boolean isNetWMName(String name) {
 157         if (g_net_protocol != null) {
 158             return g_net_protocol.isWMName(name);
 159         } else {
 160             return false;
 161         }
 162     }
 163 
 164     static void initAtoms() {
 165         final Object[][] atomInitList ={
 166             { XA_WM_STATE,                      "WM_STATE"                  },
 167 
 168             { XA_KDE_NET_WM_FRAME_STRUT,    "_KDE_NET_WM_FRAME_STRUT"       },
 169 
 170             { XA_E_FRAME_SIZE,              "_E_FRAME_SIZE"                 },
 171 
 172             { XA_KWM_WIN_ICONIFIED,          "KWM_WIN_ICONIFIED"             },


 250             long selection_owner =
 251                 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
 252                                                XAtom.get(selection_name).getAtom());
 253             if (insLog.isLoggable(Level.FINE)) {
 254                 insLog.finer("selection owner of " + selection_name
 255                              + " is " + selection_owner);
 256             }
 257 
 258             if (selection_owner != XConstants.None) {
 259                 return false;
 260             }
 261 
 262             winmgr_running = false;
 263             substruct.set_event_mask(XConstants.SubstructureRedirectMask);
 264 
 265             XToolkit.WITH_XERROR_HANDLER(detectWMHandler);
 266             XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
 267                                                 XToolkit.getDefaultRootWindow(),
 268                                                 XConstants.CWEventMask,
 269                                                 substruct.pData);
 270             XToolkit.XSync();
 271             XToolkit.RESTORE_XERROR_HANDLER();
 272 
 273             /*
 274              * If no WM is running then our selection for SubstructureRedirect
 275              * succeeded and needs to be undone (hey we are *not* a WM ;-).
 276              */
 277             if (!winmgr_running) {
 278                 substruct.set_event_mask(0);
 279                 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
 280                                                     XToolkit.getDefaultRootWindow(),
 281                                                     XConstants.CWEventMask,
 282                                                     substruct.pData);
 283                 if (insLog.isLoggable(Level.FINE)) {
 284                     insLog.finer("It looks like there is no WM thus NO_WM");
 285                 }
 286             }
 287 
 288             return !winmgr_running;
 289         } finally {
 290             substruct.dispose();


 530     /*
 531      * Is KDE2 (KWin) running?
 532      */
 533     static boolean isKDE2() {
 534         return isNetWMName("KWin");
 535     }
 536 
 537     static boolean isCompiz() {
 538         return isNetWMName("compiz");
 539     }
 540 
 541     static boolean isLookingGlass() {
 542         return isNetWMName("LG3D");
 543     }
 544 
 545     /*
 546      * Is Metacity running?
 547      */
 548     static boolean isMetacity() {
 549         return isNetWMName("Metacity");




 550     }
 551 
 552     //XXX: Perhaps we need to consider OTHER_WM as non-reparenting also?
 553     public static boolean isNonReparentingWM() {
 554         return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
 555     }
 556 
 557     /**
 558      * Indicates if the window manager does not send a synthetic
 559      * ConfigureNotify when the user resizes the window using the left or top
 560      * border of the window.
 561      * In that cases we receive a real event only.
 562      * See 6261336.
 563      */
 564     public static boolean isNoSyntheticConfigureNotifyOnLeftTopResize() {
 565         switch (XWM.getWMID()) {
 566             case XWM.CDE_WM:
 567             case XWM.MOTIF_WM:
 568             case XWM.METACITY_WM:
 569             case XWM.SAWFISH_WM:
 570                 return true;
 571             default:
 572                 return false;
 573         }
 574     }
 575 
 576     /*
 577      * Prepare IceWM check.
 578      *
 579      * The only way to detect IceWM, seems to be by setting
 580      * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
 581      * was immediately deleted by IceWM.
 582      *
 583      * But messing with PropertyNotify here is way too much trouble, so
 584      * approximate the check by setting the property in this function and
 585      * checking if it still exists later on.
 586      *
 587      * Gaa, dirty dances...
 588      */
 589     static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
 590     static final char opt[] = {
 591         'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
 592         'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
 593         '0','\0'
 594     };
 595     static boolean prepareIsIceWM() {


 669     /*
 670      * Temporary error handler that checks if selecting for
 671      * SubstructureRedirect failed.
 672      */
 673     private static boolean winmgr_running = false;
 674     private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() {
 675         @Override
 676         public int handleError(long display, XErrorEvent err) {
 677             if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) &&
 678                 (err.get_error_code() == XConstants.BadAccess))
 679             {
 680                 winmgr_running = true;
 681                 return 0;
 682             }
 683             return super.handleError(display, err);
 684         }
 685     };
 686 
 687     /*
 688      * Make an educated guess about running window manager.
 689      * XXX: would be nice to synchronize access to these variables.
 690      */
 691     private static int awt_wmgr = XWM.UNDETERMINED_WM;
 692     private static XWM wm;
 693     static XWM getWM() {
 694         if (wm == null) {
 695             wm = new XWM(awt_wmgr = getWMID());
 696         }
 697         return wm;
 698     }
 699 
 700     /**
 701      * Indicates if there's currently a window manager running.
 702      */
 703     public static boolean isRunning() {
 704         return XWM.getWMID() != XWM.NO_WM;
 705     }
 706 
 707     /**
 708      * Resets the currently detected window manager.
 709      * Must be called whenever we detect that the currently running window
 710      * manager exits.
 711      */
 712     public static void reset() {
 713         awt_wmgr = XWM.UNDETERMINED_WM;
 714         g_net_protocol = null;
 715         g_win_protocol = null;
 716         wm = null;
 717     }
 718 
 719 
 720     static int getWMID() {
 721         if (insLog.isLoggable(Level.FINEST)) {
 722             insLog.finest("awt_wmgr = " + awt_wmgr);
 723         }




 724 
 725         if (awt_wmgr != XWM.UNDETERMINED_WM) {
 726             return awt_wmgr;
 727         }
 728 
 729         XSetWindowAttributes substruct = new XSetWindowAttributes();
 730         XToolkit.awtLock();
 731         try {
 732             if (isNoWM()) {
 733                 awt_wmgr = XWM.NO_WM;
 734                 return awt_wmgr;
 735             }
 736 
 737             // Initialize _NET protocol - used to detect Window Manager.
 738             // Later, WM will initialize its own version of protocol
 739             XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
 740             l_net_protocol.detect();
 741             if (log.isLoggable(Level.FINE) && l_net_protocol.active()) {
 742                 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
 743             }


 969         if (window.isShowing() && needRemap(window)) {
 970             /*
 971              * Do the re/mapping at the Xlib level.  Since we essentially
 972              * work around a WM bug we don't want this hack to be exposed
 973              * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
 974              */
 975             window.xSetVisible(false);
 976             XToolkit.XSync();
 977             window.xSetVisible(true);
 978         }
 979     }
 980 
 981     /*
 982      * Make specified shell resizable.
 983      */
 984     static void setShellResizable(XDecoratedPeer window) {
 985         if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell resizable " + window);
 986         XToolkit.awtLock();
 987         try {
 988             Rectangle shellBounds = window.getShellBounds();
 989             Insets insets = window.getNativeInsets();
 990             shellBounds.translate(-insets.left, -insets.top);
 991             window.updateSizeHints(window.getDimensions());
 992             requestWMExtents(window.getWindow());
 993             XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
 994                                           shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
 995             /* REMINDER: will need to revisit when setExtendedStateBounds is added */
 996             //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
 997             //We need to update frame's minimum size, not to reset it
 998             removeSizeHints(window, XUtilConstants.PMaxSize);
 999             window.updateMinimumSize();
1000 
1001             /* Restore decorations */
1002             setShellDecor(window);
1003         } finally {
1004             XToolkit.awtUnlock();
1005         }
1006     }
1007 
1008     /*
1009      * Make specified shell non-resizable.
1010      * If justChangeSize is false, update decorations as well.


1052     private <T> void addProtocol(Class<T> protocolInterface, T protocol) {
1053         Collection<T> protocols = getProtocols(protocolInterface);
1054         protocols.add(protocol);
1055         protocolsMap.put(protocolInterface, protocols);
1056     }
1057 
1058     boolean supportsDynamicLayout() {
1059         int wm = getWMID();
1060         switch (wm) {
1061           case XWM.ENLIGHTEN_WM:
1062           case XWM.KDE2_WM:
1063           case XWM.SAWFISH_WM:
1064           case XWM.ICE_WM:
1065           case XWM.METACITY_WM:
1066               return true;
1067           case XWM.OPENLOOK_WM:
1068           case XWM.MOTIF_WM:
1069           case XWM.CDE_WM:
1070               return false;
1071           default:
1072               //XXX: so what about compiz?
1073               return false;
1074         }
1075     }
1076 
1077 
1078     /**
1079      * Check if state is supported.
1080      * Note that a compound state is always reported as not supported.
1081      * Note also that MAXIMIZED_BOTH is considered not a compound state.
1082      * Therefore, a compound state is just ICONIFIED | anything else.
1083      *
1084      */
1085     boolean supportsExtendedState(int state) {
1086         switch (state) {
1087           case Frame.MAXIMIZED_VERT:
1088           case Frame.MAXIMIZED_HORIZ:
1089               /*
1090                * WMs that talk NET/WIN protocol, but do not support
1091                * unidirectional maximization.
1092                */


1269                 }
1270                 if (net_protocol.doLayerProtocol()) {
1271                     addProtocol(XLayerProtocol.class, net_protocol);
1272                 }
1273             }
1274         }
1275 
1276         XWINProtocol win = g_win_protocol;
1277         if (win != null) {
1278             if (win.active()) {
1279                 if (win.doStateProtocol()) {
1280                     addProtocol(XStateProtocol.class, win);
1281                 }
1282                 if (win.doLayerProtocol()) {
1283                     addProtocol(XLayerProtocol.class, win);
1284                 }
1285             }
1286         }
1287     }
1288 
1289     /**
1290      * Returns the "default" insets for the currently running window manager.
1291      */
1292     public final Insets getDefaultInsets() {
1293         switch (WMID) {
1294             case ENLIGHTEN_WM:
1295                 return new Insets(19, 4, 4, 4);

1296             case CDE_WM:
1297                 return new Insets(28, 6, 6, 6);

1298             case NO_WM:
1299             case LG3D_WM:
1300                 return XDecoratedPeer.ZERO_INSETS;



1301             default:
1302                 return null;
1303         }
1304     }
1305 


1306     /*
1307      * Some buggy WMs ignore window gravity when processing
1308      * ConfigureRequest and position window as if the gravity is Static.
1309      * We work around this in MWindowPeer.pReshape().
1310      *
1311      * Starting with 1.5 we have introduced an Environment variable
1312      * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1313      * explicitly that the WM has this behaviour, example is FVWM.
1314      */
1315 
1316     static int awtWMStaticGravity = -1;
1317     static boolean configureGravityBuggy() {
1318 
1319         if (awtWMStaticGravity == -1) {
1320             awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1321         }
1322 
1323         if (awtWMStaticGravity == 1) {
1324             return true;
1325         }


1399                                      0, 4, false, XAtom.XA_CARDINAL);
1400         try {
1401             if (getter.execute() != XConstants.Success
1402                 || getter.getData() == 0
1403                 || getter.getActualType() != XAtom.XA_CARDINAL
1404                 || getter.getActualFormat() != 32)
1405             {
1406                 return null;
1407             } else {
1408                 return new Insets((int)Native.getCard32(getter.getData(), 2), // top
1409                                   (int)Native.getCard32(getter.getData(), 0), // left
1410                                   (int)Native.getCard32(getter.getData(), 3), // bottom
1411                                   (int)Native.getCard32(getter.getData(), 1)); // right
1412             }
1413         } finally {
1414             getter.dispose();
1415         }
1416     }
1417 
1418     /**
1419      * Asks the WM to fill Frame Extents (insets) for the window.
1420      *
1421      * @return true if a request has been actually sent, false otherwise.
1422      */
1423     public static boolean requestWMExtents(long window) {
1424         if (window == XConstants.None) { // not initialized
1425             return false;
1426         }
1427 
1428         log.fine("Requesting FRAME_EXTENTS");
1429 
1430         XClientMessageEvent msg = new XClientMessageEvent();
1431         msg.zero();
1432         msg.set_type(XConstants.ClientMessage);
1433         msg.set_display(XToolkit.getDisplay());
1434         msg.set_window(window);
1435         msg.set_format(32);
1436         XToolkit.awtLock();
1437         try {
1438             XNETProtocol net_protocol = getWM().getNETProtocol();
1439             if (net_protocol != null && net_protocol.active()) {
1440                 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
1441                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1442                                        false,
1443                                        XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1444                                        msg.getPData());
1445                 return true;
1446             }
1447             if (getWMID() == XWM.KDE2_WM) {
1448                 msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom());
1449                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1450                                        false,
1451                                        XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1452                                        msg.getPData());
1453                 return true;
1454             }

1455         } finally {
1456             XToolkit.awtUnlock();
1457             msg.dispose();
1458         }
1459         return false;
1460     }
1461 
1462     /**
1463      * Indicates if the given atom represents a property containing the extents
1464      * of a frame.





1465      */
1466     public static boolean isExtentsPropertyAtom(long atom) {
1467         return atom == XA_KDE_NET_WM_FRAME_STRUT.getAtom()
1468             || atom == XA_NET_FRAME_EXTENTS.getAtom()
1469             || atom == XA_E_FRAME_SIZE.getAtom();




1470     }






































1471 
1472     // Synchronization: XWM.class
1473     private static Integer syncDelay = null;
1474 
1475     /**
1476      * Retrieves the synchronization delay in milliseconds.
1477      *
1478      * Sending a request to the window manager and doing XSync(), we do not
1479      * immediately get the requested result because the window manager is not
1480      * the X server, but a separate process. An example of such request is the
1481      * _NET_REQUEST_FRAME_EXTENTS: the _NET_FRAME_EXTENTS window property may
1482      * be updated with a delay after the request has been dispatched to the X
1483      * server via the XSync() method.
1484      *
1485      * To wait for the response we need a delay value. This method is supposed
1486      * to return a reasonable value for such delay. The default value is
1487      * considered fine when running on a local display. However, for
1488      * connections over TCP, SSH, or other types of connections, the delay may
1489      * have to be increased. For such purposes there's a private system
1490      * property: sun.awt.wmsyncdelay that may be set to an integer value
1491      * representing the number of milliseconds to wait for a window manager's
1492      * reposnse.
1493      */
1494     public static synchronized long getSyncDelay() {
1495         if (syncDelay == null) {
1496             syncDelay = AccessController.doPrivileged(
1497                 new GetIntegerAction("sun.awt.wmsyncdelay", 150));
1498         }
1499         return syncDelay;
1500     }
1501 
1502     private static XlibWrapper.CheckEventPredicate
1503         checkExtentsUpdateEventPredicate =
1504         new XlibWrapper.CheckEventPredicate() {
1505             public boolean doesMatch(XEvent event) {
1506                 return event.get_type() == XConstants.PropertyNotify
1507                     && isExtentsPropertyAtom(event.get_xproperty().get_atom());

































1508             }
1509         };
1510 
1511     /**
1512      * Waits for a PropertyNotify containing the requested frame extents.
1513      *
1514      * @return the PropertyNotify event, or null if nothing got dispatched
1515      */
1516     public static XEvent waitForExtentsUpdateEvent() {
1517         final long TRIES = 5;
1518         long timeout = getSyncDelay();
1519         if (timeout < TRIES) {
1520             timeout = TRIES;

























1521         }
1522         final long delay = timeout / TRIES;
1523 
1524         XEvent event = null;
1525         do {
1526             XToolkit.awtLock();
1527             try {
1528                 XToolkit.XSync();
1529                 event = XlibWrapper.CheckIfEvent(XToolkit.getDisplay(),
1530                         checkExtentsUpdateEventPredicate);







1531             } finally {
1532                 XToolkit.awtUnlock();

1533             }
1534             if (event != null) {
1535                 break;
1536             }
1537             try {
1538                 Thread.sleep(delay);
1539             } catch (InterruptedException ex) {
1540             }
1541         } while ((timeout -= delay) > 0);
1542 
1543         return event;
1544     }
1545 
1546     boolean isDesktopWindow( long w ) {
1547         if (g_net_protocol != null) {
1548             XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
1549             return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
1550         } else {
1551             return false;
1552         }
1553     }
1554 
1555     public XNETProtocol getNETProtocol() {
1556         return g_net_protocol;
1557     }
1558 
1559     /**
1560      * Sets _NET_WN_ICON property on the window using the arrays of
1561      * raster-data for icons. If icons is null, removes _NET_WM_ICON
1562      * property.
1563      * This method invokes XNETProtocol.setWMIcon() for WMs that
1564      * support NET protocol.
1565      *