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