/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = EDDVCREC                                       */
/*                                                                    */
/*   Description     = Display Device Driver Function:                */
/*                     CharRect.                                      */
/*                                                                    */
/*   Function        = CharRect draws a rectangle taken from the LVB. */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
#define INCL_DDICOMFLAGS
#define INCL_DDIMISC
#define INCL_WINP_FSRS
#define INCL_NOSHIELDPROCS

#include <eddinclt.h>

#include <edddtypt.h>

#include <eddvcone.h>
#include <eddmcone.h>

#include <eddaextf.h>
#include <eddgextf.h>
#include <eddmextf.h>
#include <eddvextf.h>
#ifdef DCAF                                                               //DCAF
#include <dcafextf.h>                                                     //DCAF
#endif                                                                    //DCAF

#include <eddhcone.h>
#include <eddhtype.h>
#include <eddhmacr.h>

#include <cursor.h>

extern DDTType          DDT;
extern AVIOPB           AIxfer;
extern RGNRECT          GetClipsControl;
extern ClipRectangle    GetClipsBounds;
extern CURSORDATA       cursor_data;

/**********************************************************************/
/*                                                                    */
/* This draws a rectangle taken from LVB.                             */
/*                                                                    */
/* CharRect is given the coordinates of the bottom-left of the        */
/* rectangle and the dimensions of the rectangle. The rectangle is    */
/* clipped to the window and to the current clip regions.             */
/*                                                                    */
/* The drawing is done using PMBLKCGA or PMBLKMFI depending on        */
/* whether the length of the buffer cell in the LVB is two or four    */
/* bytes.  The same format is used for the parameter block for both   */
/* functions.                                                         */
/*                                                                    */
/**********************************************************************/

DDIENTRY eddv_CharRect (HDC             hdc,
                        PVIOPS          VioPS,
                        LPGridRectRef   ArgCharRect,
                        PDC             pdcArg,
                        ULONG           FunN)

{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    AvioRect           RectRelToWindow; /* Rect relative to window    */
    AvioRect           ClippedRect;     /* Rect clipped to window     */
    AvioPoint          ScreenLVBOrigin; /* LVB origin on screen       */
    AvioRect           ScreenRect;      /* Screen rectangle           */
    DevRect            Bounds;          /* Bounds rectangle           */
    BOOL               TextCursorExcluded;/* Flag to hold cursor state*/
    USHORT             NoOfRows;        /* Temporary variables used   */
    USHORT             NoOfCells;       /*   in calc of address       */
    USHORT             Offset;          /*   passed to A.I. call      */
    PAVIOINFO          pAvioInfo;

    /******************************************************************/
    /* Prevent compiler warnings.                                     */
    /******************************************************************/
    IgnoreParam(hdc);

    /******************************************************************/
    /* Get driver semaphore and perform entry checks                  */
    /******************************************************************/
    EnterDriver(pdcArg, FunN, EDF_STANDARD);

    /******************************************************************/
    /* Apply the DC command mask to the command bits                  */
    /******************************************************************/
    COMMANDBITS(FunN) &= pdc->DCICommandMask;

    /******************************************************************/
    /* Make the AVIO information slightly easier to access.           */
    /******************************************************************/
    pAvioInfo = &(pdc->DCIAvioInfo);

    /******************************************************************/
    /* Check if this is the same AVIO font as last time.  If not we   */
    /* must call CheckFont                                            */
    /******************************************************************/
    if ( (pAvioInfo->bCellWidth  != (BYTE)VioPS->CellImageWidth)  ||
         (pAvioInfo->bCellHeight != (BYTE)VioPS->CellImageHeight) ||
         (VioPS->CodepageID      != pdc->DCIAvioFonts[0].usCodePage) )
    {
        /**************************************************************/
        /* Only call this if the font has changed                     */
        /**************************************************************/
        CheckAVIOFonts(VioPS);

        /**************************************************************/
        /* We will need to redisplay the cursor at a position         */
        /* suitable for the new font.  Leave this to UpdateCursor.    */
        /* For now we reset the cursor showing flag (since the entire */
        /* window is redrawn) and set the cursor width to 255 (so     */
        /* UpdateCursor will recalculate cursor parameters).          */
        /**************************************************************/
//      VioPS->CursorWidth = 0xff;

        /**************************************************************/
        /* Clear the CursorShowing flag This will prevent it being    */
        /* removed if it is showing (but the entire window is redrawn */
        /* anyway) and the cursor will not be redrawn until the next  */
        /* UpdateCursor, when the new position can be calculated      */
        /**************************************************************/
//      VioPS->rgfShieldStates &= ~fCursorShowing;
    }
    else
    {
        /**************************************************************/
        /* Don't call check font, just set the offset in case the     */
        /* window has moved.                                          */
        /**************************************************************/
        CalculateGridOffsets(pAvioInfo, VioPS);
    }

    /******************************************************************/
    /* Make sure that the AVIO fonts are still cached (haven't been   */
    /* evicted)                                                       */
    /******************************************************************/
    CheckAVIOFontsCached( VioPS );

    /******************************************************************/
    /* Return immediately if this is not a Direct DC. Note that       */
    /* this does NOT return an error.                                 */
    /******************************************************************/
    if (pdc->DCIDCType != OD_DIRECT)
    {
        goto CHARRECT_OK_EXIT;
    }

    /******************************************************************/
    /* The routine takes no action if the character rectangle         */
    /* is empty.                                                      */
    /******************************************************************/
    if ( (ArgCharRect->RectWidth == 0) ||
         (ArgCharRect->RectDepth == 0) )
    {
        goto CHARRECT_OK_EXIT;
    }

    /******************************************************************/
    /* Do bounds if required                                          */
    /* bounds is required                                             */
    /******************************************************************/
#ifdef DCAF                                                               //DCAF
    if ( FunNTest(COM_BOUND | COM_ALT_BOUND) ||                           //DCAF
         DCAFBoundsRequired(FunN) )                                       //DCAF
#else                                                                     //DCAF
    if (FunNTest(COM_BOUND | COM_ALT_BOUND))
#endif                                                                    //DCAF
    {
        /**************************************************************/
        /* Calculate the coordinates of the given character rectangle */
        /* relative to the LVB window; these coords may be negative   */
        /**************************************************************/
        Bounds[0].X = (SHORT) ((LONG)ArgCharRect->StartColumn -
                                       (LONG)VioPS->WindowOriginColumn);
        Bounds[0].Y = (SHORT) ((LONG)ArgCharRect->StartRow -
                                          (LONG)VioPS->WindowOriginRow);
        Bounds[1].X =
               (SHORT) ((LONG)Bounds[0].X + (LONG)ArgCharRect->RectWidth);
        Bounds[1].Y =
               (SHORT) ((LONG)Bounds[0].Y + (LONG)ArgCharRect->RectDepth);
        Bounds[0].X *= (SHORT)VioPS->CellImageWidth;
        Bounds[0].Y *= (SHORT)VioPS->CellImageHeight;
        Bounds[1].X *= (SHORT)VioPS->CellImageWidth;
        Bounds[1].Y *= (SHORT)VioPS->CellImageHeight;

#ifdef DCAF                                                               //DCAF
        if (FunNTest(COM_BOUND | COM_ALT_BOUND))                          //DCAF
        {                                                                 //DCAF
#endif                                                                    //DCAF
        eddg_AddBounds ((pDevRect)Bounds,
                        FunN,
                        COORD_DEVICE_WORD);
#ifdef DCAF                                                               //DCAF
        }                                                                 //DCAF
                                                                          //DCAF
        /**************************************************************/  //DCAF
        /* Accumulate DCAF screen bounds if required                  */  //DCAF
        /**************************************************************/  //DCAF
        if ( DCAFBoundsRequired(FunN) )                                   //DCAF
        {                                                                 //DCAF
            AccumulateScreenBoundsThroughClips( (pDevRect)Bounds,         //DCAF
                                                COORD_DEVICE_WORD );      //DCAF
        }                                                                 //DCAF
#endif                                                                    //DCAF
    }

    /******************************************************************/
    /* Test COM_DRAW bit and rgfShieldStates to check if any drawing  */
    /* is required.                                                   */
    /******************************************************************/
    if ( !(FunNTest(COM_DRAW)) ||
         (VioPS->rgfShieldStates & fServeNobody) ||
         ( (VioPS->rgfShieldStates & fServeShieldOnly) &&
           !(VioPS->rgfShieldStates & fShieldCalling)  )  )
    {
        goto CHARRECT_OK_EXIT;
    }

    /******************************************************************/
    /* Calculate the coordinates of the given character rectangle     */
    /* relative to the LVB window; these coords may be negative       */
    /******************************************************************/
    RectRelToWindow[0].Col =
             (SHORT) ((LONG)ArgCharRect->StartColumn -
                                       (LONG)VioPS->WindowOriginColumn);
    RectRelToWindow[0].Row =
             (SHORT) ((LONG)ArgCharRect->StartRow -
                                          (LONG)VioPS->WindowOriginRow);
    RectRelToWindow[1].Col =
             (SHORT) ((LONG)RectRelToWindow[0].Col +
                                      (LONG)ArgCharRect->RectWidth - 1);
    RectRelToWindow[1].Row =
             (SHORT) ((LONG)RectRelToWindow[0].Row +
                                      (LONG)ArgCharRect->RectDepth - 1);

    /******************************************************************/
    /* Clip the character rectangle to the window; the clipped        */
    /* rectangle is given relative to the window origin.              */
    /* If the clipped rectangle is empty then no drawing is required. */
    /******************************************************************/
    ClippedRect[0].Col = max( 0, RectRelToWindow[0].Col );
    ClippedRect[0].Row = max( 0, RectRelToWindow[0].Row );
    ClippedRect[1].Col = min( (SHORT)VioPS->WindowWidth - 1,
                                              RectRelToWindow[1].Col );
    ClippedRect[1].Row = min( (SHORT)VioPS->WindowHeight - 1,
                                              RectRelToWindow[1].Row );

    /******************************************************************/
    /* Calculate the screen window origin in screen AVIO coordinates. */
    /* The DC origin might be in the middle of a partial cell and so  */
    /* we need to add in the partial offset to get to the start of the*/
    /* whole cell containing the DC origin                            */
    /******************************************************************/
    ScreenLVBOrigin.Row = ( (SHORT)(DDT.ScreenHeight - 1) -
       ((signed CHAR)VioPS->PartialCellAdjust + pdc->DCIOrigin.Y )) /
                                            (SHORT)VioPS->CellImageHeight;


    if (pdc->DCIOrigin.X > 0)
    {
        ScreenLVBOrigin.Col = (pdc->DCIOrigin.X +
                               (SHORT)VioPS->CellImageWidth - 1) /
                                             (SHORT)VioPS->CellImageWidth;
    }
    else
    {
        ScreenLVBOrigin.Col = pdc->DCIOrigin.X /
                                             (SHORT)VioPS->CellImageWidth;
    }

    /******************************************************************/
    /* Convert the clipped rectangle into screen AVIO coordinates,    */
    /* the origin of which is the top left corner of the screen.      */
    /******************************************************************/
    ScreenRect[0].Col = ScreenLVBOrigin.Col + ClippedRect[0].Col;
    ScreenRect[1].Col = ScreenLVBOrigin.Col + ClippedRect[1].Col;
    ScreenRect[0].Row = ScreenLVBOrigin.Row - ClippedRect[1].Row;
    ScreenRect[1].Row = ScreenLVBOrigin.Row - ClippedRect[0].Row;

    /******************************************************************/
    /* Clip ScreenRect so that all coordinates are positive.          */
    /******************************************************************/
    if (ScreenRect[0].Col < 0)
    {
        /**************************************************************/
        /* Adjust size of ClippedRect and make ScreenRect.Col zero    */
        /**************************************************************/
        ClippedRect[0].Col -= ScreenRect[0].Col;
        ScreenRect[0].Col = 0;
    }

    if (ScreenRect[0].Row < 0)
    {
        /**************************************************************/
        /* Adjust size of ClippedRect and make ScreenRect.Row zero    */
        /**************************************************************/
        ClippedRect[1].Row += ScreenRect[0].Row;
        ScreenRect[0].Row = 0;
    }

    if ( (ClippedRect[0].Col > ClippedRect[1].Col) ||
         (ClippedRect[0].Row > ClippedRect[1].Row) )
    {
        goto CHARRECT_OK_EXIT;
    }

    if (cursor_data.cursor_status & CURSOR_SOFTWARE )
    {
        /**************************************************************/
        /* Bounds are required so calculate and accumulate..          */
        /* Bounds are calculated in device coordinates                */
        /**************************************************************/
        Bounds[0].X = ClippedRect[0].Col * VioPS->CellImageWidth;
        Bounds[1].X = (ClippedRect[1].Col * VioPS->CellImageWidth) +
                                            (VioPS->CellImageWidth - 1);
        Bounds[0].Y = (ClippedRect[0].Row * VioPS->CellImageHeight) +
                                  (signed CHAR)VioPS->PartialCellAdjust;
        Bounds[1].Y = (ClippedRect[1].Row * VioPS->CellImageHeight) +
                       (VioPS->CellImageHeight - 1) +
                                  (signed CHAR)VioPS->PartialCellAdjust;
    }

    /**************************************************************/
    /* Set up the start postion and dimensions of the rectangle   */
    /* to be output (these are all in screen AVIO coordinates)    */
    /**************************************************************/
    AIxfer.bColumn = (BYTE)ScreenRect[0].Col;
    AIxfer.bRow    = (BYTE)ScreenRect[0].Row;
    AIxfer.bAcross =
                 (BYTE)(ScreenRect[1].Col - ScreenRect[0].Col + 1);
    AIxfer.bDown   =
                 (BYTE)(ScreenRect[1].Row - ScreenRect[0].Row + 1);

    /**************************************************************/
    /* Calculate address of character cell in top-left corner of  */
    /* clipped rectangle.                                         */
    /**************************************************************/
    NoOfRows  = (VioPS->BufferRowCount - 1) -
                      (ClippedRect[1].Row + VioPS->WindowOriginRow);
    NoOfCells = (NoOfRows * VioPS->BufferColumnCount) +
                     (ClippedRect[0].Col + VioPS->WindowOriginColumn);
    Offset = NoOfCells * VioPS->CellByteSize;

    /**************************************************************/
    /* Set up pointer to LVB for call                             */
    /**************************************************************/
    AIxfer.pLVBChars = (PBYTE)(VioPS->pLVB) + Offset;

    /**************************************************************/
    /* Set up LVB buffer width for call                           */
    /**************************************************************/
    AIxfer.bBufferWidth = (BYTE)VioPS->BufferColumnCount;

    /**************************************************************/
    /* Give destination bitmap header to PMBLK routines (it will  */
    /* be the screen)                                             */
    /**************************************************************/
    AIxfer.pbmhDest = pdc->DCISelListEntry;

    /**************************************************************/
    /* We also need to exclude the text cursor if it is currently */
    /* visible.                                                   */
    /**************************************************************/
    if ( VioPS->rgfShieldStates & fCursorShowing )
    {
        /**********************************************************/
        /* The cursor is on. Check whether it lies within the     */
        /* rectangle that we are about to draw.                   */
        /**********************************************************/
        if ( (ArgCharRect->StartColumn > VioPS->CursorCol) ||
             (ArgCharRect->StartRow > VioPS->CursorRow) ||
             ((ArgCharRect->StartColumn + ArgCharRect->RectWidth) <=
                                               VioPS->CursorCol) ||
             ((ArgCharRect->StartRow + ArgCharRect->RectDepth) <=
                                                  VioPS->CursorRow)
           )
        {
            /******************************************************/
            /* There is no intersection so we can go ahead        */
            /******************************************************/
            TextCursorExcluded = FALSE;
        }
        else
        {
            /******************************************************/
            /* The rectangles do intersect, so the text cursor    */
            /* needs to be excluded.                              */
            /* We almost never need to delete the cursor but if   */
            /* we get MFI text with background transparent then   */
            /* we can't leave it on because it may show through.  */
            /* At this stage we can only check whether the text is*/
            /* MFI or not as background opaque/transparent is     */
            /* decided on a character by character basis.         */
            /* So set the TextCursorExcluded flag anyway and for  */
            /* MFI text remove the cursor.                        */
            /******************************************************/
            /* KH 7/17/92: cannot set TextCursorExcluded TRUE if  */
            /* the cursor is not excluded since this function     */
            /* does another invert later if excluded. If the      */
            /* cursor has not been removed then it will be at     */
            /* time and we lose track of whether it is visible or */
            /* invisible. Only set TRUE if exclusion really done. */
            /******************************************************/
            TextCursorExcluded = TRUE;
            InvertAVIOCursor(VioPS);
        }
    }
    else
    {
        TextCursorExcluded = FALSE;
    }

    /**************************************************************/
    /* exclude any software cursor (pointer). Must be done        */
    /* after text cursor inversion (because text cursor routine   */
    /* already disables and reenables the mouse cursor if needed  */
    /**************************************************************/
    if ( cursor_data.cursor_status & CURSOR_SOFTWARE )
    {
        eddm_ExcludeCursor(Bounds, COORD_DEVICE_WORD);
    }

    /**************************************************************/
    /* Set up the parameters for drawing into all the cached      */
    /* clip rectangles.                                           */
    /**************************************************************/
    AIxfer.pFirstClipRect = pdc->DCIClipRects;

    /**************************************************************/
    /* Set the bounds which intersect clip to whole drawing area  */
    /**************************************************************/
    GetClipsBounds.X0 = pdc->DCIBoundingClip[0].X;
    GetClipsBounds.X1 = pdc->DCIBoundingClip[1].X + 1;
    GetClipsBounds.Y0 = pdc->DCIBoundingClip[0].Y;
    GetClipsBounds.Y1 = pdc->DCIBoundingClip[1].Y + 1;

    /**********************************************************/
    /* do the appropriate drawing to all clip regions         */
    /**********************************************************/
    GetClipsControl.ircStart = 1;
    do /* for each rectangle comprising the clip region */
    {
        /******************************************************/
        /* set the GetClips control structure number of       */
        /* rectangles returned to zero so that we will exit   */
        /* loop if all rectangles processed                   */
        /******************************************************/
        GetClipsControl.crcReturned = 0;

        /******************************************************/
        /* if the total number of clips comprising the region */
        /* exceed the size of the dc cache or NotifyClipChange*/
        /* was called then get the cache updated from engine  */
        /******************************************************/
        if ( (pdc->DCIEngineClips > CACHED_CLIPS) ||
                                           pdc->ClipChanged )
        {
            edda_GetClipRectangles();
            /**************************************************/
            /* reset the start rectangle for next iteration   */
            /**************************************************/
            GetClipsControl.ircStart +=
                                    GetClipsControl.crcReturned;

        }

        /**************************************************************/
        /* if there were some clip rectangles returned from the engine*/
        /* then call the low level blt function which can cope with   */
        /* performing the blt to all the cached clip rectangles       */
        /**************************************************************/
        if (pdc->DCIClipNum)
        {
            AIxfer.cClipRects = pdc->DCIClipNum;
            /**************************************************************/
            /* Now draw the text into all the current clip rectangles     */
            /**************************************************************/
            if (VioPS->CellByteSize == 2)
            {
                CGAText();
            }
            else
            {
                MFIText();
            }
        }

    } while ( GetClipsControl.crcReturned &&
          (GetClipsControl.ircStart <= pdc->DCIEngineClips));


    /******************************************************************/
    /* enable colour cursor (mouse pointer) updates                   */
    /******************************************************************/
    reenable_cursor();

    /**************************************************************/
    /* If the text cursor was removed - either explicitly or by   */
    /* being overwritten - then put it back.                      */
    /**************************************************************/
    if (TextCursorExcluded)
    {
        InvertAVIOCursor(VioPS);
    }


CHARRECT_OK_EXIT:
    /******************************************************************/
    /* Release driver semaphore                                       */
    /******************************************************************/
    ExitDriver(pdcArg, FunN, EDF_STANDARD);

    return(OK_ZERO);
}
