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