1 /*
   2  * Copyright 2003-2008 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 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 
  68     XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING");    /* like STRING but encoding is UTF-8 */
  69 
  70 /* Currently we only care about max_v and max_h in _NET_WM_STATE */
  71     final static int AWT_NET_N_KNOWN_STATES=2;
  72 
  73 /* Enlightenment */
  74     final static XAtom XA_E_FRAME_SIZE = new XAtom();
  75 
  76 /* KWin (KDE2) */
  77     final static XAtom XA_KDE_NET_WM_FRAME_STRUT = new XAtom();
  78 
  79 /* KWM (KDE 1.x) OBSOLETE??? */
  80     final static XAtom XA_KWM_WIN_ICONIFIED = new XAtom();
  81     final static XAtom XA_KWM_WIN_MAXIMIZED = new XAtom();
  82 
  83 /* OpenLook */
  84     final static XAtom XA_OL_DECOR_DEL = new XAtom();
  85     final static XAtom XA_OL_DECOR_HEADER = new XAtom();
  86     final static XAtom XA_OL_DECOR_RESIZE = new XAtom();
  87     final static XAtom XA_OL_DECOR_PIN = new XAtom();
  88     final static XAtom XA_OL_DECOR_CLOSE = new XAtom();
  89 
  90 /* EWMH */
  91     final static XAtom XA_NET_FRAME_EXTENTS = new XAtom();
  92     final static XAtom XA_NET_REQUEST_FRAME_EXTENTS = new XAtom();
  93 
  94     final static int
  95         UNDETERMINED_WM = 1,
  96         NO_WM = 2,
  97         OTHER_WM = 3,
  98         OPENLOOK_WM = 4,
  99         MOTIF_WM = 5,
 100         CDE_WM = 6,
 101         ENLIGHTEN_WM = 7,
 102         KDE2_WM = 8,
 103         SAWFISH_WM = 9,
 104         ICE_WM = 10,
 105         METACITY_WM = 11,
 106         COMPIZ_WM = 12,
 107         LG3D_WM = 13;
 108     public String toString() {
 109         switch  (WMID) {
 110           case NO_WM:
 111               return "NO WM";
 112           case OTHER_WM:
 113               return "Other WM";
 114           case OPENLOOK_WM:
 115               return "OPENLOOK";
 116           case MOTIF_WM:
 117               return "MWM";
 118           case CDE_WM:
 119               return "DTWM";
 120           case ENLIGHTEN_WM:
 121               return "Enlightenment";
 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"             },
 173             { XA_KWM_WIN_MAXIMIZED,          "KWM_WIN_MAXIMIZED"             },
 174 
 175             { XA_OL_DECOR_DEL,               "_OL_DECOR_DEL"                 },
 176             { XA_OL_DECOR_HEADER,            "_OL_DECOR_HEADER"              },
 177             { XA_OL_DECOR_RESIZE,            "_OL_DECOR_RESIZE"              },
 178             { XA_OL_DECOR_PIN,               "_OL_DECOR_PIN"                 },
 179             { XA_OL_DECOR_CLOSE,             "_OL_DECOR_CLOSE"               },
 180             { XA_MWM_HINTS,                  "_MOTIF_WM_HINTS"               },
 181             { XA_NET_FRAME_EXTENTS,          "_NET_FRAME_EXTENTS"            },
 182             { XA_NET_REQUEST_FRAME_EXTENTS,  "_NET_REQUEST_FRAME_EXTENTS"    },
 183         };
 184 
 185         String[] names = new String[atomInitList.length];
 186         for (int index = 0; index < names.length; index++) {
 187             names[index] = (String)atomInitList[index][1];
 188         }
 189 
 190         int atomSize = XAtom.getAtomSize();
 191         long atoms = unsafe.allocateMemory(names.length*atomSize);
 192         XToolkit.awtLock();
 193         try {
 194             int status = XlibWrapper.XInternAtoms(XToolkit.getDisplay(), names, false, atoms);
 195             if (status == 0) {
 196                 return;
 197             }
 198             for (int atom = 0, atomPtr = 0; atom < names.length; atom++, atomPtr += atomSize) {
 199                 ((XAtom)(atomInitList[atom][0])).setValues(XToolkit.getDisplay(), names[atom], XAtom.getAtom(atoms + atomPtr));
 200             }
 201         } finally {
 202             XToolkit.awtUnlock();
 203             unsafe.freeMemory(atoms);
 204         }
 205     }
 206 
 207     /*
 208      * MUST BE CALLED UNDER AWTLOCK.
 209      *
 210      * If *any* window manager is running?
 211      *
 212      * According to ICCCM 2.0 section 4.3.
 213      * WM will acquire ownership of a selection named WM_Sn, where n is
 214      * the screen number.
 215      *
 216      * No selection owner, but, perhaps it is not ICCCM compliant WM
 217      * (e.g. CDE/Sawfish).
 218      * Try selecting for SubstructureRedirect, that only one client
 219      * can select for, and if the request fails, than some other WM is
 220      * already running.
 221      *
 222      * We also treat eXcursion as NO_WM.
 223      */
 224     private static boolean isNoWM() {
 225         /*
 226          * Quick checks for specific servers.
 227          */
 228         String vendor_string = XlibWrapper.ServerVendor(XToolkit.getDisplay());
 229         if (vendor_string.indexOf("eXcursion") != -1) {
 230             /*
 231              * Use NO_WM since in all other aspects eXcursion is like not
 232              * having a window manager running. I.e. it does not reparent
 233              * top level shells.
 234              */
 235             if (insLog.isLoggable(Level.FINE)) {
 236                 insLog.finer("eXcursion means NO_WM");
 237             }
 238             return true;
 239         }
 240 
 241         XSetWindowAttributes substruct = new XSetWindowAttributes();
 242         try {
 243             /*
 244              * Let's check an owner of WM_Sn selection for the default screen.
 245              */
 246             final long default_screen_number =
 247                 XlibWrapper.DefaultScreen(XToolkit.getDisplay());
 248             final String selection_name = "WM_S" + default_screen_number;
 249 
 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();
 291         }
 292     }
 293 
 294     static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom("ENLIGHTENMENT_COMMS", false);
 295     /*
 296      * Helper function for isEnlightenment().
 297      * Enlightenment uses STRING property for its comms window id.  Gaaa!
 298      * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
 299      * is "WINID %8x".  Gee, I haven't been using scanf for *ages*... :-)
 300      */
 301     static long getECommsWindowIDProperty(long window) {
 302 
 303         if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
 304             return 0;
 305         }
 306 
 307         WindowPropertyGetter getter =
 308             new WindowPropertyGetter(window, XA_ENLIGHTENMENT_COMMS, 0, 14, false,
 309                                      XAtom.XA_STRING);
 310         try {
 311             int status = getter.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 312             if (status != XConstants.Success || getter.getData() == 0) {
 313                 return 0;
 314             }
 315 
 316             if (getter.getActualType() != XAtom.XA_STRING
 317                 || getter.getActualFormat() != 8
 318                 || getter.getNumberOfItems() != 14 || getter.getBytesAfter() != 0)
 319             {
 320                 return 0;
 321             }
 322 
 323             // Convert data to String, ASCII
 324             byte[] bytes = XlibWrapper.getStringBytes(getter.getData());
 325             String id = new String(bytes);
 326 
 327             log.finer("ENLIGHTENMENT_COMMS is " + id);
 328 
 329             // Parse WINID
 330             Pattern winIdPat = Pattern.compile("WINID\\s+(\\p{XDigit}{0,8})");
 331             try {
 332                 Matcher match = winIdPat.matcher(id);
 333                 if (match.matches()) {
 334                     log.finest("Match group count: " + match.groupCount());
 335                     String longId = match.group(1);
 336                     log.finest("Match group 1 " + longId);
 337                     long winid = Long.parseLong(longId, 16);
 338                     log.finer("Enlightenment communication window " + winid);
 339                     return winid;
 340                 } else {
 341                     log.finer("ENLIGHTENMENT_COMMS has wrong format");
 342                     return 0;
 343                 }
 344             } catch (Exception e) {
 345                 if (log.isLoggable(Level.FINER)) {
 346                     e.printStackTrace();
 347                 }
 348                 return 0;
 349             }
 350         } finally {
 351             getter.dispose();
 352         }
 353     }
 354 
 355     /*
 356      * Is Enlightenment WM running?  Congruent to awt_wm_checkAnchor, but
 357      * uses STRING property peculiar to Enlightenment.
 358      */
 359     static boolean isEnlightenment() {
 360 
 361         long root_xref = getECommsWindowIDProperty(XToolkit.getDefaultRootWindow());
 362         if (root_xref == 0) {
 363             return false;
 364         }
 365 
 366         long self_xref = getECommsWindowIDProperty(root_xref);
 367         if (self_xref != root_xref) {
 368             return false;
 369         }
 370 
 371         return true;
 372     }
 373 
 374     /*
 375      * Is CDE running?
 376      *
 377      * XXX: This is hairy...  CDE is MWM as well.  It seems we simply test
 378      * for default setup and will be bitten if user changes things...
 379      *
 380      * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root.  Take the
 381      * second element of the property and check for presence of
 382      * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
 383      *
 384      * XXX: Any header that defines this structures???
 385      */
 386     static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom("_DT_SM_WINDOW_INFO", false);
 387     static final XAtom XA_DT_SM_STATE_INFO = new XAtom("_DT_SM_STATE_INFO", false);
 388     static boolean isCDE() {
 389 
 390         if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
 391             log.log(Level.FINER, "{0} is not interned", new Object[] {XA_DT_SM_WINDOW_INFO});
 392             return false;
 393         }
 394 
 395         WindowPropertyGetter getter =
 396             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
 397                                      XA_DT_SM_WINDOW_INFO, 0, 2,
 398                                      false, XA_DT_SM_WINDOW_INFO);
 399         try {
 400             int status = getter.execute();
 401             if (status != XConstants.Success || getter.getData() == 0) {
 402                 log.finer("Getting of _DT_SM_WINDOW_INFO is not successfull");
 403                 return false;
 404             }
 405             if (getter.getActualType() != XA_DT_SM_WINDOW_INFO.getAtom()
 406                 || getter.getActualFormat() != 32
 407                 || getter.getNumberOfItems() != 2 || getter.getBytesAfter() != 0)
 408             {
 409                 log.finer("Wrong format of _DT_SM_WINDOW_INFO");
 410                 return false;
 411             }
 412 
 413             long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4);
 414 
 415             if (wmwin == 0) {
 416                 log.fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows");
 417                 return false;
 418             }
 419 
 420             /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
 421             if (!XA_DT_SM_STATE_INFO.isInterned()) {
 422                 log.log(Level.FINER, "{0} is not interned", new Object[] {XA_DT_SM_STATE_INFO});
 423                 return false;
 424             }
 425             WindowPropertyGetter getter2 =
 426                 new WindowPropertyGetter(wmwin, XA_DT_SM_STATE_INFO, 0, 1,
 427                                          false, XA_DT_SM_STATE_INFO);
 428             try {
 429                 status = getter2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 430 
 431 
 432                 if (status != XConstants.Success || getter2.getData() == 0) {
 433                     log.finer("Getting of _DT_SM_STATE_INFO is not successfull");
 434                     return false;
 435                 }
 436                 if (getter2.getActualType() != XA_DT_SM_STATE_INFO.getAtom()
 437                     || getter2.getActualFormat() != 32)
 438                 {
 439                     log.finer("Wrong format of _DT_SM_STATE_INFO");
 440                     return false;
 441                 }
 442 
 443                 return true;
 444             } finally {
 445                 getter2.dispose();
 446             }
 447         } finally {
 448             getter.dispose();
 449         }
 450     }
 451 
 452     /*
 453      * Is MWM running?  (Note that CDE will test positive as well).
 454      *
 455      * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root.  Take the
 456      * second element of the property and check for presence of
 457      * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
 458      */
 459     static final XAtom XA_MOTIF_WM_INFO = new XAtom("_MOTIF_WM_INFO", false);
 460     static final XAtom XA_DT_WORKSPACE_CURRENT = new XAtom("_DT_WORKSPACE_CURRENT", false);
 461     static boolean isMotif() {
 462 
 463         if (!(XA_MOTIF_WM_INFO.isInterned()/* && XA_DT_WORKSPACE_CURRENT.isInterned()*/) ) {
 464             return false;
 465         }
 466 
 467         WindowPropertyGetter getter =
 468             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
 469                                      XA_MOTIF_WM_INFO, 0,
 470                                      MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS,
 471                                      false, XA_MOTIF_WM_INFO);
 472         try {
 473             int status = getter.execute();
 474 
 475             if (status != XConstants.Success || getter.getData() == 0) {
 476                 return false;
 477             }
 478 
 479             if (getter.getActualType() != XA_MOTIF_WM_INFO.getAtom()
 480                 || getter.getActualFormat() != 32
 481                 || getter.getNumberOfItems() != MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS
 482                 || getter.getBytesAfter() != 0)
 483             {
 484                 return false;
 485             }
 486 
 487             long wmwin = Native.getLong(getter.getData(), 1);
 488             if (wmwin != 0) {
 489                 if (XA_DT_WORKSPACE_CURRENT.isInterned()) {
 490                     /* Now check that this window has _DT_WORKSPACE_CURRENT */
 491                     XAtom[] curws = XA_DT_WORKSPACE_CURRENT.getAtomListProperty(wmwin);
 492                     if (curws.length == 0) {
 493                         return false;
 494                     }
 495                     return true;
 496                 } else {
 497                     // No DT_WORKSPACE, however in our tests MWM sometimes can be without desktop -
 498                     // and that is still MWM.  So simply check for the validity of this window
 499                     // (through WM_STATE property).
 500                     WindowPropertyGetter state_getter =
 501                         new WindowPropertyGetter(wmwin,
 502                                                  XA_WM_STATE,
 503                                                  0, 1, false,
 504                                                  XA_WM_STATE);
 505                     try {
 506                         if (state_getter.execute() == XConstants.Success &&
 507                             state_getter.getData() != 0 &&
 508                             state_getter.getActualType() == XA_WM_STATE.getAtom())
 509                         {
 510                             return true;
 511                         }
 512                     } finally {
 513                         state_getter.dispose();
 514                     }
 515                 }
 516             }
 517         } finally {
 518             getter.dispose();
 519         }
 520         return false;
 521     }
 522 
 523     /*
 524      * Is Sawfish running?
 525      */
 526     static boolean isSawfish() {
 527         return isNetWMName("Sawfish");
 528     }
 529 
 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() {
 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             log.log(Level.FINER, "{0} is not interned", new Object[] {XA_ICEWM_WINOPTHINT});
 603             return false;
 604         }
 605 
 606         XToolkit.awtLock();
 607         try {
 608             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 609             XlibWrapper.XChangePropertyS(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
 610                                          XA_ICEWM_WINOPTHINT.getAtom(),
 611                                          XA_ICEWM_WINOPTHINT.getAtom(),
 612                                          8, XConstants.PropModeReplace,
 613                                          new String(opt));
 614             XToolkit.RESTORE_XERROR_HANDLER();
 615 
 616             if (XToolkit.saved_error != null && XToolkit.saved_error.get_error_code() != XConstants.Success) {
 617                 log.finer("Erorr getting XA_ICEWM_WINOPTHINT property");
 618                 return false;
 619             }
 620             log.finer("Prepared for IceWM detection");
 621             return true;
 622         } finally {
 623             XToolkit.awtUnlock();
 624         }
 625     }
 626 
 627     /*
 628      * Is IceWM running?
 629      *
 630      * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
 631      * false positive will be reported.
 632      */
 633     static boolean isIceWM() {
 634         if (!XA_ICEWM_WINOPTHINT.isInterned()) {
 635             log.log(Level.FINER, "{0} is not interned", new Object[] {XA_ICEWM_WINOPTHINT});
 636             return false;
 637         }
 638 
 639         WindowPropertyGetter getter =
 640             new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
 641                                      XA_ICEWM_WINOPTHINT, 0, 0xFFFF,
 642                                      true, XA_ICEWM_WINOPTHINT);
 643         try {
 644             int status = getter.execute();
 645             boolean res = (status == XConstants.Success && getter.getActualType() != 0);
 646             log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res);
 647             return !res || isNetWMName("IceWM");
 648         } finally {
 649             getter.dispose();
 650         }
 651     }
 652 
 653     /*
 654      * Is OpenLook WM running?
 655      *
 656      * This one is pretty lame, but the only property peculiar to OLWM is
 657      * _SUN_WM_PROTOCOLS(ATOM[]).  Fortunately, olwm deletes it on exit.
 658      */
 659     static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false);
 660     static boolean isOpenLook() {
 661         if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
 662             return false;
 663         }
 664 
 665         XAtom[] list = XA_SUN_WM_PROTOCOLS.getAtomListProperty(XToolkit.getDefaultRootWindow());
 666         return (list.length != 0);
 667     }
 668 
 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             }
 744             XWINProtocol win = g_win_protocol = new XWINProtocol();
 745             win.detect();
 746 
 747             /* actual check for IceWM to follow below */
 748             boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */
 749 
 750             /*
 751              * Ok, some WM is out there.  Check which one by testing for
 752              * "distinguishing" atoms.
 753              */
 754             if (isEnlightenment()) {
 755                 awt_wmgr = XWM.ENLIGHTEN_WM;
 756             } else if (isMetacity()) {
 757                 awt_wmgr = XWM.METACITY_WM;
 758             } else if (isSawfish()) {
 759                 awt_wmgr = XWM.SAWFISH_WM;
 760             } else if (isKDE2()) {
 761                 awt_wmgr =XWM.KDE2_WM;
 762             } else if (isCompiz()) {
 763                 awt_wmgr = XWM.COMPIZ_WM;
 764             } else if (isLookingGlass()) {
 765                 awt_wmgr = LG3D_WM;
 766             } else if (doIsIceWM && isIceWM()) {
 767                 awt_wmgr = XWM.ICE_WM;
 768             }
 769             /*
 770              * We don't check for legacy WM when we already know that WM
 771              * supports WIN or _NET wm spec.
 772              */
 773             else if (l_net_protocol.active()) {
 774                 awt_wmgr = XWM.OTHER_WM;
 775             } else if (win.active()) {
 776                 awt_wmgr = XWM.OTHER_WM;
 777             }
 778             /*
 779              * Check for legacy WMs.
 780              */
 781             else if (isCDE()) { /* XXX: must come before isMotif */
 782                 awt_wmgr = XWM.CDE_WM;
 783             } else if (isMotif()) {
 784                 awt_wmgr = XWM.MOTIF_WM;
 785             } else if (isOpenLook()) {
 786                 awt_wmgr = XWM.OPENLOOK_WM;
 787             } else {
 788                 awt_wmgr = XWM.OTHER_WM;
 789             }
 790 
 791             return awt_wmgr;
 792         } finally {
 793             XToolkit.awtUnlock();
 794             substruct.dispose();
 795         }
 796     }
 797 
 798 
 799 /*****************************************************************************\
 800  *
 801  * Size and decoration hints ...
 802  *
 803 \*****************************************************************************/
 804 
 805 
 806     /*
 807      * Remove size hints specified by the mask.
 808      * XXX: Why do we need this in the first place???
 809      */
 810     static void removeSizeHints(XDecoratedPeer window, long mask) {
 811         mask &= XUtilConstants.PMaxSize | XUtilConstants.PMinSize;
 812 
 813         XToolkit.awtLock();
 814         try {
 815             XSizeHints hints = window.getHints();
 816             if ((hints.get_flags() & mask) == 0) {
 817                 return;
 818             }
 819 
 820             hints.set_flags(hints.get_flags() & ~mask);
 821             if (insLog.isLoggable(Level.FINER)) insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags()));
 822             XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(),
 823                                           window.getWindow(),
 824                                           hints.pData);
 825         } finally {
 826             XToolkit.awtUnlock();
 827         }
 828     }
 829 
 830     /*
 831      * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
 832      * to be subtracted from the decorations.  Normalize decoration spec
 833      * so that we can map motif decor to something else bit-by-bit in the
 834      * rest of the code.
 835      */
 836     static int normalizeMotifDecor(int decorations) {
 837         if ((decorations & MWMConstants.MWM_DECOR_ALL) == 0) {
 838             return decorations;
 839         }
 840         int d = MWMConstants.MWM_DECOR_BORDER | MWMConstants.MWM_DECOR_RESIZEH
 841             | MWMConstants.MWM_DECOR_TITLE
 842             | MWMConstants.MWM_DECOR_MENU | MWMConstants.MWM_DECOR_MINIMIZE
 843             | MWMConstants.MWM_DECOR_MAXIMIZE;
 844         d &= ~decorations;
 845         return d;
 846     }
 847 
 848     /*
 849      * If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken
 850      * to be subtracted from the functions.  Normalize function spec
 851      * so that we can map motif func to something else bit-by-bit in the
 852      * rest of the code.
 853      */
 854     static int normalizeMotifFunc(int functions) {
 855         if ((functions & MWMConstants.MWM_FUNC_ALL) == 0) {
 856             return functions;
 857         }
 858         int f = MWMConstants.MWM_FUNC_RESIZE |
 859                 MWMConstants.MWM_FUNC_MOVE |
 860                 MWMConstants.MWM_FUNC_MAXIMIZE |
 861                 MWMConstants.MWM_FUNC_MINIMIZE |
 862                 MWMConstants.MWM_FUNC_CLOSE;
 863         f &= ~functions;
 864         return f;
 865     }
 866 
 867     /*
 868      * Infer OL properties from MWM decorations.
 869      * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
 870      */
 871     static void setOLDecor(XWindow window, boolean resizable, int decorations) {
 872         if (window == null) {
 873             return;
 874         }
 875 
 876         XAtomList decorDel = new XAtomList();
 877         decorations = normalizeMotifDecor(decorations);
 878         if (insLog.isLoggable(Level.FINER)) insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations));
 879         if ((decorations & MWMConstants.MWM_DECOR_TITLE) == 0) {
 880             decorDel.add(XA_OL_DECOR_HEADER);
 881         }
 882         if ((decorations & (MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE)) == 0) {
 883             decorDel.add(XA_OL_DECOR_RESIZE);
 884         }
 885         if ((decorations & (MWMConstants.MWM_DECOR_MENU |
 886                             MWMConstants.MWM_DECOR_MAXIMIZE |
 887                             MWMConstants.MWM_DECOR_MINIMIZE)) == 0)
 888         {
 889             decorDel.add(XA_OL_DECOR_CLOSE);
 890         }
 891         if (decorDel.size() == 0) {
 892             insLog.finer("Deleting OL_DECOR");
 893             XA_OL_DECOR_DEL.DeleteProperty(window);
 894         } else {
 895             if (insLog.isLoggable(Level.FINER)) insLog.finer("Setting OL_DECOR to " + decorDel);
 896             XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel);
 897         }
 898     }
 899 
 900     /*
 901      * Set MWM decorations.  Set MWM functions depending on resizability.
 902      */
 903     static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) {
 904         /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
 905         if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0
 906             && (decorations != MWMConstants.MWM_DECOR_ALL))
 907         {
 908             decorations = normalizeMotifDecor(decorations);
 909         }
 910         if ((functions & MWMConstants.MWM_FUNC_ALL) != 0
 911             && (functions != MWMConstants.MWM_FUNC_ALL))
 912         {
 913             functions = normalizeMotifFunc(functions);
 914         }
 915 
 916         PropMwmHints hints = window.getMWMHints();
 917         hints.set_flags(hints.get_flags() |
 918                         MWMConstants.MWM_HINTS_FUNCTIONS |
 919                         MWMConstants.MWM_HINTS_DECORATIONS);
 920         hints.set_functions(functions);
 921         hints.set_decorations(decorations);
 922 
 923         if (stateLog.isLoggable(Level.FINER)) stateLog.finer("Setting MWM_HINTS to " + hints);
 924         window.setMWMHints(hints);
 925     }
 926 
 927     /*
 928      * Under some window managers if shell is already mapped, we MUST
 929      * unmap and later remap in order to effect the changes we make in the
 930      * window manager decorations.
 931      *
 932      * N.B.  This unmapping / remapping of the shell exposes a bug in
 933      * X/Motif or the Motif Window Manager.  When you attempt to map a
 934      * widget which is positioned (partially) off-screen, the window is
 935      * relocated to be entirely on screen. Good idea.  But if both the x
 936      * and the y coordinates are less than the origin (0,0), the first
 937      * (re)map will move the window to the origin, and any subsequent
 938      * (re)map will relocate the window at some other point on the screen.
 939      * I have written a short Motif test program to discover this bug.
 940      * This should occur infrequently and it does not cause any real
 941      * problem.  So for now we'll let it be.
 942      */
 943     static boolean needRemap(XDecoratedPeer window) {
 944         // Don't remap EmbeddedFrame,
 945         // e.g. for TrayIcon it causes problems.
 946         return !window.isEmbedded();
 947     }
 948 
 949     /*
 950      * Set decoration hints on the shell to wdata->decor adjusted
 951      * appropriately if not resizable.
 952      */
 953     static void setShellDecor(XDecoratedPeer window) {
 954         int decorations = window.getDecorations();
 955         int functions = window.getFunctions();
 956         boolean resizable = window.isResizable();
 957 
 958         if (!resizable) {
 959             if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0) {
 960                 decorations |= MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE;
 961             } else {
 962                 decorations &= ~(MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE);
 963             }
 964         }
 965         setMotifDecor(window, resizable, decorations, functions);
 966         setOLDecor(window, resizable, decorations);
 967 
 968         /* Some WMs need remap to redecorate the window */
 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.
1011      * @param shellBounds bounds of the shell window
1012      */
1013     static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds,
1014                                      boolean justChangeSize)
1015     {
1016         if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions +
1017                                                        ", shellBounds " + shellBounds +", just change size: " + justChangeSize);
1018         XToolkit.awtLock();
1019         try {
1020             /* Fix min/max size hints at the specified values */
1021             if (!shellBounds.isEmpty()) {
1022                 window.updateSizeHints(newDimensions);
1023                 requestWMExtents(window.getWindow());
1024                 XToolkit.XSync();
1025                 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
1026                                               shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
1027             }
1028             if (!justChangeSize) {  /* update decorations */
1029                 setShellDecor(window);
1030             }
1031         } finally {
1032             XToolkit.awtUnlock();
1033         }
1034     }
1035 
1036 /*****************************************************************************\
1037  * Protocols support
1038  */
1039     private HashMap<Class<?>, Collection<?>> protocolsMap = new HashMap<Class<?>, Collection<?>>();
1040     /**
1041      * Returns all protocols supporting given protocol interface
1042      */
1043     <T> Collection<T> getProtocols(Class<T> protocolInterface) {
1044         Collection<T> res = (Collection<T>) protocolsMap.get(protocolInterface);
1045         if (res != null) {
1046             return res;
1047         } else {
1048             return new LinkedList<T>();
1049         }
1050     }
1051 
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                */
1093               if (getWMID() == METACITY_WM) {
1094                   /* "This is a deliberate policy decision." -hp */
1095                   return false;
1096               }
1097               /* FALLTROUGH */
1098           case Frame.MAXIMIZED_BOTH:
1099               for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1100                   if (proto.supportsState(state)) {
1101                       return true;
1102                   }
1103               }
1104           default:
1105               return false;
1106         }
1107     }
1108 
1109 /*****************************************************************************\
1110  *
1111  * Reading state from different protocols
1112  *
1113 \*****************************************************************************/
1114 
1115 
1116     int getExtendedState(XWindowPeer window) {
1117         int state = 0;
1118         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1119             state |= proto.getState(window);
1120         }
1121         if (state != 0) {
1122             return state;
1123         } else {
1124             return Frame.NORMAL;
1125         }
1126     }
1127 
1128 /*****************************************************************************\
1129  *
1130  * Notice window state change when WM changes a property on the window ...
1131  *
1132 \*****************************************************************************/
1133 
1134 
1135     /*
1136      * Check if property change is a window state protocol message.
1137      */
1138     boolean isStateChange(XDecoratedPeer window, XPropertyEvent e) {
1139         if (!window.isShowing()) {
1140             stateLog.finer("Window is not showing");
1141             return false;
1142         }
1143 
1144         int wm_state = window.getWMState();
1145         if (wm_state == XUtilConstants.WithdrawnState) {
1146             stateLog.finer("WithdrawnState");
1147             return false;
1148         } else {
1149             stateLog.finer("Window WM_STATE is " + wm_state);
1150         }
1151         boolean is_state_change = false;
1152         if (e.get_atom() == XA_WM_STATE.getAtom()) {
1153             is_state_change = true;
1154         }
1155 
1156         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1157             is_state_change |= proto.isStateChange(e);
1158             stateLog.finest(proto + ": is state changed = " + is_state_change);
1159         }
1160         return is_state_change;
1161     }
1162 
1163     /*
1164      * Returns current state (including extended) of a given window.
1165      */
1166     int getState(XDecoratedPeer window) {
1167         int res = 0;
1168         final int wm_state = window.getWMState();
1169         if (wm_state == XUtilConstants.IconicState) {
1170             res = Frame.ICONIFIED;
1171         } else {
1172             res = Frame.NORMAL;
1173         }
1174         res |= getExtendedState(window);
1175         return res;
1176     }
1177 
1178 /*****************************************************************************\
1179  *
1180  * Setting/changing window state ...
1181  *
1182 \*****************************************************************************/
1183 
1184     /**
1185      * Moves window to the specified layer, layer is one of the constants defined
1186      * in XLayerProtocol
1187      */
1188     void setLayer(XWindowPeer window, int layer) {
1189         for (XLayerProtocol proto : getProtocols(XLayerProtocol.class)) {
1190             if (proto.supportsLayer(layer)) {
1191                 proto.setLayer(window, layer);
1192             }
1193         }
1194         XToolkit.XSync();
1195     }
1196 
1197     void setExtendedState(XWindowPeer window, int state) {
1198         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1199             if (proto.supportsState(state)) {
1200                 proto.setState(window, state);
1201                 break;
1202             }
1203         }
1204 
1205         if (!window.isShowing()) {
1206             /*
1207              * Purge KWM bits.
1208              * Not really tested with KWM, only with WindowMaker.
1209              */
1210             XToolkit.awtLock();
1211             try {
1212                 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1213                                             window.getWindow(),
1214                                             XA_KWM_WIN_ICONIFIED.getAtom());
1215                 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1216                                             window.getWindow(),
1217                                             XA_KWM_WIN_MAXIMIZED.getAtom());
1218             }
1219             finally {
1220                 XToolkit.awtUnlock();
1221             }
1222         }
1223         XToolkit.XSync();
1224     }
1225 
1226 
1227     /*
1228      * Work around for 4775545.
1229      *
1230      * If WM exits while the top-level is shaded, the shaded hint remains
1231      * on the top-level properties.  When WM restarts and sees the shaded
1232      * window it can reparent it into a "pre-shaded" decoration frame
1233      * (Metacity does), and our insets logic will go crazy, b/c it will
1234      * see a huge nagative bottom inset.  There's no clean solution for
1235      * this, so let's just be weasels and drop the shaded hint if we
1236      * detect that WM exited.  NB: we are in for a race condition with WM
1237      * restart here.  NB2: e.g. WindowMaker saves the state in a private
1238      * property that this code knows nothing about, so this workaround is
1239      * not effective; other WMs might play similar tricks.
1240      */
1241     void unshadeKludge(XDecoratedPeer window) {
1242         assert(window.isShowing());
1243 
1244         for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1245             proto.unshadeKludge(window);
1246         }
1247         XToolkit.XSync();
1248     }
1249 
1250     static boolean inited = false;
1251     static void init() {
1252         if (inited) {
1253             return;
1254         }
1255 
1256         initAtoms();
1257         getWM();
1258         inited = true;
1259     }
1260 
1261     void initializeProtocols() {
1262         XNETProtocol net_protocol = g_net_protocol;
1263         if (net_protocol != null) {
1264             if (!net_protocol.active()) {
1265                 net_protocol = null;
1266             } else {
1267                 if (net_protocol.doStateProtocol()) {
1268                     addProtocol(XStateProtocol.class, net_protocol);
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         }
1326 
1327         switch(getWMID()) {
1328           case XWM.ICE_WM:
1329               /*
1330                * See bug #228981 at IceWM's SourceForge pages.
1331                * Latest stable version 1.0.8-6 still has this problem.
1332                */
1333               /**
1334                * Version 1.2.2 doesn't have this problem
1335                */
1336               // Detect IceWM version
1337               if (g_net_protocol != null) {
1338                   String wm_name = g_net_protocol.getWMName();
1339                   Pattern pat = Pattern.compile("^IceWM (\\d+)\\.(\\d+)\\.(\\d+).*$");
1340                   try {
1341                       Matcher match = pat.matcher(wm_name);
1342                       if (match.matches()) {
1343                           int v1 = Integer.parseInt(match.group(1));
1344                           int v2 = Integer.parseInt(match.group(2));
1345                           int v3 = Integer.parseInt(match.group(3));
1346                           return !(v1 > 1 || (v1 == 1 && (v2 > 2 || (v2 == 2 && v3 >=2))));
1347                       }
1348                   } catch (Exception e) {
1349                       return true;
1350                   }
1351               }
1352               return true;
1353           case XWM.ENLIGHTEN_WM:
1354               /* At least E16 is buggy. */
1355               return true;
1356           default:
1357               return false;
1358         }
1359     }
1360 
1361     /*
1362      * @return if WM implements the insets property - returns insets with values
1363      * specified in that property, null otherwise.
1364      */
1365     public static Insets getInsetsFromExtents(long window) {
1366         if (window == XConstants.None) {
1367             return null;
1368         }
1369         XNETProtocol net_protocol = getWM().getNETProtocol();
1370         if (net_protocol != null && net_protocol.active()) {
1371             Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
1372             insLog.log(Level.FINE, "_NET_FRAME_EXTENTS: {0}", insets);
1373 
1374             if (insets != null) {
1375                 return insets;
1376             }
1377         }
1378         switch(getWMID()) {
1379           case XWM.KDE2_WM:
1380               return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT);
1381           case XWM.ENLIGHTEN_WM:
1382               return getInsetsFromProp(window, XA_E_FRAME_SIZE);
1383           default:
1384               return null;
1385         }
1386     }
1387 
1388     /**
1389      * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
1390      * and converts it to Insets object.
1391      */
1392     public static Insets getInsetsFromProp(long window, XAtom atom) {
1393         if (window == XConstants.None) {
1394             return null;
1395         }
1396 
1397         WindowPropertyGetter getter =
1398             new WindowPropertyGetter(window, atom,
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      *
1566      * @return true if hint was modified successfully, false otherwise
1567      */
1568     public boolean setNetWMIcon(XWindowPeer window, java.util.List<XIconInfo> icons) {
1569         if (g_net_protocol != null && g_net_protocol.active()) {
1570             g_net_protocol.setWMIcons(window, icons);
1571             return getWMID() != ICE_WM;
1572         }
1573         return false;
1574     }
1575 }