/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    dndisp.c

Abstract:

    DOS-based NT setup program video display routines.

Author:

    Ted Miller (tedm) 30-March-1992

Revision History:

--*/


#include "winnt.h"
#include <string.h>


#define SCREEN_WIDTH        80
#define SCREEN_HEIGHT       25

#define STATUS_HEIGHT       1
#define STATUS_LEFT_MARGIN  2
#define HEADER_HEIGHT       3

//
// Display attributes
//

#define ATT_FG_BLACK        0
#define ATT_FG_BLUE         1
#define ATT_FG_GREEN        2
#define ATT_FG_CYAN         3
#define ATT_FG_RED          4
#define ATT_FG_MAGENTA      5
#define ATT_FG_YELLOW       6
#define ATT_FG_WHITE        7

#define ATT_BG_BLACK       (ATT_FG_BLACK   << 4)
#define ATT_BG_BLUE        (ATT_FG_BLUE    << 4)
#define ATT_BG_GREEN       (ATT_FG_GREEN   << 4)
#define ATT_BG_CYAN        (ATT_FG_CYAN    << 4)
#define ATT_BG_RED         (ATT_FG_RED     << 4)
#define ATT_BG_MAGENTA     (ATT_FG_MAGENTA << 4)
#define ATT_BG_YELLOW      (ATT_FG_YELLOW  << 4)
#define ATT_BG_WHITE       (ATT_FG_WHITE   << 4)

#define ATT_FG_INTENSE      8
#define ATT_BG_INTENSE     (ATT_FG_INTENSE << 4)

#define DEFAULT_ATTRIBUTE   (ATT_FG_WHITE | ATT_BG_BLUE)
#define STATUS_ATTRIBUTE    (ATT_FG_BLACK | ATT_BG_WHITE)
#define EDIT_ATTRIBUTE      (ATT_FG_BLACK | ATT_BG_WHITE)
#define EXITDLG_ATTRIBUTE   (ATT_FG_RED   | ATT_BG_WHITE)
#define GAUGE_ATTRIBUTE     (ATT_BG_BLUE  | ATT_FG_YELLOW | ATT_FG_INTENSE)


// #define USE_INT10
#ifndef USE_INT10
//
// Far address of the screen buffer.
//
#define SCREEN_BUFFER ((UCHAR _far *)0xb8000000)
#define SCREEN_BUFFER_CHR(x,y)  *(SCREEN_BUFFER + (2*((x)+(SCREEN_WIDTH*(y)))))
#define SCREEN_BUFFER_ATT(x,y)  *(SCREEN_BUFFER + (2*((x)+(SCREEN_WIDTH*(y))))+1)

BOOLEAN CursorIsActuallyOn;
#endif


//
// Make these near because they are used in _asm blocks
//
UCHAR _near CurrentAttribute;
UCHAR _near ScreenX;
UCHAR _near ScreenY;

BOOLEAN CursorOn;


VOID
DnpBlankScreenArea(
    IN UCHAR Attribute,
    IN UCHAR Left,
    IN UCHAR Right,
    IN UCHAR Top,
    IN UCHAR Bottom
    );


VOID
DnInitializeDisplay(
    VOID
    )

/*++

Routine Description:

    Put the display in a known state (80x25 standard text mode) and
    initialize the display package.

Arguments:

    None.

Return Value:

    None.

--*/

{
    CurrentAttribute = DEFAULT_ATTRIBUTE;
    CursorOn = FALSE;

    //
    // Set the display to standard 80x25 mode
    //

    _asm {
        mov ax,3        // set video mode to 3
        int 10h
    }

    //
    // Clear the entire screen
    //

    DnpBlankScreenArea(CurrentAttribute,0,SCREEN_WIDTH-1,0,SCREEN_HEIGHT-1);
    DnPositionCursor(0,0);

#ifndef USE_INT10
    //
    // Shut the cursor off.
    //
    _asm {
        mov ah,2        // function -- position cursor
        mov bh,0        // display page
        mov dh,SCREEN_HEIGHT
        mov dl,0
        int 10h
    }

    CursorIsActuallyOn = FALSE;
#endif
}


VOID
DnClearClientArea(
    VOID
    )

/*++

Routine Description:

    Clear the client area of the screen, ie, the area between the header
    and status line.

Arguments:

    None.

Return Value:

    None.

--*/

{
    DnpBlankScreenArea( CurrentAttribute,
                        0,
                        SCREEN_WIDTH-1,
                        HEADER_HEIGHT,
                        SCREEN_HEIGHT - STATUS_HEIGHT - 1
                      );

    DnPositionCursor(0,HEADER_HEIGHT);
}


VOID
DnSetGaugeAttribute(
    IN BOOLEAN Set
    )

/*++

Routine Description:

    Prepare for drawing the thermometer portion of a gas gauge.

Arguments:

    Set - if TRUE, prepare for drawing the thermometer.  If FALSE, restore
        the state for normal drawing.

Return Value:

    None.

--*/

{
    static UCHAR SavedAttribute = 0;

    if(Set) {
        if(!SavedAttribute) {
            SavedAttribute = CurrentAttribute;
            CurrentAttribute = GAUGE_ATTRIBUTE;
        }
    } else {
        if(SavedAttribute) {
            CurrentAttribute = SavedAttribute;
            SavedAttribute = 0;
        }
    }
}


VOID
DnPositionCursor(
    IN UCHAR X,
    IN UCHAR Y
    )

/*++

Routine Description:

    Position the cursor.

Arguments:

    X,Y - cursor coords

Return Value:

    None.

--*/

{
    if(X >= SCREEN_WIDTH) {
        X = 0;
        Y++;
    }

    if(Y >= SCREEN_HEIGHT) {
        Y = HEADER_HEIGHT;
    }

    ScreenX = X;
    ScreenY = Y;

    //
    // Invoke BIOS
    //

    _asm {
        mov ah,2        // function -- position cursor
        mov bh,0        // display page
        mov dh,ScreenY
        mov dl,ScreenX
        int 10h
    }

#ifndef USE_INT10
    CursorIsActuallyOn = TRUE;
#endif
}


VOID
DnWriteChar(
    IN CHAR chr
    )

/*++

Routine Description:

    Write a character in the current attribute at the current position.

Arguments:

    chr - Character to write

Return Value:

    None.

--*/

{
    if(chr == '\n') {
        ScreenX = 0;
        ScreenY++;
        return;
    }

#ifdef USE_INT10
    //
    // Position the cursor (turns it on)
    //

    DnPositionCursor(ScreenX,ScreenY);

    //
    // Output the character
    //

    _asm {
        mov ah,9        // function -- write char/attribute pair
        mov al,chr
        mov bh,0        // display page
        mov bl,CurrentAttribute
        mov cx,1        // replication factor
        int 10h
    }

    //
    // If the cursor is supposed to be off, shut it off
    //

    if(!CursorOn) {
        _asm {
            mov ah,2        // function -- position cursor
            mov bh,0        // display page
            mov dh,SCREEN_HEIGHT
            mov dl,0
            int 10h
        }
    }
#else
    SCREEN_BUFFER_CHR(ScreenX,ScreenY) = chr;
    SCREEN_BUFFER_ATT(ScreenX,ScreenY) = CurrentAttribute;

    //
    // shut cursor off if necessary
    //
    if(!CursorOn && CursorIsActuallyOn) {
        CursorIsActuallyOn = FALSE;
        _asm {
            mov ah,2        // function -- position cursor
            mov bh,0        // display page
            mov dh,SCREEN_HEIGHT
            mov dl,0
            int 10h
        }
    }
#endif
}

VOID
DnWriteString(
    IN PCHAR String
    )

/*++

Routine Description:

    Write a string on the client area in the current position and
    adjust the current position.  The string is written in the current
    attribute.

Arguments:

    String - null terminated string to write.

Return Value:

    None.

--*/

{
    PCHAR p;

    for(p=String; *p; p++) {
        DnWriteChar(*p);
        if(*p != '\n') {
            ScreenX++;
        }
    }
}


VOID
DnWriteStatusText(
    IN PCHAR FormatString OPTIONAL,
    ...
    )

/*++

Routine Description:

    Update the status area

Arguments:

    FormatString - if present, supplies a printf format string for the
        rest of the arguments.  Otherwise the status area is cleared out.

Return Value:

    None.

--*/

{
    va_list arglist;
    CHAR String[SCREEN_WIDTH+1];
    int StringLength;
    UCHAR SavedAttribute;

    //
    // First, clear out the status area.
    //

    DnpBlankScreenArea( STATUS_ATTRIBUTE,
                        0,
                        SCREEN_WIDTH-1,
                        SCREEN_HEIGHT-STATUS_HEIGHT,
                        SCREEN_HEIGHT-1
                      );

    if(FormatString) {

        va_start(arglist,FormatString);
        StringLength = vsprintf(String,FormatString,arglist);

        SavedAttribute = CurrentAttribute;
        CurrentAttribute = STATUS_ATTRIBUTE;

        DnPositionCursor(STATUS_LEFT_MARGIN,SCREEN_HEIGHT - STATUS_HEIGHT);

        DnWriteString(String);

        CurrentAttribute = SavedAttribute;
    }
}


VOID
DnSetCopyStatusText(
    IN PCHAR Caption,
    IN PCHAR Filename
    )

/*++

Routine Description:

    Write or erase a copying message in the lower right part of the screen.

Arguments:

    Filename - name of file currently being copied.  If NULL, erases the
        copy status area.

Return Value:

    None.

--*/

{
    unsigned CopyStatusAreaLen;
    CHAR StatusText[100];

    //
    // The 13 is for 8.3 and a space
    //

    CopyStatusAreaLen = strlen(Caption) + 13;

    //
    // First erase the status area.
    //

    DnpBlankScreenArea( STATUS_ATTRIBUTE,
                        (UCHAR)(SCREEN_WIDTH - CopyStatusAreaLen),
                        SCREEN_WIDTH - 1,
                        SCREEN_HEIGHT - STATUS_HEIGHT,
                        SCREEN_HEIGHT - 1
                      );

    if(Filename) {

        UCHAR SavedAttribute;
        UCHAR SavedX,SavedY;

        SavedAttribute = CurrentAttribute;
        SavedX = ScreenX;
        SavedY = ScreenY;

        CurrentAttribute = STATUS_ATTRIBUTE;
        DnPositionCursor((UCHAR)(SCREEN_WIDTH-CopyStatusAreaLen),SCREEN_HEIGHT-1);

        memset(StatusText,0,sizeof(StatusText));
        strcpy(StatusText,Caption);
        strncpy(StatusText + strlen(StatusText),Filename,12);

        DnWriteString(StatusText);

        CurrentAttribute = SavedAttribute;
        ScreenX = SavedX;
        ScreenY = SavedY;
    }
}



VOID
DnStartEditField(
    IN BOOLEAN CreateField,
    IN UCHAR X,
    IN UCHAR Y,
    IN UCHAR W
    )

/*++

Routine Description:

    Sets up the display package to start handling an edit field.

Arguments:

    CreateField - if TRUE, caller is starting an edit field interaction.
        If FALSE, he is ending one.

    X,Y,W - supply coords and width in chars of the edit field.

Return Value:

    None.

--*/

{
    static UCHAR SavedAttribute = 255;

    CursorOn = CreateField;

    if(CreateField) {

        if(SavedAttribute == 255) {
            SavedAttribute = CurrentAttribute;
            CurrentAttribute = EDIT_ATTRIBUTE;
        }

        DnpBlankScreenArea(EDIT_ATTRIBUTE,X,(UCHAR)(X+W-1),Y,Y);

    } else {

        if(SavedAttribute != 255) {
            CurrentAttribute = SavedAttribute;
            SavedAttribute = 255;
        }
    }
}


VOID
DnExitDialog(
    VOID
    )
{
    unsigned W,H,X,Y,i;
    PUCHAR CharSave;
    PUCHAR AttSave;
    ULONG Key,ValidKeys[3] = { ASCI_CR,DN_KEY_F3,0 };
    UCHAR SavedX,SavedY,SavedAttribute;
#ifndef USE_INT10
    BOOLEAN SavedCursorState = CursorOn;
#endif

    SavedAttribute = CurrentAttribute;
    CurrentAttribute = EXITDLG_ATTRIBUTE;

    SavedX = ScreenX;
    SavedY = ScreenY;

#ifndef USE_INT10
    //
    // Shut the cursor off.
    //
    CursorIsActuallyOn = FALSE;
    CursorOn = FALSE;
    _asm {
        mov ah,2        // function -- position cursor
        mov bh,0        // display page
        mov dh,SCREEN_HEIGHT
        mov dl,0
        int 10h
    }
#endif

    //
    // Count lines in the dialog and determine its width.
    //
    for(H=0; DnsExitDialog.Strings[H]; H++);
    W = strlen(DnsExitDialog.Strings[0]);

    //
    // allocate two buffers for character save and attribute save
    //
    CharSave = MALLOC(W*H,TRUE);
    AttSave = MALLOC(W*H,TRUE);

    //
    // save the screen patch
    //
    for(Y=0; Y<H; Y++) {
        for(X=0; X<W; X++) {

            UCHAR att,chr;
            UCHAR x,y;

            x = (UCHAR)(X + DnsExitDialog.X);
            y = (UCHAR)(Y + DnsExitDialog.Y);

#ifdef USE_INT10
            _asm {

                // first position cursor
                mov ah,2
                mov bh,0
                mov dh,y
                mov dl,x
                int 10h

                // now read the char/att at the cursor
                mov ah,8
                mov bh,0
                int 10h
                mov att,ah
                mov chr,al
            }
#else
            chr = SCREEN_BUFFER_CHR(x,y);
            att = SCREEN_BUFFER_ATT(x,y);
#endif

            CharSave[Y*W+X] = chr;
            AttSave[Y*W+X] = att;
        }
    }

    //
    // Put up the dialog
    //

    for(i=0; i<H; i++) {
        DnPositionCursor(DnsExitDialog.X,(UCHAR)(DnsExitDialog.Y+i));
        DnWriteString(DnsExitDialog.Strings[i]);
    }

    CurrentAttribute = SavedAttribute;

    //
    // Wait for a valid keypress
    //

    Key = DnGetValidKey(ValidKeys);
    if(Key == DN_KEY_F3) {
        DnExit(1);
    }

    //
    // Restore the patch
    //
    for(Y=0; Y<H; Y++) {
        for(X=0; X<W; X++) {

            UCHAR att,chr;
            UCHAR x,y;

            x = (UCHAR)(X + DnsExitDialog.X);
            y = (UCHAR)(Y + DnsExitDialog.Y);

            chr = CharSave[Y*W+X];
            att = AttSave[Y*W+X];

#ifdef USE_INT10
            _asm {

                // first position cursor
                mov ah,2
                mov bh,0
                mov dh,y
                mov dl,x
                int 10h

                // now write the char/att at the cursor
                mov ah,9
                mov al,chr
                mov bh,0
                mov bl,att
                mov cx,1
                int 10h
            }
#else
            SCREEN_BUFFER_CHR(x,y) = chr;
            SCREEN_BUFFER_ATT(x,y) = att;
#endif
        }
    }

    FREE(CharSave);
    FREE(AttSave);

#ifndef USE_INT10
    CursorOn = SavedCursorState;
#endif

    if(CursorOn) {
        DnPositionCursor(SavedX,SavedY);
    } else {
        ScreenX = SavedX;
        ScreenY = SavedY;
        _asm {
            mov ah,2
            mov bh,0
            mov dh,SCREEN_HEIGHT;
            mov dl,0
            int 10h
        }
#ifndef USE_INT10
        CursorIsActuallyOn = FALSE;
#endif
    }
}



//
// Internal support routines
//
VOID
DnpBlankScreenArea(
    IN UCHAR Attribute,
    IN UCHAR Left,
    IN UCHAR Right,
    IN UCHAR Top,
    IN UCHAR Bottom
    )

/*++

Routine Description:

    Invoke the BIOS to blank a region of the screen.

Arguments:

    Attribute - screen attribute to use to blank the region

    Left,Right,Top,Bottom - coords of region to blank

Return Value:

    None.

--*/

{
#ifdef USE_INT10
    //
    // Invoke the BIOS
    //

    _asm {
        mov ah,6                    // function number -- scroll window up
        xor al,al                   // function code -- blank window
        mov bh,Attribute
        mov ch,Top
        mov cl,Left
        mov dh,Bottom
        mov dl,Right
        int 10h
    }
#else
    UCHAR x,y;

    for(y=Top; y<=Bottom; y++) {
        for(x=Left; x<=Right; x++) {
            SCREEN_BUFFER_CHR(x,y) = ' ';
            SCREEN_BUFFER_ATT(x,y) = Attribute;
        }
    }
#endif
}


int
DnGetGaugeChar(
    VOID
    )
{
    return(0xdb);   //inverse square in cp437, 850, etc.
}