/*
 * Copyright (c) 1995  Jon Tombs
 * Copyright (c) 1995, 1996, 1999  XFree86 Inc
 * Copyright (c) 1998-2002 by The XFree86 Project, Inc.
 *
 * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 *
 * Written by Mark Vojkovich
 */

/*
 * This is quite literally just two files glued together:
 * hw/xfree86/common/xf86DGA.c is the first part, and
 * hw/xfree86/dixmods/extmod/xf86dga2.c is the second part.  One day, if
 * someone actually cares about DGA, it'd be nice to clean this up.  But trust
 * me, I am not that person.
 */

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <string.h>
#include <X11/X.h>
#include <X11/Xproto.h>
#include <X11/extensions/xf86dgaproto.h>

#include "dix/colormap_priv.h"
#include "dix/dix_priv.h"
#include "dix/eventconvert.h"
#include "dix/exevents_priv.h"
#include "dix/request_priv.h"
#include "dix/screen_hooks_priv.h"
#include "include/extinit.h"
#include "mi/mi_priv.h"

#include "xf86.h"
#include "xf86str.h"
#include "xf86Priv.h"
#include "dgaproc.h"
#include "dgaproc_priv.h"
#include "pixmapstr.h"
#include "inputstr.h"
#include "globals.h"
#include "servermd.h"
#include "micmap.h"
#include "xkbsrv.h"
#include "xf86Xinput.h"
#include "eventstr.h"
#include "xf86Extensions.h"
#include "misc.h"
#include "dixstruct.h"
#include "extnsionst.h"
#include "cursorstr.h"
#include "scrnintstr.h"
#include "swaprep.h"
#include "dgaproc.h"
#include "protocol-versions.h"

static DevPrivateKeyRec DGAScreenKeyRec;

#define DGAScreenKeyRegistered dixPrivateKeyRegistered(&DGAScreenKeyRec)

static void DGACloseScreen(CallbackListPtr *pcbl, ScreenPtr pScreen, void *unused);
static void DGADestroyColormap(ColormapPtr pmap);
static void DGAInstallColormap(ColormapPtr pmap);
static void DGAUninstallColormap(ColormapPtr pmap);
static void DGAHandleEvent(int screen_num, InternalEvent *event,
                           DeviceIntPtr device);

static void
 DGACopyModeInfo(DGAModePtr mode, XDGAModePtr xmode);

static unsigned char DGAReqCode = 0;
static int DGAErrorBase;
static int DGAEventBase;

#define DGA_GET_SCREEN_PRIV(pScreen) ((DGAScreenPtr) \
    dixLookupPrivate(&(pScreen)->devPrivates, &DGAScreenKeyRec))

typedef struct _FakedVisualList {
    Bool free;
    VisualPtr pVisual;
    struct _FakedVisualList *next;
} FakedVisualList;

typedef struct {
    ScrnInfoPtr pScrn;
    int numModes;
    DGAModePtr modes;
    DestroyColormapProcPtr DestroyColormap;
    InstallColormapProcPtr InstallColormap;
    UninstallColormapProcPtr UninstallColormap;
    DGADevicePtr current;
    DGAFunctionPtr funcs;
    int input;
    ClientPtr client;
    int pixmapMode;
    FakedVisualList *fakedVisuals;
    ColormapPtr dgaColormap;
    ColormapPtr savedColormap;
    Bool grabMouse;
    Bool grabKeyboard;
} DGAScreenRec, *DGAScreenPtr;

Bool
DGAInit(ScreenPtr pScreen, DGAFunctionPtr funcs, DGAModePtr modes, int num)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    DGAScreenPtr pScreenPriv;
    int i;

    if (!funcs || !funcs->SetMode || !funcs->OpenFramebuffer)
        return FALSE;

    if (!modes || num <= 0)
        return FALSE;

    if (!dixRegisterPrivateKey(&DGAScreenKeyRec, PRIVATE_SCREEN, 0))
        return FALSE;

    pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);

    if (!pScreenPriv) {
        if (!(pScreenPriv = calloc(1, sizeof(DGAScreenRec))))
            return FALSE;
        dixSetPrivate(&pScreen->devPrivates, &DGAScreenKeyRec, pScreenPriv);
        dixScreenHookClose(pScreen, DGACloseScreen);
        pScreenPriv->DestroyColormap = pScreen->DestroyColormap;
        pScreen->DestroyColormap = DGADestroyColormap;
        pScreenPriv->InstallColormap = pScreen->InstallColormap;
        pScreen->InstallColormap = DGAInstallColormap;
        pScreenPriv->UninstallColormap = pScreen->UninstallColormap;
        pScreen->UninstallColormap = DGAUninstallColormap;
    }

    pScreenPriv->pScrn = pScrn;
    pScreenPriv->numModes = num;
    pScreenPriv->modes = modes;
    pScreenPriv->current = NULL;

    pScreenPriv->funcs = funcs;
    pScreenPriv->input = 0;
    pScreenPriv->client = NULL;
    pScreenPriv->fakedVisuals = NULL;
    pScreenPriv->dgaColormap = NULL;
    pScreenPriv->savedColormap = NULL;
    pScreenPriv->grabMouse = FALSE;
    pScreenPriv->grabKeyboard = FALSE;

    for (i = 0; i < num; i++)
        modes[i].num = i + 1;

#ifdef XINERAMA
    if (!noPanoramiXExtension)
        for (i = 0; i < num; i++)
            modes[i].flags &= ~DGA_PIXMAP_AVAILABLE;
#endif /* XINERAMA */

    return TRUE;
}

/* DGAReInitModes allows the driver to re-initialize
 * the DGA mode list.
 */

Bool
DGAReInitModes(ScreenPtr pScreen, DGAModePtr modes, int num)
{
    DGAScreenPtr pScreenPriv;
    int i;

    /* No DGA? Ignore call (but don't make it look like it failed) */
    if (!DGAScreenKeyRegistered)
        return TRUE;

    pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);

    /* Same as above */
    if (!pScreenPriv)
        return TRUE;

    /* Can't do this while DGA is active */
    if (pScreenPriv->current)
        return FALSE;

    /* Quick sanity check */
    if (!num)
        modes = NULL;
    else if (!modes)
        num = 0;

    pScreenPriv->numModes = num;
    pScreenPriv->modes = modes;

    /* This practically disables DGA. So be it. */
    if (!num)
        return TRUE;

    for (i = 0; i < num; i++)
        modes[i].num = i + 1;

#ifdef XINERAMA
    if (!noPanoramiXExtension)
        for (i = 0; i < num; i++)
            modes[i].flags &= ~DGA_PIXMAP_AVAILABLE;
#endif /* XINERAMA */

    return TRUE;
}

static void
FreeMarkedVisuals(ScreenPtr pScreen)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);
    FakedVisualList *prev, *curr, *tmp;

    if (!pScreenPriv->fakedVisuals)
        return;

    prev = NULL;
    curr = pScreenPriv->fakedVisuals;

    while (curr) {
        if (curr->free) {
            tmp = curr;
            curr = curr->next;
            if (prev)
                prev->next = curr;
            else
                pScreenPriv->fakedVisuals = curr;
            free(tmp->pVisual);
            free(tmp);
        }
        else {
            prev = curr;
            curr = curr->next;
        }
    }
}

static void DGACloseScreen(CallbackListPtr *pcbl,
                           ScreenPtr pScreen, void *unused)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);
    if (!pScreenPriv)
        return;

    mieqSetHandler(ET_DGAEvent, NULL);
    pScreenPriv->pScrn->SetDGAMode(pScreenPriv->pScrn, 0, NULL);
    FreeMarkedVisuals(pScreen);

    dixScreenUnhookClose(pScreen, DGACloseScreen);
    pScreen->DestroyColormap = pScreenPriv->DestroyColormap;
    pScreen->InstallColormap = pScreenPriv->InstallColormap;
    pScreen->UninstallColormap = pScreenPriv->UninstallColormap;

    free(pScreenPriv);
    dixSetPrivate(&pScreen->devPrivates, &DGAScreenKeyRec, NULL);
}

static void
DGADestroyColormap(ColormapPtr pmap)
{
    ScreenPtr pScreen = pmap->pScreen;
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);
    VisualPtr pVisual = pmap->pVisual;

    if (pScreenPriv->fakedVisuals) {
        FakedVisualList *curr = pScreenPriv->fakedVisuals;

        while (curr) {
            if (curr->pVisual == pVisual) {
                /* We can't get rid of them yet since FreeColormap
                   still needs the pVisual during the cleanup */
                curr->free = TRUE;
                break;
            }
            curr = curr->next;
        }
    }

    if (pScreenPriv->DestroyColormap) {
        pScreen->DestroyColormap = pScreenPriv->DestroyColormap;
        (*pScreen->DestroyColormap) (pmap);
        pScreen->DestroyColormap = DGADestroyColormap;
    }
}

static void
DGAInstallColormap(ColormapPtr pmap)
{
    ScreenPtr pScreen = pmap->pScreen;
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);

    if (pScreenPriv->current && pScreenPriv->dgaColormap) {
        if (pmap != pScreenPriv->dgaColormap) {
            pScreenPriv->savedColormap = pmap;
            pmap = pScreenPriv->dgaColormap;
        }
    }

    pScreen->InstallColormap = pScreenPriv->InstallColormap;
    (*pScreen->InstallColormap) (pmap);
    pScreen->InstallColormap = DGAInstallColormap;
}

static void
DGAUninstallColormap(ColormapPtr pmap)
{
    ScreenPtr pScreen = pmap->pScreen;
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);

    if (pScreenPriv->current && pScreenPriv->dgaColormap) {
        if (pmap == pScreenPriv->dgaColormap) {
            pScreenPriv->dgaColormap = NULL;
        }
    }

    pScreen->UninstallColormap = pScreenPriv->UninstallColormap;
    (*pScreen->UninstallColormap) (pmap);
    pScreen->UninstallColormap = DGAUninstallColormap;
}

int
xf86SetDGAMode(ScrnInfoPtr pScrn, int num, DGADevicePtr devRet)
{
    ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
    DGAScreenPtr pScreenPriv;
    DGADevicePtr device;
    PixmapPtr pPix = NULL;
    DGAModePtr pMode = NULL;

    /* First check if DGAInit was successful on this screen */
    if (!DGAScreenKeyRegistered)
        return BadValue;
    pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);
    if (!pScreenPriv)
        return BadValue;

    if (!num) {
        if (pScreenPriv->current) {
            PixmapPtr oldPix = pScreenPriv->current->pPix;

            if (oldPix) {
                if (oldPix->drawable.id)
                    FreeResource(oldPix->drawable.id, X11_RESTYPE_NONE);
                else
                    dixDestroyPixmap(oldPix, 0);
            }
            free(pScreenPriv->current);
            pScreenPriv->current = NULL;
            pScrn->vtSema = TRUE;
            (*pScreenPriv->funcs->SetMode) (pScrn, NULL);
            if (pScreenPriv->savedColormap) {
                (*pScreen->InstallColormap) (pScreenPriv->savedColormap);
                pScreenPriv->savedColormap = NULL;
            }
            pScreenPriv->dgaColormap = NULL;
            (*pScrn->EnableDisableFBAccess) (pScrn, TRUE);

            FreeMarkedVisuals(pScreen);
        }

        pScreenPriv->grabMouse = FALSE;
        pScreenPriv->grabKeyboard = FALSE;

        return Success;
    }

    if (!pScrn->vtSema && !pScreenPriv->current)        /* Really switched away */
        return BadAlloc;

    if ((num > 0) && (num <= pScreenPriv->numModes))
        pMode = &(pScreenPriv->modes[num - 1]);
    else
        return BadValue;

    if (!(device = calloc(1, sizeof(DGADeviceRec))))
        return BadAlloc;

    if (!pScreenPriv->current) {
        Bool oldVTSema = pScrn->vtSema;

        pScrn->vtSema = FALSE;  /* kludge until we rewrite VT switching */
        (*pScrn->EnableDisableFBAccess) (pScrn, FALSE);
        pScrn->vtSema = oldVTSema;
    }

    if (!(*pScreenPriv->funcs->SetMode) (pScrn, pMode)) {
        free(device);
        return BadAlloc;
    }

    pScrn->currentMode = pMode->mode;

    if (!pScreenPriv->current && !pScreenPriv->input) {
        /* if it's multihead we need to warp the cursor off of
           our screen so it doesn't get trapped  */
    }

    pScrn->vtSema = FALSE;

    if (pScreenPriv->current) {
        PixmapPtr oldPix = pScreenPriv->current->pPix;

        if (oldPix) {
            if (oldPix->drawable.id)
                FreeResource(oldPix->drawable.id, X11_RESTYPE_NONE);
            else
                dixDestroyPixmap(oldPix, 0);
        }
        free(pScreenPriv->current);
        pScreenPriv->current = NULL;
    }

    if (pMode->flags & DGA_PIXMAP_AVAILABLE) {
        if ((pPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, pMode->depth, 0))) {
            (*pScreen->ModifyPixmapHeader) (pPix,
                                            pMode->pixmapWidth,
                                            pMode->pixmapHeight, pMode->depth,
                                            pMode->bitsPerPixel,
                                            pMode->bytesPerScanline,
                                            (void *) (pMode->address));
        }
    }

    devRet->mode = device->mode = pMode;
    devRet->pPix = device->pPix = pPix;
    pScreenPriv->current = device;
    pScreenPriv->pixmapMode = FALSE;
    pScreenPriv->grabMouse = TRUE;
    pScreenPriv->grabKeyboard = TRUE;

    mieqSetHandler(ET_DGAEvent, DGAHandleEvent);

    return Success;
}

static Bool
DGAChangePixmapMode(int index, int *x, int *y, int mode)
{
    DGAScreenPtr pScreenPriv;
    DGADevicePtr pDev;
    DGAModePtr pMode;
    PixmapPtr pPix;

    if (!DGAScreenKeyRegistered)
        return FALSE;

    pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    if (!pScreenPriv || !pScreenPriv->current || !pScreenPriv->current->pPix)
        return FALSE;

    pDev = pScreenPriv->current;
    pPix = pDev->pPix;
    pMode = pDev->mode;

    if (mode) {
        int shift = 2;

        if (*x > (pMode->pixmapWidth - pMode->viewportWidth))
            *x = pMode->pixmapWidth - pMode->viewportWidth;
        if (*y > (pMode->pixmapHeight - pMode->viewportHeight))
            *y = pMode->pixmapHeight - pMode->viewportHeight;

        switch (xf86Screens[index]->bitsPerPixel) {
        case 16:
            shift = 1;
            break;
        case 32:
            shift = 0;
            break;
        default:
            break;
        }

        if (BITMAP_SCANLINE_PAD == 64)
            shift++;

        *x = (*x >> shift) << shift;

        pPix->drawable.x = *x;
        pPix->drawable.y = *y;
        pPix->drawable.width = pMode->viewportWidth;
        pPix->drawable.height = pMode->viewportHeight;
    }
    else {
        pPix->drawable.x = 0;
        pPix->drawable.y = 0;
        pPix->drawable.width = pMode->pixmapWidth;
        pPix->drawable.height = pMode->pixmapHeight;
    }
    pPix->drawable.serialNumber = NEXT_SERIAL_NUMBER;
    pScreenPriv->pixmapMode = mode;

    return TRUE;
}

Bool
DGAScreenAvailable(ScreenPtr pScreen)
{
    if (!DGAScreenKeyRegistered)
        return FALSE;

    if (DGA_GET_SCREEN_PRIV(pScreen))
        return TRUE;
    return FALSE;
}

static Bool
DGAAvailable(int index)
{
    ScreenPtr pScreen;

    assert(index < MAXSCREENS);
    pScreen = screenInfo.screens[index];
    return DGAScreenAvailable(pScreen);
}

Bool
DGAActive(int index)
{
    DGAScreenPtr pScreenPriv;

    if (!DGAScreenKeyRegistered)
        return FALSE;

    pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    if (pScreenPriv && pScreenPriv->current)
        return TRUE;

    return FALSE;
}

/* Called by the extension to initialize a mode */

static int
DGASetMode(int index, int num, XDGAModePtr mode, PixmapPtr *pPix)
{
    ScrnInfoPtr pScrn = xf86Screens[index];
    DGADeviceRec device;
    int ret;

    /* We rely on the extension to check that DGA is available */

    ret = (*pScrn->SetDGAMode) (pScrn, num, &device);
    if ((ret == Success) && num) {
        DGACopyModeInfo(device.mode, mode);
        *pPix = device.pPix;
    }

    return ret;
}

/* Called from the extension to let the DDX know which events are requested */

static void
DGASelectInput(int index, ClientPtr client, long mask)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is available */
    pScreenPriv->client = client;
    pScreenPriv->input = mask;
}

static int
DGAGetViewportStatus(int index)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is active */

    if (!pScreenPriv->funcs->GetViewport)
        return 0;

    return (*pScreenPriv->funcs->GetViewport) (pScreenPriv->pScrn);
}

static int
DGASetViewport(int index, int x, int y, int mode)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    if (pScreenPriv->funcs->SetViewport)
        (*pScreenPriv->funcs->SetViewport) (pScreenPriv->pScrn, x, y, mode);
    return Success;
}

static int
BitsClear(CARD32 data)
{
    int bits = 0;
    CARD32 mask;

    for (mask = 1; mask; mask <<= 1) {
        if (!(data & mask))
            bits++;
        else
            break;
    }

    return bits;
}

static int
DGACreateColormap(int index, ClientPtr client, int id, int mode, int alloc)
{
    ScreenPtr pScreen = screenInfo.screens[index];
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);
    FakedVisualList *fvlp;
    VisualPtr pVisual;
    DGAModePtr pMode;
    ColormapPtr pmap;

    if (!mode || (mode > pScreenPriv->numModes))
        return BadValue;

    if ((alloc != AllocNone) && (alloc != AllocAll))
        return BadValue;

    pMode = &(pScreenPriv->modes[mode - 1]);

    if (!(pVisual = calloc(1, sizeof(VisualRec))))
        return BadAlloc;

    pVisual->vid = dixAllocServerXID();
    pVisual->class = pMode->visualClass;
    pVisual->nplanes = pMode->depth;
    pVisual->ColormapEntries = 1 << pMode->depth;
    pVisual->bitsPerRGBValue = (pMode->depth + 2) / 3;

    switch (pVisual->class) {
    case PseudoColor:
    case GrayScale:
    case StaticGray:
        pVisual->bitsPerRGBValue = 8;   /* not quite */
        pVisual->redMask = 0;
        pVisual->greenMask = 0;
        pVisual->blueMask = 0;
        pVisual->offsetRed = 0;
        pVisual->offsetGreen = 0;
        pVisual->offsetBlue = 0;
        break;
    case DirectColor:
    case TrueColor:
        pVisual->ColormapEntries = 1 << pVisual->bitsPerRGBValue;
        /* fall through */
    case StaticColor:
        pVisual->redMask = pMode->red_mask;
        pVisual->greenMask = pMode->green_mask;
        pVisual->blueMask = pMode->blue_mask;
        pVisual->offsetRed = BitsClear(pVisual->redMask);
        pVisual->offsetGreen = BitsClear(pVisual->greenMask);
        pVisual->offsetBlue = BitsClear(pVisual->blueMask);
    }

    if (!(fvlp = calloc(1, sizeof(FakedVisualList)))) {
        free(pVisual);
        return BadAlloc;
    }

    fvlp->free = FALSE;
    fvlp->pVisual = pVisual;
    fvlp->next = pScreenPriv->fakedVisuals;
    pScreenPriv->fakedVisuals = fvlp;

    LEGAL_NEW_RESOURCE(id, client);

    return dixCreateColormap(id, pScreen, pVisual, &pmap, alloc, client);
}

/*  Called by the extension to install a colormap on DGA active screens */

static void
DGAInstallCmap(ColormapPtr cmap)
{
    ScreenPtr pScreen = cmap->pScreen;
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);

    /* We rely on the extension to check that DGA is active */

    if (!pScreenPriv->dgaColormap)
        pScreenPriv->savedColormap = GetInstalledmiColormap(pScreen);

    pScreenPriv->dgaColormap = cmap;

    (*pScreen->InstallColormap) (cmap);
}

static int
DGASync(int index)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is active */

    if (pScreenPriv->funcs->Sync)
        (*pScreenPriv->funcs->Sync) (pScreenPriv->pScrn);

    return Success;
}

static int
DGAFillRect(int index, int x, int y, int w, int h, unsigned long color)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is active */

    if (pScreenPriv->funcs->FillRect &&
        (pScreenPriv->current->mode->flags & DGA_FILL_RECT)) {

        (*pScreenPriv->funcs->FillRect) (pScreenPriv->pScrn, x, y, w, h, color);
        return Success;
    }
    return BadMatch;
}

static int
DGABlitRect(int index, int srcx, int srcy, int w, int h, int dstx, int dsty)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is active */

    if (pScreenPriv->funcs->BlitRect &&
        (pScreenPriv->current->mode->flags & DGA_BLIT_RECT)) {

        (*pScreenPriv->funcs->BlitRect) (pScreenPriv->pScrn,
                                         srcx, srcy, w, h, dstx, dsty);
        return Success;
    }
    return BadMatch;
}

static int
DGABlitTransRect(int index,
                 int srcx, int srcy,
                 int w, int h, int dstx, int dsty, unsigned long color)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is active */

    if (pScreenPriv->funcs->BlitTransRect &&
        (pScreenPriv->current->mode->flags & DGA_BLIT_RECT_TRANS)) {

        (*pScreenPriv->funcs->BlitTransRect) (pScreenPriv->pScrn,
                                              srcx, srcy, w, h, dstx, dsty,
                                              color);
        return Success;
    }
    return BadMatch;
}

static int
DGAGetModes(int index)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is available */

    return pScreenPriv->numModes;
}

static int
DGAGetModeInfo(int index, XDGAModePtr mode, int num)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is available */

    if ((num <= 0) || (num > pScreenPriv->numModes))
        return BadValue;

    DGACopyModeInfo(&(pScreenPriv->modes[num - 1]), mode);

    return Success;
}

static void
DGACopyModeInfo(DGAModePtr mode, XDGAModePtr xmode)
{
    DisplayModePtr dmode = mode->mode;

    xmode->num = mode->num;
    xmode->name = dmode->name;
    xmode->VSync_num = (int) (dmode->VRefresh * 1000.0);
    xmode->VSync_den = 1000;
    xmode->flags = mode->flags;
    xmode->imageWidth = mode->imageWidth;
    xmode->imageHeight = mode->imageHeight;
    xmode->pixmapWidth = mode->pixmapWidth;
    xmode->pixmapHeight = mode->pixmapHeight;
    xmode->bytesPerScanline = mode->bytesPerScanline;
    xmode->byteOrder = mode->byteOrder;
    xmode->depth = mode->depth;
    xmode->bitsPerPixel = mode->bitsPerPixel;
    xmode->red_mask = mode->red_mask;
    xmode->green_mask = mode->green_mask;
    xmode->blue_mask = mode->blue_mask;
    xmode->visualClass = mode->visualClass;
    xmode->viewportWidth = mode->viewportWidth;
    xmode->viewportHeight = mode->viewportHeight;
    xmode->xViewportStep = mode->xViewportStep;
    xmode->yViewportStep = mode->yViewportStep;
    xmode->maxViewportX = mode->maxViewportX;
    xmode->maxViewportY = mode->maxViewportY;
    xmode->viewportFlags = mode->viewportFlags;
    xmode->reserved1 = mode->reserved1;
    xmode->reserved2 = mode->reserved2;
    xmode->offset = mode->offset;

    if (dmode->Flags & V_INTERLACE)
        xmode->flags |= DGA_INTERLACED;
    if (dmode->Flags & V_DBLSCAN)
        xmode->flags |= DGA_DOUBLESCAN;
}

Bool
DGAVTSwitch(void)
{
    DIX_FOR_EACH_SCREEN({
        /* Alternatively, this could send events to DGA clients */

        if (DGAScreenKeyRegistered) {
            DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(walkScreen);
            if (pScreenPriv && pScreenPriv->current)
                return FALSE;
        }
    });

    return TRUE;
}

Bool
DGAStealKeyEvent(DeviceIntPtr dev, int index, int key_code, int is_down)
{
    DGAScreenPtr pScreenPriv;
    DGAEvent event;

    if (!DGAScreenKeyRegistered)        /* no DGA */
        return FALSE;

    if (key_code < 8 || key_code > 255)
        return FALSE;

    pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    if (!pScreenPriv || !pScreenPriv->grabKeyboard)     /* no direct mode */
        return FALSE;

    event = (DGAEvent) {
        .header = ET_Internal,
        .type = ET_DGAEvent,
        .length = sizeof(event),
        .time = GetTimeInMillis(),
        .subtype = (is_down ? ET_KeyPress : ET_KeyRelease),
        .detail = key_code,
        .dx = 0,
        .dy = 0
    };
    mieqEnqueue(dev, (InternalEvent *) &event);

    return TRUE;
}

Bool
DGAStealMotionEvent(DeviceIntPtr dev, int index, int dx, int dy)
{
    DGAScreenPtr pScreenPriv;
    DGAEvent event;

    if (!DGAScreenKeyRegistered)        /* no DGA */
        return FALSE;

    pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    if (!pScreenPriv || !pScreenPriv->grabMouse)        /* no direct mode */
        return FALSE;

    event = (DGAEvent) {
        .header = ET_Internal,
        .type = ET_DGAEvent,
        .length = sizeof(event),
        .time = GetTimeInMillis(),
        .subtype = ET_Motion,
        .detail = 0,
        .dx = dx,
        .dy = dy
    };
    mieqEnqueue(dev, (InternalEvent *) &event);
    return TRUE;
}

Bool
DGAStealButtonEvent(DeviceIntPtr dev, int index, int button, int is_down)
{
    DGAScreenPtr pScreenPriv;
    DGAEvent event;

    if (!DGAScreenKeyRegistered)        /* no DGA */
        return FALSE;

    pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    if (!pScreenPriv || !pScreenPriv->grabMouse)
        return FALSE;

    event = (DGAEvent) {
        .header = ET_Internal,
        .type = ET_DGAEvent,
        .length = sizeof(event),
        .time = GetTimeInMillis(),
        .subtype = (is_down ? ET_ButtonPress : ET_ButtonRelease),
        .detail = button,
        .dx = 0,
        .dy = 0
    };
    mieqEnqueue(dev, (InternalEvent *) &event);

    return TRUE;
}

/* We have the power to steal or modify events that are about to get queued */

#define NoSuchEvent 0x80000000  /* so doesn't match NoEventMask */
static Mask filters[] = {
    NoSuchEvent,                /* 0 */
    NoSuchEvent,                /* 1 */
    KeyPressMask,               /* KeyPress */
    KeyReleaseMask,             /* KeyRelease */
    ButtonPressMask,            /* ButtonPress */
    ButtonReleaseMask,          /* ButtonRelease */
    PointerMotionMask,          /* MotionNotify (initial state) */
};

static void
DGAProcessKeyboardEvent(ScreenPtr pScreen, DGAEvent * event, DeviceIntPtr keybd)
{
    KeyClassPtr keyc = keybd->key;
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);
    DeviceIntPtr pointer = GetMaster(keybd, POINTER_OR_FLOAT);
    DeviceEvent ev = {
        .header = ET_Internal,
        .length = sizeof(ev),
        .detail.key = event->detail,
        .type = event->subtype,
        .root_x = 0,
        .root_y = 0,
        .corestate = XkbStateFieldFromRec(&keyc->xkbInfo->state)
    };
    ev.corestate |= pointer->button->state;

    UpdateDeviceState(keybd, &ev);

    if (!InputDevIsMaster(keybd))
        return;

    /*
     * Deliver the DGA event
     */
    if (pScreenPriv->client) {
        dgaEvent de = {
            .u.event.time = event->time,
            .u.event.dx = event->dx,
            .u.event.dy = event->dy,
            .u.event.screen = pScreen->myNum,
            .u.event.state = ev.corestate
        };
        de.u.u.type = DGAEventBase + GetCoreType(ev.type);
        de.u.u.detail = event->detail;

        /* If the DGA client has selected input, then deliver based on the usual filter */
        TryClientEvents(pScreenPriv->client, keybd, (xEvent *) &de, 1,
                        filters[ev.type], pScreenPriv->input, 0);
    }
    else {
        /* If the keyboard is actively grabbed, deliver a grabbed core event */
        if (keybd->deviceGrab.grab && !keybd->deviceGrab.fromPassiveGrab) {
            ev.detail.key = event->detail;
            ev.time = event->time;
            ev.root_x = event->dx;
            ev.root_y = event->dy;
            ev.corestate = event->state;
            ev.deviceid = keybd->id;
            DeliverGrabbedEvent((InternalEvent *) &ev, keybd, FALSE);
        }
    }
}

static void
DGAProcessPointerEvent(ScreenPtr pScreen, DGAEvent * event, DeviceIntPtr mouse)
{
    ButtonClassPtr butc = mouse->button;
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);
    DeviceIntPtr master = GetMaster(mouse, MASTER_KEYBOARD);
    DeviceEvent ev = {
        .header = ET_Internal,
        .length = sizeof(ev),
        .detail.key = event->detail,
        .type = event->subtype,
        .corestate = butc ? butc->state : 0
    };

    if (master && master->key)
        ev.corestate |= XkbStateFieldFromRec(&master->key->xkbInfo->state);

    UpdateDeviceState(mouse, &ev);

    if (!InputDevIsMaster(mouse))
        return;

    /*
     * Deliver the DGA event
     */
    if (pScreenPriv->client) {
        int coreEquiv = GetCoreType(ev.type);
        dgaEvent de = {
            .u.event.time = event->time,
            .u.event.dx = event->dx,
            .u.event.dy = event->dy,
            .u.event.screen = pScreen->myNum,
            .u.event.state = ev.corestate
        };
        de.u.u.type = DGAEventBase + coreEquiv;
        de.u.u.detail = event->detail;

        /* If the DGA client has selected input, then deliver based on the usual filter */
        TryClientEvents(pScreenPriv->client, mouse, (xEvent *) &de, 1,
                        filters[coreEquiv], pScreenPriv->input, 0);
    }
    else {
        /* If the pointer is actively grabbed, deliver a grabbed core event */
        if (mouse->deviceGrab.grab && !mouse->deviceGrab.fromPassiveGrab) {
            ev.detail.button = event->detail;
            ev.time = event->time;
            ev.root_x = event->dx;
            ev.root_y = event->dy;
            ev.corestate = event->state;
            /* DGA is core only, so valuators.data doesn't actually matter.
             * Mask must be set for EventToCore to create motion events. */
            SetBit(ev.valuators.mask, 0);
            SetBit(ev.valuators.mask, 1);
            DeliverGrabbedEvent((InternalEvent *) &ev, mouse, FALSE);
        }
    }
}

static Bool
DGAOpenFramebuffer(int index,
                   char **name,
                   unsigned char **mem, int *size, int *offset, int *flags)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is available */

    return (*pScreenPriv->funcs->OpenFramebuffer) (pScreenPriv->pScrn,
                                                   name, mem, size, offset,
                                                   flags);
}

static void
DGACloseFramebuffer(int index)
{
    DGAScreenPtr pScreenPriv = DGA_GET_SCREEN_PRIV(screenInfo.screens[index]);

    /* We rely on the extension to check that DGA is available */
    if (pScreenPriv->funcs->CloseFramebuffer)
        (*pScreenPriv->funcs->CloseFramebuffer) (pScreenPriv->pScrn);
}

static void
DGAHandleEvent(int screen_num, InternalEvent *ev, DeviceIntPtr device)
{
    DGAEvent *event = &ev->dga_event;
    ScreenPtr pScreen = screenInfo.screens[screen_num];
    DGAScreenPtr pScreenPriv;

    /* no DGA */
    if (!DGAScreenKeyRegistered || noXFree86DGAExtension)
	return;
    pScreenPriv = DGA_GET_SCREEN_PRIV(pScreen);

    /* DGA not initialized on this screen */
    if (!pScreenPriv)
        return;

    switch (event->subtype) {
    case KeyPress:
    case KeyRelease:
        DGAProcessKeyboardEvent(pScreen, event, device);
        break;
    case MotionNotify:
    case ButtonPress:
    case ButtonRelease:
        DGAProcessPointerEvent(pScreen, event, device);
        break;
    default:
        break;
    }
}

static void XDGAResetProc(ExtensionEntry * extEntry);

static void DGAClientStateChange(CallbackListPtr *, void *, void *);

static DevPrivateKeyRec DGAScreenPrivateKeyRec;

#define DGAScreenPrivateKey (&DGAScreenPrivateKeyRec)
#define DGAScreenPrivateKeyRegistered (DGAScreenPrivateKeyRec.initialized)
static DevPrivateKeyRec DGAClientPrivateKeyRec;

#define DGAClientPrivateKey (&DGAClientPrivateKeyRec)
static int DGACallbackRefCount = 0;

/* This holds the client's version information */
typedef struct {
    int major;
    int minor;
} DGAPrivRec, *DGAPrivPtr;

#define DGA_GETCLIENT(idx) ((ClientPtr) \
    dixLookupPrivate(&screenInfo.screens[idx]->devPrivates, DGAScreenPrivateKey))
#define DGA_SETCLIENT(idx,p) \
    dixSetPrivate(&screenInfo.screens[idx]->devPrivates, DGAScreenPrivateKey, p)

#define DGA_GETPRIV(c) ((DGAPrivPtr) \
    dixLookupPrivate(&(c)->devPrivates, DGAClientPrivateKey))
#define DGA_SETPRIV(c,p) \
    dixSetPrivate(&(c)->devPrivates, DGAClientPrivateKey, p)

static void
XDGAResetProc(ExtensionEntry * extEntry)
{
    DeleteCallback(&ClientStateCallback, DGAClientStateChange, NULL);
    DGACallbackRefCount = 0;
}

static int
ProcXDGAQueryVersion(ClientPtr client)
{
    REQUEST_SIZE_MATCH(xXDGAQueryVersionReq);

    xXDGAQueryVersionReply reply = {
        .majorVersion = SERVER_XDGA_MAJOR_VERSION,
        .minorVersion = SERVER_XDGA_MINOR_VERSION
    };

    return X_SEND_REPLY_SIMPLE(client, reply);
}

static int
ProcXDGAOpenFramebuffer(ClientPtr client)
{
    REQUEST(xXDGAOpenFramebufferReq);
    char *deviceName;
    int nameSize;

    REQUEST_SIZE_MATCH(xXDGAOpenFramebufferReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (!DGAAvailable(stuff->screen))
        return DGAErrorBase + XF86DGANoDirectVideoMode;

    xXDGAOpenFramebufferReply reply = { 0 };

    if (!DGAOpenFramebuffer(stuff->screen, &deviceName,
                            (unsigned char **) (&reply.mem1),
                            (int *) &reply.size,
                            (int *) &reply.offset,
                            (int *) &reply.extra)) {
        return BadAlloc;
    }

    nameSize = deviceName ? (strlen(deviceName) + 1) : 0;

    x_rpcbuf_t rpcbuf = { .swapped = client->swapped, .err_clear = TRUE };
    x_rpcbuf_write_CARD8s(&rpcbuf, (CARD8*)deviceName, nameSize);

    return X_SEND_REPLY_WITH_RPCBUF(client, reply, rpcbuf);
}

static int
ProcXDGACloseFramebuffer(ClientPtr client)
{
    REQUEST(xXDGACloseFramebufferReq);

    REQUEST_SIZE_MATCH(xXDGACloseFramebufferReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (!DGAAvailable(stuff->screen))
        return DGAErrorBase + XF86DGANoDirectVideoMode;

    DGACloseFramebuffer(stuff->screen);

    return Success;
}

static int
ProcXDGAQueryModes(ClientPtr client)
{
    int num;

    REQUEST(xXDGAQueryModesReq);
    xXDGAModeInfo info;
    XDGAModePtr mode;

    REQUEST_SIZE_MATCH(xXDGAQueryModesReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if ((!DGAAvailable(stuff->screen)) ||
        (!(num = DGAGetModes(stuff->screen))))
    {
        xXDGAQueryModesReply reply = { 0 };
        return X_SEND_REPLY_SIMPLE(client, reply);
    }

    if (!(mode = calloc(num, sizeof(XDGAModeRec))))
        return BadAlloc;

    for (int i = 0; i < num; i++)
        DGAGetModeInfo(stuff->screen, mode + i, i + 1);

    xXDGAQueryModesReply reply = {
        .number = num
    };

    x_rpcbuf_t rpcbuf = { .swapped = client->swapped, .err_clear = TRUE };

    for (int i = 0; i < num; i++) {
        size_t size = strlen(mode[i].name) + 1;

        info.byte_order = mode[i].byteOrder;
        info.depth = mode[i].depth;
        info.num = mode[i].num;
        info.bpp = mode[i].bitsPerPixel;
        info.name_size = (size + 3) & ~3L;
        info.vsync_num = mode[i].VSync_num;
        info.vsync_den = mode[i].VSync_den;
        info.flags = mode[i].flags;
        info.image_width = mode[i].imageWidth;
        info.image_height = mode[i].imageHeight;
        info.pixmap_width = mode[i].pixmapWidth;
        info.pixmap_height = mode[i].pixmapHeight;
        info.bytes_per_scanline = mode[i].bytesPerScanline;
        info.red_mask = mode[i].red_mask;
        info.green_mask = mode[i].green_mask;
        info.blue_mask = mode[i].blue_mask;
        info.visual_class = mode[i].visualClass;
        info.viewport_width = mode[i].viewportWidth;
        info.viewport_height = mode[i].viewportHeight;
        info.viewport_xstep = mode[i].xViewportStep;
        info.viewport_ystep = mode[i].yViewportStep;
        info.viewport_xmax = mode[i].maxViewportX;
        info.viewport_ymax = mode[i].maxViewportY;
        info.viewport_flags = mode[i].viewportFlags;
        info.reserved1 = mode[i].reserved1;
        info.reserved2 = mode[i].reserved2;

        x_rpcbuf_write_CARD8s(&rpcbuf, (CARD8*)&info, sz_xXDGAModeInfo);
        x_rpcbuf_write_CARD8s(&rpcbuf, (CARD8*)mode[i].name, size);
    }

    free(mode);

    return X_SEND_REPLY_WITH_RPCBUF(client, reply, rpcbuf);
}

static void
DGAClientStateChange(CallbackListPtr *pcbl, void *nulldata, void *calldata)
{
    NewClientInfoRec *pci = (NewClientInfoRec *) calldata;

    DIX_FOR_EACH_SCREEN({
        if (pci->client && (DGA_GETCLIENT(walkScreenIdx) == pci->client)) {
            if ((pci->client->clientState == ClientStateGone) ||
                (pci->client->clientState == ClientStateRetained))
            {
                XDGAModeRec mode;
                PixmapPtr pPix;

                DGA_SETCLIENT(walkScreenIdx, NULL);
                DGASelectInput(walkScreenIdx, NULL, 0);
                DGASetMode(walkScreenIdx, 0, &mode, &pPix);

                if (--DGACallbackRefCount == 0)
                    DeleteCallback(&ClientStateCallback, DGAClientStateChange, NULL);
            }
            break;
        }
    });
}

static int
ProcXDGASetMode(ClientPtr client)
{
    REQUEST(xXDGASetModeReq);
    XDGAModeRec mode;
    xXDGAModeInfo info;
    PixmapPtr pPix;
    ClientPtr owner;

    REQUEST_SIZE_MATCH(xXDGASetModeReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;
    owner = DGA_GETCLIENT(stuff->screen);

    if (!DGAAvailable(stuff->screen))
        return DGAErrorBase + XF86DGANoDirectVideoMode;

    if (owner && owner != client)
        return DGAErrorBase + XF86DGANoDirectVideoMode;

    xXDGASetModeReply reply = { 0 };

    if (!stuff->mode) {
        if (owner) {
            if (--DGACallbackRefCount == 0)
                DeleteCallback(&ClientStateCallback, DGAClientStateChange,
                               NULL);
        }
        DGA_SETCLIENT(stuff->screen, NULL);
        DGASelectInput(stuff->screen, NULL, 0);
        DGASetMode(stuff->screen, 0, &mode, &pPix);
        return X_SEND_REPLY_SIMPLE(client, reply);
    }

    if (Success != DGASetMode(stuff->screen, stuff->mode, &mode, &pPix))
        return BadValue;

    if (!owner) {
        if (DGACallbackRefCount++ == 0)
            AddCallback(&ClientStateCallback, DGAClientStateChange, NULL);
    }

    DGA_SETCLIENT(stuff->screen, client);

    if (pPix) {
        if (AddResource(stuff->pid, X11_RESTYPE_PIXMAP, (void *) (pPix))) {
            pPix->drawable.id = (int) stuff->pid;
            reply.flags = DGA_PIXMAP_AVAILABLE;
        }
    }

    info.byte_order = mode.byteOrder;
    info.depth = mode.depth;
    info.num = mode.num;
    info.bpp = mode.bitsPerPixel;
    info.name_size = ((strlen(mode.name) + 1) + 3) & ~3L;
    info.vsync_num = mode.VSync_num;
    info.vsync_den = mode.VSync_den;
    info.flags = mode.flags;
    info.image_width = mode.imageWidth;
    info.image_height = mode.imageHeight;
    info.pixmap_width = mode.pixmapWidth;
    info.pixmap_height = mode.pixmapHeight;
    info.bytes_per_scanline = mode.bytesPerScanline;
    info.red_mask = mode.red_mask;
    info.green_mask = mode.green_mask;
    info.blue_mask = mode.blue_mask;
    info.visual_class = mode.visualClass;
    info.viewport_width = mode.viewportWidth;
    info.viewport_height = mode.viewportHeight;
    info.viewport_xstep = mode.xViewportStep;
    info.viewport_ystep = mode.yViewportStep;
    info.viewport_xmax = mode.maxViewportX;
    info.viewport_ymax = mode.maxViewportY;
    info.viewport_flags = mode.viewportFlags;
    info.reserved1 = mode.reserved1;
    info.reserved2 = mode.reserved2;

    x_rpcbuf_t rpcbuf = { .swapped = client->swapped, .err_clear = TRUE };
    x_rpcbuf_write_binary_pad(&rpcbuf, &info, sizeof(info));
    x_rpcbuf_write_string_0t_pad(&rpcbuf, mode.name);

    return X_SEND_REPLY_WITH_RPCBUF(client, reply, rpcbuf);
}

static int
ProcXDGASetViewport(ClientPtr client)
{
    REQUEST(xXDGASetViewportReq);

    REQUEST_SIZE_MATCH(xXDGASetViewportReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    DGASetViewport(stuff->screen, stuff->x, stuff->y, stuff->flags);

    return Success;
}

static int
ProcXDGAInstallColormap(ClientPtr client)
{
    ColormapPtr cmap;
    int rc;

    REQUEST(xXDGAInstallColormapReq);

    REQUEST_SIZE_MATCH(xXDGAInstallColormapReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    rc = dixLookupResourceByType((void **) &cmap, stuff->cmap, X11_RESTYPE_COLORMAP,
                                 client, DixInstallAccess);
    if (rc != Success)
        return rc;
    DGAInstallCmap(cmap);
    return Success;
}

static int
ProcXDGASelectInput(ClientPtr client)
{
    REQUEST(xXDGASelectInputReq);

    REQUEST_SIZE_MATCH(xXDGASelectInputReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    if (DGA_GETCLIENT(stuff->screen) == client)
        DGASelectInput(stuff->screen, client, stuff->mask);

    return Success;
}

static int
ProcXDGAFillRectangle(ClientPtr client)
{
    REQUEST(xXDGAFillRectangleReq);

    REQUEST_SIZE_MATCH(xXDGAFillRectangleReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    if (Success != DGAFillRect(stuff->screen, stuff->x, stuff->y,
                               stuff->width, stuff->height, stuff->color))
        return BadMatch;

    return Success;
}

static int
ProcXDGACopyArea(ClientPtr client)
{
    REQUEST(xXDGACopyAreaReq);

    REQUEST_SIZE_MATCH(xXDGACopyAreaReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    if (Success != DGABlitRect(stuff->screen, stuff->srcx, stuff->srcy,
                               stuff->width, stuff->height, stuff->dstx,
                               stuff->dsty))
        return BadMatch;

    return Success;
}

static int
ProcXDGACopyTransparentArea(ClientPtr client)
{
    REQUEST(xXDGACopyTransparentAreaReq);

    REQUEST_SIZE_MATCH(xXDGACopyTransparentAreaReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    if (Success != DGABlitTransRect(stuff->screen, stuff->srcx, stuff->srcy,
                                    stuff->width, stuff->height, stuff->dstx,
                                    stuff->dsty, stuff->key))
        return BadMatch;

    return Success;
}

static int
ProcXDGAGetViewportStatus(ClientPtr client)
{
    REQUEST(xXDGAGetViewportStatusReq);

    REQUEST_SIZE_MATCH(xXDGAGetViewportStatusReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    xXDGAGetViewportStatusReply reply = {
        .status = DGAGetViewportStatus(stuff->screen)
    };

    return X_SEND_REPLY_SIMPLE(client, reply);
}

static int
ProcXDGASync(ClientPtr client)
{
    REQUEST(xXDGASyncReq);

    REQUEST_SIZE_MATCH(xXDGASyncReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    xXDGASyncReply reply = { 0 };
    DGASync(stuff->screen);

    return X_SEND_REPLY_SIMPLE(client, reply);
}

static int
ProcXDGASetClientVersion(ClientPtr client)
{
    REQUEST(xXDGASetClientVersionReq);

    DGAPrivPtr pPriv;

    REQUEST_SIZE_MATCH(xXDGASetClientVersionReq);
    if ((pPriv = DGA_GETPRIV(client)) == NULL) {
        pPriv = calloc(1, sizeof(DGAPrivRec));
        /* XXX Need to look into freeing this */
        if (!pPriv)
            return BadAlloc;
        DGA_SETPRIV(client, pPriv);
    }
    pPriv->major = stuff->major;
    pPriv->minor = stuff->minor;

    return Success;
}

static int
ProcXDGAChangePixmapMode(ClientPtr client)
{
    REQUEST(xXDGAChangePixmapModeReq);
    int x, y;

    REQUEST_SIZE_MATCH(xXDGAChangePixmapModeReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    x = stuff->x;
    y = stuff->y;

    if (!DGAChangePixmapMode(stuff->screen, &x, &y, stuff->flags))
        return BadMatch;

    xXDGAChangePixmapModeReply reply = {
        .x = x,
        .y = y
    };

    return X_SEND_REPLY_SIMPLE(client, reply);
}

static int
ProcXDGACreateColormap(ClientPtr client)
{
    REQUEST(xXDGACreateColormapReq);
    int result;

    REQUEST_SIZE_MATCH(xXDGACreateColormapReq);

    ScreenPtr pScreen = dixGetScreenPtr(stuff->screen);
    if (!pScreen)
        return BadValue;

    if (DGA_GETCLIENT(stuff->screen) != client)
        return DGAErrorBase + XF86DGADirectNotActivated;

    if (!stuff->mode)
        return BadValue;

    result = DGACreateColormap(stuff->screen, client, stuff->id,
                               stuff->mode, stuff->alloc);
    if (result != Success)
        return result;

    return Success;
}

#if 0
#define DGA_REQ_DEBUG
#endif

#ifdef DGA_REQ_DEBUG
static char *dgaMinor[] = {
    "QueryVersion",
    "GetVideoLL",
    "DirectVideo",
    "GetViewPortSize",
    "SetViewPort",
    "GetVidPage",
    "SetVidPage",
    "InstallColormap",
    "QueryDirectVideo",
    "ViewPortChanged",
    "10",
    "11",
    "QueryModes",
    "SetMode",
    "SetViewport",
    "InstallColormap",
    "SelectInput",
    "FillRectangle",
    "CopyArea",
    "CopyTransparentArea",
    "GetViewportStatus",
    "Sync",
    "OpenFramebuffer",
    "CloseFramebuffer",
    "SetClientVersion",
    "ChangePixmapMode",
    "CreateColormap",
};
#endif

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

    if (!client->local)
        return DGAErrorBase + XF86DGAClientNotLocal;

#ifdef DGA_REQ_DEBUG
    if (stuff->data <= X_XDGACreateColormap)
        fprintf(stderr, "    DGA %s\n", dgaMinor[stuff->data]);
#endif

    switch (stuff->data) {
        /*
         * DGA2 Protocol
         */
    case X_XDGAQueryVersion:
        return ProcXDGAQueryVersion(client);
    case X_XDGAQueryModes:
        return ProcXDGAQueryModes(client);
    case X_XDGASetMode:
        return ProcXDGASetMode(client);
    case X_XDGAOpenFramebuffer:
        return ProcXDGAOpenFramebuffer(client);
    case X_XDGACloseFramebuffer:
        return ProcXDGACloseFramebuffer(client);
    case X_XDGASetViewport:
        return ProcXDGASetViewport(client);
    case X_XDGAInstallColormap:
        return ProcXDGAInstallColormap(client);
    case X_XDGASelectInput:
        return ProcXDGASelectInput(client);
    case X_XDGAFillRectangle:
        return ProcXDGAFillRectangle(client);
    case X_XDGACopyArea:
        return ProcXDGACopyArea(client);
    case X_XDGACopyTransparentArea:
        return ProcXDGACopyTransparentArea(client);
    case X_XDGAGetViewportStatus:
        return ProcXDGAGetViewportStatus(client);
    case X_XDGASync:
        return ProcXDGASync(client);
    case X_XDGASetClientVersion:
        return ProcXDGASetClientVersion(client);
    case X_XDGAChangePixmapMode:
        return ProcXDGAChangePixmapMode(client);
    case X_XDGACreateColormap:
        return ProcXDGACreateColormap(client);
    default:
        return BadRequest;
    }
}

void
XFree86DGAExtensionInit(void)
{
    ExtensionEntry *extEntry;

    if (!dixRegisterPrivateKey(&DGAClientPrivateKeyRec, PRIVATE_CLIENT, 0))
        return;

    if (!dixRegisterPrivateKey(&DGAScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
        return;

    if ((extEntry = AddExtension(XF86DGANAME,
                                 XF86DGANumberEvents,
                                 XF86DGANumberErrors,
                                 ProcXDGADispatch,
                                 ProcXDGADispatch,
                                 XDGAResetProc, StandardMinorOpcode))) {
        int i;

        DGAReqCode = (unsigned char) extEntry->base;
        DGAErrorBase = extEntry->errorBase;
        DGAEventBase = extEntry->eventBase;
        for (i = KeyPress; i <= MotionNotify; i++)
            SetCriticalEvent(DGAEventBase + i);
    }
}
