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