/*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          = EDDVCSTR                                       */
/*                                                                    */
/*   Description     = Display Device Driver Function:                */
/*                     CharStr.                                       */
/*                                                                    */
/*   Function        = CharStr draws a string taken from the LVB.     */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
#define INCL_DDICOMFLAGS
#define INCL_DDIMISC
#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 string taken from LVB.                              */
/*                                                                    */
/*   CharStr is given the coordinates of the starting position of     */
/* the string and the length of the string. The string 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.                                                         */
/*                                                                    */
/*   The string folds at the end of a row (in the LVB) and continues  */
/* from the next row down.  The string continues for the given        */
/* string length or until the LVB is exhausted.                       */
/*                                                                    */
/*   The string is split into up to three sub-strings (where a row    */
/* refers to a row in the LVB):                                       */
/*                                                                    */
/*  -  a trailing string in the first row                             */
/*  -  a leading string in the last row                               */
/*  -  a number of full rows in between                               */
/*                                                                    */
/**********************************************************************/

DDIENTRY eddv_CharStr (HDC              hdc,
                       PVIOPS           VioPS,
                       LPGridStringRef  ArgCharString,
                       PDC              pdcArg,
                       ULONG            FunN)

{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    USHORT             CharsInFirstRow; /* from string starting posn  */
    USHORT             StringLength;    /* adjusted to lie in LVB     */
    ULONG              NoOfRects;       /* the string spilts into     */
    AvioBlock          CharRect[3];     /* character rectangles       */
                                        /*   created from string      */
    AvioRect           ScreenRect;      /* Screen rectangle           */
    AvioPoint          ScreenLVBOrigin; /* LVB origin on screen       */
    USHORT             NoOfWholeRows;   /* variables used in calc of  */
    USHORT             CharsInLastRow;  /*   character rectangles     */
    AvioRect           ClippedRect;     /* rect clipped to window     */
    DevRect            Bounds;          /* Device coords bounds rect  */
    BOOL               TextCursorExcluded;/* Flag to hold cursor state*/
    ULONG              i;               /* loop control variable      */
    USHORT             NoOfRows;        /* temporary variables used   */
    USHORT             NoOfCells;       /*   in calc of address       */
    USHORT             Offset;          /*   passed to A.I. call      */
    SHORT              CursorPosIndex;  /* For cursor exclusion       */
    SHORT              StringStartIndex;
    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;

    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);
    }
    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 CHARSTR_OK_EXIT;
    }

    /******************************************************************/
    /* Return an error if the starting position is outside the LVB or */
    /* the string length is negative                                  */
    /******************************************************************/
    if ((LONG)ArgCharString->StartColumn < 0 ||
        (LONG)ArgCharString->StartColumn >= VioPS->BufferColumnCount)
    {
        /**************************************************************/
        /* Log the error                                              */
        /**************************************************************/
        LogError(GRE_INVALIDCOLUMNINDEX);

        goto CHARSTR_ERR_EXIT;
    }

    if ((LONG)ArgCharString->StartRow < 0 ||
        (LONG)ArgCharString->StartRow >= VioPS->BufferRowCount)
    {
        /**************************************************************/
        /* Log the error                                              */
        /**************************************************************/
        LogError(GRE_INVALIDROWINDEX);

        goto CHARSTR_ERR_EXIT;
    }

    if ((LONG)ArgCharString->StringLength < 0)
    {
        /**************************************************************/
        /* Log the error                                              */
        /**************************************************************/
        LogError(GRE_NEGATIVELENGTH);

        goto CHARSTR_ERR_EXIT;
    }

    /******************************************************************/
    /* There is no need for any drawing if the string length is zero  */
    /******************************************************************/
    if (ArgCharString->StringLength == 0)
    {
        goto CHARSTR_OK_EXIT;
    }

    /******************************************************************/
    /* Accumulate bounds if required                                  */
    /******************************************************************/
    if (FunNTest(COM_BOUND | COM_ALT_BOUND) ||
#ifdef DCAF                                                               //DCAF
        DCAFBoundsRequired(FunN) ||                                       //DCAF
#endif                                                                    //DCAF
        cursor_data.cursor_status & CURSOR_SOFTWARE)
    {
        /**************************************************************/
        /* Calculate the coordinates of the given character rectangle */
        /* relative to the LVB window; these coords may be negative   */
        /**************************************************************/
        Bounds[0].X = (SHORT) ((LONG)ArgCharString->StartColumn -
                                       (LONG)VioPS->WindowOriginColumn);
        Bounds[0].Y = (SHORT) ((LONG)ArgCharString->StartRow -
                                          (LONG)VioPS->WindowOriginRow);

        CharsInFirstRow = VioPS->BufferColumnCount -
                                      (USHORT)ArgCharString->StartColumn;

        /**************************************************************/
        /* See if string will fit on the first row                    */
        /**************************************************************/
        if (ArgCharString->StringLength <= CharsInFirstRow)
        {
            Bounds[1].Y = Bounds[0].Y + 1;
            Bounds[1].X = Bounds[0].X + CharsInFirstRow;
        }
        else
        {
            /**********************************************************/
            /* See if string, after wrapping onto next row occupies   */
            /* the complete width of the buffer                       */
            /**********************************************************/
            if (ArgCharString->StringLength < VioPS->BufferColumnCount)
            {
                Bounds[1].Y = Bounds[0].Y + 2;
                Bounds[1].X = Bounds[0].X;
                Bounds[0].X = Bounds[0].X +
                (SHORT)ArgCharString->StringLength
                                            - VioPS->BufferColumnCount;
            }
            else
            {
                Bounds[0].X = VioPS->WindowOriginColumn;
                Bounds[1].X = Bounds[0].X +VioPS->BufferColumnCount;
                Bounds[1].Y = Bounds[0].Y + 1 +
                ((SHORT)ArgCharString->StringLength -
                                               CharsInFirstRow - 1 +
                VioPS->BufferColumnCount) /VioPS->BufferColumnCount;
            }
        }

        Bounds[0].X *= (SHORT)VioPS->CellImageWidth;
        Bounds[0].Y *= (SHORT)VioPS->CellImageHeight;
        Bounds[1].X *= (SHORT)VioPS->CellImageWidth;
        Bounds[1].Y *= (SHORT)VioPS->CellImageHeight;


        if (FunNTest(COM_BOUND | COM_ALT_BOUND))
        {
            eddg_AddBounds ((pDevRect)Bounds,
                            FunN,
                            COORD_DEVICE_WORD);
        }

#ifdef 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 CHARSTR_OK_EXIT;
    }

    /******************************************************************/
    /* 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;
    }

    /******************************************************************/
    /* Split the string up into character rectangles.                 */
    /* Calculate the number and coordinates of these rectangles       */
    /* (relative to the LVB origin).                                  */
    /******************************************************************/

    /******************************************************************/
    /* Get the maximum number of characters that could be in the first*/
    /* row..                                                          */
    /******************************************************************/
    CharsInFirstRow = VioPS->BufferColumnCount -
                                      (USHORT)ArgCharString->StartColumn;

    /******************************************************************/
    /* StringLength is the minimum of the string length passed in as  */
    /* a parameter and the number of characters in the LVB beyond the */
    /* start position of the string.  It then becomes the string      */
    /* length we work with.                                           */
    /******************************************************************/
    StringLength = min( (USHORT)ArgCharString->StringLength,
                         CharsInFirstRow + (VioPS->BufferColumnCount *
                                       (USHORT)ArgCharString->StartRow) );

    /******************************************************************/
    /* Since the string length is greater than zero there is always   */
    /* at least one rectangle.  If it is the only rectangle then it is*/
    /* a leading/trailing string - ie it does not necessarily begin   */
    /* at the left of the LVB or end at the right of the LVB.  If we  */
    /* find there are more rectangles then it will be a leading string*/
    /******************************************************************/
    CharRect[0].Col0 = (USHORT)ArgCharString->StartColumn;
    CharRect[0].Row0 = (USHORT)ArgCharString->StartRow;
    CharRect[0].Col1 = CharRect[0].Col0 +
                           min( StringLength, CharsInFirstRow ) - 1;
    CharRect[0].Row1 = CharRect[0].Row0;

    NoOfRects = 1;

    /******************************************************************/
    /* The usual case will be that of only a trailing string - check  */
    /* for this before calculating the other rectangles.              */
    /******************************************************************/
    if (StringLength > CharsInFirstRow)
    {
        /**************************************************************/
        /* The string we calculated above was in fact a leading       */
        /* string go on to calculate the dimensions of                */
        /* the full rows and the trailing string (there may be only   */
        /* one of these)                                              */
        /**************************************************************/

        /**************************************************************/
        /* Calculate number of whole rows left and chars in the       */
        /* last non-whole row                                         */
        /**************************************************************/
        CharsInLastRow = (StringLength - CharsInFirstRow) %
                                             VioPS->BufferColumnCount;
        NoOfWholeRows  = (StringLength - CharsInFirstRow) /
                                             VioPS->BufferColumnCount;

        if (CharsInLastRow > 0)
        {
            /**********************************************************/
            /* There is a trailing string so calculate its dimensions */
            /* and increase our rectangle count                       */
            /**********************************************************/
            CharRect[NoOfRects].Col0 = 0;
            CharRect[NoOfRects].Row0 =
                      (USHORT)ArgCharString->StartRow - NoOfWholeRows - 1;
            CharRect[NoOfRects].Col1 = CharsInLastRow - 1;
            CharRect[NoOfRects].Row1 =
                      CharRect[NoOfRects].Row0;

            NoOfRects++;
        }

        if (NoOfWholeRows > 0)
        {
            /**********************************************************/
            /* There is a block of full rows so calculate the         */
            /* dimensions and increase our rectangle count            */
            /**********************************************************/
            CharRect[NoOfRects].Col0 = 0;
            CharRect[NoOfRects].Row0 =
                          (USHORT)ArgCharString->StartRow - NoOfWholeRows;
            CharRect[NoOfRects].Col1 = VioPS->BufferColumnCount - 1;
            CharRect[NoOfRects].Row1 =
                          (USHORT)ArgCharString->StartRow - 1;

            NoOfRects++;
        }
    }

    /******************************************************************/
    /* Set the destination bitmap to be the screen.                   */
    /******************************************************************/
    AIxfer.pbmhDest = pdc->DCISelListEntry;

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


    /******************************************************************/
    /* Now we are going to do some drawing so determine whether there */
    /* is any need for cursor exculsion.                              */
    /******************************************************************/
    if (VioPS->rgfShieldStates & fCursorShowing)
    {
        /**************************************************************/
        /* Cursor is on so determine whether it lies in the path of   */
        /* this string..                                              */
        /* To do this we convert the start position to an absolute    */
        /* position in the LVB and we do the same for the cursor      */
        /* position.  With these two values and the string length we  */
        /* can determine if there is any intersection.  Note that     */
        /* this will produce intersections when the cursor is not in  */
        /* the AVIO window but it is better to do it here than do it  */
        /* on each individual rectangle.                              */
        /* The absolute position is calculated relative to the top    */
        /* left of the screen (ie flip the origin of the AVIO window. */
        /* This makes the subsequent check easier since the end       */
        /* position of the string is start position + string length   */
        /**************************************************************/
        StringStartIndex = (SHORT)(VioPS->BufferRowCount
                                            - ArgCharString->StartRow) *
                            (SHORT)VioPS->BufferColumnCount +
                             (SHORT)ArgCharString->StartColumn;

        CursorPosIndex = (SHORT)(VioPS->BufferRowCount
                                                   - VioPS->CursorRow) *
                          (SHORT)VioPS->BufferColumnCount +
                           (SHORT)VioPS->CursorCol;

        /**************************************************************/
        /* Test the cursor position against the string.  See comment  */
        /* above                                                      */
        /**************************************************************/
        if ((CursorPosIndex >= StringStartIndex) &&
            (CursorPosIndex <
                (StringStartIndex + (SHORT)ArgCharString->StringLength)))
        {
            /**********************************************************/
            /* There is an intersection in terms of the LVB - whilst  */
            /* this does not necessarily mean there will be an        */
            /* intersection on screen we will exclude the cursor.     */
            /* 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
        {
            /**********************************************************/
            /* No intersection - set the flag                         */
            /**********************************************************/
            TextCursorExcluded = FALSE;
        }
    }
    else
    {
        /**************************************************************/
        /* Cursor is off - so it is not excluded!                     */
        /**************************************************************/
        TextCursorExcluded = FALSE;
    }

    /******************************************************************/
    /* Now we have NoOfRects rectangles (in LVB coordinates) which    */
    /* we want to draw to the screen.  For each one of these          */
    /* rectangles we go through the same sequence of operations as    */
    /* we do in CharRect to clip them to the LVB window and then to   */
    /* the screen and then draw them into all the clip rectangles     */
    /******************************************************************/
    for ( i = 0; i < NoOfRects; i++ )
    {
        ClippedRect[0].Col =
               max( 0, CharRect[i].Col0 - VioPS->WindowOriginColumn);
        ClippedRect[0].Row =
               max( 0, CharRect[i].Row0 - VioPS->WindowOriginRow);
        ClippedRect[1].Col =
               min( CharRect[i].Col1 - VioPS->WindowOriginColumn,
                                               (SHORT)VioPS->WindowWidth);
        ClippedRect[1].Row =
               min( CharRect[i].Row1 - VioPS->WindowOriginRow,
                                              (SHORT)VioPS->WindowHeight);

        if (ClippedRect[0].Col <= ClippedRect[1].Col &&
            ClippedRect[0].Row <= ClippedRect[1].Row )
        {
            /**********************************************************/
            /* There is an intersection between the rectangle and the */
            /* LVB window.                                            */
            /**********************************************************/

            /**********************************************************/
            /* Calculate the rectangle that will be updated on the    */
            /* screen.                                                */
            /**********************************************************/
            ScreenRect[0].Col = ScreenLVBOrigin.Col +
                                                     ClippedRect[0].Col;
            ScreenRect[1].Col = ScreenRect[0].Col +
                              (ClippedRect[1].Col - ClippedRect[0].Col);

            ScreenRect[0].Row = ScreenLVBOrigin.Row -
                                                     ClippedRect[1].Row;
            ScreenRect[1].Row = ScreenRect[0].Row +
                              (ClippedRect[1].Row - ClippedRect[0].Row);

            /**********************************************************/
            /* Check for rectangle off left or top of screen          */
            /**********************************************************/
            if (ScreenRect[0].Col < 0)
            {
                ClippedRect[0].Col -= ScreenRect[0].Col;
                ScreenRect[0].Col = 0;
            }

            if (ScreenRect[0].Row < 0)
            {
                ClippedRect[1].Row += ScreenRect[0].Row;
                ScreenRect[0].Row = 0;
            }

            if ( (ClippedRect[0].Col > ClippedRect[1].Col) ||
                 (ClippedRect[0].Row > ClippedRect[1].Row) )
            {
                /******************************************************/
                /* This rectangle is off the screen - skip to the     */
                /* next one.                                          */
                /******************************************************/
                continue;
            }

            /**********************************************************/
            /* Set up the position and size of the rectangle in       */
            /* screen AVIO coordinates                                */
            /**********************************************************/
            AIxfer.bColumn = (BYTE)ScreenRect[0].Col;
            AIxfer.bRow    = (BYTE)ScreenRect[0].Row;
            AIxfer.bAcross =
                     (BYTE)ClippedRect[1].Col - ClippedRect[0].Col + 1;
            AIxfer.bDown   =
                     (BYTE)ClippedRect[1].Row - ClippedRect[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;

            AIxfer.pLVBChars = (PBYTE)(VioPS->pLVB) + Offset;

            /**************************************************************/
            /* 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;

            /**********************************************************/
            /* if the cursor is software then disable further drawing */
            /* and maybe exclude it                                   */
            /**********************************************************/
            if ( cursor_data.cursor_status & CURSOR_SOFTWARE )
            {
                Bounds[0].X = ScreenRect[0].Col *
                              (SHORT)VioPS->CellImageWidth;
                Bounds[1].X = Bounds[0].X +
                             AIxfer.bAcross * (SHORT)VioPS->CellImageWidth;
                Bounds[0].Y = ScreenRect[0].Row *
                              (SHORT)VioPS->CellImageHeight;
                Bounds[1].Y = Bounds[0].Y +
                              AIxfer.bDown * (SHORT)VioPS->CellImageHeight;
                eddm_ExcludeCursor(Bounds, COORD_AI);
            }

            /**********************************************************/
            /* 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));

            /**********************************************************/
            /* mark the cursor for redrawing now we're finished with  */
            /* the h/w                                                */
            /**********************************************************/
            reenable_cursor();
        }
    }

    /******************************************************************/
    /* If the text cursor was removed, put it back                    */
    /******************************************************************/
    if (TextCursorExcluded)
    {
        InvertAVIOCursor(VioPS);
    }

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

    return(OK_ZERO);

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

    return(ERROR_NEG);
}
