/*
 *Copyright (C) 2001-2004 Harold L Hunt II All Rights Reserved.
 *
 *Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 *"Software"), to deal in the Software without restriction, including
 *without limitation the rights to use, copy, modify, merge, publish,
 *distribute, sublicense, and/or sell copies of the Software, and to
 *permit persons to whom the Software is furnished to do so, subject to
 *the following conditions:
 *
 *The above copyright notice and this permission notice 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 HAROLD L HUNT II 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 Harold L Hunt II
 *shall not be used in advertising or otherwise to promote the sale, use
 *or other dealings in this Software without prior written authorization
 *from Harold L Hunt II.
 *
 * Authors:	Harold L Hunt II
 */

#ifdef HAVE_XWIN_CONFIG_H
#include <xwin-config.h>
#endif
#include "win.h"

#include "dix/colormap_priv.h"

/*
 * Local function prototypes
 */

static wBOOL CALLBACK winRedrawAllProcShadowGDI(HWND hwnd, LPARAM lParam);

static wBOOL CALLBACK winRedrawDamagedWindowShadowGDI(HWND hwnd, LPARAM lParam);

static Bool
 winAllocateFBShadowGDI(ScreenPtr pScreen);

static void
 winShadowUpdateGDI(ScreenPtr pScreen, shadowBufPtr pBuf);

static Bool
 winCloseScreenShadowGDI(ScreenPtr pScreen);

static Bool
 winInitVisualsShadowGDI(ScreenPtr pScreen);

static Bool
 winAdjustVideoModeShadowGDI(ScreenPtr pScreen);

static Bool
 winBltExposedRegionsShadowGDI(ScreenPtr pScreen);

static Bool
 winBltExposedWindowRegionShadowGDI(ScreenPtr pScreen, WindowPtr pWin);

static Bool
 winActivateAppShadowGDI(ScreenPtr pScreen);

static Bool
 winRedrawScreenShadowGDI(ScreenPtr pScreen);

static Bool
 winRealizeInstalledPaletteShadowGDI(ScreenPtr pScreen);

static Bool
 winInstallColormapShadowGDI(ColormapPtr pColormap);

static Bool
 winStoreColorsShadowGDI(ColormapPtr pmap, int ndef, xColorItem * pdefs);

static Bool
 winCreateColormapShadowGDI(ColormapPtr pColormap);

static Bool
 winDestroyColormapShadowGDI(ColormapPtr pColormap);

/*
 * Internal function to get the DIB format that is compatible with the screen
 */

static
    Bool
winQueryScreenDIBFormat(ScreenPtr pScreen, BITMAPINFOHEADER * pbmih)
{
    winScreenPriv(pScreen);
    HBITMAP hbmp;

#if ENABLE_DEBUG
    LPDWORD pdw = NULL;
#endif

    /* Create a memory bitmap compatible with the screen */
    hbmp = CreateCompatibleBitmap(pScreenPriv->hdcScreen, 1, 1);
    if (hbmp == NULL) {
        ErrorF("winQueryScreenDIBFormat - CreateCompatibleBitmap failed\n");
        return FALSE;
    }

    /* Initialize our bitmap info header */
    memset(pbmih, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD), 1);
    pbmih->biSize = sizeof(BITMAPINFOHEADER);

    /* Get the biBitCount */
    if (!GetDIBits(pScreenPriv->hdcScreen,
                   hbmp, 0, 1, NULL, (BITMAPINFO *) pbmih, DIB_RGB_COLORS)) {
        ErrorF("winQueryScreenDIBFormat - First call to GetDIBits failed\n");
        DeleteObject(hbmp);
        return FALSE;
    }

#if ENABLE_DEBUG
    /* Get a pointer to bitfields */
    pdw = (DWORD *) ((CARD8 *) pbmih + sizeof(BITMAPINFOHEADER));

    winDebug("winQueryScreenDIBFormat - First call masks: %08x %08x %08x\n",
             (unsigned int)pdw[0], (unsigned int)pdw[1], (unsigned int)pdw[2]);
#endif

    /* Get optimal color table, or the optimal bitfields */
    if (!GetDIBits(pScreenPriv->hdcScreen,
                   hbmp, 0, 1, NULL, (BITMAPINFO *) pbmih, DIB_RGB_COLORS)) {
        ErrorF("winQueryScreenDIBFormat - Second call to GetDIBits "
               "failed\n");
        DeleteObject(hbmp);
        return FALSE;
    }

    /* Free memory */
    DeleteObject(hbmp);

    return TRUE;
}

/*
 * Internal function to determine the GDI bits per rgb and bit masks
 */

static
    Bool
winQueryRGBBitsAndMasks(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    Bool fReturn = TRUE;
    LPDWORD pdw = NULL;
    DWORD dwRedBits, dwGreenBits, dwBlueBits;

    /* Color masks for 8 bpp are standardized */
    if (GetDeviceCaps(pScreenPriv->hdcScreen, RASTERCAPS) & RC_PALETTE) {
        /*
         * RGB BPP for 8 bit palletes is always 8
         * and the color masks are always 0.
         */
        pScreenPriv->dwBitsPerRGB = 8;
        pScreenPriv->dwRedMask = 0x0L;
        pScreenPriv->dwGreenMask = 0x0L;
        pScreenPriv->dwBlueMask = 0x0L;
        return TRUE;
    }

    /* Color masks for 24 bpp are standardized */
    if (GetDeviceCaps(pScreenPriv->hdcScreen, PLANES)
        * GetDeviceCaps(pScreenPriv->hdcScreen, BITSPIXEL) == 24) {
        ErrorF("winQueryRGBBitsAndMasks - GetDeviceCaps (BITSPIXEL) "
               "returned 24 for the screen.  Using default 24bpp masks.\n");

        /* 8 bits per primary color */
        pScreenPriv->dwBitsPerRGB = 8;

        /* Set screen privates masks */
        pScreenPriv->dwRedMask = WIN_24BPP_MASK_RED;
        pScreenPriv->dwGreenMask = WIN_24BPP_MASK_GREEN;
        pScreenPriv->dwBlueMask = WIN_24BPP_MASK_BLUE;

        return TRUE;
    }

    /* Allocate a bitmap header and color table */
    BITMAPINFOHEADER *pbmih = calloc(1, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
    if (pbmih == NULL) {
        ErrorF("winQueryRGBBitsAndMasks - calloc failed\n");
        return FALSE;
    }

    /* Get screen description */
    if (winQueryScreenDIBFormat(pScreen, pbmih)) {
        /* Get a pointer to bitfields */
        pdw = (DWORD *) ((CARD8 *) pbmih + sizeof(BITMAPINFOHEADER));

#if ENABLE_DEBUG
        winDebug("%s - Masks: %08x %08x %08x\n", __func__,
                 (unsigned int)pdw[0], (unsigned int)pdw[1], (unsigned int)pdw[2]);
        winDebug("%s - Bitmap: %dx%d %d bpp %d planes\n", __func__,
                 (int)pbmih->biWidth, (int)pbmih->biHeight, pbmih->biBitCount,
                 pbmih->biPlanes);
        winDebug("%s - Compression: %u %s\n", __func__,
                 (unsigned int)pbmih->biCompression,
                 (pbmih->biCompression ==
                  BI_RGB ? "(BI_RGB)" : (pbmih->biCompression ==
                                         BI_RLE8 ? "(BI_RLE8)" : (pbmih->
                                                                  biCompression
                                                                  ==
                                                                  BI_RLE4 ?
                                                                  "(BI_RLE4)"
                                                                  : (pbmih->
                                                                     biCompression
                                                                     ==
                                                                     BI_BITFIELDS
                                                                     ?
                                                                     "(BI_BITFIELDS)"
                                                                     : "")))));
#endif

        /* Handle BI_RGB case, which is returned by Wine */
        if (pbmih->biCompression == BI_RGB) {
            dwRedBits = 5;
            dwGreenBits = 5;
            dwBlueBits = 5;

            pScreenPriv->dwBitsPerRGB = 5;

            /* Set screen privates masks */
            pScreenPriv->dwRedMask = 0x7c00;
            pScreenPriv->dwGreenMask = 0x03e0;
            pScreenPriv->dwBlueMask = 0x001f;
        }
        else {
            /* Count the number of bits in each mask */
            dwRedBits = winCountBits(pdw[0]);
            dwGreenBits = winCountBits(pdw[1]);
            dwBlueBits = winCountBits(pdw[2]);

            /* Find maximum bits per red, green, blue */
            if (dwRedBits > dwGreenBits && dwRedBits > dwBlueBits)
                pScreenPriv->dwBitsPerRGB = dwRedBits;
            else if (dwGreenBits > dwRedBits && dwGreenBits > dwBlueBits)
                pScreenPriv->dwBitsPerRGB = dwGreenBits;
            else
                pScreenPriv->dwBitsPerRGB = dwBlueBits;

            /* Set screen privates masks */
            pScreenPriv->dwRedMask = pdw[0];
            pScreenPriv->dwGreenMask = pdw[1];
            pScreenPriv->dwBlueMask = pdw[2];
        }
    }
    else {
        ErrorF("winQueryRGBBitsAndMasks - winQueryScreenDIBFormat failed\n");
        fReturn = FALSE;
    }

    /* Free memory */
    free(pbmih);

    return fReturn;
}

/*
 * Redraw all ---?
 */

static wBOOL CALLBACK
winRedrawAllProcShadowGDI(HWND hwnd, LPARAM lParam)
{
    if (hwnd == (HWND) lParam)
        return TRUE;
    InvalidateRect(hwnd, NULL, FALSE);
    UpdateWindow(hwnd);
    return TRUE;
}

static wBOOL CALLBACK
winRedrawDamagedWindowShadowGDI(HWND hwnd, LPARAM lParam)
{
    BoxPtr pDamage = (BoxPtr) lParam;
    RECT rcClient, rcDamage, rcRedraw;
    POINT topLeft, bottomRight;

    if (IsIconic(hwnd))
        return TRUE;            /* Don't care minimized windows */

    /* Convert the damaged area from Screen coords to Client coords */
    topLeft.x = pDamage->x1;
    topLeft.y = pDamage->y1;
    bottomRight.x = pDamage->x2;
    bottomRight.y = pDamage->y2;
    topLeft.x += GetSystemMetrics(SM_XVIRTUALSCREEN);
    bottomRight.x += GetSystemMetrics(SM_XVIRTUALSCREEN);
    topLeft.y += GetSystemMetrics(SM_YVIRTUALSCREEN);
    bottomRight.y += GetSystemMetrics(SM_YVIRTUALSCREEN);
    ScreenToClient(hwnd, &topLeft);
    ScreenToClient(hwnd, &bottomRight);
    SetRect(&rcDamage, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);

    GetClientRect(hwnd, &rcClient);

    if (IntersectRect(&rcRedraw, &rcClient, &rcDamage)) {
        InvalidateRect(hwnd, &rcRedraw, FALSE);
        UpdateWindow(hwnd);
    }
    return TRUE;
}

/*
 * Allocate a DIB for the shadow framebuffer GDI server
 */

static Bool
winAllocateFBShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
    DIBSECTION dibsection;
    Bool fReturn = TRUE;

    /* Describe shadow bitmap to be created */
    pScreenPriv->pbmih->biWidth = pScreenInfo->dwWidth;
    pScreenPriv->pbmih->biHeight = -pScreenInfo->dwHeight;

    ErrorF("winAllocateFBShadowGDI - Creating DIB with width: %d height: %d "
           "depth: %d\n",
           (int) pScreenPriv->pbmih->biWidth,
           (int) -pScreenPriv->pbmih->biHeight, pScreenPriv->pbmih->biBitCount);

    /* Create a DI shadow bitmap with a bit pointer */
    pScreenPriv->hbmpShadow = CreateDIBSection(pScreenPriv->hdcScreen,
                                               (BITMAPINFO *) pScreenPriv->
                                               pbmih, DIB_RGB_COLORS,
                                               (VOID **) &pScreenInfo->pfb,
                                               NULL, 0);
    if (pScreenPriv->hbmpShadow == NULL || pScreenInfo->pfb == NULL) {
        winW32Error(2, "winAllocateFBShadowGDI - CreateDIBSection failed:");
        return FALSE;
    }
    else {
#if ENABLE_DEBUG
        winDebug("winAllocateFBShadowGDI - Shadow buffer allocated\n");
#endif
    }

    /* Get information about the bitmap that was allocated */
    GetObject(pScreenPriv->hbmpShadow, sizeof(dibsection), &dibsection);

#if ENABLE_DEBUG || YES
    /* Print information about bitmap allocated */
    winDebug("winAllocateFBShadowGDI - Dibsection width: %d height: %d "
             "depth: %d size image: %d\n",
             (int) dibsection.dsBmih.biWidth, (int) dibsection.dsBmih.biHeight,
             dibsection.dsBmih.biBitCount, (int) dibsection.dsBmih.biSizeImage);
#endif

    /* Select the shadow bitmap into the shadow DC */
    SelectObject(pScreenPriv->hdcShadow, pScreenPriv->hbmpShadow);

#if ENABLE_DEBUG
    winDebug("winAllocateFBShadowGDI - Attempting a shadow blit\n");
#endif

    /* Do a test blit from the shadow to the screen, I think */
    fReturn = BitBlt(pScreenPriv->hdcScreen,
                     0, 0,
                     pScreenInfo->dwWidth, pScreenInfo->dwHeight,
                     pScreenPriv->hdcShadow, 0, 0, SRCCOPY);
    if (fReturn) {
#if ENABLE_DEBUG
        winDebug("winAllocateFBShadowGDI - Shadow blit success\n");
#endif
    }
    else {
        winW32Error(2, "winAllocateFBShadowGDI - Shadow blit failure\n");
#if 0
        return FALSE;
#else
        /* ago: ignore this error. The blit fails with wine, but does not
         * cause any problems later. */

        fReturn = TRUE;
#endif
    }

    /* Look for height weirdness */
    if (dibsection.dsBmih.biHeight < 0) {
        dibsection.dsBmih.biHeight = -dibsection.dsBmih.biHeight;
    }

    /* Set screeninfo stride */
    pScreenInfo->dwStride = ((dibsection.dsBmih.biSizeImage
                              / dibsection.dsBmih.biHeight)
                             * 8) / pScreenInfo->dwBPP;

#if ENABLE_DEBUG || YES
    winDebug("winAllocateFBShadowGDI - Created shadow stride: %d\n",
             (int) pScreenInfo->dwStride);
#endif

    /* Redraw all windows */
    if (pScreenInfo->fMultiWindow)
        EnumThreadWindows(g_dwCurrentThreadID, winRedrawAllProcShadowGDI, 0);

    return fReturn;
}

static void
winFreeFBShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;

    /* Free the shadow bitmap */
    DeleteObject(pScreenPriv->hbmpShadow);

    /* Invalidate the ScreenInfo's fb pointer */
    pScreenInfo->pfb = NULL;
}

/*
 * Blit the damaged regions of the shadow fb to the screen
 */

static void
winShadowUpdateGDI(ScreenPtr pScreen, shadowBufPtr pBuf)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
    RegionPtr damage = DamageRegion(pBuf->pDamage);
    DWORD dwBox = RegionNumRects(damage);
    BoxPtr pBox = RegionRects(damage);
    int x, y, w, h;
    HRGN hrgnCombined = NULL;

#ifdef XWIN_UPDATESTATS
    static DWORD s_dwNonUnitRegions = 0;
    static DWORD s_dwTotalUpdates = 0;
    static DWORD s_dwTotalBoxes = 0;
#endif
    BoxPtr pBoxExtents = RegionExtents(damage);

    /*
     * Return immediately if the app is not active
     * and we are fullscreen, or if we have a bad display depth
     */
    if ((!pScreenPriv->fActive && pScreenInfo->fFullScreen)
        || pScreenPriv->fBadDepth)
        return;

#ifdef XWIN_UPDATESTATS
    ++s_dwTotalUpdates;
    s_dwTotalBoxes += dwBox;

    if (dwBox != 1) {
        ++s_dwNonUnitRegions;
        ErrorF("winShadowUpdatGDI - dwBox: %d\n", dwBox);
    }

    if ((s_dwTotalUpdates % 100) == 0)
        ErrorF("winShadowUpdateGDI - %d%% non-unity regions, avg boxes: %d "
               "nu: %d tu: %d\n",
               (s_dwNonUnitRegions * 100) / s_dwTotalUpdates,
               s_dwTotalBoxes / s_dwTotalUpdates,
               s_dwNonUnitRegions, s_dwTotalUpdates);
#endif                          /* XWIN_UPDATESTATS */

    /*
     * Handle small regions with multiple blits,
     * handle large regions by creating a clipping region and
     * doing a single blit constrained to that clipping region.
     */
    if (!pScreenInfo->fMultiWindow &&
        (pScreenInfo->dwClipUpdatesNBoxes == 0 ||
         dwBox < pScreenInfo->dwClipUpdatesNBoxes)) {
        /* Loop through all boxes in the damaged region */
        while (dwBox--) {
            /*
             * Calculate x offset, y offset, width, and height for
             * current damage box
             */
            x = pBox->x1;
            y = pBox->y1;
            w = pBox->x2 - pBox->x1;
            h = pBox->y2 - pBox->y1;

            BitBlt(pScreenPriv->hdcScreen,
                   x, y, w, h, pScreenPriv->hdcShadow, x, y, SRCCOPY);

            /* Get a pointer to the next box */
            ++pBox;
        }
    }
    else if (!pScreenInfo->fMultiWindow) {

        /* Compute a GDI region from the damaged region */
        hrgnCombined =
            CreateRectRgn(pBoxExtents->x1, pBoxExtents->y1, pBoxExtents->x2,
                          pBoxExtents->y2);

        /* Install the GDI region as a clipping region */
        SelectClipRgn(pScreenPriv->hdcScreen, hrgnCombined);
        DeleteObject(hrgnCombined);
        hrgnCombined = NULL;

        /*
         * Blit the shadow buffer to the screen,
         * constrained to the clipping region.
         */
        BitBlt(pScreenPriv->hdcScreen,
               pBoxExtents->x1, pBoxExtents->y1,
               pBoxExtents->x2 - pBoxExtents->x1,
               pBoxExtents->y2 - pBoxExtents->y1,
               pScreenPriv->hdcShadow,
               pBoxExtents->x1, pBoxExtents->y1, SRCCOPY);

        /* Reset the clip region */
        SelectClipRgn(pScreenPriv->hdcScreen, NULL);
    }

    /* Redraw all multiwindow windows */
    if (pScreenInfo->fMultiWindow)
        EnumThreadWindows(g_dwCurrentThreadID,
                          winRedrawDamagedWindowShadowGDI,
                          (LPARAM) pBoxExtents);
}

static Bool
winInitScreenShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);

    /* Get device contexts for the screen and shadow bitmap */
    pScreenPriv->hdcScreen = GetDC(pScreenPriv->hwndScreen);
    pScreenPriv->hdcShadow = CreateCompatibleDC(pScreenPriv->hdcScreen);

    /* Allocate bitmap info header */
    pScreenPriv->pbmih = calloc(1, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
    if (pScreenPriv->pbmih == NULL) {
        ErrorF("winInitScreenShadowGDI - calloc () failed\n");
        return FALSE;
    }

    /* Query the screen format */
    if (!winQueryScreenDIBFormat(pScreen, pScreenPriv->pbmih)) {
        ErrorF("winInitScreenShadowGDI - winQueryScreenDIBFormat failed\n");
        return FALSE;
    }

    /* Determine our color masks */
    if (!winQueryRGBBitsAndMasks(pScreen)) {
        ErrorF("winInitScreenShadowGDI - winQueryRGBBitsAndMasks failed\n");
        return FALSE;
    }

    return winAllocateFBShadowGDI(pScreen);
}

/* See Porting Layer Definition - p. 33 */
/*
 * We wrap whatever CloseScreen procedure was specified by fb;
 * a pointer to said procedure is stored in our privates.
 */

static Bool
winCloseScreenShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
    Bool fReturn = TRUE;

#if ENABLE_DEBUG
    winDebug("winCloseScreenShadowGDI - Freeing screen resources\n");
#endif

    /* Flag that the screen is closed */
    pScreenPriv->fClosed = TRUE;
    pScreenPriv->fActive = FALSE;

    fReturn = fbCloseScreen(pScreen);

    /* Delete the window property */
    RemoveProp(pScreenPriv->hwndScreen, WIN_SCR_PROP);

    /* Free the shadow DC; which allows the bitmap to be freed */
    DeleteDC(pScreenPriv->hdcShadow);

    winFreeFBShadowGDI(pScreen);

    /* Free the screen DC */
    ReleaseDC(pScreenPriv->hwndScreen, pScreenPriv->hdcScreen);

    /* Delete tray icon, if we have one */
    if (!pScreenInfo->fNoTrayIcon)
        winDeleteNotifyIcon(pScreenPriv);

    /* Free the exit confirmation dialog box, if it exists */
    if (g_hDlgExit != NULL) {
        DestroyWindow(g_hDlgExit);
        g_hDlgExit = NULL;
    }

    /* Kill our window */
    if (pScreenPriv->hwndScreen) {
        DestroyWindow(pScreenPriv->hwndScreen);
        pScreenPriv->hwndScreen = NULL;
    }

    /* Destroy the thread startup mutex */
    pthread_mutex_destroy(&pScreenPriv->pmServerStarted);

    /* Invalidate our screeninfo's pointer to the screen */
    pScreenInfo->pScreen = NULL;

    /* Free the screen privates for this screen */
    free((void *) pScreenPriv);

    return fReturn;
}

/*
 * Tell mi what sort of visuals we need.
 *
 * Generally we only need one visual, as our screen can only
 * handle one format at a time, I believe.  You may want
 * to verify that last sentence.
 */

static Bool
winInitVisualsShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;

    /* Display debugging information */
    ErrorF("winInitVisualsShadowGDI - Masks %08x %08x %08x BPRGB %d d %d "
           "bpp %d\n",
           (unsigned int) pScreenPriv->dwRedMask,
           (unsigned int) pScreenPriv->dwGreenMask,
           (unsigned int) pScreenPriv->dwBlueMask,
           (int) pScreenPriv->dwBitsPerRGB,
           (int) pScreenInfo->dwDepth, (int) pScreenInfo->dwBPP);

    /* Create a single visual according to the Windows screen depth */
    switch (pScreenInfo->dwDepth) {
    case 24:
    case 16:
    case 15:
        /* Setup the real visual */
        if (!miSetVisualTypesAndMasks(pScreenInfo->dwDepth,
                                      TrueColorMask,
                                      pScreenPriv->dwBitsPerRGB,
                                      -1,
                                      pScreenPriv->dwRedMask,
                                      pScreenPriv->dwGreenMask,
                                      pScreenPriv->dwBlueMask)) {
            ErrorF("winInitVisualsShadowGDI - miSetVisualTypesAndMasks "
                   "failed\n");
            return FALSE;
        }

#ifdef XWIN_EMULATEPSEUDO
        if (!pScreenInfo->fEmulatePseudo)
            break;

        /* Setup a pseudocolor visual */
        if (!miSetVisualTypesAndMasks(8, PseudoColorMask, 8, -1, 0, 0, 0)) {
            ErrorF("winInitVisualsShadowGDI - miSetVisualTypesAndMasks "
                   "failed for PseudoColor\n");
            return FALSE;
        }
#endif
        break;

    case 8:
        if (!miSetVisualTypesAndMasks(pScreenInfo->dwDepth,
                                      PseudoColorMask,
                                      pScreenPriv->dwBitsPerRGB,
                                      PseudoColor,
                                      pScreenPriv->dwRedMask,
                                      pScreenPriv->dwGreenMask,
                                      pScreenPriv->dwBlueMask)) {
            ErrorF("winInitVisualsShadowGDI - miSetVisualTypesAndMasks "
                   "failed\n");
            return FALSE;
        }
        break;

    default:
        ErrorF("winInitVisualsShadowGDI - Unknown screen depth\n");
        return FALSE;
    }

#if ENABLE_DEBUG
    winDebug("winInitVisualsShadowGDI - Returning\n");
#endif

    return TRUE;
}

/*
 * Adjust the proposed video mode
 */

static Bool
winAdjustVideoModeShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
    HDC hdc;
    DWORD dwBPP;

    hdc = GetDC(NULL);

    /* We're in serious trouble if we can't get a DC */
    if (hdc == NULL) {
        ErrorF("winAdjustVideoModeShadowGDI - GetDC () failed\n");
        return FALSE;
    }

    /* Query GDI for current display depth */
    dwBPP = GetDeviceCaps(hdc, BITSPIXEL);

    /* GDI cannot change the screen depth, so always use GDI's depth */
    pScreenInfo->dwBPP = dwBPP;

    /* Release our DC */
    ReleaseDC(NULL, hdc);
    hdc = NULL;

    return TRUE;
}

/*
 * Blt exposed regions to the screen
 */

static Bool
winBltExposedRegionsShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
    winPrivCmapPtr pCmapPriv = NULL;
    HDC hdcUpdate;
    PAINTSTRUCT ps;

    /* BeginPaint gives us an hdc that clips to the invalidated region */
    hdcUpdate = BeginPaint(pScreenPriv->hwndScreen, &ps);
    /* Avoid the BitBlt if the PAINTSTRUCT region is bogus */
    if (ps.rcPaint.right == 0 && ps.rcPaint.bottom == 0 &&
        ps.rcPaint.left == 0 && ps.rcPaint.top == 0) {
        EndPaint(pScreenPriv->hwndScreen, &ps);
        return 0;
    }

    /* Realize the palette, if we have one */
    if (pScreenPriv->pcmapInstalled != NULL) {
        pCmapPriv = winGetCmapPriv(pScreenPriv->pcmapInstalled);

        SelectPalette(hdcUpdate, pCmapPriv->hPalette, FALSE);
        RealizePalette(hdcUpdate);
    }

    /* Try to copy from the shadow buffer to the invalidated region */
    if (!BitBlt(hdcUpdate,
                ps.rcPaint.left, ps.rcPaint.top,
                ps.rcPaint.right - ps.rcPaint.left,
                ps.rcPaint.bottom - ps.rcPaint.top,
                pScreenPriv->hdcShadow,
                ps.rcPaint.left,
                ps.rcPaint.top,
                SRCCOPY)) {
        LPVOID lpMsgBuf;

        /* Display an error message */
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                      FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL,
                      GetLastError(),
                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                      (LPTSTR) &lpMsgBuf, 0, NULL);

        ErrorF("winBltExposedRegionsShadowGDI - BitBlt failed: %s\n",
               (LPSTR) lpMsgBuf);
        LocalFree(lpMsgBuf);
    }

    /* EndPaint frees the DC */
    EndPaint(pScreenPriv->hwndScreen, &ps);

    /* Redraw all windows */
    if (pScreenInfo->fMultiWindow)
        EnumThreadWindows(g_dwCurrentThreadID, winRedrawAllProcShadowGDI,
                          (LPARAM) pScreenPriv->hwndScreen);

    return TRUE;
}

/*
 * Blt exposed region to the given HWND
 */

static Bool
winBltExposedWindowRegionShadowGDI(ScreenPtr pScreen, WindowPtr pWin)
{
    winScreenPriv(pScreen);
    winPrivWinPtr pWinPriv = winGetWindowPriv(pWin);

    HWND hWnd = pWinPriv->hWnd;
    HDC hdcUpdate;
    PAINTSTRUCT ps;

    hdcUpdate = BeginPaint(hWnd, &ps);
    /* Avoid the BitBlt if the PAINTSTRUCT region is bogus */
    if (ps.rcPaint.right == 0 && ps.rcPaint.bottom == 0 &&
        ps.rcPaint.left == 0 && ps.rcPaint.top == 0) {
        EndPaint(hWnd, &ps);
        return 0;
    }

    if (pWin->redirectDraw != RedirectDrawNone) {
        HBITMAP hBitmap;
        HDC hdcPixmap;
        PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin);
        winPrivPixmapPtr pPixmapPriv = winGetPixmapPriv(pPixmap);

        /* window pixmap format is the same as the screen pixmap */
        assert(pPixmap->drawable.bitsPerPixel > 8);

        /* Get the window bitmap from the pixmap */
        hBitmap = pPixmapPriv->hBitmap;

        /* XXX: There may be a need for a slow-path here: If hBitmap is NULL
           (because we couldn't back the pixmap with a Windows DIB), we should
           fall-back to creating a Windows DIB from the pixmap, then deleting it
           after the BitBlt (as this this code did before the fast-path was
           added). */
        if (!hBitmap) {
            ErrorF("winBltExposedWindowRegionShadowGDI - slow path unimplemented\n");
        }

        /* Select the window bitmap into a screen-compatible DC */
        hdcPixmap = CreateCompatibleDC(pScreenPriv->hdcScreen);
        SelectObject(hdcPixmap, hBitmap);

        /* Blt from the window bitmap to the invalidated region */
        if (!BitBlt(hdcUpdate,
                    ps.rcPaint.left, ps.rcPaint.top,
                    ps.rcPaint.right - ps.rcPaint.left,
                    ps.rcPaint.bottom - ps.rcPaint.top,
                    hdcPixmap,
                    ps.rcPaint.left + pWin->borderWidth,
                    ps.rcPaint.top + pWin->borderWidth,
                    SRCCOPY))
            ErrorF("winBltExposedWindowRegionShadowGDI - BitBlt failed: 0x%08x\n",
                   (unsigned int)GetLastError());

        /* Release DC */
        DeleteDC(hdcPixmap);
    }
    else
    {
    /* Try to copy from the shadow buffer to the invalidated region */
    if (!BitBlt(hdcUpdate,
                ps.rcPaint.left, ps.rcPaint.top,
                ps.rcPaint.right - ps.rcPaint.left,
                ps.rcPaint.bottom - ps.rcPaint.top,
                pScreenPriv->hdcShadow,
                ps.rcPaint.left + pWin->drawable.x,
                ps.rcPaint.top + pWin->drawable.y,
                SRCCOPY)) {
        LPVOID lpMsgBuf;

        /* Display an error message */
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                      FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL,
                      GetLastError(),
                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                      (LPTSTR) &lpMsgBuf, 0, NULL);

        ErrorF("winBltExposedWindowRegionShadowGDI - BitBlt failed: %s\n",
               (LPSTR) lpMsgBuf);
        LocalFree(lpMsgBuf);
    }
    }

    /* If part of the invalidated region is outside the window (which can happen
       if the native window is being re-sized), fill that area with black */
    if (ps.rcPaint.right > ps.rcPaint.left + pWin->drawable.width) {
        BitBlt(hdcUpdate,
               ps.rcPaint.left + pWin->drawable.width,
               ps.rcPaint.top,
               ps.rcPaint.right - (ps.rcPaint.left + pWin->drawable.width),
               ps.rcPaint.bottom - ps.rcPaint.top,
               NULL,
               0, 0,
               BLACKNESS);
    }

    if (ps.rcPaint.bottom > ps.rcPaint.top + pWin->drawable.height) {
        BitBlt(hdcUpdate,
               ps.rcPaint.left,
               ps.rcPaint.top + pWin->drawable.height,
               ps.rcPaint.right - ps.rcPaint.left,
               ps.rcPaint.bottom - (ps.rcPaint.top + pWin->drawable.height),
               NULL,
               0, 0,
               BLACKNESS);
    }

    /* EndPaint frees the DC */
    EndPaint(hWnd, &ps);

    return TRUE;
}

/*
 * Do any engine-specific appliation-activation processing
 */

static Bool
winActivateAppShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;

    /*
     * 2004/04/12 - Harold - We perform the restoring or minimizing
     * manually for ShadowGDI in fullscreen modes so that this engine
     * will perform just like ShadowDD and ShadowDDNL in fullscreen mode;
     * if we do not do this then our fullscreen window will appear in the
     * z-order when it is deactivated and it can be uncovered by resizing
     * or minimizing another window that is on top of it, which is not how
     * the DirectDraw engines work.  Therefore we keep this code here to
     * make sure that all engines work the same in fullscreen mode.
     */

    /*
     * Are we active?
     * Are we fullscreen?
     */
    if (pScreenPriv->fActive && pScreenInfo->fFullScreen) {
        /*
         * Activating, attempt to bring our window
         * to the top of the display
         */
        ShowWindow(pScreenPriv->hwndScreen, SW_RESTORE);
    }
    else if (!pScreenPriv->fActive && pScreenInfo->fFullScreen) {
        /*
         * Deactivating, stuff our window onto the
         * task bar.
         */
        ShowWindow(pScreenPriv->hwndScreen, SW_MINIMIZE);
    }

    return TRUE;
}

/*
 * Reblit the shadow framebuffer to the screen.
 */

static Bool
winRedrawScreenShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;

    /* Redraw the whole window, to take account for the new colors */
    BitBlt(pScreenPriv->hdcScreen,
           0, 0,
           pScreenInfo->dwWidth, pScreenInfo->dwHeight,
           pScreenPriv->hdcShadow, 0, 0, SRCCOPY);

    /* Redraw all windows */
    if (pScreenInfo->fMultiWindow)
        EnumThreadWindows(g_dwCurrentThreadID, winRedrawAllProcShadowGDI, 0);

    return TRUE;
}

/*
 * Realize the currently installed colormap
 */

static Bool
winRealizeInstalledPaletteShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winPrivCmapPtr pCmapPriv = NULL;

#if ENABLE_DEBUG
    winDebug("winRealizeInstalledPaletteShadowGDI\n");
#endif

    /* Don't do anything if there is not a colormap */
    if (pScreenPriv->pcmapInstalled == NULL) {
#if ENABLE_DEBUG
        winDebug("winRealizeInstalledPaletteShadowGDI - No colormap "
                 "installed\n");
#endif
        return TRUE;
    }

    pCmapPriv = winGetCmapPriv(pScreenPriv->pcmapInstalled);

    /* Realize our palette for the screen */
    if (RealizePalette(pScreenPriv->hdcScreen) == GDI_ERROR) {
        ErrorF("winRealizeInstalledPaletteShadowGDI - RealizePalette () "
               "failed\n");
        return FALSE;
    }

    /* Set the DIB color table */
    if (SetDIBColorTable(pScreenPriv->hdcShadow,
                         0,
                         WIN_NUM_PALETTE_ENTRIES, pCmapPriv->rgbColors) == 0) {
        ErrorF("winRealizeInstalledPaletteShadowGDI - SetDIBColorTable () "
               "failed\n");
        return FALSE;
    }

    return TRUE;
}

/*
 * Install the specified colormap
 */

static Bool
winInstallColormapShadowGDI(ColormapPtr pColormap)
{
    ScreenPtr pScreen = pColormap->pScreen;

    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;

    winCmapPriv(pColormap);

    /*
     * Tell Windows to install the new colormap
     */
    if (SelectPalette(pScreenPriv->hdcScreen,
                      pCmapPriv->hPalette, FALSE) == NULL) {
        ErrorF("winInstallColormapShadowGDI - SelectPalette () failed\n");
        return FALSE;
    }

    /* Realize the palette */
    if (GDI_ERROR == RealizePalette(pScreenPriv->hdcScreen)) {
        ErrorF("winInstallColormapShadowGDI - RealizePalette () failed\n");
        return FALSE;
    }

    /* Set the DIB color table */
    if (SetDIBColorTable(pScreenPriv->hdcShadow,
                         0,
                         WIN_NUM_PALETTE_ENTRIES, pCmapPriv->rgbColors) == 0) {
        ErrorF("winInstallColormapShadowGDI - SetDIBColorTable () failed\n");
        return FALSE;
    }

    /* Redraw the whole window, to take account for the new colors */
    BitBlt(pScreenPriv->hdcScreen,
           0, 0,
           pScreenInfo->dwWidth, pScreenInfo->dwHeight,
           pScreenPriv->hdcShadow, 0, 0, SRCCOPY);

    /* Save a pointer to the newly installed colormap */
    pScreenPriv->pcmapInstalled = pColormap;

    /* Redraw all windows */
    if (pScreenInfo->fMultiWindow)
        EnumThreadWindows(g_dwCurrentThreadID, winRedrawAllProcShadowGDI, 0);

    return TRUE;
}

/*
 * Store the specified colors in the specified colormap
 */

static Bool
winStoreColorsShadowGDI(ColormapPtr pColormap, int ndef, xColorItem * pdefs)
{
    ScreenPtr pScreen = pColormap->pScreen;

    winScreenPriv(pScreen);
    winCmapPriv(pColormap);
    ColormapPtr curpmap = pScreenPriv->pcmapInstalled;

    /* Put the X colormap entries into the Windows logical palette */
    if (SetPaletteEntries(pCmapPriv->hPalette,
                          pdefs[0].pixel,
                          ndef, pCmapPriv->peColors + pdefs[0].pixel) == 0) {
        ErrorF("winStoreColorsShadowGDI - SetPaletteEntries () failed\n");
        return FALSE;
    }

    /* Don't install the Windows palette if the colormap is not installed */
    if (pColormap != curpmap) {
        return TRUE;
    }

    /* Try to install the newly modified colormap */
    if (!winInstallColormapShadowGDI(pColormap)) {
        ErrorF("winInstallColormapShadowGDI - winInstallColormapShadowGDI "
               "failed\n");
        return FALSE;
    }

#if 0
    /* Tell Windows that the palette has changed */
    RealizePalette(pScreenPriv->hdcScreen);

    /* Set the DIB color table */
    if (SetDIBColorTable(pScreenPriv->hdcShadow,
                         pdefs[0].pixel,
                         ndef, pCmapPriv->rgbColors + pdefs[0].pixel) == 0) {
        ErrorF("winInstallColormapShadowGDI - SetDIBColorTable () failed\n");
        return FALSE;
    }

    /* Save a pointer to the newly installed colormap */
    pScreenPriv->pcmapInstalled = pColormap;
#endif

    return TRUE;
}

/*
 * Colormap initialization procedure
 */

static Bool
winCreateColormapShadowGDI(ColormapPtr pColormap)
{
    LPLOGPALETTE lpPaletteNew = NULL;
    DWORD dwEntriesMax;
    VisualPtr pVisual;
    HPALETTE hpalNew = NULL;

    winCmapPriv(pColormap);

    /* Get a pointer to the visual that the colormap belongs to */
    pVisual = pColormap->pVisual;

    /* Get the maximum number of palette entries for this visual */
    dwEntriesMax = pVisual->ColormapEntries;

    /* Allocate a Windows logical color palette with max entries */
    lpPaletteNew = calloc(1, sizeof(LOGPALETTE)
                          + (dwEntriesMax - 1) * sizeof(PALETTEENTRY));
    if (lpPaletteNew == NULL) {
        ErrorF("winCreateColormapShadowGDI - Couldn't allocate palette "
               "with %d entries\n", (int) dwEntriesMax);
        return FALSE;
    }

    /* Set the logical palette structure */
    lpPaletteNew->palVersion = 0x0300;
    lpPaletteNew->palNumEntries = dwEntriesMax;

    /* Tell Windows to create the palette */
    hpalNew = CreatePalette(lpPaletteNew);
    if (hpalNew == NULL) {
        ErrorF("winCreateColormapShadowGDI - CreatePalette () failed\n");
        free(lpPaletteNew);
        return FALSE;
    }

    /* Save the Windows logical palette handle in the X colormaps' privates */
    pCmapPriv->hPalette = hpalNew;

    /* Free the palette initialization memory */
    free(lpPaletteNew);

    return TRUE;
}

/*
 * Colormap destruction procedure
 */

static Bool
winDestroyColormapShadowGDI(ColormapPtr pColormap)
{
    winScreenPriv(pColormap->pScreen);
    winCmapPriv(pColormap);

    /*
     * Is colormap to be destroyed the default?
     *
     * Non-default colormaps should have had winUninstallColormap
     * called on them before we get here.  The default colormap
     * will not have had winUninstallColormap called on it.  Thus,
     * we need to handle the default colormap in a special way.
     */
    if (pColormap->flags & CM_IsDefault) {
#if ENABLE_DEBUG
        winDebug("winDestroyColormapShadowGDI - Destroying default "
                 "colormap\n");
#endif

        /*
         * FIXME: Walk the list of all screens, popping the default
         * palette out of each screen device context.
         */

        /* Pop the palette out of the device context */
        SelectPalette(pScreenPriv->hdcScreen,
                      GetStockObject(DEFAULT_PALETTE), FALSE);

        /* Clear our private installed colormap pointer */
        pScreenPriv->pcmapInstalled = NULL;
    }

    /* Try to delete the logical palette */
    if (DeleteObject(pCmapPriv->hPalette) == 0) {
        ErrorF("winDestroyColormap - DeleteObject () failed\n");
        return FALSE;
    }

    /* Invalidate the colormap privates */
    pCmapPriv->hPalette = NULL;

    return TRUE;
}

/*
 * Set engine specific functions
 */

Bool
winSetEngineFunctionsShadowGDI(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;

    /* Set our pointers */
    pScreenPriv->pwinAllocateFB = winAllocateFBShadowGDI;
    pScreenPriv->pwinFreeFB = winFreeFBShadowGDI;
    pScreenPriv->pwinShadowUpdate = winShadowUpdateGDI;
    pScreenPriv->pwinInitScreen = winInitScreenShadowGDI;
    pScreenPriv->pwinCloseScreen = winCloseScreenShadowGDI;
    pScreenPriv->pwinInitVisuals = winInitVisualsShadowGDI;
    pScreenPriv->pwinAdjustVideoMode = winAdjustVideoModeShadowGDI;
    if (pScreenInfo->fFullScreen)
        pScreenPriv->pwinCreateBoundingWindow =
            winCreateBoundingWindowFullScreen;
    else
        pScreenPriv->pwinCreateBoundingWindow = winCreateBoundingWindowWindowed;
    pScreenPriv->pwinBltExposedRegions = winBltExposedRegionsShadowGDI;
    pScreenPriv->pwinBltExposedWindowRegion = winBltExposedWindowRegionShadowGDI;
    pScreenPriv->pwinActivateApp = winActivateAppShadowGDI;
    pScreenPriv->pwinRedrawScreen = winRedrawScreenShadowGDI;
    pScreenPriv->pwinRealizeInstalledPalette =
        winRealizeInstalledPaletteShadowGDI;
    pScreenPriv->pwinInstallColormap = winInstallColormapShadowGDI;
    pScreenPriv->pwinStoreColors = winStoreColorsShadowGDI;
    pScreenPriv->pwinCreateColormap = winCreateColormapShadowGDI;
    pScreenPriv->pwinDestroyColormap = winDestroyColormapShadowGDI;
    pScreenPriv->pwinCreatePrimarySurface = NULL;
    pScreenPriv->pwinReleasePrimarySurface = NULL;

    return TRUE;
}
