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