/*
 *  Decoding and switching routines for the X11 protocol
 *
 *	James Peterson, 1988
 *
 * Copyright (C) 1988 MCC
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, 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 name of MCC not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  MCC makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL MCC 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.
 *
 */
/*
 * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

#include "scope.h"
#include "x11.h"
#include "extensions.h"
#include "lbxscope.h"

/*
  There are 4 types of things in X11: requests, replies, errors, and events.

  Each of them has a format defined by a small integer that defines
  the type of the thing.

  Requests have an opcode in the first byte.
  Events have a code in the first byte.
  Errors have a code in the second byte (the first byte is 0)
  Replies ...

  Replies have a sequence number in bytes 2 and 3.  The sequence
  number should be used to identify the request that was sent, and
  from that request we can determine the type of the reply.
*/


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


/*
  We need to keep the sequence number for a request to match it with
  an expected reply.  The sequence number is associated only with the
  particular connection that we have. We would expect these replies
  to be handled as a FIFO queue.
*/

struct QueueEntry {
    struct QueueEntry *Next;
    long SequenceNumber;
    short Request;
    short RequestMinor;
};

/* free space list of Q entries */

static struct QueueEntry *FreeQEntries = NULL;

/* ************************************************************ */
static struct QueueEntry *
NewQEntry(long SequenceNumber, short Request, short RequestMinor)
{
    struct QueueEntry *p;

    /* Get a Queue Entry */
    if (FreeQEntries == NULL) {
        /* create new queue entry */
        p = malloc(sizeof(*p));
        if (p == NULL)
            panic("unable to allocate new QueueEntry");
    }
    else {
        /* reuse an old queue entry */
        p = FreeQEntries;
        FreeQEntries = FreeQEntries->Next;
    }

    /* fill in its values */
    p->Next = NULL;
    p->SequenceNumber = SequenceNumber;
    p->Request = Request;
    p->RequestMinor = RequestMinor;
    return (p);
}

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

/* define a queue of entries for each FD */

struct QueueHeader {
    struct QueueEntry *Head;
    struct QueueEntry *Tail;
};

static struct QueueHeader *ReplyQ;

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

void
InitReplyQ(void)
{
    ReplyQ = calloc(MaxFD, sizeof(struct QueueHeader));
    if (ReplyQ == NULL)
        panic("unable to allocate ReplyQ");
}

void
FlushReplyQ(FD fd)
{
    struct QueueEntry *p;
    struct QueueEntry *NextQEntry;

    /* go down the reply queue and free all entries */
    for (p = ReplyQ[fd].Head; p != NULL; p = NextQEntry) {
        NextQEntry = p->Next;

        /* put freed entry on list of free entries (for later reuse)  */
        p->Next = FreeQEntries;
        FreeQEntries = p;
    }

    ReplyQ[fd].Head = NULL;
    ReplyQ[fd].Tail = NULL;
}

static void
DumpReplyQ(FD fd)
{
    fprintf(stderr, "ReplyQ[%d] = { Head 0x%lx; Tail 0x%lx }\n", fd,
            (unsigned long) ReplyQ[fd].Head, (unsigned long) ReplyQ[fd].Tail);
    {
        struct QueueEntry *p;

        for (p = ReplyQ[fd].Head; p != NULL; p = p->Next)
            fprintf(stderr,
                    "0x%lx = { Next 0x%lx; SequenceNumber %ld; Request %d }\n",
                    (unsigned long) p, (unsigned long) p->Next,
                    (unsigned long) p->SequenceNumber, p->Request);
    }
}

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

/* A reply is expected to the type of request given for the fd associated
   with this one */

static void
SequencedReplyExpected(FD fd, long SequenceNumber,
                       short RequestType, short RequestMinorType)
{
    struct QueueEntry *p;

    debug(8, (stderr,
              "Reply expected: sequence %ld and request type %d,%d for fd %d\n",
              SequenceNumber, RequestType, RequestMinorType, fd));
    /* create a new queue entry */
    p = NewQEntry(SequenceNumber, RequestType, RequestMinorType);

    /* find the server associated with this client */
    fd = FDPair(fd);
    if (fd < 0 || fd >= MaxFD)
        return;

    /* attach the new queue entry to the end of the queue for the Server */
    if (ReplyQ[fd].Tail != NULL)
        (ReplyQ[fd].Tail)->Next = p;
    else
        ReplyQ[fd].Head = p;
    ReplyQ[fd].Tail = p;

    debug(8, (stderr, "Save sequence %ld and request type %d,%d for fd %d\n",
              p->SequenceNumber, p->Request, p->RequestMinor, fd));
}

static FD Lastfd;
static long LastSequenceNumber;
static short LastReplyType;
static short LastReplyMinorType;

/* search for the type of request that is associated with a reply
   to the given sequence number for this fd */

short
CheckReplyTable(FD fd, short SequenceNumber, short *minorp)
{
    struct QueueEntry *p;
    struct QueueEntry *trailer;

    if (debuglevel & 128)
        DumpReplyQ(fd);
    for (trailer = NULL, p = ReplyQ[fd].Head;
         p != NULL; trailer = p, p = p->Next) {
        /* look for matching sequence number in queue of this fd */
        if (SequenceNumber == ((short) (0xFFFF & p->SequenceNumber))) {
            /* save the Request type */
            Lastfd = fd;
            LastSequenceNumber = p->SequenceNumber;
            LastReplyType = p->Request;
            LastReplyMinorType = p->RequestMinor;

            /* pull the queue entry out of the queue for this fd */
            if (trailer == NULL)
                ReplyQ[fd].Head = p->Next;
            else
                trailer->Next = p->Next;
            if (ReplyQ[fd].Tail == p)
                ReplyQ[fd].Tail = trailer;

            /* put freed entry on list of free entries (for later reuse)  */
            p->Next = FreeQEntries;
            FreeQEntries = p;

            debug(8, (stderr, "Reply on fd %d for sequence %d is type %d,%x\n",
                      fd, SequenceNumber, LastReplyType, LastReplyMinorType));
            *minorp = LastReplyMinorType;
            return (LastReplyType);
        }
    }

    /* not expecting a reply for that sequence number */
    debug(8, (stderr, "Reply on fd %d for sequence %d is not found\n",
              fd, SequenceNumber));
    return (0);
}


/* ************************************************************ */
/* A reply is expected to the type of request given for the
   sequence number associated with this fd */

void
ReplyExpected(FD fd, short Request)
{
    SequencedReplyExpected(fd, CS[fd].SequenceNumber, Request, 0);
}

void
ExtendedReplyExpected(FD fd, short Request, short RequestMinor)
{
    SequencedReplyExpected(fd, CS[fd].SequenceNumber, Request, RequestMinor);
}

/* ************************************************************ */
/* another reply is expected for the same reply as we just had */
/* This is only used with ListFontsWithInfo */

void
KeepLastReplyExpected(void)
{
    SequencedReplyExpected(Lastfd, LastSequenceNumber, LastReplyType,
                           LastReplyMinorType);
}

#define DUMP_REQUEST	0
#define DUMP_REPLY	1
#define DUMP_EVENT	2
#define DUMP_ERROR	3

static const char *const simple_names[] = {
    "REQUEST",
    "REPLY  ",
    "EVENT  ",
    "ERROR  ",
};

static void
SimpleDump(int type, FD fd, short Major, short Minor, long bytes)
{
    PrintTime();
    fprintf(stdout, "@@%s %3d %3d %3d %7ld\n",
            simple_names[type], ClientNumber(fd), Major, Minor, bytes);
}

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

void
DecodeRequest(FD fd, const unsigned char *buf, long n)
{
    short Request = IByte(&buf[0]);
    short RequestMinor = Request >= 128 ? IByte(&buf[1]) : 0;
    unsigned long seq;

    CS[fd].SequenceNumber += 1;
    seq = CS[fd].SequenceNumber;
    if (CS[fd].littleEndian) {
        SBf[0] = seq;
        SBf[1] = seq >> 8;
        SBf[2] = seq >> 16;
        SBf[3] = seq >> 24;
    }
    else {
        SBf[0] = seq >> 24;
        SBf[1] = seq >> 16;
        SBf[2] = seq >> 8;
        SBf[3] = seq;
    }
    SetIndentLevel(PRINTCLIENT);

    /* Special handling of QueryExtension to save extension names */
    if (Request == 98) {
        ProcessQueryExtensionRequest(seq, buf);
    }

    if (Verbose < 0) {
        SimpleDump(DUMP_REQUEST, fd, Request, RequestMinor, n);
        return;
    }

    if (Raw || (Verbose > 3))
        DumpItem("Request", fd, buf, n);

    if (Request < 0 || 127 < Request) {
        ExtensionRequest(fd, buf, Request);
    }
    else
        switch (Request) {
        case 1:
            CreateWindow(fd, buf);
            break;
        case 2:
            ChangeWindowAttributes(fd, buf);
            break;
        case 3:
            GetWindowAttributes(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 4:
            DestroyWindow(fd, buf);
            break;
        case 5:
            DestroySubwindows(fd, buf);
            break;
        case 6:
            ChangeSaveSet(fd, buf);
            break;
        case 7:
            ReparentWindow(fd, buf);
            break;
        case 8:
            MapWindow(fd, buf);
            break;
        case 9:
            MapSubwindows(fd, buf);
            break;
        case 10:
            UnmapWindow(fd, buf);
            break;
        case 11:
            UnmapSubwindows(fd, buf);
            break;
        case 12:
            ConfigureWindow(fd, buf);
            break;
        case 13:
            CirculateWindow(fd, buf);
            break;
        case 14:
            GetGeometry(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 15:
            QueryTree(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 16:
            InternAtom(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 17:
            GetAtomName(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 18:
            ChangeProperty(fd, buf);
            break;
        case 19:
            DeleteProperty(fd, buf);
            break;
        case 20:
            GetProperty(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 21:
            ListProperties(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 22:
            SetSelectionOwner(fd, buf);
            break;
        case 23:
            GetSelectionOwner(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 24:
            ConvertSelection(fd, buf);
            break;
        case 25:
            SendEvent(fd, buf);
            break;
        case 26:
            GrabPointer(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 27:
            UngrabPointer(fd, buf);
            break;
        case 28:
            GrabButton(fd, buf);
            break;
        case 29:
            UngrabButton(fd, buf);
            break;
        case 30:
            ChangeActivePointerGrab(fd, buf);
            break;
        case 31:
            GrabKeyboard(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 32:
            UngrabKeyboard(fd, buf);
            break;
        case 33:
            GrabKey(fd, buf);
            break;
        case 34:
            UngrabKey(fd, buf);
            break;
        case 35:
            AllowEvents(fd, buf);
            break;
        case 36:
            GrabServer(fd, buf);
            break;
        case 37:
            UngrabServer(fd, buf);
            break;
        case 38:
            QueryPointer(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 39:
            GetMotionEvents(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 40:
            TranslateCoordinates(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 41:
            WarpPointer(fd, buf);
            break;
        case 42:
            SetInputFocus(fd, buf);
            break;
        case 43:
            GetInputFocus(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 44:
            QueryKeymap(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 45:
            OpenFont(fd, buf);
            break;
        case 46:
            CloseFont(fd, buf);
            break;
        case 47:
            QueryFont(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 48:
            QueryTextExtents(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 49:
            ListFonts(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 50:
            ListFontsWithInfo(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 51:
            SetFontPath(fd, buf);
            break;
        case 52:
            GetFontPath(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 53:
            CreatePixmap(fd, buf);
            break;
        case 54:
            FreePixmap(fd, buf);
            break;
        case 55:
            CreateGC(fd, buf);
            break;
        case 56:
            ChangeGC(fd, buf);
            break;
        case 57:
            CopyGC(fd, buf);
            break;
        case 58:
            SetDashes(fd, buf);
            break;
        case 59:
            SetClipRectangles(fd, buf);
            break;
        case 60:
            FreeGC(fd, buf);
            break;
        case 61:
            ClearArea(fd, buf);
            break;
        case 62:
            CopyArea(fd, buf);
            break;
        case 63:
            CopyPlane(fd, buf);
            break;
        case 64:
            PolyPoint(fd, buf);
            break;
        case 65:
            PolyLine(fd, buf);
            break;
        case 66:
            PolySegment(fd, buf);
            break;
        case 67:
            PolyRectangle(fd, buf);
            break;
        case 68:
            PolyArc(fd, buf);
            break;
        case 69:
            FillPoly(fd, buf);
            break;
        case 70:
            PolyFillRectangle(fd, buf);
            break;
        case 71:
            PolyFillArc(fd, buf);
            break;
        case 72:
            PutImage(fd, buf);
            break;
        case 73:
            GetImage(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 74:
            PolyText8(fd, buf);
            break;
        case 75:
            PolyText16(fd, buf);
            break;
        case 76:
            ImageText8(fd, buf);
            break;
        case 77:
            ImageText16(fd, buf);
            break;
        case 78:
            CreateColormap(fd, buf);
            break;
        case 79:
            FreeColormap(fd, buf);
            break;
        case 80:
            CopyColormapAndFree(fd, buf);
            break;
        case 81:
            InstallColormap(fd, buf);
            break;
        case 82:
            UninstallColormap(fd, buf);
            break;
        case 83:
            ListInstalledColormaps(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 84:
            AllocColor(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 85:
            AllocNamedColor(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 86:
            AllocColorCells(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 87:
            AllocColorPlanes(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 88:
            FreeColors(fd, buf);
            break;
        case 89:
            StoreColors(fd, buf);
            break;
        case 90:
            StoreNamedColor(fd, buf);
            break;
        case 91:
            QueryColors(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 92:
            LookupColor(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 93:
            CreateCursor(fd, buf);
            break;
        case 94:
            CreateGlyphCursor(fd, buf);
            break;
        case 95:
            FreeCursor(fd, buf);
            break;
        case 96:
            RecolorCursor(fd, buf);
            break;
        case 97:
            QueryBestSize(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 98:
            QueryExtension(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 99:
            ListExtensions(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 100:
            ChangeKeyboardMapping(fd, buf);
            break;
        case 101:
            GetKeyboardMapping(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 102:
            ChangeKeyboardControl(fd, buf);
            break;
        case 103:
            GetKeyboardControl(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 104:
            Bell(fd, buf);
            break;
        case 105:
            ChangePointerControl(fd, buf);
            break;
        case 106:
            GetPointerControl(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 107:
            SetScreenSaver(fd, buf);
            break;
        case 108:
            GetScreenSaver(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 109:
            ChangeHosts(fd, buf);
            break;
        case 110:
            ListHosts(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 111:
            SetAccessControl(fd, buf);
            break;
        case 112:
            SetCloseDownMode(fd, buf);
            break;
        case 113:
            KillClient(fd, buf);
            break;
        case 114:
            RotateProperties(fd, buf);
            break;
        case 115:
            ForceScreenSaver(fd, buf);
            break;
        case 116:
            SetPointerMapping(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 117:
            GetPointerMapping(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 118:
            SetModifierMapping(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 119:
            GetModifierMapping(fd, buf);
            ReplyExpected(fd, Request);
            break;
        case 127:
            NoOperation(fd, buf);
            break;
        default:
            warn("Unimplemented request opcode");
            break;
        }
}

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

void
DecodeReply(FD fd, const unsigned char *buf, long n)
{
    short SequenceNumber = IShort(&buf[2]);
    short RequestMinor;
    short Request = CheckReplyTable(fd, SequenceNumber, &RequestMinor);

    /* Special handling of QueryExtension to save extension names */
    if (Request == 98) {
        ProcessQueryExtensionReply(SequenceNumber, buf);
    }

    if (Verbose < 0) {
        SimpleDump(DUMP_REPLY, fd, Request, RequestMinor, n);
        return;
    }

    SetIndentLevel(PRINTSERVER);
    RBf[0] = Request; /* for the PrintField in the Reply procedure */
    RBf[1] = RequestMinor;
    if (Raw || (Verbose > 3))
        DumpItem("Reply", fd, buf, n);
    if (Request < 0 || 127 < Request) {
        ExtensionReply(fd, buf, Request, RequestMinor);
    }
    else
        switch (Request) {
        case 0:
            UnknownReply(buf);
            break;
        case 3:
            GetWindowAttributesReply(buf);
            break;
        case 14:
            GetGeometryReply(buf);
            break;
        case 15:
            QueryTreeReply(buf);
            break;
        case 16:
            InternAtomReply(buf);
            break;
        case 17:
            GetAtomNameReply(buf);
            break;
        case 20:
            GetPropertyReply(buf);
            break;
        case 21:
            ListPropertiesReply(buf);
            break;
        case 23:
            GetSelectionOwnerReply(buf);
            break;
        case 26:
            GrabPointerReply(buf);
            break;
        case 31:
            GrabKeyboardReply(buf);
            break;
        case 38:
            QueryPointerReply(buf);
            break;
        case 39:
            GetMotionEventsReply(buf);
            break;
        case 40:
            TranslateCoordinatesReply(buf);
            break;
        case 43:
            GetInputFocusReply(buf);
            break;
        case 44:
            QueryKeymapReply(buf);
            break;
        case 47:
            QueryFontReply(buf);
            break;
        case 48:
            QueryTextExtentsReply(buf);
            break;
        case 49:
            ListFontsReply(buf);
            break;
        case 50:
            ListFontsWithInfoReply(buf);
            break;
        case 52:
            GetFontPathReply(buf);
            break;
        case 73:
            GetImageReply(buf);
            break;
        case 83:
            ListInstalledColormapsReply(buf);
            break;
        case 84:
            AllocColorReply(buf);
            break;
        case 85:
            AllocNamedColorReply(buf);
            break;
        case 86:
            AllocColorCellsReply(buf);
            break;
        case 87:
            AllocColorPlanesReply(buf);
            break;
        case 91:
            QueryColorsReply(buf);
            break;
        case 92:
            LookupColorReply(buf);
            break;
        case 97:
            QueryBestSizeReply(buf);
            break;
        case 98:
            QueryExtensionReply(buf);
            break;
        case 99:
            ListExtensionsReply(buf);
            break;
        case 101:
            GetKeyboardMappingReply(buf);
            break;
        case 103:
            GetKeyboardControlReply(buf);
            break;
        case 106:
            GetPointerControlReply(buf);
            break;
        case 108:
            GetScreenSaverReply(buf);
            break;
        case 110:
            ListHostsReply(buf);
            break;
        case 116:
            SetPointerMappingReply(buf);
            break;
        case 117:
            GetPointerMappingReply(buf);
            break;
        case 118:
            SetModifierMappingReply(buf);
            break;
        case 119:
            GetModifierMappingReply(buf);
            break;
        default:
            warn("Unimplemented reply opcode");
            break;
        }
}

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

void
DecodeError(FD fd, const unsigned char *buf, long n)
{
    short Error = IByte(&buf[1]);
    short Request = 0;
    short RequestMinor = 0;

    Request = CheckReplyTable(fd, (short) IShort(&buf[2]), &RequestMinor);

    if (Verbose < 0) {
        SimpleDump(DUMP_ERROR, fd, Request, RequestMinor, n);
        return;
    }

    SetIndentLevel(PRINTSERVER);
    if (Raw || (Verbose > 3))
        DumpItem("Error", fd, buf, n);

    if (Error < 1 || Error > 17) {
        ExtensionError(fd, buf, Error);
    }
    else
        switch (Error) {
        case 1:
            RequestError(buf);
            break;
        case 2:
            ValueError(buf);
            break;
        case 3:
            WindowError(buf);
            break;
        case 4:
            PixmapError(buf);
            break;
        case 5:
            AtomError(buf);
            break;
        case 6:
            CursorError(buf);
            break;
        case 7:
            FontError(buf);
            break;
        case 8:
            MatchError(buf);
            break;
        case 9:
            DrawableError(buf);
            break;
        case 10:
            AccessError(buf);
            break;
        case 11:
            AllocError(buf);
            break;
        case 12:
            ColormapError(buf);
            break;
        case 13:
            GContextError(buf);
            break;
        case 14:
            IDChoiceError(buf);
            break;
        case 15:
            NameError(buf);
            break;
        case 16:
            LengthError(buf);
            break;
        case 17:
            ImplementationError(buf);
            break;
        default:
            warn("Unimplemented error code");
            UnknownError(buf);
            break;
        }
}

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

void
DecodeEvent(FD fd, const unsigned char *buf, long n)
{
    short Event = IByte(&buf[0]);
    short EventMinor = Event == LBXEvent ? IByte(&buf[1]) : 0;

    if (Verbose < 0) {
        SimpleDump(DUMP_EVENT, fd, Event, EventMinor, n);
        return;
    }

    SetIndentLevel(PRINTSERVER);
    if (Raw || (Verbose > 3))
        DumpItem("Event", fd, buf, n);
    /* high-order bit means SendEvent generated */
    if (Event & 0x80) {
        debug(8, (stderr, "SendEvent generated event 0x%x\n", Event));
        Event = Event & 0x7F;
    }

    if (Event < 2 || Event > 34) {
        ExtensionEvent(fd, buf, Event);
    }
    else
        switch (Event) {
        case 2:
            KeyPressEvent(buf);
            break;
        case 3:
            KeyReleaseEvent(buf);
            break;
        case 4:
            ButtonPressEvent(buf);
            break;
        case 5:
            ButtonReleaseEvent(buf);
            break;
        case 6:
            MotionNotifyEvent(buf);
            break;
        case 7:
            EnterNotifyEvent(buf);
            break;
        case 8:
            LeaveNotifyEvent(buf);
            break;
        case 9:
            FocusInEvent(buf);
            break;
        case 10:
            FocusOutEvent(buf);
            break;
        case 11:
            KeymapNotifyEvent(buf);
            break;
        case 12:
            ExposeEvent(buf);
            break;
        case 13:
            GraphicsExposureEvent(buf);
            break;
        case 14:
            NoExposureEvent(buf);
            break;
        case 15:
            VisibilityNotifyEvent(buf);
            break;
        case 16:
            CreateNotifyEvent(buf);
            break;
        case 17:
            DestroyNotifyEvent(buf);
            break;
        case 18:
            UnmapNotifyEvent(buf);
            break;
        case 19:
            MapNotifyEvent(buf);
            break;
        case 20:
            MapRequestEvent(buf);
            break;
        case 21:
            ReparentNotifyEvent(buf);
            break;
        case 22:
            ConfigureNotifyEvent(buf);
            break;
        case 23:
            ConfigureRequestEvent(buf);
            break;
        case 24:
            GravityNotifyEvent(buf);
            break;
        case 25:
            ResizeRequestEvent(buf);
            break;
        case 26:
            CirculateNotifyEvent(buf);
            break;
        case 27:
            CirculateRequestEvent(buf);
            break;
        case 28:
            PropertyNotifyEvent(buf);
            break;
        case 29:
            SelectionClearEvent(buf);
            break;
        case 30:
            SelectionRequestEvent(buf);
            break;
        case 31:
            SelectionNotifyEvent(buf);
            break;
        case 32:
            ColormapNotifyEvent(buf);
            break;
        case 33:
            ClientMessageEvent(buf);
            break;
        case 34:
            MappingNotifyEvent(buf);
            break;
        default:
            warn("Unimplemented event code");
            UnknownEvent(buf);
            break;
        }
}
