/*
 * Copyright 2002-2004 Red Hat Inc., Durham, North Carolina.
 *
 * 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 on 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
 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
 * 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.
 */

/*
 * Authors:
 *   Rickard E. (Rik) Faith <faith@redhat.com>
 *
 */

/** \file
 * This file implements the server-side part of the DMX protocol. A
 * vector of fucntions is provided at extension initialization time, so
 * most all of the useful functions in this file are declared static and
 * do not appear in the doxygen documentation.
 *
 * Much of the low-level work is done by functions in \a dmxextension.c
 *
 * Please see the Client-to-Server DMX Extension to the X Protocol
 * document for details about the protocol.  */

#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif

#include <X11/X.h>
#include <X11/Xproto.h>
#include "misc.h"
#include "os.h"
#include "dixstruct.h"
#include "extnsionst.h"
#include "extinit.h"
#include "opaque.h"

#include "dmx.h"
#include "dmxextension.h"
#include <X11/extensions/dmxproto.h>
#include <X11/extensions/dmx.h>
#include "protocol-versions.h"

#ifdef PANORAMIX
#include "panoramiXsrv.h"
#endif

static unsigned char DMXCode;

static int
_DMXXineramaActive(void)
{
#ifdef PANORAMIX
    return !noPanoramiXExtension;
#else
    return 0;
#endif
}

static void
dmxSetScreenAttribute(int bit, DMXScreenAttributesPtr attr, CARD32 value)
{
    switch (1 << bit) {
    case DMXScreenWindowWidth:
        attr->screenWindowWidth = value;
        break;
    case DMXScreenWindowHeight:
        attr->screenWindowHeight = value;
        break;
    case DMXScreenWindowXoffset:
        attr->screenWindowXoffset = value;
        break;
    case DMXScreenWindowYoffset:
        attr->screenWindowYoffset = value;
        break;
    case DMXRootWindowWidth:
        attr->rootWindowWidth = value;
        break;
    case DMXRootWindowHeight:
        attr->rootWindowHeight = value;
        break;
    case DMXRootWindowXoffset:
        attr->rootWindowXoffset = value;
        break;
    case DMXRootWindowYoffset:
        attr->rootWindowYoffset = value;
        break;
    case DMXRootWindowXorigin:
        attr->rootWindowXorigin = value;
        break;
    case DMXRootWindowYorigin:
        attr->rootWindowYorigin = value;
        break;
    }
}

static int
dmxFetchScreenAttributes(unsigned int mask,
                         DMXScreenAttributesPtr attr, CARD32 *value_list)
{
    int i;
    CARD32 *value = value_list;
    int count = 0;

    for (i = 0; i < 32; i++) {
        if (mask & (1 << i)) {
            dmxSetScreenAttribute(i, attr, *value);
            ++value;
            ++count;
        }
    }
    return count;
}

static void
dmxSetDesktopAttribute(int bit, DMXDesktopAttributesPtr attr, CARD32 value)
{
    switch (1 << bit) {
    case DMXDesktopWidth:
        attr->width = value;
        break;
    case DMXDesktopHeight:
        attr->height = value;
        break;
    case DMXDesktopShiftX:
        attr->shiftX = value;
        break;
    case DMXDesktopShiftY:
        attr->shiftY = value;
        break;
    }
}

static int
dmxFetchDesktopAttributes(unsigned int mask,
                          DMXDesktopAttributesPtr attr, CARD32 *value_list)
{
    int i;
    CARD32 *value = value_list;
    int count = 0;

    for (i = 0; i < 32; i++) {
        if (mask & (1 << i)) {
            dmxSetDesktopAttribute(i, attr, *value);
            ++value;
            ++count;
        }
    }
    return count;
}

static void
dmxSetInputAttribute(int bit, DMXInputAttributesPtr attr, CARD32 value)
{
    switch (1 << bit) {
    case DMXInputType:
        attr->inputType = value;
        break;
    case DMXInputPhysicalScreen:
        attr->physicalScreen = value;
        break;
    case DMXInputSendsCore:
        attr->sendsCore = ! !value;
        break;
    }
}

static int
dmxFetchInputAttributes(unsigned int mask,
                        DMXInputAttributesPtr attr, CARD32 *value_list)
{
    int i;
    CARD32 *value = value_list;
    int count = 0;

    for (i = 0; i < 32; i++) {
        if (mask & (1 << i)) {
            dmxSetInputAttribute(i, attr, *value);
            ++value;
            ++count;
        }
    }
    return count;
}

static int
ProcDMXQueryVersion(ClientPtr client)
{
    xDMXQueryVersionReply rep = {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .majorVersion = SERVER_DMX_MAJOR_VERSION,
        .minorVersion = SERVER_DMX_MINOR_VERSION,
        .patchVersion = SERVER_DMX_PATCH_VERSION
    };

    REQUEST_SIZE_MATCH(xDMXQueryVersionReq);

    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.majorVersion);
        swapl(&rep.minorVersion);
        swapl(&rep.patchVersion);
    }
    WriteToClient(client, sizeof(xDMXQueryVersionReply), &rep);
    return Success;
}

static int
ProcDMXSync(ClientPtr client)
{
    xDMXSyncReply rep;

    REQUEST_SIZE_MATCH(xDMXSyncReq);

    dmxFlushPendingSyncs();

    rep = (xDMXSyncReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = 0
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
    }
    WriteToClient(client, sizeof(xDMXSyncReply), &rep);
    return Success;
}

static int
ProcDMXForceWindowCreation(ClientPtr client)
{
    xDMXForceWindowCreationReply rep;

    REQUEST(xDMXForceWindowCreationReq);
    WindowPtr pWin;

    REQUEST_SIZE_MATCH(xDMXForceWindowCreationReq);

#ifdef PANORAMIX
    if (!noPanoramiXExtension) {
        PanoramiXRes *win;
        int i;

        if (Success != dixLookupResourceByType((void **) &win,
                                               stuff->window, XRT_WINDOW,
                                               client, DixReadAccess))
            return -1;          /* BadWindow */

        FOR_NSCREENS(i) {
            if (Success != dixLookupWindow(&pWin, win->info[i].id, client,
                                           DixReadAccess))
                return -1;      /* BadWindow */

            dmxForceWindowCreation(pWin);
        }
        goto doreply;
    }
#endif

    if (Success != dixLookupWindow(&pWin, stuff->window, client, DixReadAccess))
        return -1;              /* BadWindow */

    dmxForceWindowCreation(pWin);
 doreply:
    dmxFlushPendingSyncs();
    rep = (xDMXForceWindowCreationReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = 0
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
    }
    WriteToClient(client, sizeof(xDMXForceWindowCreationReply), &rep);
    return Success;
}

static int
ProcDMXGetScreenCount(ClientPtr client)
{
    xDMXGetScreenCountReply rep;

    REQUEST_SIZE_MATCH(xDMXGetScreenCountReq);

    rep = (xDMXGetScreenCountReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .screenCount = dmxGetNumScreens()
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.screenCount);
    }
    WriteToClient(client, sizeof(xDMXGetScreenCountReply), &rep);
    return Success;
}

static int
ProcDMXGetScreenAttributes(ClientPtr client)
{
    REQUEST(xDMXGetScreenAttributesReq);
    xDMXGetScreenAttributesReply rep;
    int length;
    int paddedLength;
    DMXScreenAttributesRec attr;

    REQUEST_SIZE_MATCH(xDMXGetScreenAttributesReq);

    if (stuff->physicalScreen < 0
        || stuff->physicalScreen >= dmxGetNumScreens())
        return BadValue;

    if (!dmxGetScreenAttributes(stuff->physicalScreen, &attr))
        return BadValue;

    length = attr.displayName ? strlen(attr.displayName) : 0;
    paddedLength = pad_to_int32(length);

    rep = (xDMXGetScreenAttributesReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length =
            bytes_to_int32((sizeof(xDMXGetScreenAttributesReply) -
                            sizeof(xGenericReply))
                           + paddedLength),
        .displayNameLength = length,
        .logicalScreen = attr.logicalScreen,
        .screenWindowWidth = attr.screenWindowWidth,
        .screenWindowHeight = attr.screenWindowHeight,
        .screenWindowXoffset = attr.screenWindowXoffset,
        .screenWindowYoffset = attr.screenWindowYoffset,
        .rootWindowWidth = attr.rootWindowWidth,
        .rootWindowHeight = attr.rootWindowHeight,
        .rootWindowXoffset = attr.rootWindowXoffset,
        .rootWindowYoffset = attr.rootWindowYoffset,
        .rootWindowXorigin = attr.rootWindowXorigin,
        .rootWindowYorigin = attr.rootWindowYorigin
    };

    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.displayNameLength);
        swapl(&rep.logicalScreen);
        swaps(&rep.screenWindowWidth);
        swaps(&rep.screenWindowHeight);
        swaps(&rep.screenWindowXoffset);
        swaps(&rep.screenWindowYoffset);
        swaps(&rep.rootWindowWidth);
        swaps(&rep.rootWindowHeight);
        swaps(&rep.rootWindowXoffset);
        swaps(&rep.rootWindowYoffset);
        swaps(&rep.rootWindowXorigin);
        swaps(&rep.rootWindowYorigin);
    }
    WriteToClient(client, sizeof(xDMXGetScreenAttributesReply), &rep);
    if (length)
        WriteToClient(client, length, attr.displayName);
    return Success;
}

static int
ProcDMXChangeScreensAttributes(ClientPtr client)
{
    REQUEST(xDMXChangeScreensAttributesReq);
    xDMXChangeScreensAttributesReply rep;
    int status = DMX_BAD_XINERAMA;
    unsigned int mask = 0;
    unsigned int i;
    CARD32 *screen_list;
    CARD32 *mask_list;
    CARD32 *value_list;
    DMXScreenAttributesPtr attribs;
    int errorScreen = 0;
    unsigned int len;
    int ones = 0;

    REQUEST_AT_LEAST_SIZE(xDMXChangeScreensAttributesReq);
    len =
        client->req_len -
        bytes_to_int32(sizeof(xDMXChangeScreensAttributesReq));
    if (len < stuff->screenCount + stuff->maskCount)
        return BadLength;

    screen_list = (CARD32 *) (stuff + 1);
    mask_list = &screen_list[stuff->screenCount];
    value_list = &mask_list[stuff->maskCount];

    for (i = 0; i < stuff->maskCount; i++)
        ones += Ones(mask_list[i]);
    if (len != stuff->screenCount + stuff->maskCount + ones)
        return BadLength;

    if (!_DMXXineramaActive())
        goto noxinerama;

    if (!(attribs = xallocarray(stuff->screenCount, sizeof(*attribs))))
        return BadAlloc;

    for (i = 0; i < stuff->screenCount; i++) {
        int count;

        if (i < stuff->maskCount)
            mask = mask_list[i];
        dmxGetScreenAttributes(screen_list[i], &attribs[i]);
        count = dmxFetchScreenAttributes(mask, &attribs[i], value_list);
        value_list += count;
    }

#ifdef PANORAMIX
    status = dmxConfigureScreenWindows(stuff->screenCount,
                                       screen_list, attribs, &errorScreen);
#endif

    free(attribs);

    if (status == BadValue)
        return status;

 noxinerama:
    rep = (xDMXChangeScreensAttributesReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = status,
        .errorScreen = errorScreen
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
        swapl(&rep.errorScreen);
    }
    WriteToClient(client, sizeof(xDMXChangeScreensAttributesReply), &rep);
    return Success;
}

static int
ProcDMXAddScreen(ClientPtr client)
{
    REQUEST(xDMXAddScreenReq);
    xDMXAddScreenReply rep;
    int status = 0;
    CARD32 *value_list;
    DMXScreenAttributesRec attr;
    int count;
    char *name;
    int len;
    int paddedLength;

    REQUEST_AT_LEAST_SIZE(xDMXAddScreenReq);
    paddedLength = pad_to_int32(stuff->displayNameLength);
    len = client->req_len - bytes_to_int32(sizeof(xDMXAddScreenReq));
    if (len != Ones(stuff->valueMask) + paddedLength / 4)
        return BadLength;

    memset(&attr, 0, sizeof(attr));
    dmxGetScreenAttributes(stuff->physicalScreen, &attr);
    value_list = (CARD32 *) (stuff + 1);
    count = dmxFetchScreenAttributes(stuff->valueMask, &attr, value_list);

    if (!(name = malloc(stuff->displayNameLength + 1 + 4)))
        return BadAlloc;
    memcpy(name, &value_list[count], stuff->displayNameLength);
    name[stuff->displayNameLength] = '\0';
    attr.displayName = name;

    status = dmxAttachScreen(stuff->physicalScreen, &attr);

    free(name);

    rep = (xDMXAddScreenReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = status,
        .physicalScreen = stuff->physicalScreen
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
        swapl(&rep.physicalScreen);
    }
    WriteToClient(client, sizeof(xDMXAddScreenReply), &rep);
    return Success;
}

static int
ProcDMXRemoveScreen(ClientPtr client)
{
    REQUEST(xDMXRemoveScreenReq);
    xDMXRemoveScreenReply rep;
    int status = 0;

    REQUEST_SIZE_MATCH(xDMXRemoveScreenReq);

    status = dmxDetachScreen(stuff->physicalScreen);

    rep = (xDMXRemoveScreenReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = status
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
    }
    WriteToClient(client, sizeof(xDMXRemoveScreenReply), &rep);
    return Success;
}

#ifdef PANORAMIX
static int
dmxPopulatePanoramiX(ClientPtr client, Window window,
                     CARD32 *screens, CARD32 *windows,
                     xRectangle *pos, xRectangle *vis)
{
    WindowPtr pWin;
    PanoramiXRes *win;
    int i;
    int count = 0;
    DMXWindowAttributesRec attr;

    if (Success != dixLookupResourceByType((void **) &win,
                                           window, XRT_WINDOW,
                                           client, DixReadAccess))
        return -1;              /* BadWindow */

    FOR_NSCREENS(i) {
        if (Success != dixLookupWindow(&pWin, win->info[i].id, client,
                                       DixReadAccess))
            return -1;          /* BadWindow */
        if (dmxGetWindowAttributes(pWin, &attr)) {
            screens[count] = attr.screen;
            windows[count] = attr.window;
            pos[count] = attr.pos;
            vis[count] = attr.vis;
            ++count;            /* Only count existing windows */
        }
    }
    return count;
}
#endif

static int
dmxPopulate(ClientPtr client, Window window, CARD32 *screens,
            CARD32 *windows, xRectangle *pos, xRectangle *vis)
{
    WindowPtr pWin;
    DMXWindowAttributesRec attr;

#ifdef PANORAMIX
    if (!noPanoramiXExtension)
        return dmxPopulatePanoramiX(client, window, screens, windows, pos, vis);
#endif

    if (Success != dixLookupWindow(&pWin, window, client, DixReadAccess))
        return -1;              /* BadWindow */

    dmxGetWindowAttributes(pWin, &attr);
    *screens = attr.screen;
    *windows = attr.window;
    *pos = attr.pos;
    *vis = attr.vis;
    return 1;
}

static int
dmxMaxNumScreens(void)
{
#ifdef PANORAMIX
    if (!noPanoramiXExtension)
        return PanoramiXNumScreens;
#endif
    return 1;
}

static int
ProcDMXGetWindowAttributes(ClientPtr client)
{
    REQUEST(xDMXGetWindowAttributesReq);
    xDMXGetWindowAttributesReply rep;
    int i;
    CARD32 *screens;
    CARD32 *windows;
    xRectangle *pos, *vis;
    int count = dmxMaxNumScreens();

    REQUEST_SIZE_MATCH(xDMXGetWindowAttributesReq);

    if (!(screens = xallocarray(count, sizeof(*screens))))
        return BadAlloc;
    if (!(windows = xallocarray(count, sizeof(*windows)))) {
        free(screens);
        return BadAlloc;
    }
    if (!(pos = xallocarray(count, sizeof(*pos)))) {
        free(windows);
        free(screens);
        return BadAlloc;
    }
    if (!(vis = xallocarray(count, sizeof(*vis)))) {
        free(pos);
        free(windows);
        free(screens);
        return BadAlloc;
    }

    if ((count = dmxPopulate(client, stuff->window, screens, windows,
                             pos, vis)) < 0) {
        free(vis);
        free(pos);
        free(windows);
        free(screens);
        return BadWindow;
    }

    rep = (xDMXGetWindowAttributesReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = count * 6,
        .screenCount = count
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.screenCount);
        for (i = 0; i < count; i++) {
            swapl(&screens[i]);
            swapl(&windows[i]);

            swaps(&pos[i].x);
            swaps(&pos[i].y);
            swaps(&pos[i].width);
            swaps(&pos[i].height);

            swaps(&vis[i].x);
            swaps(&vis[i].y);
            swaps(&vis[i].width);
            swaps(&vis[i].height);
        }
    }

    dmxFlushPendingSyncs();

    WriteToClient(client, sizeof(xDMXGetWindowAttributesReply), &rep);
    if (count) {
        WriteToClient(client, count * sizeof(*screens), screens);
        WriteToClient(client, count * sizeof(*windows), windows);
        WriteToClient(client, count * sizeof(*pos), pos);
        WriteToClient(client, count * sizeof(*vis), vis);
    }

    free(vis);
    free(pos);
    free(windows);
    free(screens);

    return Success;
}

static int
ProcDMXGetDesktopAttributes(ClientPtr client)
{
    xDMXGetDesktopAttributesReply rep;
    DMXDesktopAttributesRec attr;

    REQUEST_SIZE_MATCH(xDMXGetDesktopAttributesReq);

    dmxGetDesktopAttributes(&attr);

    rep = (xDMXGetDesktopAttributesReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .width = attr.width,
        .height = attr.height,
        .shiftX = attr.shiftX,
        .shiftY = attr.shiftY
    };

    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swaps(&rep.width);
        swaps(&rep.height);
        swaps(&rep.shiftX);
        swaps(&rep.shiftY);
    }
    WriteToClient(client, sizeof(xDMXGetDesktopAttributesReply), &rep);
    return Success;
}

static int
ProcDMXChangeDesktopAttributes(ClientPtr client)
{
    REQUEST(xDMXChangeDesktopAttributesReq);
    xDMXChangeDesktopAttributesReply rep;
    int status = DMX_BAD_XINERAMA;
    CARD32 *value_list;
    DMXDesktopAttributesRec attr;
    int len;

    REQUEST_AT_LEAST_SIZE(xDMXChangeDesktopAttributesReq);
    len = client->req_len - (sizeof(xDMXChangeDesktopAttributesReq) >> 2);
    if (len != Ones(stuff->valueMask))
        return BadLength;

    if (!_DMXXineramaActive())
        goto noxinerama;

    value_list = (CARD32 *) (stuff + 1);

    dmxGetDesktopAttributes(&attr);
    dmxFetchDesktopAttributes(stuff->valueMask, &attr, value_list);

#ifdef PANORAMIX
    status = dmxConfigureDesktop(&attr);
#endif
    if (status == BadValue)
        return status;

 noxinerama:
    rep = (xDMXChangeDesktopAttributesReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = status
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
    }
    WriteToClient(client, sizeof(xDMXChangeDesktopAttributesReply), &rep);
    return Success;
}

static int
ProcDMXGetInputCount(ClientPtr client)
{
    xDMXGetInputCountReply rep;

    REQUEST_SIZE_MATCH(xDMXGetInputCountReq);

    rep = (xDMXGetInputCountReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .inputCount = dmxGetInputCount()
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.inputCount);
    }
    WriteToClient(client, sizeof(xDMXGetInputCountReply), &rep);
    return Success;
}

static int
ProcDMXGetInputAttributes(ClientPtr client)
{
    REQUEST(xDMXGetInputAttributesReq);
    xDMXGetInputAttributesReply rep;
    int length;
    int paddedLength;
    DMXInputAttributesRec attr;

    REQUEST_SIZE_MATCH(xDMXGetInputAttributesReq);

    if (dmxGetInputAttributes(stuff->deviceId, &attr))
        return BadValue;

    length = attr.name ? strlen(attr.name) : 0;
    paddedLength = pad_to_int32(length);

    rep = (xDMXGetInputAttributesReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = bytes_to_int32(paddedLength),

        .inputType = attr.inputType,
        .physicalScreen = attr.physicalScreen,
        .physicalId = attr.physicalId,
        .nameLength = length,
        .isCore = attr.isCore,
        .sendsCore = attr.sendsCore,
        .detached = attr.detached
    };

    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.inputType);
        swapl(&rep.physicalScreen);
        swapl(&rep.physicalId);
        swapl(&rep.nameLength);
    }
    WriteToClient(client, sizeof(xDMXGetInputAttributesReply), &rep);
    if (length)
        WriteToClient(client, length, attr.name);
    return Success;
}

static int
ProcDMXAddInput(ClientPtr client)
{
    REQUEST(xDMXAddInputReq);
    xDMXAddInputReply rep;
    int status = 0;
    CARD32 *value_list;
    DMXInputAttributesRec attr;
    int count;
    char *name;
    int len;
    int paddedLength;
    int id = -1;

    REQUEST_AT_LEAST_SIZE(xDMXAddInputReq);
    paddedLength = pad_to_int32(stuff->displayNameLength);
    len = client->req_len - (sizeof(xDMXAddInputReq) >> 2);
    if (len != Ones(stuff->valueMask) + paddedLength / 4)
        return BadLength;

    memset(&attr, 0, sizeof(attr));
    value_list = (CARD32 *) (stuff + 1);
    count = dmxFetchInputAttributes(stuff->valueMask, &attr, value_list);

    if (!(name = malloc(stuff->displayNameLength + 1 + 4)))
        return BadAlloc;
    memcpy(name, &value_list[count], stuff->displayNameLength);
    name[stuff->displayNameLength] = '\0';
    attr.name = name;

    status = dmxAddInput(&attr, &id);

    free(name);

    if (status)
        return status;

    rep = (xDMXAddInputReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = status,
        .physicalId = id
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
        swapl(&rep.physicalId);
    }
    WriteToClient(client, sizeof(xDMXAddInputReply), &rep);
    return Success;
}

static int
ProcDMXRemoveInput(ClientPtr client)
{
    REQUEST(xDMXRemoveInputReq);
    xDMXRemoveInputReply rep;
    int status = 0;

    REQUEST_SIZE_MATCH(xDMXRemoveInputReq);

    status = dmxRemoveInput(stuff->physicalId);

    if (status)
        return status;

    rep = (xDMXRemoveInputReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .length = 0,
        .status = status
    };
    if (client->swapped) {
        swaps(&rep.sequenceNumber);
        swapl(&rep.length);
        swapl(&rep.status);
    }
    WriteToClient(client, sizeof(xDMXRemoveInputReply), &rep);
    return Success;
}

static int
ProcDMXDispatch(ClientPtr client)
{
    REQUEST(xReq);

    switch (stuff->data) {
    case X_DMXQueryVersion:
        return ProcDMXQueryVersion(client);
    case X_DMXSync:
        return ProcDMXSync(client);
    case X_DMXForceWindowCreation:
        return ProcDMXForceWindowCreation(client);
    case X_DMXGetScreenCount:
        return ProcDMXGetScreenCount(client);
    case X_DMXGetScreenAttributes:
        return ProcDMXGetScreenAttributes(client);
    case X_DMXChangeScreensAttributes:
        return ProcDMXChangeScreensAttributes(client);
    case X_DMXAddScreen:
        return ProcDMXAddScreen(client);
    case X_DMXRemoveScreen:
        return ProcDMXRemoveScreen(client);
    case X_DMXGetWindowAttributes:
        return ProcDMXGetWindowAttributes(client);
    case X_DMXGetDesktopAttributes:
        return ProcDMXGetDesktopAttributes(client);
    case X_DMXChangeDesktopAttributes:
        return ProcDMXChangeDesktopAttributes(client);
    case X_DMXGetInputCount:
        return ProcDMXGetInputCount(client);
    case X_DMXGetInputAttributes:
        return ProcDMXGetInputAttributes(client);
    case X_DMXAddInput:
        return ProcDMXAddInput(client);
    case X_DMXRemoveInput:
        return ProcDMXRemoveInput(client);

    case X_DMXGetScreenInformationDEPRECATED:
    case X_DMXForceWindowCreationDEPRECATED:
    case X_DMXReconfigureScreenDEPRECATED:
        return BadImplementation;

    default:
        return BadRequest;
    }
}

static int _X_COLD
SProcDMXQueryVersion(ClientPtr client)
{
    REQUEST(xDMXQueryVersionReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXQueryVersionReq);
    return ProcDMXQueryVersion(client);
}

static int _X_COLD
SProcDMXSync(ClientPtr client)
{
    REQUEST(xDMXSyncReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXSyncReq);
    return ProcDMXSync(client);
}

static int _X_COLD
SProcDMXForceWindowCreation(ClientPtr client)
{
    REQUEST(xDMXForceWindowCreationReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXForceWindowCreationReq);
    swapl(&stuff->window);
    return ProcDMXForceWindowCreation(client);
}

static int _X_COLD
SProcDMXGetScreenCount(ClientPtr client)
{
    REQUEST(xDMXGetScreenCountReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXGetScreenCountReq);
    return ProcDMXGetScreenCount(client);
}

static int _X_COLD
SProcDMXGetScreenAttributes(ClientPtr client)
{
    REQUEST(xDMXGetScreenAttributesReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXGetScreenAttributesReq);
    swapl(&stuff->physicalScreen);
    return ProcDMXGetScreenAttributes(client);
}

static int _X_COLD
SProcDMXChangeScreensAttributes(ClientPtr client)
{
    REQUEST(xDMXChangeScreensAttributesReq);

    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xDMXGetScreenAttributesReq);
    swapl(&stuff->screenCount);
    swapl(&stuff->maskCount);
    SwapRestL(stuff);
    return ProcDMXGetScreenAttributes(client);
}

static int _X_COLD
SProcDMXAddScreen(ClientPtr client)
{
    int paddedLength;

    REQUEST(xDMXAddScreenReq);

    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xDMXAddScreenReq);
    swapl(&stuff->displayNameLength);
    swapl(&stuff->valueMask);
    paddedLength = pad_to_int32(stuff->displayNameLength);
    SwapLongs((CARD32 *) (stuff + 1), LengthRestL(stuff) - paddedLength / 4);
    return ProcDMXAddScreen(client);
}

static int _X_COLD
SProcDMXRemoveScreen(ClientPtr client)
{
    REQUEST(xDMXRemoveScreenReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXRemoveScreenReq);
    swapl(&stuff->physicalScreen);
    return ProcDMXRemoveScreen(client);
}

static int _X_COLD
SProcDMXGetWindowAttributes(ClientPtr client)
{
    REQUEST(xDMXGetWindowAttributesReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXGetWindowAttributesReq);
    swapl(&stuff->window);
    return ProcDMXGetWindowAttributes(client);
}

static int _X_COLD
SProcDMXGetDesktopAttributes(ClientPtr client)
{
    REQUEST(xDMXGetDesktopAttributesReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXGetDesktopAttributesReq);
    return ProcDMXGetDesktopAttributes(client);
}

static int _X_COLD
SProcDMXChangeDesktopAttributes(ClientPtr client)
{
    REQUEST(xDMXChangeDesktopAttributesReq);

    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xDMXChangeDesktopAttributesReq);
    swapl(&stuff->valueMask);
    SwapRestL(stuff);
    return ProcDMXChangeDesktopAttributes(client);
}

static int _X_COLD
SProcDMXGetInputCount(ClientPtr client)
{
    REQUEST(xDMXGetInputCountReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXGetInputCountReq);
    return ProcDMXGetInputCount(client);
}

static int _X_COLD
SProcDMXGetInputAttributes(ClientPtr client)
{
    REQUEST(xDMXGetInputAttributesReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXGetInputAttributesReq);
    swapl(&stuff->deviceId);
    return ProcDMXGetInputAttributes(client);
}

static int _X_COLD
SProcDMXAddInput(ClientPtr client)
{
    int paddedLength;

    REQUEST(xDMXAddInputReq);

    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xDMXAddInputReq);
    swapl(&stuff->displayNameLength);
    swapl(&stuff->valueMask);
    paddedLength = pad_to_int32(stuff->displayNameLength);
    SwapLongs((CARD32 *) (stuff + 1), LengthRestL(stuff) - paddedLength / 4);
    return ProcDMXAddInput(client);
}

static int _X_COLD
SProcDMXRemoveInput(ClientPtr client)
{
    REQUEST(xDMXRemoveInputReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDMXRemoveInputReq);
    swapl(&stuff->physicalId);
    return ProcDMXRemoveInput(client);
}

static int _X_COLD
SProcDMXDispatch(ClientPtr client)
{
    REQUEST(xReq);

    switch (stuff->data) {
    case X_DMXQueryVersion:
        return SProcDMXQueryVersion(client);
    case X_DMXSync:
        return SProcDMXSync(client);
    case X_DMXForceWindowCreation:
        return SProcDMXForceWindowCreation(client);
    case X_DMXGetScreenCount:
        return SProcDMXGetScreenCount(client);
    case X_DMXGetScreenAttributes:
        return SProcDMXGetScreenAttributes(client);
    case X_DMXChangeScreensAttributes:
        return SProcDMXChangeScreensAttributes(client);
    case X_DMXAddScreen:
        return SProcDMXAddScreen(client);
    case X_DMXRemoveScreen:
        return SProcDMXRemoveScreen(client);
    case X_DMXGetWindowAttributes:
        return SProcDMXGetWindowAttributes(client);
    case X_DMXGetDesktopAttributes:
        return SProcDMXGetDesktopAttributes(client);
    case X_DMXChangeDesktopAttributes:
        return SProcDMXChangeDesktopAttributes(client);
    case X_DMXGetInputCount:
        return SProcDMXGetInputCount(client);
    case X_DMXGetInputAttributes:
        return SProcDMXGetInputAttributes(client);
    case X_DMXAddInput:
        return SProcDMXAddInput(client);
    case X_DMXRemoveInput:
        return SProcDMXRemoveInput(client);

    case X_DMXGetScreenInformationDEPRECATED:
    case X_DMXForceWindowCreationDEPRECATED:
    case X_DMXReconfigureScreenDEPRECATED:
        return BadImplementation;

    default:
        return BadRequest;
    }
}

/** Initialize the extension. */
void
DMXExtensionInit(void)
{
    ExtensionEntry *extEntry;

    if ((extEntry = AddExtension(DMX_EXTENSION_NAME, 0, 0,
                                 ProcDMXDispatch, SProcDMXDispatch,
                                 NULL, StandardMinorOpcode)))
        DMXCode = extEntry->base;
}
