jdk/src/solaris/native/sun/awt/awt_wm.c
author trims
Thu, 11 Nov 2010 23:30:49 -0800
changeset 7128 a683e80b9ca8
parent 5506 202f599c92aa
permissions -rw-r--r--
6997698: Bump the HS20 build number to 03 Summary: Update the HS20 build number to 03 Reviewed-by: jcoomes

/*
 * 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();
}