/************************************************************
Copyright 1987 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.

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


/* $Header: events.c,v 1.76 87/06/24 14:48:52 swick Exp $ */

#include "X.h"
#include "misc.h"
#include "resource.h"
#define NEED←EVENTS
#define NEED←REPLIES
#include "Xproto.h"
#include "windowstr.h"
#include "inputstr.h"
#include "scrnintstr.h"
#include "cursorstr.h"

#include "dixstruct.h"
#include "opaque.h"

extern WindowRec WindowTable[];
extern int swappedClients[];
extern void EnqueueEvent();
extern void NoticeTimeAndState();
extern Bool CheckDeviceGrabs();
extern WindowPtr CheckMotion();
extern void FixUpEventFromWindow();
extern void DeactivatePointerGrab();
extern WindowPtr RootForWindow();
extern void ActivatePointerGrab();
extern void ChangeToCursor();
extern void PostNewCursor();
extern void ActivateKeyboardGrab();
extern void DeactivateKeyboardGrab();
extern int DeliverEventsToWindow();

extern int (* EventSwapVector[128]) ();
extern void (* ReplySwapVector[256]) ();

#define NoSuchEvent 0x80000000	/* so doesn't match NoEventMask */
#define StructureAndSubMask ( StructureNotifyMask | SubstructureNotifyMask )
#define AllButtonsMask ( \
	Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask )
#define MotionMask ( \
	PointerMotionMask | PointerMotionHintMask | Button1MotionMask | \
	Button2MotionMask | Button3MotionMask | Button4MotionMask | \
	Button5MotionMask | ButtonMotionMask )
#define PropagateMask ( \
	KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | \
	MotionMask )
#define AllModifiersMask ( \
	ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | \
	Mod3Mask | Mod4Mask | Mod5Mask )
#define Motion←Filter(state) (PointerMotionMask | \
		(AllButtonsMask & state) | buttonMotionMask);


#define WID(w) ((w) ? ((w)->wid) : 0)

#define BitOn(ptr, bit) \
	((BYTE *) (ptr))[(bit)>>3] |= (1 << ((bit) & 7))
#define BitOff(ptr, bit) \
	((BYTE *) (ptr))[(bit)>>3] &= ~(1 << ((bit) & 7))
#define IsOn(ptr, bit) \
	(((BYTE *) (ptr))[(bit)>>3] & (1 << ((bit) & 7)))

extern debug←events;
extern InputInfo inputInfo;

extern KeySymsRec curKeySyms;

extern GrabRec keybdGrab;	/* used for active grabs */
extern GrabRec ptrGrab;

#define MAX←QUEUED←EVENTS 100
extern struct {
    unsigned int	num;
    QdEventRec		pending, free;	/* only forw, back used */
    DeviceIntPtr	replayDev;	/* kludgy rock to put flag for */
    WindowPtr		replayWin;	/*   ComputeFreezes            */
    Bool		playingEvents;
} syncEvents;

/*
 * The window trace information is used to avoid having to compute all the
 * windows between the root and the current pointer window each time a button
 * or key goes down. The grabs on each of those windows must be checked.
 */
extern WindowPtr *spriteTrace;
#define ROOT spriteTrace[0]
extern int spriteTraceSize;
extern int spriteTraceGood;

extern WindowPtr *focusTrace;
extern int focusTraceSize;
extern int focusTraceGood;

extern CARD16 keyButtonState;
extern int buttonsDown;		/* number of buttons currently down */
extern Mask buttonMotionMask;

typedef struct {
    int		x, y;
} HotSpot;

extern  struct {
    CursorPtr	current;
    BoxRec	hotLimits;	/* logical constraints */
    BoxRec	physLimits;	/* of hot spot due to hardware limits */
    WindowPtr	win;
    HotSpot	hot;
} sprite;			/* info about the cursor sprite */

extern Bool lastWasMotion;

extern void DoEnterLeaveEvents();	/* merely forward declarations */
extern WindowPtr XYToWindow();
extern Bool CheckKeyboardGrabs();
extern void NormalKeyboardEvent();
extern int DeliverDeviceEvents();

extern ScreenPtr currentScreen;

extern CARD16 stateMasks[MAP←LENGTH];	/* only for default keyboard ? XXX */

extern int lastEventMask;

#define CantBeFiltered NoEventMask
extern int filters[128];

void
ProcessPointerEvent (xE, mouse)
    register xEvent 		*xE;
    register DeviceIntPtr 	mouse;
{
    Mask    		filterToUse;
    register int    	key;
    register GrabPtr	grab = mouse->grab;
    WindowPtr		pWin;
    Bool		moveIt = FALSE;
    Bool                deactivateGrab = FALSE;

    if (xE->u.keyButtonPointer.rootX < sprite.physLimits.x1)
    {
	xE->u.keyButtonPointer.rootX = sprite.physLimits.x1;
	moveIt = TRUE;
    }
    else if (xE->u.keyButtonPointer.rootX >= sprite.physLimits.x2)
    {
	xE->u.keyButtonPointer.rootX = sprite.physLimits.x2 - 1;
	moveIt = TRUE;
    }
    if (xE->u.keyButtonPointer.rootY < sprite.physLimits.y1)
    {
	xE->u.keyButtonPointer.rootY = sprite.physLimits.y1;
	moveIt = TRUE;
    }
    else if (xE->u.keyButtonPointer.rootY >= sprite.physLimits.y2)
    {
	xE->u.keyButtonPointer.rootY = sprite.physLimits.y2 - 1;
	moveIt = TRUE;
    }
    if (moveIt)
	(*currentScreen->SetCursorPosition)(
	    currentScreen, xE->u.keyButtonPointer.rootX,
	    xE->u.keyButtonPointer.rootY);
    if (mouse->sync.frozen)
    {
	EnqueueEvent(mouse, xE);
	return;
    }
    NoticeTimeAndState(xE);
    key = xE->u.u.detail;
    pWin = sprite.win;
    switch (xE->u.u.type)
    {
	case ButtonPress: 
	    lastWasMotion = FALSE;
	    buttonsDown++;
	    filterToUse = filters[ButtonPress];
	    xE->u.u.detail = mouse->u.ptr.map[key];
	    if (xE->u.u.detail <= 5)
		keyButtonState |= stateMasks[xE->u.u.detail];
	    if (!grab)
		if (CheckDeviceGrabs(mouse, xE, 0, FALSE))
		    return;
	    break;
	case ButtonRelease: 
            lastWasMotion = FALSE;
	    buttonsDown--;
	    filterToUse = filters[ButtonRelease];
	    xE->u.u.detail = mouse->u.ptr.map[key];
	    if (xE->u.u.detail <= 5)
		keyButtonState &= ~stateMasks[xE->u.u.detail];
	    if ((!(keyButtonState & AllButtonsMask)) &&
		(mouse->u.ptr.autoReleaseGrab))
		deactivateGrab = TRUE;
	    break;
	case MotionNotify: 
            pWin = CheckMotion(
	        xE->u.keyButtonPointer.rootX, xE->u.keyButtonPointer.rootY, 
		FALSE);
            if (!pWin)
                return ;
	    filterToUse = Motion←Filter(keyButtonState);
	    break;
	default: 
	    FatalError("bogus pointer event from ddx");
    }
    buttonMotionMask = (buttonsDown) ? ButtonMotionMask : 0;
    if (grab)
    {
	Bool syncIt;
	if ((!grab->ownerEvents) ||
		(!(syncIt = DeliverDeviceEvents(pWin, xE, grab, NullWindow))))
	{
	    FixUpEventFromWindow(xE, grab->window, None, TRUE);
	    syncIt = TryClientEvents(
		grab->client, xE, 1, grab->eventMask, filterToUse, grab);
	}
	if (syncIt && (mouse->sync.state == FREEZE←NEXT←EVENT))
	{
	    mouse->sync.state = FROZEN←WITH←EVENT;
	    mouse->sync.frozen = TRUE;
	    mouse->sync.event = *xE;
	}
    }
    else
	DeliverDeviceEvents(pWin, xE, NullGrab, NullWindow);
    if (deactivateGrab)
        DeactivatePointerGrab(mouse);
}

void
ProcessOtherEvent (xE, pDevice)
    xEvent *xE;
    DevicePtr pDevice;
{
/*	XXX What should be done here ?
    Bool propogate = filters[xE->type];
*/
}

#define AtMostOneClient \
	(SubstructureRedirectMask | ResizeRedirectMask | ButtonPressMask)

void
RecalculateDeliverableEvents(pWin)
    WindowPtr pWin;
{
    OtherClients * others;
    WindowPtr child;
    Mask hintMask;

    pWin->allEventMasks = pWin->eventMask;
    hintMask = pWin->eventMask & PointerMotionHintMask;
    for (others = OTHERCLIENTS(pWin); others; others = others->next)
    {
	pWin->allEventMasks |= others->mask;
/*	hintMask &= (others->mask & PointerMotionHintMask); */
    }
/*
    if (!hintMask)
        pWin->allEventMasks &= ~PointerMotionHintMask;
*/
    if (pWin->parent)
	pWin->deliverableEvents = pWin->allEventMasks |
	    (pWin->parent->deliverableEvents & ~pWin->dontPropagateMask &
	     PropagateMask);
    else
	pWin->deliverableEvents = pWin->allEventMasks;
    for (child = pWin->firstChild; child; child = child->nextSib)
	RecalculateDeliverableEvents(child);
}

static int
OtherClientGone(pWin, id)
    WindowPtr pWin;
    long   id;
{
    register OtherClientsPtr *next;
    register OtherClientsPtr other;

    for (next = (OtherClientsPtr *)&(pWin->otherClients);
	 *next; next = &((*next)->next))
    {
	if ((other = *next)->resource == id)
	{
	    *next = other->next;
	    Xfree(other);
	    RecalculateDeliverableEvents(pWin);
	    return;
	}
    }
    FatalError("client not on event list");
}

int
PassiveClientGone(pWin, id)
    WindowPtr pWin;
    long   id;
{
    register GrabPtr *next;
    register GrabPtr grab;

    for (next = (GrabPtr *)&(pWin->passiveGrabs);
	 *next; next = &((*next)->next))
    {
	if ((grab = *next)->resource == id)
	{
	    *next = grab->next;
	    Xfree(grab);
	    return;
	}
    }
    FatalError("client not on passive grab list");
}

int
EventSelectForWindow(pWin, client, mask)
	WindowPtr pWin;
	ClientPtr client;
	Mask mask;
{
    Mask check;
    OtherClients * others;

    check = (mask & AtMostOneClient);
    if (check & pWin->allEventMasks)
    {				       /* It is illegal for two different
				          clients to select on any of the
				          events for AtMostOneClient. However,
				          it is OK, for some client to
				          continue selecting on one of those
				          events.  */
	if ((pWin->client != client) && (check & pWin->eventMask))
	    return BadAccess;
	for (others = OTHERCLIENTS(pWin); others; others = others->next)
	{
	    if ((others->client != client) && (check & others->mask))
		return BadAccess;
	}
    }
    if (pWin->client == client)
	pWin->eventMask = mask;
    else
    {
	for (others = OTHERCLIENTS(pWin); others; others = others->next)
	{
	    if (others->client == client)
	    {
		if (mask == 0)
		{
		    FreeResource(others->resource, RC←NONE);
		    return Success;
		}
		else
		    others->mask = mask;
		goto maskSet;
	    }
	}
	others = (OtherClients *) Xalloc(sizeof(OtherClients));
	others->client = client;
	others->mask = mask;
	others->resource = FakeClientID((int)client->index);
	others->next = OTHERCLIENTS(pWin);
	pWin->otherClients = (pointer)others;
	AddResource(others->resource, RT←FAKE, (pointer) pWin, OtherClientGone, RC←CORE);
    }
maskSet: 
    RecalculateDeliverableEvents(pWin);
    return Success;
}

int
EventSuppressForWindow(pWin, client, mask)
	WindowPtr pWin;
	ClientPtr client;
	Mask mask;
{
    pWin->dontPropagateMask = mask;
    RecalculateDeliverableEvents(pWin);
    return Success;
}


/* returns true if b is a descendent of a */
Bool
IsParent(a, b)
    register WindowPtr a, b;
{
    for (b = b->parent; b; b = b->parent)
	if (b == a) return TRUE;
    return FALSE;
}

static WindowPtr 
CommonAncestor(a, b)
    register WindowPtr a, b;
{
    for (b = b->parent; b; b = b->parent)
	if (IsParent(b, a)) return b;
    return NullWindow;
}

static void
EnterLeaveEvent(type, mode, detail, pWin)
    int type, mode, detail;
    WindowPtr pWin;
{
    xEvent		event;
    DeviceIntPtr	keybd = inputInfo.keyboard;
    WindowPtr		focus = keybd->u.keybd.focus.win;

    event.u.u.type = type;
    event.u.u.detail = detail;
    event.u.enterLeave.time = currentTime.milliseconds;
    event.u.enterLeave.rootX = sprite.hot.x;
    event.u.enterLeave.rootY = sprite.hot.y;
    FixUpEventFromWindow(&event, pWin, None, TRUE);		
 /* This call counts on same initial structure beween enter & button events */
    event.u.enterLeave.state = keyButtonState;
    event.u.enterLeave.mode = mode;
    event.u.enterLeave.flags = ELFlagSameScreen;	/* XXX */
    if ((focus != NoneWin) &&
	((pWin == focus) || (focus == PointerRootWin) ||
	 IsParent(focus, pWin)))
	event.u.enterLeave.flags |= ELFlagFocus;
    DeliverEventsToWindow(pWin, &event, 1, (Mask)filters[type], NullGrab);
    if (type == EnterNotify)
    {
	xKeymapEvent ke;
	ke.type = KeymapNotify;
	bcopy(&keybd->down[1], &ke.map[0], 31);
    }
}

static void
EnterNotifies(ancestor, child, mode, detail)
    WindowPtr ancestor, child;
    int mode, detail;
{
    if (!child || (ancestor == child))
	return;
    EnterNotifies(ancestor, child->parent, mode, detail);
    EnterLeaveEvent(EnterNotify, mode, detail, child);
}

/* dies horribly if ancestor is not an ancestor of child */
static void
LeaveNotifies(child, ancestor, mode, detail, doAncestor)
    WindowPtr child, ancestor;
    int detail, mode;
{
    register WindowPtr  pWin;

    if (ancestor == child)
	return;
    for (pWin = child->parent; pWin != ancestor; pWin = pWin->parent)
	EnterLeaveEvent(LeaveNotify, mode, detail, pWin);
    if (doAncestor)
	EnterLeaveEvent(LeaveNotify, mode, detail, ancestor);
}

void
DoEnterLeaveEvents(fromWin, toWin, mode)
    WindowPtr fromWin, toWin;
    int mode;
{
    if (fromWin == toWin)
	return;
    if (IsParent(fromWin, toWin))
    {
	EnterLeaveEvent(LeaveNotify, mode, NotifyInferior, fromWin);
	EnterNotifies(fromWin, toWin->parent, mode, NotifyVirtual);
	EnterLeaveEvent(EnterNotify, mode, NotifyAncestor, toWin);
    }
    else if (IsParent(toWin, fromWin))
    {
	EnterLeaveEvent(LeaveNotify, mode, NotifyAncestor, fromWin);
	LeaveNotifies(fromWin, toWin, mode, NotifyVirtual, FALSE);
	EnterLeaveEvent(EnterNotify, mode, NotifyInferior, toWin);
    }
    else
    { /* neither fromWin nor toWin is descendent of the other */
	WindowPtr common = CommonAncestor(toWin, fromWin);
	/* common == NullWindow ==> different screens */
	EnterLeaveEvent(LeaveNotify, mode, NotifyNonlinear, fromWin);
	if (common)
	{
	    LeaveNotifies(
		fromWin, common, mode, NotifyNonlinearVirtual, FALSE);
	    EnterNotifies(common, toWin->parent, mode, NotifyNonlinearVirtual);
	}
	else
	{
	    LeaveNotifies(
		fromWin, RootForWindow(fromWin), mode,
		NotifyNonlinearVirtual, TRUE);
	    EnterNotifies(
		RootForWindow(toWin), toWin->parent, mode, NotifyNonlinearVirtual);
	}
	EnterLeaveEvent(EnterNotify, mode, NotifyNonlinear, toWin);
    }
}

static void
FocusEvent(type, mode, detail, pWin)
    int type, mode, detail;
    WindowPtr pWin;
{
   xEvent	event;
   DeviceIntPtr	keybd = inputInfo.keyboard;

    event.u.focus.mode = mode;
    event.u.u.type = type;
    event.u.u.detail = detail;
    event.u.focus.window = pWin->wid;
    DeliverEventsToWindow(pWin, &event, 1, (Mask)filters[type], NullGrab);
    if (type == FocusIn)
    {
	xKeymapEvent ke;
	ke.type = KeymapNotify;
	bcopy(keybd->down, &ke.map[0], 31);
	DeliverEventsToWindow(pWin, &event, 1, KeymapStateMask, NullGrab);
    }
}

 /*
  * recursive because it is easier
  * no-op if child not descended from ancestor
  */
static Bool
FocusInEvents(ancestor, child, skipChild, mode, detail, doAncestor)
    WindowPtr ancestor, child, skipChild;
    int mode, detail;
    Bool doAncestor;
{
    if (child == NullWindow)
	return FALSE;
    if (ancestor == child)
    {
	if (doAncestor)
	    FocusEvent(FocusIn, mode, detail, child);
	return TRUE;
    }
    if (FocusInEvents(
	ancestor, child->parent, skipChild, mode, detail, doAncestor))
    {
	if (child != skipChild)
	    FocusEvent(FocusIn, mode, detail, child);
	return TRUE;
    }
    return FALSE;
}

/* dies horribly if ancestor is not an ancestor of child */
static void
FocusOutEvents(child, ancestor, mode, detail, doAncestor)
    WindowPtr child, ancestor;
    int detail;
    Bool doAncestor;
{
    register WindowPtr  pWin;

    for (pWin = child; pWin != ancestor; pWin = pWin->parent)
	FocusEvent(FocusOut, mode, detail, pWin);
    if (doAncestor)
	FocusEvent(FocusOut, mode, detail, ancestor);
}

void
DoFocusEvents(fromWin, toWin, mode)
    WindowPtr fromWin, toWin;
    int mode;
{
    int     out, in;		       /* for holding details for to/from
				          PointerRoot/None */

    out = (fromWin == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
    in = (toWin == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
 /* wrong values if neither, but then not referenced */

    if ((toWin == NullWindow) || (toWin == PointerRootWin))
    {
	if ((fromWin == NullWindow) || (fromWin == PointerRootWin))
	    FocusEvent(FocusOut, mode, out, ROOT);
	else
	{
	    if (IsParent(fromWin, sprite.win))
	      FocusOutEvents(sprite.win, fromWin, mode, NotifyPointer, FALSE);
	    FocusEvent(FocusOut, mode, NotifyNonlinear, fromWin);
	    FocusOutEvents(
	      fromWin->parent, ROOT, mode, NotifyNonlinearVirtual, TRUE);
	}
	FocusEvent(FocusIn, mode, in, ROOT);
    }
    else
    {
	if ((fromWin == NullWindow) || (fromWin == PointerRootWin))
	{
	    FocusEvent(FocusOut, mode, out, ROOT);
	    FocusInEvents(
		ROOT, toWin, toWin, mode, NotifyNonlinearVirtual, TRUE);
	    FocusEvent(FocusIn, mode, NotifyNonlinear, toWin);
	    FocusInEvents(
		toWin, sprite.win, NullWindow, mode, NotifyPointer, FALSE);
	}
	else
	{
	    if (IsParent(toWin, fromWin))
	    {
		FocusEvent(FocusOut, mode, NotifyAncestor, fromWin);
		FocusOutEvents(
		    fromWin->parent, toWin, mode, NotifyVirtual, FALSE);
		FocusEvent(FocusIn, mode, NotifyInferior, toWin);
		if ((IsParent(toWin, sprite.win)) &&
			(sprite.win != fromWin) &&
			(!IsParent(fromWin, sprite.win)) &&
			(!IsParent(sprite.win, fromWin)))
		    FocusInEvents(
			toWin, sprite.win, NullWindow, mode,
			NotifyPointer, FALSE);
	    }
	    else
		if (IsParent(fromWin, toWin))
		{
		    if ((IsParent(fromWin, sprite.win)) &&
			    (sprite.win != fromWin) &&
			    (!IsParent(toWin, sprite.win)) &&
			    (!IsParent(sprite.win, toWin)))
			FocusOutEvents(
			    sprite.win, fromWin, mode, NotifyPointer, FALSE);
		    FocusEvent(FocusOut, mode, NotifyInferior, fromWin);
		    FocusInEvents(
			toWin, fromWin, fromWin, mode, NotifyVirtual, FALSE);
		    FocusEvent(FocusIn, mode, NotifyAncestor, toWin);
		}
		else
		{
		/* neither fromWin or toWin is child of other */
		    WindowPtr common = CommonAncestor(toWin, fromWin);
		/* common == NullWindow ==> different screens XXX */
		    if (IsParent(fromWin, sprite.win))
			FocusOutEvents(
			    sprite.win, fromWin, mode, NotifyPointer, FALSE);
		    FocusEvent(FocusOut, mode, NotifyNonlinear, fromWin);
		    FocusOutEvents(
			fromWin->parent, common, mode, NotifyNonlinearVirtual,
			FALSE);
		    FocusInEvents(
			common, toWin, toWin, mode, NotifyNonlinearVirtual,
			FALSE);
		    FocusEvent(FocusIn, mode, NotifyNonlinear, toWin);
		    if (IsParent(toWin, sprite.win))
			FocusInEvents(
			    toWin, sprite.win, NullWindow, mode,
			    NotifyPointer, FALSE);
		}
	}
    }
}

/* XXX SetInputFocus does not enumerate all roots, handle screen crossings */
int
ProcSetInputFocus(client)
    ClientPtr client;
{
    TimeStamp			time;
    WindowPtr			focusWin;
    int				mode;
    register DeviceIntPtr	kbd = inputInfo.keyboard;
    register FocusPtr		focus = &kbd->u.keybd.focus;
    REQUEST(xSetInputFocusReq);

    REQUEST←SIZE←MATCH(xSetInputFocusReq);
    if ((stuff->revertTo != RevertToParent) &&
	    (stuff->revertTo != RevertToPointerRoot) &&
	    (stuff->revertTo != RevertToNone))
    {
	client->errorValue = stuff->revertTo;
	return BadValue;
    }
    time = ClientTimeToServerTime(stuff->time);
    if ((stuff->focus == None) || (stuff->focus == PointerRoot))
	focusWin = (WindowPtr)(stuff->focus);
    else if (!(focusWin = (WindowPtr)LookupID(
	stuff->focus, RT←WINDOW, RC←CORE)))
    {
	client->errorValue = stuff->focus;
	return BadWindow;
    }
    if ((CompareTimeStamps(time, currentTime) == LATER) ||
	    (CompareTimeStamps(time, focus->time) == EARLIER))
	return Success;
    mode = (kbd->grab) ? NotifyWhileGrabbed : NotifyNormal;
    DoFocusEvents(focus->win, focusWin, mode);
    focus->time = time;
    focus->revert = stuff->revertTo;
    focus->win = focusWin;
    if (focusWin == NoneWin)
        focusTraceGood = 0;
    else if (focusWin == PointerRootWin)
    {
        focusTraceGood = 1;
        focusTrace[0] = ROOT;
    }
    else
    {
        int depth=0;
        WindowPtr pWin;
        for (pWin = focusWin; pWin; pWin = pWin->parent) depth++;
        if (depth > focusTraceSize)
        {
	    focusTraceSize = depth+1;
	    focusTrace = (WindowPtr *)Xrealloc(
		    focusTrace, focusTraceSize*sizeof(WindowPtr));
	}
        for (pWin = focusWin; pWin; pWin = pWin->parent, depth--) 
	    focusTrace[depth] = pWin;
    }
    return Success;
}

int
ProcGetInputFocus(client)
    ClientPtr client;
{
    xGetInputFocusReply rep;
    REQUEST(xReq);
    FocusPtr focus = &(inputInfo.keyboard->u.keybd.focus);

    REQUEST←SIZE←MATCH(xReq);
    rep.type = X←Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    if (focus->win == NoneWin)
	rep.focus = None;
    else if (focus->win == PointerRootWin)
	rep.focus = PointerRoot;
    else rep.focus = focus->win->wid;
    rep.revertTo = focus->revert;
    WriteReplyToClient(client, sizeof(xGetInputFocusReply), &rep);
    return Success;
}

int
ProcGrabPointer(client)
    ClientPtr client;
{
    xGrabPointerReply rep;
    DeviceIntPtr device = inputInfo.pointer;
    GrabPtr grab = device->grab;
    WindowPtr pWin, confineTo;
    CursorPtr cursor;
    REQUEST(xGrabPointerReq);
    TimeStamp time;

    REQUEST←SIZE←MATCH(xGrabPointerReq);
    if ((stuff->pointerMode != GrabModeSync) && 
	(stuff->pointerMode != GrabModeAsync) && 
	(stuff->keyboardMode != GrabModeSync) && 
	(stuff->keyboardMode != GrabModeAsync))
        return BadValue;

    pWin = (WindowPtr)LookupID(stuff->grabWindow, RT←WINDOW, RC←CORE);
    if (!pWin)
    {
	client->errorValue = stuff->grabWindow;
	return BadWindow;
    }
    if (stuff->confineTo == None)
	confineTo = NullWindow;
    else
    {
	confineTo =
	    (WindowPtr)LookupID(stuff->grabWindow, RT←WINDOW, RC←CORE);
	if (!confineTo)
	{
	    client->errorValue = stuff->grabWindow;
	    return BadWindow;
	}
    }
    if (stuff->cursor == None)
	cursor = NullCursor;
    else
    {
	cursor = (CursorPtr)LookupID(stuff->cursor, RT←CURSOR, RC←CORE);
	if (!cursor)
	    return BadCursor;
    }
	/* at this point, some sort of reply is guaranteed. */
    time = ClientTimeToServerTime(stuff->time);
    rep.type = X←Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = 0;
    if ((grab) && (grab->client != client))
	rep.status = AlreadyGrabbed;
    else if (!pWin->realized)
	rep.status = GrabNotViewable;
    else if (device->sync.frozen &&
	     ((device->sync.other && (device->sync.other->client != client)) ||
	     ((device->sync.state >= FROZEN) &&
	      (device->grab->client != client))))
	rep.status = GrabFrozen;
    else if ((CompareTimeStamps(time, currentTime) == LATER) ||
	     (device->grab &&
	     (CompareTimeStamps(time, device->grabTime) == EARLIER)))
	rep.status = GrabInvalidTime;
    else
    {
	ptrGrab.u.ptr.cursor = cursor;
	ptrGrab.client = client;
	ptrGrab.ownerEvents = stuff->ownerEvents;
	ptrGrab.eventMask = stuff->eventMask;
	ptrGrab.u.ptr.confineTo = confineTo;
	ptrGrab.window = pWin;
	ptrGrab.keyboardMode = stuff->keyboardMode;
	ptrGrab.pointerMode = stuff->pointerMode;
	ptrGrab.device = inputInfo.pointer;
	ActivatePointerGrab(inputInfo.pointer, &ptrGrab, time, FALSE);
	rep.status = GrabSuccess;
    }
    WriteReplyToClient(client, sizeof(xGrabPointerReply), &rep);
    if (cursor)
	ChangeToCursor(cursor);
    return Success;
}

int
ProcChangeActivePointerGrab(client)
    ClientPtr client;
{
    DeviceIntPtr device = inputInfo.pointer;
    register GrabPtr grab = device->grab;
    CursorPtr newCursor;
    REQUEST(xChangeActivePointerGrabReq);
    TimeStamp time;

    REQUEST←SIZE←MATCH(xChangeActivePointerGrabReq);
    if (!grab)
	return Success;
    if (grab->client != client)
	return BadAccess;
    if (stuff->cursor == None)
	grab->u.ptr.cursor = NullCursor;
    else
    {
	newCursor = (CursorPtr)LookupID(stuff->cursor, RT←CURSOR, RC←CORE);
	if (!newCursor)
	    return BadCursor;
    }
    time = ClientTimeToServerTime(stuff->time);
    if ((CompareTimeStamps(time, currentTime) == LATER) ||
	     (CompareTimeStamps(time, device->grabTime) == EARLIER))
	return Success;
    grab->u.ptr.cursor = newCursor;
    PostNewCursor();
    /* if mouse motion is newly turned on, it should probably send a motion
       event */
    grab->eventMask = stuff->eventMask;
    return Success;
}

int
ProcUngrabPointer(client)
    ClientPtr client;
{
    DeviceIntPtr device = inputInfo.pointer;
    GrabPtr grab = device->grab;
    TimeStamp time;
    REQUEST(xResourceReq);

    REQUEST←SIZE←MATCH(xResourceReq);
    time = ClientTimeToServerTime(stuff->id);
    if ((CompareTimeStamps(time, currentTime) != LATER) &&
	    (CompareTimeStamps(time, device->grabTime) != EARLIER) &&
	    (grab) && (grab->client == client))
	DeactivatePointerGrab(inputInfo.pointer);
    return Success;
}

int
ProcGrabKeyboard(client)
    ClientPtr client;
{
    xGrabKeyboardReply rep;
    DeviceIntPtr device = inputInfo.keyboard;
    GrabPtr grab = device->grab;
    WindowPtr pWin;
    TimeStamp time;
    REQUEST(xGrabKeyboardReq);

    REQUEST←SIZE←MATCH(xGrabKeyboardReq);
    if ((stuff->pointerMode != GrabModeSync) && 
	(stuff->pointerMode != GrabModeAsync) && 
	(stuff->keyboardMode != GrabModeSync) && 
	(stuff->keyboardMode != GrabModeAsync))
        return BadValue;
    pWin = (WindowPtr) LookupID(stuff->grabWindow, RT←WINDOW, RC←CORE);
    if (!pWin)
    {
	client->errorValue = stuff->grabWindow;
	return BadWindow;
    }
    time = ClientTimeToServerTime(stuff->time);
    rep.type = X←Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = 0;
    if ((grab) && (grab->client != client))
	rep.status = AlreadyGrabbed;
    else if (!pWin->realized)
	rep.status = GrabNotViewable;
    else if ((CompareTimeStamps(time, currentTime) == LATER) ||
	     (device->grab &&
	     (CompareTimeStamps(time, device->grabTime) == EARLIER)))
	rep.status = GrabInvalidTime;
    else if (device->sync.frozen &&
	     ((device->sync.other && (device->sync.other->client != client)) ||
	     ((device->sync.state >= FROZEN) &&
	      (device->grab->client != client))))
	rep.status = GrabFrozen;
    else
    {
	keybdGrab.window = pWin;
	keybdGrab.client = client;
	keybdGrab.keyboardMode = stuff->keyboardMode;
	keybdGrab.pointerMode = stuff->pointerMode;
	keybdGrab.eventMask = KeyPressMask | KeyReleaseMask;
	keybdGrab.device = inputInfo.keyboard;
	ActivateKeyboardGrab(
	    device, &keybdGrab, ClientTimeToServerTime(stuff->time), FALSE);
	DoFocusEvents(device->u.keybd.focus.win, pWin, NotifyGrab);
	rep.status = GrabSuccess;
    }
    WriteReplyToClient(client, sizeof(xGrabKeyboardReply), &rep);
    return Success;
}

int
ProcUngrabKeyboard(client)
    ClientPtr client;
{
    DeviceIntPtr device = inputInfo.keyboard;
    GrabPtr grab = device->grab;
    TimeStamp time;
    REQUEST(xResourceReq);

    REQUEST←SIZE←MATCH(xResourceReq);
    time = ClientTimeToServerTime(stuff->id);
    if ((CompareTimeStamps(time, currentTime) != LATER) &&
	(CompareTimeStamps(time, device->grabTime) != EARLIER) &&
	(grab) && (grab->client == client))
    {
	DoFocusEvents(grab->window, device->u.keybd.focus.win, NotifyUngrab);
	DeactivateKeyboardGrab(device);
    }
    return Success;
}

void
SetPointerStateMasks(ptr)
    DevicePtr ptr;
{
 /* all have to be defined since some button might be mapped here */
    stateMasks[1] = Button1Mask;
    stateMasks[2] = Button2Mask;
    stateMasks[3] = Button3Mask;
    stateMasks[4] = Button4Mask;
    stateMasks[5] = Button5Mask;
}

void
SetKeyboardStateMasks(keybd)
    DeviceIntPtr keybd;
{
#define SET←MOD←STATE(entry, mask) \
    if (keybd->u.keybd.modMap.entry != NoSymbol)\
        stateMasks[keybd->u.keybd.modMap.entry] = mask;

    /* Note that 1-5 entries are buttons */

    int     i;
    for (i = 8; i < MAP←LENGTH; i++)
	stateMasks[i] = 0;
    SET←MOD←STATE(lock,   LockMask);

    SET←MOD←STATE(shiftA, ShiftMask);
    SET←MOD←STATE(shiftB, ShiftMask);

    SET←MOD←STATE(controlA, ControlMask);
    SET←MOD←STATE(controlB, ControlMask);

    SET←MOD←STATE(mod1A,    Mod1Mask);
    SET←MOD←STATE(mod1B,    Mod1Mask);

    SET←MOD←STATE(mod2A,    Mod2Mask);
    SET←MOD←STATE(mod2B,    Mod2Mask);

    SET←MOD←STATE(mod3A,    Mod3Mask);
    SET←MOD←STATE(mod3B,    Mod3Mask);

    SET←MOD←STATE(mod4A,    Mod4Mask);
    SET←MOD←STATE(mod4B,    Mod4Mask);

    SET←MOD←STATE(mod5A,    Mod5Mask);
    SET←MOD←STATE(mod5B,    Mod5Mask);
#undef SET←MOD←STATE
}

DevicePtr
AddInputDevice(deviceProc, autoStart)
    DeviceProc deviceProc;
    Bool autoStart;
{
    DeviceIntPtr d;
    if (inputInfo.numDevices == inputInfo.arraySize)
    {
	inputInfo.arraySize += 5;
	inputInfo.devices = (DeviceIntPtr *)Xrealloc(
	    inputInfo.devices, inputInfo.arraySize * sizeof(DeviceIntPtr));
    }
    d = (DeviceIntPtr) Xalloc(sizeof(DeviceIntRec));
    inputInfo.devices[inputInfo.numDevices++] = d;
    d->public.on = FALSE;
    d->public.processInputProc = NoopDDA;
    d->deviceProc = deviceProc;
    d->startup = autoStart;
    d->sync.frozen = FALSE;
    d->sync.other = NullGrab;
    d->sync.state = NOT←GRABBED;
    return &d->public;
}

DevicesDescriptor
GetInputDevices()
{
    DevicesDescriptor devs;
    devs.count = inputInfo.numDevices;
    devs.devices = (DevicePtr *)inputInfo.devices;
    return devs;
}

void
InitEvents()
{
    curKeySyms.map = (KeySym *)NULL;
    curKeySyms.minKeyCode = 0;
    curKeySyms.maxKeyCode = 0;
    curKeySyms.mapWidth = 0;

    currentScreen = &screenInfo.screen[0];
    inputInfo.numDevices = 0;
    if (spriteTraceSize == 0)
    {
	spriteTraceSize = 20;
	spriteTrace = (WindowPtr *)Xalloc(20*sizeof(WindowPtr));
    }
    spriteTraceGood = 0;
    if (focusTraceSize == 0)
    {
	focusTraceSize = 20;
	focusTrace = (WindowPtr *)Xalloc(20*sizeof(WindowPtr));
    }
    focusTraceGood = 0;
    lastEventMask = OwnerGrabButtonMask;
    sprite.win = NullWindow;
    sprite.current = NullCursor;
    sprite.hotLimits.x1 = 0;
    sprite.hotLimits.y1 = 0;
    sprite.hotLimits.x2 = currentScreen->width;
    sprite.hotLimits.y2 = currentScreen->height;
    lastWasMotion = FALSE;
    syncEvents.replayDev = (DeviceIntPtr)NULL;
    syncEvents.pending.forw = &syncEvents.pending;
    syncEvents.pending.back = &syncEvents.pending;
    syncEvents.free.forw = &syncEvents.free;
    syncEvents.free.back = &syncEvents.free;
    syncEvents.num = 0;
    syncEvents.playingEvents = FALSE;
    currentTime.months = 0;
    currentTime.milliseconds = GetTimeInMillis();
}

int
InitAndStartDevices(argc, argv)
    int argc;
    char *argv[];
{
    int     i;
    DeviceIntPtr d;

    for (i = 0; i < inputInfo.numDevices; i++)
    {
	d = inputInfo.devices[i];
	if ((*d->deviceProc) (d, DEVICE←INIT, argc, argv) == Success)
	    d->inited = TRUE;
	else
	    d->inited = FALSE;
    }
 /* do not turn any devices on until all have been inited */
    for (i = 0; i < inputInfo.numDevices; i++)
    {
	d = inputInfo.devices[i];
	if ((d->startup) && (d->inited))
	    (*d->deviceProc) (d, DEVICE←ON, argc, argv);
    }
    if (inputInfo.pointer && inputInfo.pointer->inited &&
	    inputInfo.keyboard && inputInfo.keyboard->inited)
	return Success;
    return BadImplementation;
}

void
CloseDownDevices(argc, argv)
    int argc;
    char *argv[];
{
    int     		i;
    DeviceIntPtr	d;

    Xfree(curKeySyms.map);

    for (i = inputInfo.numDevices - 1; i >= 0; i--)
    {
	d = inputInfo.devices[i];
	if (d->inited)
	    (*d->deviceProc) (d, DEVICE←CLOSE, argc, argv);
	Xfree(inputInfo.devices[i]);
    }
}

int
NumMotionEvents()
{
    return inputInfo.numMotionEvents;
}

void
RegisterPointerDevice(device, numMotionEvents)
    DevicePtr device;
    int numMotionEvents;
{
    inputInfo.pointer = (DeviceIntPtr)device;
    inputInfo.numMotionEvents = numMotionEvents;
    device->processInputProc = ProcessPointerEvent;
}

void
RegisterKeyboardDevice(device)
    DevicePtr device;
{
    inputInfo.keyboard = (DeviceIntPtr)device;
    device->processInputProc = ProcessKeyboardEvent;
}