/*
 * Copyright (c) 1995, 2010, 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.
 */

#include "awt_p.h"

#include <sys/time.h>
#include <limits.h>
#include <locale.h>

#ifndef HEADLESS
#include <X11/cursorfont.h>
#include <Xm/MenuShell.h>
#include <Xm/RowColumn.h>
#endif /* !HEADLESS */

#include <jvm.h>
#include <jni.h>
#include <jlong.h>
#include <jni_util.h>

/* JNI headers */
#include "java_awt_AWTEvent.h"
#include "java_awt_Frame.h"
#include "java_awt_SystemColor.h"
#include "sun_awt_motif_MToolkit.h"

/* JNI field and method ids */
#include "awt_Component.h"
#include "awt_Cursor.h"
#include "awt_MenuComponent.h"
#include "awt_TopLevel.h"
#include "canvas.h"
#include "color.h"
#include "awt_mgrsel.h"
#include "awt_wm.h"
#include "awt_DrawingSurface.h"
#include "awt_Window.h"
#include "awt_xembed.h"
#include "awt_xembed_server.h"

extern JavaVM *jvm;

#ifndef HEADLESS
#ifdef __linux__
extern void statusWindowEventHandler(XEvent event);
#endif
#endif /* !HEADLESS */

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
{
#ifndef HEADLESS
    awt_util_debug_init();
#endif /* !HEADLESS */
    jvm = vm;
    return JNI_VERSION_1_2;
}

JNIEXPORT jboolean JNICALL AWTIsHeadless() {
#ifdef HEADLESS
    return JNI_TRUE;
#else
    return JNI_FALSE;
#endif
}

#ifndef HEADLESS
static jlong awtJNI_TimeMillis(void);
extern void awt_initialize_Xm_DnD(Display*);
extern void awt_initialize_DataTransferer();

extern Display *awt_init_Display(JNIEnv *env);

extern void X11SD_LibDispose(JNIEnv *env);

extern Widget drag_source = NULL;

extern struct ComponentIDs componentIDs;
extern struct MenuComponentIDs menuComponentIDs;
extern struct MComponentPeerIDs mComponentPeerIDs;
extern struct WindowIDs windowIDs;

static Atom _XA_XSETTINGS_SETTINGS = None;
struct xsettings_callback_cookie {
    jobject mtoolkit;
    jmethodID upcallMID;
};

static struct xsettings_callback_cookie xsettings_callback_cookie;


static XEvent focusOutEvent;

static void awt_pipe_init(void);
static void processOneEvent(XtInputMask iMask);
extern void waitForEvents(JNIEnv *env, int32_t fdXPipe, int32_t fdAWTPipe);
#ifdef USE_SELECT
static void performSelect(JNIEnv *env, int32_t fdXPipe, int32_t fdAWTPipe);
#else
static void performPoll(JNIEnv *env,int32_t fdXPipe, int32_t fdAWTPipe);
#endif


#include <X11/Intrinsic.h>
#include <dlfcn.h>
#include <fcntl.h>

#ifdef USE_SELECT
#if defined(AIX)
#include <sys/select.h>
#endif
#else
#include <poll.h>
#ifndef POLLRDNORM
#define POLLRDNORM POLLIN
#endif
#endif

#ifdef NDEBUG
#undef DEBUG            /* NDEBUG overrides DEBUG */
#endif

static struct WidgetInfo *awt_winfo = (struct WidgetInfo *) NULL;
static struct MenuList* menu_list = (struct MenuList*) NULL;

#ifndef bzero
#define bzero(a,b) memset(a, 0, b)
#endif

static jboolean syncUpdated = JNI_FALSE;
static jboolean syncFailed = JNI_FALSE;
static jint eventNumber = 0;
static void syncWait_eventHandler(XEvent *);
static Atom oops_atom = None;
static Atom wm_selection = None;
static Atom version_atom = None;

static Boolean inSyncWait = False;

Widget grabbed_widget = NULL;

XtAppContext awt_appContext;
Widget awt_root_shell;
Pixel awt_defaultBg;
Pixel awt_defaultFg;
int32_t awt_multiclick_time;        /* milliseconds */
uint32_t awt_MetaMask = 0;
uint32_t awt_AltMask = 0;
uint32_t awt_NumLockMask = 0;
uint32_t awt_ModeSwitchMask = 0;
Cursor awt_scrollCursor;
Boolean  awt_ModLockIsShiftLock = False;
extern Boolean awt_UseType4Patch;
extern Boolean awt_UseXKB;

#define SPECIAL_KEY_EVENT 2

/* implement a "putback queue" -- see comments on awt_put_back_event() */
#define PUTBACK_QUEUE_MIN_INCREMENT 5   /* min size increase */
static XEvent *putbackQueue = NULL;     /* the queue -- next event is 0 */
static int32_t putbackQueueCount = 0;   /* # of events available on queue */
static int32_t putbackQueueCapacity = 0;        /* total capacity of queue */
static XtInputMask awt_events_pending(XtAppContext appContext);
static int32_t awt_get_next_put_back_event(XEvent *xev_out);

#define AWT_FLUSH_TIMEOUT    ((uint32_t)100) /* milliseconds */
#define AWT_MIN_POLL_TIMEOUT ((uint32_t)0) /* milliseconds */
#define AWT_MAX_POLL_TIMEOUT ((uint32_t)250) /* milliseconds */

#define AWT_POLL_BUFSIZE        100
#define AWT_READPIPE            (awt_pipe_fds[0])
#define AWT_WRITEPIPE           (awt_pipe_fds[1])
#define AWT_FLUSHOUTPUT_NOW()  \
{                              \
    XFlush(awt_display);       \
    awt_next_flush_time = 0LL; \
}

typedef XtIntervalId (*XTFUNC)();

static jobject  awt_MainThread = NULL;
static char     read_buf[AWT_POLL_BUFSIZE + 1];    /* dummy buf to empty pipe */
static int32_t      awt_pipe_fds[2];                   /* fds for wkaeup pipe */
static Boolean  awt_pipe_inited = False;           /* make sure pipe is initialized before write */
static int32_t      def_poll_timeout = AWT_MAX_POLL_TIMEOUT;   /* default value for timeout */
static jlong awt_next_flush_time = 0LL; /* 0 == no scheduled flush */
static void     *xt_lib = NULL;
static XTFUNC   xt_timeout = NULL;

#ifdef DEBUG_AWT_LOCK

int32_t awt_locked = 0;
char *lastF = "";
int32_t lastL = -1;

#endif

#ifndef NOMODALFIX
extern Boolean awt_isModal();
extern Boolean awt_isWidgetModal(Widget w);
#endif

Boolean keyboardGrabbed = False;

static uint32_t curPollTimeout = AWT_MAX_POLL_TIMEOUT;

/* Font information to feed Motif widgets. */
static const char      *motifFontList;
static XFontSet        defaultMotifFontSet;
static XFontStruct     *defaultMotifFontStruct;
static const char *defaultMotifFont =  /* a.k.a "fixed", known everywhere */
        "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1";

XFontSet getMotifFontSet() {
    char    **missingList;
    int32_t     missingCount;
    char    *defChar;

    return XCreateFontSet(awt_display, motifFontList,
                          &missingList, &missingCount, &defChar);
}

XFontStruct *getMotifFontStruct() {
    return XLoadQueryFont(awt_display, defaultMotifFont);
}

XmFontList getMotifFontList() {
    XmFontListEntry motifFontListEntry;
    XmFontList fontlist;

    if (strchr(motifFontList, ',') == NULL) {
        /* If the default font is a single font. */
        if (defaultMotifFontStruct == NULL)
            defaultMotifFontStruct = getMotifFontStruct();
        motifFontListEntry = XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG,
                                                   XmFONT_IS_FONT,
                                           (XtPointer)defaultMotifFontStruct);
    }
    else {
        /* If the default font is multiple fonts. */
        if (defaultMotifFontSet == NULL)
            defaultMotifFontSet = getMotifFontSet();
            motifFontListEntry = XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG,
                                               XmFONT_IS_FONTSET,
                                               (XtPointer)defaultMotifFontSet);
    }
    fontlist = XmFontListAppendEntry(NULL, motifFontListEntry);
    XmFontListEntryFree(&motifFontListEntry);
    return fontlist;
}

static void
awt_set_poll_timeout (uint32_t newTimeout)
{
    DTRACE_PRINTLN1("awt_set_poll_timeout(%lu)", newTimeout);

    newTimeout = max(AWT_MIN_POLL_TIMEOUT, newTimeout);
    newTimeout = min(AWT_MAX_POLL_TIMEOUT, newTimeout);
    newTimeout = min(newTimeout, curPollTimeout);
    curPollTimeout = newTimeout;

} /* awt_set_poll_timeout */

/*
 * Gets the best timeout for the next call to poll() or select().
 * If timedOut is True, we assume that our previous timeout elapsed
 * with no events/timers arriving. Therefore, we can increase the
 * next timeout slightly.
 */
static uint32_t
awt_get_poll_timeout( Boolean timedOut )
{
    uint32_t timeout = AWT_MAX_POLL_TIMEOUT;

    DTRACE_PRINTLN2("awt_get_poll_timeout(%s), awt_next_flush_time:%ld",
        (remove?"true":"false"),
        awt_next_flush_time);

    if (timedOut) {
        /* add 1/16 (plus 1, in case the division truncates to 0) */
        curPollTimeout += ((curPollTimeout>>4) + 1);
        curPollTimeout = min(AWT_MAX_POLL_TIMEOUT, curPollTimeout);
    }
    if (awt_next_flush_time > 0) {
        int32_t flushDiff = (int32_t)(awt_next_flush_time - awtJNI_TimeMillis());
        timeout = min(curPollTimeout, flushDiff);
    } else {
        timeout = curPollTimeout;
    }

    return timeout;
} /* awt_get_poll_timeout() */

static jlong
awtJNI_TimeMillis(void)
{
    struct timeval t;

    gettimeofday(&t, 0);

    return jlong_add(jlong_mul(jint_to_jlong(t.tv_sec), jint_to_jlong(1000)),
                     jint_to_jlong(t.tv_usec / 1000));
}

static int32_t
xtError()
{
#ifdef DEBUG
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    jio_fprintf(stderr, "Xt error\n");
    JNU_ThrowNullPointerException(env, "NullPointerException");
#endif
    return 0;
}

static int32_t
xIOError(Display *dpy)
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    jclass cl = (*env)->FindClass(env, "java/lang/Thread");

    if (errno == EPIPE) {
        jio_fprintf(stderr, "X connection to %s host broken (explicit kill or server shutdown)\n", XDisplayName(NULL));
    }
    AWT_NOFLUSH_UNLOCK();
    JVM_RaiseSignal(JVM_SIGTERM); /* Shut down cleanly */
    if (cl != NULL) {
        JVM_Sleep(env, cl, 20000);
    }

    return 0; /* to keep compiler happy */
}

/* Like XKeysymToKeycode, but ensures that keysym is the primary
 * symbol on the keycode returned.  Returns zero otherwise.
 */
static int32_t
keysym_to_keycode_if_primary(Display *dpy, KeySym sym)
{
    KeyCode code;
    KeySym primary;

    code = XKeysymToKeycode(dpy, sym);
    if (code == 0) {
        return 0;
    }

    primary = XKeycodeToKeysym(dpy, code, 0);
    if (sym == primary) {
        return code;
    } else {
        return 0;
    }
}
/*
 * +kb or -kb ?
 */
static Boolean
isXKBenabled(Display *display) {
    int mop, beve, berr;
    /*
     * NB: TODO: hope it will return False if XkbIgnoreExtension was called!
     */
    return XQueryExtension(display, "XKEYBOARD", &mop, &beve, &berr);
}


/* Assign meaning - alt, meta, etc. - to X modifiers mod1 ... mod5.
 * Only consider primary symbols on keycodes attached to modifiers.
 */
static void
setup_modifier_map(Display *disp)
{
    KeyCode metaL      = keysym_to_keycode_if_primary(disp, XK_Meta_L);
    KeyCode metaR      = keysym_to_keycode_if_primary(disp, XK_Meta_R);
    KeyCode altL       = keysym_to_keycode_if_primary(disp, XK_Alt_L);
    KeyCode altR       = keysym_to_keycode_if_primary(disp, XK_Alt_R);
    KeyCode numLock    = keysym_to_keycode_if_primary(disp, XK_Num_Lock);
    KeyCode modeSwitch = keysym_to_keycode_if_primary(disp, XK_Mode_switch);
    KeyCode shiftLock  = keysym_to_keycode_if_primary(disp, XK_Shift_Lock);
    KeyCode capsLock   = keysym_to_keycode_if_primary(disp, XK_Caps_Lock);

    XModifierKeymap *modmap = NULL;
    int32_t nkeys, modn, i;
    char *ptr = NULL;

    DTRACE_PRINTLN("In setup_modifier_map");

    modmap = XGetModifierMapping(disp);
    nkeys = modmap->max_keypermod;

    for (modn = Mod1MapIndex;
         (modn <= Mod5MapIndex) &&
             (awt_MetaMask == 0 || awt_AltMask == 0 ||
              awt_NumLockMask == 0 || awt_ModeSwitchMask == 0);
         ++modn)
    {
        static const uint32_t modmask[8] = {
            ShiftMask, LockMask, ControlMask,
            Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
        };


        for (i = 0; i < nkeys; ++i) {
            /* for each keycode attached to this modifier */
            KeyCode keycode = modmap->modifiermap[modn * nkeys + i];
            if (keycode == 0) {
                continue;
            }

            if (awt_MetaMask == 0 && (keycode == metaL || keycode == metaR)) {
                awt_MetaMask = modmask[modn];
                DTRACE_PRINTLN2("    awt_MetaMask       = %d, modn = %d", awt_MetaMask, modn);
                break;
            } else if (awt_AltMask == 0 && (keycode == altL || keycode == altR)) {
                awt_AltMask = modmask[modn];
                DTRACE_PRINTLN2("    awt_AltMask        = %d, modn = %d", awt_AltMask, modn);
                break;
            } else if (awt_NumLockMask == 0 && keycode == numLock) {
                awt_NumLockMask = modmask[modn];
                DTRACE_PRINTLN2("    awt_NumLockMask    = %d, modn = %d", awt_NumLockMask, modn);
                break;
            } else if (awt_ModeSwitchMask == 0 && keycode == modeSwitch) {
                awt_ModeSwitchMask = modmask[modn];
                DTRACE_PRINTLN2("    awt_ModeSwitchMask = %d, modn = %d", awt_ModeSwitchMask, modn);
                break;
            }
        }
    }
    for(i = 0; i < nkeys; i++) {
        KeyCode keycode = modmap->modifiermap[LockMapIndex * nkeys + i];
        if (keycode == 0) {
            break;
        }
        if (keycode == shiftLock) {
            awt_ModLockIsShiftLock = True;
            break;
        }
        if (keycode == capsLock) {
            break;
        }
    }

    DTRACE_PRINTLN1("    ShiftMask          = %d", ShiftMask);
    DTRACE_PRINTLN1("    ControlMask        = %d", ControlMask);

    XFreeModifiermap(modmap);
    ptr = getenv("_AWT_USE_TYPE4_PATCH");
    if( ptr != NULL && ptr[0] != 0 ) {
        if( strncmp("true", ptr, 4) == 0 ) {
           awt_UseType4Patch = True;
        }else if( strncmp("false", ptr, 5) == 0 ) {
           awt_UseType4Patch = False;
        }
    }
    awt_UseXKB = isXKBenabled(disp);

}


Boolean scrollBugWorkAround;


void
awt_output_flush()
{
    char c = 'p';

    if (awt_next_flush_time == 0)
    {
        Boolean needsWakeup = False;
        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
        if (awt_pipe_inited && (awt_get_poll_timeout(False) > (2*AWT_FLUSH_TIMEOUT))){
            needsWakeup = True;
        }
        /* awt_next_flush_time affects awt_get_poll_timeout(), so set
         * the variable *after* calling the function.
         */
        awt_next_flush_time = awtJNI_TimeMillis() + AWT_FLUSH_TIMEOUT;
        if (needsWakeup)
        {
            /* write to the utility pipe to wake up the event
             * loop, if it's sleeping
             */
            write ( AWT_WRITEPIPE, &c, 1 );
        }
    }
#ifdef FLUSHDEBUG
else
jio_fprintf(stderr, "!");
#endif
} /* awt_output_flush() */

void
null_event_handler(Widget w, XtPointer client_data,
                   XEvent * event, Boolean * cont)
{
    /* do nothing */
}

struct WidgetInfo *
findWidgetInfo(Widget widget)
{
    struct WidgetInfo *cw;

    for (cw = awt_winfo; cw != NULL; cw = cw->next) {
        if (cw->widget == widget || cw->origin == widget) {
            return cw;
        }
    }
    return NULL;
}

void
awt_addWidget(Widget w, Widget origin, void *peer, jlong event_flags)
{
    if (findWidgetInfo(w) != NULL) return;

    if (!XtIsSubclass(w, xmFileSelectionBoxWidgetClass)) {
        struct WidgetInfo *nw = (struct WidgetInfo *) malloc(sizeof(struct WidgetInfo));

        if (nw) {
            nw->widget     = w;
            nw->origin     = origin;
            nw->peer       = peer;
            nw->event_mask = event_flags;
            nw->next       = awt_winfo;
            awt_winfo      = nw;

            if (event_flags & java_awt_AWTEvent_MOUSE_EVENT_MASK) {
                XtAddEventHandler(w,
                                  ButtonPressMask | ButtonReleaseMask |
                                  EnterWindowMask | LeaveWindowMask,
                                  False, null_event_handler, NULL);
                if (w != origin) {
                    XtAddEventHandler(origin,
                                      ButtonPressMask | ButtonReleaseMask |
                                      EnterWindowMask | LeaveWindowMask,
                                      False, null_event_handler, NULL);
                }
            }
            if (event_flags & java_awt_AWTEvent_MOUSE_MOTION_EVENT_MASK) {
                XtAddEventHandler(w,
                                  PointerMotionMask,
                                  False, null_event_handler, NULL);
                if (w != origin) {
                    XtAddEventHandler(origin,
                                      PointerMotionMask,
                                      False, null_event_handler, NULL);
                }
            }
            if (event_flags & java_awt_AWTEvent_KEY_EVENT_MASK) {
                XtAddEventHandler(w,
                                  KeyPressMask | KeyReleaseMask,
                                  False, null_event_handler, NULL);
                if (w != origin) {
                    XtAddEventHandler(origin,
                                      KeyPressMask | KeyReleaseMask,
                                      False, null_event_handler, NULL);
                }
            }
        } else {
            JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
            JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
        }

    }
}

void
awt_delWidget(Widget w)
{
    struct WidgetInfo *cw;

    if (awt_winfo != NULL) {
        if ((awt_winfo->widget == w) ||
            (awt_winfo->origin == w)) {
            cw = awt_winfo;
            awt_winfo = awt_winfo->next;
            free((void *) cw);
        } else {
            struct WidgetInfo *pw;

            for (pw = awt_winfo, cw = awt_winfo->next;
                 cw != NULL;
                 pw = cw, cw = cw->next) {
                if ((cw->widget == w) ||
                    (cw->origin == w)) {
                    pw->next = cw->next;
                    free((void *) cw);
                    break;
                }
            }
        }
    }
}


void *
findPeer(Widget * pwidget)
{
    struct WidgetInfo   *cw;
    Widget widgetParent;
    void * peer;

    if ((cw = findWidgetInfo(*pwidget)) != NULL) {
        return cw->peer;
    }
    /* fix for 4053856, robi.khan@eng
       couldn't find peer corresponding to widget
       but the widget may be child of one with
       a peer, so recurse up the hierarchy */
    widgetParent = XtParent(*pwidget);
    if (widgetParent != NULL ) {
        peer = findPeer(&widgetParent);
        if( peer != NULL ) {
        /* found peer attached to ancestor of given
           widget, so set widget return value as well */
            *pwidget = widgetParent;
            return peer;
        }
    }

    return NULL;
}

Boolean
awt_isAwtWidget(Widget widget)
{
    return (findWidgetInfo(widget) != NULL);
}


static Boolean
awt_isAwtMenuWidget(Widget wdgt) {
    struct MenuList* cur;

    if (!XtIsSubclass(wdgt, xmRowColumnWidgetClass)) {
        return False;
    }
    for (cur = menu_list; cur != NULL; cur = cur->next) {
        if (cur->menu == wdgt) {
            return True;
        }
    }
    return False;
}

void
awt_addMenuWidget(Widget wdgt) {
    DASSERT(XtIsSubclass(wdgt, xmRowColumnWidgetClass));

    if (!awt_isAwtMenuWidget(wdgt)) {
        struct MenuList* ml = (struct MenuList*) malloc(sizeof(struct MenuList));
        if (ml != NULL) {
            ml->menu = wdgt;
            ml->next = menu_list;
            menu_list = ml;
        } else {
            JNIEnv* env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_2);
            JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
        }
    }
}

void
awt_delMenuWidget(Widget wdgt) {
    struct MenuList** pp;
    struct MenuList* p;

    DASSERT(XtIsSubclass(wdgt, xmRowColumnWidgetClass));

    for (pp = &menu_list; *pp != NULL; pp = &((*pp)->next)) {
        if ((*pp)->menu == wdgt) {
            p = *pp;
            *pp = (*pp)->next;
            free((void*)p);
            break;
        }
    }
}


static Widget
getShellWidgetByPart(Widget part) {
    int i;
    for (i = 0; i < 3; i++) {
        if (part == NULL) return NULL;
        if (XtIsShell(part)) return part;
        part = XtParent(part);
    }
    return NULL;
}

static Boolean
isTheSameShellWidget(Widget shell, Widget w) {
    Widget s1, s2;
    if (shell == NULL || w == NULL) return False;
    s1 = getShellWidgetByPart(shell);
    s2 = getShellWidgetByPart(w);
    if (s1 == s2 && s1 != NULL) {
        return True;
    } else {
        return False;
    }
}

static Boolean
shouldDispatchToWidget(XEvent * xev)
{
  /* If this function returns False, that means that it has not pre-posted
     this event to Java. The caller will then dispatch the event to Motif,
     and our handlers will be called to post it to Java.
     If this function returns true, then this function has posted this event
     to java before returning. The caller will not dispatch it to Motif;
     it will be dispatched to Motif via the putbackQueue after it has been
     processed by Java */

    Window win;
    Widget widget = NULL;
    struct WidgetInfo *winfo;
    void *peer = NULL;
    Boolean cont = FALSE;

    switch (xev->type) {
        case KeyPress:
        case KeyRelease:
            win = xev->xkey.window;
            break;
        case FocusIn:
        case FocusOut:
            win = xev->xfocus.window;
            break;
        case ButtonPress:
        case ButtonRelease:
            win = xev->xbutton.window;
            break;
        case MotionNotify:
            win = xev->xmotion.window;
            break;
        case EnterNotify:
        case LeaveNotify:
            win = xev->xcrossing.window;
            break;
        default:
            return False;
    }

    if ((widget = XtWindowToWidget(awt_display, win)) == NULL) {
        return False;
    }

    if (xev->type == KeyPress || xev->type == KeyRelease) {
        Widget focusWidget = XmGetFocusWidget(widget);

        /* Fix for 4328561 by ibd@sparc.spb.su
           If the widget is a Choice, the widget with focus is probably lying
           outside the current widget's sub-hierarchy, so we have to go up the
           hierarchy to reach it */

        if ((focusWidget == NULL) && XmIsMenuShell(widget)) {
            if ((widget = XtParent(widget)) != NULL) {
                focusWidget = XmGetFocusWidget(widget);
            } else {
                return False;
            }

            /* In this case, focus widget should be CascadeButtonGadget type,
               but we should send the events to its parent */
            if (focusWidget != NULL && XmIsCascadeButtonGadget(focusWidget)) {
                widget = XtParent(focusWidget);
            } else {
                /* If something went wrong, restore the original status */
                widget = XtWindowToWidget(awt_display, win);
            }
        }

        /* if focus owner is null, redirect key events to focused window */
        if (focusWidget == NULL && findWidgetInfo(widget) == NULL) {
            focusWidget = findTopLevelByShell(widget);
        }

        /* If we are on a non-choice widget, process events in a normal way */
        if ((focusWidget != NULL) && (focusWidget != widget)) {
            if (isTheSameShellWidget(focusWidget, widget)) {
                focusWidget = findTopLevelByShell(widget);
            }
            if (focusWidget != NULL) {
                peer = findPeer(&focusWidget);
            }
            if (peer != NULL) {
                widget = focusWidget;
                win = xev->xkey.window = XtWindow(focusWidget);
            }
        }
    }

    if ((winfo = findWidgetInfo(widget)) == NULL) {
        return False;
    }

    /*
     * Fix for bug 4145193
     *
     * If a menu is up (not just a popup menu), prevent awt components from
     * getting any events until the menu is popped down.
     * Before this fix, the fact that mouse/button events were
     * preposted to the Java event queue was causing the ButtonRelease
     * (needed to pop menu down) to be seen by the menu's parent and
     * not the menu.
     */
    if (awtMenuIsActive()) {
        Widget focusWidget = XmGetFocusWidget(widget);

        if (focusWidget == NULL) {
            return False;
        }

        /* If we are on a choice, dispatch the events to widget, but do not
         * dispatch the events if we are on popped up menu.
         */
        if (!XmIsRowColumn(widget) || !XmIsCascadeButtonGadget(focusWidget)) {
            /* Fix for 4328557 by ibd@sparc.spb.su
             * If we are dragging mouse from choice and are currently outside
             * of it, dispatch events to the choice - the source of dragging.
             */

            if ((drag_source != NULL) && (widget != drag_source) &&
                (peer = findPeer(&drag_source))) {
                awt_canvas_handleEvent(drag_source, peer, xev, winfo, &cont, TRUE);
            }
            return False;
        }
    }

    /* If the keyboard is grabbed by a popup (such as a choice) during
       a time when a focus proxy is in effect, the abovefocusIsOnMenu
       test will not detect the sitation because the focus will be on
       the proxy. But we need events to go to Motif first, so that the
       grab can be undone when appropriate. */
    if (keyboardGrabbed) {
        return False;
    }

    /* If it's a keyboard event, we need to find the peer associated */
    /* with the widget that has the focus rather than the widget */
    /* associated with the window in the X event. */

    switch (xev->type) {
      case KeyPress:
      case KeyRelease:
          if (!(winfo->event_mask & java_awt_AWTEvent_KEY_EVENT_MASK))
              return False;
          break;
        case FocusIn:
        case FocusOut:
            if (!(winfo->event_mask & java_awt_AWTEvent_FOCUS_EVENT_MASK))
                return False;
            break;
        case ButtonPress:
        case ButtonRelease:
            if (!(winfo->event_mask & java_awt_AWTEvent_MOUSE_EVENT_MASK)) {
                return False;
            }
            break;
        case EnterNotify:
        case LeaveNotify:
            /*
             * Do not post the enter/leave event if it's on a subwidget
             * within the component.
             */
            if (!(winfo->event_mask & java_awt_AWTEvent_MOUSE_EVENT_MASK) ||
                widget != winfo->origin)
                return False;
            break;
        case MotionNotify:
            if (!(winfo->event_mask & java_awt_AWTEvent_MOUSE_MOTION_EVENT_MASK))
                return False;
            break;
        default:
            return False;
    }

    peer = winfo->peer;

    /* If we found a widget and a suitable peer (either the focus
       peer above or the one associated with the widget then we
       dispatch to it. */
    if (peer == NULL) {
        return False;
    }

    /*
     * Fix for bug 4173714 - java.awt.button behaves differently under
     * Win32/Solaris.
     * Component should not get any events when it's disabled.
     */
    if (!XtIsSensitive(widget)) {
        if (xev->type == EnterNotify) {
            updateCursor(peer, CACHE_UPDATE);
        }
        return False;
    }

    awt_canvas_handleEvent(widget, peer, xev, winfo, &cont, TRUE);
    return (!cont);
} /* shouldDispatchToWidget() */


void set_toolkit_busy(Boolean busy) {

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    static jclass awtAutoShutdownClass = NULL;
    static jmethodID notifyBusyMethodID = NULL;
    static jmethodID notifyFreeMethodID = NULL;

    if (awtAutoShutdownClass == NULL) {
        jclass awtAutoShutdownClassLocal = (*env)->FindClass(env, "sun/awt/AWTAutoShutdown");
        if ((*env)->ExceptionOccurred(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        DASSERT(awtAutoShutdownClassLocal != NULL);
        if (awtAutoShutdownClassLocal == NULL) {
            return;
        }

        awtAutoShutdownClass = (jclass)(*env)->NewGlobalRef(env, awtAutoShutdownClassLocal);
        (*env)->DeleteLocalRef(env, awtAutoShutdownClassLocal);

        notifyBusyMethodID = (*env)->GetStaticMethodID(env, awtAutoShutdownClass,
                                                    "notifyToolkitThreadBusy", "()V");
        if ((*env)->ExceptionOccurred(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        notifyFreeMethodID = (*env)->GetStaticMethodID(env, awtAutoShutdownClass,
                                                    "notifyToolkitThreadFree", "()V");
        if ((*env)->ExceptionOccurred(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        DASSERT(notifyBusyMethodID != NULL);
        DASSERT(notifyFreeMethodID != NULL);
        if (notifyBusyMethodID == NULL || notifyFreeMethodID == NULL) {
            return;
        }
    } /* awtAutoShutdownClass == NULL*/

    if (busy) {
        (*env)->CallStaticVoidMethod(env, awtAutoShutdownClass,
                                     notifyBusyMethodID);
    } else {
        (*env)->CallStaticVoidMethod(env, awtAutoShutdownClass,
                                     notifyFreeMethodID);
    }

    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
    }
}

#ifdef DEBUG
static int32_t debugPrintLineCount = 0;   /* limit debug output per line */
#endif

/*
 * This is the main Xt event loop for the AWT.
 *
 * Because java applications are multithreaded, but X and Xt
 * are thread-dumb, we must make special considerations to
 * make ensure that the X/Xt libraries are not entered by
 * multiple threads simultaneously.
 *
 * The biggest difference between the standard Xt loop
 * and this loop is that we go to great lengths never to block
 * in the X libraries. We poll() on the X event pipe, waiting
 * for events, rather than simply calling XtAppNextEvent() and
 * blocking. If this thread were to block in XtAppNextEvent(),
 * no other thread could enter (e.g., to perform a paint or
 * retrieve data).
 */
/* #ifdef DEBUG */
    int32_t  numEventsHandled = 0;
/* #endif */
static void
awt_MToolkit_loop(JNIEnv *env)
{
    XtInputMask iMask;
    int32_t  fdXPipe = -1;              /* pipe where X events arrive */

    /* only privileged thread should be running here */
    DASSERT(awt_currentThreadIsPrivileged(env));

    /* The pipe where X events arrive */
    fdXPipe = ConnectionNumber(awt_display) ;

    /* We execute events while locked, unlocking only when waiting
     * for an event
     */
    AWT_LOCK();

    /* Create the AWT utility pipe. See the comments on awt_pipe_init() */
    awt_pipe_init();

    /*
     * Need to flush here in case data on the connection was read
     * before we acquired the monitor.
     *
     * I don't get this, but I'm too chicken to remove it. -jethro 2Sep98
     */
    AWT_FLUSHOUTPUT_NOW();

    /*
     * ACTUALLY PROCESS EVENTS
     */
    while(True) {

        /* process all events in the queue */
/*      #ifdef DEBUG */
/*          numEventsHandled = 0; */
/*      #endif */
        while (((iMask = awt_events_pending(awt_appContext)) & XtIMAll) > 0) {

/*          #ifdef DEBUG */
                ++numEventsHandled;
/*          #endif */
            processOneEvent(iMask);

        }  /* end while awt_events_pending() */
        /* At this point, we have exhausted the event queue */

        /* print the number of events handled in parens */
        DTRACE_PRINT1("(%d events)",(int32_t)numEventsHandled);
#ifdef DEBUG
        if (++debugPrintLineCount > 8) {
            DTRACE_PRINTLN("");
            debugPrintLineCount = 0;
        }
#endif

        AWT_NOTIFY_ALL();               /* wake up modalWait() */

        set_toolkit_busy(False);

        /* Here, we wait for X events, outside of the X libs. When
         * it's likely that an event is waiting, we process the queue
         */
        waitForEvents(env, fdXPipe, AWT_READPIPE);

        set_toolkit_busy(True);

    } /* while(True) */

    /* If we ever exit the loop, must unlock the toolkit */

} /* awt_MToolkit_loop() */

/*
 * Creates the AWT utility pipe. This pipe exists solely so that
 * we can cause the main event thread to wake up from a poll() or
 * select() by writing to this pipe.
 */
static void
awt_pipe_init(void) {

    if (awt_pipe_inited) {
        return;
    }

    if ( pipe ( awt_pipe_fds ) == 0 )
    {
        /*
        ** the write wakes us up from the infinite sleep, which
        ** then we cause a delay of AWT_FLUSHTIME and then we
        ** flush.
        */
        int32_t flags = 0;
        awt_set_poll_timeout (def_poll_timeout);
        /* set the pipe to be non-blocking */
        flags = fcntl ( AWT_READPIPE, F_GETFL, 0 );
        fcntl( AWT_READPIPE, F_SETFL, flags | O_NDELAY | O_NONBLOCK );
        flags = fcntl ( AWT_WRITEPIPE, F_GETFL, 0 );
        fcntl( AWT_WRITEPIPE, F_SETFL, flags | O_NDELAY | O_NONBLOCK );
        awt_pipe_inited = True;
    }
    else
    {
        AWT_READPIPE = -1;
        AWT_WRITEPIPE = -1;
        awt_pipe_inited = False;
    }
} /* awt_pipe_init() */

static Window
proxyTopLevel(Window proxyWindow) {
    Window parent = None, root = None, *children = NULL, retvalue = None;
    uint32_t nchildren = 0;
    Status res = XQueryTree(awt_display, proxyWindow, &root, &parent,
             &children, &nchildren);
    if (res != 0) {
        if (nchildren > 0) {
            retvalue = children[0];
        }
        else retvalue = None;
        if (children != NULL) {
            XFree(children);
        }
        return retvalue;
    } else {
        return None;
    }
}

static jclass clazzF, clazzD = NULL;

static Boolean
initClazzD(JNIEnv *env) {
    jclass t_clazzD = (*env)->FindClass(env, "java/awt/Dialog");
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
    }
    DASSERT(t_clazzD != NULL);
    if (t_clazzD == NULL) {
        return False;
    }
    clazzD = (*env)->NewGlobalRef(env, t_clazzD);
    DASSERT(clazzD != NULL);
    (*env)->DeleteLocalRef(env, t_clazzD);
    return True;
}

Boolean
isFrameOrDialog(jobject target, JNIEnv *env) {
    if ((*env)->EnsureLocalCapacity(env, 1) < 0) {
        return False;
    }

    if (clazzF == NULL) {
        jclass t_clazzF = (*env)->FindClass(env, "java/awt/Frame");
        if ((*env)->ExceptionOccurred(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        DASSERT(t_clazzF != NULL);
        if (t_clazzF == NULL) {
            return False;
        }
        clazzF = (*env)->NewGlobalRef(env, t_clazzF);
        DASSERT(clazzF != NULL);
        (*env)->DeleteLocalRef(env, t_clazzF);
    }

    if (clazzD == NULL && !initClazzD(env)) {
        return False;
    }

    return (*env)->IsInstanceOf(env, target, clazzF) ||
        (*env)->IsInstanceOf(env, target, clazzD);
}

Boolean
isDialog(jobject target, JNIEnv *env) {
    if (clazzD == NULL && !initClazzD(env)) {
        return False;
    }
    return (*env)->IsInstanceOf(env, target, clazzD);
}

// Returns a local ref to a decorated owner of the target,
// or NULL if the target is Frame or Dialog itself.
// The local ref returned should be deleted by the caller.
jobject
getOwningFrameOrDialog(jobject target, JNIEnv *env) {
    jobject _target = (*env)->NewLocalRef(env, target);
    jobject parent = _target;
    Boolean isSelfFrameOrDialog = True;

    while (!isFrameOrDialog(parent, env)) {
        isSelfFrameOrDialog = False;
        parent = (*env)->CallObjectMethod(env, _target, componentIDs.getParent);
        (*env)->DeleteLocalRef(env, _target);
        _target = parent;
    }

    if (isSelfFrameOrDialog) {
        (*env)->DeleteLocalRef(env, parent);
        return NULL;
    }
    return parent;
}

Widget
findWindowsProxy(jobject window, JNIEnv *env) {
    struct ComponentData *cdata;
    jobject tlPeer;
    jobject owner_prev = NULL, owner_new = NULL;
    /* the owner of a Window is in its parent field */
    /* we may have a chain of Windows; go up the chain till we find the
       owning Frame or Dialog */
    if ((*env)->EnsureLocalCapacity(env, 4) < 0) {
        return NULL;
    }

    if (window == NULL) return NULL;

    owner_prev = (*env)->NewLocalRef(env, window);
    while (!JNU_IsNull(env, owner_prev) && !(isFrameOrDialog(owner_prev, env))) {
        owner_new = (*env)->CallObjectMethod(env, owner_prev, componentIDs.getParent);
        (*env)->DeleteLocalRef(env, owner_prev);
        owner_prev = owner_new;
    }

    if (owner_prev == NULL) return NULL;

    tlPeer = (*env)->GetObjectField(env, owner_prev, componentIDs.peer);
    (*env)->DeleteLocalRef(env, owner_prev);
    if (tlPeer == NULL) return NULL;

    cdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env, tlPeer, mComponentPeerIDs.pData);
    (*env)->DeleteLocalRef(env, tlPeer);

    if (cdata == NULL) return NULL;
    return(findFocusProxy(cdata->widget));
}

jobject
findTopLevel(jobject peer, JNIEnv *env) {
    jobject target_prev = NULL;
    static jclass clazzW = NULL;

    if ((*env)->EnsureLocalCapacity(env, 3) < 0) {
        return NULL;
    }

    if (clazzW == NULL) {
        jclass t_clazzW = (*env)->FindClass(env, "java/awt/Window");
        if ((*env)->ExceptionOccurred(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        DASSERT(t_clazzW != NULL);
        if (t_clazzW == NULL) {
            return NULL;
        }
        clazzW = (*env)->NewGlobalRef(env, t_clazzW);
        DASSERT(clazzW != NULL);
        (*env)->DeleteLocalRef(env, t_clazzW);
    }
    target_prev = (*env)->GetObjectField(env, peer, mComponentPeerIDs.target);
    if (target_prev == NULL) {
        return NULL;
    }

    while ((target_prev != NULL)
           && !(*env)->IsInstanceOf(env, target_prev, clazzW) )
    {
        /* go up the hierarchy until we find a window */
        jobject target_new = (*env)->CallObjectMethod(env, target_prev, componentIDs.getParent);
        (*env)->DeleteLocalRef(env, target_prev);
        target_prev = target_new;
    }
    return target_prev;
}

static Window
rootWindow(Window w) {
    Window root = None;
    Window parent = None;
    Window *children = NULL;
    uint32_t nchildren = 0;

    if (w != None) {
        Status res = XQueryTree(awt_display, w, &root, &parent, &children, &nchildren);
        if (res == 0) {
            return None;
        }
        if (children != NULL) {
            XFree(children);
        }
  return root;
    } else {
        return None;
    }
}

Boolean IsRootOf(Window root, Window child) {
    Window w_root = None, w_parent = None, * children = NULL;
    uint32_t c_count = 0;
    if (root == None || child == None) {
        return False;
    }
    do {
        w_root = None;
        w_parent = None;
        children = NULL;
        c_count = 0;
        if (XQueryTree(awt_display, child, &w_root, &w_parent,
                       &children, &c_count)) {
            if (children != NULL) {
                XFree(children);
            }
            if (w_parent == None) {
                return False;
            }
            if (w_parent == root) {
                return True;
            }
        } else {
            return False;
        }
        child = w_parent;
    } while (True);
}

Window findShellByProxy(Window proxy) {
    Widget proxy_wid = XtWindowToWidget(awt_display, proxy);
    while (proxy_wid != NULL && !XtIsShell(proxy_wid)) {
        proxy_wid = XtParent(proxy_wid);
    }
    if (proxy_wid == NULL) {
        return None;
    }
    return XtWindow(proxy_wid);
}

// Window which contains focus owner when focus proxy is enabled
Window trueFocusWindow = None;
// Window which works as proxy for input events for real focus owner.
Window focusProxyWindow = None;

void clearFocusPathOnWindow(Window win) {
    if (focusProxyWindow != None && IsRootOf(win, trueFocusWindow)) {
        XEvent ev;
        memset(&ev, 0, sizeof(ev));
        ev.type = FocusOut;
        ev.xany.send_event = True;
        ev.xany.display = awt_display;
        ev.xfocus.mode = NotifyNormal;
        ev.xfocus.detail = NotifyNonlinear;
        {
            Window root = rootWindow(trueFocusWindow);
            JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
            ev.xfocus.window = trueFocusWindow;
            while (ev.xfocus.window != root &&
                   ev.xfocus.window != None) {
                Widget w = XtWindowToWidget(awt_display,
                                            ev.xfocus.window);
                awt_put_back_event(env, &ev);
                if (w == NULL) {
                    break;
                }
                if (XtParent(w) != NULL) {
                    ev.xfocus.window = XtWindow(XtParent(w));
                } else {
                    ev.xfocus.window = None;
                }
            }
        }
        XSetInputFocus(awt_display, findShellByProxy(focusProxyWindow), RevertToPointerRoot, CurrentTime);
        trueFocusWindow = None;
        focusProxyWindow = None;
    }
}
void clearFocusPath(Widget shell) {
    Window w = None;
    if (shell == NULL) {
        return;
    }
    w = XtWindow(shell);
    clearFocusPathOnWindow(w);
}

void globalClearFocusPath(Widget focusOwnerShell ) {
    if (focusProxyWindow != None) {
        Window shellWindow = findShellByProxy(trueFocusWindow);
        if (shellWindow != None) {
            Widget shell = XtWindowToWidget(awt_display, shellWindow);
            if (shell != NULL && shell != focusOwnerShell) {
                clearFocusPath(shell);
            }
        }
    }
}

static void
focusEventForProxy(XEvent xev,
                   JNIEnv *env,
                   Window *trueFocusWindow,
                   Window *focusProxyWindow) {

    DASSERT (trueFocusWindow != NULL && focusProxyWindow != NULL);
  if (xev.type == FocusOut) {
    if (xev.xfocus.window == *focusProxyWindow) {
            if (*trueFocusWindow != None) {
                Window root = rootWindow(*trueFocusWindow);
      focusOutEvent.xfocus.window = *trueFocusWindow;
#ifdef DEBUG_FOCUS
      printf(" nulling out proxy; putting back event"
             "\n");
#endif

      while (focusOutEvent.xfocus.window != root &&
             focusOutEvent.xfocus.window != None) {
        Widget w = XtWindowToWidget(awt_display,
                                    focusOutEvent.xfocus.window);
        awt_put_back_event(env, &focusOutEvent);
        if (w != NULL && XtParent(w) != NULL) {
          focusOutEvent.xfocus.window = XtWindow(XtParent(w));
        } else {
          focusOutEvent.xfocus.window = None;
        }
      }
      *trueFocusWindow = None;
      *focusProxyWindow = None;
      return;
    } else {
#ifdef DEBUG_FOCUS
      printf("\n");
#endif
      return;
    }
  } else {
#ifdef DEBUG_FOCUS
    printf("\n");
#endif
    return;
  }
    }
}

static void
focusEventForFrame(XEvent xev, Window focusProxyWindow) {
  if (xev.type == FocusIn) {
    if (focusProxyWindow != None) {
      /* eat it */
      return;
    } else /* FocusIn on Frame or Dialog */ {
      XtDispatchEvent(&xev);
    }
  } else /* FocusOut on Frame or Dialog */{
    XtDispatchEvent(&xev);
  }
}

static void
focusEventForWindow(XEvent xev, JNIEnv *env, Window *trueFocusWindow,
                    Window *focusProxyWindow, jobject target) {
  XEvent pev;
  if (xev.type == FocusIn && xev.xfocus.mode == NotifyNormal) {
    /* If it's a FocusIn, allow it to process, then set
       focus to focus proxy */
    Widget focusProxy;
    focusProxy = findWindowsProxy(target, env);
    if (focusProxy != NULL) {
      XtDispatchEvent(&xev);
      *focusProxyWindow = XtWindow(focusProxy);

      XSetInputFocus(awt_display, *focusProxyWindow,
                     RevertToParent,
                     CurrentTime);

      XPeekEvent(awt_display, &pev);
      while (pev.type == FocusIn) {
        XNextEvent(awt_display, &xev);
        XPeekEvent(awt_display, &pev);
      }
      *trueFocusWindow = xev.xany.window;

    } /* otherwise error */
  } else /* FocusOut */ {
    /* If it's a FocusOut on a Window, discard it unless
       it's an event generated by us. */
    if (xev.xany.send_event) {
      XtDispatchEvent(&xev);
    }
  }
}

Boolean
isAncestor(Window ancestor, Window child) {
  Window *children;
  uint32_t nchildren;
  Boolean retvalue = False;

  while (child != ancestor) {
    Window parent, root;
    Status status;

    status = XQueryTree(awt_display, child, &root, &parent,
                        &children, &nchildren);
    if (status == 0) return False; /* should be an error of some sort? */

    if (parent == root) {
      if (child != ancestor) {
        retvalue = False;
        break;
      } else {
        retvalue = True;
        break;
      }
    }
    if (parent == ancestor) { retvalue = True; break; }
    if (nchildren > 0) XFree(children);
    child = parent;
  }
  if (nchildren > 0) XFree(children);
  return retvalue;
}

/**
 * Returns focusability of the corresponding Java Window object
 */
Boolean
isFocusableWindow(Window w) {
    Widget wid = NULL;
    JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);

    wid = XtWindowToWidget(awt_display, w);
    while (wid != NULL && !XtIsShell(wid)) {
        wid = XtParent(wid);
    }

    // If the window doesn't have shell consider it focusable as all windows
    // are focusable by default
    if (wid == NULL) return True;

    return isFocusableWindowByShell(env, wid);
}

void postUngrabEvent(Widget shell) {
    JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
    Widget canvas = findTopLevelByShell(shell);
    if (canvas != NULL) {
        jobject peer = findPeer(&canvas);
        if (peer != NULL) {
            JNU_CallMethodByName(env, NULL, peer, "postUngrabEvent", "()V", NULL);
        }
    }
}

Boolean eventInsideGrabbed(XEvent * ev) {
    if (grabbed_widget == NULL) {
        return False;
    }

    switch (ev->xany.type) {
      case LeaveNotify:
      case ButtonPress:
      case ButtonRelease:
      case MotionNotify:
      case EnterNotify:
      {
          JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
          Widget grab = findTopLevelByShell(grabbed_widget);
          if (grab != NULL) {
              jobject peer = findPeer(&grab);
              Widget target = XtWindowToWidget(awt_display, ev->xbutton.window);
              jobject targetPeer = findPeer(&target);
              if (peer != NULL) {
                  return JNU_CallMethodByName(env, NULL, peer, "processUngrabMouseEvent", "(Lsun/awt/motif/MComponentPeer;III)Z",
                                              targetPeer, ev->xbutton.x_root, ev->xbutton.y_root,
                                              ev->xany.type, NULL).z;
              }
          }
          return False;
      }
      case FocusOut:
          if (ev->xfocus.window == XtWindow(grabbed_widget) ||
              isAncestor(XtWindow(grabbed_widget), ev->xfocus.window))
          {
              postUngrabEvent(grabbed_widget);
              return True;
          }
      default:
          return True;
    }
}

/**
 * Processes and removes one X/Xt event from the Xt event queue.
 * Handles events pushed back via awt_put_back_event() FIRST,
 * then new events on the X queue
 */
static void
processOneEvent(XtInputMask iMask) {
            XEvent xev;
            Boolean haveEvent = False;
            if (putbackQueueCount > 0) {
                // There is a pushed-back event - handle it first
                if (awt_get_next_put_back_event(&xev) == 0) {
                    if (xev.xany.send_event != SPECIAL_KEY_EVENT) {
#ifdef DEBUG_FOCUS
                        if (xev.type == FocusOut) {
                            printf("putback FocusOut on window %d, mode %d, "
                                   "detail %d, send_event  %d\n",
                                   xev.xfocus.window, xev.xfocus.mode,
                                   xev.xfocus.detail, xev.xfocus.send_event);
                        }
#endif
                        eventNumber++;
                        XtDispatchEvent(&xev);
                        return;
                    } else {
                        haveEvent = True;
                    }
                }
            }

            if (haveEvent || XtAppPeekEvent(awt_appContext, &xev)) {
             /*
              * Fix for BugTraq ID 4041235, 4100167:
              * First check that the event still has a widget, because
              * the widget may have been destroyed by another thread.
              */
              Widget widget=XtWindowToWidget(awt_display, xev.xany.window);
              eventNumber++;
#ifdef __linux__
              statusWindowEventHandler(xev);
#endif
              xembed_eventHandler(&xev);
              xembed_serverEventHandler(&xev);
              syncWait_eventHandler(&xev);

              if (!haveEvent && awt_dnd_process_event(&xev)) {
                  return;
              }

              if ((widget == NULL) || (!XtIsObject(widget)) ||
                  (widget->core.being_destroyed)) {
                /*
                 * if we get here, the event could be one of
                 * the following:
                 * - notification that a "container" of
                 *    any of our embedded frame has been moved
                 * - event understandable by XFilterEvent
                 * - for one of our old widget which has gone away
                 */
                XNextEvent(awt_display, &xev);

                if (widget == NULL) {
                    /* an embedded frame container has been moved? */
                    if (awt_util_processEventForEmbeddedFrame(&xev)) {
                        return;
                    }

                    /* manager selections related event? */
                    if (awt_mgrsel_processEvent(&xev)) {
                        return;
                    }
                }

                /*
                 * Fix for BugTraq ID 4196573:
                 * Call XFilterEvent() to give a chance to X Input
                 * Method to process this event before being
                 * discarded.
                 */
                (void) XFilterEvent(&xev, NULL);
                return;
              }

              /* There is an X event on the queue. */
              switch (xev.type) {
              case KeyPress:
              case KeyRelease:
              case ButtonPress:
              case ButtonRelease:
              case MotionNotify:
              case EnterNotify:
              case LeaveNotify:
                /* Fix for BugTraq ID 4048060. Dispatch scrolling events
                   immediately to the ScrollBar widget to prevent spurious
                   continuous scrolling. Otherwise, if the application is busy,
                   the ButtonRelease event is not dispatched in time to prevent
                   a ScrollBar timeout from expiring, and restarting the
                   continuous scrolling timer.
                   */
                  if ((xev.type == ButtonPress                          ||
                       xev.type == ButtonRelease                                ||
                       (xev.type == MotionNotify                                &&
                        (xev.xmotion.state == Button1Mask                       ||
                         xev.xmotion.state == Button2Mask                       ||
                         xev.xmotion.state == Button3Mask)))            &&
                      (XtIsSubclass(widget, xmScrollBarWidgetClass))) {
                      /* Use XNextEvent instead of XtAppNextEvent, because
                         XtAppNextEvent processes timers before getting the next X
                         event, causing a race condition, since the TimerEvent
                         callback in the ScrollBar widget restarts the continuous
                         scrolling timer.
                      */
                      XNextEvent(awt_display, &xev);

                      XtDispatchEvent(&xev);
                      XSync(awt_display, False);

                      // This is the event on scrollbar.  Key, Motion,
                      // Enter/Leave dispatch as usual, Button should
                      // generate Ungrab after Java mouse event
                      if (xev.type == ButtonPress && grabbed_widget != NULL) {
                          eventInsideGrabbed(&xev);
                      }
                  }
                  else {
                      if (!haveEvent) XtAppNextEvent(awt_appContext, &xev);

                      // This is an event on one of our widgets.  Key,
                      // Motion, Enter/Leave dispatch as usual, Button
                      // should generate Ungrab after Java mouse event
/*                       if (grabbed_widget != NULL && !eventInsideGrabbed(&xev)) { */
/*                           return; */
/*                       } */

                      if (xev.type == ButtonPress) {
                          Window window = findShellByProxy(xev.xbutton.window);
                          if (window != None) {
                              XWindowAttributes winAttr;
                              memset(&winAttr, 0, sizeof(XWindowAttributes));
                              XGetWindowAttributes(awt_display, window, &winAttr);
                              if (winAttr.override_redirect == TRUE && isFocusableWindow(window)) {
                                  XSetInputFocus(awt_display, window, RevertToPointerRoot, CurrentTime);
                              }
                          }
                      }
                      if(xev.type == KeyPress) {
#ifdef DEBUG_FOCUS
                          printf("KeyPress on window %d\n", xev.xany.window);
#endif
                      }

                    /* this could be moved to shouldDispatchToWidget */
                    /* if there is a proxy in effect, dispatch key events
                       through the proxy */
                    if ((xev.type == KeyPress || xev.type == KeyRelease) &&
                        !keyboardGrabbed && !haveEvent) {
                        if (focusProxyWindow != None) {
                            Widget widget;
                            struct WidgetInfo *winfo;
                            Boolean cont;
                            /* Key event should be posted to the top-level
                               widget of the proxy */
                            xev.xany.window = proxyTopLevel(focusProxyWindow);
                            widget = XtWindowToWidget(awt_display,
                                                      xev.xany.window);
                            if (widget == NULL) return;
                            if ((winfo = findWidgetInfo(widget)) == NULL) {
                                return;
                            }
                            awt_canvas_handleEvent(widget, winfo->peer, &xev,
                                                   winfo, &cont, TRUE);
                            return;
                        }
                    }
                    if (!shouldDispatchToWidget(&xev)) {
                        XtDispatchEvent(&xev);
                    }

                    // See comment above - "after time" is here.
                    if (grabbed_widget != NULL && xev.type == ButtonPress) {
                        eventInsideGrabbed(&xev);
                    }
                }


              break;

              case FocusIn:
              case FocusOut: {
                  void *peer;
                  jobject target;

                  JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);

#ifdef DEBUG_FOCUS
                  if (xev.type == FocusIn) {

                      fprintf(stderr, "FocusIn on window %x, mode %d, detail %d, "
                             "send_event %d\n", xev.xfocus.window,
                             xev.xfocus.mode, xev.xfocus.detail,
                             xev.xfocus.send_event);
                  } else {
                      fprintf(stderr, "FocusOut on window %x, mode %d, detail %d, "
                             "send_event %d\n", xev.xfocus.window,
                             xev.xfocus.mode, xev.xfocus.detail,
                             xev.xfocus.send_event);
                  }
#endif
                  XtAppNextEvent(awt_appContext, &xev);

                  if (xev.xfocus.detail == NotifyVirtual ||
                      xev.xfocus.detail == NotifyNonlinearVirtual) {
#ifdef DEBUG_FOCUS
                      printf("discarding\n");
#endif
                      return;
                  }

                  // Check for xembed on this window. If it is active and this is not XEmbed focus
                  // event(send_event = 0) then we should skip it
                  if (isXEmbedActiveByWindow(xev.xfocus.window) && !xev.xfocus.send_event) {
                      return;
                  }

                  /* In general, we need to to block out focus events
                     that are caused by keybaord grabs initiated by
                     dragging the title bar or the scrollbar. But we
                     need to let through the ones that are aimed at
                     choice boxes or menus. So we keep track of when
                     the keyboard is grabbed by a popup. */

                  if (awt_isAwtMenuWidget(widget)) {
                    if (xev.type == FocusIn &&
                        xev.xfocus.mode == NotifyGrab) {
                          extern Boolean poppingDown;
                          if (!poppingDown) {
                      keyboardGrabbed = True;
                           }
                      } else /* FocusOut */ {
                          if (xev.type == FocusOut &&
                              xev.xfocus.mode == NotifyUngrab) {
                        keyboardGrabbed = False;
                      }
                    }
                  }

                  if (focusProxyWindow != None) {
#ifdef DEBUG_FOCUS
                      printf("non-null proxy; proxy = %d ", focusProxyWindow);
#endif
                      if (trueFocusWindow != None) {
                        /* trueFocusWindow should never be None here, but if
                           things ever get skewed, we want to be able to
                           recover rather than crash */
                        focusEventForProxy(xev, env, &trueFocusWindow,
                                           &focusProxyWindow);
                      return;
                      } else {
                        /* beartrap -- remove before shipping */
                        /* printf("trueFocusWindow None in processOneEvent;\n"); */
                        /* printf("Please file a bug\n"); */
                      }
                  }

                  peer = findPeer(&widget);
                  if (peer == NULL) {
#ifdef DEBUG_FOCUS
                      printf("null peer -- shouldn't see in java handler\n");
#endif
                      XtDispatchEvent(&xev);
                      return;
                  }

                  /* Find the top-level component */

                  if ((*env)->EnsureLocalCapacity(env, 1) < 0) {
                    return;
                  }
                  target = findTopLevel(peer, env);
                  if (target == NULL) {
                      JNU_ThrowNullPointerException(env, "component without a "
                                                    "window");
                      return;
                  }

                  if (isFrameOrDialog(target, env)) {
#ifdef DEBUG_FOCUS
                      printf("Focus event directed at a frame; frame = %d\n",
                             xev.xany.window);
#endif
                      focusEventForFrame(xev, focusProxyWindow);
                      (*env)->DeleteLocalRef(env, target);
                      return;
                  } else {
#ifdef DEBUG_FOCUS
                      printf("Focus event directed at a window; window = %d\n",
                             xev.xany.window);
#endif
                      focusEventForWindow(xev, env, &trueFocusWindow,
                                          &focusProxyWindow, target);
                      (*env)->DeleteLocalRef(env, target);
                      return;
                  }
              }

              case UnmapNotify:
#ifdef DEBUG_FOCUS
                printf("Unmap on window %d\n", xev.xany.window);
                printf("True focus window is %d\n", trueFocusWindow);
#endif
                clearFocusPathOnWindow(xev.xunmap.window);

              default:
                XtAppProcessEvent(awt_appContext, iMask);
                break;
              }
            }
            else {
              /* There must be a timer, alternate input, or signal event. */
              XtAppProcessEvent(awt_appContext, iMask & ~XtIMXEvent);
            }

} /* processOneEvent() */

/*
 * Waits for X/Xt events to appear on the pipe. Returns only when
 * it is likely (but not definite) that there are events waiting to
 * be processed.
 *
 * This routine also flushes the outgoing X queue, when the
 * awt_next_flush_time has been reached.
 *
 * If fdAWTPipe is greater or equal than zero the routine also
 * checks if there are events pending on the putback queue.
 */
void
waitForEvents(JNIEnv *env, int32_t fdXPipe, int32_t fdAWTPipe) {

        while ((fdAWTPipe >= 0 && awt_events_pending(awt_appContext) == 0) ||
               (fdAWTPipe <  0 && XtAppPending(awt_appContext) == 0)) {
#ifdef USE_SELECT
            performSelect(env,fdXPipe,fdAWTPipe);
#else
            performPoll(env,fdXPipe,fdAWTPipe);
#endif
            if ((awt_next_flush_time > 0) &&
                (awtJNI_TimeMillis() > awt_next_flush_time)) {
                AWT_FLUSHOUTPUT_NOW();
            }
        }  /* end while awt_events_pending() == 0 */
} /* waitForEvents() */

/*************************************************************************
 **                                                                     **
 ** WE USE EITHER select() OR poll(), DEPENDING ON THE USE_SELECT       **
 ** COMPILE-TIME CONSTANT.                                              **
 **                                                                     **
 *************************************************************************/

#ifdef USE_SELECT

static struct fd_set rdset;
struct timeval sel_time;

/*
 * Performs select() on both the X pipe and our AWT utility pipe.
 * Returns when data arrives or the operation times out.
 *
 * Not all Xt events come across the X pipe (e.g., timers
 * and alternate inputs), so we must time out every now and
 * then to check the Xt event queue.
 *
 * The fdAWTPipe will be empty when this returns.
 */
static void
performSelect(JNIEnv *env, int32_t fdXPipe, int32_t fdAWTPipe) {

            int32_t result;
            int32_t count;
            int32_t nfds = 1;
            uint32_t timeout = awt_get_poll_timeout(False);

            /* Fixed 4250354 7/28/99 ssi@sparc.spb.su
             * Cleaning up Global Refs in case of No Events
             */
            awtJNI_CleanupGlobalRefs();

            FD_ZERO( &rdset );
            FD_SET(fdXPipe, &rdset);
            if (fdAWTPipe >= 0) {
                nfds++;
                FD_SET(fdAWTPipe, &rdset);
            }
            if (timeout == 0) {
                // be sure other threads get a chance
                awtJNI_ThreadYield(env);
            }
            // set the appropriate time values. The DASSERT() in
            // MToolkit_run() makes sure that this will not overflow
            sel_time.tv_sec = (timeout * 1000) / (1000 * 1000);
            sel_time.tv_usec = (timeout * 1000) % (1000 * 1000);
            AWT_NOFLUSH_UNLOCK();
            result = select(nfds, &rdset, 0, 0, &sel_time);
            AWT_LOCK();

            /* reset tick if this was not a time out */
            if (result == 0) {
                /* select() timed out -- update timeout value */
                awt_get_poll_timeout(True);
            }
            if (fdAWTPipe >= 0 && FD_ISSET ( fdAWTPipe, &rdset ) )
            {
                /* There is data on the AWT pipe - empty it */
                do {
                    count = read(fdAWTPipe, read_buf, AWT_POLL_BUFSIZE );
                } while (count == AWT_POLL_BUFSIZE );
            }
} /* performSelect() */

#else /* !USE_SELECT */

/*
 * Polls both the X pipe and our AWT utility pipe. Returns
 * when there is data on one of the pipes, or the operation times
 * out.
 *
 * Not all Xt events come across the X pipe (e.g., timers
 * and alternate inputs), so we must time out every now and
 * then to check the Xt event queue.
 *
 * The fdAWTPipe will be empty when this returns.
 */
static void
performPoll(JNIEnv *env, int32_t fdXPipe, int32_t fdAWTPipe) {

            static struct pollfd pollFds[2];
            uint32_t timeout = awt_get_poll_timeout(False);
            int32_t result;
            int32_t count;

            /* Fixed 4250354 7/28/99 ssi@sparc.spb.su
             * Cleaning up Global Refs in case of No Events
             */
            awtJNI_CleanupGlobalRefs();

            pollFds[0].fd = fdXPipe;
            pollFds[0].events = POLLRDNORM;
            pollFds[0].revents = 0;

            pollFds[1].fd = fdAWTPipe;
            pollFds[1].events = POLLRDNORM;
            pollFds[1].revents = 0;

            AWT_NOFLUSH_UNLOCK();

            /* print the poll timeout time in brackets */
            DTRACE_PRINT1("[%dms]",(int32_t)timeout);
#ifdef DEBUG
            if (++debugPrintLineCount > 8) {
                DTRACE_PRINTLN("");
                debugPrintLineCount = 0;
            }
#endif
            /* ACTUALLY DO THE POLL() */
            if (timeout == 0) {
                // be sure other threads get a chance
                awtJNI_ThreadYield(env);
            }
            result = poll( pollFds, 2, (int32_t) timeout );

#ifdef DEBUG
            DTRACE_PRINT1("[poll()->%d]", result);
            if (++debugPrintLineCount > 8) {
                DTRACE_PRINTLN("");
                debugPrintLineCount = 0;
            }
#endif
            AWT_LOCK();
            if (result == 0) {
                /* poll() timed out -- update timeout value */
                awt_get_poll_timeout(True);
            }
            if ( pollFds[1].revents )
            {
                /* There is data on the AWT pipe - empty it */
                do {
                    count = read(AWT_READPIPE, read_buf, AWT_POLL_BUFSIZE );
                } while (count == AWT_POLL_BUFSIZE );
                DTRACE_PRINTLN1("wokeup on AWTPIPE, timeout:%d", timeout);
            }
            return;

} /* performPoll() */

#endif /* !USE_SELECT */

/*
 * Pushes an X event back on the queue to be handled
 * later.
 *
 * Ignores the request if event is NULL
 */
void
awt_put_back_event(JNIEnv *env, XEvent *event) {

    Boolean addIt = True;
    if (putbackQueueCount >= putbackQueueCapacity) {
        /* not enough room - alloc 50% more space */
        int32_t newCapacity;
        XEvent *newQueue;
        newCapacity = putbackQueueCapacity * 3 / 2;
        if ((newCapacity - putbackQueueCapacity)
                                        < PUTBACK_QUEUE_MIN_INCREMENT) {
            /* always increase by at least min increment */
            newCapacity = putbackQueueCapacity + PUTBACK_QUEUE_MIN_INCREMENT;
        }
        newQueue = (XEvent*)realloc(
                        putbackQueue, newCapacity*(sizeof(XEvent)));
        if (newQueue == NULL) {
            JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
            addIt = False;
        } else {
            putbackQueue = newQueue;
            putbackQueueCapacity = newCapacity;
        }
    }
    if (addIt) {
        char oneChar = 'p';
        memcpy(&(putbackQueue[putbackQueueCount]), event, sizeof(XEvent));
        putbackQueueCount++;

        // wake up the event loop, if it's sleeping
        write (AWT_WRITEPIPE, &oneChar, 1);
    }

    return;
} /* awt_put_back_event() */

/*
 * Gets the next event that has been pushed back onto the queue.
 * Returns 0 and fills in xev_out if successful
 */
static int32_t
awt_get_next_put_back_event(XEvent *xev_out) {

    Boolean err = False;
    if (putbackQueueCount < 1) {
        err = True;
    } else {
        memcpy(xev_out, &(putbackQueue[0]), sizeof(XEvent));
    }
    if (!err) {
        /* remove it from the queue */
        if (putbackQueueCount == 1) {

            // queue is now empty
            if (putbackQueueCapacity > PUTBACK_QUEUE_MIN_INCREMENT) {

                /* Too much space -- delete it and rebuild later */
                free(putbackQueue);
                putbackQueue = NULL;
                putbackQueueCapacity = 0;
            }
        } else {
            /* more than 1 event in queue - shift all events to the left */
            /* We don't free the allocated memory until the queue
               becomes empty, just 'cause it's easier that way. */
            /* NOTE: use memmove(), because the memory blocks overlap */
            memmove(&(putbackQueue[0]), &(putbackQueue[1]),
                (putbackQueueCount-1)*sizeof(XEvent));
        }
        --putbackQueueCount;
    }
    DASSERT(putbackQueueCount >= 0);

    return (err? -1:0);

} /* awt_get_next_put_back_event() */

/**
 * Determines whether or not there are X or Xt events pending.
 * Looks at the putbackQueue.
 */
static XtInputMask
awt_events_pending(XtAppContext appContext) {
    XtInputMask imask = 0L;
    imask = XtAppPending(appContext);
    if (putbackQueueCount > 0) {
        imask |= XtIMXEvent;
    }
    return imask;
}


#ifndef NOMODALFIX
#define WIDGET_ARRAY_SIZE 5;
static int32_t arraySize = 0;
static int32_t arrayIndx = 0;
static Widget *dShells = NULL;

void
awt_shellPoppedUp(Widget shell,
                   XtPointer modal,
                   XtPointer call_data)
{
    if (arrayIndx == arraySize ) {
        /* if we have not allocate an array, do it first */
        if (arraySize == 0) {
            arraySize += WIDGET_ARRAY_SIZE;
            dShells = (Widget *) malloc(sizeof(Widget) * arraySize);
        } else {
            arraySize += WIDGET_ARRAY_SIZE;
            dShells = (Widget *) realloc((void *)dShells, sizeof(Widget) * arraySize);
        }
    }

    dShells[arrayIndx] = shell;
    arrayIndx++;
}

void
awt_shellPoppedDown(Widget shell,
                   XtPointer modal,
                   XtPointer call_data)
{
    arrayIndx--;

    if (dShells[arrayIndx] == shell) {
        dShells[arrayIndx] = NULL;
        return;
    } else {
        int32_t i;

        /* find the position of the shell in the array */
        for (i = arrayIndx; i >= 0; i--) {
            if (dShells[i] == shell) {
                break;
            }
        }

        /* remove the found element */
        while (i <= arrayIndx-1) {
            dShells[i] = dShells[i+1];
            i++;
        }
    }
}

Boolean
awt_isWidgetModal(Widget widget)
{
    Widget w;

    for (w = widget; !XtIsShell(w); w = XtParent(w)) { }

    while (w != NULL) {
        if (w == dShells[arrayIndx-1]) {
            return True;
        }
        w = XtParent(w);
    }
    return False;
}

Boolean
awt_isModal()
{
    return (arrayIndx > 0);
}
#endif // NOMODALFIX


/*
 * Simply waits for terminateFn() to return True. Waits on the
 * awt lock and is notified to check its state by the main event
 * loop whenever the Xt event queue is empty.
 *
 * NOTE: when you use this routine check if it can be called on the event
 * dispatch thread during drag-n-drop operation and update
 * secondary_loop_event() predicate to prevent deadlock.
 */
void
awt_MToolkit_modalWait(int32_t (*terminateFn) (void *data), void *data )
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    AWT_LOCK();
    AWT_FLUSHOUTPUT_NOW();
    while ((*terminateFn) (data) == 0) {
        AWT_WAIT(AWT_MAX_POLL_TIMEOUT);
        if ((*env)->ExceptionCheck(env)) {
            (*env)->ExceptionClear(env);
            break;
        }
    }
    AWT_NOTIFY_ALL();
    AWT_UNLOCK();
}

static uint32_t
colorToRGB(XColor * color)
{
    int32_t rgb = 0;

    rgb |= ((color->red >> 8) << 16);
    rgb |= ((color->green >> 8) << 8);
    rgb |= ((color->blue >> 8) << 0);

    return rgb;
}

/*
 * fix for bug #4088106 - ugly text boxes and grayed out looking text
 */

XmColorProc oldColorProc;

void
ColorProc(XColor* bg_color,
          XColor* fg_color,
          XColor* sel_color,
          XColor* ts_color,
          XColor* bs_color)
{
    unsigned long plane_masks[1];
    unsigned long colors[5];

    AwtGraphicsConfigDataPtr defaultConfig =
        getDefaultConfig(DefaultScreen(awt_display));

    /* use the default procedure to calculate colors */
    oldColorProc(bg_color, fg_color, sel_color, ts_color, bs_color);

    /* check if there is enought free color cells */
    if (XAllocColorCells(awt_display, defaultConfig->awt_cmap, False,
        plane_masks, 0, colors, 5)) {
        XFreeColors(awt_display, defaultConfig->awt_cmap, colors, 5, 0);
        return;
    }

    /* find the closest matches currently available */
    fg_color->pixel = defaultConfig->AwtColorMatch(fg_color->red   >> 8,
                                                   fg_color->green >> 8,
                                                   fg_color->blue  >> 8,
                                                   defaultConfig);
    fg_color->flags = DoRed | DoGreen | DoBlue;
    XQueryColor(awt_display, defaultConfig->awt_cmap, fg_color);
    sel_color->pixel = defaultConfig->AwtColorMatch(sel_color->red   >> 8,
                                                    sel_color->green >> 8,
                                                    sel_color->blue  >> 8,
                                                    defaultConfig);
    sel_color->flags = DoRed | DoGreen | DoBlue;
    XQueryColor(awt_display, defaultConfig->awt_cmap, sel_color);
    ts_color->pixel = defaultConfig->AwtColorMatch(ts_color->red   >> 8,
                                                   ts_color->green >> 8,
                                                   ts_color->blue  >> 8,
                                                   defaultConfig);
    ts_color->flags = DoRed | DoGreen | DoBlue;
    XQueryColor(awt_display, defaultConfig->awt_cmap, ts_color);
    bs_color->pixel = defaultConfig->AwtColorMatch(bs_color->red   >> 8,
                                                   bs_color->green >> 8,
                                                   bs_color->blue  >> 8,
                                                   defaultConfig);
    bs_color->flags = DoRed | DoGreen | DoBlue;
    XQueryColor(awt_display, defaultConfig->awt_cmap, bs_color);
}


/*
 * Read _XSETTINGS_SETTINGS property from _XSETTINGS selection owner
 * and pass its value to the java world for processing.
 */
/*static*/ void
awt_xsettings_update(int scr, Window owner, void *cookie)
{
    Display *dpy = awt_display;
    int status;

    JNIEnv *env;
    jobject mtoolkit;
    jmethodID upcall;
    jbyteArray array;

    struct xsettings_callback_cookie *upcall_cookie = cookie;

    /* Returns of XGetWindowProperty */
    Atom actual_type;
    int actual_format;
    unsigned long nitems;
    unsigned long bytes_after;
    unsigned char *xsettings;

    DTRACE_PRINTLN2("XS: update screen %d, owner 0x%08lx",
                    scr, owner);

#if 1 /* XXX: kludge */
    /*
     * As toolkit cannot yet cope with per-screen desktop properties,
     * only report XSETTINGS changes on the default screen.  This
     * should be "good enough" for most cases.
     */
    if (scr != DefaultScreen(dpy)) {
        DTRACE_PRINTLN2("XS: XXX: default screen is %d, update is for %d, ignoring", DefaultScreen(dpy), scr);
        return;
    }
#endif /* kludge */

    env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    DASSERT(env != NULL);

    DASSERT(upcall_cookie != NULL);
    mtoolkit = upcall_cookie->mtoolkit;
    upcall = upcall_cookie->upcallMID;

    DASSERT(!JNU_IsNull(env, mtoolkit));
    DASSERT(upcall != NULL);

    /*
     * XXX: move awt_getPropertyFOO from awt_wm.c to awt_util.c and
     * use the appropriate one.
     */
    status = XGetWindowProperty(dpy, owner,
                 _XA_XSETTINGS_SETTINGS, 0, 0xFFFF, False,
                 _XA_XSETTINGS_SETTINGS,
                 &actual_type, &actual_format, &nitems, &bytes_after,
                 &xsettings);

    if (status != Success) {
        DTRACE_PRINTLN("XS:   unable to read _XSETTINGS");
        return;
    }

    if (xsettings == NULL) {
        DTRACE_PRINTLN("XS:   reading _XSETTINGS, got NULL");
        return;
    }

    if (actual_type != _XA_XSETTINGS_SETTINGS) {
        XFree(xsettings);       /* NULL data already catched above */
        DTRACE_PRINTLN("XS:   _XSETTINGS_SETTINGS is not of type _XSETTINGS_SETTINGS");
        return;
    }

    DTRACE_PRINTLN1("XS:   read %lu bytes of _XSETTINGS_SETTINGS",
                    nitems);

    /* ok, propagate xsettings to the toolkit for processing */
    if ((*env)->EnsureLocalCapacity(env, 1) < 0) {
        DTRACE_PRINTLN("XS:   EnsureLocalCapacity failed");
        XFree(xsettings);
        return;
    }

    array = (*env)->NewByteArray(env, (jint)nitems);
    if (JNU_IsNull(env, array)) {
        DTRACE_PRINTLN("awt_xsettings_update: NewByteArray failed");
        XFree(xsettings);
        return;
    }

    (*env)->SetByteArrayRegion(env, array, 0, (jint)nitems,
                               (jbyte *)xsettings);
    XFree(xsettings);

    (*env)->CallVoidMethod(env, mtoolkit, upcall, (jint)scr, array);
    (*env)->DeleteLocalRef(env, array);
}


/*
 * Event handler for events on XSETTINGS selection owner.
 * We are interested in PropertyNotify only.
 */
static void
awt_xsettings_callback(int scr, XEvent *xev, void *cookie)
{
    Display *dpy = awt_display; /* xev->xany.display */
    XPropertyEvent *ev;

    if (xev->type != PropertyNotify) {
        DTRACE_PRINTLN2("XS: awt_xsettings_callback(%d) event %d ignored",
                        scr, xev->type);
        return;
    }

    ev = &xev->xproperty;

    if (ev->atom == None) {
        DTRACE_PRINTLN("XS: awt_xsettings_callback(%d) atom == None");
        return;
    }

#ifdef DEBUG
    {
        char *name;

        DTRACE_PRINT2("XS: awt_xsettings_callback(%d) 0x%08lx ",
                      scr, ev->window);
        name = XGetAtomName(dpy, ev->atom);
        if (name == NULL) {
            DTRACE_PRINT1("atom #%d", ev->atom);
        } else {
            DTRACE_PRINT1("%s", name);
            XFree(name);
        }
        DTRACE_PRINTLN1(" %s", ev->state == PropertyNewValue ?
                                        "changed" : "deleted");
    }
#endif

    if (ev->atom != _XA_XSETTINGS_SETTINGS) {
        DTRACE_PRINTLN("XS:   property != _XSETTINGS_SETTINGS ...  ignoring");
        return;
    }


    if (ev->state == PropertyDelete) {
        /* XXX: notify toolkit to reset to "defaults"? */
        return;
    }

    awt_xsettings_update(scr, ev->window, cookie);
}


/*
 * Owner of XSETTINGS selection changed on the given screen.
 */
static void
awt_xsettings_owner_callback(int scr, Window owner, long *data_unused,
                             void *cookie)
{
    if (owner == None) {
        DTRACE_PRINTLN("XS: awt_xsettings_owner_callback: owner = None");
        /* XXX: reset to defaults??? */
        return;
    }

    DTRACE_PRINTLN1("XS: awt_xsettings_owner_callback: owner = 0x%08lx",
                    owner);

    awt_xsettings_update(scr, owner, cookie);
}

/*
 * Returns a reference to the class java.awt.Component.
 */
jclass
getComponentClass(JNIEnv *env)
{
    static jclass componentCls = NULL;

    // get global reference of java/awt/Component class (run only once)
    if (componentCls == NULL) {
        jclass componentClsLocal = (*env)->FindClass(env, "java/awt/Component");
        DASSERT(componentClsLocal != NULL);
        if (componentClsLocal == NULL) {
            /* exception already thrown */
            return NULL;
        }
        componentCls = (jclass)(*env)->NewGlobalRef(env, componentClsLocal);
        (*env)->DeleteLocalRef(env, componentClsLocal);
    }
    return componentCls;
}


/*
 * Returns a reference to the class java.awt.MenuComponent.
 */
jclass
getMenuComponentClass(JNIEnv *env)
{
    static jclass menuComponentCls = NULL;

    // get global reference of java/awt/MenuComponent class (run only once)
    if (menuComponentCls == NULL) {
        jclass menuComponentClsLocal = (*env)->FindClass(env, "java/awt/MenuComponent");
        DASSERT(menuComponentClsLocal != NULL);
        if (menuComponentClsLocal == NULL) {
            /* exception already thrown */
            return NULL;
        }
        menuComponentCls = (jclass)(*env)->NewGlobalRef(env, menuComponentClsLocal);
        (*env)->DeleteLocalRef(env, menuComponentClsLocal);
    }
    return menuComponentCls;
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    init
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL
Java_sun_awt_motif_MToolkit_init(JNIEnv *env, jobject this,
    jstring mainClassName)
{
    char *appName = NULL;
    char *mainChars = NULL;

    int32_t   argc     = 0;
    char *argv[10] = { NULL };

    /*
     * Note: The MToolkit object depends on the static initializer
     * of X11GraphicsEnvironment to initialize the connection to
     * the X11 server.
     */
    XFontStruct *xfont;
    XmFontListEntry tmpFontListEntry;
    char *multiclick_time_query;
    AwtGraphicsConfigDataPtr defaultConfig =
        getDefaultConfig(DefaultScreen(awt_display));
    AwtScreenDataPtr defaultScreen =
        getScreenData(DefaultScreen(awt_display));

    static String fallback_resources[] =
    {
        "*enableThinThickness:                   True",
        "*XmFileSelectionBox.fileFilterStyle:    XmFILTER_HIDDEN_FILES",
        "*XmFileSelectionBox.pathMode:           XmPATH_MODE_RELATIVE",
        "*XmFileSelectionBox.resizePolicy:       XmRESIZE_GROW",
        "*XmFileSelectionBox*dirTextLabelString:         Enter path or folder name:",
        "*XmFileSelectionBox*applyLabelString:           Update",
        "*XmFileSelectionBox*selectionLabelString:       Enter file name:",
        "*XmFileSelectionBox*dirListLabelString:         Folders",
        NULL                        /* Must be NULL terminated */
    };

    focusOutEvent.type = FocusOut;
    focusOutEvent.xfocus.send_event = True;
    focusOutEvent.xfocus.display = awt_display;
    focusOutEvent.xfocus.mode = NotifyNormal;
    focusOutEvent.xfocus.detail = NotifyNonlinear;

    /* Need to make sure this is deleted someplace! */
    AWT_LOCK();

    XSetIOErrorHandler(xIOError);

    if (!XSupportsLocale()) {
        jio_fprintf(stderr,
                    "current locale is not supported in X11, locale is set to C");
        setlocale(LC_ALL, "C");
    }
    if (!XSetLocaleModifiers("")) {
        jio_fprintf(stderr, "X locale modifiers are not supported, using default");
    }
#ifdef NETSCAPE
    if (awt_init_xt) {
        XtToolkitInitialize();
    }
#else
    XtToolkitInitialize();
#endif

    {
        jclass  fontConfigClass;
        jmethodID methID;
        jstring jFontList;
        char       *cFontRsrc;
        char       *cFontRsrc2;

        fontConfigClass = (*env)->FindClass(env, "sun/awt/motif/MFontConfiguration");
        methID = (*env)->GetStaticMethodID(env, fontConfigClass,
                                           "getDefaultMotifFontSet",
                                           "()Ljava/lang/String;");
        jFontList = (*env)->CallStaticObjectMethod(env, fontConfigClass, methID);
        if (jFontList == NULL) {
            motifFontList =
                "-monotype-arial-regular-r-normal--*-140-*-*-p-*-iso8859-1";
        } else {
            motifFontList = JNU_GetStringPlatformChars(env, jFontList, NULL);
        }

        /* fprintf(stderr, "motifFontList: %s\n", motifFontList); */

        cFontRsrc = malloc(strlen(motifFontList) + 20);
        strcpy(cFontRsrc, "*fontList: ");
        strcat(cFontRsrc, motifFontList);
        cFontRsrc2 = malloc(strlen(motifFontList) + 20);
        strcpy(cFontRsrc2, "*labelFontList: ");
        strcat(cFontRsrc2, motifFontList);

        argc = 1;
        argv[argc++] = "-xrm";
        argv[argc++] = cFontRsrc;
        argv[argc++] = "-xrm";
        argv[argc++] = cFontRsrc2;
        argv[argc++] = "-font";
        argv[argc++] = (char *)defaultMotifFont;
    }

    awt_appContext = XtCreateApplicationContext();
    XtAppSetErrorHandler(awt_appContext, (XtErrorHandler) xtError);
    XtAppSetFallbackResources(awt_appContext, fallback_resources);

    appName = NULL;
    mainChars = NULL;
    if (!JNU_IsNull(env, mainClassName)) {
        mainChars = (char *)JNU_GetStringPlatformChars(env, mainClassName, NULL);
        appName = mainChars;
    }
    if (appName == NULL || appName[0] == '\0') {
        appName = "AWT";
    }

    XtDisplayInitialize(awt_appContext, awt_display,
                        appName, /* application name  */
                        appName, /* application class */
                        NULL, 0, &argc, argv);

    /* Root shell widget that serves as a parent for all AWT top-levels.    */
    awt_root_shell = XtVaAppCreateShell(appName, /* application name  */
                                        appName, /* application class */
                                        applicationShellWidgetClass,
                                        awt_display,
                                        /* va_list */
                                        XmNmappedWhenManaged, False,
                                        NULL);
    XtRealizeWidget(awt_root_shell);

    if (mainChars != NULL) {
        JNU_ReleaseStringPlatformChars(env, mainClassName, mainChars);
    }

    awt_mgrsel_init();
    awt_wm_init();
    init_xembed();

    /*
     * Find the correct awt_multiclick_time to use. We normally
     * would call XtMultiClickTime() and wouldn't have to do
     * anything special, but because OpenWindows defines its own
     * version (OpenWindows.MultiClickTimeout), we need to
     * determine out which resource to use.
     *
     * We do this by searching in order for:
     *
     *   1) an explicit definition of multiClickTime
     *      (this is the resource that XtGetMultiClickTime uses)
     *
     * if that fails, search for:
     *
     *   2) an explicit definition of Openwindows.MultiClickTimeout
     *
     * if both searches fail:
     *
     *   3) use the fallback provided by XtGetMultiClickTime()
     *      (which is 200 milliseconds... I looked at the source :-)
     *
     */
    multiclick_time_query = XGetDefault(awt_display, "*", "multiClickTime");
    if (multiclick_time_query) {
        awt_multiclick_time = XtGetMultiClickTime(awt_display);
    } else {
        multiclick_time_query = XGetDefault(awt_display,
                                            "OpenWindows", "MultiClickTimeout");
        if (multiclick_time_query) {
            /* Note: OpenWindows.MultiClickTimeout is in tenths of
               a second, so we need to multiply by 100 to convert to
               milliseconds */
            awt_multiclick_time = atoi(multiclick_time_query) * 100;
        } else {
            awt_multiclick_time = XtGetMultiClickTime(awt_display);
        }
    }

    /*
    scrollBugWorkAround =
        (strcmp(XServerVendor(awt_display), "Sun Microsystems, Inc.") == 0
         && XVendorRelease(awt_display) == 3400);
    */
    scrollBugWorkAround = TRUE;

    /*
     * Create the cursor for TextArea scrollbars...
     */
    awt_scrollCursor = XCreateFontCursor(awt_display, XC_left_ptr);

    awt_defaultBg = defaultConfig->AwtColorMatch(200, 200, 200, defaultConfig);
    awt_defaultFg = defaultScreen->blackpixel;
    setup_modifier_map(awt_display);

    awt_initialize_DataTransferer();
    awt_initialize_Xm_DnD(awt_display);

    /*
     * fix for bug #4088106 - ugly text boxes and grayed out looking text
     */
    oldColorProc = XmGetColorCalculation();
    XmSetColorCalculation(ColorProc);

    AWT_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    run
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MToolkit_run
  (JNIEnv *env, jobject this)
{
    /*
     * in performSelect(), we multiply the timeout by 1000. Make sure
     * that the maximum value will not cause an overflow.
     */
    DASSERT(AWT_MAX_POLL_TIMEOUT <= (ULONG_MAX/1000));

    awt_MainThread = (*env)->NewGlobalRef(env, awtJNI_GetCurrentThread(env));
    awt_MToolkit_loop(env); /* never returns */
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    makeColorModel
 * Signature: ()Ljava/awt/image/ColorModel;
 */
JNIEXPORT jobject JNICALL Java_sun_awt_motif_MToolkit_makeColorModel
  (JNIEnv *env, jclass this)
{
    AwtGraphicsConfigDataPtr defaultConfig =
        getDefaultConfig(DefaultScreen(awt_display));

    return awtJNI_GetColorModel(env, defaultConfig);
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    getScreenResolution
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MToolkit_getScreenResolution
  (JNIEnv *env, jobject this)
{
    return (jint) ((DisplayWidth(awt_display, DefaultScreen(awt_display))
                    * 25.4) /
                   DisplayWidthMM(awt_display, DefaultScreen(awt_display)));
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    getScreenWidth
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MToolkit_getScreenWidth
  (JNIEnv *env, jobject this)
{
    return DisplayWidth(awt_display, DefaultScreen(awt_display));
}
/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    getScreenHeight
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MToolkit_getScreenHeight
  (JNIEnv *env, jobject this)
{
    return DisplayHeight(awt_display, DefaultScreen(awt_display));
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    beep
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MToolkit_beep
  (JNIEnv *env, jobject this)
{
    AWT_LOCK();
    XBell(awt_display, 0);
    AWT_FLUSH_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    shutdown
 * Signature: ()V
 */

JNIEXPORT void JNICALL Java_sun_awt_motif_MToolkit_shutdown
  (JNIEnv *env, jobject this)
{
    X11SD_LibDispose(env);
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    getLockingKeyStateNative
 * Signature: (I)B
 */
JNIEXPORT jboolean JNICALL Java_sun_awt_motif_MToolkit_getLockingKeyStateNative
  (JNIEnv *env, jobject this, jint awtKey)
{
    KeySym sym;
    KeyCode keyCode;
    uint32_t byteIndex;
    uint32_t bitIndex;
    char keyVector[32];

    AWT_LOCK();

    sym = awt_getX11KeySym(awtKey);
    keyCode = XKeysymToKeycode(awt_display, sym);
    if (sym == NoSymbol || keyCode == 0) {
        JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "Keyboard doesn't have requested key");
        AWT_UNLOCK();
        return False;
    }

    byteIndex = (keyCode/8);
    bitIndex = keyCode & 7;
    XQueryKeymap(awt_display, keyVector);

    AWT_UNLOCK();

    return (1 & (keyVector[byteIndex] >> bitIndex));
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    loadSystemColors
 * Signature: ([I)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MToolkit_loadSystemColors
  (JNIEnv *env, jobject this, jintArray systemColors)
{
    Widget frame, panel, control, menu, text, scrollbar;
    Colormap cmap;
    Pixel bg, fg, highlight, shadow;
    Pixel pixels[java_awt_SystemColor_NUM_COLORS];
    XColor *colorsPtr;
    jint rgbColors[java_awt_SystemColor_NUM_COLORS];
    int32_t count = 0;
    int32_t i, j;
    Arg args[10];
    int32_t argc;
    AwtGraphicsConfigDataPtr defaultConfig =
        getDefaultConfig(DefaultScreen(awt_display));

    AWT_LOCK();

    /*
     * initialize array of pixels
     */
    for (i = 0; i < java_awt_SystemColor_NUM_COLORS; i++) {
        pixels[i] = -1;
    }

    /*
     * Create phantom widgets in order to determine the default
     * colors;  this is somewhat inelegant, however it is the simplest
     * and most reliable way to determine the system's default colors
     * for objects.
     */
    argc = 0;
    XtSetArg(args[argc], XmNbuttonFontList,  getMotifFontList()); argc++;
    XtSetArg(args[argc], XmNlabelFontList,   getMotifFontList()); argc++;
    XtSetArg(args[argc], XmNtextFontList,    getMotifFontList()); argc++;
    frame = XtAppCreateShell("AWTColors", "XApplication",
                             vendorShellWidgetClass,
                             awt_display,
                             args, argc);
    /*
      XtSetMappedWhenManaged(frame, False);
      XtRealizeWidget(frame);
    */
    panel = XmCreateDrawingArea(frame, "awtPanelColor", NULL, 0);
    argc = 0;
    XtSetArg(args[argc], XmNfontList,        getMotifFontList()); argc++;
    control = XmCreatePushButton(panel, "awtControlColor", args, argc);
    argc = 0;
    XtSetArg(args[argc], XmNlabelFontList,   getMotifFontList()); argc++;
    XtSetArg(args[argc], XmNbuttonFontList,  getMotifFontList()); argc++;
    menu = XmCreatePulldownMenu(control, "awtColorMenu", args, argc);
    argc = 0;
    XtSetArg(args[argc], XmNfontList,        getMotifFontList()); argc++;
    text = XmCreateText(panel, "awtTextColor", args, argc);
    scrollbar = XmCreateScrollBar(panel, "awtScrollbarColor", NULL, 0);

    XtVaGetValues(panel,
                  XmNbackground, &bg,
                  XmNforeground, &fg,
                  XmNcolormap, &cmap,
                  NULL);

    pixels[java_awt_SystemColor_WINDOW] = bg;
    count++;
    pixels[java_awt_SystemColor_INFO] = bg;
    count++;
    pixels[java_awt_SystemColor_WINDOW_TEXT] = fg;
    count++;
    pixels[java_awt_SystemColor_INFO_TEXT] = fg;
    count++;

    XtVaGetValues(menu,
                  XmNbackground, &bg,
                  XmNforeground, &fg,
                  NULL);

    pixels[java_awt_SystemColor_MENU] = bg;
    count++;
    pixels[java_awt_SystemColor_MENU_TEXT] = fg;
    count++;

    XtVaGetValues(text,
                  XmNbackground, &bg,
                  XmNforeground, &fg,
                  NULL);

    pixels[java_awt_SystemColor_TEXT] = bg;
    count++;
    pixels[java_awt_SystemColor_TEXT_TEXT] = fg;
    count++;
    pixels[java_awt_SystemColor_TEXT_HIGHLIGHT] = fg;
    count++;
    pixels[java_awt_SystemColor_TEXT_HIGHLIGHT_TEXT] = bg;
    count++;

    XtVaGetValues(control,
                  XmNbackground, &bg,
                  XmNforeground, &fg,
                  XmNtopShadowColor, &highlight,
                  XmNbottomShadowColor, &shadow,
                  NULL);

    pixels[java_awt_SystemColor_CONTROL] = bg;
    count++;
    pixels[java_awt_SystemColor_CONTROL_TEXT] = fg;
    count++;
    pixels[java_awt_SystemColor_CONTROL_HIGHLIGHT] = highlight;
    count++;
    pixels[java_awt_SystemColor_CONTROL_LT_HIGHLIGHT] = highlight;
    count++;
    pixels[java_awt_SystemColor_CONTROL_SHADOW] = shadow;
    count++;
    pixels[java_awt_SystemColor_CONTROL_DK_SHADOW] = shadow;
    count++;

    XtVaGetValues(scrollbar,
                  XmNbackground, &bg,
                  NULL);
    pixels[java_awt_SystemColor_SCROLLBAR] = bg;
    count++;

    /*
     * Convert pixel values to RGB
     */
    colorsPtr = (XColor *) malloc(count * sizeof(XColor));
    j = 0;
    for (i = 0; i < java_awt_SystemColor_NUM_COLORS; i++) {
        if (pixels[i] != -1) {
            colorsPtr[j++].pixel = pixels[i];
        }
    }
    XQueryColors(awt_display, cmap, colorsPtr, count);

    /* Get current System Colors */

    (*env)->GetIntArrayRegion (env, systemColors, 0,
                              java_awt_SystemColor_NUM_COLORS,
                              rgbColors);

    /*
     * Fill systemColor array with new rgb values
     */

    j = 0;
    for (i = 0; i < java_awt_SystemColor_NUM_COLORS; i++) {
        if (pixels[i] != -1) {
            uint32_t rgb = colorToRGB(&colorsPtr[j++]);

            /*
              printf("SystemColor[%d] = %x\n", i, rgb);
            */
            rgbColors[i] = (rgb | 0xFF000000);
        }
    }

    (*env)->SetIntArrayRegion(env,
                              systemColors,
                              0,
                              java_awt_SystemColor_NUM_COLORS,
                              rgbColors);

    /* Duplicate system colors. If color allocation is unsuccessful,
       system colors will be approximated with matched colors */
    if (defaultConfig->awt_depth == 8)
        awt_allocate_systemcolors(colorsPtr, count, defaultConfig);

    /*
     * Cleanup
     */
    XtDestroyWidget(frame);
    free(colorsPtr);

    AWT_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    isDynamicLayoutSupportedNative
 * Signature: ()Z
 *
 * Note: there doesn't seem to be a protocol for querying the WM
 * about its opaque resize settings, so this function just returns
 * whether there is a solid resize option available for that WM.
 */
JNIEXPORT jboolean JNICALL
Java_sun_awt_motif_MToolkit_isDynamicLayoutSupportedNative(JNIEnv *env, jobject this)
{
    enum wmgr_t wm;

    AWT_LOCK();
    wm = awt_wm_getRunningWM();
    AWT_UNLOCK();

    switch (wm) {
      case ENLIGHTEN_WM:
      case KDE2_WM:
      case SAWFISH_WM:
      case ICE_WM:
      case METACITY_WM:
        return JNI_TRUE;
      case OPENLOOK_WM:
      case MOTIF_WM:
      case CDE_WM:
        return JNI_FALSE;
      default:
        return JNI_FALSE;
    }
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    isFrameStateSupported
 * Signature: (I)Z
 */
JNIEXPORT jboolean JNICALL
Java_sun_awt_motif_MToolkit_isFrameStateSupported(JNIEnv *env, jobject this,
    jint state)
{
    if (state == java_awt_Frame_NORMAL || state == java_awt_Frame_ICONIFIED) {
        return JNI_TRUE;
    } else {
        return awt_wm_supportsExtendedState(state) ? JNI_TRUE : JNI_FALSE;
    }
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    getMulticlickTime
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MToolkit_getMulticlickTime
  (JNIEnv *env, jobject this)
{
    return awt_multiclick_time;
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    getNumMouseButtons
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MToolkit_getNumMouseButtons
  (JNIEnv *env, jobject this)
{
    jint res = 0;
    AWT_LOCK();
    res = XGetPointerMapping(awt_display, NULL, 0);
    AWT_UNLOCK();
    return res;
}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    loadXSettings
 * Signature: ()V
 */
JNIEXPORT void JNICALL
Java_sun_awt_motif_MToolkit_loadXSettings(JNIEnv *env, jobject this)
{
    static Boolean registered = False;

    jclass mtoolkitCLS;
    Display *dpy = awt_display;
    const Window *owners;
    int scr;

    AWT_LOCK();

    if (registered) {
        AWT_UNLOCK();
        return;
    }

    if (_XA_XSETTINGS_SETTINGS == None) {
        _XA_XSETTINGS_SETTINGS = XInternAtom(dpy, "_XSETTINGS_SETTINGS", False);
        if (_XA_XSETTINGS_SETTINGS == None) {
            JNU_ThrowNullPointerException(env,
                "unable to intern _XSETTINGS_SETTINGS");
            AWT_UNLOCK();
            return;
        }
    }

    mtoolkitCLS = (*env)->GetObjectClass(env, this);

    xsettings_callback_cookie.mtoolkit =
        (*env)->NewGlobalRef(env, this);
    xsettings_callback_cookie.upcallMID =
        (*env)->GetMethodID(env, mtoolkitCLS,
                            "parseXSettings", "(I[B)V");

    if (JNU_IsNull(env, xsettings_callback_cookie.upcallMID)) {
        JNU_ThrowNoSuchMethodException(env,
            "sun.awt.motif.MToolkit.parseXSettings");
        AWT_UNLOCK();
        return;
    }

    owners = awt_mgrsel_select("_XSETTINGS", PropertyChangeMask,
                               &xsettings_callback_cookie,
                               awt_xsettings_callback,
                               awt_xsettings_owner_callback);
    if (owners == NULL) {
        JNU_ThrowNullPointerException(env,
            "unable to regiser _XSETTINGS with mgrsel");
        AWT_UNLOCK();
        return;
    }

    registered = True;

    for (scr = 0; scr < ScreenCount(dpy); ++scr) {
        if (owners[scr] == None) {
            DTRACE_PRINTLN1("XS: MToolkit.loadXSettings: none on screen %d",
                            scr);
            continue;
        }

        awt_xsettings_update(scr, owners[scr], &xsettings_callback_cookie);
    }

    AWT_UNLOCK();
}

JNIEXPORT jboolean JNICALL
Java_sun_awt_motif_MToolkit_isAlwaysOnTopSupported(JNIEnv *env, jobject toolkit) {
    Boolean res;
    AWT_LOCK();
    res = awt_wm_supportsAlwaysOnTop();
    AWT_UNLOCK();
    return res;
}

/*
 * Returns true if the current thread is privileged. Currently,
 * only the main event loop thread is considered to be privileged.
 */
Boolean
awt_currentThreadIsPrivileged(JNIEnv *env) {
    return (*env)->IsSameObject(env,
                        awt_MainThread, awtJNI_GetCurrentThread(env));
}

JNIEXPORT jboolean JNICALL
Java_sun_awt_motif_MToolkit_isSyncUpdated(JNIEnv *env, jobject toolkit) {
    return syncUpdated;
}

JNIEXPORT jboolean JNICALL
Java_sun_awt_motif_MToolkit_isSyncFailed(JNIEnv *env, jobject toolkit) {
    return syncFailed;
}

JNIEXPORT void JNICALL
Java_sun_awt_motif_MToolkit_updateSyncSelection(JNIEnv *env, jobject toolkit) {

    // AWT_LOCK is held by calling function
    if (wm_selection == None) {
        wm_selection = XInternAtom(awt_display, "WM_S0", False);
    }
    if (version_atom == None) {
        version_atom = XInternAtom(awt_display, "VERSION", False);
    }
    if (oops_atom == None) {
        oops_atom = XInternAtom(awt_display, "OOPS", False);
    }
    syncUpdated = False;
    syncFailed = False;
    XConvertSelection(awt_display, wm_selection, version_atom, oops_atom, XtWindow(awt_root_shell), CurrentTime);
    XSync(awt_display, False);
    inSyncWait = True; // Protect from spurious events
    // Calling function will call AWT_LOCK_WAIT instead of AWT_UNLOCK
}

JNIEXPORT jint JNICALL
Java_sun_awt_motif_MToolkit_getEventNumber(JNIEnv *env, jobject toolkit) {
    // AWT_LOCK must be held by the calling method
    return eventNumber;
}

static void
syncWait_eventHandler(XEvent * event) {
    static jmethodID syncNotifyMID = NULL;
    if (event != NULL && event->xany.type == SelectionNotify &&
        event->xselection.requestor == XtWindow(awt_root_shell) &&
        event->xselection.property == oops_atom &&
        inSyncWait)
    {
        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
        syncUpdated = True;
        inSyncWait = False;
        AWT_NOTIFY_ALL();
    } else if (event != NULL && event->xany.type == SelectionNotify &&
               event->xselection.requestor == XtWindow(awt_root_shell) &&
               event->xselection.target == version_atom &&
               event->xselection.property == None &&
               XGetSelectionOwner(awt_display, wm_selection) == None &&
               event->xselection.selection == wm_selection)
    {
        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
        syncFailed = True;
        inSyncWait = False;
        AWT_NOTIFY_ALL();
    }
}

JNIEXPORT void JNICALL
Java_sun_awt_motif_MToolkit_nativeGrab(JNIEnv *env, jobject toolkit, jobject window) {
    struct FrameData    *wdata;
    static Cursor cursor = None;
    int grab_result;

    AWT_LOCK();

    wdata = (struct FrameData *)
        JNU_GetLongFieldAsPtr(env, window, mComponentPeerIDs.pData);

    if (wdata == NULL ||
        wdata->winData.comp.widget == NULL ||
        wdata->winData.shell == NULL)
    {
        AWT_UNLOCK();
        return;
    }
    if (None == cursor) {
        cursor = XCreateFontCursor(awt_display, XC_hand2);
    }
    grabbed_widget = wdata->winData.shell;
    grab_result = XGrabPointer(awt_display, XtWindow(wdata->winData.shell),
                               True, (ButtonPressMask | ButtonReleaseMask
                                      | EnterWindowMask | LeaveWindowMask | PointerMotionMask
                                      | ButtonMotionMask),
                               GrabModeAsync, GrabModeAsync, None,
                               cursor, CurrentTime);
    if (GrabSuccess != grab_result) {
        XUngrabPointer(awt_display, CurrentTime);
        AWT_UNLOCK();
        DTRACE_PRINTLN1("XGrabPointer() failed, result %d", grab_result);
        return;
    }
    grab_result = XGrabKeyboard(awt_display, XtWindow(wdata->winData.shell),
                                True,
                                GrabModeAsync, GrabModeAsync, CurrentTime);
    if (GrabSuccess != grab_result) {
        XUngrabKeyboard(awt_display, CurrentTime);
        XUngrabPointer(awt_display, CurrentTime);
        DTRACE_PRINTLN1("XGrabKeyboard() failed, result %d", grab_result);
    }
    AWT_UNLOCK();
}

JNIEXPORT void JNICALL
Java_sun_awt_motif_MToolkit_nativeUnGrab(JNIEnv *env, jobject toolkit, jobject window) {
    struct FrameData    *wdata;

    AWT_LOCK();

    wdata = (struct FrameData *)
        JNU_GetLongFieldAsPtr(env, window, mComponentPeerIDs.pData);

    if (wdata == NULL ||
        wdata->winData.comp.widget == NULL ||
        wdata->winData.shell == NULL)
    {
        AWT_UNLOCK();
        return;
    }

    XUngrabPointer(awt_display, CurrentTime);
    XUngrabKeyboard(awt_display, CurrentTime);
    grabbed_widget = NULL;
    AWT_FLUSHOUTPUT_NOW();

    AWT_UNLOCK();

}

/*
 * Class:     sun_awt_motif_MToolkit
 * Method:    getWMName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL
Java_sun_awt_motif_MToolkit_getWMName(JNIEnv *env, jclass this)
{
    enum wmgr_t wm;

    AWT_LOCK();
    wm = awt_wm_getRunningWM();
    AWT_UNLOCK();

    switch (wm) {
      case NO_WM:
          return (*env)->NewStringUTF(env, "NO_WM");
      case OTHER_WM:
          return (*env)->NewStringUTF(env, "OTHER_WM");
      case ENLIGHTEN_WM:
          return (*env)->NewStringUTF(env, "ENLIGHTEN_WM");
      case KDE2_WM:
          return (*env)->NewStringUTF(env, "KDE2_WM");
      case SAWFISH_WM:
          return (*env)->NewStringUTF(env, "SAWFISH_WM");
      case ICE_WM:
          return (*env)->NewStringUTF(env, "ICE_WM");
      case METACITY_WM:
          return (*env)->NewStringUTF(env, "METACITY_WM");
      case OPENLOOK_WM:
          return (*env)->NewStringUTF(env, "OPENLOOK_WM");
      case MOTIF_WM:
          return (*env)->NewStringUTF(env, "MOTIF_WM");
      case CDE_WM:
          return (*env)->NewStringUTF(env, "CDE_WM");
    }
    return (*env)->NewStringUTF(env, "UNDETERMINED_WM");
}


#endif /* !HEADLESS */
