/*
 *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 "winmsg.h"

/*
 * Global variables for function pointers into
 * dynamically loaded libraries
 */
FARPROC g_fpDirectDrawCreate = NULL;
FARPROC g_fpDirectDrawCreateClipper = NULL;

/*
  module handle for dynamically loaded directdraw library
*/
static HMODULE g_hmodDirectDraw = NULL;

/*
 * Detect engines supported by current Windows version
 * DirectDraw version and hardware
 */

void
winDetectSupportedEngines(void)
{
    OSVERSIONINFO osvi;

    /* Initialize the engine support flags */
    g_dwEnginesSupported = WIN_SERVER_SHADOW_GDI;

#ifdef XWIN_NATIVEGDI
    g_dwEnginesSupported |= WIN_SERVER_NATIVE_GDI;
#endif

    /* Get operating system version information */
    ZeroMemory(&osvi, sizeof(osvi));
    osvi.dwOSVersionInfoSize = sizeof(osvi);
    GetVersionEx(&osvi);

    /* Do we have DirectDraw? */
    if (g_hmodDirectDraw != NULL) {
        LPDIRECTDRAW lpdd = NULL;
        LPDIRECTDRAW4 lpdd4 = NULL;
        HRESULT ddrval;

        /* Was the DirectDrawCreate function found? */
        if (g_fpDirectDrawCreate == NULL) {
            /* No DirectDraw support */
            return;
        }

        /* DirectDrawCreate exists, try to call it */
        /* Create a DirectDraw object, store the address at lpdd */
        ddrval = (*g_fpDirectDrawCreate) (NULL, (void **) &lpdd, NULL);
        if (FAILED(ddrval)) {
            /* No DirectDraw support */
            winErrorFVerb(2,
                          "winDetectSupportedEngines - DirectDraw not installed\n");
            return;
        }
        else {
            /* We have DirectDraw */
            winErrorFVerb(2,
                          "winDetectSupportedEngines - DirectDraw installed\n");
            g_dwEnginesSupported |= WIN_SERVER_SHADOW_DD;

#ifdef XWIN_PRIMARYFB
            /* Allow PrimaryDD engine if NT */
            if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
                g_dwEnginesSupported |= WIN_SERVER_PRIMARY_DD;
                winErrorFVerb(2,
                              "winDetectSupportedEngines - Allowing PrimaryDD\n");
            }
#endif
        }

        /* Try to query for DirectDraw4 interface */
        ddrval = IDirectDraw_QueryInterface(lpdd,
                                            &IID_IDirectDraw4,
                                            (LPVOID *) & lpdd4);
        if (SUCCEEDED(ddrval)) {
            /* We have DirectDraw4 */
            winErrorFVerb(2,
                          "winDetectSupportedEngines - DirectDraw4 installed\n");
            g_dwEnginesSupported |= WIN_SERVER_SHADOW_DDNL;
        }

        /* Cleanup DirectDraw interfaces */
        if (lpdd4 != NULL)
            IDirectDraw_Release(lpdd4);
        if (lpdd != NULL)
            IDirectDraw_Release(lpdd);
    }

    winErrorFVerb(2,
                  "winDetectSupportedEngines - Returning, supported engines %08x\n",
                  (unsigned int) g_dwEnginesSupported);
}

/*
 * Set the engine type, depending on the engines
 * supported for this screen, and whether the user
 * suggested an engine type
 */

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

    /* Get a DC */
    hdc = GetDC(NULL);
    if (hdc == NULL) {
        ErrorF("winSetEngine - Couldn't get an HDC\n");
        return FALSE;
    }

    /*
     * pScreenInfo->dwBPP may be 0 to indicate that the current screen
     * depth is to be used.  Thus, we must query for the current display
     * depth here.
     */
    dwBPP = GetDeviceCaps(hdc, BITSPIXEL);

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

    /* ShadowGDI is the only engine that supports windowed PseudoColor */
    if (dwBPP == 8 && !pScreenInfo->fFullScreen) {
        winErrorFVerb(2,
                      "winSetEngine - Windowed && PseudoColor => ShadowGDI\n");
        pScreenInfo->dwEngine = WIN_SERVER_SHADOW_GDI;

        /* Set engine function pointers */
        winSetEngineFunctionsShadowGDI(pScreen);
        return TRUE;
    }

    /* ShadowGDI is the only engine that supports Multi Window Mode */
    if (FALSE
#ifdef XWIN_MULTIWINDOWEXTWM
        || pScreenInfo->fMWExtWM
#endif
#ifdef XWIN_MULTIWINDOW
        || pScreenInfo->fMultiWindow
#endif
        ) {
        winErrorFVerb(2,
                      "winSetEngine - Multi Window or Rootless => ShadowGDI\n");
        pScreenInfo->dwEngine = WIN_SERVER_SHADOW_GDI;

        /* Set engine function pointers */
        winSetEngineFunctionsShadowGDI(pScreen);
        return TRUE;
    }

    /* If the user's choice is supported, we'll use that */
    if (g_dwEnginesSupported & pScreenInfo->dwEnginePreferred) {
        winErrorFVerb(2, "winSetEngine - Using user's preference: %d\n",
                      (int) pScreenInfo->dwEnginePreferred);
        pScreenInfo->dwEngine = pScreenInfo->dwEnginePreferred;

        /* Setup engine function pointers */
        switch (pScreenInfo->dwEngine) {
        case WIN_SERVER_SHADOW_GDI:
            winSetEngineFunctionsShadowGDI(pScreen);
            break;
        case WIN_SERVER_SHADOW_DD:
            winSetEngineFunctionsShadowDD(pScreen);
            break;
        case WIN_SERVER_SHADOW_DDNL:
            winSetEngineFunctionsShadowDDNL(pScreen);
            break;
#ifdef XWIN_PRIMARYFB
        case WIN_SERVER_PRIMARY_DD:
            winSetEngineFunctionsPrimaryDD(pScreen);
            break;
#endif
#ifdef XWIN_NATIVEGDI
        case WIN_SERVER_NATIVE_GDI:
            winSetEngineFunctionsNativeGDI(pScreen);
            break;
#endif
        default:
            FatalError("winSetEngine - Invalid engine type\n");
        }
        return TRUE;
    }

    /* ShadowDDNL has good performance, so why not */
    if (g_dwEnginesSupported & WIN_SERVER_SHADOW_DDNL) {
        winErrorFVerb(2, "winSetEngine - Using Shadow DirectDraw NonLocking\n");
        pScreenInfo->dwEngine = WIN_SERVER_SHADOW_DDNL;

        /* Set engine function pointers */
        winSetEngineFunctionsShadowDDNL(pScreen);
        return TRUE;
    }

    /* ShadowDD is next in line */
    if (g_dwEnginesSupported & WIN_SERVER_SHADOW_DD) {
        winErrorFVerb(2, "winSetEngine - Using Shadow DirectDraw\n");
        pScreenInfo->dwEngine = WIN_SERVER_SHADOW_DD;

        /* Set engine function pointers */
        winSetEngineFunctionsShadowDD(pScreen);
        return TRUE;
    }

    /* ShadowGDI is next in line */
    if (g_dwEnginesSupported & WIN_SERVER_SHADOW_GDI) {
        winErrorFVerb(2, "winSetEngine - Using Shadow GDI DIB\n");
        pScreenInfo->dwEngine = WIN_SERVER_SHADOW_GDI;

        /* Set engine function pointers */
        winSetEngineFunctionsShadowGDI(pScreen);
        return TRUE;
    }

    return TRUE;
}

/*
 * Get procedure addresses for DirectDrawCreate and DirectDrawCreateClipper
 */

Bool
winGetDDProcAddresses(void)
{
    Bool fReturn = TRUE;

    /* Load the DirectDraw library */
    g_hmodDirectDraw = LoadLibraryEx("ddraw.dll", NULL, 0);
    if (g_hmodDirectDraw == NULL) {
        ErrorF("winGetDDProcAddresses - Could not load ddraw.dll\n");
        fReturn = TRUE;
        goto winGetDDProcAddresses_Exit;
    }

    /* Try to get the DirectDrawCreate address */
    g_fpDirectDrawCreate = GetProcAddress(g_hmodDirectDraw, "DirectDrawCreate");
    if (g_fpDirectDrawCreate == NULL) {
        ErrorF("winGetDDProcAddresses - Could not get DirectDrawCreate "
               "address\n");
        fReturn = TRUE;
        goto winGetDDProcAddresses_Exit;
    }

    /* Try to get the DirectDrawCreateClipper address */
    g_fpDirectDrawCreateClipper = GetProcAddress(g_hmodDirectDraw,
                                                 "DirectDrawCreateClipper");
    if (g_fpDirectDrawCreateClipper == NULL) {
        ErrorF("winGetDDProcAddresses - Could not get "
               "DirectDrawCreateClipper address\n");
        fReturn = FALSE;
        goto winGetDDProcAddresses_Exit;
    }

    /*
     * Note: Do not unload ddraw.dll here.  Do it in GiveUp
     */

 winGetDDProcAddresses_Exit:
    /* Unload the DirectDraw library if we failed to initialize */
    if (!fReturn && g_hmodDirectDraw != NULL) {
        FreeLibrary(g_hmodDirectDraw);
        g_hmodDirectDraw = NULL;
    }

    return fReturn;
}

void
winReleaseDDProcAddresses(void)
{
    if (g_hmodDirectDraw != NULL) {
        FreeLibrary(g_hmodDirectDraw);
        g_hmodDirectDraw = NULL;
        g_fpDirectDrawCreate = NULL;
        g_fpDirectDrawCreateClipper = NULL;
    }
}