text: /*
   * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  #ifdef HEADLESS
      #error This file should not be included in headless library
  #endif
  
  /*
   * Some SCIENCE stuff happens, and it is CONFUSING
   */
  
  #include "awt_p.h"
  
  #include <X11/Xproto.h>
  #include <X11/Xlib.h>
  #include <X11/Xatom.h>
  #include <Xm/MwmUtil.h>
  
  /* JNI headers */
  #include "java_awt_Frame.h"     /* for frame state constants */
  
  #include "awt_wm.h"
  #include "awt_util.h"           /* for X11 error handling macros */
  
  /*
   * NB: 64 bit awareness.
   *
   * Since this code reads/writes window properties heavily, one thing
   * should be noted well.  Xlib uses C type 'long' for properties of
   * format 32.  Fortunately, typedef for Atom is 'long' as well, so
   * passing property data as or casting returned property data to
   * arrays of atoms is safe.
   */
  
  
  /*
   * Atoms used to communicate with window manager(s).
   * Naming convention:
   *   o  for atom  "FOO" the variable is  "XA_FOO"
   *   o  for atom "_BAR" the variable is "_XA_BAR"
   * Don't forget to add initialization to awt_wm_initAtoms below.
   */
  
  /*
   * Before WM rototill JDK used to check for running WM by just testing
   * if certain atom is interned or not.  We'd better not confuse older
   * JDK by interning these atoms.  Use awt_wm_atomInterned() to intern
   * them lazily.
   *
   * ENLIGHTENMENT_COMMS
   * _ICEWM_WINOPTHINT
   * _SAWMILL_TIMESTAMP
   * _DT_SM_WINDOW_INFO
   * _MOTIF_WM_INFO
   * _SUN_WM_PROTOCOLS
   */
  
  /* Good old ICCCM */
  static Atom XA_WM_STATE;
  
  /* New "netwm" spec from www.freedesktop.org */
  static Atom XA_UTF8_STRING;     /* like STRING but encoding is UTF-8 */
  static Atom _XA_NET_SUPPORTING_WM_CHECK;
  static Atom _XA_NET_SUPPORTED;  /* list of protocols (property of root) */
  static Atom _XA_NET_WM_NAME;    /* window property */
  static Atom _XA_NET_WM_STATE;   /* both window property and request */
  
  /*
   * _NET_WM_STATE is a list of atoms.
   * NB: Standard spelling is "HORZ" (yes, without an 'I'), but KDE2
   * uses misspelled "HORIZ" (see KDE bug #20229).  This was fixed in
   * KDE 2.2.  Under earlier versions of KDE2 horizontal and full
   * maximization doesn't work .
   */
  static Atom _XA_NET_WM_STATE_MAXIMIZED_HORZ;
  static Atom _XA_NET_WM_STATE_MAXIMIZED_VERT;
  static Atom _XA_NET_WM_STATE_SHADED;
  static Atom _XA_NET_WM_STATE_ABOVE;
  static Atom _XA_NET_WM_STATE_BELOW;
  static Atom _XA_NET_WM_STATE_HIDDEN;
  
  /* Currently we only care about max_v and max_h in _NET_WM_STATE */
  #define AWT_NET_N_KNOWN_STATES 2
  
  /* Gnome WM spec (superseded by "netwm" above, but still in use) */
  static Atom _XA_WIN_SUPPORTING_WM_CHECK;
  static Atom _XA_WIN_PROTOCOLS;
  static Atom _XA_WIN_STATE;
  static Atom _XA_WIN_LAYER;
  
  /* Enlightenment */
  static Atom _XA_E_FRAME_SIZE;
  
  /* KWin (KDE2) */
  static Atom _XA_KDE_NET_WM_FRAME_STRUT;
  
  /* KWM (KDE 1.x) OBSOLETE??? */
  static Atom XA_KWM_WIN_ICONIFIED;
  static Atom XA_KWM_WIN_MAXIMIZED;
  
  /* OpenLook */
  static Atom _XA_OL_DECOR_DEL;
  static Atom _XA_OL_DECOR_HEADER;
  static Atom _XA_OL_DECOR_RESIZE;
  static Atom _XA_OL_DECOR_PIN;
  static Atom _XA_OL_DECOR_CLOSE;
  
  /* For _NET_WM_STATE ClientMessage requests */
  #define _NET_WM_STATE_REMOVE    0 /* remove/unset property */
  #define _NET_WM_STATE_ADD       1 /* add/set property      */
  #define _NET_WM_STATE_TOGGLE    2 /* toggle property       */
  
  /* _WIN_STATE bits */
  #define WIN_STATE_STICKY          (1<<0) /* everyone knows sticky            */
  #define WIN_STATE_MINIMIZED       (1<<1) /* Reserved - definition is unclear */
  #define WIN_STATE_MAXIMIZED_VERT  (1<<2) /* window in maximized V state      */
  #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /* window in maximized H state      */
  #define WIN_STATE_HIDDEN          (1<<4) /* not on taskbar but window visible*/
  #define WIN_STATE_SHADED          (1<<5) /* shaded (MacOS / Afterstep style) */
  #define WIN_LAYER_ONTOP           6
  #define WIN_LAYER_NORMAL          4
  
  #define  URGENCY_HINT             (1<<8)
  #define  LAYER_ALWAYS_ON_TOP      1
  #define  LAYER_NORMAL             0
  
  
  /*
   * Intern a bunch of atoms we are going use.
   */
  static void
  awt_wm_initAtoms(void)
  {
      /* Minimize X traffic by creating atoms en mass...  This requires
         slightly more code but reduces number of server requests. */
      struct atominit {
          Atom *atomptr;
          const char *name;
      };
  
      /* Just add new atoms to this list */
      static struct atominit atom_list[] = {
          { &XA_WM_STATE,                      "WM_STATE"                      },
  
          { &XA_UTF8_STRING,                   "UTF8_STRING"                   },
  
          { &_XA_NET_SUPPORTING_WM_CHECK,      "_NET_SUPPORTING_WM_CHECK"      },
          { &_XA_NET_SUPPORTED,                "_NET_SUPPORTED"                },
          { &_XA_NET_WM_STATE,                 "_NET_WM_STATE"                 },
          { &_XA_NET_WM_STATE_MAXIMIZED_VERT,  "_NET_WM_STATE_MAXIMIZED_VERT"  },
          { &_XA_NET_WM_STATE_MAXIMIZED_HORZ,  "_NET_WM_STATE_MAXIMIZED_HORZ"  },
          { &_XA_NET_WM_STATE_SHADED,          "_NET_WM_STATE_SHADED"          },
          { &_XA_NET_WM_STATE_ABOVE,           "_NET_WM_STATE_ABOVE"           },
          { &_XA_NET_WM_STATE_BELOW,           "_NET_WM_STATE_BELOW"           },
          { &_XA_NET_WM_STATE_HIDDEN,          "_NET_WM_STATE_HIDDEN"          },
          { &_XA_NET_WM_NAME,                  "_NET_WM_NAME"                  },
  
          { &_XA_WIN_SUPPORTING_WM_CHECK,      "_WIN_SUPPORTING_WM_CHECK"      },
          { &_XA_WIN_PROTOCOLS,                "_WIN_PROTOCOLS"                },
          { &_XA_WIN_STATE,                    "_WIN_STATE"                    },
          { &_XA_WIN_LAYER,                    "_WIN_LAYER"                    },
  
          { &_XA_KDE_NET_WM_FRAME_STRUT,       "_KDE_NET_WM_FRAME_STRUT"       },
  
          { &_XA_E_FRAME_SIZE,                 "_E_FRAME_SIZE"                 },
  
          { &XA_KWM_WIN_ICONIFIED,             "KWM_WIN_ICONIFIED"             },
          { &XA_KWM_WIN_MAXIMIZED,             "KWM_WIN_MAXIMIZED"             },
  
          { &_XA_OL_DECOR_DEL,                 "_OL_DECOR_DEL"                 },
          { &_XA_OL_DECOR_HEADER,              "_OL_DECOR_HEADER"              },
          { &_XA_OL_DECOR_RESIZE,              "_OL_DECOR_RESIZE"              },
          { &_XA_OL_DECOR_PIN,                 "_OL_DECOR_PIN"                 },
          { &_XA_OL_DECOR_CLOSE,               "_OL_DECOR_CLOSE"               }
      };
  #define ATOM_LIST_LENGTH (sizeof(atom_list)/sizeof(atom_list[0]))
  
      const char *names[ATOM_LIST_LENGTH];
      Atom atoms[ATOM_LIST_LENGTH];
      Status status;
      size_t i;
  
      /* Fill the array of atom names */
      for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
          names[i] = atom_list[i].name;
      }
  
      DTRACE_PRINT("WM: initializing atoms ...  ");
      status = XInternAtoms(awt_display, (char**)names, ATOM_LIST_LENGTH,
                            False, atoms);
      if (status == 0) {
          DTRACE_PRINTLN("failed");
          return;
      }
  
      /* Store returned atoms into corresponding global variables */
      DTRACE_PRINTLN("ok");
      for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
          *atom_list[i].atomptr = atoms[i];
      }
  #undef ATOM_LIST_LENGTH
  }
  
  
  /*
   * When checking for various WMs don't intern certain atoms we use to
   * distinguish those WMs.  Rather check if the atom is interned first.
   * If it's not, further tests are not necessary anyway.
   * This also saves older JDK a great deal of confusion (4487993).
   */
  static Boolean
  awt_wm_atomInterned(Atom *pa, const char *name)
  {
      DASSERT(pa != NULL);
      if (*pa == None) {
          DASSERT(name != NULL);
          *pa = XInternAtom(awt_display, name, True);
          if (*pa == None) {
              DTRACE_PRINTLN1("\"%s\" is not interned", name);
              return False;
          } else {
              return True;
          }
      } else {
          return True;
      }
  }
  
  
  
  /*****************************************************************************\
   *
   * DTRACE utils for various states ...
   *
  \*****************************************************************************/
  
  
  static void
  awt_wm_dtraceWMState(uint32_t wm_state)
  {
  #ifdef DEBUG
      DTRACE_PRINT("WM_STATE = ");
      switch (wm_state) {
        case WithdrawnState:
            DTRACE_PRINTLN("Withdrawn");
            break;
        case NormalState:
            DTRACE_PRINTLN("Normal");
            break;
        case IconicState:
            DTRACE_PRINTLN("Iconic");
            break;
        default:
            DTRACE_PRINTLN1("unknown state %d", wm_state);
            break;
      }
  #endif /* DEBUG */
  }
  
  static void
  awt_wm_dtraceStateNet(Atom *net_wm_state, unsigned long nitems)
  {
  #ifdef DEBUG
      unsigned long i;
  
      DTRACE_PRINT("_NET_WM_STATE = {");
      for (i = 0; i < nitems; ++i) {
          char *name, *print_name;
          name = XGetAtomName(awt_display, net_wm_state[i]);
          if (name == NULL) {
              print_name = "???";
          } else if (strncmp(name, "_NET_WM_STATE", 13) == 0) {
              print_name = name + 13; /* skip common prefix to reduce noice */
          } else {
              print_name = name;
          }
          DTRACE_PRINT1(" %s", print_name);
          if (name) {
              XFree(name);
          }
      }
      DTRACE_PRINTLN(" }");
  #endif
  }
  
  
  static void
  awt_wm_dtraceStateWin(uint32_t win_state)
  {
  #ifdef DEBUG
      DTRACE_PRINT("_WIN_STATE = {");
      if (win_state & WIN_STATE_STICKY) {
          DTRACE_PRINT(" STICKY");
      }
      if (win_state & WIN_STATE_MINIMIZED) {
          DTRACE_PRINT(" MINIMIZED");
      }
      if (win_state & WIN_STATE_MAXIMIZED_VERT) {
          DTRACE_PRINT(" MAXIMIZED_VERT");
      }
      if (win_state & WIN_STATE_MAXIMIZED_HORIZ) {
          DTRACE_PRINT(" MAXIMIZED_HORIZ");
      }
      if (win_state & WIN_STATE_HIDDEN) {
          DTRACE_PRINT(" HIDDEN");
      }
      if (win_state & WIN_STATE_SHADED) {
          DTRACE_PRINT(" SHADED");
      }
      DTRACE_PRINTLN(" }");
  #endif
  }
  
  
  static void
  awt_wm_dtraceStateJava(jint java_state)
  {
  #ifdef DEBUG
      DTRACE_PRINT("java state = ");
      if (java_state == java_awt_Frame_NORMAL) {
          DTRACE_PRINTLN("NORMAL");
      }
      else {
          DTRACE_PRINT("{");
          if (java_state & java_awt_Frame_ICONIFIED) {
              DTRACE_PRINT(" ICONIFIED");
          }
          if ((java_state & java_awt_Frame_MAXIMIZED_BOTH)
                         == java_awt_Frame_MAXIMIZED_BOTH)
          {
              DTRACE_PRINT(" MAXIMIZED_BOTH");
          }
          else if (java_state & java_awt_Frame_MAXIMIZED_HORIZ) {
              DTRACE_PRINT(" MAXIMIZED_HORIZ");
          }
          else if (java_state & java_awt_Frame_MAXIMIZED_VERT) {
              DTRACE_PRINT(" MAXIMIZED_VERT");
          }
          DTRACE_PRINTLN(" }");
      }
  #endif /* DEBUG */
  }
  
  
  
  /*****************************************************************************\
   *
   * Utility functions ...
   *
  \*****************************************************************************/
  
  /*
   * Instead of validating window id, we simply call XGetWindowProperty,
   * but temporary install this function as the error handler to ignore
   * BadWindow error.
   */
  int /* but ingored */
  xerror_ignore_bad_window(Display *dpy, XErrorEvent *err)
  {
      XERROR_SAVE(err);
      if (err->error_code == BadWindow) {
          DTRACE_PRINTLN("IGNORING BadWindow");
          return 0; /* ok to fail */
      }
      else {
          return (*xerror_saved_handler)(dpy, err);
      }
  }
  
  
  /*
   * Convenience wrapper for XGetWindowProperty for XA_ATOM properties.
   * E.g. WM_PROTOCOLS, _NET_WM_STATE, _OL_DECOR_DEL.
   * It's up to caller to XFree returned value.
   * Number of items returned is stored to nitems_ptr (if non-null).
   */
  static Atom *
  awt_getAtomListProperty(Window w, Atom property, unsigned long *nitems_ptr)
  {
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems_stub;
      unsigned long bytes_after;
      Atom *list;
  
      if (nitems_ptr == NULL) {
          /* Caller is not interested in the number of items,
             provide a stub for XGetWindowProperty */
          nitems_ptr = &nitems_stub;
      }
  
      status = XGetWindowProperty(awt_display, w,
                   property, 0, 0xFFFF, False, XA_ATOM,
                   &actual_type, &actual_format, nitems_ptr, &bytes_after,
                   (unsigned char **)&list);
  
      if (status != Success || list == NULL) {
          *nitems_ptr = 0;
          return NULL;
      }
  
      if (actual_type != XA_ATOM || actual_format != 32) {
          XFree(list);
          *nitems_ptr = 0;
          return NULL;
      }
  
      if (*nitems_ptr == 0) {
          XFree(list);
          return NULL;
      }
  
      return list;
  }
  
  
  /*
   * Auxiliary function that returns the value of 'property' of type
   * 'property_type' on window 'w'.  Format of the property must be 8.
   * Terminating zero added by XGetWindowProperty is preserved.
   * It's up to caller to XFree the result.
   */
  static unsigned char *
  awt_getProperty8(Window w, Atom property, Atom property_type)
  {
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      unsigned char *string;
  
      /* BadWindow is ok and will be blocked by our special handler */
      WITH_XERROR_HANDLER(xerror_ignore_bad_window);
      {
          status = XGetWindowProperty(awt_display, w,
                       property, 0, 0xFFFF, False, property_type,
                       &actual_type, &actual_format, &nitems, &bytes_after,
                       &string);
      }
      RESTORE_XERROR_HANDLER;
  
      if (status != Success || string == NULL) {
          return NULL;
      }
  
      if (actual_type != property_type || actual_format != 8) {
          XFree(string);
          return NULL;
      }
  
      /* XGetWindowProperty kindly supplies terminating zero */
      return string;
  }
  
  
  /*
   * Auxiliary function that returns the value of 'property' of type
   * 'property_type' on window 'w'.  Format of the property must be 32.
   */
  static int32_t
  awt_getProperty32(Window w, Atom property, Atom property_type)
  {
      /* Property value*/
      int32_t value;
  
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
  
      /* BadWindow is ok and will be blocked by our special handler */
      WITH_XERROR_HANDLER(xerror_ignore_bad_window);
      {
          status = XGetWindowProperty(awt_display, w,
                       property, 0, 1, False, property_type,
                       &actual_type, &actual_format, &nitems, &bytes_after,
                       (unsigned char **)&data);
      }
      RESTORE_XERROR_HANDLER;
  
      if (status != Success || data == NULL) {
          return 0;
      }
  
      if (actual_type != property_type || actual_format != 32) {
          XFree(data);            /* NULL data already catched above */
          return 0;
      }
  
      value = (int32_t)*data;
      XFree(data);
  
      return value;
  }
  
  
  
  /*****************************************************************************\
   *
   * Detecting WM ...
   *
  \*****************************************************************************/
  
  
  
  /*
   * Check for anchor_prop(anchor_type) on root, take the value as the
   * window id and check if that window exists and has anchor_prop(anchor_type)
   * with the same value (i.e. pointing back to self).
   *
   * Returns the anchor window, as some WM may put interesting stuff in
   * its properties (e.g. sawfish).
   */
  static Window
  awt_wm_checkAnchor(Atom anchor_prop, Atom anchor_type)
  {
      Window root_xref;
      Window self_xref;
  
      root_xref = (Window)awt_getProperty32(DefaultRootWindow(awt_display),
                                            anchor_prop, anchor_type);
      if (root_xref == None) {
          DTRACE_PRINTLN("no");
          return None;
      }
  
      DTRACE_PRINT1("0x%x ...  ", (unsigned int)root_xref);
      self_xref = (Window)awt_getProperty32(root_xref,
                                            anchor_prop, anchor_type);
      if (self_xref != root_xref) {
          DTRACE_PRINTLN("stale");
          return None;
      }
  
      DTRACE_PRINTLN("ok");
      return self_xref;
  }
  
  
  /*
   * New WM spec: KDE 2.0.1, sawfish 0.3x, ...
   * <http://www.freedesktop.org/standards/wm-spec.html>
   */
  static Window
  awt_wm_isNetSupporting(void)
  {
      static Boolean checked = False;
      static Window isNetSupporting = None;
  
      if (checked) {
          return isNetSupporting;
      }
  
      DTRACE_PRINT("WM: checking for _NET_SUPPORTING ...  ");
      isNetSupporting = awt_wm_checkAnchor(_XA_NET_SUPPORTING_WM_CHECK,
                                           XA_WINDOW);
      checked = True;
      return isNetSupporting;
  }
  
  
  /*
   * Old Gnome WM spec: WindowMaker, Enlightenment, IceWM ...
   * <http://developer.gnome.org/doc/standards/wm/book1.html>
   */
  static Window
  awt_wm_isWinSupporting(void)
  {
      static Boolean checked = False;
      static Window isWinSupporting = None;
  
      if (checked) {
          return isWinSupporting;
      }
  
      DTRACE_PRINT("WM: checking for _WIN_SUPPORTING ...  ");
      isWinSupporting = awt_wm_checkAnchor(_XA_WIN_SUPPORTING_WM_CHECK,
                                           XA_CARDINAL);
      checked = True;
      return isWinSupporting;
  }
  
  
  /*
   * Check that that the list of protocols specified by WM in property
   * named LIST_NAME on the root window contains protocol PROTO.
   */
  static Boolean
  awt_wm_checkProtocol(Atom list_name, Atom proto)
  {
      Atom *protocols;
      unsigned long nproto;
      Boolean found;
      unsigned long i;
  
      protocols = awt_getAtomListProperty(DefaultRootWindow(awt_display),
                                          list_name, &nproto);
      if (protocols == NULL) {
          return False;
      }
  
      found = False;
      for (i = 0; i < nproto; ++i) {
          if (protocols[i] == proto) {
              found = True;
              break;
          }
      }
  
      if (protocols != NULL) {
          XFree(protocols);
      }
      return found;
  }
  
  static Boolean
  awt_wm_doStateProtocolNet(void)
  {
      static Boolean checked = False;
      static Boolean supported = False;
  
      if (checked) {
          return supported;
      }
  
      if (awt_wm_isNetSupporting()) {
          DTRACE_PRINT("WM: checking for _NET_WM_STATE in _NET_SUPPORTED ...  ");
          supported = awt_wm_checkProtocol(_XA_NET_SUPPORTED, _XA_NET_WM_STATE);
          DTRACE_PRINTLN1("%s", supported ? "yes" : "no");
      }
  
      checked = True;
      return supported;
  }
  
  static Boolean
  awt_wm_doStateProtocolWin(void)
  {
      static Boolean checked = False;
      static Boolean supported = False;
  
      if (checked) {
          return supported;
      }
  
      if (awt_wm_isWinSupporting()) {
          DTRACE_PRINT("WM: checking for _WIN_STATE in _WIN_PROTOCOLS ...  ");
          supported = awt_wm_checkProtocol(_XA_WIN_PROTOCOLS, _XA_WIN_STATE);
          DTRACE_PRINTLN1("%s", supported ? "yes" : "no");
      }
      checked = True;
      return supported;
  }
  
  
  
  /*
   * Helper function for awt_wm_isEnlightenment.
   * Enlightenment uses STRING property for its comms window id.  Gaaa!
   * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
   * is "WINID %8x".  Gee, I haven't been using scanf for *ages*... :-)
   */
  static Window
  awt_getECommsWindowIDProperty(Window w)
  {
      static Atom XA_ENLIGHTENMENT_COMMS = None;
  
      /* Property value*/
      Window value;
  
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      unsigned char *data;
  
      if (!awt_wm_atomInterned(&XA_ENLIGHTENMENT_COMMS, "ENLIGHTENMENT_COMMS")) {
          return False;
      }
  
      /* BadWindow is ok and will be blocked by our special handler */
      WITH_XERROR_HANDLER(xerror_ignore_bad_window);
      {
          status = XGetWindowProperty(awt_display, w,
                       XA_ENLIGHTENMENT_COMMS, 0, 14, False, XA_STRING,
                       &actual_type, &actual_format, &nitems, &bytes_after,
                       &data);
      }
      RESTORE_XERROR_HANDLER;
  
      if (status != Success || data == NULL) {
          DTRACE_PRINTLN("no ENLIGHTENMENT_COMMS");
          return None;
      }
  
      if (actual_type != XA_STRING || actual_format != 8
          || nitems != 14 || bytes_after != 0)
      {
          DTRACE_PRINTLN("malformed ENLIGHTENMENT_COMMS");
          XFree(data);            /* NULL data already catched above */
          return None;
      }
  
      value = None;
      sscanf((char *)data, "WINID %8lx", &value); /* NB: 64 bit: XID is long */
      XFree(data);
  
      return value;
  }
  
  
  /*
   * Is Enlightenment WM running?  Congruent to awt_wm_checkAnchor, but
   * uses STRING property peculiar to Enlightenment.
   */
  static Boolean
  awt_wm_isEnlightenment(void)
  {
      Window root_xref;
      Window self_xref;
  
      DTRACE_PRINT("WM: checking for Enlightenment ...  ");
      root_xref = awt_getECommsWindowIDProperty(DefaultRootWindow(awt_display));
      if (root_xref == None) {
          return False;
      }
  
      DTRACE_PRINT1("0x%x ...  ", root_xref);
      self_xref = awt_getECommsWindowIDProperty(root_xref);
      if (self_xref != root_xref) {
          return False;
      }
  
      DTRACE_PRINTLN("ok");
      return True;
  }
  
  
  /*
   * Is CDE running?
   *
   * XXX: This is hairy...  CDE is MWM as well.  It seems we simply test
   * for default setup and will be bitten if user changes things...
   *
   * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root.  Take the
   * second element of the property and check for presence of
   * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
   *
   * XXX: Any header that defines this structures???
   */
  static Boolean
  awt_wm_isCDE(void)
  {
      static Atom _XA_DT_SM_WINDOW_INFO = None;
      static Atom _XA_DT_SM_STATE_INFO = None;
  
      /* Property value*/
      Window wmwin;
  
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
  
      DTRACE_PRINT("WM: checking for CDE ...  ");
  
      if (!awt_wm_atomInterned(&_XA_DT_SM_WINDOW_INFO, "_DT_SM_WINDOW_INFO")) {
          return False;
      }
  
      status = XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
                   _XA_DT_SM_WINDOW_INFO, 0, 2, False, _XA_DT_SM_WINDOW_INFO,
                   &actual_type, &actual_format, &nitems, &bytes_after,
                   (unsigned char **)&data);
  
      if (status != Success || data == NULL) {
          DTRACE_PRINTLN("no _DT_SM_WINDOW_INFO on root");
          return False;
      }
  
      if (actual_type != _XA_DT_SM_WINDOW_INFO || actual_format != 32
          || nitems != 2 || bytes_after != 0)
      {
          DTRACE_PRINTLN("malformed _DT_SM_WINDOW_INFO on root");
          XFree(data);            /* NULL data already catched above */
          return False;
      }
  
      wmwin = (Window)data[1];
      XFree(data);
  
      /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
  
      if (!awt_wm_atomInterned(&_XA_DT_SM_STATE_INFO, "_DT_SM_STATE_INFO")) {
          return False;
      }
  
      /* BadWindow is ok and will be blocked by our special handler */
      WITH_XERROR_HANDLER(xerror_ignore_bad_window);
      {
          status = XGetWindowProperty(awt_display, wmwin,
                       _XA_DT_SM_STATE_INFO, 0, 1, False, _XA_DT_SM_STATE_INFO,
                       &actual_type, &actual_format, &nitems, &bytes_after,
                       (unsigned char **)&data);
      }
      RESTORE_XERROR_HANDLER;
  
      if (status != Success || data == NULL) {
          DTRACE_PRINTLN("no _DT_SM_STATE_INFO");
          return False;
      }
  
      if (actual_type != _XA_DT_SM_STATE_INFO || actual_format != 32) {
          DTRACE_PRINTLN("malformed _DT_SM_STATE_INFO");
          XFree(data);            /* NULL data already catched above */
          return False;
      }
  
      DTRACE_PRINTLN("yes");
      XFree(data);
      return True;
  }
  
  /*
   * Is MWM running?  (Note that CDE will test positive as well).
   *
   * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root.  Take the
   * second element of the property and check for presence of
   * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
   */
  static Boolean
  awt_wm_isMotif(void)
  {
      /*
       * Grr.  Motif just had to be different, ain't it!?  Everyone use
       * "XA" for things of type Atom, but motif folks chose to define
       * _XA_MOTIF_* to be atom *names*.  How pathetic...
       */
  #undef _XA_MOTIF_WM_INFO
      static Atom _XA_MOTIF_WM_INFO = None;
      static Atom _XA_DT_WORKSPACE_CURRENT = None;
  
      /* Property value */
      Window wmwin;
      Atom *curws;
  
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
  
      DTRACE_PRINT("WM: checking for MWM ...  ");
  
      if (!awt_wm_atomInterned(&_XA_MOTIF_WM_INFO, "_MOTIF_WM_INFO")
          || !awt_wm_atomInterned(&_XA_DT_WORKSPACE_CURRENT, "_DT_WORKSPACE_CURRENT"))
      {
          return False;
      }
  
  
      status = XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
                   _XA_MOTIF_WM_INFO, 0, PROP_MOTIF_WM_INFO_ELEMENTS, False,
                   _XA_MOTIF_WM_INFO, &actual_type,
                   &actual_format, &nitems, &bytes_after,
                   (unsigned char **)&data);
  
      if (status != Success || data == NULL) {
          DTRACE_PRINTLN("no _MOTIF_WM_INFO on root");
          return False;
      }
  
      if (actual_type != _XA_MOTIF_WM_INFO || actual_format != 32
          || nitems != PROP_MOTIF_WM_INFO_ELEMENTS || bytes_after != 0)
      {
          DTRACE_PRINTLN("malformed _MOTIF_WM_INFO on root");
          XFree(data);            /* NULL data already catched above */
          return False;
      }
  
      /* NB: 64 bit: Cannot cast data to MotifWmInfo */
      wmwin = (Window)data[1];
      XFree(data);
  
      /* Now check that this window has _DT_WORKSPACE_CURRENT */
      curws = awt_getAtomListProperty(wmwin, _XA_DT_WORKSPACE_CURRENT, NULL);
      if (curws == NULL) {
          DTRACE_PRINTLN("no _DT_WORKSPACE_CURRENT");
          return False;
      }
  
      DTRACE_PRINTLN("yes");
      XFree(curws);
      return True;
  }
  
  
  static Boolean
  awt_wm_isNetWMName(char *name)
  {
      Window anchor;
      unsigned char *net_wm_name;
      Boolean matched;
  
      anchor = awt_wm_isNetSupporting();
      if (anchor == None) {
          return False;
      }
  
      DTRACE_PRINT1("WM: checking for %s by _NET_WM_NAME ...  ", name);
  
      /*
       * Check both UTF8_STRING and STRING.  We only call this function
       * with ASCII names and UTF8 preserves ASCII bit-wise.  wm-spec
       * mandates UTF8_STRING for _NET_WM_NAME but at least sawfish-1.0
       * still uses STRING.  (mmm, moving targets...).
       */
      net_wm_name = awt_getProperty8(anchor, _XA_NET_WM_NAME, XA_UTF8_STRING);
      if (net_wm_name == NULL) {
          net_wm_name = awt_getProperty8(anchor, _XA_NET_WM_NAME, XA_STRING);
      }
  
      if (net_wm_name == NULL) {
          DTRACE_PRINTLN("no (missing _NET_WM_NAME)");
          return False;
      }
  
      matched = (strcmp((char *)net_wm_name, name) == 0);
      if (matched) {
          DTRACE_PRINTLN("yes");
      } else {
          DTRACE_PRINTLN1("no (_NET_WM_NAME = \"%s\")", net_wm_name);
      }
      XFree(net_wm_name);
      return matched;
  }
  
  /*
   * Is Sawfish running?
   */
  static Boolean
  awt_wm_isSawfish(void)
  {
      return awt_wm_isNetWMName("Sawfish");
  }
  
  /*
   * Is KDE2 (KWin) running?
   */
  static Boolean
  awt_wm_isKDE2(void)
  {
      return awt_wm_isNetWMName("KWin");
  }
  
  
  /*
   * Is Metacity running?
   */
  static Boolean
  awt_wm_isMetacity(void)
  {
      return awt_wm_isNetWMName("Metacity");
  }
  
  
  /*
   * Temporary error handler that ensures that we know if
   * XChangeProperty succeeded or not.
   */
  static int /* but ignored */
  xerror_verify_change_property(Display *dpy, XErrorEvent *err)
  {
      XERROR_SAVE(err);
      if (err->request_code == X_ChangeProperty) {
          return 0;
      }
      else {
          return (*xerror_saved_handler)(dpy, err);
      }
  }
  
  
  /*
   * Prepare IceWM check.
   *
   * The only way to detect IceWM, seems to be by setting
   * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
   * was immediately deleted by IceWM.
   *
   * But messing with PropertyNotify here is way too much trouble, so
   * approximate the check by setting the property in this function and
   * checking if it still exists later on.
   *
   * Gaa, dirty dances...
   */
  static Boolean
  awt_wm_prepareIsIceWM(void)
  {
      static Atom _XA_ICEWM_WINOPTHINT = None;
  
      /*
       * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
       * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
       */
      static unsigned char opt[] = {
          'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
          'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
          '0','\0'
      };
  
      DTRACE_PRINT("WM: scheduling check for IceWM ...  ");
  
      if (!awt_wm_atomInterned(&_XA_ICEWM_WINOPTHINT, "_ICEWM_WINOPTHINT")) {
          return False;
      }
  
      WITH_XERROR_HANDLER(xerror_verify_change_property);
      {
          XChangeProperty(awt_display, DefaultRootWindow(awt_display),
                          _XA_ICEWM_WINOPTHINT, _XA_ICEWM_WINOPTHINT, 8,
                          PropModeReplace, opt, sizeof(opt));
      }
      RESTORE_XERROR_HANDLER;
  
      if (xerror_code != Success) {
          DTRACE_PRINTLN1("can't set _ICEWM_WINOPTHINT, error = %d",
                          xerror_code);
          return False;
      }
      else {
          DTRACE_PRINTLN("scheduled");
          return True;
      }
  }
  
  /*
   * Is IceWM running?
   *
   * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
   * false positive will be reported.
   */
  static Boolean
  awt_wm_isIceWM(void)
  {
      static Atom _XA_ICEWM_WINOPTHINT = None;
  
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      unsigned char *data;
  
      DTRACE_PRINT("WM: checking for IceWM ...  ");
  
      if (!awt_wm_atomInterned(&_XA_ICEWM_WINOPTHINT, "_ICEWM_WINOPTHINT")) {
          return False;
      }
  
      XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
                   _XA_ICEWM_WINOPTHINT, 0, 0xFFFF, True, /* NB: deleting! */
                   _XA_ICEWM_WINOPTHINT, &actual_type,
                   &actual_format, &nitems, &bytes_after,
                   &data);
  
      if (data != NULL) {
          XFree(data);
      }
  
      if (actual_type == None) {
          DTRACE_PRINTLN("yes");
          return True;
      }
      else {
          DTRACE_PRINTLN("no");
          return False;
      }
  }
  
  /*
   * Is OpenLook WM running?
   *
   * This one is pretty lame, but the only property peculiar to OLWM is
   * _SUN_WM_PROTOCOLS(ATOM[]).  Fortunately, olwm deletes it on exit.
   */
  static Boolean
  awt_wm_isOpenLook(void)
  {
      static Atom _XA_SUN_WM_PROTOCOLS = None;
      Atom *list;
  
      DTRACE_PRINT("WM: checking for OpenLook WM ...  ");
  
      if (!awt_wm_atomInterned(&_XA_SUN_WM_PROTOCOLS, "_SUN_WM_PROTOCOLS")) {
          return False;
      }
  
      list = awt_getAtomListProperty(DefaultRootWindow(awt_display),
                                     _XA_SUN_WM_PROTOCOLS, NULL);
      if (list == NULL) {
          DTRACE_PRINTLN("no _SUN_WM_PROTOCOLS on root");
          return False;
      }
  
      DTRACE_PRINTLN("yes");
      XFree(list);
      return True;
  }
  
  
  
  static Boolean winmgr_running = False;
  
  /*
   * Temporary error handler that checks if selecting for
   * SubstructureRedirect failed.
   */
  static int /* but ignored */
  xerror_detect_wm(Display *dpy, XErrorEvent *err)
  {
      XERROR_SAVE(err);
      if (err->request_code == X_ChangeWindowAttributes
          && err->error_code == BadAccess)
      {
          DTRACE_PRINTLN("some WM is running (hmm, we'll see)");
          winmgr_running = True;
          return 0;
      }
      else {
          return (*xerror_saved_handler)(dpy, err);
      }
  }
  
  
  /*
   * Make an educated guess about running window manager.
   * XXX: ideally, we should detect wm restart.
   */
  enum wmgr_t
  awt_wm_getRunningWM(void)
  {
      /*
       * Ideally, we should support cases when a different WM is started
       * during a Java app lifetime.
       */
      static enum wmgr_t awt_wmgr = UNDETERMINED_WM;
  
      XSetWindowAttributes substruct;
      const char *vendor_string;
      Boolean doIsIceWM;
  
      if (awt_wmgr != UNDETERMINED_WM) {
          return awt_wmgr;
      }
  
      /*
       * Quick checks for specific servers.
       */
      vendor_string = ServerVendor(awt_display);
      if (strstr(vendor_string, "eXcursion") != NULL) {
          /*
           * Use NO_WM since in all other aspects eXcursion is like not
           * having a window manager running. I.e. it does not reparent
           * top level shells.
           */
          DTRACE_PRINTLN("WM: eXcursion detected - treating as NO_WM");
          awt_wmgr = NO_WM;
          return awt_wmgr;
      }
  
      /*
       * If *any* window manager is running?
       *
       * Try selecting for SubstructureRedirect, that only one client
       * can select for, and if the request fails, than some other WM is
       * already running.
       */
      winmgr_running = 0;
      substruct.event_mask = SubstructureRedirectMask;
  
      DTRACE_PRINT("WM: trying SubstructureRedirect ...  ");
      WITH_XERROR_HANDLER(xerror_detect_wm);
      {
          XChangeWindowAttributes(awt_display, DefaultRootWindow(awt_display),
                                  CWEventMask, &substruct);
      }
      RESTORE_XERROR_HANDLER;
  
      /*
       * If no WM is running than our selection for SubstructureRedirect
       * succeeded and needs to be undone (hey we are *not* a WM ;-).
       */
      if (!winmgr_running) {
          DTRACE_PRINTLN("no WM is running");
          awt_wmgr = NO_WM;
          substruct.event_mask = 0;
          XChangeWindowAttributes(awt_display, DefaultRootWindow(awt_display),
                                  CWEventMask, &substruct);
          return NO_WM;
      }
  
      /* actual check for IceWM to follow below */
      doIsIceWM = awt_wm_prepareIsIceWM(); /* and let IceWM to act */
  
      if (awt_wm_isNetSupporting()) {
          awt_wm_doStateProtocolNet();
      }
      if (awt_wm_isWinSupporting()) {
          awt_wm_doStateProtocolWin();
      }
  
      /*
       * Ok, some WM is out there.  Check which one by testing for
       * "distinguishing" atoms.
       */
      if (doIsIceWM && awt_wm_isIceWM()) {
          awt_wmgr = ICE_WM;
      }
      else if (awt_wm_isEnlightenment()) {
          awt_wmgr = ENLIGHTEN_WM;
      }
      else if (awt_wm_isMetacity()) {
          awt_wmgr = METACITY_WM;
      }
      else if (awt_wm_isSawfish()) {
          awt_wmgr = SAWFISH_WM;
      }
      else if (awt_wm_isKDE2()) {
          awt_wmgr = KDE2_WM;
      }
      /*
       * We don't check for legacy WM when we already know that WM
       * supports WIN or _NET wm spec.
       */
      else if (awt_wm_isNetSupporting()) {
          DTRACE_PRINTLN("WM: other WM (supports _NET)");
          awt_wmgr = OTHER_WM;
      }
      else if (awt_wm_isWinSupporting()) {
          DTRACE_PRINTLN("WM: other WM (supports _WIN)");
          awt_wmgr = OTHER_WM;
      }
      /*
       * Check for legacy WMs.
       */
      else if (awt_wm_isCDE()) {  /* XXX: must come before isMotif */
          awt_wmgr = CDE_WM;
      }
      else if (awt_wm_isMotif()) {
          awt_wmgr = MOTIF_WM;
      }
      else if (awt_wm_isOpenLook()) {
          awt_wmgr = OPENLOOK_WM;
      }
      else {
          DTRACE_PRINTLN("WM: some other legacy WM");
          awt_wmgr = OTHER_WM;
      }
  
      return awt_wmgr;
  }
  
  
  /*
   * Some buggy WMs ignore window gravity when processing
   * ConfigureRequest and position window as if the gravity is Static.
   * We work around this in MWindowPeer.pReshape().
   */
  Boolean
  awt_wm_configureGravityBuggy(void)
  {
      static int env_not_checked = 1;
      static int env_buggy = 0;
  
      if (env_not_checked) {
          DTRACE_PRINT("WM: checking for _JAVA_AWT_WM_STATIC_GRAVITY in environment ...  ");
          if (getenv("_JAVA_AWT_WM_STATIC_GRAVITY") != NULL) {
              DTRACE_PRINTLN("set");
              env_buggy = 1;
          } else {
              DTRACE_PRINTLN("no");
          }
          env_not_checked = 0;
      }
  
      if (env_buggy) {
          return True;
      }
  
      switch (awt_wm_getRunningWM()) {
        case ICE_WM:
            /*
             * See bug #228981 at IceWM's SourceForge pages.
             * Latest stable version 1.0.8-6 still has this problem.
             */
            return True;
  
        case ENLIGHTEN_WM:
            /* At least E16 is buggy. */
            return True;
  
        default:
            return False;
      }
  }
  
  /**
   * Check if state is supported.
   * Note that a compound state is always reported as not supported.
   * Note also that MAXIMIZED_BOTH is considered not a compound state.
   * Therefore, a compound state is just ICONIFIED | anything else.
   *
   */
  Boolean
  awt_wm_supportsExtendedState(jint state)
  {
      switch (state) {
        case java_awt_Frame_MAXIMIZED_VERT:
        case java_awt_Frame_MAXIMIZED_HORIZ:
            /*
             * WMs that talk NET/WIN protocol, but do not support
             * unidirectional maximization.
             */
            if (awt_wm_getRunningWM() == METACITY_WM) {
                /* "This is a deliberate policy decision." -hp */
                return JNI_FALSE;
            }
            /* FALLTROUGH */
        case java_awt_Frame_MAXIMIZED_BOTH:
            return (awt_wm_doStateProtocolNet() || awt_wm_doStateProtocolWin());
        default:
            return JNI_FALSE;
      }
  }
  
  
  
  
  /*****************************************************************************\
   *
   * Size and decoration hints ...
   *
  \*****************************************************************************/
  
  
  /*
   * Remove size hints specified by the mask.
   * XXX: Why do we need this in the first place???
   */
  void
  awt_wm_removeSizeHints(Widget shell, long mask)
  {
      Display *dpy = XtDisplay(shell);
      Window shell_win = XtWindow(shell);
      XSizeHints *hints = XAllocSizeHints();
      long ignore = 0;
  
      if (hints == NULL) {
          DTRACE_PRINTLN("WM: removeSizeHints FAILED to allocate XSizeHints");
          return;
      }
  
      /* sanitize the mask, only do these hints */
      mask &= (PMaxSize|PMinSize|USPosition|PPosition);
  
      XGetWMNormalHints(dpy, shell_win, hints, &ignore);
      if ((hints->flags & mask) == 0) {
          XFree(hints);
          return;
      }
  
  #ifdef DEBUG
      DTRACE_PRINT("WM: removing hints");
  
      if (mask & PMaxSize) {
          DTRACE_PRINT(" Max = ");
          if (hints->flags & PMaxSize) {
              DTRACE_PRINT2("%d x %d;", hints->max_width, hints->max_height);
          } else {
              DTRACE_PRINT("none;");
          }
      }
  
      if (mask & PMinSize) {
          DTRACE_PRINT(" Min = ");
          if (hints->flags & PMinSize) {
              DTRACE_PRINT2("%d x %d;", hints->min_width, hints->min_height);
          } else {
              DTRACE_PRINT("none;");
          }
      }
  
      DTRACE_PRINTLN("");
  #endif
  
      hints->flags &= ~mask;
      XSetWMNormalHints(dpy, shell_win, hints);
      XFree(hints);
  }
  
  /*
   *
   *
   */
  static void
  awt_wm_proclaimUrgency(struct FrameData *wdata)
  {
      Display *dpy = XtDisplay(wdata->winData.shell);
      Window shell_win = XtWindow(wdata->winData.shell);
  
      XWMHints *hints = XGetWMHints(dpy, shell_win);
      if( hints == NULL ) {
         /* For now just */ return;
      }
      if ((hints->flags & URGENCY_HINT) != 0) {
          /* it's here already */
          XFree(hints);
          return;
      }
      hints->flags |= URGENCY_HINT;
      XSetWMHints(dpy, shell_win, hints);
      XFree(hints);
  }
  
  /*
   * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
   * to be subtracted from the decorations.  Normalize decoration spec
   * so that we can map motif decor to something else bit-by-bit in the
   * rest of the code.
   */
  static int
  awt_wm_normalizeMotifDecor(int decorations)
  {
      int d;
  
      if (!(decorations & MWM_DECOR_ALL))
          return decorations;     /* already normalized */
  
      d = MWM_DECOR_BORDER |MWM_DECOR_RESIZEH | MWM_DECOR_TITLE
          | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE;
      d &= ~decorations;
      return d;
  }
  
  
  /*
   * Infer OL properties from MWM decorations.
   * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
   */
  static void
  awt_wm_setOLDecor(struct FrameData *wdata, Boolean resizable, int decorations)
  {
      Window shell_win = XtWindow(wdata->winData.shell);
      Atom decorDel[3];
      int nitems;
  
      if (shell_win == None) {
          DTRACE_PRINTLN("WM: setOLDecor - no window, returning");
          return;
      }
  
      decorations = awt_wm_normalizeMotifDecor(decorations);
      DTRACE_PRINT("WM: _OL_DECOR_DEL = {");
  
      nitems = 0;
      if (!(decorations & MWM_DECOR_TITLE)) {
          DTRACE_PRINT(" _OL_DECOR_HEADER");
          decorDel[nitems++] = _XA_OL_DECOR_HEADER;
      }
      if (!(decorations & (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE))) {
          DTRACE_PRINT(" _OL_DECOR_RESIZE");
          decorDel[nitems++] = _XA_OL_DECOR_RESIZE;
      }
      if (!(decorations & (MWM_DECOR_MENU | MWM_DECOR_MAXIMIZE
                           | MWM_DECOR_MINIMIZE)))
      {
          DTRACE_PRINT(" _OL_DECOR_CLOSE");
          decorDel[nitems++] = _XA_OL_DECOR_CLOSE;
      }
      DTRACE_PRINT(" }");
  
      if (nitems == 0) {
          DTRACE_PRINTLN(" ...  removing");
          XDeleteProperty(awt_display, shell_win, _XA_OL_DECOR_DEL);
      }
      else {
          DTRACE_PRINTLN(" ...  setting");
          XChangeProperty(awt_display, shell_win,
                          _XA_OL_DECOR_DEL, XA_ATOM, 32,
                          PropModeReplace, (unsigned char *)decorDel, nitems);
      }
  }
  
  /*
   * Set MWM decorations.  Infer MWM functions from decorations.
   */
  static void
  awt_wm_setMotifDecor(struct FrameData *wdata, Boolean resizable, int decorations)
  {
      int functions;
  
      /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
      if ((decorations & MWM_DECOR_ALL) && (decorations != MWM_DECOR_ALL)) {
          decorations = awt_wm_normalizeMotifDecor(decorations);
          DTRACE_PRINTLN1("WM: setMotifDecor normalize exclusions, decor = 0x%X",
                          decorations);
      }
  
      DTRACE_PRINT("WM: setMotifDecor functions = {");
      functions = 0;
  
      if (decorations & MWM_DECOR_ALL) {
          DTRACE_PRINT(" ALL");
          functions |= MWM_FUNC_ALL;
      }
      else {
          /*
           * Functions we always want to be enabled as mwm(1) and
           * descendants not only hide disabled functions away from
           * user, but also ignore corresponding requests from the
           * program itself (e.g. 4442047).
           */
          DTRACE_PRINT(" CLOSE MOVE MINIMIZE");
          functions |= (MWM_FUNC_CLOSE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE);
  
          if (resizable) {
              DTRACE_PRINT(" RESIZE MAXIMIZE");
              functions |= MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
          }
      }
  
      DTRACE_PRINTLN(" }");
  
      XtVaSetValues(wdata->winData.shell,
                    XmNmwmDecorations, decorations,
                    XmNmwmFunctions, functions,
                    NULL);
  }
  
  
  /*
   * Under some window managers if shell is already mapped, we MUST
   * unmap and later remap in order to effect the changes we make in the
   * window manager decorations.
   *
   * N.B.  This unmapping / remapping of the shell exposes a bug in
   * X/Motif or the Motif Window Manager.  When you attempt to map a
   * widget which is positioned (partially) off-screen, the window is
   * relocated to be entirely on screen. Good idea.  But if both the x
   * and the y coordinates are less than the origin (0,0), the first
   * (re)map will move the window to the origin, and any subsequent
   * (re)map will relocate the window at some other point on the screen.
   * I have written a short Motif test program to discover this bug.
   * This should occur infrequently and it does not cause any real
   * problem.  So for now we'll let it be.
   */
  static Boolean
  awt_wm_needRemap()
  {
      switch (awt_wm_getRunningWM()) {
  #if 0 /* XXX */
        case OPENLOOK_WM:
        case MOTIF_WM:
        case CDE_WM:
        case ICE_WM:
        case ENLIGHTEN_WM:
            return True;
  #endif
        default:
            return True;
      }
  }
  
  /*
   * Set decoration hints on the shell to wdata->decor adjusted
   * appropriately if not resizable.
   */
  void
  awt_wm_setShellDecor(struct FrameData *wdata, Boolean resizable)
  {
      int decorations = wdata->decor;
  
      DTRACE_PRINTLN3("WM: setShellDecor(0x%x/0x%x, %s)",
                      wdata->winData.shell, XtWindow(wdata->winData.shell),
                      resizable ? "resizable" : "not resizable");
  
      if (!resizable) {
          if (decorations & MWM_DECOR_ALL) {
              decorations |= (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
          }
          else {
              decorations &= ~(MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
          }
      }
  
      DTRACE_PRINTLN1("WM:     decorations = 0x%X", decorations);
      awt_wm_setMotifDecor(wdata, resizable, decorations);
      awt_wm_setOLDecor(wdata, resizable, decorations);
  
      /* Some WMs need remap to redecorate the window */
      if (wdata->isShowing && awt_wm_needRemap()) {
          /*
           * Do the re/mapping at the Xlib level.  Since we essentially
           * work around a WM bug we don't want this hack to be exposed
           * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
           */
          Display *dpy = XtDisplay(wdata->winData.shell);
          Window shell_win = XtWindow(wdata->winData.shell);
  
          DTRACE_PRINT("WM: setShellDecor REMAPPING ...  ");
          XUnmapWindow(dpy, shell_win);
          XSync(dpy, False);      /* give WM a chance to catch up */
          XMapWindow(dpy, shell_win);
          DTRACE_PRINTLN("done");
      }
  }
  
  
  /*
   * Make specified shell resizable.
   */
  void
  awt_wm_setShellResizable(struct FrameData *wdata)
  {
      DTRACE_PRINTLN2("WM: setShellResizable(0x%x/0x%x)",
                      wdata->winData.shell, XtWindow(wdata->winData.shell));
  
      XtVaSetValues(wdata->winData.shell,
                    XmNallowShellResize, True,
                    XmNminWidth,  XtUnspecifiedShellInt,
                    XmNminHeight, XtUnspecifiedShellInt,
                    XmNmaxWidth,  XtUnspecifiedShellInt,
                    XmNmaxHeight, XtUnspecifiedShellInt,
                    NULL);
  
      /* REMINDER: will need to revisit when setExtendedStateBounds is added */
      awt_wm_removeSizeHints(wdata->winData.shell, PMinSize|PMaxSize);
  
      /* Restore decorations */
      awt_wm_setShellDecor(wdata, True);
  }
  
  
  /*
   * Make specified shell non-resizable.
   * If justChangeSize is false, update decorations as well.
   */
  void
  awt_wm_setShellNotResizable(struct FrameData *wdata,
                              int32_t width, int32_t height,
                              Boolean justChangeSize)
  {
      DTRACE_PRINTLN5("WM: setShellNotResizable(0x%x/0x%x, %d, %d, %s)",
                      wdata->winData.shell, XtWindow(wdata->winData.shell),
                      width, height,
                      justChangeSize ? "size only" : "redecorate");
  
      /* Fix min/max size hints at the specified values */
      if ((width > 0) && (height > 0)) {
          XtVaSetValues(wdata->winData.shell,
                        XmNwidth,     (XtArgVal)width,
                        XmNheight,    (XtArgVal)height,
                        XmNminWidth,  (XtArgVal)width,
                        XmNminHeight, (XtArgVal)height,
                        XmNmaxWidth,  (XtArgVal)width,
                        XmNmaxHeight, (XtArgVal)height,
                        NULL);
      }
  
      if (!justChangeSize) {      /* update decorations */
          awt_wm_setShellDecor(wdata, False);
      }
  }
  
  
  /*
   * Helper function for awt_wm_getInsetsFromProp.
   * Read property of type CARDINAL[4] = { left, right, top, bottom }
   */
  static Boolean
  awt_wm_readInsetsArray(Window shell_win, Atom insets_property,
      int32_t *top, int32_t *left, int32_t *bottom, int32_t *right)
  {
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      long *insets = NULL;        /* NB: 64 bit: Format 32 props are 'long' */
  
      status = XGetWindowProperty (awt_display, shell_win,
                   insets_property, 0, 4, False, XA_CARDINAL,
                   &actual_type, &actual_format, &nitems, &bytes_after,
                   (unsigned char **)&insets);
  
      if (status != Success || insets == NULL) {
          DTRACE_PRINTLN("failed");
          return False;
      }
  
      if (actual_type != XA_CARDINAL || actual_format != 32) {
          DTRACE_PRINTLN("type/format mismatch");
          XFree(insets);
          return False;
      }
  
      *left   = (int32_t)insets[0];
      *right  = (int32_t)insets[1];
      *top    = (int32_t)insets[2];
      *bottom = (int32_t)insets[3];
      XFree(insets);
  
      /* Order is that of java.awt.Insets.toString */
      DTRACE_PRINTLN4("[top=%d,left=%d,bottom=%d,right=%d]",
                      *top, *left, *bottom, *right);
      return True;
  }
  
  /*
   * If WM implements the insets property - fill insets with values
   * specified in that property.
   */
  Boolean
  awt_wm_getInsetsFromProp(Window shell_win,
      int32_t *top, int32_t *left, int32_t *bottom, int32_t *right)
  {
      switch (awt_wm_getRunningWM()) {
  
        case ENLIGHTEN_WM:
            DTRACE_PRINT("WM: reading _E_FRAME_SIZE ...  ");
            return awt_wm_readInsetsArray(shell_win, _XA_E_FRAME_SIZE,
                                          top, left, bottom, right);
  
  #if 0
       /*
        * uwe: disabled for now, as KDE seems to supply bogus values
        * when we maximize iconified frame.  Need to verify with KDE2.1.
        * NB: Also note, that "external" handles (e.g. in laptop decor)
        * are also included in the frame strut, which is probably not
        * what we want.
        */
        case KDE2_WM:
            DTRACE_PRINT("WM: reading _KDE_NET_WM_FRAME_STRUT ...  ");
            return awt_wm_readInsetsArray(shell_win, _XA_KDE_NET_WM_FRAME_STRUT,
                                          top, left, bottom, right);
  #endif
  
        default:
            return False;
      }
  }
  
  /*
   * XmNiconic and Map/UnmapNotify (that XmNiconic relies on) are
   * unreliable, since mapping changes can happen for a virtual desktop
   * switch or MacOS style shading that became quite popular under X as
   * well.  Yes, it probably should not be this way, as it violates
   * ICCCM, but reality is that quite a lot of window managers abuse
   * mapping state.
   */
  int
  awt_wm_getWMState(Window shell_win)
  {
      /* Request status */
      int status;
  
      /* Returns of XGetWindowProperty */
      Atom actual_type;
      int actual_format;
      unsigned long nitems;
      unsigned long bytes_after;
      long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
  
      int wm_state;
  
      status = XGetWindowProperty(awt_display, shell_win,
                   XA_WM_STATE, 0, 1, False, XA_WM_STATE,
                   &actual_type, &actual_format, &nitems, &bytes_after,
                   (unsigned char **)&data);
  
      if (status != Success || data == NULL) {
          return WithdrawnState;
      }
  
      if (actual_type != XA_WM_STATE) {
          DTRACE_PRINTLN1("WM:     WM_STATE(0x%x) - wrong type", shell_win);
          XFree(data);
          return WithdrawnState;
      }
  
      wm_state = (int)*data;
      XFree(data);
      return wm_state;
  }
  
  
  
  /*****************************************************************************\
   *
   * Reading state from properties WM puts on our window ...
   *
  \*****************************************************************************/
  
  /*
   * New "NET" WM spec: _NET_WM_STATE/Atom[]
   */
  static jint
  awt_wm_getStateNet(Window shell_win)
  {
      Atom *net_wm_state;
      jint java_state;
      unsigned long nitems;
      unsigned long i;
  
      net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
      if (nitems == 0) {
          DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
          if (net_wm_state) {
              XFree(net_wm_state);
          }
          return java_awt_Frame_NORMAL;
      }
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateNet(net_wm_state, nitems);
  #endif
  
      java_state = java_awt_Frame_NORMAL;
      for (i = 0; i < nitems; ++i) {
          if (net_wm_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
              java_state |= java_awt_Frame_MAXIMIZED_VERT;
          }
          else if (net_wm_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
              java_state |= java_awt_Frame_MAXIMIZED_HORIZ;
          }
      }
      XFree(net_wm_state);
      return java_state;
  }
  
  Boolean
  awt_wm_isStateNetHidden(Window shell_win)
  {
      Atom *net_wm_state;
      Boolean result = False;
      unsigned long nitems;
      unsigned long i;
  
      net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
      if (nitems == 0) {
          DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
          if (net_wm_state) {
              XFree(net_wm_state);
          }
          return False;
      }
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateNet(net_wm_state, nitems);
  #endif
  
      for (i = 0; i < nitems; ++i) {
          if (net_wm_state[i] == _XA_NET_WM_STATE_HIDDEN) {
              result = True;
          }
      }
      XFree(net_wm_state);
      return result;
  }
  
  /*
   * Similar code to getStateNet, to get layer state.
   */
  static int
  awt_wm_getLayerNet(Window shell_win)
  {
      Atom *net_wm_state;
      int java_state;
      unsigned long nitems;
      unsigned long i;
  
      net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
      if (nitems == 0) {
          DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
          if (net_wm_state) {
              XFree(net_wm_state);
          }
          return LAYER_NORMAL;
      }
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateNet(net_wm_state, nitems);
  #endif
  
      java_state = LAYER_NORMAL;
      for (i = 0; i < nitems; ++i) {
          if (net_wm_state[i] == _XA_NET_WM_STATE_ABOVE) {
              java_state = LAYER_ALWAYS_ON_TOP;
          }
      }
      XFree(net_wm_state);
      return java_state;
  }
  
  /*
   * Old Gnome spec: _WIN_STATE/CARDINAL
   */
  static jint
  awt_wm_getStateWin(Window shell_win)
  {
      long win_state;
      jint java_state;
  
      win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateWin(win_state);
  #endif
  
      java_state = java_awt_Frame_NORMAL;
      if (win_state & WIN_STATE_MAXIMIZED_VERT) {
          java_state |= java_awt_Frame_MAXIMIZED_VERT;
      }
      if (win_state & WIN_STATE_MAXIMIZED_HORIZ) {
          java_state |= java_awt_Frame_MAXIMIZED_HORIZ;
      }
      return java_state;
  }
  
  /*
   * Code similar to getStateWin, to get layer state.
   */
  static int
  awt_wm_getLayerWin(Window shell_win)
  {
      long win_state;
      jint java_state;
  
      win_state = awt_getProperty32(shell_win, _XA_WIN_LAYER, XA_CARDINAL);
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateWin(win_state);
  #endif
  
      java_state = LAYER_NORMAL;
      if (win_state == WIN_LAYER_ONTOP) {
          java_state = LAYER_ALWAYS_ON_TOP;
      }
      return java_state;
  }
  
  
  static jint
  awt_wm_getExtendedState(Window shell_win)
  {
      if (awt_wm_doStateProtocolNet()) {
          return awt_wm_getStateNet(shell_win);
      }
      else if (awt_wm_doStateProtocolWin()) {
          return awt_wm_getStateWin(shell_win);
      }
      else {
          return java_awt_Frame_NORMAL;
      }
  }
  
  jint
  awt_wm_getState(struct FrameData *wdata)
  {
      Window shell_win = XtWindow(wdata->winData.shell);
      jint java_state;
  
      DTRACE_PRINTLN2("WM: getState(0x%x/0x%x)",
                      wdata->winData.shell, shell_win);
  
      if (shell_win == None) {
          DTRACE_PRINTLN("WM:     no window, use wdata");
          java_state = wdata->state;
      }
      else {
          int wm_state = awt_wm_getWMState(shell_win);
          if (wm_state == WithdrawnState) {
              DTRACE_PRINTLN("WM:     window withdrawn, use wdata");
              java_state = wdata->state;
          }
          else {
  #ifdef DEBUG
              DTRACE_PRINT("WM:     ");
              awt_wm_dtraceWMState(wm_state);
  #endif
              if (wm_state == IconicState) {
                  java_state = java_awt_Frame_ICONIFIED;
              } else {
                  java_state = java_awt_Frame_NORMAL;
              }
              java_state |= awt_wm_getExtendedState(shell_win);
          }
      }
  
  #ifdef DEBUG
      DTRACE_PRINT("WM: ");
      awt_wm_dtraceStateJava(java_state);
  #endif
  
      return java_state;
  }
  
  
  
  /*****************************************************************************\
   *
   * Notice window state change when WM changes a property on the window ...
   *
  \*****************************************************************************/
  
  
  /*
   * Check if property change is a window state protocol message.
   * If it is - return True and return the new state in *pstate.
   */
  Boolean
  awt_wm_isStateChange(struct FrameData *wdata, XPropertyEvent *e, jint *pstate)
  {
      Window shell_win = XtWindow(wdata->winData.shell);
      Boolean is_state_change = False;
      int wm_state;
  
      if (!wdata->isShowing) {
          return False;
      }
  
      wm_state = awt_wm_getWMState(shell_win);
      if (wm_state == WithdrawnState) {
          return False;
      }
  
      if (e->atom == XA_WM_STATE) {
          is_state_change = True;
      }
      else if (e->atom == _XA_NET_WM_STATE) {
          is_state_change = awt_wm_doStateProtocolNet();
      }
      else if (e->atom == _XA_WIN_STATE) {
          is_state_change = awt_wm_doStateProtocolWin();
      }
  
      if (is_state_change) {
  #ifdef DEBUG
          Widget shell = wdata->winData.shell;
          char *name = XGetAtomName(XtDisplay(shell), e->atom);
          DTRACE_PRINTLN4("WM: PropertyNotify(0x%x/0x%x) %s %s",
                          shell, XtWindow(shell),
                          name != NULL ? name : "???",
                          e->state == PropertyNewValue ? "changed" : "deleted");
          if (name != NULL) {
              XFree(name);
          }
          DTRACE_PRINT("WM:     ");
          awt_wm_dtraceWMState(wm_state);
  #endif
          if (wm_state == IconicState) {
              *pstate = java_awt_Frame_ICONIFIED;
          } else {
              *pstate = java_awt_Frame_NORMAL;
          }
          *pstate |= awt_wm_getExtendedState(shell_win);
  
  #ifdef DEBUG
          DTRACE_PRINT("WM: ");
          awt_wm_dtraceStateJava(*pstate);
  #endif
      }
  
      return is_state_change;
  }
  
  
  
  
  /*****************************************************************************\
   *
   * Setting/changing window state ...
   *
  \*****************************************************************************/
  
  /*
   * Request a state transition from a _NET supporting WM by sending
   * _NET_WM_STATE ClientMessage to root window.
   */
  static void
  awt_wm_requestStateNet(struct FrameData *wdata, jint state)
  {
      Widget shell = wdata->winData.shell;
      Window shell_win = XtWindow(shell);
      XClientMessageEvent req;
      jint old_net_state;
      jint max_changed;
  
      /* must use awt_wm_setInitialStateNet for withdrawn windows */
      DASSERT(wdata->isShowing);
  
      /*
       * We have to use toggle for maximization because of transitions
       * from maximization in one direction only to maximization in the
       * other direction only.
       */
      old_net_state = awt_wm_getStateNet(shell_win);
      max_changed = (state ^ old_net_state) & java_awt_Frame_MAXIMIZED_BOTH;
  
      switch (max_changed) {
        case 0:
            DTRACE_PRINTLN("WM: requestStateNet - maximization unchanged");
            return;
  
        case java_awt_Frame_MAXIMIZED_HORIZ:
            DTRACE_PRINTLN("WM: requestStateNet - toggling MAX_HORZ");
            req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
            req.data.l[2] = 0;
            break;
  
        case java_awt_Frame_MAXIMIZED_VERT:
            DTRACE_PRINTLN("WM: requestStateNet - toggling MAX_VERT");
            req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
            req.data.l[2] = 0;
            break;
  
        default: /* both */
            DTRACE_PRINTLN("WM: requestStateNet - toggling HORZ + VERT");
            req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
            req.data.l[2] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
            break;
      }
  
      req.type         = ClientMessage;
      req.window       = XtWindow(shell);
      req.message_type = _XA_NET_WM_STATE;
      req.format       = 32;
      req.data.l[0]    = _NET_WM_STATE_TOGGLE;
  
      XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
                 (SubstructureRedirectMask | SubstructureNotifyMask),
                 (XEvent *)&req);
  }
  
  
  /*
   * Request state transition from a Gnome WM (_WIN protocol) by sending
   * _WIN_STATE ClientMessage to root window.
   */
  static void
  awt_wm_requestStateWin(struct FrameData *wdata, jint state)
  {
      Widget shell = wdata->winData.shell;
      XClientMessageEvent req;
      long win_state;             /* typeof(XClientMessageEvent.data.l) */
  
      /* must use awt_wm_setInitialStateWin for withdrawn windows */
      DASSERT(wdata->isShowing);
  
      win_state = 0;
      if (state & java_awt_Frame_MAXIMIZED_VERT) {
          win_state |= WIN_STATE_MAXIMIZED_VERT;
      }
      if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
          win_state |= WIN_STATE_MAXIMIZED_HORIZ;
      }
  
      req.type         = ClientMessage;
      req.window       = XtWindow(shell);
      req.message_type = _XA_WIN_STATE;
      req.format       = 32;
      req.data.l[0]    = (WIN_STATE_MAXIMIZED_HORIZ | WIN_STATE_MAXIMIZED_VERT);
      req.data.l[1]    = win_state;
  
      XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
                 (SubstructureRedirectMask | SubstructureNotifyMask),
                 (XEvent *)&req);
  }
  
  
  /*
   * Specify initial state for _NET supporting WM by setting
   * _NET_WM_STATE property on the window to the desired state before
   * mapping it.
   */
  static void
  awt_wm_setInitialStateNet(struct FrameData *wdata, jint state)
  {
      Widget shell = wdata->winData.shell;
      Window shell_win = XtWindow(shell);
      Display *dpy = XtDisplay(shell);
  
      Atom *old_state;
      unsigned long nitems;
  
      /* must use awt_wm_requestStateNet for managed windows */
      DASSERT(!wdata->isShowing);
  
      /* Be careful to not wipe out state bits we don't understand */
      old_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
  
      if (nitems == 0) {
          /*
           * Empty or absent _NET_WM_STATE - set a new one if necessary.
           */
          Atom net_wm_state[AWT_NET_N_KNOWN_STATES];
  
          if (old_state != NULL) {
              XFree(old_state);
          }
  
          if (state & java_awt_Frame_MAXIMIZED_VERT) {
              net_wm_state[nitems++] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
          }
          if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
              net_wm_state[nitems++] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
          }
          DASSERT(nitems <= AWT_NET_N_KNOWN_STATES);
  
          if (nitems == 0) {
              DTRACE_PRINTLN("WM:     initial _NET_WM_STATE not necessary");
              return;
          }
  
  #ifdef DEBUG
          DTRACE_PRINT("WM:     setting initial ");
          awt_wm_dtraceStateNet(net_wm_state, nitems);
  #endif
          XChangeProperty(dpy, shell_win,
                          _XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
                          (unsigned char *)net_wm_state, nitems);
      }
      else {
          /*
           * Tweak existing _NET_WM_STATE, preserving bits we don't use.
           */
          jint want= state        /* which flags we want */
              & (java_awt_Frame_MAXIMIZED_HORIZ | java_awt_Frame_MAXIMIZED_VERT);
  
          jint has = 0;           /* which flags the window already has */
          int mode;               /* property mode: replace/append */
  
          Atom *new_state;        /* new _net_wm_state value */
          int new_nitems;
          unsigned long i;
  
  #ifdef DEBUG
          DTRACE_PRINT("WM:     already has ");
          awt_wm_dtraceStateNet(old_state, nitems);
  #endif
  
          for (i = 0; i < nitems; ++i) {
              if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
                  has |= java_awt_Frame_MAXIMIZED_HORIZ;
              }
              else if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
                  has |= java_awt_Frame_MAXIMIZED_VERT;
              }
          }
  
          if ((has ^ want) == 0) {
              DTRACE_PRINTLN("WM:     no changes to _NET_WM_STATE necessary");
              XFree(old_state);
              return;
          }
  
          new_nitems = 0;
          if (has == 0) {         /* only adding flags */
              new_state = calloc(AWT_NET_N_KNOWN_STATES, sizeof(Atom));
              mode = PropModeAppend;
          }
          else {
              new_state = calloc(nitems + AWT_NET_N_KNOWN_STATES, sizeof(Atom));
              mode = PropModeReplace;
          }
  
          if (has != 0) {         /* copy existing flags */
              DTRACE_PRINT("WM:    ");
              for (i = 0; i < nitems; ++i) {
                  if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
                      if (want & java_awt_Frame_MAXIMIZED_HORIZ) {
                          DTRACE_PRINT(" keep _HORZ");
                      } else {
                          DTRACE_PRINT(" drop _HORZ");
                          continue;
                      }
                  }
                  else if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
                      if (want & java_awt_Frame_MAXIMIZED_VERT) {
                          DTRACE_PRINT(" keep _VERT");
                      } else {
                          DTRACE_PRINT(" drop _VERT");
                          continue;
                      }
                  }
                  new_state[new_nitems++] = old_state[i];
              }
          }
  
          /* Add missing flags */
          if ((want & java_awt_Frame_MAXIMIZED_HORIZ)
               && !(has & java_awt_Frame_MAXIMIZED_HORIZ))
          {
              DTRACE_PRINT(" add _HORZ");
              new_state[new_nitems] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
              ++new_nitems;
          }
          if ((want & java_awt_Frame_MAXIMIZED_VERT)
               && !(has & java_awt_Frame_MAXIMIZED_VERT))
          {
              DTRACE_PRINT(" add _VERT");
              new_state[new_nitems] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
              ++new_nitems;
          }
  
          DTRACE_PRINTLN(mode == PropModeReplace ?
                         " ...  replacing" : " ...  appending");
          XChangeProperty(dpy, shell_win,
                          _XA_NET_WM_STATE, XA_ATOM, 32, mode,
                          (unsigned char *)new_state, new_nitems);
          XFree(old_state);
          XFree(new_state);
      }
  }
  
  
  /*
   * Specify initial state for a Gnome WM (_WIN protocol) by setting
   * WIN_STATE property on the window to the desired state before
   * mapping it.
   */
  static void
  awt_wm_setInitialStateWin(struct FrameData *wdata, jint state)
  {
      Display *dpy = XtDisplay(wdata->winData.shell);
      Window shell_win = XtWindow(wdata->winData.shell);
      long win_state, old_win_state;
  
      /* must use awt_wm_requestStateWin for managed windows */
      DASSERT(!wdata->isShowing);
  
      /* Be careful to not wipe out state bits we don't understand */
      win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
      old_win_state = win_state;
  #ifdef DEBUG
      if (win_state != 0) {
          DTRACE_PRINT("WM:     already has ");
          awt_wm_dtraceStateWin(win_state);
      }
  #endif
  
      /*
       * In their stupid quest of reinventing every wheel, Gnome WM spec
       * have its own "minimized" hint (instead of using initial state
       * and WM_STATE hints).  This is bogus, but, apparently, some WMs
       * pay attention.
       */
      if (state & java_awt_Frame_ICONIFIED) {
          win_state |= WIN_STATE_MINIMIZED;
      } else {
          win_state &= ~WIN_STATE_MINIMIZED;
      }
  
      if (state & java_awt_Frame_MAXIMIZED_VERT) {
          win_state |= WIN_STATE_MAXIMIZED_VERT;
      } else {
          win_state &= ~WIN_STATE_MAXIMIZED_VERT;
      }
  
      if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
          win_state |= WIN_STATE_MAXIMIZED_HORIZ;
      } else {
          win_state &= ~WIN_STATE_MAXIMIZED_HORIZ;
      }
  
      if (old_win_state ^ win_state) {
  #ifdef DEBUG
          DTRACE_PRINT("WM:     setting initial ");
          awt_wm_dtraceStateWin(win_state);
  #endif
          XChangeProperty(dpy, shell_win,
            _XA_WIN_STATE, XA_CARDINAL, 32, PropModeReplace,
            (unsigned char *)&win_state, 1);
      }
  #ifdef DEBUG
      else {
          DTRACE_PRINTLN("WM:     no changes to _WIN_STATE necessary");
      }
  #endif
  }
  
  /*
   * Request a layer change from a _NET supporting WM by sending
   * _NET_WM_STATE ClientMessage to root window.
   */
  static void
  awt_wm_requestLayerNet(struct FrameData *wdata, int state)
  {
      Widget shell = wdata->winData.shell;
      Window shell_win = XtWindow(shell);
      XClientMessageEvent req;
      int currentLayer;
      long cmd;
  
      /* must use awt_wm_setInitialLayerNet for withdrawn windows */
      DASSERT(wdata->isShowing);
  
      currentLayer = awt_wm_getLayerNet(shell_win);
      if(state == currentLayer) {
         return;
      }
      cmd = currentLayer == LAYER_ALWAYS_ON_TOP && state == LAYER_NORMAL ?
                                                    _NET_WM_STATE_REMOVE :
            currentLayer == LAYER_NORMAL && state == LAYER_ALWAYS_ON_TOP  ?
                                                    _NET_WM_STATE_ADD :
                                                    _NET_WM_STATE_ADD;
      req.type          = ClientMessage;
      req.window          = XtWindow(shell);
      req.message_type = _XA_NET_WM_STATE;
      req.format          = 32;
      req.data.l[0]    = cmd;
      req.data.l[1]    = _XA_NET_WM_STATE_ABOVE;
      req.data.l[2]    = 0L;
  
      XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
             (SubstructureRedirectMask | SubstructureNotifyMask),
             (XEvent *)&req);
  }
  
  /*
   * Request a layer change from a Gnome WM (_WIN protocol) by sending
   * _WIN_LAYER ClientMessage to root window.
   */
  static void
  awt_wm_requestLayerWin(struct FrameData *wdata, int state)
  {
      Widget shell = wdata->winData.shell;
      XClientMessageEvent req;
      Display *dpy = XtDisplay(shell);
  
      /* must use awt_wm_setInitialLayerWin for withdrawn windows */
      DASSERT(wdata->isShowing);
  
      req.type          = ClientMessage;
      req.window          = XtWindow(shell);
      req.message_type = _XA_WIN_LAYER;
      req.format          = 32;
      req.data.l[0]    = state == LAYER_NORMAL ? WIN_LAYER_NORMAL : WIN_LAYER_ONTOP;
      req.data.l[1]    = 0L;
      req.data.l[2]    = 0L;
  
      XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
             /*(SubstructureRedirectMask |*/
                 SubstructureNotifyMask,
             (XEvent *)&req);
  }
  /*
   * Specify initial layer for _NET supporting WM by setting
   * _NET_WM_STATE property on the window to the desired state before
   * mapping it.
   * NB: looks like it doesn't have any effect.
   */
  static void
  awt_wm_setInitialLayerNet(struct FrameData *wdata, int state)
  {
      Widget shell = wdata->winData.shell;
      Window shell_win = XtWindow(shell);
      Display *dpy = XtDisplay(shell);
  
      Atom *old_state;
      unsigned long nitems;
      Atom new_state = _XA_NET_WM_STATE_ABOVE;
  
      /* must use awt_wm_requestLayerNet for managed windows */
      DASSERT(!wdata->isShowing);
  
      /* Be careful to not wipe out state bits we don't understand */
      old_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
  
      if (nitems == 0 && state != LAYER_ALWAYS_ON_TOP) {
          if (old_state != NULL) {
              XFree(old_state);
          }
          return;
      }else if( nitems == 0 && state == LAYER_ALWAYS_ON_TOP) {
          unsigned long data[2];
          /* create new state */
          if (old_state != NULL) {
              XFree(old_state);
          }
          nitems = 1;
          data[0] = new_state;
          data[1] = 0;
          XChangeProperty(dpy, shell_win,
                  _XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
                  (unsigned char *)data, nitems);
              XSync(dpy, False);
      }else { /* nitems > 0 */
          unsigned long i;
          Boolean bShift = False;
          int mode;
          for(i = 0; i < nitems; i++) {
              if( bShift ) {
                  old_state[i-1] = old_state[i];
              }else if( old_state[i] == _XA_NET_WM_STATE_ABOVE ) {
                  if(state == LAYER_ALWAYS_ON_TOP) {
                      /* no change necessary */
                      XFree(old_state);
                      return;
                  }else{
                      /* wipe off this atom */
                      bShift = True;
                  }
              }
          }
  
          if( bShift ) {
              /* atom was found and removed: change property */
              mode = PropModeReplace;
              nitems--;
          }else if( state != LAYER_ALWAYS_ON_TOP ) {
              /* atom was not found and not needed */
              XFree( old_state);
              return;
          }else {
              /* must add new atom */
              mode = PropModeAppend;
              nitems = 1;
          }
  
          XChangeProperty(dpy, shell_win,
                  _XA_NET_WM_STATE, XA_ATOM, 32, mode,
                  mode == PropModeAppend ?
                              (unsigned char *)(&new_state) :
                              (unsigned char *)old_state, nitems);
          XFree(old_state);
              XSync(dpy, False);
      }
  }
  
  /*
   * Specify initial layer for a Gnome WM (_WIN protocol) by setting
   * WIN_LAYER property on the window to the desired state before
   * mapping it.
   */
  static void
  awt_wm_setInitialLayerWin(struct FrameData *wdata, int state)
  {
      Display *dpy = XtDisplay(wdata->winData.shell);
      Window shell_win = XtWindow(wdata->winData.shell);
      long win_state, old_win_state;
      int currentLayer;
  
      /* must use awt_wm_requestLayerWin for managed windows */
      DASSERT(!wdata->isShowing);
  
      currentLayer = awt_wm_getLayerWin(shell_win);
      if( currentLayer == state ) {
          /* no change necessary */
          return;
      }
      if( state == LAYER_ALWAYS_ON_TOP ) {
          win_state = WIN_LAYER_ONTOP;
      }else {
          win_state = WIN_LAYER_NORMAL;
      }
  
      XChangeProperty(dpy, shell_win,
              _XA_WIN_LAYER, XA_CARDINAL, 32, PropModeReplace,
              (unsigned char *)&win_state, 1);
  }
  
  void
  awt_wm_setExtendedState(struct FrameData *wdata, jint state)
  {
      Display *dpy = XtDisplay(wdata->winData.shell);
      Window shell_win = XtWindow(wdata->winData.shell);
  
  #ifdef DEBUG
      DTRACE_PRINT2("WM: setExtendedState(0x%x/0x%x) ",
                    wdata->winData.shell, shell_win);
      awt_wm_dtraceStateJava(state);
  #endif
  
      if (wdata->isShowing) {
          /*
           * If the window is managed by WM, we should send
           * ClientMessage requests.
           */
          if (awt_wm_doStateProtocolNet()) {
              awt_wm_requestStateNet(wdata, state);
          }
          else if (awt_wm_doStateProtocolWin()) {
              awt_wm_requestStateWin(wdata, state);
          }
          XSync(dpy, False);
      }
      else {
          /*
           * If the window is withdrawn we should set necessary
           * properties directly to the window before mapping it.
           */
          if (awt_wm_doStateProtocolNet()) {
              awt_wm_setInitialStateNet(wdata, state);
          }
          else if (awt_wm_doStateProtocolWin()) {
              awt_wm_setInitialStateWin(wdata, state);
          }
  #if 1
          /*
           * Purge KWM bits.
           * Not really tested with KWM, only with WindowMaker.
           */
          XDeleteProperty(dpy, shell_win, XA_KWM_WIN_ICONIFIED);
          XDeleteProperty(dpy, shell_win, XA_KWM_WIN_MAXIMIZED);
  #endif /* 1 */
      }
  }
  
  static Boolean
  awt_wm_supportsLayersNet() {
      Boolean supported = awt_wm_doStateProtocolNet();
  
      /*
         In fact, WM may report this not supported but do support.
       */
      supported &= awt_wm_checkProtocol(_XA_NET_SUPPORTED, _XA_NET_WM_STATE_ABOVE);
      return supported;
  }
  
  static Boolean
  awt_wm_supportsLayersWin() {
      Boolean supported = awt_wm_doStateProtocolWin();
      /*
       * In fact, WM may report this supported but do not support.
       */
      supported &= awt_wm_checkProtocol(_XA_WIN_PROTOCOLS, _XA_WIN_LAYER);
      return supported;
  }
  
  void
  awt_wm_updateAlwaysOnTop(struct FrameData *wdata, jboolean bLayerState) {
      Display *dpy = XtDisplay(wdata->winData.shell);
      Window shell_win = XtWindow(wdata->winData.shell);
      int layerState = bLayerState ? LAYER_ALWAYS_ON_TOP : LAYER_NORMAL;
  
      if (wdata->isShowing) {
          /**
             We don't believe anyone, and now send both ClientMessage requests.
             And eg Metacity under RH 6.1 required both to work.
           **/
          awt_wm_requestLayerNet(wdata, layerState);
          awt_wm_requestLayerWin(wdata, layerState);
      } else {
          /**
             We don't believe anyone, and now set both atoms.
             And eg Metacity under RH 6.1 required both to work.
           **/
          awt_wm_setInitialLayerNet(wdata, layerState);
          awt_wm_setInitialLayerWin(wdata, layerState);
      }
      XSync(dpy, False);
  }
  
  /*
   * Work around for 4775545.  _NET version.
   */
  static void
  awt_wm_unshadeKludgeNet(struct FrameData *wdata)
  {
      Display *dpy = XtDisplay(wdata->winData.shell);
      Window shell_win = XtWindow(wdata->winData.shell);
      Atom *net_wm_state;
      Boolean shaded;
      unsigned long nitems;
      unsigned long i;
  
      net_wm_state = awt_getAtomListProperty(shell_win,
                                             _XA_NET_WM_STATE, &nitems);
      if (nitems == 0) {
          DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
          if (net_wm_state) {
              XFree(net_wm_state);
          }
          return;
      }
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateNet(net_wm_state, nitems);
  #endif
  
      shaded = False;
      for (i = 0; i < nitems; ++i) {
          if (net_wm_state[i] == _XA_NET_WM_STATE_SHADED) {
              shaded = True;
              break;
          }
      }
  
      if (!shaded) {
          DTRACE_PRINTLN("WM:     not _SHADED, no workaround necessary");
          return;
      }
  
      DTRACE_PRINTLN("WM:     removing _SHADED");
      ++i;                        /* skip _SHADED  */
      while (i < nitems) {        /* copy the rest */
          net_wm_state[i-1] = net_wm_state[i];
          ++i;
      }
      --nitems;                   /* _SHADED has been removed */
  
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateNet(net_wm_state, nitems);
  #endif
  
      WITH_XERROR_HANDLER(xerror_verify_change_property);
      {
          XChangeProperty(dpy, shell_win,
                          _XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
                          (unsigned char *)net_wm_state, nitems);
      }
      RESTORE_XERROR_HANDLER;
  
      if (xerror_code != Success) {
          DTRACE_PRINTLN1("WM:     XChangeProperty failed, error = %d",
                          xerror_code);
      }
  
      XFree(net_wm_state);
  }
  
  
  /*
   * Work around for 4775545.  _WIN version.
   */
  static void
  awt_wm_unshadeKludgeWin(struct FrameData *wdata)
  {
      Display *dpy = XtDisplay(wdata->winData.shell);
      Window shell_win = XtWindow(wdata->winData.shell);
      long win_state;
  
      win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
  #ifdef DEBUG
      DTRACE_PRINT("WM:     ");
      awt_wm_dtraceStateWin(win_state);
  #endif
  
      if ((win_state & WIN_STATE_SHADED) == 0) {
          DTRACE_PRINTLN("WM:     not _SHADED, no workaround necessary");
          return;
      }
  
      win_state &= ~WIN_STATE_SHADED;
      XChangeProperty(dpy, shell_win,
                      _XA_WIN_STATE, XA_CARDINAL, 32, PropModeReplace,
                      (unsigned char *)&win_state, 1);
  }
  
  
  /*
   * Work around for 4775545.
   *
   * If WM exits while the top-level is shaded, the shaded hint remains
   * on the top-level properties.  When WM restarts and sees the shaded
   * window it can reparent it into a "pre-shaded" decoration frame
   * (Metacity does), and our insets logic will go crazy, b/c it will
   * see a huge nagative bottom inset.  There's no clean solution for
   * this, so let's just be weasels and drop the shaded hint if we
   * detect that WM exited.  NB: we are in for a race condition with WM
   * restart here.  NB2: e.g. WindowMaker saves the state in a private
   * property that this code knows nothing about, so this workaround is
   * not effective; other WMs might play similar tricks.
   */
  void
  awt_wm_unshadeKludge(struct FrameData *wdata)
  {
      DTRACE_PRINTLN("WM: unshade kludge");
      DASSERT(wdata->isShowing);
  
      if (awt_wm_doStateProtocolNet()) {
          awt_wm_unshadeKludgeNet(wdata);
      }
      else if (awt_wm_doStateProtocolWin()) {
          awt_wm_unshadeKludgeWin(wdata);
      }
  #ifdef DEBUG
      else {
          DTRACE_PRINTLN("WM:     not a _NET or _WIN supporting WM");
      }
  #endif
  
      XSync(XtDisplay(wdata->winData.shell), False);
  }
  
  
  void
  awt_wm_init(void)
  {
      static Boolean inited = False;
      if (inited) {
          return;
      }
  
      awt_wm_initAtoms();
      awt_wm_getRunningWM();
      inited = True;
  }
  
  Boolean awt_wm_supportsAlwaysOnTop() {
      return awt_wm_supportsLayersNet() || awt_wm_supportsLayersWin();
  }
  