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