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