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