/******************************** MODULE HEADER ****************************
 * devcaps.c
 *      Implements the DrvDeviceCapabilities function - returns information
 *      about the printers capabilities.
 *
 *
 *  Copyright (C)  1993,  Microsoft Corporation.
 *
 ****************************************************************************/

#include        "rasuipch.h"
#pragma hdrstop("rasuipch.h")
#define GLOBAL_MACS
#include "gblmac.h"
#undef GLOBAL_MACS

/*
 *   The following sizes are copied from the Win 3.1 driver.  They do not
 *  appear to be defined in any public place,  although it looks like
 *  they should be.
 */

#define CCHBINNAME      24      /* Characters allowed for bin names */
#define CCHDEVNAME      64      /* Driver file name limits */
#define CDEPENDENTFILES     3       /* # of dependent files. */
#define CCHPAPERNAME        64      /* Max length of paper size names */


/*
 *    A couple of macros to turn master units into 0.1mm units - the
 *  value needed for some of these calls.
 */

#define MAST01mmX(xx) (((xx) * 254 + pdh->ptMaster.x / 2) / pdh->ptMaster.x)
#define MAST01mmY(yy) (((yy) * 254 + pdh->ptMaster.y / 2) / pdh->ptMaster.y)



/*
 *   Useful external variables that we use.
 */

extern  HANDLE      hHeap;         /* For all our memory wants */

/*
 *    Local function prototypes.
 */

void
vGetBinNames(
int     iCount,
short  *psIndex,
WCHAR  *pwchOut,                /* Where the output is created */
PRASDDUIINFO  pRasdduiInfo      /* Rasddui UI data */
);


/******************************* Function Header *****************************
 * DrvDeviceCapabilities
 *      Called by an application wanting to find out what this particular
 *      printer is able to do.
 *
 * RETURNS:
 *      -1 on error,  else depends upon information requested.
 *
 * HISTORY:
 *  15:09 on Tue 13 Apr 1993    -by-    Lindsay Harris   [lindsayh]
 *      First version, borrowed from Win 3.1
 *
 *****************************************************************************/

DWORD
DrvDeviceCapabilities( hPrinter, pDeviceName, iDevCap, pvOutput, pDMIn )
HANDLE    hPrinter;          /* Access to registry via spooler */
PWSTR     pDeviceName;       /* Particular printer model name */
WORD      iDevCap;           /* Capability required */
void     *pvOutput;          /* Output area (for some) */
DEVMODE  *pDMIn;             /* DEVMODE defining mode of operation etc */
{


    int          iI;         /* Classic loop index */
    int          iCount;     /* Count the number of items */
    short       *psIndex;    /* For scanning arrays of offsets */

    DWORD        dwRet;      /* Return  value */

    PRINTER_INFO PI;         /* To set things up */
    POINTw       ptwSize;    /* Win 3.1 POINT structure (2 shorts) */

    EXTDEVMODE   edm;        /* Working copy of DEVMODE + */
    EXTDEVMODE  *pEDM;       /* Access to the above, or what's passed in */

    DOWNLOADINFO *pDLI;      /* Check for download capabilities */
    PAGECONTROL  *pPageCtrl; /* Number of copies info */
    PAPERSIZE    *pSize;
    long         lTmp;
    PRASDDUIINFO pRasdduiInfo = NULL; /* Common UI Data */
    DWORD        OEMResult = GDI_ERROR;

TRY

    /*
     *   Note that the output area is one of many kinds,  depending upon the
     *  type of information requested.  To make the code cleaner,  here
     *  are some macros to define different flavours of output area.
     */

#define psOutput   ((short *)pvOutput)
#define pbOutput   ((BYTE *)pvOutput)
#define pptOutput  ((POINT *)pvOutput)
#define pdwOutput  ((DWORD *)pvOutput)
#define pwchOutput ((WCHAR *)pvOutput)

    /* Allocate the global data structure */
    if (!(pRasdduiInfo = pGetUIInfo(hPrinter, pDMIn, ePrinter, eNoChange, eNoHelp)))
    {
        RASUIDBGP(DEBUG_ERROR,("Rasddui!DrvDeviceCapabilities: Allocation failed; pRasdduiInfo is NULL\n") );
        return  (DWORD)-1;
    }
    pEDM = pRasdduiInfo->pEDM;
    
    //undefined the macro fGeneral to stop compilation error.
    #if defined(fGeneral)
    #undef  fGeneral
    #endif

    /*
     *    Red tape is all processed,  so now determine the information
     *  that the user requested!
     */

    dwRet = (DWORD)-1;              /* Default error return */


    /* Call the OEM.
     */
    OEMResult = OEMDrvDeviceCapabilities(pRasdduiInfo, hPrinter, pDeviceName, iDevCap, pvOutput, pDMIn);
    
    /* DC_FIELDS. If the result is GDI_ERROR, null it out so the OR below has no effect.
     */
    if (iDevCap == DC_FIELDS) {
       if (OEMResult == GDI_ERROR) {
         OEMResult = 0;
       }
    }
    
    /* Not DC_FIELDS. Accept a good return and leave.
     */
    else {
      if (OEMResult != GDI_ERROR) {
         dwRet = OEMResult;
         LEAVE;
      }
    }
    

    // initialize psize , we need to check fGeneral bit
    pSize = GetTableInfo( pdh, HE_PAPERSIZE, pEDM );

    switch( iDevCap )
    {
    case DC_FIELDS:
        dwRet = (DWORD)pEDM->dm.dmFields | OEMResult;
        break;

    case DC_DUPLEX:
        // return 1 if the printer is capable of duplex printing.
        // Otherwise, return 0.
        dwRet =  (DWORD)(pEDM->dm.dmFields & DM_DUPLEX ? TRUE : FALSE);
        break;

    case DC_SIZE:
        // return the size of DEVMODE structure.
        dwRet =   (DWORD)pEDM->dm.dmSize;
        break;

    case DC_EXTRA:
        // return the size of DRIVEREXTRA structure.
        dwRet =  (DWORD)pEDM->dm.dmDriverExtra;
        break;

    case DC_VERSION:
        dwRet =  (DWORD)pEDM->dm.dmSpecVersion;
        break;

    case DC_DRIVER:
        dwRet =  (DWORD)pEDM->dm.dmDriverVersion;
        break;

    case DC_BINS:                  /* Paper bin IDs */
    case DC_BINNAMES:              /* Paper bin IDs and names */

        psIndex = (short *)((BYTE *)pdh + pdh->loHeap +
                                        pModel->rgoi[ MD_OI_PAPERSOURCE ]);

        // count the # of items in the list.
        for( iCount = 0; psIndex[iCount] != 0; iCount++ )
                              ;

        if( pvOutput )
        {
            /*
             *    An output area address,  so put the data in there.
             */

            switch( iDevCap )
            {
            case DC_BINS:
                //Add the Form source as the first source; This psedo source
                //maps to The Print Manager Setting for the forms for a
                //given printer.
                *psOutput++ = DMBIN_FORMSOURCE;
                for( iI = 0; iI < iCount; iI++, ++psIndex )
                {
                    PAPERSOURCE   *pPS;

                    pPS = GetTableInfoIndex( pdh, HE_PAPERSOURCE,
                                                              *psIndex - 1 );
                    *psOutput++ = pPS->sPaperSourceID;
                }
                break;

            case DC_BINNAMES:
                vGetBinNames( iCount, psIndex, pvOutput,pRasdduiInfo );
                break;

            }

        }

        //Add a psedo source for every printer which will map to
        //Printmanager setting for the Forms. So the driver report
        //one extra source.
        dwRet = (DWORD)iCount+1;
        break;

    case DC_PAPERS:                /* Available forms by index numner */
    case DC_PAPERNAMES:            /* Paper sizes by name, order as DC_PAPERS */
    case DC_PAPERSIZE:             /* Paper sizes in units of 0.1mm */
        dwRet = 0;
        for( iI = 0; pFD[ iI ].pFI; iI++ )
        {
            /*
             *    Check that this form is suitable with this printer.
             *  This happens if ANY bit in dwSource is set.
             */


            if( pFD[ iI ].dwSource )
            {
                /*   Valid data,  so up the count & copy the name (if needed) */

                ++dwRet;                 /* One more to return  */

                if( pvOutput )
                {
                    /*  Output is desired too!  */

                    switch( iDevCap )
                    {
                    case  DC_PAPERS:
                        *psOutput++ = iI + 1;    /* These are 1 based */
                        break;


                    case  DC_PAPERNAMES:
                        wcsncpy( pwchOutput, pFD[ iI ].pFI->pName, CCHPAPERNAME );
                        pwchOutput += CCHPAPERNAME;
                        break;

                    case  DC_PAPERSIZE:

                        /*  Convert from Master Units to 0.1mm */
                        pptOutput->x = MAST01mmX( pFD[ iI ].pFI->Size.cx );
                        pptOutput->y = MAST01mmY( pFD[ iI ].pFI->Size.cy );

                        // We need to consider PS_ROTATE bit in here
                        // Fixes a problem when printing on envelopes

                        if( pSize->fGeneral & PS_ROTATE )
                        {
                            lTmp = pptOutput->x;
                            pptOutput->x = pptOutput->y;
                            pptOutput->y = lTmp;
                        }

                        pptOutput++;
                        break;
                    }
                }

            }

        }
        break;

    case DC_MINEXTENT:      /* Return minimum paper size allowed */
    case DC_MAXEXTENT:      /* Ditto, but maximum paper size */

        if( iDevCap  == DC_MINEXTENT )
        {
            ptwSize = pModel->ptMin;           /* Minimum size, master units */
        }
        else
        {
            /*
             *   Maximum size:  there are several minor points to consider,
             *  namely that there is a no limit value that we turn into
             *  something else.
             */

            ptwSize.x = pModel->sMaxPhysWidth;       /* No question */
            ptwSize.y = pModel->ptMax.y;
            if( ptwSize.y == NOT_USED )
                ptwSize.y = 0x7fff;                  /* Win 3.1 compatability */
        }
        dwRet = MAKELONG( MAST01mmX( ptwSize.x ), MAST01mmY( ptwSize.y ) );
        break;

    case DC_ENUMRESOLUTIONS:
        /*
         *     Return the list of resolutions supported by the device.
         * Each resolution is represented by a pair of DWORD's for
         * the X and Y values respectively.
         * If pvOutput is null, simply return the # of resolution supported.
         */

        psIndex = (short *)((BYTE *)pdh + pdh->loHeap +
                                           pModel->rgoi[ MD_OI_RESOLUTION ]);

        /*
         *   Scan through the array of resolutions available for this
         *  printer.   If there is an output area,  then copy the results
         *  there as we go.
         */

        for( iI = 0; psIndex[ iI ]; iI++ )
        {

            if( pvOutput )
            {
                /*  Copy the numbers to the output area */
                RESOLUTION *pRes;

                pRes = GetTableInfoIndex( pdh, HE_RESOLUTION, psIndex[ iI ] - 1 );

                *pdwOutput++ = (DWORD) ((pdh->ptMaster.x /
                                 pRes->ptTextScale.x) >> pRes->ptScaleFac.x);
                *pdwOutput++ = (DWORD) ((pdh->ptMaster.y /
                                   pRes->ptTextScale.y) >> pRes->ptScaleFac.y);
            }

        }
        dwRet = (DWORD)iI;

        break;


    case DC_FILEDEPENDENCIES:
#if 0
        if (pvOutput)
        {
            lstrcpy(pvOutput, (LPSTR)szUnidrv);
            pvOutput += CCHDEVNAME;
            lstrcpy(pvOutput, (LPSTR)szDMColor);
            pvOutput += CCHDEVNAME;
            lstrcpy(pvOutput, (LPSTR)szUnihelp);
        }
        dwRet = (DWORD) CDEPENDENTFILES;
#else
        dwRet = 0;
        if( pwchOutput )
            *pwchOutput = (WCHAR)0;
#endif
        break;

    case DC_TRUETYPE:              /* What can we do with TrueType */

        if( !(pEDM->dm.dmFields & DM_TTOPTION) )
        {
            // TrueType fonts are not available
            dwRet = (DWORD) 0;
            break;
        }


#if defined( DCTT_DOWNLOAD ) || defined( DCTT_BITMAP )
        pDLI = GetTableInfo( pdh, HE_DOWNLOADINFO, pEDM );
        if (pDLI && pDLI->cbBitmapFontDescriptor == sizeof( SF_HEADER ) &&
            !(pModel->fGeneral & MD_SERIAL) )
        {
            dwRet = (DWORD) (DCTT_BITMAP | DCTT_DOWNLOAD);
        }
        else
            dwRet = (DWORD) DCTT_BITMAP;
#else
        pDLI;        /* Stop warning */
        dwRet = 0;
#endif

        break;

    case DC_ORIENTATION:
        // return the position of landscape relative (couterclock-wise)
        // to portrait. "0" means no landscape orientation available.
        // "90" is what seen on PCL page printers, and "270" is for
        // dot-matirx printers.


        if( pModel->fGeneral & MD_LANDSCAPE_RT90 )
            dwRet = 90;
        else
            dwRet = 270;
        break;

    case DC_COPIES:             /* Maximum number of copies we can print */

        pPageCtrl = GetTableInfo( pdh, HE_PAGECONTROL, pEDM );

        if( pPageCtrl )
            dwRet = (DWORD)pPageCtrl->sMaxCopyCount;
        else
            dwRet = 1;
        break;

#if 0
/* !!!LindsayH - what the hell is DC_BINADJUST??? */
    case DC_BINADJUST:
        {
        PAPERSOURCE *pSrc;

        pSrc = GetTableInfo( pdh, HE_PAPERSOURCE, pEDM );
        if (pSrc->sBinAdjust >= DCBA_FIRST && pSrc->sBinAdjust <= DCBA_LAST)
            dwRet = (DWORD) pSrc->sBinAdjust;
        else
            dwRet = (DWORD) -1;
        break;
        }
#endif
    }
ENDTRY

FINALLY
    vReleaseUIInfo(&pRasdduiInfo);
ENDFINALLY

    return  dwRet;
}



/*************************** Function Header *********************************
 * vGetBinNames
 *      Fills in the output area to contain the names of paper sources.  These
 *      are returned as Unicode strings.
 *
 * RETURNS:
 *      Nothing.
 *
 * HISTORY:
 *  10:53 on Wed 14 Apr 1993    -by-    Lindsay Harris   [lindsayh]
 *      Mark I, based on Win 3.1 code.
 *
 *****************************************************************************/

void
vGetBinNames(
int     iCount,
short  *psIndex,
WCHAR  *pwchOut,                /* Where the output is created */
PRASDDUIINFO  pRasdduiInfo      /* Rasddui UI data */
)
{
    int     iI;               /* Loop index */
    int     iID;              /* The paper source ID, turned into name */

    PAPERSOURCE  *pSource;    /* To reference the data as we go along */


    //Set first name of the source pseduo source which maps to
    //Printman forms setting.
    LoadStringW( hModule, SRC_FORMSOURCE, pwchOut, CCHBINNAME );
    pwchOut += CCHBINNAME;           /* Skip over this one  */

    for( iI = 0; iI < iCount && *psIndex; iI++, ++psIndex )
    {
        pSource = GetTableInfoIndex( pdh, HE_PAPERSOURCE, *psIndex - 1 );
        iID = pSource->sPaperSourceID;

        if( iID <= DMBIN_USER )
        {
            /*  Standard name that we have */
            LoadStringW( hModule, iID + SOURCE, pwchOut, CCHBINNAME );
        }
        else
        {
            /*  Minidriver defined form name */
            iLoadStringW( &WinResData, iID, pwchOut, CCHBINNAME);
        }

        pwchOut += CCHBINNAME;           /* Skip over this one  */
    }

    return;
}