/** @file
 *
 * Linux Additions X11 graphics driver
 */

/*
 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 USA or visit http://www.sun.com if you need
 * additional information or have any questions.
 * --------------------------------------------------------------------
 *
 * This code is based on:
 *
 * X11 VESA driver
 *
 * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com)
 *
 * 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
 * CONECTIVA LINUX 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 Conectiva Linux shall
 * not be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization from
 * Conectiva Linux.
 *
 * Authors: Paulo César Pereira de Andrade <pcpa@conectiva.com.br>
 */

#ifdef DEBUG_michael
# define DEBUG_VIDEO 1
#endif
#ifdef DEBUG_VIDEO

#define TRACE \
do { \
    xf86Msg(X_INFO, __PRETTY_FUNCTION__); \
    xf86Msg(X_INFO, ": entering\n"); \
} while(0)
#define TRACE2 \
do { \
    xf86Msg(X_INFO, __PRETTY_FUNCTION__); \
    xf86Msg(X_INFO, ": leaving\n"); \
} while(0)
#define TRACE3(...) \
do { \
    xf86Msg(X_INFO, __PRETTY_FUNCTION__); \
    xf86Msg(X_INFO, __VA_ARGS__); \
} while(0)

#else  /* DEBUG_VIDEO not defined */

#define TRACE       do { } while(0)
#define TRACE2      do { } while(0)
#define TRACE3(...) do { } while(0)

#endif  /* DEBUG_VIDEO not defined */

#ifdef XFree86LOADER
# include "xorg-server.h"
#else
# ifdef HAVE_CONFIG_H
#  include "config.h"
# endif
#endif
#include "vboxvideo.h"
#include "version-generated.h"
#include <xf86.h>

/* All drivers initialising the SW cursor need this */
#include "mipointer.h"

/* All drivers implementing backing store need this */
#include "mibstore.h"

/* Colormap handling */
#include "micmap.h"
#include "xf86cmap.h"

/* DPMS */
/* #define DPMS_SERVER
#include "extensions/dpms.h" */

/* Mandatory functions */

static const OptionInfoRec * VBOXAvailableOptions(int chipid, int busid);
static void VBOXIdentify(int flags);
static Bool VBOXProbe(DriverPtr drv, int flags);
static Bool VBOXPreInit(ScrnInfoPtr pScrn, int flags);
static Bool VBOXScreenInit(int Index, ScreenPtr pScreen, int argc,
                           char **argv);
static Bool VBOXEnterVT(int scrnIndex, int flags);
static void VBOXLeaveVT(int scrnIndex, int flags);
static Bool VBOXCloseScreen(int scrnIndex, ScreenPtr pScreen);
static Bool VBOXSaveScreen(ScreenPtr pScreen, int mode);
static Bool VBOXSwitchMode(int scrnIndex, DisplayModePtr pMode, int flags);
static Bool VBOXSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode);
static void VBOXAdjustFrame(int scrnIndex, int x, int y, int flags);
static void VBOXFreeScreen(int scrnIndex, int flags);
static void VBOXFreeRec(ScrnInfoPtr pScrn);
static void VBOXDisplayPowerManagementSet(ScrnInfoPtr pScrn, int mode,
                                          int flags);

/* locally used functions */
static Bool VBOXMapVidMem(ScrnInfoPtr pScrn);
static void VBOXUnmapVidMem(ScrnInfoPtr pScrn);
static void VBOXLoadPalette(ScrnInfoPtr pScrn, int numColors,
                            int *indices,
                            LOCO *colors, VisualPtr pVisual);
static void SaveFonts(ScrnInfoPtr pScrn);
static void RestoreFonts(ScrnInfoPtr pScrn);
static Bool VBOXSaveRestore(ScrnInfoPtr pScrn,
                            vbeSaveRestoreFunction function);

/* Initialise DGA */

static Bool VBOXDGAInit(ScrnInfoPtr pScrn, ScreenPtr pScreen);

/*
 * This contains the functions needed by the server after loading the
 * driver module.  It must be supplied, and gets added the driver list by
 * the Module Setup funtion in the dynamic case.  In the static case a
 * reference to this is compiled in, and this requires that the name of
 * this DriverRec be an upper-case version of the driver name.
 */

_X_EXPORT DriverRec VBOXDRV = {
    VBOX_VERSION,
    VBOX_DRIVER_NAME,
    VBOXIdentify,
    VBOXProbe,
    VBOXAvailableOptions,
    NULL,
    0,
    NULL
};

/* Supported chipsets */
static SymTabRec VBOXChipsets[] =
{
    {VBOX_VESA_DEVICEID, "vbox"},
    {-1,	 NULL}
};

static PciChipsets VBOXPCIchipsets[] = {
  { VBOX_DEVICEID, VBOX_DEVICEID, RES_SHARED_VGA },
  { -1,		-1,		    RES_UNDEFINED },
};

typedef enum {
    OPTION_SHADOW_FB,
    OPTION_DFLT_REFRESH,
    OPTION_MODESET_CLEAR_SCREEN
} VBOXOpts;

/* No options for now */
static const OptionInfoRec VBOXOptions[] = {
    { -1,		NULL,		OPTV_NONE,	{0},	FALSE }
};

/*
 * List of symbols from other modules that this module references.  This
 * list is used to tell the loader that it is OK for symbols here to be
 * unresolved providing that it hasn't been told that they haven't been
 * told that they are essential via a call to xf86LoaderReqSymbols() or
 * xf86LoaderReqSymLists().  The purpose is this is to avoid warnings about
 * unresolved symbols that are not required.
 */
static const char *fbSymbols[] = {
    "fbPictureInit",
    "fbScreenInit",
    NULL
};

static const char *shadowfbSymbols[] = {
  "ShadowFBInit2",
  NULL
};

static const char *vbeSymbols[] = {
    "VBEExtendedInit",
    "VBEFindSupportedDepths",
    "VBEGetModeInfo",
    "VBEGetVBEInfo",
    "VBEGetVBEMode",
    "VBEPrintModes",
    "VBESaveRestore",
    "VBESetDisplayStart",
    "VBESetGetDACPaletteFormat",
    "VBESetGetLogicalScanlineLength",
    "VBESetGetPaletteData",
    "VBESetModeNames",
    "VBESetModeParameters",
    "VBESetVBEMode",
    "VBEValidateModes",
    "vbeDoEDID",
    "vbeFree",
    NULL
};

static const char *ramdacSymbols[] = {
    "xf86InitCursor",
    "xf86CreateCursorInfoRec",
    NULL
};

#ifdef XFree86LOADER
/* Module loader interface */
static MODULESETUPPROTO(vboxSetup);

static XF86ModuleVersionInfo vboxVersionRec =
{
    VBOX_DRIVER_NAME,
    "Sun Microsystems, Inc.",
    MODINFOSTRING1,
    MODINFOSTRING2,
    XORG_VERSION_CURRENT,
    1,                          /* Module major version. Xorg-specific */
    0,                          /* Module minor version. Xorg-specific */
    1,                          /* Module patchlevel. Xorg-specific */
    ABI_CLASS_VIDEODRV,	        /* This is a video driver */
    ABI_VIDEODRV_VERSION,
    MOD_CLASS_VIDEODRV,
    {0, 0, 0, 0}
};

/*
 * This data is accessed by the loader.  The name must be the module name
 * followed by "ModuleData".
 */
_X_EXPORT XF86ModuleData vboxvideoModuleData = { &vboxVersionRec, vboxSetup, NULL };

static pointer
vboxSetup(pointer Module, pointer Options, int *ErrorMajor, int *ErrorMinor)
{
    static Bool Initialised = FALSE;

    if (!Initialised)
    {
        Initialised = TRUE;
        xf86AddDriver(&VBOXDRV, Module, 0);
        LoaderRefSymLists(fbSymbols,
                          shadowfbSymbols,
                          vbeSymbols,
                          ramdacSymbols,
                          NULL);
        return (pointer)TRUE;
    }

    if (ErrorMajor)
        *ErrorMajor = LDR_ONCEONLY;
    return (NULL);
}

#endif  /* XFree86Loader defined */

static const OptionInfoRec *
VBOXAvailableOptions(int chipid, int busid)
{
    return (VBOXOptions);
}

static void
VBOXIdentify(int flags)
{
    xf86PrintChipsets(VBOX_NAME, "guest driver for VirtualBox", VBOXChipsets);
}

/*
 * This function is called once, at the start of the first server generation to
 * do a minimal probe for supported hardware.
 */

static Bool
VBOXProbe(DriverPtr drv, int flags)
{
    Bool foundScreen = FALSE;
    int numDevSections, numUsed;
    GDevPtr *devSections;
    int *usedChips;
    int i;

    /*
     * Find the config file Device sections that match this
     * driver, and return if there are none.
     */
    if ((numDevSections = xf86MatchDevice(VBOX_NAME,
					  &devSections)) <= 0)
	return (FALSE);

    /* PCI BUS */
    if (xf86GetPciVideoInfo()) {
	numUsed = xf86MatchPciInstances(VBOX_NAME, VBOX_VENDORID,
					VBOXChipsets, VBOXPCIchipsets,
					devSections, numDevSections,
					drv, &usedChips);
	if (numUsed > 0) {
	    if (flags & PROBE_DETECT)
		foundScreen = TRUE;
	    else {
		for (i = 0; i < numUsed; i++) {
		    ScrnInfoPtr pScrn = NULL;
		    /* Allocate a ScrnInfoRec  */
		    if ((pScrn = xf86ConfigPciEntity(pScrn,0,usedChips[i],
						     VBOXPCIchipsets,NULL,
						     NULL,NULL,NULL,NULL))) {
			pScrn->driverVersion = VBOX_VERSION;
			pScrn->driverName    = VBOX_DRIVER_NAME;
			pScrn->name	     = VBOX_NAME;
			pScrn->Probe	     = VBOXProbe;
                        pScrn->PreInit       = VBOXPreInit;
			pScrn->ScreenInit    = VBOXScreenInit;
			pScrn->SwitchMode    = VBOXSwitchMode;
			pScrn->AdjustFrame   = VBOXAdjustFrame;
			pScrn->EnterVT       = VBOXEnterVT;
			pScrn->LeaveVT       = VBOXLeaveVT;
			pScrn->FreeScreen    = VBOXFreeScreen;
			foundScreen = TRUE;
		    }
		}
	    }
	    xfree(usedChips);
	}
    }
    xfree(devSections);

    return (foundScreen);
}

static VBOXPtr
VBOXGetRec(ScrnInfoPtr pScrn)
{
    if (!pScrn->driverPrivate)
    {
        pScrn->driverPrivate = xcalloc(sizeof(VBOXRec), 1);
#if 0
        ((VBOXPtr)pScrn->driverPrivate)->vbox_fd = -1;
#endif
    }

    return ((VBOXPtr)pScrn->driverPrivate);
}

static void
VBOXFreeRec(ScrnInfoPtr pScrn)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);
#if 0
    xfree(pVBox->vbeInfo);
#endif
    xfree(pVBox->savedPal);
    xfree(pVBox->fonts);
    xfree(pScrn->driverPrivate);
    pScrn->driverPrivate = NULL;
}

/*
 * QUOTE from the XFree86 DESIGN document:
 *
 * The purpose of this function is to find out all the information
 * required to determine if the configuration is usable, and to initialise
 * those parts of the ScrnInfoRec that can be set once at the beginning of
 * the first server generation.
 *
 * (...)
 *
 * This includes probing for video memory, clocks, ramdac, and all other
 * HW info that is needed. It includes determining the depth/bpp/visual
 * and related info. It includes validating and determining the set of
 * video modes that will be used (and anything that is required to
 * determine that).
 *
 * This information should be determined in the least intrusive way
 * possible. The state of the HW must remain unchanged by this function.
 * Although video memory (including MMIO) may be mapped within this
 * function, it must be unmapped before returning.
 *
 * END QUOTE
 */

static Bool
VBOXPreInit(ScrnInfoPtr pScrn, int flags)
{
    VBOXPtr pVBox;
    Gamma gzeros = {0.0, 0.0, 0.0};
    rgb rzeros = {0, 0, 0};
    ClockRange *clockRanges;
    int i;
    DisplayModePtr m_prev;
    char *pcHostModeName = NULL;

    /* Are we really starting the server, or is this just a dummy run? */
    if (flags & PROBE_DETECT)
        return (FALSE);

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
               "VirtualBox guest additions video driver version "
               VBOX_VERSION_STRING "\n");

     /* Get our private data from the ScrnInfoRec structure. */
    pVBox = VBOXGetRec(pScrn);

    /* Initialise the guest library */
    vbox_init(pScrn->scrnIndex, pVBox);

    /* Entity information seems to mean bus information. */
    pVBox->pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
    if (pVBox->pEnt->location.type != BUS_PCI)
        return FALSE;

    /* The ramdac module is needed for the hardware cursor. */
    if (!xf86LoadSubModule(pScrn, "ramdac"))
        return FALSE;
    xf86LoaderReqSymLists(ramdacSymbols, NULL);

    /* We need the vbe module because we use VBE code to save and restore
       text mode, in order to keep our code simple. */
    if (!xf86LoadSubModule(pScrn, "vbe"))
        return (FALSE);
    xf86LoaderReqSymLists(vbeSymbols, NULL);

    /* The framebuffer module. */
    if (xf86LoadSubModule(pScrn, "fb") == NULL)
        return (FALSE);
    xf86LoaderReqSymLists(fbSymbols, NULL);

    if (!xf86LoadSubModule(pScrn, "shadowfb"))
        return FALSE;
    xf86LoaderReqSymLists(shadowfbSymbols, NULL);

    pVBox->pciInfo = xf86GetPciInfoForEntity(pVBox->pEnt->index);
    pVBox->pciTag = pciTag(pVBox->pciInfo->bus,
                           pVBox->pciInfo->device,
                           pVBox->pciInfo->func);

    /* Set up our ScrnInfoRec structure to describe our virtual
       capabilities to X. */

    pScrn->rgbBits = 8;

    /* Let's create a nice, capable virtual monitor. */
    pScrn->monitor = pScrn->confScreen->monitor;
    pScrn->monitor->DDC = NULL;
    pScrn->monitor->nHsync = 1;
    pScrn->monitor->hsync[0].lo = 1;
    pScrn->monitor->hsync[0].hi = 10000;
    pScrn->monitor->nVrefresh = 1;
    pScrn->monitor->vrefresh[0].lo = 1;
    pScrn->monitor->vrefresh[0].hi = 100;

    pScrn->chipset = "vbox";
    pScrn->progClock = TRUE;

    /* Determine the size of the VBox video RAM from PCI data*/
#if 0
    pScrn->videoRam = 1 << pVBox->pciInfo->size[0];
#endif
    /* Using the PCI information caused problems with non-powers-of-two
       sized video RAM configurations */
    pScrn->videoRam = inl(VBE_DISPI_IOPORT_DATA) / 1024;

    /* Set up clock information that will support all modes we need. */
    clockRanges = xnfcalloc(sizeof(ClockRange), 1);
    clockRanges->next = NULL;
    clockRanges->minClock = 1000;
    clockRanges->maxClock = 1000000000;
    clockRanges->clockIndex = -1;
    clockRanges->ClockMulFactor = 1;
    clockRanges->ClockDivFactor = 1;

    /* Query the host for the preferred resolution and colour depth */
    {
        uint32_t cx = 0, cy = 0, iDisplay = 0, cBits = 24;

        if (vboxGetDisplayChangeRequest(pScrn, &cx, &cy, &cBits, &iDisplay))
        {
            /* We only support 16 and 24 bits depth (i.e. 16 and 32bpp) */
            if (cBits != 16)
                cBits = 24;
            if ((0 != cx) && (0 != cy)) {
                cx -= cx % 8;
                pcHostModeName = XNFprintf("%dx%d", cx, cy);
            }
        }
        if (!xf86SetDepthBpp(pScrn, cBits, 0, 0, Support32bppFb))
            return FALSE;
    }
    if (pScrn->bitsPerPixel != 32 && pScrn->bitsPerPixel != 16)
    {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "The VBox additions only support 16 and 32bpp graphics modes\n");
        return FALSE;
    }
    xf86PrintDepthBpp(pScrn);

    /* To get around the problem of SUSE specifying a single, invalid mode in their
     * Xorg.conf by default, we add additional modes to the end of the user specified
     * list. This means that if all user modes are invalid, X will try our mode before
     * falling back to its standard mode list. */
    if (pScrn->display->modes == NULL)
    {
        i = 0;
        /* The user specified no modes at all. */
        pScrn->display->modes = xnfalloc(5 * sizeof(char*));
    }
    else
    {
        for (i = 0; pScrn->display->modes[i] != NULL; i++);
        pScrn->display->modes =   xnfrealloc(pScrn->display->modes, (i + 5)
                                * sizeof(char *));
    }
    /* Add additional modes to the end of the mode list in case the others are
       all invalid. */
    if (pcHostModeName != NULL)
    {
        pScrn->display->modes[i] = pcHostModeName;
        ++i;
    }
    if (vboxHostLikesVideoMode(pScrn, 1024, 768, pScrn->bitsPerPixel))
    {
        pScrn->display->modes[i] = "1024x768";
        ++i;
    }
    if (vboxHostLikesVideoMode(pScrn, 800, 600, pScrn->bitsPerPixel))
    {
        pScrn->display->modes[i] = "800x600";
        ++i;
    }
    /* A mode of last resort */
    pScrn->display->modes[i] = "640x480";
    ++i;
    pScrn->display->modes[i] = NULL;

    /* Create a builtin mode for every specified mode. This allows to specify arbitrary
     * screen resolutions */
    m_prev = NULL;
    for (i = 0; pScrn->display->modes[i] != NULL; i++)
    {
        DisplayModePtr m;
        int x = 0, y = 0;

        if (sscanf(pScrn->display->modes[i], "%dx%d", &x, &y) == 2)
        {
            m                = xnfcalloc(sizeof(DisplayModeRec), 1);
            m->status        = MODE_OK;
            m->type          = M_T_BUILTIN;
            /* VBox only supports screen widths which are a multiple of 8 */
            m->HDisplay      = (x + 7) & ~7;
            m->HSyncStart    = m->HDisplay + 2;
            m->HSyncEnd      = m->HDisplay + 4;
            m->HTotal        = m->HDisplay + 6;
            m->VDisplay      = y;
            m->VSyncStart    = m->VDisplay + 2;
            m->VSyncEnd      = m->VDisplay + 4;
            m->VTotal        = m->VDisplay + 6;
            m->Clock         = m->HTotal * m->VTotal * 60 / 1000; /* kHz */
            m->name          = strdup(pScrn->display->modes[i]);
            if (!m_prev)
                pScrn->modePool = m;
            else
                m_prev->next = m;
            m->prev = m_prev;
            m_prev  = m;
        }
    }

    /* Colour weight - we always call this, since we are always in
       truecolour. */
    if (!xf86SetWeight(pScrn, rzeros, rzeros))
        return (FALSE);

    /* visual init */
    if (!xf86SetDefaultVisual(pScrn, -1))
        return (FALSE);

    xf86SetGamma(pScrn, gzeros);

    /* Set a sane minimum mode size and the maximum allowed by the available VRAM */
    {
        unsigned maxSize, trySize = 512;

        do {
            maxSize = trySize;
            trySize += 128;
        } while (trySize * trySize * pScrn->bitsPerPixel / 8 < (unsigned)pScrn->videoRam * 1024);

        /* I don't know exactly what these are for (and they are only used in a couple
           of places in the X server code). */
        pScrn->display->virtualX = maxSize;
        pScrn->display->virtualY = maxSize;
        xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                   "The maximum supported resolution is currently %dx%d\n", maxSize, maxSize);
    }

    /* Filter out video modes not supported by the virtual hardware
       we described. */
    i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
                          pScrn->display->modes,
                          clockRanges, NULL, 64, pScrn->display->virtualX, 1,
                          64, pScrn->display->virtualY, 0, 0,
                          pScrn->videoRam * 1024, LOOKUP_BEST_REFRESH);

    if (i <= 0) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No usable graphics modes found.\n");
        return (FALSE);
    }
    xf86PruneDriverModes(pScrn);

    pScrn->currentMode = pScrn->modes;
    pScrn->displayWidth = pScrn->virtualX;

    xf86PrintModes(pScrn);

    /* Set display resolution.  Perhaps we should read this from the host. */
    xf86SetDpi(pScrn, 96, 96);

    if (pScrn->modes == NULL) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No graphics modes available\n");
        return (FALSE);
    }

    /* options */
    xf86CollectOptions(pScrn, NULL);
    if (!(pVBox->Options = xalloc(sizeof(VBOXOptions))))
        return FALSE;
    memcpy(pVBox->Options, VBOXOptions, sizeof(VBOXOptions));
    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pVBox->Options);

    /* Framebuffer-related setup */
    pScrn->bitmapBitOrder = BITMAP_BIT_ORDER;
    return (TRUE);
}

/*
 * QUOTE from the XFree86 DESIGN document:
 *
 * This is called at the start of each server generation.
 *
 * (...)
 *
 * Decide which operations need to be placed under resource access
 * control. (...) Map any video memory or other memory regions. (...)
 * Save the video card state. (...) Initialise the initial video
 * mode.
 *
 * End QUOTE.Initialise the initial video mode.
 */
static Bool
VBOXScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    VBOXPtr pVBox = VBOXGetRec(pScrn);
    VisualPtr visual;
    unsigned flags;

    /* We make use of the X11 VBE code to save and restore text mode, in
       order to keep our code simple. */
    if ((pVBox->pVbe = VBEExtendedInit(NULL, pVBox->pEnt->index,
                                       SET_BIOS_SCRATCH
                                       | RESTORE_BIOS_SCRATCH)) == NULL)
        return (FALSE);

    if (pVBox->mapPhys == 0) {
        pVBox->mapPhys = pVBox->pciInfo->memBase[0];
/*        pVBox->mapSize = 1 << pVBox->pciInfo->size[0]; */
        /* Using the PCI information caused problems with
           non-powers-of-two sized video RAM configurations */
        pVBox->mapSize = inl(VBE_DISPI_IOPORT_DATA);
        pVBox->mapOff = 0;
    }

    if (!VBOXMapVidMem(pScrn))
        return (FALSE);

    /* save current video state */
    VBOXSaveRestore(pScrn, MODE_SAVE);
    pVBox->savedPal = VBESetGetPaletteData(pVBox->pVbe, FALSE, 0, 256,
                                           NULL, FALSE, FALSE);

    /* set first video mode */
    if (!VBOXSetMode(pScrn, pScrn->currentMode))
        return FALSE;

    /* set the viewport */
    VBOXAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);

    /* Blank the screen for aesthetic reasons. */
    VBOXSaveScreen(pScreen, SCREEN_SAVER_ON);

    /* mi layer - reset the visual list (?)*/
    miClearVisualTypes();
    if (!xf86SetDefaultVisual(pScrn, -1))
        return (FALSE);
    if (!miSetVisualTypes(pScrn->depth, TrueColorMask,
                          pScrn->rgbBits, TrueColor))
        return (FALSE);
    if (!miSetPixmapDepths())
        return (FALSE);

    /* I checked in the sources, and XFree86 4.2 does seem to support
       this function for 32bpp. */
    if (!fbScreenInit(pScreen, pVBox->base,
                      pScrn->virtualX, pScrn->virtualY,
                      pScrn->xDpi, pScrn->yDpi,
                      pScrn->displayWidth, pScrn->bitsPerPixel))
        return (FALSE);

    /* Fixup RGB ordering */
    visual = pScreen->visuals + pScreen->numVisuals;
    while (--visual >= pScreen->visuals) {
        if ((visual->class | DynamicClass) == DirectColor) {
            visual->offsetRed   = pScrn->offset.red;
            visual->offsetGreen = pScrn->offset.green;
            visual->offsetBlue  = pScrn->offset.blue;
            visual->redMask     = pScrn->mask.red;
            visual->greenMask   = pScrn->mask.green;
            visual->blueMask    = pScrn->mask.blue;
        }
    }

    /* must be after RGB ordering fixed */
    fbPictureInit(pScreen, 0, 0);

    VBOXDGAInit(pScrn, pScreen);

    xf86SetBlackWhitePixels(pScreen);
    miInitializeBackingStore(pScreen);
    xf86SetBackingStore(pScreen);

    /* software cursor */
    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

    /* colourmap code - apparently, we need this even in Truecolour */
    if (!miCreateDefColormap(pScreen))
	return (FALSE);

    flags = CMAP_RELOAD_ON_MODE_SWITCH;

    if(!xf86HandleColormaps(pScreen, 256,
        8 /* DAC is switchable to 8 bits per primary color */,
        VBOXLoadPalette, NULL, flags))
        return (FALSE);

    pVBox->CloseScreen = pScreen->CloseScreen;
    pScreen->CloseScreen = VBOXCloseScreen;
    pScreen->SaveScreen = VBOXSaveScreen;

    /* However, we probably do want to support power management -
       even if we just use a dummy function. */
    xf86DPMSInit(pScreen, VBOXDisplayPowerManagementSet, 0);

    /* Report any unused options (only for the first generation) */
    if (serverGeneration == 1)
        xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);

    if (vbox_open (pScrn, pScreen, pVBox)) {
        if (vbox_cursor_init(pScreen) != TRUE)
            xf86DrvMsg(scrnIndex, X_ERROR,
                       "Unable to start the VirtualBox mouse pointer integration with the host system.\n");
        if (vboxEnableVbva(pScrn) == TRUE)
            xf86DrvMsg(scrnIndex, X_INFO,
                      "The VBox video extensions are now enabled.\n");
    }
    return (TRUE);
}

static Bool
VBOXEnterVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    if (!VBOXSetMode(pScrn, pScrn->currentMode))
        return FALSE;
    VBOXAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
    if (pVBox->useVbva == TRUE)
        vboxEnableVbva(pScrn);
    return TRUE;
}

static void
VBOXLeaveVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    VBOXSaveRestore(pScrn, MODE_RESTORE);
    if (pVBox->useVbva == TRUE)
        vboxDisableVbva(pScrn);
}

static Bool
VBOXCloseScreen(int scrnIndex, ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    if (pVBox->useVbva == TRUE)
        vboxDisableVbva(pScrn);
    if (pScrn->vtSema) {
	VBOXSaveRestore(xf86Screens[scrnIndex], MODE_RESTORE);
	if (pVBox->savedPal)
	    VBESetGetPaletteData(pVBox->pVbe, TRUE, 0, 256,
				 pVBox->savedPal, FALSE, TRUE);
	VBOXUnmapVidMem(pScrn);
    }
    if (pVBox->pDGAMode) {
	xfree(pVBox->pDGAMode);
	pVBox->pDGAMode = NULL;
	pVBox->nDGAMode = 0;
    }
    pScrn->vtSema = FALSE;

    pScreen->CloseScreen = pVBox->CloseScreen;
    return pScreen->CloseScreen(scrnIndex, pScreen);
}

static Bool
VBOXSwitchMode(int scrnIndex, DisplayModePtr pMode, int flags)
{
    ScrnInfoPtr pScrn;
    VBOXPtr pVBox;

    pScrn = xf86Screens[scrnIndex];  /* Why does X have three ways of refering to the screen? */
    pVBox = VBOXGetRec(pScrn);
    if (pVBox->useVbva == TRUE)
        if (vboxDisableVbva(pScrn) != TRUE)  /* This would be bad. */
            return FALSE;
    if (VBOXSetMode(pScrn, pMode) != TRUE)
        return FALSE;
    if (pVBox->useVbva == TRUE)
        if (vboxEnableVbva(pScrn) != TRUE)  /* Bad but not fatal */
            pVBox->useVbva = FALSE;
    return TRUE;
}

/* Set a graphics mode */
static Bool
VBOXSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode)
{
    int bpp = pScrn->depth == 24 ? 32 : 16;
    int xRes = pMode->HDisplay;
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    if (pScrn->virtualX * pScrn->virtualY * bpp / 8
        >= pScrn->videoRam * 1024)
    {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "Unable to set up a virtual screen size of %dx%d with %d Kb of video memory.  Please increase the video memory size.\n",
                   pScrn->virtualX, pScrn->virtualY, pScrn->videoRam);
        return FALSE;
    }
    /* We only support horizontal resolutions which are a multiple of 8.  Round down if
       necessary. */
    if (xRes % 8 != 0)
    {
        xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                   "VirtualBox only supports screen widths which are a multiple of 8.  Rounding down from %d to %d\n",
                   xRes, xRes - (xRes % 8));
        xRes = xRes - (xRes % 8);
    }

    /* We force a software cursor if the guests virtual resolution is different
     * to it's actual resolution, as in this case host and guest will disagree
     * about the pointer position. */
    if (   (pScrn->virtualX - xRes >= 8)
        || (pScrn->virtualY - pMode->VDisplay >= 8))
        pVBox->forceSWCursor = TRUE;
    else
        pVBox->forceSWCursor = FALSE;

    pScrn->vtSema = TRUE;
    /* Disable linear framebuffer mode before making changes to the resolution. */
    outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
    outw(VBE_DISPI_IOPORT_DATA,
         VBE_DISPI_DISABLED);
    /* Unlike the resolution, the depth is fixed for a given screen
       for the lifetime of the X session. */
    outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
    outw(VBE_DISPI_IOPORT_DATA, bpp);
    /* HDisplay and VDisplay are actually monitor information about
       the display part of the scanlines. */
    outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
    outw(VBE_DISPI_IOPORT_DATA, xRes);
    outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
    outw(VBE_DISPI_IOPORT_DATA, pMode->VDisplay);
    /* Enable linear framebuffer mode. */
    outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
    outw(VBE_DISPI_IOPORT_DATA,
         VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
    /* Set the virtual resolution.  We are still using VESA to control
       the virtual offset. */
    outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIRT_WIDTH);
    outw(VBE_DISPI_IOPORT_DATA, pScrn->displayWidth);
    return (TRUE);
}

static void
VBOXAdjustFrame(int scrnIndex, int x, int y, int flags)
{
    VBOXPtr pVBox = VBOXGetRec(xf86Screens[scrnIndex]);
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];

    pVBox->viewportX = x;
    pVBox->viewportY = y;
    /* If VBVA is enabled the graphics card will not notice the change. */
    if (pVBox->useVbva == TRUE)
        vboxDisableVbva(pScrn);
    VBESetDisplayStart(pVBox->pVbe, x, y, TRUE);
    if (pVBox->useVbva == TRUE)
        vboxEnableVbva(pScrn);
}

static void
VBOXFreeScreen(int scrnIndex, int flags)
{
    VBOXFreeRec(xf86Screens[scrnIndex]);
}

static Bool
VBOXMapVidMem(ScrnInfoPtr pScrn)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    if (pVBox->base != NULL)
        return (TRUE);

    pScrn->memPhysBase = pVBox->mapPhys;
    pScrn->fbOffset = pVBox->mapOff;

    pVBox->base = xf86MapPciMem(pScrn->scrnIndex,
                                VIDMEM_FRAMEBUFFER,
                                pVBox->pciTag, pVBox->mapPhys,
                                (unsigned) pVBox->mapSize);

    if (pVBox->base) {
        pScrn->memPhysBase = pVBox->mapPhys;
        pVBox->VGAbase = xf86MapDomainMemory(pScrn->scrnIndex, 0,
                                             pVBox->pciTag,
                                             0xa0000, 0x10000);
    }
    /* We need this for saving/restoring textmode */
    pVBox->ioBase = pScrn->domainIOBase;

    return (pVBox->base != NULL);
}

static void
VBOXUnmapVidMem(ScrnInfoPtr pScrn)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    if (pVBox->base == NULL)
        return;

    xf86UnMapVidMem(pScrn->scrnIndex, pVBox->base,
                    (unsigned) pVBox->mapSize);
    xf86UnMapVidMem(pScrn->scrnIndex, pVBox->VGAbase, 0x10000);
    pVBox->base = NULL;
}

static void
VBOXLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices,
		LOCO *colors, VisualPtr pVisual)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);
    int i, idx;
#define VBOXDACDelay()							    \
    do {								    \
	   (void)inb(pVBox->ioBase + VGA_IOBASE_COLOR + VGA_IN_STAT_1_OFFSET); \
	   (void)inb(pVBox->ioBase + VGA_IOBASE_COLOR + VGA_IN_STAT_1_OFFSET); \
    } while (0)

    for (i = 0; i < numColors; i++) {
	   idx = indices[i];
	   outb(pVBox->ioBase + VGA_DAC_WRITE_ADDR, idx);
	   VBOXDACDelay();
	   outb(pVBox->ioBase + VGA_DAC_DATA, colors[idx].red);
	   VBOXDACDelay();
	   outb(pVBox->ioBase + VGA_DAC_DATA, colors[idx].green);
	   VBOXDACDelay();
	   outb(pVBox->ioBase + VGA_DAC_DATA, colors[idx].blue);
	   VBOXDACDelay();
    }
}

/*
 * Just adapted from the std* functions in vgaHW.c
 */
static void
WriteAttr(VBOXPtr pVBox, int index, int value)
{
    (void) inb(pVBox->ioBase + VGA_IOBASE_COLOR + VGA_IN_STAT_1_OFFSET);

    index |= 0x20;
    outb(pVBox->ioBase + VGA_ATTR_INDEX, index);
    outb(pVBox->ioBase + VGA_ATTR_DATA_W, value);
}

static int
ReadAttr(VBOXPtr pVBox, int index)
{
    (void) inb(pVBox->ioBase + VGA_IOBASE_COLOR + VGA_IN_STAT_1_OFFSET);

    index |= 0x20;
    outb(pVBox->ioBase + VGA_ATTR_INDEX, index);
    return (inb(pVBox->ioBase + VGA_ATTR_DATA_R));
}

#define WriteMiscOut(value)	outb(pVBox->ioBase + VGA_MISC_OUT_W, value)
#define ReadMiscOut()		inb(pVBox->ioBase + VGA_MISC_OUT_R)
#define WriteSeq(index, value) \
        outb(pVBox->ioBase + VGA_SEQ_INDEX, (index));\
        outb(pVBox->ioBase + VGA_SEQ_DATA, value)

static int
ReadSeq(VBOXPtr pVBox, int index)
{
    outb(pVBox->ioBase + VGA_SEQ_INDEX, index);

    return (inb(pVBox->ioBase + VGA_SEQ_DATA));
}

#define WriteGr(index, value)				\
    outb(pVBox->ioBase + VGA_GRAPH_INDEX, index);	\
    outb(pVBox->ioBase + VGA_GRAPH_DATA, value)

static int
ReadGr(VBOXPtr pVBox, int index)
{
    outb(pVBox->ioBase + VGA_GRAPH_INDEX, index);

    return (inb(pVBox->ioBase + VGA_GRAPH_DATA));
}

#define WriteCrtc(index, value)						     \
    outb(pVBox->ioBase + (VGA_IOBASE_COLOR + VGA_CRTC_INDEX_OFFSET), index); \
    outb(pVBox->ioBase + (VGA_IOBASE_COLOR + VGA_CRTC_DATA_OFFSET), value)

static void
SeqReset(VBOXPtr pVBox, Bool start)
{
    if (start) {
	   WriteSeq(0x00, 0x01);		/* Synchronous Reset */
    }
    else {
	   WriteSeq(0x00, 0x03);		/* End Reset */
    }
}

static void
SaveFonts(ScrnInfoPtr pScrn)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);
    unsigned char miscOut, attr10, gr4, gr5, gr6, seq2, seq4, scrn;

    if (pVBox->fonts != NULL)
	return;

    /* If in graphics mode, don't save anything */
    attr10 = ReadAttr(pVBox, 0x10);
    if (attr10 & 0x01)
	return;

    pVBox->fonts = xalloc(16384);

    /* save the registers that are needed here */
    miscOut = ReadMiscOut();
    gr4 = ReadGr(pVBox, 0x04);
    gr5 = ReadGr(pVBox, 0x05);
    gr6 = ReadGr(pVBox, 0x06);
    seq2 = ReadSeq(pVBox, 0x02);
    seq4 = ReadSeq(pVBox, 0x04);

    /* Force into colour mode */
    WriteMiscOut(miscOut | 0x01);

    scrn = ReadSeq(pVBox, 0x01) | 0x20;
    SeqReset(pVBox, TRUE);
    WriteSeq(0x01, scrn);
    SeqReset(pVBox, FALSE);

    WriteAttr(pVBox, 0x10, 0x01);	/* graphics mode */

    /*font1 */
    WriteSeq(0x02, 0x04);	/* write to plane 2 */
    WriteSeq(0x04, 0x06);	/* enable plane graphics */
    WriteGr(0x04, 0x02);	/* read plane 2 */
    WriteGr(0x05, 0x00);	/* write mode 0, read mode 0 */
    WriteGr(0x06, 0x05);	/* set graphics */
    slowbcopy_frombus(pVBox->VGAbase, pVBox->fonts, 8192);

    /* font2 */
    WriteSeq(0x02, 0x08);	/* write to plane 3 */
    WriteSeq(0x04, 0x06);	/* enable plane graphics */
    WriteGr(0x04, 0x03);	/* read plane 3 */
    WriteGr(0x05, 0x00);	/* write mode 0, read mode 0 */
    WriteGr(0x06, 0x05);	/* set graphics */
    slowbcopy_frombus(pVBox->VGAbase, pVBox->fonts + 8192, 8192);

    scrn = ReadSeq(pVBox, 0x01) & ~0x20;
    SeqReset(pVBox, TRUE);
    WriteSeq(0x01, scrn);
    SeqReset(pVBox, FALSE);

    /* Restore clobbered registers */
    WriteAttr(pVBox, 0x10, attr10);
    WriteSeq(0x02, seq2);
    WriteSeq(0x04, seq4);
    WriteGr(0x04, gr4);
    WriteGr(0x05, gr5);
    WriteGr(0x06, gr6);
    WriteMiscOut(miscOut);
}

static void
RestoreFonts(ScrnInfoPtr pScrn)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);
    unsigned char miscOut, attr10, gr1, gr3, gr4, gr5, gr6, gr8, seq2, seq4, scrn;

    if (pVBox->fonts == NULL)
        return;

    /* save the registers that are needed here */
    miscOut = ReadMiscOut();
    attr10 = ReadAttr(pVBox, 0x10);
    gr1 = ReadGr(pVBox, 0x01);
    gr3 = ReadGr(pVBox, 0x03);
    gr4 = ReadGr(pVBox, 0x04);
    gr5 = ReadGr(pVBox, 0x05);
    gr6 = ReadGr(pVBox, 0x06);
    gr8 = ReadGr(pVBox, 0x08);
    seq2 = ReadSeq(pVBox, 0x02);
    seq4 = ReadSeq(pVBox, 0x04);

    /* Force into colour mode */
    WriteMiscOut(miscOut | 0x01);

    scrn = ReadSeq(pVBox, 0x01) & ~0x20;
    SeqReset(pVBox, TRUE);
    WriteSeq(0x01, scrn);
    SeqReset(pVBox, FALSE);

    WriteAttr(pVBox, 0x10, 0x01);	/* graphics mode */
    if (pScrn->depth == 4) {
	/* GJA */
	WriteGr(0x03, 0x00);	/* don't rotate, write unmodified */
	WriteGr(0x08, 0xFF);	/* write all bits in a byte */
	WriteGr(0x01, 0x00);	/* all planes come from CPU */
    }

    WriteSeq(0x02, 0x04);   /* write to plane 2 */
    WriteSeq(0x04, 0x06);   /* enable plane graphics */
    WriteGr(0x04, 0x02);    /* read plane 2 */
    WriteGr(0x05, 0x00);    /* write mode 0, read mode 0 */
    WriteGr(0x06, 0x05);    /* set graphics */
    slowbcopy_tobus(pVBox->fonts, pVBox->VGAbase, 8192);

    WriteSeq(0x02, 0x08);   /* write to plane 3 */
    WriteSeq(0x04, 0x06);   /* enable plane graphics */
    WriteGr(0x04, 0x03);    /* read plane 3 */
    WriteGr(0x05, 0x00);    /* write mode 0, read mode 0 */
    WriteGr(0x06, 0x05);    /* set graphics */
    slowbcopy_tobus(pVBox->fonts + 8192, pVBox->VGAbase, 8192);

    scrn = ReadSeq(pVBox, 0x01) & ~0x20;
    SeqReset(pVBox, TRUE);
    WriteSeq(0x01, scrn);
    SeqReset(pVBox, FALSE);

    /* restore the registers that were changed */
    WriteMiscOut(miscOut);
    WriteAttr(pVBox, 0x10, attr10);
    WriteGr(0x01, gr1);
    WriteGr(0x03, gr3);
    WriteGr(0x04, gr4);
    WriteGr(0x05, gr5);
    WriteGr(0x06, gr6);
    WriteGr(0x08, gr8);
    WriteSeq(0x02, seq2);
    WriteSeq(0x04, seq4);
}

static Bool
VBOXSaveScreen(ScreenPtr pScreen, int mode)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    VBOXPtr pVBox = VBOXGetRec(pScrn);
    Bool on = xf86IsUnblank(mode);

    if (on)
	SetTimeSinceLastInputEvent();

    if (pScrn->vtSema) {
	unsigned char scrn = ReadSeq(pVBox, 0x01);

	if (on)
	    scrn &= ~0x20;
	else
	    scrn |= 0x20;
	SeqReset(pVBox, TRUE);
 	WriteSeq(0x01, scrn);
	SeqReset(pVBox, FALSE);
    }

    return (TRUE);
}

Bool
VBOXSaveRestore(ScrnInfoPtr pScrn, vbeSaveRestoreFunction function)
{
    VBOXPtr pVBox;

    if (MODE_QUERY < 0 || function > MODE_RESTORE)
	return (FALSE);

    pVBox = VBOXGetRec(pScrn);


    /* Query amount of memory to save state */
    if (function == MODE_QUERY ||
	(function == MODE_SAVE && pVBox->state == NULL))
    {

	/* Make sure we save at least this information in case of failure */
	(void)VBEGetVBEMode(pVBox->pVbe, &pVBox->stateMode);
	SaveFonts(pScrn);

        if (!VBESaveRestore(pVBox->pVbe,function,(pointer)&pVBox->state,
                            &pVBox->stateSize,&pVBox->statePage))
            return FALSE;
    }

    /* Save/Restore Super VGA state */
    if (function != MODE_QUERY) {
        Bool retval = TRUE;

        if (function == MODE_RESTORE)
            memcpy(pVBox->state, pVBox->pstate,
                   (unsigned) pVBox->stateSize);

        if ((retval = VBESaveRestore(pVBox->pVbe,function,
                                     (pointer)&pVBox->state,
                                     &pVBox->stateSize,&pVBox->statePage))
             && function == MODE_SAVE)
        {
            /* don't rely on the memory not being touched */
            if (pVBox->pstate == NULL)
                pVBox->pstate = xalloc(pVBox->stateSize);
            memcpy(pVBox->pstate, pVBox->state,
                   (unsigned) pVBox->stateSize);
        }

        if (function == MODE_RESTORE)
        {
            VBESetVBEMode(pVBox->pVbe, pVBox->stateMode, NULL);
            RestoreFonts(pScrn);
        }

	if (!retval)
	    return (FALSE);

    }

    return (TRUE);
}

static void
VBOXDisplayPowerManagementSet(ScrnInfoPtr pScrn, int mode,
                int flags)
{
    /* VBox is always power efficient... */
}




/***********************************************************************
 * DGA stuff
 ***********************************************************************/
static Bool VBOXDGAOpenFramebuffer(ScrnInfoPtr pScrn, char **DeviceName,
				   unsigned char **ApertureBase,
				   int *ApertureSize, int *ApertureOffset,
				   int *flags);
static Bool VBOXDGASetMode(ScrnInfoPtr pScrn, DGAModePtr pDGAMode);
static void VBOXDGASetViewport(ScrnInfoPtr pScrn, int x, int y, int flags);

static Bool
VBOXDGAOpenFramebuffer(ScrnInfoPtr pScrn, char **DeviceName,
		       unsigned char **ApertureBase, int *ApertureSize,
		       int *ApertureOffset, int *flags)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    *DeviceName = NULL;		/* No special device */
    *ApertureBase = (unsigned char *)(long)(pVBox->mapPhys);
    *ApertureSize = pVBox->mapSize;
    *ApertureOffset = pVBox->mapOff;
    *flags = DGA_NEED_ROOT;

    return (TRUE);
}

static Bool
VBOXDGASetMode(ScrnInfoPtr pScrn, DGAModePtr pDGAMode)
{
    DisplayModePtr pMode;
    int scrnIdx = pScrn->pScreen->myNum;
    int frameX0, frameY0;

    if (pDGAMode) {
	pMode = pDGAMode->mode;
	frameX0 = frameY0 = 0;
    }
    else {
	if (!(pMode = pScrn->currentMode))
	    return (TRUE);

	frameX0 = pScrn->frameX0;
	frameY0 = pScrn->frameY0;
    }

    if (!(*pScrn->SwitchMode)(scrnIdx, pMode, 0))
	return (FALSE);
    (*pScrn->AdjustFrame)(scrnIdx, frameX0, frameY0, 0);

    return (TRUE);
}

static void
VBOXDGASetViewport(ScrnInfoPtr pScrn, int x, int y, int flags)
{
    (*pScrn->AdjustFrame)(pScrn->pScreen->myNum, x, y, flags);
}

static int
VBOXDGAGetViewport(ScrnInfoPtr pScrn)
{
    return (0);
}

static DGAFunctionRec VBOXDGAFunctions =
{
    VBOXDGAOpenFramebuffer,
    NULL,       /* CloseFramebuffer */
    VBOXDGASetMode,
    VBOXDGASetViewport,
    VBOXDGAGetViewport,
    NULL,       /* Sync */
    NULL,       /* FillRect */
    NULL,       /* BlitRect */
    NULL,       /* BlitTransRect */
};

static void
VBOXDGAAddModes(ScrnInfoPtr pScrn)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);
    DisplayModePtr pMode = pScrn->modes;
    DGAModePtr pDGAMode;

    do {
	pDGAMode = xrealloc(pVBox->pDGAMode,
			    (pVBox->nDGAMode + 1) * sizeof(DGAModeRec));
	if (!pDGAMode)
	    break;

	pVBox->pDGAMode = pDGAMode;
	pDGAMode += pVBox->nDGAMode;
	(void)memset(pDGAMode, 0, sizeof(DGAModeRec));

	++pVBox->nDGAMode;
	pDGAMode->mode = pMode;
	pDGAMode->flags = DGA_CONCURRENT_ACCESS | DGA_PIXMAP_AVAILABLE;
	pDGAMode->byteOrder = pScrn->imageByteOrder;
	pDGAMode->depth = pScrn->depth;
	pDGAMode->bitsPerPixel = pScrn->bitsPerPixel;
	pDGAMode->red_mask = pScrn->mask.red;
	pDGAMode->green_mask = pScrn->mask.green;
	pDGAMode->blue_mask = pScrn->mask.blue;
        pDGAMode->visualClass = TrueColor;
	pDGAMode->xViewportStep = 1;
	pDGAMode->yViewportStep = 1;
	pDGAMode->viewportWidth = pMode->HDisplay;
	pDGAMode->viewportHeight = pMode->VDisplay;

	pDGAMode->bytesPerScanline = pVBox->maxBytesPerScanline;
	pDGAMode->imageWidth = pMode->HDisplay;
	pDGAMode->imageHeight =  pMode->VDisplay;
	pDGAMode->pixmapWidth = pDGAMode->imageWidth;
	pDGAMode->pixmapHeight = pDGAMode->imageHeight;
	pDGAMode->maxViewportX = pScrn->virtualX -
				    pDGAMode->viewportWidth;
	pDGAMode->maxViewportY = pScrn->virtualY -
				    pDGAMode->viewportHeight;

	pDGAMode->address = pVBox->base;

	pMode = pMode->next;
    } while (pMode != pScrn->modes);
}

static Bool
VBOXDGAInit(ScrnInfoPtr pScrn, ScreenPtr pScreen)
{
    VBOXPtr pVBox = VBOXGetRec(pScrn);

    if (!pVBox->nDGAMode)
	VBOXDGAAddModes(pScrn);

    return (DGAInit(pScreen, &VBOXDGAFunctions,
	    pVBox->pDGAMode, pVBox->nDGAMode));
}
