/* $XConsortium: Event.c,v 1.137 92/11/19 17:24:47 converse Exp $ */

/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

#include "IntrinsicI.h"
#include "Shell.h"
#include "StringDefs.h"

#if __STDC__
#define Const const
#else
#define Const /**/
#endif

#define NonMaskableMask ((EventMask)0x80000000L)

/*
 * These are definitions to make the code that handles exposure compresssion
 * easier to read.
 *
 * COMP_EXPOSE      - The compression exposure field of "widget"
 * COMP_EXPOSE_TYPE - The type of compression (lower 4 bits of COMP_EXPOSE.
 * GRAPHICS_EXPOSE  - TRUE if the widget wants graphics expose events
 *                    dispatched.
 * NO_EXPOSE        - TRUE if the widget wants No expose events dispatched.
 */

#define COMP_EXPOSE   (widget->core.widget_class->core_class.compress_exposure)
#define COMP_EXPOSE_TYPE (COMP_EXPOSE & 0x0f)
#define GRAPHICS_EXPOSE  ((XtExposeGraphicsExpose & COMP_EXPOSE) || \
			  (XtExposeGraphicsExposeMerged & COMP_EXPOSE))
#define NO_EXPOSE        (XtExposeNoExpose & COMP_EXPOSE)

EventMask XtBuildEventMask(widget)
    Widget widget;
{
    XtEventTable ev;
    EventMask	mask = 0L;

    for (ev = widget->core.event_table; ev != NULL; ev = ev->next)
	if (ev->select) mask |= ev->mask;
    if (widget->core.widget_class->core_class.expose != NULL)
	mask |= ExposureMask;
    if (widget->core.widget_class->core_class.visible_interest) 
	mask |= VisibilityChangeMask;
    if (widget->core.tm.translations)
	mask |= widget->core.tm.translations->eventMask;

    return mask & ~NonMaskableMask;
}

static void
RemoveEventHandler(widget, eventMask, other, proc, closure, raw)
Widget	        widget;
EventMask       eventMask;
Boolean	        other;
XtEventHandler  proc;
XtPointer	closure;
Boolean	        raw;
{
    XtEventRec *p, **pp;
    EventMask oldMask = XtBuildEventMask(widget);

    if (raw) raw = 1;
    pp = &widget->core.event_table;
    while ((p = *pp) &&
	   (p->proc != proc || p->closure != closure || p->select == raw))
	pp = &p->next;
    if (!p) return;

    /* un-register it */
    eventMask &= ~NonMaskableMask;
    if (other)
	eventMask |= NonMaskableMask;
    p->mask &= ~eventMask;

    if (!p->mask) {        /* delete it entirely */
        *pp = p->next;
	XtFree((char *)p);	
    }

    /* Reset select mask if realized and not raw. */
    if ( !raw && XtIsRealized(widget) && !widget->core.being_destroyed) {
	EventMask mask = XtBuildEventMask(widget);

	if (oldMask != mask) 
	    XSelectInput(XtDisplay(widget), XtWindow(widget), mask);
    }
}

/*	Function Name: AddEventHandler
 *	Description: An Internal routine that does the actual work of
 *                   adding the event handlers.
 *	Arguments: widget - widget to register an event handler for.
 *                 eventMask - events to mask for.
 *                 other - pass non maskable events to this proceedure.
 *                 proc - proceedure to register.
 *                 closure - data to pass to the event hander.
 *                 position - where to add this event handler.
 *                 force_new_position - If the element is already in the
 *                                      list, this will force it to the 
 *                                      beginning or end depending on position.
 *                 raw - If FALSE call XSelectInput for events in mask.
 *	Returns: none
 */

static void 
AddEventHandler(widget, eventMask, other, proc, 
		closure, position, force_new_position, raw)
Widget	        widget;
EventMask       eventMask;
Boolean         other, force_new_position, raw;
XtEventHandler  proc;
XtPointer	closure;
XtListPosition  position;
{
    register XtEventRec *p, **pp;
    EventMask oldMask;
    
    eventMask &= ~NonMaskableMask;
    if (other)
	eventMask |= NonMaskableMask;
    if (!eventMask) return;
    
    if (XtIsRealized(widget) && !raw) oldMask = XtBuildEventMask(widget);
    
    if (raw) raw = 1;
    pp = &widget->core.event_table;
    while ((p = *pp) &&
	   (p->proc != proc || p->closure != closure || p->select == raw))
	pp = &p->next;

    if (!p) {		                /* New proc to add to list */
	p = XtNew(XtEventRec);
	p->proc = proc;
	p->closure = closure;
	p->mask = eventMask;
	p->select = ! raw;
	
	if (position == XtListHead) {
	    p->next = widget->core.event_table;
	    widget->core.event_table = p;
	} else {
	    *pp = p;
	    p->next = NULL;
	}
    } 
    else {
	if (force_new_position) {
	    *pp = p->next;

	    if (position == XtListHead) {
		p->next = widget->core.event_table;
		widget->core.event_table = p;
	    } else {
	       	/*
		 * Find the last element in the list.
		 */
		while (*pp)
		    pp = &(*pp)->next;
		*pp = p;
		p->next = NULL;
	    }
	}

	/* update existing proc */
	p->mask |= eventMask;
    }

    if (XtIsRealized(widget) && !raw) {
	EventMask mask = XtBuildEventMask(widget);

	if (oldMask != mask) 
	    XSelectInput(XtDisplay(widget), XtWindow(widget), mask);
    }

}

#if NeedFunctionPrototypes
void XtRemoveEventHandler(
    Widget	    widget,
    EventMask       eventMask,
    _XtBoolean	    other,
    XtEventHandler  proc,
    XtPointer	    closure
    )
#else
void XtRemoveEventHandler(widget, eventMask, other, proc, closure)
    Widget	    widget;
    EventMask       eventMask;
    Boolean	    other;
    XtEventHandler  proc;
    XtPointer	    closure;
#endif
{
    RemoveEventHandler(widget, eventMask, other, proc, closure, FALSE);
}

#if NeedFunctionPrototypes
void XtAddEventHandler(
    Widget	    widget,
    EventMask       eventMask,
    _XtBoolean      other,
    XtEventHandler  proc,
    XtPointer	    closure
    )
#else
void XtAddEventHandler(widget, eventMask, other, proc, closure)
    Widget	    widget;
    EventMask       eventMask;
    Boolean         other;
    XtEventHandler  proc;
    XtPointer	    closure;
#endif
{
    AddEventHandler(widget, eventMask, other, 
		    proc, closure, XtListTail, FALSE, FALSE);
}

#if NeedFunctionPrototypes
void XtInsertEventHandler(
    Widget	    widget,
    EventMask       eventMask,
    _XtBoolean      other,
    XtEventHandler  proc,
    XtPointer	    closure,
    XtListPosition  position
    )
#else
void XtInsertEventHandler(widget, eventMask, other, proc, closure, position)
    Widget	    widget;
    EventMask       eventMask;
    Boolean         other;
    XtEventHandler  proc;
    XtPointer	    closure;
    XtListPosition  position;
#endif
{
    AddEventHandler(widget, eventMask, other, 
		    proc, closure, position, TRUE, FALSE);
}

#if NeedFunctionPrototypes
void XtRemoveRawEventHandler(
    Widget	    widget,
    EventMask       eventMask,
    _XtBoolean	    other,
    XtEventHandler  proc,
    XtPointer	    closure
    )
#else
void XtRemoveRawEventHandler(widget, eventMask, other, proc, closure)
    Widget	    widget;
    EventMask       eventMask;
    Boolean	    other;
    XtEventHandler  proc;
    XtPointer	    closure;
#endif
{
    RemoveEventHandler(widget, eventMask, other, proc, closure, TRUE);
}

#if NeedFunctionPrototypes
void XtInsertRawEventHandler(
    Widget	    widget,
    EventMask       eventMask,
    _XtBoolean	    other,
    XtEventHandler  proc,
    XtPointer	    closure,
    XtListPosition  position
    )
#else
void XtInsertRawEventHandler(widget, eventMask, other, proc, closure, position)
    Widget	    widget;
    EventMask       eventMask;
    Boolean	    other;
    XtEventHandler  proc;
    XtPointer	    closure;
    XtListPosition  position;
#endif
{
    AddEventHandler(widget, eventMask, other, 
		    proc, closure, position, TRUE, TRUE);
}

#if NeedFunctionPrototypes
void XtAddRawEventHandler(
    Widget	    widget,
    EventMask       eventMask,
    _XtBoolean      other,
    XtEventHandler  proc,
    XtPointer	    closure
    )
#else
void XtAddRawEventHandler(widget, eventMask, other, proc, closure)
    Widget	    widget;
    EventMask       eventMask;
    Boolean         other;
    XtEventHandler  proc;
    XtPointer	    closure;
#endif
{
    AddEventHandler(widget, eventMask, other, 
		    proc, closure, XtListTail, FALSE, TRUE);
}

typedef struct _WWPair {
    struct _WWPair *next;
    Window window;
    Widget widget;
} *WWPair;

typedef struct _WWTable {
    unsigned int mask;		/* size of hash table - 1 */
    unsigned int rehash;	/* mask - 2 */
    unsigned int occupied;	/* number of occupied entries */
    unsigned int fakes;		/* number occupied by WWfake */
    Widget *entries;		/* the entries */
    WWPair pairs;		/* bogus entries */
} *WWTable;

static Const WidgetRec WWfake;	/* placeholder for deletions */

#define WWHASH(tab,win) ((win) & tab->mask)
#define WWREHASHVAL(tab,win) ((((win) % tab->rehash) + 2) | 1)
#define WWREHASH(tab,idx,rehash) ((idx + rehash) & tab->mask)
#define WWTABLE(display) (_XtGetPerDisplay(display)->WWtable)

static void ExpandWWTable();

void _XtRegisterWindow(window, widget)
    register Window window;
    register Widget widget;
{
    register WWTable tab;
    register int idx, rehash;
    register Widget entry;

    tab = WWTABLE(XtDisplay(widget));
    if (window != XtWindow(widget)) {
	WWPair pair;
	pair = XtNew(struct _WWPair);
	pair->next = tab->pairs;
	pair->window = window;
	pair->widget = widget;
	tab->pairs = pair;
	return;
    }
    if ((tab->occupied + (tab->occupied >> 2)) > tab->mask)
	ExpandWWTable(tab);

    idx = WWHASH(tab, window);
    if ((entry = tab->entries[idx]) && entry != &WWfake) {
	rehash = WWREHASHVAL(tab, window);
	do {
	    idx = WWREHASH(tab, idx, rehash);
	} while ((entry = tab->entries[idx]) && entry != &WWfake);
    }
    if (!entry)
	tab->occupied++;
    else if (entry == &WWfake)
	tab->fakes--;
    tab->entries[idx] = widget;
}

void _XtUnregisterWindow(window, widget)
    register Window window;
    register Widget widget;
{
    register WWTable tab;
    register int idx, rehash;
    register Widget entry;

    tab = WWTABLE(XtDisplay(widget));
    if (window != XtWindow(widget)) {
	WWPair *prev, pair;

	prev = &tab->pairs;
	while ((pair = *prev) && pair->window != window)
	    prev = &pair->next;
	if (pair) {
	    *prev = pair->next;
	    XtFree((char *)pair);
	}
	return;
    }
    idx = WWHASH(tab, window);
    if (entry = tab->entries[idx]) {
	if (entry != widget) {
	    rehash = WWREHASHVAL(tab, window);
	    do {
		idx = WWREHASH(tab, idx, rehash);
		if (!(entry = tab->entries[idx]))
		    return;
	    } while (entry != widget);
	}
	tab->entries[idx] = (Widget)&WWfake;
	tab->fakes++;
    }
}

static void ExpandWWTable(tab)
    register WWTable tab;
{
    unsigned int oldmask;
    register Widget *oldentries, *entries;
    register int oldidx, newidx, rehash;
    register Widget entry;

    oldmask = tab->mask;
    oldentries = tab->entries;
    tab->occupied -= tab->fakes;
    tab->fakes = 0;
    if ((tab->occupied + (tab->occupied >> 2)) > tab->mask) {
	tab->mask = (tab->mask << 1) + 1;
	tab->rehash = tab->mask - 2;
    }
    entries = tab->entries = (Widget *) XtCalloc(tab->mask+1, sizeof(Widget));
    for (oldidx = 0; oldidx <= oldmask; oldidx++) {
	if ((entry = oldentries[oldidx]) && entry != &WWfake) {
	    newidx = WWHASH(tab, XtWindow(entry));
	    if (entries[newidx]) {
		rehash = WWREHASHVAL(tab, XtWindow(entry));
		do {
		    newidx = WWREHASH(tab, newidx, rehash);
		} while (entries[newidx]);
	    }
	    entries[newidx] = entry;
	}
    }
    XtFree((char *)oldentries);
}

Widget XtWindowToWidget(display, window)
    register Display *display;
    register Window window;
{
    register WWTable tab;
    register int idx, rehash;
    register Widget entry;
    WWPair pair;

    if (!window) return NULL;
    tab = WWTABLE(display);
    idx = WWHASH(tab, window);
    if ((entry = tab->entries[idx]) && XtWindow(entry) != window) {
	rehash = WWREHASHVAL(tab, window);
	do {
	    idx = WWREHASH(tab, idx, rehash);
	} while ((entry = tab->entries[idx]) && XtWindow(entry) != window);
    }
    if (entry)
	return entry;
    for (pair = tab->pairs; pair; pair = pair->next) {
	if (pair->window == window)
	    return pair->widget;
    }
    return NULL;
}

void _XtAllocWWTable(pd)
    XtPerDisplay pd;
{
    register WWTable tab;

    tab = (WWTable) XtMalloc(sizeof(struct _WWTable));
    tab->mask = 0x7f;
    tab->rehash = tab->mask - 2;
    tab->entries = (Widget *) XtCalloc(tab->mask+1, sizeof(Widget));
    tab->occupied = 0;
    tab->fakes = 0;
    tab->pairs = NULL;
    pd->WWtable = tab;
}

void _XtFreeWWTable(pd)
    register XtPerDisplay pd;
{
    register WWPair pair, next;

    for (pair = pd->WWtable->pairs; pair; pair = next) {
	next = pair->next;
	XtFree((char *)pair);
    }
    XtFree((char *)pd->WWtable->entries);
    XtFree((char *)pd->WWtable);
}

#define EHMAXSIZE 25 /* do not make whopping big */

static Boolean CallEventHandlers(widget, event, mask)
    Widget     widget;
    XEvent    *event;
    EventMask  mask;
{
    register XtEventRec *p;   
    XtEventHandler *proc;
    XtPointer *closure;
    XtEventHandler procs[EHMAXSIZE];
    XtPointer closures[EHMAXSIZE];
    Boolean cont_to_disp = True;
    int i, numprocs;

    /* Have to copy the procs into an array, because one of them might
     * call XtRemoveEventHandler, which would break our linked list. */

    numprocs = 0;
    for (p=widget->core.event_table; p; p = p->next) {
	if (mask & p->mask)
	    numprocs++;
    }
    if (numprocs > EHMAXSIZE) {
	proc = (XtEventHandler *)XtMalloc(numprocs * (sizeof(XtEventHandler) +
						      sizeof(XtPointer)));
	closure = (XtPointer *)(proc + numprocs);
    } else {
	proc = procs;
	closure = closures;
    }
    numprocs = 0;
    for (p=widget->core.event_table; p; p = p->next) {
	if (mask & p->mask) {
	    proc[numprocs] = p->proc;
	    closure[numprocs] = p->closure;
	    numprocs++;
	}
    }
    for (i = 0; i < numprocs && cont_to_disp; i++)
	(*(proc[i]))(widget, closure[i], event, &cont_to_disp);
    if (numprocs > EHMAXSIZE)
	XtFree((char *)proc);
    return cont_to_disp;
}

static Region nullRegion;
static void CompressExposures();

/* keep this SMALL to avoid blowing stack cache! */
/* because some compilers allocate all local locals on procedure entry */
#define EHSIZE 4

#define XtDidNothing	False
#define XtDidDispatch	True
#define XtDidFilter	2

static Boolean DispatchEvent(event, widget, mask, pd)
    register XEvent    *event;
    Widget    widget;
    EventMask mask;
    XtPerDisplay pd;
{
    register XtEventRec *p;   
    XEvent nextEvent;
    Boolean was_dispatched = XtDidNothing;
    Boolean call_tm = XtDidNothing;
    Boolean cont_to_disp;

    if (XFilterEvent(event, XtWindow(widget)))
	return XtDidFilter;
	
    if ( (mask == ExposureMask) ||
	 ((event->type == NoExpose) && NO_EXPOSE) ||
	 ((event->type == GraphicsExpose) && GRAPHICS_EXPOSE) ) {

	if (widget->core.widget_class->core_class.expose != NULL ) {

	    /* We need to mask off the bits that could contain the information
	     * about whether or not we desire Graphics and NoExpose events.  */

	    if ( (COMP_EXPOSE_TYPE == XtExposeNoCompress) || 
		 (event->type == NoExpose) )

		(*widget->core.widget_class->core_class.expose)
		    (widget, event, (Region)NULL);
	    else {
		CompressExposures(event, widget, pd);
	    }
	    was_dispatched = XtDidDispatch;
	}
    }

    if (mask == EnterWindowMask &&
	    widget->core.widget_class->core_class.compress_enterleave) {
	if (XPending(event->xcrossing.display)) {
	    XPeekEvent(event->xcrossing.display, &nextEvent);
	    if (nextEvent.type == LeaveNotify &&
		  event->xcrossing.window == nextEvent.xcrossing.window &&
		(event->xcrossing.detail != NotifyInferior &&
		 nextEvent.xcrossing.detail != NotifyInferior ||
		 event->xcrossing.detail == NotifyInferior &&
		 nextEvent.xcrossing.detail == NotifyInferior)) {
		/* skip the enter/leave pair */
		XNextEvent(event->xcrossing.display, &nextEvent);
		return XtDidNothing;
	    }
	}
    }

    if (event->type == MotionNotify &&
	    widget->core.widget_class->core_class.compress_motion) {
	while (XPending(event->xmotion.display)) {
	    XPeekEvent(event->xmotion.display, &nextEvent);
	    if (nextEvent.type == MotionNotify &&
		    event->xmotion.window == nextEvent.xmotion.window &&
		    event->xmotion.subwindow == nextEvent.xmotion.subwindow) {
		/* replace the current event with the next one */
		XNextEvent(event->xmotion.display, event);
	    } else break;
	}
    }

    if ((mask == VisibilityChangeMask) &&
        XtClass(widget)->core_class.visible_interest) {
	    was_dispatched = XtDidDispatch;
	    /* our visibility just changed... */
	    switch (((XVisibilityEvent *)event)->state) {
		case VisibilityUnobscured:
		    widget->core.visible = TRUE;
		    break;

		case VisibilityPartiallyObscured:
		    /* what do we want to say here? */
		    /* well... some of us is visible */
		    widget->core.visible = TRUE;
		    break;

		case VisibilityFullyObscured:
		    widget->core.visible = FALSE;
		    /* do we want to mark our children obscured? */
		    break;
	    }
	}

    /* to maintain "copy" semantics we check TM now but call later */
    if (widget->core.tm.translations &&
	(mask & widget->core.tm.translations->eventMask))
	call_tm = XtDidDispatch;

    cont_to_disp = True;
    p=widget->core.event_table;
    if (p) {
	if (p->next) {
	    XtEventHandler proc[EHSIZE];
	    XtPointer closure[EHSIZE];
	    int numprocs = 0;

	    /* Have to copy the procs into an array, because one of them might
	     * call XtRemoveEventHandler, which would break our linked list. */

	    for (; p; p = p->next) {
		if (mask & p->mask) {
		    if (numprocs >= EHSIZE)
			break;
		    proc[numprocs] = p->proc;
		    closure[numprocs] = p->closure;
		    numprocs++;
		}
	    }
	    if (numprocs) {
		if (p) {
		    cont_to_disp = CallEventHandlers(widget, event, mask);
		} else {
		    int i;
		    for (i = 0; i < numprocs && cont_to_disp; i++)
			(*(proc[i]))(widget, closure[i], event, &cont_to_disp);
		}
		was_dispatched = XtDidDispatch;
	    }
	} else if (mask & p->mask) {
	    (*p->proc)(widget, p->closure, event, &cont_to_disp);
	    was_dispatched = XtDidDispatch;
	}
    }
    if (call_tm && cont_to_disp)
	_XtTranslateEvent(widget, event);
    return (was_dispatched|call_tm);
}

/*
 * This structure is passed into the check exposure proc.
 */

typedef struct _CheckExposeInfo {
    int type1, type2;		/* Types of events to check for. */
    Boolean maximal;		/* Ignore non-exposure events? */
    Boolean non_matching;	/* Was there an event that did not 
				   match eighter type? */
    Window window;		/* Window to match. */
} CheckExposeInfo;

#define GetCount(ev) ( ((ev)->type == GraphicsExpose) ? \
		       (ev)->xgraphicsexpose.count : (ev)->xexpose.count)

/*	Function Name: CompressExposures
 *	Description: Handles all exposure compression
 *	Arguments: event - the xevent that is to be dispatched
 *                 widget - the widget that this event occured in.
 *	Returns: none.
 *
 *      NOTE: Event must be of type Expose or GraphicsExpose.
 */

static void SendExposureEvent();
static Bool CheckExposureEvent();

static void
CompressExposures(event, widget, pd)
Widget widget;
XEvent * event;
XtPerDisplay pd;
{
    CheckExposeInfo info;
    int count;

    XtAddExposureToRegion(event, pd->region);

    if ( GetCount(event) != 0 )
	return;

    if ( (COMP_EXPOSE_TYPE == XtExposeCompressSeries) ||
	 (XEventsQueued(XtDisplay(widget), QueuedAfterReading) == 0) ) {
	SendExposureEvent(event, widget, pd);
	return;
    }

    if (COMP_EXPOSE & XtExposeGraphicsExposeMerged) {
	info.type1 = Expose;
	info.type2 = GraphicsExpose;
    }
    else {
	info.type1 = event->type;
	info.type2 = 0;
    }
    info.maximal =(COMP_EXPOSE_TYPE == XtExposeCompressMaximal) ? True : False;
    info.non_matching = FALSE;
    info.window = XtWindow(widget);

/*
 * We have to be very careful here not to hose down the processor
 * when blocking until count gets to zero.
 *
 * First, check to see if there are any events in the queue for this
 * widget, and of the correct type.
 *
 * Once we cannot find any more events, check to see that count is zero. 
 * If it is not then block until we get another exposure event.
 *
 * If we find no more events, and count on the last one we saw was zero we
 * we can be sure that all events have been processed.
 *
 * Unfortunately, we wind up having to look at the entire queue
 * event if we're not doing Maximal compression, due to the
 * semantics of XCheckIfEvent (we can't abort without re-ordering
 * the event queue as a side-effect).
 */

    count = 0;
    while (TRUE) {
	XEvent event_return;

	if (XCheckIfEvent(XtDisplay(widget), &event_return, 
			  CheckExposureEvent, (char *) &info)) {

	    count = GetCount(&event_return);
	    XtAddExposureToRegion(&event_return, pd->region);
	}
	else if (count != 0) {
	    XIfEvent(XtDisplay(widget), &event_return,
		     CheckExposureEvent, (char *) &info);
	    count = GetCount(&event_return);
	    XtAddExposureToRegion(&event_return, pd->region);
	}
	else /* count == 0 && XCheckIfEvent Failed. */
	    break;
    }

    SendExposureEvent(event, widget, pd);
}

/*	Function Name: SendExposureEvent
 *	Description: Sets the x, y, width, and height of the event
 *                   to be the clip box of Expose Region.
 *	Arguments: event  - the X Event to mangle; Expose or GraphicsExpose.
 *                 widget - the widget that this event occured in.
 *                 pd     - the per display information for this widget.
 *	Returns: none.
 */

static void
SendExposureEvent(event, widget, pd)
XEvent * event;
Widget widget;
XtPerDisplay pd;
{
    XtExposeProc expose = widget->core.widget_class->core_class.expose;
    XRectangle rect;

    XClipBox(pd->region, &rect);
    if (event->type == Expose) {
	event->xexpose.x = rect.x;
	event->xexpose.y = rect.y;
	event->xexpose.width = rect.width;
	event->xexpose.height = rect.height;
    }
    else {			/* Graphics Expose Event. */
	event->xgraphicsexpose.x = rect.x;
	event->xgraphicsexpose.y = rect.y;
	event->xgraphicsexpose.width = rect.width;
	event->xgraphicsexpose.height = rect.height;
    }
    (*expose)(widget, event, pd->region);
    (void) XIntersectRegion(nullRegion, pd->region, pd->region);
}

/*	Function Name: CheckExposureEvent
 *	Description: Checks the event cue for an expose event
 *	Arguments: display - the display connection.
 *                 event - the event to check.
 *                 arg - a pointer to the exposure info structure.
 *	Returns: TRUE if we found an event of the correct type
 *               with the right window.
 *
 * NOTE: The only valid types (info.type1 and info.type2) are Expose
 *       and GraphicsExpose.
 */

/* ARGSUSED */
static Bool
CheckExposureEvent(disp, event, arg)
Display * disp;
XEvent * event;
char * arg;
{
    CheckExposeInfo * info = ((CheckExposeInfo *) arg);

    if ( (info->type1 == event->type) || (info->type2 == event->type)) {
	if (!info->maximal && info->non_matching) return FALSE;
	if (event->type == GraphicsExpose)
	    return(event->xgraphicsexpose.drawable == info->window);
	return(event->xexpose.window == info->window);
    }
    info->non_matching = TRUE;
    return(FALSE);
}

static EventMask Const masks[] = {
	0,			    /* Error, should never see  */
	0,			    /* Reply, should never see  */
	KeyPressMask,		    /* KeyPress			*/
	KeyReleaseMask,		    /* KeyRelease		*/
	ButtonPressMask,	    /* ButtonPress		*/
	ButtonReleaseMask,	    /* ButtonRelease		*/
	PointerMotionMask	    /* MotionNotify		*/
		| ButtonMotionMask,
	EnterWindowMask,	    /* EnterNotify		*/
	LeaveWindowMask,	    /* LeaveNotify		*/
	FocusChangeMask,	    /* FocusIn			*/
	FocusChangeMask,	    /* FocusOut			*/
	KeymapStateMask,	    /* KeymapNotify		*/
	ExposureMask,		    /* Expose			*/
	NonMaskableMask,	    /* GraphicsExpose, in GC    */
	NonMaskableMask,	    /* NoExpose, in GC		*/
	VisibilityChangeMask,       /* VisibilityNotify		*/
	SubstructureNotifyMask,     /* CreateNotify		*/
	StructureNotifyMask	    /* DestroyNotify		*/
		| SubstructureNotifyMask,
	StructureNotifyMask	    /* UnmapNotify		*/
		| SubstructureNotifyMask,
	StructureNotifyMask	    /* MapNotify		*/
		| SubstructureNotifyMask,
	SubstructureRedirectMask,   /* MapRequest		*/
	StructureNotifyMask	    /* ReparentNotify		*/
		| SubstructureNotifyMask,
	StructureNotifyMask	    /* ConfigureNotify		*/
		| SubstructureNotifyMask,
	SubstructureRedirectMask,   /* ConfigureRequest		*/
	StructureNotifyMask	    /* GravityNotify		*/
		| SubstructureNotifyMask,
	ResizeRedirectMask,	    /* ResizeRequest		*/
	StructureNotifyMask	    /* CirculateNotify		*/
		| SubstructureNotifyMask,
	SubstructureRedirectMask,   /* CirculateRequest		*/
	PropertyChangeMask,	    /* PropertyNotify		*/
	NonMaskableMask,	    /* SelectionClear		*/
	NonMaskableMask,	    /* SelectionRequest		*/
	NonMaskableMask,	    /* SelectionNotify		*/
	ColormapChangeMask,	    /* ColormapNotify		*/
	NonMaskableMask,	    /* ClientMessage		*/
	NonMaskableMask		    /* MappingNotify		*/
};

EventMask _XtConvertTypeToMask (eventType)
    int		eventType;
{
    eventType &= 0x7f;	/* Events sent with XSendEvent have high bit set. */
    if (eventType < XtNumber(masks))
	return masks[eventType];
    else
	return 0;
}

Boolean _XtOnGrabList(widget, grabList)
    register Widget widget;
    XtGrabRec  *grabList;
{
    register XtGrabRec* gl;
    for (; widget != NULL; widget = (Widget)widget->core.parent) {
	for (gl = grabList; gl != NULL; gl = gl->next) {
	    if (gl->widget == widget) return TRUE;
	    if (gl->exclusive) break;
	}
    }
    return FALSE;
}

static Widget LookupSpringLoaded(grabList)
    XtGrabList	grabList;
{
    XtGrabList	gl;

    for (gl = grabList; gl != NULL; gl = gl->next) {
	if (gl->spring_loaded)
	  if (XtIsSensitive(gl->widget))
	    return gl->widget;
	  else
	    return NULL;
	if (gl->exclusive) break;
    }
    return NULL;
}

typedef enum _GrabType {pass, ignore, remap} GrabType;

static Boolean DecideToDispatch(event)
    XEvent  *event;
{
    register    Widget widget;
    EventMask   mask;
    GrabType    grabType;
    Widget	dspWidget;
    Time	time = 0;
    XtPerDisplay pd;
    XtPerDisplayInput pdi;
    XtGrabList  grabList;

    widget = XtWindowToWidget (event->xany.display, event->xany.window);
    pd = _XtGetPerDisplay(event->xany.display);
    pdi = _XtGetPerDisplayInput(event->xany.display);
    grabList = *_XtGetGrabList(pdi);
    
    mask = _XtConvertTypeToMask(event->xany.type);

    grabType = pass;
    switch (event->xany.type & 0x7f) {

      case KeyPress:
      case KeyRelease:		grabType = remap; time = event->xkey.time;
				break;

      case ButtonPress:
      case ButtonRelease:	grabType = remap; time = event->xbutton.time;
				break;

      case MotionNotify:	grabType = ignore; time = event->xmotion.time;
#define XKnownButtons (Button1MotionMask|Button2MotionMask|Button3MotionMask|\
                       Button4MotionMask|Button5MotionMask)
	                        mask |= (event->xmotion.state & XKnownButtons);
#undef XKnownButtons
				break;

      case EnterNotify:
				grabType = ignore; /* fall through */
      case LeaveNotify:		time = event->xcrossing.time; break;

      case PropertyNotify:	time = event->xproperty.time; break;

      case SelectionClear:	time = event->xselectionclear.time; break;
    }

    if (time) pd->last_timestamp = time;

    if (widget == NULL) {
	if (grabType != remap)
	    return XFilterEvent(event, None);
	/* event occurred in a non-widget window, but we've promised also
	   to dispatch it to the nearest accessible spring_loaded widget */
	else if ((widget = LookupSpringLoaded(grabList)) != NULL)
	    return DispatchEvent(event, widget, mask, pd);
	return XFilterEvent(event, None);
    }

    switch(grabType) {
	case pass:
	    return DispatchEvent(event, widget, mask, pd);

	case ignore:
	    if ((grabList == NULL || _XtOnGrabList(widget,grabList))
		&& XtIsSensitive(widget)) {
		return DispatchEvent(event, widget, mask, pd);
	    }
	    return XtDidNothing;

	case remap:
	    
	    {
		Boolean was_dispatched = XtDidNothing;
		extern Widget _XtFindRemapWidget();
		extern void _XtUngrabBadGrabs();

		dspWidget = _XtFindRemapWidget(event, widget, mask, pdi);
		
		if ((grabList == NULL || 
		     _XtOnGrabList(dspWidget, grabList)) &&
		    XtIsSensitive(dspWidget)) {
		    was_dispatched = DispatchEvent(event, dspWidget,
						   mask, pd);
		    if (was_dispatched & XtDidFilter)
			return was_dispatched;
		}
		else _XtUngrabBadGrabs(event, widget, mask, pdi);
		
		/* Also dispatch to nearest accessible spring_loaded. */
		/* Fetch this afterward to reflect modal list changes */
		grabList = *_XtGetGrabList(pdi);
		widget = LookupSpringLoaded(grabList);
		if (widget != NULL && widget != dspWidget) {
		    was_dispatched |= DispatchEvent(event, widget,
						    mask, pd);
		}
		
		return was_dispatched;
	    }
    }
    /* should never reach here */
    return XtDidNothing;
}

Boolean XtDispatchEvent (event)
    XEvent  *event;
{
    Boolean was_dispatched;
    XtAppContext app = XtDisplayToApplicationContext(event->xany.display);
    int dispatch_level = ++app->dispatch_level;
    int starting_count = app->destroy_count;
    void _XtRefreshMapping();

    if (event->xany.type == MappingNotify)
	_XtRefreshMapping(event, True);

    /*
     * To make recursive XtDispatchEvent work, we need to do phase 2 destroys
     * only on those widgets destroyed by this particular dispatch.
     *
     */

    was_dispatched = DecideToDispatch(event);

    if (app->destroy_count > starting_count)
	_XtDoPhase2Destroy(app, dispatch_level);

    app->dispatch_level = dispatch_level - 1;

    if (_XtSafeToDestroy(app)) {
	if (_XtAppDestroyCount != 0) _XtDestroyAppContexts();
	if (_XtDpyDestroyCount != 0) _XtCloseDisplays();
    }
    
    return (was_dispatched != XtDidNothing);
}

/* ARGSUSED */
static void GrabDestroyCallback(widget, closure, call_data)
    Widget  widget;
    XtPointer closure;
    XtPointer call_data;
{
    /* Remove widget from grab list if it destroyed */
    XtRemoveGrab(widget);
}

static XtGrabRec *NewGrabRec(widget, exclusive, spring_loaded)
    Widget  widget;
    Boolean exclusive;
    Boolean spring_loaded;
{
    register XtGrabList    gl;

    gl		      = XtNew(XtGrabRec);
    gl->next	      = NULL;
    gl->widget        = widget;
    gl->exclusive     = exclusive;
    gl->spring_loaded = spring_loaded;

    return gl;
}

#if NeedFunctionPrototypes
void XtAddGrab(
    Widget  widget,
    _XtBoolean exclusive,
    _XtBoolean spring_loaded
    )
#else
void XtAddGrab(widget, exclusive, spring_loaded)
    Widget  widget;
    Boolean exclusive;
    Boolean spring_loaded;
#endif
{
    register    XtGrabList gl;
    XtGrabList	*grabListPtr;

    grabListPtr = 
      _XtGetGrabList(_XtGetPerDisplayInput(XtDisplay(widget)));
      
					 

    if (spring_loaded && !exclusive) {
	XtAppWarningMsg(XtWidgetToApplicationContext(widget),
		"grabError", "xtAddGrab", XtCXtToolkitError,
		"XtAddGrab requires exclusive grab if spring_loaded is TRUE",
		(String *) NULL, (Cardinal *) NULL);
	exclusive = TRUE;
    }

    gl = NewGrabRec(widget, exclusive, spring_loaded);
    gl->next = *grabListPtr;
    *grabListPtr = gl;

    XtAddCallback (widget, XtNdestroyCallback, 
	    GrabDestroyCallback, (XtPointer) NULL);
}

void XtRemoveGrab(widget)
    Widget  widget;
{
    register XtGrabList gl;
    register Boolean done;
    XtGrabList	*grabListPtr;

    grabListPtr = 
      _XtGetGrabList(_XtGetPerDisplayInput(XtDisplay(widget)));
      
    for (gl = *grabListPtr; gl != NULL; gl = gl->next) {
	if (gl->widget == widget) break;
    }

    if (gl == NULL) {
	    XtAppWarningMsg(XtWidgetToApplicationContext(widget),
		       "grabError","xtRemoveGrab",XtCXtToolkitError,
		       "XtRemoveGrab asked to remove a widget not on the list",
		       (String *)NULL, (Cardinal *)NULL);
	    return;
	}	

    do {
	gl = *grabListPtr;
	done = (gl->widget == widget);
	*grabListPtr = gl->next;
	XtRemoveCallback(gl->widget, XtNdestroyCallback,
		GrabDestroyCallback, (XtPointer)NULL);
	XtFree((char *)gl);
    } while (! done);
    return;
}

void XtMainLoop()
{
	XtAppMainLoop(_XtDefaultAppContext());
}

void XtAppMainLoop(app)
	XtAppContext app;
{
    XEvent event;

    for (;;) {
    	XtAppNextEvent(app, &event);
	XtDispatchEvent(&event);
    }
}


void _XtEventInitialize()
{
    static Boolean initialized = FALSE;
    if (initialized) return;
    initialized = TRUE;

    nullRegion = XCreateRegion();
}

void XtAddExposureToRegion(event, region)
    XEvent   *event;
    Region   region;
{
    XRectangle rect;

    switch (event->type) {
	case Expose:
		rect.x = event->xexpose.x;
		rect.y = event->xexpose.y;
		rect.width = event->xexpose.width;
		rect.height = event->xexpose.height;
		break;
	case GraphicsExpose:
		rect.x = event->xgraphicsexpose.x;
		rect.y = event->xgraphicsexpose.y;
		rect.width = event->xgraphicsexpose.width;
		rect.height = event->xgraphicsexpose.height;
		break;
	default:
		return;
    }

    XUnionRectWithRegion(&rect, region, region);
}


void _XtFreeEventTable(event_table)
    XtEventTable *event_table;
{
    register XtEventTable event;

    event = *event_table;
    while (event != NULL) {
	register XtEventTable next = event->next;
	XtFree((char *) event);
	event = next;
    }
}

Time XtLastTimestampProcessed(dpy)
    Display *dpy;
{
    return _XtGetPerDisplay(dpy)->last_timestamp;
}
      

void _XtSendFocusEvent(child, type)
    Widget child;
    int type;
{

    child = XtIsWidget(child) ? child : _XtWindowedAncestor(child);
    if (XtIsSensitive(child) && !child->core.being_destroyed
	&& XtIsRealized(child)
	&& (XtBuildEventMask(child) & FocusChangeMask))
    {
	XFocusChangeEvent event;

	event.type = type;
	event.serial = LastKnownRequestProcessed(XtDisplay(child));
	event.send_event = True;
	event.display = XtDisplay(child);
	event.window = XtWindow(child);
	event.mode = NotifyNormal;
	event.detail = NotifyAncestor;
	DispatchEvent((XEvent*)&event, child, _XtConvertTypeToMask(type),
		      _XtGetPerDisplay(XtDisplay(child)));
    }
}
