/*static char *SCCSID = "@(#)bvsdinit.c 6.1 90/11/16";*/
/*static char *SCCSID = "@(#)bvsdinit.c 1.10 90/01/28";*/
/****************************************************************************/
/*                                                                          */
/*                       IBM/Microsoft Confidential                         */
/*                                                                          */
/*                 Copyright (c) IBM Corporation  1987, 1990                */
/*                 Copyright (c) Microsoft Corp.  1987, 1990                */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/

#include <ctype.h>
#include <string.h>
#include <doscalls.h>

#include <os2def.h>
#define INCL_WINSHELLDATA
#include <pmshl.h>
#include <infoseg.h>
#include <dos.h>
#include <error2.h>

#include "bvsldrvr.h"
#include "bvsdata.h"
#include "subcalls.h"

/*********************** START OF SPECIFICATIONS ***********************/
/*                                                                     */
/* SUBROUTINE NAME:    BVSDINIT                                        */
/*                                                                     */
/* DESCRIPTIVE NAME:   Base Video Subsystem DynLink Initialization     */
/*                                                                     */
/* FUNCTION:   Perform all global BVS initialization.                  */
/*                                                                     */
/* NOTES:      Executes on level 3.  This routine is called at shell   */
/*             initialization time.  Prior to executing this routine   */
/*             all BVS calls are restricted to those supported by the  */
/*             Generic VDH.                                            */
/*                                                                     */
/* ENTRY POINT:  BVSDINIT                                              */
/*   LINKAGE:  Call BVSDINIT from BVSMAIN                              */
/*                                                                     */
/* INPUT:  None                                                        */
/*                                                                     */
/* EXIT-NORMAL:  Always                                                */
/*                                                                     */
/* EXIT-ERROR:  None                                                   */
/*                                                                     */
/* EFFECTS:    None                                                    */
/*                                                                     */
/* INTERNAL REFERENCES: None                                           */
/*   ROUTINES:  CPInit                                                 */
/*                                                                     */
/* EXTERNAL REFERENCES: CDIB                                           */
/*   ROUTINES:  DOSOPEN, DOSCLOSE, DOSGETSHRSEG,                       */
/*     DosQueryProfileSize, DosQueryProfileString, DosReallocSeg,      */
/*     DosLoadDriver, DosUnloadDriver                                  */
/************************ END OF SPECIFICATIONS ************************/

/*Begin BvsDInit
*   If Global Initalization of data structures is needed
*       call BvsGlobalInit to initialize those structures
*   If ShellInit
*       BvsGetVideoCallTables
*       If some valid video devices found
*           Call DosUnloadDriver to remove the Generic VDH
*       else
*           set the Generic VDH as the only VDH
*       Call vInitHEDSG to initialize the hard error screen group
*       Mark ShellInit as found
*       Call MouInitReal to inform Real Mode pointer
*   Elseif First VIO call
*       BvsGetVideoCallTables
*   Endif
*   Set return code to zero
* End BvsDInit
*/

unsigned far pascal
BvsDInit(fnVioFunction)
unsigned fnVioFunction;
{
    unsigned rc;
    unsigned (far pascal *funcTemp)();  /* @T25 */
    unsigned hModule;                   /* @T25 */

    SaveRegs();                         /* save register values */
    rc = NO_ERROR;                      /* preset good error code */

    if (!(GlobalFlags & gf_globinitdone)) { /* have not initialized global data */
        if (BvsGlobalInit() == 0) {     /* init global data */       /* @P1 */
            GlobalFlags |= gf_globinitdone;
        }
    }
    if (fnVioFunction == fnVioShellInit) { /* special shell init processing */

        fFillDevBlk = fcFillLogicDeviceBlock; /* @T15, @P1 */
        uLoadWinthornVDH(bvsglobal.cmn_calltables); /* @T19 */

        if (!(rc = BvsGetVideoCallTables (bvsglobal.cmn_calltables, /* init VDHs */
            pszProtPtrNames,            /* @P1 */
            pszRealPtrNames))) {        /* @P1 */
            fShellInited = TRUE;        /* shell successfully initialized */
/*@T51      if (fGetEnableEntry(&funcTemp,&hModule,szMouMod,szMouInitReal)) { */
/*@T51          funcTemp ((char far *)((ULONG)iPowerUpVDH)); /* @T25 */
/*@T51      } */
        }

    } else if ( !fFillDevBlk ) {        /* First call   D203 Start,D261,@T15 */

        fFillDevBlk = fcFillInitDeviceBlock; /* @P1,@T15,D261 */
        rc = BvsGetVideoCallTables (bvsglobal.cmn_calltables,
        pszProtPtrNames,
        pszRealPtrNames);               /* @P1,@T15,D261 */

    }                                   /****** End of DCR 203 ******   @T15 */
    RestoreRegs();                      /* restore register values */
    return (rc);                        /* return error code */
}

/*----------------------------------------------------------------------*/
/* The following routines were added to support DCR 261:        D261    */
/*      vGetTagValue, vMoveEnvVar, vReadEnvVars                 D261    */
/*----------------------------------------------------------------------*/

/***    vGetTagValue - extract the value of the given tag
*
*       This routine scans the source string for a tag.  If it finds the
*       tag, it allocates space in the global segment for the value
*       and copies it.  If not, leave the value string ptr untouched.
*
*       PSEUDOCODE:
*
*           seach for the tag in the souce string
*           if the tag is found
*               allocate area for tag value
*               copy tag value to area and update pointer
*
*       ENTRY   ppszValue       - ptr to ptr to value string
*               pszTag          - ptr to tag string
*               pszSource       - ptr to source string
*
*       EXIT    nothing returned
*/

void near
vGetTagValue(ppszValue,pszTag,pszSource)
char far **ppszValue;
char far *pszTag;
char far *pszSource;
{
    char far *pszValue;
    char far *pch;
    unsigned n;

    if (pch = strstr(pszSource,pszTag)) { /* Tag found */

        pszValue = pch += strlen(pszTag); /* advance to the end of the tag */

        while (*pch && (*pch != ')')) { /* until char is paren or terminator */
            pch++;
        }
        if (pch = pchGrowGlobal((n = pch - pszValue) + 1)) { /* Make room */
            *ppszValue = strncpy(pch,pszValue,n); /* Save value */
        }
    }
}

/***    vMoveEnvVar - Move an environment variable
*
*       This routine moves an uppercase copy of an environment variable from
*       from the environment to the specified destination
*
*       PSEUDOCODE
*
*       ENTRY   pszEnvVarName  - ptr to variable name
*               pszEnvVarValue - ptr to variable value in environment
*               pszEnvVarDest  - ptr to variable value destination
*
*       EXIT    returns nothing
*/

void near
vMoveEnvVar(pszEnvVarName, pszEnvVarValue, pszEnvVarDest)
char far * pszEnvVarName;
char far * pszEnvVarValue;
char far * pszEnvVarDest;
{
    char far *pchSource;
    char far *pchDest;

    strupr(strcpy(pszEnvVarDest,pszEnvVarValue)); /* Copy the upcase value */

#if 0    /* disable for the Service PAK */                           //56320
    pchDest   = pszEnvVarValue - strlen(pszEnvVarName)  - 1; /* &VarStart */
    pchSource = pszEnvVarValue + strlen(pszEnvVarValue) + 1; /* &VarEnd */

    *pchDest++ = *pchSource++;          /* Move one byte of env data */
    do {
        *pchDest++ = *pchSource++;      /* Remove var from env */
    } while ( *((unsigned far *)(pchDest-2)) ); /* All not yet moved */
#endif                                                               //56320

}

/***    vReadEnvVars - Read the Environment Variables set in CONFIG.SYS
*
*       This routine reads all of the environment variables that were set
*       in CONFIG.SYS for use by the video subsystem.
*
*       PSEUDOCODE
*
*       ENTRY
*
*       EXIT    returns nothing
*/

void near
vReadEnvVars()
{
    char far *psz;
    char far *pszCur;
    char far *pszTmp;
    char far *pszVDHs;
    char far *pszDevice;

    if (!DOSSCANENV( (char far *) szVideoWindow, /* Get window string,@S42*/
        (char far * far *) &pszTmp )) { /*@S42*/

        if (pszWindowBVH = pchGrowGlobal(strlen(pszTmp)+1)) { /*@S42*/

            vMoveEnvVar(szVideoWindow,pszTmp,pszWindowBVH); /*@S42*/

        }                               /*@S42*/
    }                                   /*@S42*/

    if (!pszWindowBVH) {                /*@S42*/
        pszWindowBVH = szWindowBVH;     /*@S42*/
    }                                   /*@S42*/

    if (!DOSSCANENV( (char far *) szVideoDevices, /* Get the list */
        (char far * far *) &pszTmp )) {
        if (fGetTempBuf(&pszVDHs,strlen(pszTmp)+2)) { /* Get work string with 0,0 */
            vMoveEnvVar(szVideoDevices,pszTmp,pszVDHs); /* Move value to work string */

            for (pszCur = pszTmp = pszVDHs;;) { /* Loop until 0,0 found */
                if (!*pszTmp || (*pszTmp==',')) {
                    *pszTmp++ = 0;      /* Separate list into strings */

                    if (!DOSSCANENV( (char far *)pszCur, /* Get the env variable */
                        (char far * far *)&psz )) {
                        if (fGetTempBuf(&pszDevice,strlen(psz)+1)) { /* Get another string */
                            cVideoDevices++; /* Update count of devices, B702118 */

                            vMoveEnvVar(pszCur,psz,pszDevice); /* Move to work string, B702438 */

                            vGetTagValue(&pszDeviceString[cVideoDevices],
                            szDeviceTag,pszDevice); /* Get DEVICE() tag value */

                            vGetTagValue(&pszProtPtrNames[cVideoDevices],
                            szPtrDevPTag,pszDevice); /* Get PTRDEVP() tag value */

                            vGetTagValue(&pszRealPtrNames[cVideoDevices],
                            szPtrDevRTag,pszDevice); /* Get PTRDEVR() tag value */

                            vFreeTempBuf(pszDevice); /* Free the work string */
                        }
                    }
                    if (!*(pszCur = pszTmp)) { /* Point to next string */
                        break;
                    }
                } else {
                    pszTmp++;
                }
            }
            vFreeTempBuf(pszVDHs);      /* Free the work string */
        }
    }
}

/***    BvsGlobalInit - initialize global data structures
*
*       This routine initializes fields in BVS global data segments.
*       The fields being initialized are:  Info Segs (LDT and GDT)
*       are gotten, call table pointers are set to tables in memory,
*       and the list of per screen group data selectors (LVB, BVSCB
*       and Environment) is resized and the selectors are initialized
*       to 0.
*
*       ENTRY   none
*
*       EXIT    return  -  0 if initializations succeeded
*                          errorcode otherwise
*/
unsigned near
BvsGlobalInit()
{
    struct InfoSegGDT far * pgdtinfo = NULL;
    unsigned i;
    unsigned rc;

    if (!(rc = DOSGETINFOSEG ((unsigned far *)&bvsglobal.cmn_SISsel,
        (unsigned far *)&bvsglobal.cmn_LISsel))) { /*@S10*/

        FP_SEG(pgdtinfo) =  bvsglobal.cmn_SISsel; /*@S10*/
        bvsglobal.cmn_maxbase = pgdtinfo->MaxScrnGrp - 1; /*@S10*/

        vReadEnvVars();                 /* Read values from CONFIG.SYS, @S10*/
        pszDeviceString[0] = pszDeviceString[iPowerUpVDH]; /*@S16*/
        pszProtPtrNames[0] = pszProtPtrNames[iPowerUpVDH]; /*@S16*/
        pszRealPtrNames[0] = pszRealPtrNames[iPowerUpVDH]; /*@S16*/
        if (!(i = cVideoDevices)) {     /*@S10*/
            i++;                        /*@S10*/
        }                               /*@S10*/
/*                                                                     @D549*
 * Seltable entry: BVSCB FLAGS ENVB LVB ENVB2 LVB2 ...                     *
 * (i+1) = number of dword pairs, i being # of ENVB/LVBs, 1 for BVSCB/FLAGS*
 *                                                                         */
        bvsglobal.cmn_seltablesz = 4 * (i + 1);                      /*@D549*/

    }
    return (rc);
}

/***    DosUnloadVideoHandler - unload a video device handler
*
*       This routine gets a pointer to the device table entry for the
*       specified video device.  Once it has it, it unlinks the process
*       from each of the dynlink components in the VDH.
*
*       PSEUDOCODE
*           get a pointer to the device table entry (fFindDevice)
*           for each component of the device handler
*               if the component is a dynlink
*                   DosFreeModule on the component
*
*       ENTRY   pchCallVector   - pointer to the call table for the device
*               pszDeviceString - device string.
*
*       EXIT    pchCallVector   - initialized on the first call to the VDH
*               return          - 0 if success
*                                 otherwise errorcode
*/
unsigned near
DosUnloadVideoHandler (pszDevice)
char far *  pszDevice;
{
    t_dev far * pdevDev;
    unsigned i;
    unsigned rc;

    if (!fFindDevice (&pdevDev, pszDevice, (char far *) szDefEntry,
        uVideoEnableCategory, uVideoEnableFunction)) {
        return (ERROR_DEVICE_NOT_LOADED);
    }
    vFreeDevTableEntry(pdevDev);        /*@S16*/
    return (NO_ERROR);
}

/***    BvsGetPtrDrawName - get the name of a pointer draw device
*
*       This routine gets a pointer to a string that contains the name
*       of the pointer draw device for the configuration and the mode
*       (Real or Protect) specified.
*
*       PSEUDOCODE
*           save all registers
*           preset a bad error code
*           if the shell is initialized
*               if the configuation number is valid
*                   if the request is for real mode
*                       set up the pointer to the real mode ptr device name
*                       set a good error code
*                   if the request is for protect
*                       set up the pointer to the protect mode ptr device name
*                       set a good error code
*           restore all registers
*           return the error code
*
*       ENTRY   uConfigNum      - the configuration number
*               uPtrType        - the mode for which the name is desired
*               ppszDeviceName  - pointer at which to return the name
*
*       EXIT    *ppszDeviceName - name of the device in question
*               return          - 0 if success
*                                 otherwise errorcode
*/

unsigned far pascal
BvsGetPtrDrawName(uConfigNum, uPtrType, ppszDeviceName)
unsigned uConfigNum;
unsigned uPtrType;
char far **ppszDeviceName;
{
    unsigned rc;
    char far *pszTemp;                  /*D261*/

    SaveRegs();                         /* save all registers */
    rc = ERROR_INVALID_PARAMETER;       /* preset bad return code */
    if (fShellInited) {                 /* shell is initialized, names valid */
        if (uConfigNum == 0) {          /* BUGBUG should 0 remap to current */
            uConfigNum = iPowerUpVDH;   /* configuration? */
        }
        if (uConfigNum <= iLastVDH) {   /* config number is valid */
            if (uPtrType == uRealModePtr) { /* request real mode ptr name */
                pszTemp = pszRealPtrNames[uConfigNum]; /* get name */
                rc = NO_ERROR;          /* set good return code */
            } else if (uPtrType == uProtModePtr) { /* protect mode ptr request */
                pszTemp = pszProtPtrNames[uConfigNum];
                rc = NO_ERROR;          /* set good error code */
            }
        }
    }
    if (rc == NO_ERROR) {               /*D261*/
        if (pszTemp == NULL) {          /*D261*/
            pszTemp = szDefPtrDev;      /* Use default name if null, D261*/
        }                               /*D261*/
        *ppszDeviceName = pszTemp;      /* Set pointer if successful, D261*/
    }                                   /*D261*/

    RestoreRegs();                      /* restore all registers */
    return (rc);                        /* return error code */
}

/*@T15 = 03/03/89 TPL, DCR 203 changes, P3730 */
/*@TB3 = 03/16/89 TLB - Exit if error_vio_internal_resource returned    */
/*                      from BVSGETVIDEOCALLTABLES.  PTR B700462        */
/*@T19 = 03/16/89 TPL, Avoid loading AVIO at boot time, P6741 */
/*@T21 = 03/22/89 TPL, DCR 203 fix, SM83191 */
/*@P1  = 03/25/89  PL, DCR 132 changes */
/*JOPT = 03/30/89 STJ, Convert all local routines to NEAR subroutines   */
/*@T25 = 04/04/89 TPL, Sizzle work, B701685 */
/*D261 = 04/26/89 STJ, Use CONFIG.SYS instead of OS2.INI and optimize   */
/*B702118 = 05/01/89 STJ, Record presence of devices only if variable found */
/*@S10 = 05/11/89 STJ, Retain separate ENVB/LVB for each config, B701174 */
/*B702438 = 05/25/89 STJ, Remove environment variables correctly */
/*@T51 = 05/26/89 TPL, Removal of 3xbox code for 2.0, DCR 432.10 */
/*@S16 = 06/26/89 STJ, Retry loading all VDHs at Shell Init time, HZ00235 */
/*@D549 = 11/02/89 CJJ, Seltable changes due to rangeless sessions, DCR 549 */
/*@S42 = 11/08/89 STJ, Add VIDEO_WINDOW support */
