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