/*++

Copyright (c) 1998-1999 Microsoft Corporation

Module Name:

    ocpage.cpp

Abstract:

    This file implements the display page setup.

Environment:

    WIN32 User Mode

--*/

#include "precomp.h"
#pragma hdrstop

#include <stdio.h>
#include <tchar.h>
#include <devguid.h>


//
// Defines
//

#define DEFAULT_XRESOLUTION    640
#define DEFAULT_YRESOLUTION    480
#define DEFAULT_BPP            15
#define DEFAULT_VREFRESH       60
#define MIN_XRESOLUTION        800
#define MIN_YRESOLUTION        600


//
// Global Data
//

BOOL g_IsSetupInitComponentInitialized = FALSE;
SETUP_INIT_COMPONENT g_SetupInitComponent;


//
// Function prototypes
//

DWORD
HandleOcInitComponent(
    PSETUP_INIT_COMPONENT SetupInitComponent
    );

DWORD
HandleOcCompleteInstallation(
    VOID
    );

BOOL 
MigrateUnattendedSettings(
    HDEVINFO hDevInfo
    );

VOID
MigrateRegistrySettings(
    HDEVINFO hDevInfo
    );

VOID
MigrateRegistrySettingsBasedOnBusLocation(
    HDEVINFO hDevInfo,
    HKEY hPhysicalDeviceKey,
    DWORD LogicalDevicesCount,
    DWORD BusNumber,
    DWORD Address
    );

VOID
MigrateRegistrySettingsLegacy(
    HDEVINFO hDevInfo,
    HKEY hPhysicalDeviceKey
    );

VOID
MigrateRegistrySettingsHelper(
    HDEVINFO hDevInfo,
    PSP_DEVINFO_DATA pDevInfoData,
    HKEY hPhysicalDeviceKey,
    DWORD LogicalDevicesCount
    );

VOID
MigrateDeviceKeySettings(
    HDEVINFO hDevInfo,
    PSP_DEVINFO_DATA pDevInfoData,
    HKEY hLogicalDeviceKey,
    DWORD Index
    );


//
// Implementation
//


extern "C" {

DWORD
DisplayOcSetupProc(
    IN LPCVOID ComponentId,
    IN LPCVOID SubcomponentId,
    IN UINT Function,
    IN UINT_PTR Param1,
    IN OUT PVOID Param2
    )
{
    switch (Function) {
    
    case OC_PREINITIALIZE:
        return OCFLAG_UNICODE;

    case OC_INIT_COMPONENT:
        return HandleOcInitComponent((PSETUP_INIT_COMPONENT)Param2);

    case OC_QUERY_STATE:
        return SubcompOn; // we are always installed

    case OC_COMPLETE_INSTALLATION:
        return HandleOcCompleteInstallation();

    default:
        break;
    }

    return ERROR_SUCCESS;
}

} // extern "C"


DWORD
HandleOcInitComponent(
    PSETUP_INIT_COMPONENT SetupInitComponent
    )
{
    DWORD retValue = ERROR_SUCCESS;

    if (OCMANAGER_VERSION <= SetupInitComponent->OCManagerVersion) {

        SetupInitComponent->ComponentVersion = OCMANAGER_VERSION;
        
        g_IsSetupInitComponentInitialized = TRUE;
        CopyMemory(
            &g_SetupInitComponent,
            (LPVOID)SetupInitComponent,
            sizeof(SETUP_INIT_COMPONENT));
    
    } else {
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_100);

        retValue = ERROR_CALL_NOT_IMPLEMENTED;
    }

    return retValue;
}


DWORD
HandleOcCompleteInstallation(
    VOID
    )
{
    BOOL bUnattended = FALSE;
    HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
    HKEY hKey;
    
    DeskOpenLog();
    
    hDevInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_DISPLAY,
                                   NULL,
                                   NULL,
                                   DIGCF_PRESENT);

    if (hDevInfo == INVALID_HANDLE_VALUE) {
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_101);
        goto Cleanup;
    }

    if ((g_SetupInitComponent.SetupData.OperationFlags & SETUPOP_BATCH) != 0) {
        
        //
        // Unattended settings
        //

        bUnattended = MigrateUnattendedSettings(hDevInfo);
    }

    if ((!bUnattended) && 
        ((g_SetupInitComponent.SetupData.OperationFlags & SETUPOP_NTUPGRADE) != 0)) {

        //
        // Registry settings
        //

        MigrateRegistrySettings(hDevInfo);
    }
    
Cleanup:

    RegDeleteKey(HKEY_LOCAL_MACHINE, SZ_DETECT_DISPLAY);
    RegDeleteKey(HKEY_LOCAL_MACHINE, SZ_NEW_DISPLAY);

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
                     SZ_UPDATE_SETTINGS_PATH,
                     0,
                     KEY_WRITE,
                     &hKey) == ERROR_SUCCESS) {
    
        SHDeleteKey(hKey, SZ_UPDATE_SETTINGS_KEY);
        RegCloseKey(hKey);
    
    } else {
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_102);
    }
    
    if (hDevInfo != INVALID_HANDLE_VALUE) {
        SetupDiDestroyDeviceInfoList(hDevInfo);
    }

    DeskCloseLog();
    
    return ERROR_SUCCESS;
}


BOOL
MigrateUnattendedSettings(
    HDEVINFO hDevInfo
    )
{
    INFCONTEXT context;
    HINF hInf;
    TCHAR szName[128];
    DWORD value;
    DWORD cFields = 0;
    DWORD BitsPerPel = 0, XResolution = 0, YResolution = 0, VRefresh = 0;
    DWORD UsePreferredMode = 0;
    DWORD AttachedToDesktop = 0;
    SP_DEVINFO_DATA DevInfoData;
    SP_DEVICE_INTERFACE_DATA InterfaceData;
    HKEY hInterfaceKey = (HKEY)INVALID_HANDLE_VALUE;
    HKEY hInterfaceLogicalDeviceKey = (HKEY)INVALID_HANDLE_VALUE;
    DWORD DevInfoIndex = 0;

    //
    // Get the handle to the answer file
    //

    hInf = g_SetupInitComponent.HelperRoutines.GetInfHandle(
        INFINDEX_UNATTENDED,
        g_SetupInitComponent.HelperRoutines.OcManagerContext);

    if ((hInf == NULL) || 
        (hInf == (HINF)INVALID_HANDLE_VALUE)) {
        
        return FALSE;
    }
    
    //
    // Read the settings from the answer file
    //

    if (SetupFindFirstLine(hInf, TEXT("Display"), NULL, &context)) {
        
        do {

            if (SetupGetStringField(&context,
                                    0,
                                    szName,
                                    ARRAYSIZE(szName),
                                    &value)) {
    
                if (lstrcmpi(szName, TEXT("BitsPerPel")) == 0) {

                    if (SetupGetIntField(&context, 1, (PINT)&value)) {

                        ++cFields;
                        BitsPerPel = value;
                    
                    } else {

                        SetupGetStringField(&context,
                                            1,
                                            szName,
                                            ARRAYSIZE(szName),
                                            &value);
                        DeskLogError(LogSevInformation,
                                     IDS_SETUPLOG_MSG_096,
                                     szName);
                    }
                
                } else if (lstrcmpi(szName, TEXT("Xresolution")) == 0) {

                    if (SetupGetIntField(&context, 1, (PINT)&value)) {

                        ++cFields;
                        XResolution = value;
                    
                    } else {
                        
                        SetupGetStringField(&context,
                                            1,
                                            szName,
                                            ARRAYSIZE(szName),
                                            &value);
                        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_060);
                    }
                
                } else if (lstrcmpi(szName, TEXT("YResolution")) == 0) {

                    if (SetupGetIntField(&context, 1, (PINT) &value)) {

                        ++cFields;
                        YResolution = value;
                    
                    } else {
                        
                        SetupGetStringField(&context,
                                            1,
                                            szName,
                                            ARRAYSIZE(szName),
                                            &value);
                        DeskLogError(LogSevInformation,
                                     IDS_SETUPLOG_MSG_062,
                                     szName);
                    }
                
                } else if (lstrcmpi( szName, TEXT("VRefresh")) == 0) {
                    
                    if (SetupGetIntField(&context, 1, (PINT) &value)) {
                        
                        ++cFields;
                        VRefresh = value;
                    
                    } else {
                        
                        SetupGetStringField(&context,
                                            1,
                                            szName,
                                            ARRAYSIZE(szName),
                                            &value);
                        DeskLogError(LogSevInformation,
                                     IDS_SETUPLOG_MSG_064,
                                     szName);
                    }
                
                } else {

                    DeskLogError(LogSevInformation,
                                 IDS_SETUPLOG_MSG_065,
                                 szName);
                }
            }
    
        } while (SetupFindNextLine(&context, &context));

    }

    if (cFields == 0) {

        //
        // The answer file doesn't contain any display settings
        //

        goto Fallout;
    }

    //
    // "Normalize" the display settings
    //

    AttachedToDesktop = 1;

    if (BitsPerPel == 0) {

        DeskLogError(LogSevInformation,
                     IDS_SETUPLOG_MSG_069,
                     DEFAULT_BPP);

        BitsPerPel = DEFAULT_BPP;
    }

    if ((XResolution == 0) || (YResolution == 0)) {

        DeskLogError(LogSevInformation,
                     IDS_SETUPLOG_MSG_067,
                     DEFAULT_XRESOLUTION, 
                     DEFAULT_YRESOLUTION);

        XResolution = DEFAULT_XRESOLUTION;
        YResolution = DEFAULT_YRESOLUTION;
    }                                                  

    if (VRefresh == 0) {
        
        DeskLogError(LogSevInformation,
                     IDS_SETUPLOG_MSG_068,
                     DEFAULT_VREFRESH);

        VRefresh = DEFAULT_VREFRESH;
    }

    //
    // Apply the display settings to all video cards
    //

    DevInfoIndex = 0;
    DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    
    while (SetupDiEnumDeviceInfo(hDevInfo, DevInfoIndex, &DevInfoData)) {

        InterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

        if (!SetupDiCreateDeviceInterface(hDevInfo,
                                          &DevInfoData,
                                          &GUID_DISPLAY_ADAPTER_INTERFACE,
                                          NULL,  
                                          0,
                                          &InterfaceData)) {
            
            DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_103);
            goto NextDevice;
        }

        hInterfaceKey = SetupDiCreateDeviceInterfaceRegKey(hDevInfo,
                                                           &InterfaceData, 
                                                           0,
                                                           KEY_SET_VALUE,
                                                           NULL,
                                                           NULL);

        if (hInterfaceKey == INVALID_HANDLE_VALUE) {
            
            DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_104);
            goto NextDevice;
        }

        if (RegCreateKeyEx(hInterfaceKey, 
                           TEXT("0"),
                           0,
                           NULL,
                           REG_OPTION_NON_VOLATILE,
                           KEY_WRITE,
                           NULL,
                           &hInterfaceLogicalDeviceKey,
                           NULL) != ERROR_SUCCESS) {

            DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_105, 0);
            hInterfaceLogicalDeviceKey = (HKEY)INVALID_HANDLE_VALUE;
            goto NextDevice;
        }

        //
        // Do not use the preferred mode for unattended installs
        //

        UsePreferredMode = 0;
        
        RegSetValueEx(hInterfaceLogicalDeviceKey, 
                      SZ_VU_PREFERRED_MODE, 
                      0, 
                      REG_DWORD, 
                      (PBYTE)&UsePreferredMode, 
                      sizeof(UsePreferredMode));

        //
        // AttachedToDesktop
        //

        RegSetValueEx(hInterfaceLogicalDeviceKey, 
                      SZ_VU_ATTACHED_TO_DESKTOP, 
                      0, 
                      REG_DWORD, 
                      (PBYTE)&AttachedToDesktop, 
                      sizeof(AttachedToDesktop));

        //
        // BitsPerPel
        //

        if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                          SZ_VU_BITS_PER_PEL, 
                          0, 
                          REG_DWORD, 
                          (PBYTE)&BitsPerPel, 
                          sizeof(BitsPerPel)) == ERROR_SUCCESS) {

            DeskLogError(LogSevInformation, 
                         IDS_SETUPLOG_MSG_106, 
                         BitsPerPel);
        }

        //
        // XResolution
        //

        if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                          SZ_VU_X_RESOLUTION, 
                          0, 
                          REG_DWORD, 
                          (PBYTE)&XResolution, 
                          sizeof(XResolution)) == ERROR_SUCCESS) {

            DeskLogError(LogSevInformation, 
                         IDS_SETUPLOG_MSG_107, 
                         XResolution);
        }

        //
        // dwYResolution
        //

        if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                          SZ_VU_Y_RESOLUTION, 
                          0, 
                          REG_DWORD, 
                          (PBYTE)&YResolution, 
                          sizeof(YResolution)) == ERROR_SUCCESS) {

            DeskLogError(LogSevInformation, 
                         IDS_SETUPLOG_MSG_108, 
                         YResolution);
        }

        //
        // dwVRefresh
        //

        if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                          SZ_VU_VREFRESH, 
                          0, 
                          REG_DWORD, 
                          (PBYTE)&VRefresh, 
                          sizeof(VRefresh)) == ERROR_SUCCESS) {

            DeskLogError(LogSevInformation, 
                         IDS_SETUPLOG_MSG_109, 
                         VRefresh);
        }

NextDevice:

        if (hInterfaceLogicalDeviceKey != INVALID_HANDLE_VALUE) {
        
            RegCloseKey(hInterfaceLogicalDeviceKey);
            hInterfaceLogicalDeviceKey = (HKEY)INVALID_HANDLE_VALUE;
        }

        if (hInterfaceKey != INVALID_HANDLE_VALUE) {
        
            RegCloseKey(hInterfaceKey);
            hInterfaceKey = (HKEY)INVALID_HANDLE_VALUE;
        }

        DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
        ++DevInfoIndex;
    }

Fallout:

    return (cFields != 0);
}


VOID
MigrateRegistrySettings(
    HDEVINFO hDevInfo
    )
{
    HKEY hKey = 0, hPhysicalDeviceKey = 0;
    DWORD PhysicalDevicesCount = 0, LogicalDevicesCount = 0;
    DWORD cb = 0, PhysicalDevice = 0, Failed = 0;
    TCHAR Buffer[20];
    BOOL IsLegacy;
    DWORD BusNumber = 0, Address = 0;

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
                     SZ_UPDATE_SETTINGS,
                     0,
                     KEY_READ,
                     &hKey) != ERROR_SUCCESS) {

        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_075);

        hKey = 0;
        goto Cleanup;
    }

    cb = sizeof(DWORD);
    if ((RegQueryValueEx(hKey,
                         SZ_UPGRADE_FAILED_ALLOW_INSTALL,
                         NULL,
                         NULL,
                         (LPBYTE)&Failed,
                         &cb) == ERROR_SUCCESS) &&
         (Failed != 0)) {

        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_076);
        goto Cleanup;
    }

    cb = sizeof(PhysicalDevicesCount);
    if (RegQueryValueEx(hKey,        
                        SZ_VU_COUNT,
                        0,
                        NULL,
                        (PBYTE)&PhysicalDevicesCount,
                        &cb) != ERROR_SUCCESS) {
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_110);
        goto Cleanup;
    }

    for(PhysicalDevice = 0; 
        PhysicalDevice < PhysicalDevicesCount; 
        PhysicalDevice++) {
        
        _tcscpy(Buffer, SZ_VU_PHYSICAL);
        _stprintf(Buffer + _tcslen(Buffer), TEXT("%d"), PhysicalDevice);

        if (RegOpenKeyEx(hKey, 
                         Buffer,
                         0,
                         KEY_READ,
                         &hPhysicalDeviceKey) != ERROR_SUCCESS) {
        
            DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_111);
            hPhysicalDeviceKey = 0;
            goto NextPhysicalDevice;
        }

        //
        // Get the count of logical devices 
        //

        cb = sizeof(LogicalDevicesCount);
        if (RegQueryValueEx(hPhysicalDeviceKey,
                            SZ_VU_COUNT,
                            0,
                            NULL,
                            (PBYTE)&LogicalDevicesCount,
                            &cb) != ERROR_SUCCESS) {
            
            DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_112);
            goto NextPhysicalDevice;
        }

        //
        // Get the bus number and address 
        //

        IsLegacy = TRUE;
        cb = sizeof(BusNumber);
        if (RegQueryValueEx(hPhysicalDeviceKey,
                            SZ_VU_BUS_NUMBER,
                            0,
                            NULL,
                            (PBYTE)&BusNumber,
                            &cb) == ERROR_SUCCESS) {

            cb = sizeof(Address);
            if (RegQueryValueEx(hPhysicalDeviceKey,
                            SZ_VU_ADDRESS,
                            0,
                            NULL,
                            (PBYTE)&Address,
                            &cb) == ERROR_SUCCESS) {
            
                IsLegacy = FALSE;
            }
        }

        if (!IsLegacy) {

            MigrateRegistrySettingsBasedOnBusLocation(hDevInfo,
                                                      hPhysicalDeviceKey, 
                                                      LogicalDevicesCount,
                                                      BusNumber,
                                                      Address);
        
        } else if ((PhysicalDevicesCount == 1) &&
                   (LogicalDevicesCount == 1)) {

            //
            // If legacy, we support migration of a single device.
            //

            MigrateRegistrySettingsLegacy(hDevInfo,
                                          hPhysicalDeviceKey);
        }

NextPhysicalDevice:

        if (hPhysicalDeviceKey != 0) {
        
            RegCloseKey(hPhysicalDeviceKey);
            hPhysicalDeviceKey = 0;
        }
    }

Cleanup:

    if (hKey != 0) {
        RegCloseKey(hKey);
    }

    return;
}


VOID
MigrateRegistrySettingsBasedOnBusLocation(
    HDEVINFO hDevInfo,
    HKEY hPhysicalDeviceKey,
    DWORD LogicalDevicesCount,
    DWORD BusNumber,
    DWORD Address
    )
{
    SP_DEVINFO_DATA DevInfoData;
    DWORD CurrentBusNumber = 0, CurrentAddress = 0;
    DWORD DevInfoIndex = 0;
    BOOL bFound = FALSE;

    //
    // Let's find the device with the same bus number and address
    //

    DevInfoIndex = 0;
    DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    
    while (SetupDiEnumDeviceInfo(hDevInfo, DevInfoIndex, &DevInfoData)) {

        if (SetupDiGetDeviceRegistryProperty(hDevInfo,
                                             &DevInfoData,
                                             SPDRP_BUSNUMBER,
                                             NULL,
                                             (PBYTE)&CurrentBusNumber,
                                             sizeof(CurrentBusNumber),
                                             NULL) && 
            
            (CurrentBusNumber == BusNumber) &&

            SetupDiGetDeviceRegistryProperty(hDevInfo,
                                             &DevInfoData,
                                             SPDRP_ADDRESS,
                                             NULL,
                                             (PBYTE)&CurrentAddress,
                                             sizeof(CurrentAddress),
                                             NULL) &&

            (CurrentAddress == Address)) {
            
            //
            // We found the device with the same bus number and address
            // So ... migrate the settings 
            //
                        
            MigrateRegistrySettingsHelper(hDevInfo,
                                          &DevInfoData,
                                          hPhysicalDeviceKey,
                                          LogicalDevicesCount);
            
            //
            // We are done
            //

            bFound = TRUE;
            break;
        }

        //
        // Next device
        //

        DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
        ++DevInfoIndex;
    }

    if (!bFound) {

        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_113);
    }

    return;
}


VOID
MigrateRegistrySettingsLegacy(
    HDEVINFO hDevInfo,
    HKEY hPhysicalDeviceKey
    )
{
    SP_DEVINFO_DATA DevInfoData0, DevInfoData1;
    
    DevInfoData0.cbSize = sizeof(SP_DEVINFO_DATA);
    if (!SetupDiEnumDeviceInfo(hDevInfo, 0, &DevInfoData0)) {
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_115);
        return;
    }

    DevInfoData1.cbSize = sizeof(SP_DEVINFO_DATA);
    if (SetupDiEnumDeviceInfo(hDevInfo, 1, &DevInfoData1)) {
        
        //
        // There are at least 2 video devices in the system
        // We don't know which device to apply the settings to.
        // So, just ignore this case
        //
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_114);
        return;
    }

    MigrateRegistrySettingsHelper(hDevInfo,
                                  &DevInfoData0,
                                  hPhysicalDeviceKey,
                                  1); // there is only one logical device
}


VOID
MigrateRegistrySettingsHelper(
    HDEVINFO hDevInfo,
    PSP_DEVINFO_DATA pDevInfoData,
    HKEY hPhysicalDeviceKey,
    DWORD LogicalDevicesCount
    )
{
    SP_DEVICE_INTERFACE_DATA InterfaceData;
    HKEY hInterfaceKey = 0;
    HKEY hInterfaceLogicalDeviceKey = 0;
    HKEY hLogicalDeviceKey = 0;
    TCHAR Buffer[20];
    DWORD cb = 0, LogicalDevice = 0;
    DWORD UsePreferredMode = 0;
    DWORD AttachedToDesktop = 0;
    DWORD RelativeX = 0;
    DWORD RelativeY = 0;
    DWORD BitsPerPel = 0;
    DWORD XResolution = 0;
    DWORD YResolution = 0;
    DWORD VRefresh = 0;
    DWORD Flags = 0;

    InterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    if (!SetupDiCreateDeviceInterface(hDevInfo,
                                      pDevInfoData,
                                      &GUID_DISPLAY_ADAPTER_INTERFACE,
                                      NULL,  
                                      0,
                                      &InterfaceData)) {
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_103);
        return;
    }

    hInterfaceKey = SetupDiCreateDeviceInterfaceRegKey(hDevInfo,
                                                       &InterfaceData, 
                                                       0,
                                                       KEY_SET_VALUE,
                                                       NULL,
                                                       NULL);

    if (hInterfaceKey == INVALID_HANDLE_VALUE) {
        
        DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_104);
        return;
    }

    for (LogicalDevice = 0;
         LogicalDevice < LogicalDevicesCount;
         ++LogicalDevice) {

        _tcscpy(Buffer, SZ_VU_LOGICAL);
        _stprintf(Buffer + _tcslen(Buffer),  TEXT("%d"), LogicalDevice);

        if (RegOpenKeyEx(hPhysicalDeviceKey, 
                        Buffer,
                        0,
                        KEY_READ,
                        &hLogicalDeviceKey) != ERROR_SUCCESS) {
            
            //
            // We can not go on with this physical device
            // The LogicalDevices order is important for DualView
            //
            
            DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_116);
            break;
        }

        _stprintf(Buffer,  TEXT("%d"), LogicalDevice);
        if (RegCreateKeyEx(hInterfaceKey, 
                           Buffer,
                           0,
                           NULL,
                           REG_OPTION_NON_VOLATILE,
                           KEY_WRITE,
                           NULL,
                           &hInterfaceLogicalDeviceKey,
                           NULL) != ERROR_SUCCESS) {

            //
            // We can not go on with this physical device
            // The LogicalDevices order is important for DualView
            //
            
            DeskLogError(LogSevInformation, IDS_SETUPLOG_MSG_105, LogicalDevice);
            RegCloseKey(hLogicalDeviceKey);
            break;
        }

        //
        // Use preferred mode?
        //

        cb = sizeof(XResolution);
        if (RegQueryValueEx(hLogicalDeviceKey,
                            SZ_VU_X_RESOLUTION,
                            0,
                            NULL,
                            (PBYTE)&XResolution,
                            &cb) != ERROR_SUCCESS) {
    
            XResolution = DEFAULT_XRESOLUTION;
        }
    
        cb = sizeof(YResolution);
        if (RegQueryValueEx(hLogicalDeviceKey,
                            SZ_VU_Y_RESOLUTION,
                            0,
                            NULL,
                            (PBYTE)&YResolution,
                            &cb) != ERROR_SUCCESS) {
    
            YResolution = DEFAULT_YRESOLUTION;
        }

        UsePreferredMode = ((XResolution < MIN_XRESOLUTION) || 
                            (YResolution < MIN_YRESOLUTION));
        
        RegSetValueEx(hInterfaceLogicalDeviceKey, 
                      SZ_VU_PREFERRED_MODE, 
                      0, 
                      REG_DWORD, 
                      (PBYTE)&UsePreferredMode, 
                      sizeof(UsePreferredMode));

        if (UsePreferredMode) {

            DeskLogError(LogSevInformation, 
                         IDS_SETUPLOG_MSG_130);

        } else {

            //
            // AttachedToDesktop
            //
        
            cb = sizeof(AttachedToDesktop);
            if (RegQueryValueEx(hLogicalDeviceKey,
                                SZ_VU_ATTACHED_TO_DESKTOP,
                                0,
                                NULL,
                                (PBYTE)&AttachedToDesktop,
                                &cb) == ERROR_SUCCESS) {
        
                if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                                  SZ_VU_ATTACHED_TO_DESKTOP, 
                                  0, 
                                  REG_DWORD, 
                                  (PBYTE)&AttachedToDesktop, 
                                  sizeof(AttachedToDesktop)) == ERROR_SUCCESS) {
    
                    DeskLogError(LogSevInformation, 
                                 IDS_SETUPLOG_MSG_117, 
                                 AttachedToDesktop);
                }
            }
    
            //
            // RelativeX
            //
        
            cb = sizeof(RelativeX);
            if (RegQueryValueEx(hLogicalDeviceKey,
                            SZ_VU_RELATIVE_X,
                            0,
                            NULL,
                            (PBYTE)&RelativeX,
                            &cb) == ERROR_SUCCESS) {
        
                if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                                  SZ_VU_RELATIVE_X, 
                                  0, 
                                  REG_DWORD, 
                                  (PBYTE)&RelativeX, 
                                  sizeof(RelativeX)) == ERROR_SUCCESS) {
    
                DeskLogError(LogSevInformation, 
                             IDS_SETUPLOG_MSG_118, 
                             RelativeX);
                }
    
            }
        
            //
            // RelativeY
            //
        
            cb = sizeof(RelativeY);
            if (RegQueryValueEx(hLogicalDeviceKey,
                                SZ_VU_RELATIVE_Y,
                                0,
                                NULL,
                                (PBYTE)&RelativeY,
                                &cb) == ERROR_SUCCESS) {
        
                if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                                  SZ_VU_RELATIVE_Y, 
                                  0, 
                                  REG_DWORD, 
                                  (PBYTE)&RelativeY, 
                                  sizeof(RelativeY)) == ERROR_SUCCESS) {
                
                    DeskLogError(LogSevInformation, 
                                 IDS_SETUPLOG_MSG_119, 
                                 RelativeY);
                }
            }
    
            //
            // BitsPerPel
            //
        
            cb = sizeof(BitsPerPel);
            if (RegQueryValueEx(hLogicalDeviceKey,
                                SZ_VU_BITS_PER_PEL,
                                0,
                                NULL,
                                (PBYTE)&BitsPerPel,
                                &cb) == ERROR_SUCCESS) {
        
                if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                                  SZ_VU_BITS_PER_PEL, 
                                  0, 
                                  REG_DWORD, 
                                  (PBYTE)&BitsPerPel, 
                                  sizeof(BitsPerPel)) == ERROR_SUCCESS) {
                    
                    DeskLogError(LogSevInformation, 
                                 IDS_SETUPLOG_MSG_120, 
                                 BitsPerPel);
                }
            }
        
            //
            // XResolution
            //
        
            if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                              SZ_VU_X_RESOLUTION, 
                              0, 
                              REG_DWORD, 
                              (PBYTE)&XResolution, 
                              sizeof(XResolution)) == ERROR_SUCCESS) {
                
                DeskLogError(LogSevInformation, 
                             IDS_SETUPLOG_MSG_121, 
                             XResolution);
            }
        
            //
            // dwYResolution
            //
        
            if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                              SZ_VU_Y_RESOLUTION, 
                              0, 
                              REG_DWORD, 
                              (PBYTE)&YResolution, 
                              sizeof(YResolution)) == ERROR_SUCCESS) {
                
                DeskLogError(LogSevInformation, 
                             IDS_SETUPLOG_MSG_122, 
                             YResolution);
            }
        
            //
            // dwVRefresh
            //
        
            cb = sizeof(VRefresh);
            if (RegQueryValueEx(hLogicalDeviceKey,
                                SZ_VU_VREFRESH,
                                0,
                                NULL,
                                (PBYTE)&VRefresh,
                                &cb) == ERROR_SUCCESS) {
            
                if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                                  SZ_VU_VREFRESH, 
                                  0, 
                                  REG_DWORD, 
                                  (PBYTE)&VRefresh, 
                                  sizeof(VRefresh)) == ERROR_SUCCESS) {
                    
                    DeskLogError(LogSevInformation, 
                                 IDS_SETUPLOG_MSG_123, 
                                 VRefresh);
                }
            }
        
            //
            // Flags
            //
        
            cb = sizeof(Flags);
            if (RegQueryValueEx(hLogicalDeviceKey,
                                SZ_VU_FLAGS,
                                0,
                                NULL,
                                (PBYTE)&Flags,
                                &cb) == ERROR_SUCCESS) {
        
                if (RegSetValueEx(hInterfaceLogicalDeviceKey, 
                                  SZ_VU_FLAGS, 
                                  0, 
                                  REG_DWORD, 
                                  (PBYTE)&Flags, 
                                  sizeof(Flags)) == ERROR_SUCCESS) {
                    
                    DeskLogError(LogSevInformation, 
                                 IDS_SETUPLOG_MSG_124, 
                                 Flags);
                }
            }
        }
    
        //
        // Migrate the hardware acceleration and the pruning mode
        //

        MigrateDeviceKeySettings(hDevInfo,
                                 pDevInfoData,
                                 hLogicalDeviceKey,
                                 LogicalDevice);

        RegCloseKey(hLogicalDeviceKey);
        RegCloseKey(hInterfaceLogicalDeviceKey);
    }

    RegCloseKey(hInterfaceKey);
}


VOID
MigrateDeviceKeySettings(
    HDEVINFO hDevInfo,
    PSP_DEVINFO_DATA pDevInfoData,
    HKEY hLogicalDeviceKey,
    DWORD Index
    )
{
    HKEY hkPnP = (HKEY)INVALID_HANDLE_VALUE;
    HKEY hkDevice = (HKEY)INVALID_HANDLE_VALUE;
    LPTSTR pBuffer = NULL;
    DWORD dwSize, len, cb;
    DWORD HwAcceleration, PruningMode;

    //
    // Open the PnP key
    //

    hkPnP = SetupDiOpenDevRegKey(hDevInfo,
                                 pDevInfoData,
                                 DICS_FLAG_GLOBAL,
                                 0,
                                 DIREG_DEV,
                                 KEY_READ);

    if (hkPnP == INVALID_HANDLE_VALUE) {

        DeskLogError(LogSevInformation, 
                     IDS_SETUPLOG_MSG_127,
                     TEXT("SetupDiOpenDevRegKey"));

        goto Fallout;
    }

    //
    // Try to get the GUID from the PnP key
    //

    dwSize = 0;
    if (RegQueryValueEx(hkPnP,
                        SZ_GUID,
                        0,
                        NULL,
                        NULL,
                        &dwSize) != ERROR_SUCCESS) {
        
        DeskLogError(LogSevInformation, 
                     IDS_SETUPLOG_MSG_127,
                     TEXT("RegQueryValueEx"));

        goto Fallout;
    }

    len = _tcslen(SZ_VIDEO_DEVICES);
    
    pBuffer = (LPTSTR)LocalAlloc(LPTR, 
                                 dwSize + (len + 6) * sizeof(TCHAR));
    
    if (pBuffer == NULL) {

        DeskLogError(LogSevInformation, 
                     IDS_SETUPLOG_MSG_127,
                     TEXT("LocalAlloc"));

        goto Fallout;
    }
    
    _tcscpy(pBuffer, SZ_VIDEO_DEVICES);

    if (RegQueryValueEx(hkPnP,
                        SZ_GUID,
                        0,
                        NULL,
                        (PBYTE)(pBuffer + len),
                        &dwSize) != ERROR_SUCCESS) {
        
        DeskLogError(LogSevInformation, 
                     IDS_SETUPLOG_MSG_127,
                     TEXT("RegQueryValueEx"));

        goto Fallout;
    }

    _stprintf(pBuffer + _tcslen(pBuffer), L"\\%04d", Index);

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
                     pBuffer,
                     0,
                     KEY_WRITE,
                     &hkDevice) != ERROR_SUCCESS) {
        
        DeskLogError(LogSevInformation, 
                     IDS_SETUPLOG_MSG_127,
                     TEXT("RegOpenKeyEx"));

        hkDevice = (HKEY)INVALID_HANDLE_VALUE;
        goto Fallout;
    }

    //
    // Hardware acceleration
    //

    cb = sizeof(HwAcceleration);
    if (RegQueryValueEx(hLogicalDeviceKey,
                        SZ_HW_ACCELERATION,
                        0,
                        NULL,
                        (PBYTE)&HwAcceleration,
                        &cb) == ERROR_SUCCESS) {

        RegSetValueEx(hkDevice, 
                      SZ_HW_ACCELERATION, 
                      0, 
                      REG_DWORD, 
                      (PBYTE)&HwAcceleration, 
                      sizeof(HwAcceleration));
    }

    //
    // Pruning mode
    //

    cb = sizeof(PruningMode);
    if (RegQueryValueEx(hLogicalDeviceKey,
                        SZ_PRUNNING_MODE,
                        0,
                        NULL,
                        (PBYTE)&PruningMode,
                        &cb) == ERROR_SUCCESS) {

        RegSetValueEx(hkDevice, 
                      SZ_PRUNNING_MODE, 
                      0, 
                      REG_DWORD, 
                      (PBYTE)&PruningMode, 
                      sizeof(PruningMode));
    }

Fallout:

    if (hkPnP != INVALID_HANDLE_VALUE) {
        RegCloseKey(hkPnP);
    }

    if (pBuffer != NULL) {
        LocalFree(pBuffer);
    }
    
    if (hkDevice != INVALID_HANDLE_VALUE) {
        RegCloseKey(hkDevice);
    }
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


// type constants for DrawArrow

#define AW_TOP      1   // top
#define AW_BOTTOM   2   // bottom
#define AW_LEFT     3   // left
#define AW_RIGHT    4   // right

#define CCH_MAX_STRING          256

typedef struct _NEW_DESKTOP_PARAM {
    LPDEVMODE lpdevmode;
    LPTSTR pwszDevice;
    DWORD  dwTimeout;
} NEW_DESKTOP_PARAM, *PNEW_DESKTOP_PARAM;

// table of resolutions that we show off.
// if the resolution is larger, then we show that one too.

typedef struct tagRESTAB {
    INT xRes;
    INT yRes;
    COLORREF crColor;           // color to paint this resolution
} RESTAB;

RESTAB ResTab[] ={
   { 1600, 1200, RGB(255,0,0)},
   { 1280, 1024, RGB(0,255,0)},
   { 1152,  900, RGB(0,0,255)},
   { 1024,  768, RGB(255,0,0)},
   {  800,  600, RGB(0,255,0)},
   // 640x480 or 640x400 handled specially
   { 0, 0, 0}         // end of table
   };

DWORD WINAPI
ApplyNowThd(
    LPVOID lpThreadParameter
    );

void
DrawBmp(
    HDC hDC
    );

VOID 
LabelResolution( 
    HDC hDC, 
    INT xmin, 
    INT ymin, 
    INT xmax, 
    INT ymax 
    );

static VOID 
PaintRect(
    HDC hDC,       
    INT lowx,      
    INT lowy,      
    INT highx,     
    INT highy,     
    COLORREF rgb,  
    UINT idString  
    );

VOID 
DrawArrows( 
    HDC hDC, 
    INT xRes, 
    INT yRes 
    );

static VOID 
LabelRect(
    HDC hDC, 
    PRECT pRect, 
    UINT idString 
    );

static VOID 
DrawArrow( 
    HDC hDC, 
    INT type, 
    INT xPos, 
    INT yPos, 
    COLORREF crPenColor 
    );

VOID 
MakeRect( 
    PRECT pRect, 
    INT xmin, 
    INT ymin, 
    INT xmax, 
    INT ymax
    );

DWORD
DisplayTestSettingsW(
    LPDEVMODEW lpDevMode,
    LPWSTR     pwszDevice,
    DWORD      dwTimeout
    )
{
    HANDLE hThread;
    DWORD idThread;
    DWORD bTest;
    NEW_DESKTOP_PARAM desktopParam;

    if (!lpDevMode || !pwszDevice) 
        return FALSE;

    if (dwTimeout == 0) 
        dwTimeout = NORMAL_TIMEOUT;

    desktopParam.lpdevmode = lpDevMode; 
    desktopParam.pwszDevice = pwszDevice;
    desktopParam.dwTimeout = dwTimeout;

    hThread = CreateThread(NULL,
                           4096,
                           ApplyNowThd,
                           (LPVOID) &desktopParam,
                           SYNCHRONIZE | THREAD_QUERY_INFORMATION,
                           &idThread
                           );

    WaitForSingleObject(hThread, INFINITE);
    GetExitCodeThread(hThread, &bTest);
    CloseHandle(hThread);

    return bTest;
}


DWORD WINAPI
ApplyNowThd(
    LPVOID lpThreadParameter
    )
{
    PNEW_DESKTOP_PARAM lpDesktopParam = (PNEW_DESKTOP_PARAM) lpThreadParameter;
    HDESK hdsk = NULL;
    HDESK hdskDefault = NULL;
    BOOL bTest = FALSE;
    HDC hdc;


    //
    // HACK:
    // We need to make a USER call before calling the desktop stuff so we can
    // sure our threads internal data structure are associated with the default
    // desktop.
    // Otherwise USER has problems closing the desktop with our thread on it.
    //
    GetSystemMetrics(SM_CXSCREEN);

    //
    // Create the desktop
    //
    hdskDefault = GetThreadDesktop(GetCurrentThreadId());

    if (hdskDefault != NULL) {
        hdsk = CreateDesktop(TEXT("Display.Cpl Desktop"),
                             lpDesktopParam->pwszDevice,
                             lpDesktopParam->lpdevmode,
                             0,
                             MAXIMUM_ALLOWED,
                             NULL);

        if (hdsk != NULL) {
            //
            // use the desktop for this thread
            //
            if (SetThreadDesktop(hdsk)) {
                hdc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
                if (hdc) {
                    DrawBmp(hdc);
                    DeleteDC(hdc);
                    bTest = TRUE;
                }

                //
                // Sleep for some seconds so you have time to look at the screen.
                //
                Sleep(lpDesktopParam->dwTimeout);
            }
        }

        //
        // Reset the thread to the right desktop
        //
        SetThreadDesktop(hdskDefault);
        SwitchDesktop(hdskDefault);

        //
        // Can only close the desktop after we have switched to the new one.
        //
        if (hdsk != NULL) {
            CloseDesktop(hdsk);
        }
    }

    ExitThread((DWORD) bTest);
    return 0;
}


/****************************************************************************

    FUNCTION: DrawBmp

    PURPOSE:  Show off a fancy screen so the user has some idea
              of what will be seen given this resolution, colour
              depth and vertical refresh rate.  Note that we do not
              try to simulate the font sizes.

****************************************************************************/
void
DrawBmp(
    HDC hDC
    )
{
    INT    nBpp;          // bits per pixel
    INT    nWidth;        // width of screen in pixels
    INT    nHeight;       // height of screen in pixels
    INT    xUsed,yUsed;   // amount of x and y to use for dense bitmap
    INT    dx,dy;         // delta x and y for color bars
    RECT   rct;           // rectangle for passing bounds
    HFONT  hPrevFont=0;   // previous font in DC
    HFONT  hNewFont;      // new font if possible
    HPEN   hPrevPen;      // previous pen handle
    INT    x,y,i;
    INT    off;           // offset in dx units

    hNewFont = (HFONT)NULL;

    if (hNewFont)                              // if no font, use old
        hPrevFont= (HFONT) SelectObject(hDC, hNewFont);

    // get surface information
    nBpp= GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
    nWidth= GetDeviceCaps(hDC, HORZRES);
    nHeight= GetDeviceCaps(hDC, VERTRES);

    // background for everything is yellow.
    PaintRect(hDC, 0, 0, nWidth, nHeight, RGB(255,255,0), 0);
    LabelResolution( hDC, 0,0,nWidth, nHeight );

    // Background for various resolutions
    // biggest ones first
    for(i = 0; ResTab[i].xRes !=0; i++) {

        // Only draw if it will show
        //if ((nWidth >= ResTab[i].xRes) | ( nHeight>=ResTab[i].yRes ) )
        if ((nWidth >= ResTab[i].xRes) || (nHeight >= ResTab[i].yRes)) {
           PaintRect(hDC, 0, 0, ResTab[i].xRes, ResTab[i].yRes, ResTab[i].crColor, 0);
           LabelResolution(hDC, 0, 0, ResTab[i].xRes, ResTab[i].yRes);
        }
    }

    // color bars - only in standard vga area

    xUsed= min(nWidth, 640);    // only use vga width
    yUsed= min(nHeight, 480);   // could be 400 on some boards
    dx = xUsed / 2;
    dy = yUsed / 6;

    PaintRect(hDC, 0,    0, dx, dy*1,  RGB(255,0,0),   IDS_COLOR_RED);
    PaintRect(hDC, 0, dy*1, dx, dy*2,  RGB(0,255,0),   IDS_COLOR_GREEN);
    PaintRect(hDC, 0, dy*2, dx, dy*3,  RGB(0,0,255),   IDS_COLOR_BLUE);
    PaintRect(hDC, 0, dy*3, dx, dy*4,  RGB(255,255,0), IDS_COLOR_YELLOW);
    PaintRect(hDC, 0, dy*4, dx, dy*5,  RGB(255,0,255), IDS_COLOR_MAGENTA);
    PaintRect(hDC, 0, dy*5, dx, yUsed, RGB(0,255,255), IDS_COLOR_CYAN);

    // gradations of colors for true color detection
    for (x = dx; x < xUsed; x++) {
        int level;

        level = 255 - (256 * (x-dx)) / dx;
        PaintRect(hDC, x, dy*0, x+1,  dy*1, RGB(level,0,0 ), 0);
        PaintRect(hDC, x, dy*1, x+1,  dy*2, RGB(0,level,0 ), 0);
        PaintRect(hDC, x, dy*2, x+1,  dy*3, RGB(0,0,level ), 0);
        PaintRect(hDC, x, dy*5, x+1,  dy*6, RGB(level,level,level), 0);
    }

    MakeRect(&rct, dx, 0, dx * 2, dy * 1);
    LabelRect(hDC, &rct, IDS_RED_SHADES);
    MakeRect(&rct, dx, dy, dx * 2, dy * 2);
    LabelRect(hDC, &rct, IDS_GREEN_SHADES);
    MakeRect(&rct, dx, 2 * dy, dx * 2, dy * 3);
    LabelRect(hDC, &rct, IDS_BLUE_SHADES);
    MakeRect(&rct, dx, 5 * dy, dx * 2, dy * 6);
    LabelRect(hDC, &rct, IDS_GRAY_SHADES);

    // horizontal lines for interlace detection
    off = 3;
    PaintRect(hDC, dx, dy*off, xUsed, dy * (off+1), RGB(255,255,255), 0); // white
    hPrevPen = (HPEN) SelectObject(hDC, GetStockObject(BLACK_PEN));

    for (y = dy * off; y < dy * (off+1); y = y+2) {
        MoveToEx(hDC, dx,     y, NULL);
        LineTo(  hDC, dx * 2, y);
    }

    SelectObject(hDC, hPrevPen);
    MakeRect(&rct, dx, dy * off, dx * 2, dy * (off+1));
    LabelRect(hDC, &rct, IDS_PATTERN_HORZ);

    // vertical lines for bad dac detection
    off = 4;
    PaintRect(hDC, dx, dy * off, xUsed,dy * (off+1), RGB(255,255,255), 0); // white
    hPrevPen= (HPEN) SelectObject(hDC, GetStockObject(BLACK_PEN));

    for (x = dx; x < xUsed; x = x+2) {
        MoveToEx(hDC, x, dy * off, NULL);
        LineTo(  hDC, x, dy * (off+1));
    }

    SelectObject(hDC, hPrevPen);
    MakeRect(&rct, dx, dy * off, dx * 2, dy * (off+1));
    LabelRect(hDC, &rct, IDS_PATTERN_VERT);

    DrawArrows(hDC, nWidth, nHeight);

    LabelResolution(hDC, 0, 0, xUsed, yUsed);

    // delete created font if one was created
    if (hPrevFont) {
        hPrevFont = (HFONT) SelectObject(hDC, hPrevFont);
        DeleteObject(hPrevFont);
    }
}


/****************************************************************************

    FUNCTION: LabelResolution

    PURPOSE:  Labels the resolution in a form a user may understand.
              FEATURE: We could label vertically too.

****************************************************************************/

VOID 
LabelResolution( 
    HDC hDC, 
    INT xmin, 
    INT ymin, 
    INT xmax, 
    INT ymax 
    )
{
   TCHAR szRes[120];    // text for resolution
   TCHAR szFmt[CCH_MAX_STRING];    // format string
   SIZE  size;
   INT iStatus;

   iStatus = LoadString(hInstance, ID_DSP_TXT_XBYY /* remove IDS_RESOLUTION_FMT */, szFmt, ARRAYSIZE(szFmt) );
   if (!iStatus || iStatus == sizeof(szFmt)) {
       lstrcpy(szFmt,TEXT("%d x %d"));   // make sure we get something
   }

   wsprintf(szRes, szFmt, xmax, ymax);

   SetBkMode(hDC, TRANSPARENT);
   SetTextColor(hDC, RGB(0,0,0));

   GetTextExtentPoint32(hDC, szRes, lstrlen(szRes), &size);

   // Text near bottom of screen ~10 pixels from bottom
   TextOut(hDC, xmax/2 - size.cx/2, ymax - 10-size.cy, szRes, lstrlen(szRes));
}


/****************************************************************************

    FUNCTION: PaintRect

    PURPOSE:  Color in a rectangle and label it.

****************************************************************************/

static VOID 
PaintRect(
    HDC hDC,         // DC to paint
    INT lowx,        // coordinates describing rectangle to fill
    INT lowy,        //
    INT highx,       //
    INT highy,       //
    COLORREF rgb,    // color to fill in rectangle with
    UINT idString    // resource ID to use to label or 0 is none
    )  
{
    RECT rct;
    HBRUSH hBrush;

    MakeRect(&rct, lowx, lowy, highx, highy);

    hBrush = CreateSolidBrush(rgb);
    if (hBrush)
    {
        FillRect(hDC, &rct, hBrush);
        DeleteObject(hBrush);
    }

    LabelRect(hDC, &rct, idString);
}


/****************************************************************************

    FUNCTION: DrawArrows

    PURPOSE:  Draw all the arrows showing edges of resolution.

****************************************************************************/

VOID 
DrawArrows( 
    HDC hDC, 
    INT xRes, 
    INT yRes 
    )
{
    INT dx,dy;
    INT x,y;
    COLORREF color= RGB(0,0,0);    // color of arrow

    dx= xRes/8;
    dy= yRes/8;

    for (x = 0; x < xRes; x += dx) {
        DrawArrow(hDC, AW_TOP,    dx/2+x,   0,      color);
        DrawArrow(hDC, AW_BOTTOM, dx/2+x,   yRes-1, color);
    }

    for (y = 0; y < yRes; y += dy) {
        DrawArrow(hDC, AW_LEFT,       0, dy/2+y,   color);
        DrawArrow(hDC, AW_RIGHT, xRes-1, dy/2+y,   color);
    }
}


/****************************************************************************

    FUNCTION: LabelRect

    PURPOSE:  Label a rectangle with centered text given resource ID.

****************************************************************************/

static VOID 
LabelRect(
    HDC hDC, 
    PRECT pRect, 
    UINT idString 
    )
{
    UINT iStatus;
    INT xStart, yStart;
    SIZE size;              // for size of string
    TCHAR szMsg[CCH_MAX_STRING];

    if (idString == 0)     // make it easy to ignore call
        return;

    SetBkMode(hDC, OPAQUE);
    SetBkColor(hDC, RGB(0,0,0));
    SetTextColor(hDC, RGB(255,255,255));

    // center
    xStart = (pRect->left + pRect->right) / 2;
    yStart = (pRect->top + pRect->bottom) / 2;

    iStatus = LoadString(hInstance, idString, szMsg, ARRAYSIZE(szMsg));
    if (!iStatus) {
        return;      // can't find string - print nothing
    }

    GetTextExtentPoint32(hDC, szMsg, lstrlen(szMsg), &size);
    TextOut(hDC, xStart-size.cx/2, yStart-size.cy/2, szMsg, lstrlen(szMsg));
}


/****************************************************************************

    FUNCTION: DrawArrow

    PURPOSE:  Draw one arrow in a given color.

****************************************************************************/

static VOID 
DrawArrow( 
    HDC hDC, 
    INT type, 
    INT xPos, 
    INT yPos, 
    COLORREF crPenColor 
    )
{
    INT shaftlen=30;         // length of arrow shaft
    INT headlen=15;          // height or width of arrow head (not length)
    HPEN hPen, hPrevPen = NULL;   // pens
    INT x,y;
    INT xdir, ydir;          // directions of x and y (1,-1)

    hPen= CreatePen( PS_SOLID, 1, crPenColor );
    if( hPen )
        hPrevPen= (HPEN) SelectObject( hDC, hPen );

    MoveToEx( hDC, xPos, yPos, NULL );

    xdir= ydir= 1;   // defaults
    switch( type )
    {
        case AW_BOTTOM:
            ydir= -1;
        case AW_TOP:
            LineTo(hDC, xPos, yPos+ydir*shaftlen);

            for( x=0; x<3; x++ )
            {
                MoveToEx( hDC, xPos,             yPos+ydir*x, NULL );
                LineTo(   hDC, xPos-(headlen-x), yPos+ydir*headlen );
                MoveToEx( hDC, xPos,             yPos+ydir*x, NULL );
                LineTo(   hDC, xPos+(headlen-x), yPos+ydir*headlen );
            }
            break;

        case AW_RIGHT:
            xdir= -1;
        case AW_LEFT:
            LineTo( hDC, xPos + xdir*shaftlen, yPos );

            for( y=0; y<3; y++ )
            {
                MoveToEx( hDC, xPos + xdir*y, yPos, NULL );
                LineTo(   hDC, xPos + xdir*headlen, yPos+(headlen-y));
                MoveToEx( hDC, xPos + xdir*y, yPos, NULL );
                LineTo(   hDC, xPos + xdir*headlen, yPos-(headlen-y));
            }
            break;
    }

    if( hPrevPen )
        SelectObject( hDC, hPrevPen );

    if (hPen)
        DeleteObject(hPen);

}


/****************************************************************************

    FUNCTION: MakeRect

    PURPOSE:  Fill in RECT structure given contents.

****************************************************************************/

VOID 
MakeRect( 
    PRECT pRect, 
    INT xmin, 
    INT ymin, 
    INT xmax, 
    INT ymax
    )
{
    pRect->left= xmin;
    pRect->right= xmax;
    pRect->bottom= ymin;
    pRect->top= ymax;
}