view enable.c @ 0:20b4ed4eebe3

Checked in Windows 9x display minidriver for VirtualBox.
author Michal Necasek <mnecasek@yahoo.com>
date Sun, 19 Jun 2022 17:39:17 +0200
parents
children 0c8e36318ee3
line wrap: on
line source

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

Copyright (c) 2012-2022  Michal Necasek

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS 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.

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

/* GDI Enable (with ReEnable) and Disable implementation. */

#include "winhack.h"
#include <gdidefs.h>
#include <dibeng.h>
#include <minivdd.h>
#include "minidrv.h"

#include <string.h>

/* Pretend we have a 208 by 156 mm screen. */
#define DISPLAY_HORZ_MM     208
#define DISPLAY_VERT_MM     156

/* Size in English units. Looks pretty random. */
#define DISPLAY_SIZE_EN     325
/* Size in twips. Just as random. */
#define DISPLAY_SIZE_TWP    2340

LPDIBENGINE lpDriverPDevice = 0;    /* This device's PDEV that's passed to GDI. */
WORD wEnabled = 0;                  /* Is this device enabled? */
RGBQUAD FAR *lpColorTable = 0;      /* Current color table. */

static BYTE bReEnabling = 0;        /* Set when re-enabling PDEV. */
static WORD wDIBPdevSize = 0;


/* 1bpp Color Table (non-palettized). */
const DIBColorEntry DIB1ColorTable[] = {
    /* blue  red  green flags */
    { 0   , 0   , 0   , 0          },
    { 0xFF, 0xFF, 0xFF, MAPTOWHITE }
};

/* 4bpp Color Table (non-palettized). */
const DIBColorEntry DIB4ColorTable[] = {
    /* blue  red  green flags */
    { 0   , 0   , 0   , 0          },
    { 0   , 0   , 0x80, 0          },
    { 0   , 0x80, 0   , 0          },
    { 0   , 0x80, 0x80, 0          },
    { 0x80, 0   , 0   , 0          },
    { 0x80, 0   , 0x80, 0          },
    { 0x80, 0x80, 0   , 0          },
    { 0xC0, 0xC0, 0xC0, MAPTOWHITE },
    { 0x80, 0x80, 0x80, MAPTOWHITE },
    { 0   , 0   , 0xFF, 0          },
    { 0   , 0xFF, 0   , MAPTOWHITE },
    { 0   , 0xFF, 0xFF, MAPTOWHITE },
    { 0xFF, 0   , 0   , 0          },
    { 0xFF, 0   , 0xFF, 0          },
    { 0xFF, 0xFF, 0   , MAPTOWHITE },
    { 0xFF, 0xFF, 0xFF, MAPTOWHITE }
};

/* 8bpp Color Table (palettized), first 10 entries. */
const DIBColorEntry DIB8ColorTable1[] = {
    /* blue  red  green flags */
    { 0   , 0   , 0   , 0          },
    { 0   , 0   , 0x80, 0          },
    { 0   , 0x80, 0   , 0          },
    { 0   , 0x80, 0x80, 0          },
    { 0x80, 0   , 0   , 0          },
    { 0x80, 0   , 0x80, 0          },
    { 0x80, 0x80, 0   , 0          },
    { 0xC0, 0xC0, 0xC0, MAPTOWHITE },
    { 0xC0, 0xDC, 0xC0, MAPTOWHITE | NONSTATIC },
    { 0xF0, 0xCA, 0xA6, MAPTOWHITE | NONSTATIC }
};

/* 8bpp Color Table (palettized), all middle entries (10-245). */
const DIBColorEntry DIB8ColorTable2[] = {
    /* blue  red  green flags */
    { 0, 0, 0, NONSTATIC }
};

/* 8bpp Color Table (palettized), last 10 entries (246-255). */
const DIBColorEntry DIB8ColorTable3[] = {
    /* blue  red  green flags */
    { 0xF0, 0xFB, 0xFF, MAPTOWHITE | NONSTATIC },
    { 0xA4, 0xA0, 0xA0, MAPTOWHITE | NONSTATIC },
    { 0x80, 0x80, 0x80, MAPTOWHITE },
    { 0   , 0   , 0xFF, 0          },
    { 0   , 0xFF, 0   , MAPTOWHITE },
    { 0   , 0xFF, 0xFF, MAPTOWHITE },
    { 0xFF, 0   , 0   , 0          },
    { 0xFF, 0   , 0xFF, 0          },
    { 0xFF, 0xFF, 0   , MAPTOWHITE },
    { 0xFF, 0xFF, 0xFF, MAPTOWHITE }
};


/* An undocumented function called by USER to check if display driver
 * contains a more suitable version of a resource.
 * Exported as ordinal 450.
 */
DWORD WINAPI __loadds GetDriverResourceID( WORD wResID, LPSTR lpResType )
{
    if( wResID == OBJ_FONT ) {
        if( wDpi != 96 ) {
            return( 2003 ); /* If DPI is not 96, return fonts120.bin instead. */
        }
    }
    return( wResID );
}

/* Some genius at Microsoft decided that CreateDIBPDevice returns the result
 * in EAX, not DX:AX. This is not at all documented in the Win95 DDK, but it
 * is stated clearly in U.S. Patent 6,525,743 (granted in 2003).
 * We just create a tiny thunk to produce a sane calling convention.
 */
DWORD PASCAL CreateDIBPDeviceX( LPBITMAPINFO lpInfo, LPPDEVICE lpDevice, LPVOID lpBits, WORD wFlags );
#pragma aux CreateDIBPDeviceX = \
    "call   CreateDIBPDevice"   \
    "mov    dx, ax"             \
    "shr    eax, 16"            \
    "xchg   ax, dx"

#pragma code_seg( _INIT )

/* GDI calls Enable twice at startup, first to query the GDIINFO structure
 * and then to initialize the video hardware.
 */
UINT WINAPI __loadds Enable( LPVOID lpDevice, UINT style, LPSTR lpDeviceType,
                             LPSTR lpOutputFile, LPVOID lpStuff )
{
    WORD    rc;
    WORD    wPalCnt;

    dbg_printf( "Enable: lpDevice=%WP style=%X bReEnabling=%u wPalettized=%u\n", lpDevice, style, bReEnabling, wPalettized );
    if( !(style & 1) ) {    /* Only test the low bit! */
        LPDIBENGINE     lpEng = lpDevice;
        LPBITMAPINFO    lpInfo;
        WORD            wFlags;
        DWORD           dwRet;

        /* Initialize the PDEVICE. */
        lpDriverPDevice = lpDevice;
        rc = PhysicalEnable();
        if( !rc ) {
            dbg_printf( "Enable: PhysicalEnable failed!\n" );
            return( 0 );
        }
        if( !bReEnabling ) {
            int_2Fh( STOP_IO_TRAP );
        }

        /* Pass down to the DIB engine. */
        DIB_Enable( lpDevice, style, lpDeviceType, lpOutputFile, lpStuff );

        if( wPalettized )
            DIB_SetPaletteTranslateExt( NULL, lpDriverPDevice );

        dbg_printf( "Enable: wBpp=%u wDpi=%u wScreenX=%u wScreenY=%u wDIBPdevSize=%x\n",
                    wBpp, wDpi, wScreenX, wScreenY, wDIBPdevSize );

        /* Fill out the bitmap header. */
        /// @todo Does wDIBPdevSize have to equal sizeof(DIBENGINE)?
        lpInfo = (LPVOID)((LPBYTE)lpDevice + wDIBPdevSize);
        _fmemset( &lpInfo->bmiHeader, 0, sizeof( lpInfo->bmiHeader ) );
        lpInfo->bmiHeader.biSize     = sizeof( lpInfo->bmiHeader );
        lpInfo->bmiHeader.biWidth    = wScreenX;
        lpInfo->bmiHeader.biHeight   = wScreenY;
        lpInfo->bmiHeader.biPlanes   = 1;
        lpInfo->bmiHeader.biBitCount = wBpp;

        /* Set up the color table for non-direct color modes. */
        if( wBpp <= 8 ) {
            DIBColorEntry FAR   *lpDefaultClr;

            switch( wBpp ) {
            case 8:
                lpDefaultClr = DIB8ColorTable1;
                wPalCnt = sizeof( DIB8ColorTable1 );
                break;
            case 4:
                lpDefaultClr = DIB4ColorTable;
                wPalCnt = sizeof( DIB4ColorTable );
                break;
            case 1:
            default:
                lpDefaultClr = DIB1ColorTable;
                wPalCnt = sizeof( DIB1ColorTable );
                break;
            }
            lpColorTable = &lpInfo->bmiColors;

            if( !bReEnabling ) {
                _fmemcpy( lpColorTable, lpDefaultClr, wPalCnt );
                /* For 8bpp, fix up the rest of the palette. */
                if( wBpp == 8 ) {
                    int     i;

                    /* The entries at index 10 to 245 are all the same. */
                    for( i = 10; i < 246; ++i )
                        lpColorTable[i] = DIB8ColorTable2[0];

                    _fmemcpy( &lpColorTable[246], DIB8ColorTable3, sizeof( DIB8ColorTable3 ) );
                }
            }
        }

        wFlags = wPDeviceFlags;
        if( wPalettized )
            wFlags |= PALETTIZED;

        /* Call the DIB Engine to set up the PDevice. */
        dbg_printf( "lpInfo=%WP lpDevice=%WP lpColorTable=%WP wFlags=%X ScreenSelector=%X\n", lpInfo, lpDevice, lpColorTable, wFlags, ScreenSelector );
        dwRet = CreateDIBPDeviceX( lpInfo, lpDevice, ScreenSelector :> 0, wFlags );
        if( !dwRet ) {
            dbg_printf( "Enable: CreateDIBPDevice failed!\n" );
            return( 0 );
        }
        dbg_printf( "Enable: CreateDIBPDevice returned %lX\n", dwRet );

        /* Now fill out the begin/end access callbacks. */
        lpEng->deBeginAccess = DIB_BeginAccess;
        lpEng->deEndAccess   = DIB_EndAccess;

        /* Program the DAC in non-direct color modes. */
        if( wBpp <= 8 ) {
            switch( wBpp ) {
            case 8:
                wPalCnt = 256;
                break;
            case 4:
                wPalCnt = 16;
                break;
            case 1:
            default:
                wPalCnt = 2;
                break;
            }
            SetRAMDAC_far( 0, wPalCnt, lpColorTable );
        }

        if( !bReEnabling ) {
            HookInt2Fh();
        }
        wEnabled = 1;
        return( 1 );
    } else {
        /* Fill out GDIINFO for GDI. */
        LPGDIINFO   lpInfo = lpDevice;

        /* Start with passing down to the DIB engine. It will set dpCurves through dpStyleLen. */
        DIB_Enable( lpDevice, style, lpDeviceType, lpOutputFile, lpStuff );

        /* Fill out some static data. Note that some fields are set by the DIB Engine
         * and we don't touch them (curves, lines, polygons etc.).
         */
        lpInfo->dpVersion    = DRV_VERSION;
        lpInfo->dpTechnology = DT_RASDISPLAY;
        lpInfo->dpHorzSize   = DISPLAY_HORZ_MM;
        lpInfo->dpVertSize   = DISPLAY_VERT_MM;
        lpInfo->dpPlanes     = 1;
        lpInfo->dpCapsFE     = 0;
        lpInfo->dpNumFonts   = 0;

        /* Now set the fields that depend on current mode. */
        lpInfo->dpHorzRes = wScrX;
        lpInfo->dpVertRes = wScrY;

        lpInfo->dpMLoWin.xcoord = DISPLAY_HORZ_MM * 10;
        lpInfo->dpMLoWin.ycoord = DISPLAY_VERT_MM * 10;
        lpInfo->dpMLoVpt.xcoord = wScrX;
        lpInfo->dpMLoVpt.ycoord = -wScrY;

        lpInfo->dpMHiWin.xcoord = DISPLAY_HORZ_MM * 100;
        lpInfo->dpMHiWin.ycoord = DISPLAY_VERT_MM * 100;
        lpInfo->dpMHiVpt.xcoord = wScrX;
        lpInfo->dpMHiVpt.ycoord = -wScrY;

        /* These calculations are a wild guess and probably don't matter. */
        lpInfo->dpELoWin.xcoord = DISPLAY_SIZE_EN;
        lpInfo->dpELoWin.ycoord = DISPLAY_SIZE_EN;
        lpInfo->dpELoVpt.xcoord = wScrX / 5;
        lpInfo->dpELoVpt.ycoord = -lpInfo->dpELoVpt.xcoord;

        lpInfo->dpELoWin.xcoord = DISPLAY_SIZE_EN * 5;
        lpInfo->dpELoWin.ycoord = DISPLAY_SIZE_EN * 5;
        lpInfo->dpEHiVpt.xcoord = wScrX / 10;
        lpInfo->dpEHiVpt.ycoord = -lpInfo->dpEHiVpt.xcoord;

        lpInfo->dpTwpWin.xcoord = DISPLAY_SIZE_TWP;
        lpInfo->dpTwpWin.ycoord = DISPLAY_SIZE_TWP;
        lpInfo->dpTwpVpt.xcoord = wScrX / 10;
        lpInfo->dpTwpVpt.ycoord = -lpInfo->dpTwpVpt.xcoord;

        /* Update more GDIINFO bits. */
        lpInfo->dpLogPixelsX = wDpi;
        lpInfo->dpLogPixelsY = wDpi;
        lpInfo->dpBitsPixel  = wBpp;
        lpInfo->dpDCManage   = DC_IgnoreDFNP;
        lpInfo->dpCaps1 |= C1_BYTE_PACKED | C1_COLORCURSOR | C1_REINIT_ABLE | C1_SLOW_CARD;

        /* Grab the DIB Engine PDevice size before we add to it. */
        wDIBPdevSize = lpInfo->dpDEVICEsize;
        dbg_printf( "Enable: wDIBPdevSize=%X wScrX=%u wScrY=%u wBpp=%u wDpi=%u wScreenX=%u wScreenY=%u\n",
                    wDIBPdevSize, wScrX, wScrY, wBpp, wDpi, wScreenX, wScreenY );

        lpInfo->dpNumBrushes = -1;  /* Too many to count, always the same.. */

        if( wBpp == 8 ) {
            if( wPalettized ) {
                lpInfo->dpNumPens     = 16;     /* Pens realized by driver. */
                lpInfo->dpNumColors   = 20;     /* Colors in color table. */
                lpInfo->dpNumPalReg   = 256;
                lpInfo->dpPalReserved = 20;
                lpInfo->dpColorRes    = 18;
                lpInfo->dpRaster     |= RC_DIBTODEV + RC_PALETTE + RC_SAVEBITMAP;
            } else {
                lpInfo->dpNumPens     = 256;    /* Pens realized by driver. */
                lpInfo->dpNumColors   = 256;    /* Colors in color table. */
                lpInfo->dpNumPalReg   = 0;
                lpInfo->dpPalReserved = 0;
                lpInfo->dpColorRes    = 0;
                lpInfo->dpRaster     |= RC_DIBTODEV;
            }
            lpInfo->dpDEVICEsize += sizeof( BITMAPINFOHEADER ) + 256 * 4;
        } else if( wBpp > 8 ) {
            lpInfo->dpNumPens     = -1;     /* Pens realized by driver. */
            lpInfo->dpNumColors   = -1;     /* Colors in color table. */
            lpInfo->dpNumPalReg   = 0;
            lpInfo->dpPalReserved = 0;
            lpInfo->dpColorRes    = 0;
            lpInfo->dpRaster     |= RC_DIBTODEV;
            lpInfo->dpDEVICEsize += sizeof( BITMAPINFOHEADER );
        } else if( wBpp < 8 ) {
            WORD    wCount;

            wCount = 1 << wBpp; /* 2 or 4 for 2 or 16 bpp. */
            lpInfo->dpNumPens     = wCount; /* Pens realized by driver. */
            lpInfo->dpNumColors   = wCount; /* Colors in color table. */
            lpInfo->dpNumPalReg   = 0;
            lpInfo->dpPalReserved = 0;
            lpInfo->dpColorRes    = 0;
            lpInfo->dpRaster     |= RC_DIBTODEV;
            wCount *= 4;
            lpInfo->dpDEVICEsize += sizeof( BITMAPINFOHEADER ) + 8 + wCount;
        }
        dbg_printf( "sizeof(GDIINFO)=%d (%X), dpDEVICEsize=%X\n", sizeof( GDIINFO ), sizeof( GDIINFO ), lpInfo->dpDEVICEsize );
        return( sizeof( GDIINFO ) );
    }
}


/* The ReEnable function is called to dynamically change resolution.
 * It must query the new display mode settings and then call Enable.
 * NB: Windows 9x will not dynamically change the color depth, only
 * resolution. Documented in MS KB Article Q127139.
 */
UINT WINAPI __loadds ReEnable( LPVOID lpDevice, LPGDIINFO lpInfo )
{
    WORD    wLastValidBpp  = wBpp;
    WORD    wLastValidX    = wScreenX;
    WORD    wLastValidY    = wScreenY;
    WORD    rc;

    dbg_printf( "ReEnable: lpDevice=%WP lpInfo=%WP wScreenX=%u wScreenY=%u\n", lpDevice, lpInfo, wScreenX, wScreenY );

    /* Figure out the new mode. */
    ReadDisplayConfig();
    dbg_printf( "ReEnable: wScreenX=%u wScreenY=%u wBpp=%u\n", wScreenX, wScreenY, wBpp );

    /* Let Enable know it doesn't need to do everything. */
    bReEnabling = 1;

    /* Don't let the cursor mess with things. */
    DIB_BeginAccess( lpDevice, 0, 0, wScreenX - 1, wScreenY - 1, CURSOREXCLUDE );

    /* Create a new PDevice and set the new mode. Returns zero on failure. */
    rc = Enable( lpDevice, 0, NULL, NULL, NULL );

    /* Drawing the cursor is safe again. */
    DIB_EndAccess( lpDevice, CURSOREXCLUDE );

    if( rc ) {
        /* Enable succeeded, fill out GDIINFO. */
        Enable( lpInfo, 1, NULL, NULL, NULL );
        rc = 1;
    } else {
        dbg_printf( "ReEnable: Enable failed!\n" );
        /* Couldn't set new mode. Try to get the old one back. */
        wScreenX = wLastValidX;
        wScreenY = wLastValidY;
        wBpp     = wLastValidBpp;

        Enable( lpDevice, 0, NULL, NULL, NULL );

        /* And force a repaint. */
        RepaintFunc();
        rc = 0;
    }

    bReEnabling = 0;
    return( rc );
}

void int_10h( unsigned ax );
#pragma aux int_10h =   \
    "int    10h"        \
    parm [ax];

/* Disable graphics and go back to a text mode. */
UINT WINAPI __loadds Disable( LPVOID lpDevice )
{
    LPDIBENGINE lpEng = lpDevice;

    dbg_printf( "Disable: lpDevice=%WP\n", lpDevice );

    /* Start disabling and mark the PDevice busy. */
    wEnabled = 0;
    lpEng->deFlags |= BUSY; /// @todo Does this need to be a locked op?

    /* Re-enable I/O trapping before we start setting a standard VGA mode. */
    int_2Fh( START_IO_TRAP );

    /* Tell VDD we're going away. */
    CallVDD( VDD_DRIVER_UNREGISTER );

    /* Set standard 80x25 text mode using the BIOS. */
    int_10h( 3 );

    /* And unhook INT 2F. */
    UnhookInt2Fh();

    return( 1 );
}