1 /*
   2  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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.util.regex.*;
  35 import java.awt.Frame;
  36 import java.awt.Rectangle;
  37 import java.util.*;
  38 import java.util.logging.Level;
  39 import java.util.logging.LogManager;
  40 import java.util.logging.Logger;
  41 import java.awt.Insets;
  42 
  43 /**
  44  * Class incapsulating knowledge about window managers in general
  45  * Descendants should provide some information about specific window manager.
  46  */
  47 class XWM implements MWMConstants, XUtilConstants {
  48 
  49     private final static Logger log = Logger.getLogger("sun.awt.X11.XWM");
  50     private final static Logger insLog = Logger.getLogger("sun.awt.X11.insets.XWM");
  51     private final static Logger stateLog = Logger.getLogger("sun.awt.X11.states.XWM");
  52 
  53     static final XAtom XA_MWM_HINTS = new XAtom();
  54 
  55     private static Unsafe unsafe = XlibWrapper.unsafe;
  56 
  57 
  58 /* Good old ICCCM */
  59     static XAtom XA_WM_STATE = new XAtom();
  60 
  61 
  62     XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING");    /* like STRING but encoding is UTF-8 */
  63 
  64 /* Currently we only care about max_v and max_h in _NET_WM_STATE */
  65     final static int AWT_NET_N_KNOWN_STATES=2;
  66 
  67 /* Enlightenment */
  68     final static XAtom XA_E_FRAME_SIZE = new XAtom();
  69 
  70 /* KWin (KDE2) */
  71     final static XAtom XA_KDE_NET_WM_FRAME_STRUT = new XAtom();
  72 
  73 /* KWM (KDE 1.x) OBSOLETE??? */
  74     final static XAtom XA_KWM_WIN_ICONIFIED = new XAtom();
  75     final static XAtom XA_KWM_WIN_MAXIMIZED = new XAtom();
  76 
  77 /* OpenLook */
  78     final static XAtom XA_OL_DECOR_DEL = new XAtom();
  79     final static XAtom XA_OL_DECOR_HEADER = new XAtom();
  80     final static XAtom XA_OL_DECOR_RESIZE = new XAtom();
  81     final static XAtom XA_OL_DECOR_PIN = new XAtom();
  82     final static XAtom XA_OL_DECOR_CLOSE = new XAtom();
  83 
  84 /* EWMH */
  85     final static XAtom XA_NET_FRAME_EXTENTS = new XAtom();
  86     final static XAtom XA_NET_REQUEST_FRAME_EXTENTS = new XAtom();
  87 
  88     final static int
  89         UNDETERMINED_WM = 1,
  90         NO_WM = 2,
  91         OTHER_WM = 3,
  92         OPENLOOK_WM = 4,
  93         MOTIF_WM = 5,
  94         CDE_WM = 6,
  95         ENLIGHTEN_WM = 7,
  96         KDE2_WM = 8,
  97         SAWFISH_WM = 9,
  98         ICE_WM = 10,
  99         METACITY_WM = 11,
 100         COMPIZ_WM = 12,
 101         LG3D_WM = 13;
 102     public String toString() {
 103         switch  (WMID) {
 104           case NO_WM:
 105               return "NO WM";
 106           case OTHER_WM:
 107               return "Other WM";
 108           case OPENLOOK_WM:
 109               return "OPENLOOK";
 110           case MOTIF_WM:
 111               return "MWM";
 112           case CDE_WM:
 113               return "DTWM";
 114           case ENLIGHTEN_WM:
 115               return "Enlightenment";
 116           case KDE2_WM:
 117               return "KWM2";
 118           case SAWFISH_WM:
 119               return "Sawfish";
 120           case ICE_WM:
 121               return "IceWM";
 122           case METACITY_WM:
 123               return "Metacity";
 124           case COMPIZ_WM:
 125               return "Compiz";
 126           case LG3D_WM:
 127               return "LookingGlass";
 128           case UNDETERMINED_WM:
 129           default:
 130               return "Undetermined WM";
 131         }
 132     }
 133 
 134 
 135     int WMID;
 136     static final Insets zeroInsets = new Insets(0, 0, 0, 0);
 137     static final Insets defaultInsets = new Insets(25, 5, 5, 5);
 138 
 139     XWM(int WMID) {
 140         this.WMID = WMID;
 141         initializeProtocols();
 142         if (log.isLoggable(Level.FINE)) log.fine("Window manager: " + toString());
 143     }
 144     int getID() {
 145         return WMID;
 146     }
 147 
 148 
 149     static Insets normalize(Insets insets) {
 150         if (insets.top > 64 || insets.top < 0) {
 151             insets.top = 28;
 152         }
 153         if (insets.left > 32 || insets.left < 0) {
 154             insets.left = 6;
 155         }
 156         if (insets.right > 32 || insets.right < 0) {
 157             insets.right = 6;
 158         }
 159         if (insets.bottom > 32 || insets.bottom < 0) {
 160             insets.bottom = 6;
 161         }
 162         return insets;
 163     }
 164 
 165     static XNETProtocol g_net_protocol = null;
 166     static XWINProtocol g_win_protocol = null;
 167     static boolean isNetWMName(String name) {
 168         if (g_net_protocol != null) {
 169             return g_net_protocol.isWMName(name);
 170         } else {
 171             return false;
 172         }
 173     }
 174 
 175     static void initAtoms() {
 176         final Object[][] atomInitList ={
 177             { XA_WM_STATE,                      "WM_STATE"                  },
 178 
 179             { XA_KDE_NET_WM_FRAME_STRUT,    "_KDE_NET_WM_FRAME_STRUT"       },
 180 
 181             { XA_E_FRAME_SIZE,              "_E_FRAME_SIZE"                 },
 182 
 183             { XA_KWM_WIN_ICONIFIED,          "KWM_WIN_ICONIFIED"             },
 184             { XA_KWM_WIN_MAXIMIZED,          "KWM_WIN_MAXIMIZED"             },
 185 
 186             { XA_OL_DECOR_DEL,               "_OL_DECOR_DEL"                 },
 187             { XA_OL_DECOR_HEADER,            "_OL_DECOR_HEADER"              },
 188             { XA_OL_DECOR_RESIZE,            "_OL_DECOR_RESIZE"              },
 189             { XA_OL_DECOR_PIN,               "_OL_DECOR_PIN"                 },
 190             { XA_OL_DECOR_CLOSE,             "_OL_DECOR_CLOSE"               },
 191             { XA_MWM_HINTS,                  "_MOTIF_WM_HINTS"               },
 192             { XA_NET_FRAME_EXTENTS,          "_NET_FRAME_EXTENTS"            },
 193             { XA_NET_REQUEST_FRAME_EXTENTS,  "_NET_REQUEST_FRAME_EXTENTS"    },
 194         };
 195 
 196         String[] names = new String[atomInitList.length];
 197         for (int index = 0; index < names.length; index++) {
 198             names[index] = (String)atomInitList[index][1];
 199         }
 200 
 201         int atomSize = XAtom.getAtomSize();
 202         long atoms = unsafe.allocateMemory(names.length*atomSize);
 203         XToolkit.awtLock();
 204         try {
 205             int status = XlibWrapper.XInternAtoms(XToolkit.getDisplay(), names, false, atoms);
 206             if (status == 0) {
 207                 return;
 208             }
 209             for (int atom = 0, atomPtr = 0; atom < names.length; atom++, atomPtr += atomSize) {
 210                 ((XAtom)(atomInitList[atom][0])).setValues(XToolkit.getDisplay(), names[atom], XAtom.getAtom(atoms + atomPtr));
 211             }
 212         } finally {
 213             XToolkit.awtUnlock();
 214             unsafe.freeMemory(atoms);
 215         }
 216     }
 217 
 218     /*
 219      * MUST BE CALLED UNDER AWTLOCK.
 220      *
 221      * If *any* window manager is running?
 222      *
 223      * According to ICCCM 2.0 section 4.3.
 224      * WM will acquire ownership of a selection named WM_Sn, where n is
 225      * the screen number.
 226      *
 227      * No selection owner, but, perhaps it is not ICCCM compliant WM
 228      * (e.g. CDE/Sawfish).
 229      * Try selecting for SubstructureRedirect, that only one client
 230      * can select for, and if the request fails, than some other WM is
 231      * already running.
 232      *
 233      * We also treat eXcursion as NO_WM.
 234      */
 235     private static boolean isNoWM() {
 236         /*
 237          * Quick checks for specific servers.
 238          */
 239         String vendor_string = XlibWrapper.ServerVendor(XToolkit.getDisplay());
 240         if (vendor_string.indexOf("eXcursion") != -1) {
 241             /*
 242              * Use NO_WM since in all other aspects eXcursion is like not
 243              * having a window manager running. I.e. it does not reparent
 244              * top level shells.
 245              */
 246             if (insLog.isLoggable(Level.FINE)) {
 247                 insLog.finer("eXcursion means NO_WM");
 248             }
 249             return true;
 250         }
 251 
 252         XSetWindowAttributes substruct = new XSetWindowAttributes();
 253         try {
 254             /*
 255              * Let's check an owner of WM_Sn selection for the default screen.
 256              */
 257             final long default_screen_number =
 258                 XlibWrapper.DefaultScreen(XToolkit.getDisplay());
 259             final String selection_name = "WM_S" + default_screen_number;
 260 
 261             long selection_owner =
 262                 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
 263                                                XAtom.get(selection_name).getAtom());
 264             if (insLog.isLoggable(Level.FINE)) {
 265                 insLog.finer("selection owner of " + selection_name
 266                              + " is " + selection_owner);
 267             }
 268 
 269             if (selection_owner != XConstants.None) {
 270                 return false;
 271             }
 272 
 273             winmgr_running = false;
 274             substruct.set_event_mask(XlibWrapper.SubstructureRedirectMask);
 275 
 276             XToolkit.WITH_XERROR_HANDLER(detectWMHandler);
 277             XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
 278                                                 XToolkit.getDefaultRootWindow(),
 279                                                 XlibWrapper.CWEventMask,
 280                                                 substruct.pData);
 281             XToolkit.RESTORE_XERROR_HANDLER();
 282 
 283             /*
 284              * If no WM is running then our selection for SubstructureRedirect
 285              * succeeded and needs to be undone (hey we are *not* a WM ;-).
 286              */
 287             if (!winmgr_running) {
 288                 substruct.set_event_mask(0);
 289                 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
 290                                                     XToolkit.getDefaultRootWindow(),
 291                                                     XlibWrapper.CWEventMask,
 292                                                     substruct.pData);
 293                 if (insLog.isLoggable(Level.FINE)) {
 294                     insLog.finer("It looks like there is no WM thus NO_WM");
 295                 }
 296             }
 297 
 298             return !winmgr_running;
 299         } finally {
 300             substruct.dispose();
 301         }
 302     }
 303 
 304     static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom("ENLIGHTENMENT_COMMS", false);
 305     /*
 306      * Helper function for isEnlightenment().
 307      * Enlightenment uses STRING property for its comms window id.  Gaaa!
 308      * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
 309      * is "WINID %8x".  Gee, I haven't been using scanf for *ages*... :-)
 310      */
 311     static long getECommsWindowIDProperty(long window) {
 312 
 313         if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
 314             return 0;
 315         }
 316 
 317         WindowPropertyGetter getter =
 318             new WindowPropertyGetter(window, XA_ENLIGHTENMENT_COMMS, 0, 14, false,
 319                                      XAtom.XA_STRING);
 320         try {
 321             int status = getter.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 322             if (status != XlibWrapper.Success || getter.getData() == 0) {
 323                 return 0;
 324             }
 325 
 326             if (getter.getActualType() != XAtom.XA_STRING
 327                 || getter.getActualFormat() != 8
 328                 || getter.getNumberOfItems() != 14 || getter.getBytesAfter() != 0)
 329             {
 330                 return 0;
 331             }
 332 
 333             // Convert data to String, ASCII
 334             byte[] bytes = XlibWrapper.getStringBytes(getter.getData());
 335             String id = new String(bytes);
 336 
 337             log.finer("ENLIGHTENMENT_COMMS is " + id);
 338 
 339             // Parse WINID
 340             Pattern winIdPat = Pattern.compile("WINID\\s+(\\p{XDigit}{0,8})");
 341             try {
 342                 Matcher match = winIdPat.matcher(id);
 343                 if (match.matches()) {
 344                     log.finest("Match group count: " + match.groupCount());
 345                     String longId = match.group(1);
 346                     log.finest("Match group 1 " + longId);
 347                     long winid = Long.parseLong(longId, 16);
 348                     log.finer("Enlightenment communication window " + winid);
 349                     return winid;
 350                 } else {
 351                     log.finer("ENLIGHTENMENT_COMMS has wrong format");
 352                     return 0;
 353                 }
 354             } catch (Exception e) {
 355                 if (log.isLoggable(Level.FINER)) {
 356                     e.printStackTrace();
 357                 }
 358                 return 0;
 359             }
 360         } finally {
 361             getter.dispose();
 362         }
 363     }
 364 
 365     /*
 366      * Is Enlightenment WM running?  Congruent to awt_wm_checkAnchor, but
 367      * uses STRING property peculiar to Enlightenment.
 368      */
 369     static boolean isEnlightenment() {
 370 
 371         long root_xref = getECommsWindowIDProperty(XToolkit.getDefaultRootWindow());
 372         if (root_xref == 0) {
 373             return false;
 374         }
 375 
 376         long self_xref = getECommsWindowIDProperty(root_xref);
 377         if (self_xref != root_xref) {
 378             return false;
 379         }
 380 
 381         return true;
 382     }
 383 
 384     /*
 385      * Is CDE running?
 386      *
 387      * XXX: This is hairy...  CDE is MWM as well.  It seems we simply test
 388      * for default setup and will be bitten if user changes things...
 389      *
 390      * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root.  Take the
 391      * second element of the property and check for presence of
 392      * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
 393      *
 394      * XXX: Any header that defines this structures???
 395      */
 396     static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom("_DT_SM_WINDOW_INFO", false);
 397     static final XAtom XA_DT_SM_STATE_INFO = new XAtom("_DT_SM_STATE_INFO", false);
 398     static boolean isCDE() {
 399 
 400         if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
 401             if (log.isLoggable(Level.FINER)) {
 402                 log.log(Level.FINER, "{0} is not interned",
 403                         new Object[] {String.valueOf(XA_DT_SM_WINDOW_INFO)});
 404             }
 405             return false;
 406         }
 407 
 408         WindowPropertyGetter getter =
 409             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
 410                                      XA_DT_SM_WINDOW_INFO, 0, 2,
 411                                      false, XA_DT_SM_WINDOW_INFO);
 412         try {
 413             int status = getter.execute();
 414             if (status != XlibWrapper.Success || getter.getData() == 0) {
 415                 log.finer("Getting of _DT_SM_WINDOW_INFO is not successfull");
 416                 return false;
 417             }
 418             if (getter.getActualType() != XA_DT_SM_WINDOW_INFO.getAtom()
 419                 || getter.getActualFormat() != 32
 420                 || getter.getNumberOfItems() != 2 || getter.getBytesAfter() != 0)
 421             {
 422                 log.finer("Wrong format of _DT_SM_WINDOW_INFO");
 423                 return false;
 424             }
 425 
 426             long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4);
 427 
 428             if (wmwin == 0) {
 429                 log.fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows");
 430                 return false;
 431             }
 432 
 433             /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
 434             if (!XA_DT_SM_STATE_INFO.isInterned()) {
 435                 if (log.isLoggable(Level.FINER)) {
 436                     log.log(Level.FINER, "{0} is not interned",
 437                             new Object[] {String.valueOf(XA_DT_SM_STATE_INFO)});
 438                 }
 439                 return false;
 440             }
 441             WindowPropertyGetter getter2 =
 442                 new WindowPropertyGetter(wmwin, XA_DT_SM_STATE_INFO, 0, 1,
 443                                          false, XA_DT_SM_STATE_INFO);
 444             try {
 445                 status = getter2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 446 
 447 
 448                 if (status != XlibWrapper.Success || getter2.getData() == 0) {
 449                     log.finer("Getting of _DT_SM_STATE_INFO is not successfull");
 450                     return false;
 451                 }
 452                 if (getter2.getActualType() != XA_DT_SM_STATE_INFO.getAtom()
 453                     || getter2.getActualFormat() != 32)
 454                 {
 455                     log.finer("Wrong format of _DT_SM_STATE_INFO");
 456                     return false;
 457                 }
 458 
 459                 return true;
 460             } finally {
 461                 getter2.dispose();
 462             }
 463         } finally {
 464             getter.dispose();
 465         }
 466     }
 467 
 468     /*
 469      * Is MWM running?  (Note that CDE will test positive as well).
 470      *
 471      * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root.  Take the
 472      * second element of the property and check for presence of
 473      * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
 474      */
 475     static final XAtom XA_MOTIF_WM_INFO = new XAtom("_MOTIF_WM_INFO", false);
 476     static final XAtom XA_DT_WORKSPACE_CURRENT = new XAtom("_DT_WORKSPACE_CURRENT", false);
 477     static boolean isMotif() {
 478 
 479         if (!(XA_MOTIF_WM_INFO.isInterned()/* && XA_DT_WORKSPACE_CURRENT.isInterned()*/) ) {
 480             return false;
 481         }
 482 
 483         WindowPropertyGetter getter =
 484             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
 485                                      XA_MOTIF_WM_INFO, 0,
 486                                      PROP_MOTIF_WM_INFO_ELEMENTS,
 487                                      false, XA_MOTIF_WM_INFO);
 488         try {
 489             int status = getter.execute();
 490 
 491             if (status != XlibWrapper.Success || getter.getData() == 0) {
 492                 return false;
 493             }
 494 
 495             if (getter.getActualType() != XA_MOTIF_WM_INFO.getAtom()
 496                 || getter.getActualFormat() != 32
 497                 || getter.getNumberOfItems() != PROP_MOTIF_WM_INFO_ELEMENTS
 498                 || getter.getBytesAfter() != 0)
 499             {
 500                 return false;
 501             }
 502 
 503             long wmwin = Native.getLong(getter.getData(), 1);
 504             if (wmwin != 0) {
 505                 if (XA_DT_WORKSPACE_CURRENT.isInterned()) {
 506                     /* Now check that this window has _DT_WORKSPACE_CURRENT */
 507                     XAtom[] curws = XA_DT_WORKSPACE_CURRENT.getAtomListProperty(wmwin);
 508                     if (curws.length == 0) {
 509                         return false;
 510                     }
 511                     return true;
 512                 } else {
 513                     // No DT_WORKSPACE, however in our tests MWM sometimes can be without desktop -
 514                     // and that is still MWM.  So simply check for the validity of this window
 515                     // (through WM_STATE property).
 516                     WindowPropertyGetter state_getter =
 517                         new WindowPropertyGetter(wmwin,
 518                                                  XA_WM_STATE,
 519                                                  0, 1, false,
 520                                                  XA_WM_STATE);
 521                     try {
 522                         if (state_getter.execute() == XlibWrapper.Success &&
 523                             state_getter.getData() != 0 &&
 524                             state_getter.getActualType() == XA_WM_STATE.getAtom())
 525                         {
 526                             return true;
 527                         }
 528                     } finally {
 529                         state_getter.dispose();
 530                     }
 531                 }
 532             }
 533         } finally {
 534             getter.dispose();
 535         }
 536         return false;
 537     }
 538 
 539     /*
 540      * Is Sawfish running?
 541      */
 542     static boolean isSawfish() {
 543         return isNetWMName("Sawfish");
 544     }
 545 
 546     /*
 547      * Is KDE2 (KWin) running?
 548      */
 549     static boolean isKDE2() {
 550         return isNetWMName("KWin");
 551     }
 552 
 553     static boolean isCompiz() {
 554         return isNetWMName("compiz");
 555     }
 556 
 557     static boolean isLookingGlass() {
 558         return isNetWMName("LG3D");
 559     }
 560 
 561     /*
 562      * Is Metacity running?
 563      */
 564     static boolean isMetacity() {
 565         return isNetWMName("Metacity");
 566 //         || (
 567 //             XA_NET_SUPPORTING_WM_CHECK.
 568 //             getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
 569 //                            getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
 570     }
 571 
 572     static boolean isNonReparentingWM() {
 573         return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
 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() {
 596         /*
 597          * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
 598          * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
 599          */
 600 
 601         if (!XA_ICEWM_WINOPTHINT.isInterned()) {
 602             if (log.isLoggable(Level.FINER)) {
 603                 log.log(Level.FINER, "{0} is not interned",
 604                         new Object[] {String.valueOf(XA_ICEWM_WINOPTHINT)});
 605             }
 606             return false;
 607         }
 608 
 609         XToolkit.awtLock();
 610         try {
 611             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 612             XlibWrapper.XChangePropertyS(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
 613                                          XA_ICEWM_WINOPTHINT.getAtom(),
 614                                          XA_ICEWM_WINOPTHINT.getAtom(),
 615                                          8, XlibWrapper.PropModeReplace,
 616                                          new String(opt));
 617             XToolkit.RESTORE_XERROR_HANDLER();
 618 
 619             if (XToolkit.saved_error != null && XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
 620                 log.finer("Erorr getting XA_ICEWM_WINOPTHINT property");
 621                 return false;
 622             }
 623             log.finer("Prepared for IceWM detection");
 624             return true;
 625         } finally {
 626             XToolkit.awtUnlock();
 627         }
 628     }
 629 
 630     /*
 631      * Is IceWM running?
 632      *
 633      * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
 634      * false positive will be reported.
 635      */
 636     static boolean isIceWM() {
 637         if (!XA_ICEWM_WINOPTHINT.isInterned()) {
 638             if (log.isLoggable(Level.FINER)) {
 639                 log.log(Level.FINER, "{0} is not interned",
 640                         new Object[] {String.valueOf(XA_ICEWM_WINOPTHINT)});
 641             }
 642             return false;
 643         }
 644 
 645         WindowPropertyGetter getter =
 646             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
 647                                      XA_ICEWM_WINOPTHINT, 0, 0xFFFF,
 648                                      true, XA_ICEWM_WINOPTHINT);
 649         try {
 650             int status = getter.execute();
 651             boolean res = (status == XlibWrapper.Success && getter.getActualType() != 0);
 652             log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res);
 653             return !res || isNetWMName("IceWM");
 654         } finally {
 655             getter.dispose();
 656         }
 657     }
 658 
 659     /*
 660      * Is OpenLook WM running?
 661      *
 662      * This one is pretty lame, but the only property peculiar to OLWM is
 663      * _SUN_WM_PROTOCOLS(ATOM[]).  Fortunately, olwm deletes it on exit.
 664      */
 665     static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false);
 666     static boolean isOpenLook() {
 667         if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
 668             return false;
 669         }
 670 
 671         XAtom[] list = XA_SUN_WM_PROTOCOLS.getAtomListProperty(XToolkit.getDefaultRootWindow());
 672         return (list.length != 0);
 673     }
 674 
 675     /*
 676      * Temporary error handler that checks if selecting for
 677      * SubstructureRedirect failed.
 678      */
 679     private static boolean winmgr_running = false;
 680     private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() {
 681         @Override
 682         public int handleError(long display, XErrorEvent err) {
 683             if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) &&
 684                 (err.get_error_code() == XConstants.BadAccess))
 685             {
 686                 winmgr_running = true;
 687                 return 0;
 688             }
 689             return super.handleError(display, err);
 690         }
 691     };
 692 
 693     /*
 694      * Make an educated guess about running window manager.
 695      * XXX: ideally, we should detect wm restart.
 696      */
 697     static int awt_wmgr = XWM.UNDETERMINED_WM;
 698     static XWM wm;
 699     static XWM getWM() {
 700         if (wm == null) {
 701             wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
 702         }
 703         return wm;
 704     }
 705     static int getWMID() {
 706         if (insLog.isLoggable(Level.FINEST)) {
 707             insLog.finest("awt_wmgr = " + awt_wmgr);
 708         }
 709         /*
 710          * Ideally, we should support cases when a different WM is started
 711          * during a Java app lifetime.
 712          */
 713 
 714         if (awt_wmgr != XWM.UNDETERMINED_WM) {
 715             return awt_wmgr;
 716         }
 717 
 718         XSetWindowAttributes substruct = new XSetWindowAttributes();
 719         XToolkit.awtLock();
 720         try {
 721             if (isNoWM()) {
 722                 awt_wmgr = XWM.NO_WM;
 723                 return awt_wmgr;
 724             }
 725 
 726             // Initialize _NET protocol - used to detect Window Manager.
 727             // Later, WM will initialize its own version of protocol
 728             XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
 729             l_net_protocol.detect();
 730             if (log.isLoggable(Level.FINE) && l_net_protocol.active()) {
 731                 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
 732             }
 733             XWINProtocol win = g_win_protocol = new XWINProtocol();
 734             win.detect();
 735 
 736             /* actual check for IceWM to follow below */
 737             boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */
 738 
 739             /*
 740              * Ok, some WM is out there.  Check which one by testing for
 741              * "distinguishing" atoms.
 742              */
 743             if (isEnlightenment()) {
 744                 awt_wmgr = XWM.ENLIGHTEN_WM;
 745             } else if (isMetacity()) {
 746                 awt_wmgr = XWM.METACITY_WM;
 747             } else if (isSawfish()) {
 748                 awt_wmgr = XWM.SAWFISH_WM;
 749             } else if (isKDE2()) {
 750                 awt_wmgr =XWM.KDE2_WM;
 751             } else if (isCompiz()) {
 752                 awt_wmgr = XWM.COMPIZ_WM;
 753             } else if (isLookingGlass()) {
 754                 awt_wmgr = LG3D_WM;
 755             } else if (doIsIceWM && isIceWM()) {
 756                 awt_wmgr = XWM.ICE_WM;
 757             }
 758             /*
 759              * We don't check for legacy WM when we already know that WM
 760              * supports WIN or _NET wm spec.
 761              */
 762             else if (l_net_protocol.active()) {
 763                 awt_wmgr = XWM.OTHER_WM;
 764             } else if (win.active()) {
 765                 awt_wmgr = XWM.OTHER_WM;
 766             }
 767             /*
 768              * Check for legacy WMs.
 769              */
 770             else if (isCDE()) { /* XXX: must come before isMotif */
 771                 awt_wmgr = XWM.CDE_WM;
 772             } else if (isMotif()) {
 773                 awt_wmgr = XWM.MOTIF_WM;
 774             } else if (isOpenLook()) {
 775                 awt_wmgr = XWM.OPENLOOK_WM;
 776             } else {
 777                 awt_wmgr = XWM.OTHER_WM;
 778             }
 779 
 780             return awt_wmgr;
 781         } finally {
 782             XToolkit.awtUnlock();
 783             substruct.dispose();
 784         }
 785     }
 786 
 787 
 788 /*****************************************************************************\
 789  *
 790  * Size and decoration hints ...
 791  *
 792 \*****************************************************************************/
 793 
 794 
 795     /*
 796      * Remove size hints specified by the mask.
 797      * XXX: Why do we need this in the first place???
 798      */
 799     static void removeSizeHints(XDecoratedPeer window, long mask) {
 800         mask &= PMaxSize | PMinSize;
 801 
 802         XToolkit.awtLock();
 803         try {
 804             XSizeHints hints = window.getHints();
 805             if ((hints.get_flags() & mask) == 0) {
 806                 return;
 807             }
 808 
 809             hints.set_flags(hints.get_flags() & ~mask);
 810             if (insLog.isLoggable(Level.FINER)) insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags()));
 811             XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(),
 812                                           window.getWindow(),
 813                                           hints.pData);
 814         } finally {
 815             XToolkit.awtUnlock();
 816         }
 817     }
 818 
 819     /*
 820      * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
 821      * to be subtracted from the decorations.  Normalize decoration spec
 822      * so that we can map motif decor to something else bit-by-bit in the
 823      * rest of the code.
 824      */
 825     static int normalizeMotifDecor(int decorations) {
 826         if ((decorations & MWM_DECOR_ALL) == 0) {
 827             return decorations;
 828         }
 829         int d = MWM_DECOR_BORDER | MWM_DECOR_RESIZEH
 830             | MWM_DECOR_TITLE
 831             | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE
 832             | MWM_DECOR_MAXIMIZE;
 833         d &= ~decorations;
 834         return d;
 835     }
 836 
 837     /*
 838      * If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken
 839      * to be subtracted from the functions.  Normalize function spec
 840      * so that we can map motif func to something else bit-by-bit in the
 841      * rest of the code.
 842      */
 843     static int normalizeMotifFunc(int functions) {
 844         if ((functions & MWM_FUNC_ALL) == 0) {
 845             return functions;
 846         }
 847         int f = MWM_FUNC_RESIZE |
 848                 MWM_FUNC_MOVE |
 849                 MWM_FUNC_MAXIMIZE |
 850                 MWM_FUNC_MINIMIZE |
 851                 MWM_FUNC_CLOSE;
 852         f &= ~functions;
 853         return f;
 854     }
 855 
 856     /*
 857      * Infer OL properties from MWM decorations.
 858      * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
 859      */
 860     static void setOLDecor(XWindow window, boolean resizable, int decorations) {
 861         if (window == null) {
 862             return;
 863         }
 864 
 865         XAtomList decorDel = new XAtomList();
 866         decorations = normalizeMotifDecor(decorations);
 867         if (insLog.isLoggable(Level.FINER)) insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations));
 868         if ((decorations & MWM_DECOR_TITLE) == 0) {
 869             decorDel.add(XA_OL_DECOR_HEADER);
 870         }
 871         if ((decorations & (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE)) == 0) {
 872             decorDel.add(XA_OL_DECOR_RESIZE);
 873         }
 874         if ((decorations & (MWM_DECOR_MENU |
 875                             MWM_DECOR_MAXIMIZE |
 876                             MWM_DECOR_MINIMIZE)) == 0)
 877         {
 878             decorDel.add(XA_OL_DECOR_CLOSE);
 879         }
 880         if (decorDel.size() == 0) {
 881             insLog.finer("Deleting OL_DECOR");
 882             XA_OL_DECOR_DEL.DeleteProperty(window);
 883         } else {
 884             if (insLog.isLoggable(Level.FINER)) insLog.finer("Setting OL_DECOR to " + decorDel);
 885             XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel);
 886         }
 887     }
 888 
 889     /*
 890      * Set MWM decorations.  Set MWM functions depending on resizability.
 891      */
 892     static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) {
 893         /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
 894         if ((decorations & MWM_DECOR_ALL) != 0
 895             && (decorations != MWM_DECOR_ALL))
 896         {
 897             decorations = normalizeMotifDecor(decorations);
 898         }
 899         if ((functions & MWM_FUNC_ALL) != 0
 900             && (functions != MWM_FUNC_ALL))
 901         {
 902             functions = normalizeMotifFunc(functions);
 903         }
 904 
 905         PropMwmHints hints = window.getMWMHints();
 906         hints.set_flags(hints.get_flags() | MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS);
 907         hints.set_functions(functions);
 908         hints.set_decorations(decorations);
 909 
 910         if (stateLog.isLoggable(Level.FINER)) stateLog.finer("Setting MWM_HINTS to " + hints);
 911         window.setMWMHints(hints);
 912     }
 913 
 914     /*
 915      * Under some window managers if shell is already mapped, we MUST
 916      * unmap and later remap in order to effect the changes we make in the
 917      * window manager decorations.
 918      *
 919      * N.B.  This unmapping / remapping of the shell exposes a bug in
 920      * X/Motif or the Motif Window Manager.  When you attempt to map a
 921      * widget which is positioned (partially) off-screen, the window is
 922      * relocated to be entirely on screen. Good idea.  But if both the x
 923      * and the y coordinates are less than the origin (0,0), the first
 924      * (re)map will move the window to the origin, and any subsequent
 925      * (re)map will relocate the window at some other point on the screen.
 926      * I have written a short Motif test program to discover this bug.
 927      * This should occur infrequently and it does not cause any real
 928      * problem.  So for now we'll let it be.
 929      */
 930     static boolean needRemap(XDecoratedPeer window) {
 931         // Don't remap EmbeddedFrame,
 932         // e.g. for TrayIcon it causes problems.
 933         return !window.isEmbedded();
 934     }
 935 
 936     /*
 937      * Set decoration hints on the shell to wdata->decor adjusted
 938      * appropriately if not resizable.
 939      */
 940     static void setShellDecor(XDecoratedPeer window) {
 941         int decorations = window.getDecorations();
 942         int functions = window.getFunctions();
 943         boolean resizable = window.isResizable();
 944 
 945         if (!resizable) {
 946             if ((decorations & MWM_DECOR_ALL) != 0) {
 947                 decorations |= MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE;
 948             } else {
 949                 decorations &= ~(MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
 950             }
 951         }
 952         setMotifDecor(window, resizable, decorations, functions);
 953         setOLDecor(window, resizable, decorations);
 954 
 955         /* Some WMs need remap to redecorate the window */
 956         if (window.isShowing() && needRemap(window)) {
 957             /*
 958              * Do the re/mapping at the Xlib level.  Since we essentially
 959              * work around a WM bug we don't want this hack to be exposed
 960              * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
 961              */
 962             window.xSetVisible(false);
 963             XToolkit.XSync();
 964             window.xSetVisible(true);
 965         }
 966     }
 967 
 968     /*
 969      * Make specified shell resizable.
 970      */
 971     static void setShellResizable(XDecoratedPeer window) {
 972         if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell resizable " + window);
 973         XToolkit.awtLock();
 974         try {
 975             Rectangle shellBounds = window.getShellBounds();
 976             shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top);
 977             window.updateSizeHints(window.getDimensions());
 978             requestWMExtents(window.getWindow());
 979             XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
 980                                           shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
 981             /* REMINDER: will need to revisit when setExtendedStateBounds is added */
 982             //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
 983             //We need to update frame's minimum size, not to reset it
 984             removeSizeHints(window, PMaxSize);
 985             window.updateMinimumSize();
 986 
 987             /* Restore decorations */
 988             setShellDecor(window);
 989         } finally {
 990             XToolkit.awtUnlock();
 991         }
 992     }
 993 
 994     /*
 995      * Make specified shell non-resizable.
 996      * If justChangeSize is false, update decorations as well.
 997      * @param shellBounds bounds of the shell window
 998      */
 999     static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds,
1000                                      boolean justChangeSize)
1001     {
1002         if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions +
1003                                                        ", shellBounds " + shellBounds +", just change size: " + justChangeSize);
1004         XToolkit.awtLock();
1005         try {
1006             /* Fix min/max size hints at the specified values */
1007             if (!shellBounds.isEmpty()) {
1008                 window.updateSizeHints(newDimensions);
1009                 requestWMExtents(window.getWindow());
1010                 XToolkit.XSync();
1011                 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
1012                                               shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
1013             }
1014             if (!justChangeSize) {  /* update decorations */
1015                 setShellDecor(window);
1016             }
1017         } finally {
1018             XToolkit.awtUnlock();
1019         }
1020     }
1021 
1022 /*****************************************************************************\
1023  * Protocols support
1024  */
1025     HashMap<Class<?>, Collection<XProtocol>> protocolsMap = new HashMap<Class<?>, Collection<XProtocol>>();
1026     /**
1027      * Returns all protocols supporting given protocol interface
1028      */
1029     Collection<XProtocol> getProtocols(Class protocolInterface) {
1030         Collection<XProtocol> res = protocolsMap.get(protocolInterface);
1031         if (res != null) {
1032             return (Collection<XProtocol>)res;
1033         } else {
1034             return new LinkedList<XProtocol>();
1035         }
1036     }
1037 
1038     void addProtocol(Class protocolInterface, XProtocol protocol) {
1039         Collection<XProtocol> protocols = getProtocols(protocolInterface);
1040         protocols.add(protocol);
1041         protocolsMap.put(protocolInterface, protocols);
1042     }
1043 
1044     boolean supportsDynamicLayout() {
1045         int wm = getWMID();
1046         switch (wm) {
1047           case XWM.ENLIGHTEN_WM:
1048           case XWM.KDE2_WM:
1049           case XWM.SAWFISH_WM:
1050           case XWM.ICE_WM:
1051           case XWM.METACITY_WM:
1052               return true;
1053           case XWM.OPENLOOK_WM:
1054           case XWM.MOTIF_WM:
1055           case XWM.CDE_WM:
1056               return false;
1057           default:
1058               return false;
1059         }
1060     }
1061 
1062 
1063     /**
1064      * Check if state is supported.
1065      * Note that a compound state is always reported as not supported.
1066      * Note also that MAXIMIZED_BOTH is considered not a compound state.
1067      * Therefore, a compound state is just ICONIFIED | anything else.
1068      *
1069      */
1070     boolean supportsExtendedState(int state) {
1071         switch (state) {
1072           case Frame.MAXIMIZED_VERT:
1073           case Frame.MAXIMIZED_HORIZ:
1074               /*
1075                * WMs that talk NET/WIN protocol, but do not support
1076                * unidirectional maximization.
1077                */
1078               if (getWMID() == METACITY_WM) {
1079                   /* "This is a deliberate policy decision." -hp */
1080                   return false;
1081               }
1082               /* FALLTROUGH */
1083           case Frame.MAXIMIZED_BOTH:
1084               Iterator iter = getProtocols(XStateProtocol.class).iterator();
1085               while (iter.hasNext()) {
1086                   XStateProtocol proto = (XStateProtocol)iter.next();
1087                   if (proto.supportsState(state)) {
1088                       return true;
1089                   }
1090               }
1091           default:
1092               return false;
1093         }
1094     }
1095 
1096 /*****************************************************************************\
1097  *
1098  * Reading state from different protocols
1099  *
1100 \*****************************************************************************/
1101 
1102 
1103     int getExtendedState(XWindowPeer window) {
1104         Iterator iter = getProtocols(XStateProtocol.class).iterator();
1105         int state = 0;
1106         while (iter.hasNext()) {
1107             XStateProtocol proto = (XStateProtocol)iter.next();
1108             state |= proto.getState(window);
1109         }
1110         if (state != 0) {
1111             return state;
1112         } else {
1113             return Frame.NORMAL;
1114         }
1115     }
1116 
1117 /*****************************************************************************\
1118  *
1119  * Notice window state change when WM changes a property on the window ...
1120  *
1121 \*****************************************************************************/
1122 
1123 
1124     /*
1125      * Check if property change is a window state protocol message.
1126      * If it is - return the new state as Integer, otherwise return null
1127      */
1128     Integer isStateChange(XDecoratedPeer window, XPropertyEvent e) {
1129         if (!window.isShowing()) {
1130             stateLog.finer("Window is not showing");
1131             return null;
1132         }
1133 
1134         int wm_state = window.getWMState();
1135         if (wm_state == XlibWrapper.WithdrawnState) {
1136             stateLog.finer("WithdrawnState");
1137             return null;
1138         } else {
1139             stateLog.finer("Window WM_STATE is " + wm_state);
1140         }
1141         boolean is_state_change = false;
1142         if (e.get_atom() == XA_WM_STATE.getAtom()) {
1143             is_state_change = true;
1144         }
1145 
1146         Iterator iter = getProtocols(XStateProtocol.class).iterator();
1147         while (iter.hasNext()) {
1148             XStateProtocol proto = (XStateProtocol)iter.next();
1149             is_state_change |= proto.isStateChange(e);
1150         }
1151         int res = 0;
1152 
1153         if (is_state_change) {
1154             if (wm_state == XlibWrapper.IconicState) {
1155                 res = Frame.ICONIFIED;
1156             } else {
1157                 res = Frame.NORMAL;
1158             }
1159             res |= getExtendedState(window);
1160         }
1161         if (is_state_change) {
1162             return Integer.valueOf(res);
1163         } else {
1164             return null;
1165         }
1166     }
1167 
1168 /*****************************************************************************\
1169  *
1170  * Setting/changing window state ...
1171  *
1172 \*****************************************************************************/
1173 
1174     /**
1175      * Moves window to the specified layer, layer is one of the constants defined
1176      * in XLayerProtocol
1177      */
1178     void setLayer(XWindowPeer window, int layer) {
1179         Iterator iter = getProtocols(XLayerProtocol.class).iterator();
1180         while (iter.hasNext()) {
1181             XLayerProtocol proto = (XLayerProtocol)iter.next();
1182             if (proto.supportsLayer(layer)) {
1183                 proto.setLayer(window, layer);
1184             }
1185         }
1186         XToolkit.XSync();
1187     }
1188 
1189     void setExtendedState(XWindowPeer window, int state) {
1190         Iterator iter = getProtocols(XStateProtocol.class).iterator();
1191         while (iter.hasNext()) {
1192             XStateProtocol proto = (XStateProtocol)iter.next();
1193             if (proto.supportsState(state)) {
1194                 proto.setState(window, state);
1195                 break;
1196             }
1197         }
1198 
1199         if (!window.isShowing()) {
1200             /*
1201              * Purge KWM bits.
1202              * Not really tested with KWM, only with WindowMaker.
1203              */
1204             XToolkit.awtLock();
1205             try {
1206                 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1207                                             window.getWindow(),
1208                                             XA_KWM_WIN_ICONIFIED.getAtom());
1209                 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1210                                             window.getWindow(),
1211                                             XA_KWM_WIN_MAXIMIZED.getAtom());
1212             }
1213             finally {
1214                 XToolkit.awtUnlock();
1215             }
1216         }
1217         XToolkit.XSync();
1218     }
1219 
1220 
1221     /*
1222      * Work around for 4775545.
1223      *
1224      * If WM exits while the top-level is shaded, the shaded hint remains
1225      * on the top-level properties.  When WM restarts and sees the shaded
1226      * window it can reparent it into a "pre-shaded" decoration frame
1227      * (Metacity does), and our insets logic will go crazy, b/c it will
1228      * see a huge nagative bottom inset.  There's no clean solution for
1229      * this, so let's just be weasels and drop the shaded hint if we
1230      * detect that WM exited.  NB: we are in for a race condition with WM
1231      * restart here.  NB2: e.g. WindowMaker saves the state in a private
1232      * property that this code knows nothing about, so this workaround is
1233      * not effective; other WMs might play similar tricks.
1234      */
1235     void unshadeKludge(XDecoratedPeer window) {
1236         assert(window.isShowing());
1237 
1238         Iterator iter = getProtocols(XStateProtocol.class).iterator();
1239         while (iter.hasNext()) {
1240             XStateProtocol proto = (XStateProtocol)iter.next();
1241             proto.unshadeKludge(window);
1242         }
1243         XToolkit.XSync();
1244     }
1245 
1246     static boolean inited = false;
1247     static void init() {
1248         if (inited) {
1249             return;
1250         }
1251 
1252         initAtoms();
1253         getWM();
1254         inited = true;
1255     }
1256 
1257     void initializeProtocols() {
1258         XNETProtocol net_protocol = g_net_protocol;
1259         if (net_protocol != null) {
1260             if (!net_protocol.active()) {
1261                 net_protocol = null;
1262             } else {
1263                 if (net_protocol.doStateProtocol()) {
1264                     addProtocol(XStateProtocol.class, net_protocol);
1265                 }
1266                 if (net_protocol.doLayerProtocol()) {
1267                     addProtocol(XLayerProtocol.class, net_protocol);
1268                 }
1269             }
1270         }
1271 
1272         XWINProtocol win = g_win_protocol;
1273         if (win != null) {
1274             if (win.active()) {
1275                 if (win.doStateProtocol()) {
1276                     addProtocol(XStateProtocol.class, win);
1277                 }
1278                 if (win.doLayerProtocol()) {
1279                     addProtocol(XLayerProtocol.class, win);
1280                 }
1281             }
1282         }
1283     }
1284 
1285     HashMap storedInsets = new HashMap();
1286     Insets guessInsets(XDecoratedPeer window) {
1287         Insets res = (Insets)storedInsets.get(window.getClass());
1288         if (res == null) {
1289             switch (WMID) {
1290               case ENLIGHTEN_WM:
1291                   res = new Insets(19, 4, 4, 4);
1292                   break;
1293               case CDE_WM:
1294                   res = new Insets(28, 6, 6, 6);
1295                   break;
1296               case NO_WM:
1297               case LG3D_WM:
1298                   res = zeroInsets;
1299                   break;
1300               case MOTIF_WM:
1301               case OPENLOOK_WM:
1302               default:
1303                   res = defaultInsets;
1304             }
1305         }
1306         if (insLog.isLoggable(Level.FINEST)) insLog.finest("WM guessed insets: " + res);
1307         return res;
1308     }
1309     /*
1310      * Some buggy WMs ignore window gravity when processing
1311      * ConfigureRequest and position window as if the gravity is Static.
1312      * We work around this in MWindowPeer.pReshape().
1313      *
1314      * Starting with 1.5 we have introduced an Environment variable
1315      * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1316      * explicitly that the WM has this behaviour, example is FVWM.
1317      */
1318 
1319     static int awtWMStaticGravity = -1;
1320     static boolean configureGravityBuggy() {
1321 
1322         if (awtWMStaticGravity == -1) {
1323             awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1324         }
1325 
1326         if (awtWMStaticGravity == 1) {
1327             return true;
1328         }
1329 
1330         switch(getWMID()) {
1331           case XWM.ICE_WM:
1332               /*
1333                * See bug #228981 at IceWM's SourceForge pages.
1334                * Latest stable version 1.0.8-6 still has this problem.
1335                */
1336               /**
1337                * Version 1.2.2 doesn't have this problem
1338                */
1339               // Detect IceWM version
1340               if (g_net_protocol != null) {
1341                   String wm_name = g_net_protocol.getWMName();
1342                   Pattern pat = Pattern.compile("^IceWM (\\d+)\\.(\\d+)\\.(\\d+).*$");
1343                   try {
1344                       Matcher match = pat.matcher(wm_name);
1345                       if (match.matches()) {
1346                           int v1 = Integer.parseInt(match.group(1));
1347                           int v2 = Integer.parseInt(match.group(2));
1348                           int v3 = Integer.parseInt(match.group(3));
1349                           return !(v1 > 1 || (v1 == 1 && (v2 > 2 || (v2 == 2 && v3 >=2))));
1350                       }
1351                   } catch (Exception e) {
1352                       return true;
1353                   }
1354               }
1355               return true;
1356           case XWM.ENLIGHTEN_WM:
1357               /* At least E16 is buggy. */
1358               return true;
1359           default:
1360               return false;
1361         }
1362     }
1363 
1364     /*
1365      * @return if WM implements the insets property - returns insets with values
1366      * specified in that property, null otherwise.
1367      */
1368     public static Insets getInsetsFromExtents(long window) {
1369         if (window == XConstants.None) {
1370             return null;
1371         }
1372         XNETProtocol net_protocol = getWM().getNETProtocol();
1373         if (net_protocol != null && net_protocol.active()) {
1374             Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
1375             if (insLog.isLoggable(Level.FINE)) {
1376                 insLog.log(Level.FINE, "_NET_FRAME_EXTENTS: {0}", String.valueOf(insets));
1377             }
1378             if (insets != null) {
1379                 return insets;
1380             }
1381         }
1382         switch(getWMID()) {
1383           case XWM.KDE2_WM:
1384               return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT);
1385           case XWM.ENLIGHTEN_WM:
1386               return getInsetsFromProp(window, XA_E_FRAME_SIZE);
1387           default:
1388               return null;
1389         }
1390     }
1391 
1392     /**
1393      * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
1394      * and converts it to Insets object.
1395      */
1396     public static Insets getInsetsFromProp(long window, XAtom atom) {
1397         if (window == XConstants.None) {
1398             return null;
1399         }
1400 
1401         WindowPropertyGetter getter =
1402             new WindowPropertyGetter(window, atom,
1403                                      0, 4, false, XAtom.XA_CARDINAL);
1404         try {
1405             if (getter.execute() != XlibWrapper.Success
1406                 || getter.getData() == 0
1407                 || getter.getActualType() != XAtom.XA_CARDINAL
1408                 || getter.getActualFormat() != 32)
1409             {
1410                 return null;
1411             } else {
1412                 return new Insets((int)Native.getCard32(getter.getData(), 2), // top
1413                                   (int)Native.getCard32(getter.getData(), 0), // left
1414                                   (int)Native.getCard32(getter.getData(), 3), // bottom
1415                                   (int)Native.getCard32(getter.getData(), 1)); // right
1416             }
1417         } finally {
1418             getter.dispose();
1419         }
1420     }
1421 
1422     /**
1423      * Asks WM to fill Frame Extents (insets) for the window.
1424      */
1425     public static void requestWMExtents(long window) {
1426         if (window == XConstants.None) { // not initialized
1427             return;
1428         }
1429 
1430         log.fine("Requesting FRAME_EXTENTS");
1431 
1432         XClientMessageEvent msg = new XClientMessageEvent();
1433         msg.zero();
1434         msg.set_type(XlibWrapper.ClientMessage);
1435         msg.set_display(XToolkit.getDisplay());
1436         msg.set_window(window);
1437         msg.set_format(32);
1438         XToolkit.awtLock();
1439         try {
1440             XNETProtocol net_protocol = getWM().getNETProtocol();
1441             if (net_protocol != null && net_protocol.active()) {
1442                 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
1443                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1444                                        false, XlibWrapper.SubstructureRedirectMask | XlibWrapper.SubstructureNotifyMask,
1445                                        msg.getPData());
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, XlibWrapper.SubstructureRedirectMask | XlibWrapper.SubstructureNotifyMask,
1451                                        msg.getPData());
1452             }
1453             // XXX: should we wait for response? XIfEvent() would be useful here :)
1454         } finally {
1455             XToolkit.awtUnlock();
1456             msg.dispose();
1457         }
1458     }
1459 
1460     /* syncTopLEvelPos() is necessary to insure that the window manager has in
1461      * fact moved us to our final position relative to the reParented WM window.
1462      * We have noted a timing window which our shell has not been moved so we
1463      * screw up the insets thinking they are 0,0.  Wait (for a limited period of
1464      * time to let the WM hava a chance to move us.
1465      * @param window window ID of the shell, assuming it is the window
1466      * which will NOT have zero coordinates after the complete
1467      * reparenting
1468      */
1469     boolean syncTopLevelPos(long window, XWindowAttributes attrs) {
1470         int tries = 0;
1471         XToolkit.awtLock();
1472         try {
1473             do {
1474                 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData);
1475                 if (attrs.get_x() != 0 || attrs.get_y() != 0) {
1476                     return true;
1477                 }
1478                 tries++;
1479                 XToolkit.XSync();
1480             } while (tries < 50);
1481         }
1482         finally {
1483             XToolkit.awtUnlock();
1484         }
1485         return false;
1486     }
1487 
1488     Insets getInsets(XDecoratedPeer win, long window, long parent) {
1489         /*
1490          * Unfortunately the concept of "insets" borrowed to AWT
1491          * from Win32 is *absolutely*, *unbelievably* foreign to
1492          * X11.  Few WMs provide the size of frame decor
1493          * (i.e. insets) in a property they set on the client
1494          * window, so we check if we can get away with just
1495          * peeking at it.  [Future versions of wm-spec might add a
1496          * standardized hint for this].
1497          *
1498          * Otherwise we do some special casing.  Actually the
1499          * fallback code ("default" case) seems to cover most of
1500          * the existing WMs (modulo Reparent/Configure order
1501          * perhaps?).
1502          *
1503          * Fallback code tries to account for the two most common cases:
1504          *
1505          * . single reparenting
1506          *       parent window is the WM frame
1507          *       [twm, olwm, sawfish]
1508          *
1509          * . double reparenting
1510          *       parent is a lining exactly the size of the client
1511          *       grandpa is the WM frame
1512          *       [mwm, e!, kwin, fvwm2 ... ]
1513          */
1514         Insets correctWM = XWM.getInsetsFromExtents(window);
1515         if (insLog.isLoggable(Level.FINER)) {
1516             insLog.log(Level.FINER, "Got insets from property: {0}", String.valueOf(correctWM));
1517         }
1518 
1519         if (correctWM == null) {
1520             correctWM = new Insets(0,0,0,0);
1521 
1522             correctWM.top = -1;
1523             correctWM.left = -1;
1524 
1525             XWindowAttributes lwinAttr = new XWindowAttributes();
1526             XWindowAttributes pattr = new XWindowAttributes();
1527             try {
1528                 switch (XWM.getWMID()) {
1529                   /* should've been done in awt_wm_getInsetsFromProp */
1530                   case XWM.ENLIGHTEN_WM: {
1531                       /* enlightenment does double reparenting */
1532                       syncTopLevelPos(parent, lwinAttr);
1533                       correctWM.left = lwinAttr.get_x();
1534                       correctWM.top = lwinAttr.get_y();
1535                       /*
1536                        * Now get the actual dimensions of the parent window
1537                        * resolve the difference.  We can't rely on the left
1538                        * to be equal to right or bottom...  Enlightment
1539                        * breaks that assumption.
1540                        */
1541                       XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1542                                                        XlibUtil.getParentWindow(parent),
1543                                                        pattr.pData);
1544                       correctWM.right = pattr.get_width() -
1545                           (lwinAttr.get_width() + correctWM.left);
1546                       correctWM.bottom = pattr.get_height() -
1547                           (lwinAttr.get_height() + correctWM.top);
1548 
1549                       break;
1550                   }
1551                   case XWM.ICE_WM: // for 1.2.2.
1552                   case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */
1553                   case XWM.CDE_WM:
1554                   case XWM.MOTIF_WM: {
1555                       /* these are double reparenting too */
1556                       if (syncTopLevelPos(parent, lwinAttr)) {
1557                           correctWM.top = lwinAttr.get_y();
1558                           correctWM.left = lwinAttr.get_x();
1559                           correctWM.right = correctWM.left;
1560                           correctWM.bottom = correctWM.left;
1561                       } else {
1562                           return null;
1563                       }
1564                       break;
1565                   }
1566                   case XWM.SAWFISH_WM:
1567                   case XWM.OPENLOOK_WM: {
1568                       /* single reparenting */
1569                       syncTopLevelPos(window, lwinAttr);
1570                       correctWM.top    = lwinAttr.get_y();
1571                       correctWM.left   = lwinAttr.get_x();
1572                       correctWM.right  = correctWM.left;
1573                       correctWM.bottom = correctWM.left;
1574                       break;
1575                   }
1576                   case XWM.OTHER_WM:
1577                   default: {                /* this is very similar to the E! case above */
1578                       if (insLog.isLoggable(Level.FINEST)) {
1579                           insLog.log(Level.FINEST, "Getting correct insets for OTHER_WM/default, parent: {0}",
1580                                                    String.valueOf(parent));
1581                       }
1582                       syncTopLevelPos(parent, lwinAttr);
1583                       int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1584                                                                     window, lwinAttr.pData);
1585                       status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1586                                                                 parent, pattr.pData);
1587                       if (lwinAttr.get_root() == parent) {
1588                           insLog.finest("our parent is root so insets should be zero");
1589                           correctWM = new Insets(0, 0, 0, 0);
1590                           break;
1591                       }
1592 
1593                       /*
1594                        * Check for double-reparenting WM.
1595                        *
1596                        * If the parent is exactly the same size as the
1597                        * top-level assume taht it's the "lining" window and
1598                        * that the grandparent is the actual frame (NB: we
1599                        * have already handled undecorated windows).
1600                        *
1601                        * XXX: what about timing issues that syncTopLevelPos
1602                        * is supposed to work around?
1603                        */
1604                       if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
1605                           && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
1606                           && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
1607                       {
1608                           if (insLog.isLoggable(Level.FINEST)) {
1609                               insLog.log(Level.FINEST, "Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1610                                          new Object[] {String.valueOf(lwinAttr), String.valueOf(pattr),
1611                                                        String.valueOf(parent), String.valueOf(window)});
1612                           }
1613                           lwinAttr.set_x(pattr.get_x());
1614                           lwinAttr.set_y(pattr.get_y());
1615                           lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
1616 
1617                           final long grand_parent = XlibUtil.getParentWindow(parent);
1618 
1619                           if (grand_parent == lwinAttr.get_root()) {
1620                               // This is not double-reparenting in a
1621                               // general sense - we simply don't have
1622                               // correct insets - return null to try to
1623                               // get insets later
1624                               return null;
1625                           } else {
1626                               parent = grand_parent;
1627                               XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1628                                                                parent,
1629                                                                pattr.pData);
1630                           }
1631                       }
1632                       /*
1633                        * XXX: To be absolutely correct, we'd need to take
1634                        * parent's border-width into account too, but the
1635                        * rest of the code is happily unaware about border
1636                        * widths and inner/outer distinction, so for the time
1637                        * being, just ignore it.
1638                        */
1639                       if (insLog.isLoggable(Level.FINEST)) {
1640                           insLog.log(Level.FINEST, "Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1641                                      new Object[] {String.valueOf(lwinAttr), String.valueOf(pattr),
1642                                                    String.valueOf(parent), String.valueOf(window)});
1643                       }
1644                       correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
1645                                              lwinAttr.get_x() + lwinAttr.get_border_width(),
1646                                              pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
1647                                              pattr.get_width() -  (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
1648                       break;
1649                   } /* default */
1650                 } /* switch (runningWM) */
1651             } finally {
1652                 lwinAttr.dispose();
1653                 pattr.dispose();
1654             }
1655         }
1656         if (storedInsets.get(win.getClass()) == null) {
1657             storedInsets.put(win.getClass(), correctWM);
1658         }
1659         return correctWM;
1660     }
1661     boolean isDesktopWindow( long w ) {
1662         if (g_net_protocol != null) {
1663             XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
1664             return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
1665         } else {
1666             return false;
1667         }
1668     }
1669 
1670     public XNETProtocol getNETProtocol() {
1671         return g_net_protocol;
1672     }
1673 
1674     /**
1675      * Sets _NET_WN_ICON property on the window using the arrays of
1676      * raster-data for icons. If icons is null, removes _NET_WM_ICON
1677      * property.
1678      * This method invokes XNETProtocol.setWMIcon() for WMs that
1679      * support NET protocol.
1680      *
1681      * @return true if hint was modified successfully, false otherwise
1682      */
1683     public boolean setNetWMIcon(XWindowPeer window, java.util.List<XIconInfo> icons) {
1684         if (g_net_protocol != null && g_net_protocol.active()) {
1685             g_net_protocol.setWMIcons(window, icons);
1686             return getWMID() != ICE_WM;
1687         }
1688         return false;
1689     }
1690 }