/*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          = EDDVSREC                                       */
/*                                                                    */
/*   Description     = Display Device Driver Function:                */
/*                     ScrollRect.                                    */
/*                                                                    */
/*   Function        = ScrollRect scrolls a character rectangle in    */
/*                     the LVB                                        */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
#define INCL_DDICOMFLAGS
#define INCL_DDIMISC
#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>
#include <hwaccess.h>

#ifdef _8514
#include <8514.h>
#endif

extern DDTType             DDT;
extern AVIOPB              AIxfer;
#ifndef   _8514
extern MMReg               ShadowXGARegs;
#else
extern MM8514Reg           Shadow8514Regs;
#endif
extern RGNRECT             GetClipsControl;
extern ClipRectangle       GetClipsBounds;

extern CURSORDATA          cursor_data;

/**********************************************************************/
/*                                                                    */
/* ScrollRect scrolls a rectangle across the screen.  The function is */
/* given the rectangle to be scrolled, the number of rows and         */
/* columns to be scrolled and a character cell to be used to fill the */
/* tail of the scroll rectangle.                                      */
/*                                                                    */
/* The scrolled rectangle is drawn using PMSCROLL.  This does a       */
/* BitBlt Screen -> Screen.                                           */
/*                                                                    */
/* The tail is that part of the window which was covered by the       */
/* rectangle in its old position which is not covered by the          */
/* rectangle in its new position.                                     */
/*                                                                    */
/* The tail should be filled with the fill cell unless the pointer    */
/* to the fill cell is NULL, in which case the tail region should     */
/* be redrawn directly from the LVB.                                  */
/*                                                                    */
/**********************************************************************/

DDIENTRY eddv_ScrollRect (HDC                hdc,
                          PVIOPS             VioPS,
                          LPScrollRectRef    ArgScrollRect,
                          PDC                pdcArg,
                          ULONG              FunN)

{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    AvioPoint          ScreenLVBOrigin; /* LVB origin on screen       */
    AvioRect           ScreenRect;      /* Screen rectangle           */
    AvioRect           LVBClipRect;     /* LVB clip rectangle         */
    AvioRect           ClippedRect;     /* Clipped LVB rectangle      */
    AvioRect           LVBBounds;       /* LVB bounds rectangle       */
    DevRect            Bounds;          /* Bounds rectangle           */
    lpClipRectangle    ClipRect;        /* Pointer to clip rects      */
    LONG               VertShift;       /* Absolute scroll distance   */
    LONG               HorzShift;       /* Absolute scroll distance   */
    BOOL               PartialTopCells;   /* Flag for scroll calcns   */
    BOOL               PartialBottomCells;/* Flag for scroll calcns   */
    BOOL               PartialLeftCells;  /* Flag for scroll calcns   */
    BOOL               PartialRightCells; /* Flag for scroll calcns   */
    USHORT             NoOfRows;        /* Temporary variables used   */
    USHORT             NoOfCells;       /*   in calc of address       */
    USHORT             Offset;          /*   passed to A.I. call      */
    USHORT             i;               /* loop control variable      */
    BYTE               Saveddown;       /* used in diagonal scroll    */
    BYTE               Savedacross;     /* used in diagonal scroll    */
    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 everything    */
    /* will be set up                                                 */
    /******************************************************************/
    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.                                          */
        /**************************************************************/

        /**************************************************************/
        /* Calculate the grid x offset.                               */
        /**************************************************************/
        pAvioInfo->sXoffset = pdc->DCIOrigin.X %
                                             (SHORT)VioPS->CellImageWidth;

        if (pAvioInfo->sXoffset > 0)
        {
            pAvioInfo->sXoffset -= VioPS->CellImageWidth;
        }

        /**************************************************************/
        /* Calculate the grid y offset.                               */
        /**************************************************************/
        pAvioInfo->sYoffset = ((DDT.ScreenHeight-1 -
                        (pdc->DCIOrigin.Y +
                      (signed CHAR)VioPS->PartialCellAdjust) + 1) %
                                           (SHORT)VioPS->CellImageHeight);
        if (pAvioInfo->sYoffset > 0)
        {
            pAvioInfo->sYoffset -= VioPS->CellImageHeight;
        }

    }

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

    /******************************************************************/
    /* Calculate the rectangle bounds in LVB coordinates relative to  */
    /* the LVB window.                                                */
    /******************************************************************/
    LVBBounds[0].Col = (SHORT)( (LONG)ArgScrollRect->StartColumn -
                                      (LONG)VioPS->WindowOriginColumn );

    LVBBounds[1].Col = (SHORT)( LVBBounds[0].Col +
                                    (SHORT)ArgScrollRect->RectWidth - 1 );

    LVBBounds[0].Row = (SHORT)( (LONG)ArgScrollRect->StartRow -
                                         (LONG)VioPS->WindowOriginRow );

    LVBBounds[1].Row = (SHORT)( LVBBounds[0].Row +
                                    (SHORT)ArgScrollRect->RectDepth - 1 );

    /******************************************************************/
    /* If bounds required. We MUST do this before we check the DRAW   */
    /* bit to see if any drawing is to be done.                       */
    /******************************************************************/
    if ( FunNTest(COM_BOUND | COM_ALT_BOUND) ||
#ifdef DCAF                                                               //DCAF
         DCAFBoundsRequired(FunN) ||                                      //DCAF
#endif                                                                    //DCAF
         (cursor_data.cursor_status & CURSOR_SOFTWARE) )
    {
        /**************************************************************/
        /* Convert to device coordinates                              */
        /**************************************************************/
        Bounds[0].X = LVBBounds[0].Col * VioPS->CellImageWidth;
        Bounds[0].Y = LVBBounds[0].Row * VioPS->CellImageHeight +
                                  (signed CHAR)VioPS->PartialCellAdjust;
        Bounds[1].X = LVBBounds[1].Col * VioPS->CellImageWidth +
                                            (VioPS->CellImageWidth - 1);
        Bounds[1].Y = LVBBounds[1].Row * VioPS->CellImageHeight +
                         (VioPS->CellImageHeight - 1) +
                                  (signed CHAR)VioPS->PartialCellAdjust;

        /**************************************************************/
        /* update the global bounds                                   */
        /**************************************************************/
        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 SCROLLRECT_OK_EXIT;
    }

    /******************************************************************/
    /* Test whether the rectangle is completely outside the LVB       */
    /* window. If it is then we can just return immediately.          */
    /******************************************************************/
    if ( (LVBBounds[0].Col >= (SHORT)VioPS->WindowWidth) ||
         (LVBBounds[0].Row >= (SHORT)VioPS->WindowHeight) ||
         (LVBBounds[1].Col < 0) ||
         (LVBBounds[1].Row < 0) )
    {
        goto SCROLLRECT_OK_EXIT;
    }

    /******************************************************************/
    /* Clip the LVB bounds to the LVB window.                         */
    /* The X coords should be in the range [0..VioPS->WindowWidth-1]  */
    /* The Y coords should be in the range [0..VioPS->WindowHeight-1] */
    /******************************************************************/
    LVBBounds[0].Col = max(0, LVBBounds[0].Col);
    LVBBounds[1].Col = min(LVBBounds[1].Col,(SHORT)VioPS->WindowWidth-1);
    LVBBounds[0].Row = max(0, LVBBounds[0].Row);
    LVBBounds[1].Row = min(LVBBounds[1].Row,(SHORT)VioPS->WindowHeight-1);

    /******************************************************************/
    /* We need to exclude the text cursor if it is currently visible  */
    /******************************************************************/
    if (VioPS->rgfShieldStates & fCursorShowing)
    {
        /**************************************************************/
        /* For simplicity we will allways exclude the cursor if it is */
        /* visible.  It has to be physically removed and the flag     */
        /* altered because the scroll could move it to another        */
        /* position in the window                                     */
        /**************************************************************/
        InvertAVIOCursor(VioPS);
        VioPS->rgfShieldStates &= ~fCursorShowing;
    }

    /******************************************************************/
    /* 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 LVB bounds into screen AVIO coordinates, the       */
    /* origin of which is the top left corner of the screen.          */
    /******************************************************************/
    ScreenRect[0].Col = ScreenLVBOrigin.Col + LVBBounds[0].Col;
    ScreenRect[1].Col = ScreenLVBOrigin.Col + LVBBounds[1].Col;
    ScreenRect[0].Row = ScreenLVBOrigin.Row - LVBBounds[1].Row;
    ScreenRect[1].Row = ScreenLVBOrigin.Row - LVBBounds[0].Row;

    /******************************************************************/
    /* Calculate the absolute shift values                            */
    /******************************************************************/
//  HorzShift = abs((SHORT)ArgScrollRect->HorzMovement);
//  VertShift = abs((SHORT)ArgScrollRect->VertMovement);
    HorzShift = abs(ArgScrollRect->HorzMovement); /*RM*/
    VertShift = abs(ArgScrollRect->VertMovement); /*RM*/

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

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

    /******************************************************************/
    /* Set up a pointer to the clip rectangles.                       */
    /******************************************************************/
    ClipRect = pdc->DCIClipRects;

    /******************************************************************/
    /* before we start to use the hardware, if the cursor is          */
    /* software then the cursor may need to be excluded and further   */
    /* drawing on the mouse interrupt thread must be disabled.        */
    /* Done after text cursor inversion because that subroutine       */
    /* already disables (and reenables) the cursor around it          */
    /******************************************************************/
    if ( cursor_data.cursor_status & CURSOR_SOFTWARE )
    {
#ifdef TOO_BIG
        /**************************************************************/
        /* these bounds are 'correct' but are not restricted to the   */
        /* window size - so the cursor is removed even if it is       */
        /* outside the window (but within the LVB area)               */
        /**************************************************************/
        eddm_ExcludeCursor(Bounds, COORD_DEVICE_WORD);
#else /* TOO_BIG */
        /**************************************************************/
        /* these bounds restrict the cursor exclusion to the amount   */
        /* of the LVB within the window                               */
        /* since these bounds are more complex they may be       in   */
        /* some particular situation I've not yet come across, in     */
        /* which case either fix them, or return to the 'correct'     */
        /* but 'too big' bounds used in the other half of this ifdef  */
        /**************************************************************/
        Bounds[0].X = ScreenRect[0].Col * VioPS->CellImageWidth +
                        pAvioInfo->sXoffset;
        Bounds[1].X = (ScreenRect[1].Col+1) * VioPS->CellImageWidth -
                        1 + pAvioInfo->sXoffset;
        Bounds[0].Y = ScreenRect[0].Row * VioPS->CellImageHeight +
                        pAvioInfo->sYoffset;
        Bounds[1].Y = (ScreenRect[1].Row+1) * VioPS->CellImageHeight -
                        1 + pAvioInfo->sYoffset;
        /**************************************************************/
        /* keep these bounds on the left of the screen                */
        /**************************************************************/
        if (Bounds[0].X < 0)
        {
            Bounds[0].X = 0;
        }
        if (Bounds[1].X < 0)
        {
            Bounds[1].X = 0;
        }
        eddm_ExcludeCursor(Bounds, COORD_AI);
#endif /* TOO_BIG */
    }

    /******************************************************************/
    /* Set foreground mix as overpaint.                               */
    /******************************************************************/
    #ifndef   _8514
    ShadowXGARegs.FgMix       = HWMIX_SOURCE;
    ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;
    #else
    Shadow8514Regs.Function_1.Mix  = FUNC_S;
    Shadow8514Regs.Function_1.SrcSelect = FUNC_2OP_COL1;
    Shadow8514Regs.Color_Comp = COLCOMP_ALWAYS;
    #endif
    TransferShadowRegisters(TSR_COLOUR_MIX);

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

        }

        /**************************************************************/
        /* Now perform the ScrollRect for each cached clip rectangle  */
        /**************************************************************/
        for (i = 0; i < pdc->DCIClipNum; i++)
        {
            /**************************************************************/
            /* Calculate which LVB cells the clip rectangle covers.       */
            /**************************************************************/
            LVBClipRect[0].Col =
                          (pdc->DCIClipRects[i].X0 - pAvioInfo->sXoffset) /
                                                      VioPS->CellImageWidth;

            LVBClipRect[0].Row =
                          (pdc->DCIClipRects[i].Y0 - pAvioInfo->sYoffset) /
                                                     VioPS->CellImageHeight;

            LVBClipRect[1].Col =
                          (pdc->DCIClipRects[i].X1 - pAvioInfo->sXoffset) /
                                                      VioPS->CellImageWidth;

            LVBClipRect[1].Row =
                          (pdc->DCIClipRects[i].Y1 - pAvioInfo->sYoffset) /
                                                     VioPS->CellImageHeight;

            /**************************************************************/
            /* Calcuate whether the cells at the top, bottom, left        */
            /* and right of the clip region are whollly or partially      */
            /* visible. This affects the size of the areas that we scroll */
            /* and repaint                                                */
            /**************************************************************/
            PartialTopCells =
                  ( (pdc->DCIClipRects[i].Y0 - pAvioInfo->sYoffset -
                        LVBClipRect[0].Row * VioPS->CellImageHeight) != 0 );

            PartialBottomCells =
                  ( (pdc->DCIClipRects[i].Y1 - pAvioInfo->sYoffset -
                        LVBClipRect[1].Row * VioPS->CellImageHeight) !=
                                                 VioPS->CellImageHeight-1 );

            PartialLeftCells =
                  ( (pdc->DCIClipRects[i].X0 - pAvioInfo->sXoffset -
                        LVBClipRect[0].Col * VioPS->CellImageWidth) != 0 );

            PartialRightCells =
                  ( (pdc->DCIClipRects[i].X1 - pAvioInfo->sXoffset -
                        LVBClipRect[1].Col * VioPS->CellImageWidth) !=
                                                 VioPS->CellImageWidth-1 );

            /**************************************************************/
            /* Now intersect the clip rectangle with the screen           */
            /* rectangle that needs to be updated.                        */
            /**************************************************************/
            ClippedRect[0].Col = max(LVBClipRect[0].Col, ScreenRect[0].Col);
            ClippedRect[1].Col = min(LVBClipRect[1].Col, ScreenRect[1].Col);
            ClippedRect[0].Row = max(LVBClipRect[0].Row, ScreenRect[0].Row);
            ClippedRect[1].Row = min(LVBClipRect[1].Row, ScreenRect[1].Row);

            /**************************************************************/
            /* If the clip rectangle does not intersect with the screen   */
            /* rectangle then we can skip to the end of the loop          */
            /* immediately.                                               */
            /**************************************************************/
            if ( (ClippedRect[0].Col > ClippedRect[1].Col) ||
                 (ClippedRect[0].Row > ClippedRect[1].Row) )
            {
                /**********************************************************/
                /* There is no intersection. Skip to the next clip        */
                /* rectangle.                                             */
                /**********************************************************/
                continue;
            }

            /**********************************************************/
            /* We are going to be drawing into a single clip          */
            /**********************************************************/
            AIxfer.cClipRects = 1;

            /**************************************************************/
            /* Check whether the scroll distance is bigger than the size  */
            /* of the rectangle to be updated. If it is bigger, we just   */
            /* have to repaint the whole rectangle from the LVB.          */
            /**************************************************************/
//          if (((SHORT)HorzShift <= ClippedRect[1].Col - ClippedRect[0].Col -
//                          (SHORT)(PartialLeftCells | PartialRightCells) ) &&
//              ((SHORT)VertShift <= ClippedRect[1].Row - ClippedRect[0].Row -
//                           (SHORT)(PartialTopCells | PartialBottomCells) ) )
            if ((HorzShift <= (LONG)(ClippedRect[1].Col - ClippedRect[0].Col -
                            (SHORT)(PartialLeftCells | PartialRightCells)) ) &&
                (VertShift <= (LONG)(ClippedRect[1].Row - ClippedRect[0].Row -
                             (SHORT)(PartialTopCells | PartialBottomCells)) ) )
            {
                /**********************************************************/
                /* Set up parameter block for PMSCROLL call.              */
                /**********************************************************/

                /**********************************************************/
                /* Set up the width and height of the rectangle that we   */
                /* are going to move.                                     */
                /**********************************************************/
                AIxfer.bAcross = (BYTE)
                  (ClippedRect[1].Col - ClippedRect[0].Col + 1 - HorzShift);

                AIxfer.bDown = (BYTE)
                  (ClippedRect[1].Row - ClippedRect[0].Row + 1 - VertShift);

                /**********************************************************/
                /* Calculate the row number of the top left hand corner   */
                /* of the source rectangle, relative to the top-left      */
                /* corner of the screen.                                  */
                /**********************************************************/
                if (ArgScrollRect->VertMovement == 0)
                {
                    /******************************************************/
                    /* There is no vertical movement.                     */
                    /******************************************************/
                    AIxfer.bSourceRow = (BYTE)ClippedRect[0].Row;
                }
                else if (ArgScrollRect->VertMovement < 0)
                {
                    /******************************************************/
                    /* The scroll is upwards.                             */
                    /******************************************************/
                    AIxfer.bSourceRow = (BYTE)(ClippedRect[0].Row + VertShift);

                    /******************************************************/
                    /* If the bottom cells in the rectangle are not whole */
                    /* then there is no point copying them - we will      */
                    /* redraw them from the LVB.                          */
                    /******************************************************/
                    AIxfer.bDown -= (BYTE)PartialBottomCells;
                }
                else
                {
                    /*******************************************************/
                    /* The scroll is downwards.                            */
                    /*******************************************************/
                    AIxfer.bSourceRow  = (BYTE)ClippedRect[0].Row +
                                                       (BYTE)PartialTopCells;

                    /*******************************************************/
                    /* If the top cells in the rectangle are not whole     */
                    /* then there is no point copying them - we will       */
                    /* redraw them from the LVB. Adjust the source row     */
                    /* and rectangle height accordingly.                   */
                    /*******************************************************/
                    AIxfer.bDown -= (BYTE)PartialTopCells;
                }

                /**********************************************************/
                /* Calculate the column number of the top left hand       */
                /* corner of the source rectangle.                        */
                /**********************************************************/
                if (ArgScrollRect->HorzMovement == 0)
                {
                    /******************************************************/
                    /* There is no horizontal movement.                   */
                    /******************************************************/
                    AIxfer.bSourceColumn = (BYTE)ClippedRect[0].Col;
                }
                else if (ArgScrollRect->HorzMovement < 0)
                {
                    /******************************************************/
                    /* The scroll is leftwards.                           */
                    /******************************************************/
                    AIxfer.bSourceColumn = (BYTE)(ClippedRect[0].Col + HorzShift);

                    /******************************************************/
                    /* If the right-hand cells in the rectangle are not   */
                    /* whole then there is no point copying them - we will*/
                    /* redraw them from the LVB.                          */
                    /******************************************************/
                    AIxfer.bAcross -= (BYTE)PartialRightCells;
                }
                else
                {
                    /******************************************************/
                    /* The scroll is rightwards.                          */
                    /******************************************************/
                    AIxfer.bSourceColumn = (BYTE)ClippedRect[0].Col +
                                                     (BYTE)PartialLeftCells;

                    /******************************************************/
                    /* If the left-hand cells in the rectangle are not    */
                    /* whole then there is no point copying them - we will*/
                    /* redraw them from the LVB. Adjust the source column */
                    /* and width accordingly.                             */
                    /******************************************************/
                    AIxfer.bAcross -= (BYTE)PartialLeftCells;
                }

                /**********************************************************/
                /* Calculate the row number of the top left hand corner   */
                /* of the destination rectangle.                          */
                /**********************************************************/
                AIxfer.bDestRow  = (BYTE)((SHORT)AIxfer.bSourceRow +
                                        (SHORT)ArgScrollRect->VertMovement );

                /**********************************************************/
                /* Calculate the column number of the top left hand       */
                /* corner of the destination rectangle.                   */
                /**********************************************************/
                AIxfer.bDestColumn = (BYTE)((SHORT)AIxfer.bSourceColumn +
                                       (SHORT)ArgScrollRect->HorzMovement );


                /******************************************************/
                /* AVIOScroll takes its clip rectangle from           */
                /* pCurrentClipRect                                   */
                /******************************************************/
                AIxfer.pCurrentClipRect = &(pdc->DCIClipRects[i]);

                /**********************************************************/
                /* Perform the scroll.                                    */
                /**********************************************************/
                AVIOScroll( &(pdc->DCIAvioInfo));

                /**********************************************************/
                /* Now we need to redraw the uncovered area(s) from the   */
                /* LVB. First check for a vertical scroll.                */
                /**********************************************************/
                if (ArgScrollRect->VertMovement)
                {
                    /**************************************************/
                    /* these values need saving in case we are doing  */
                    /* a diagonal scroll                              */
                    /**************************************************/
                    Savedacross = AIxfer.bAcross;
                    Saveddown   = AIxfer.bDown;

                    /******************************************************/
                    /* We can use the values set up in the PMSCROLL       */
                    /* parameter block.                                   */
                    /******************************************************/
                    if (ArgScrollRect->HorzMovement < 0)
                    {
                        /**********************************************/
                        /* diagonal scroll to the left                */
                        /**********************************************/
                        AIxfer.bColumn = AIxfer.bDestColumn;
                    }
                    else
                    {
                        /**********************************************/
                        /* right diagonal or just vertical scroll     */
                        /**********************************************/
                        AIxfer.bColumn = AIxfer.bSourceColumn;
                    }

                    if (ArgScrollRect->VertMovement < 0)
                    {
                        /**************************************************/
                        /* The scroll is upwards.                         */
                        /**************************************************/
                        AIxfer.bRow = AIxfer.bDestRow + AIxfer.bDown;
                        AIxfer.bDown=(BYTE)(VertShift + PartialBottomCells);
                    }
                    else
                    {
                        /**************************************************/
                        /* The scroll is downwards.                       */
                        /**************************************************/
                        AIxfer.bRow = AIxfer.bSourceRow;
                        AIxfer.bDown = (BYTE)(VertShift + PartialTopCells);
                    }
                    /**************************************************/
                    /* The width of the block is the same as the one  */
                    /* that was scrolled so plus any amount of        */
                    /* horizontal scroll that may have occurred       */
                    /**************************************************/
                    AIxfer.bAcross += (BYTE)HorzShift;

                    /******************************************************/
                    /* Calculate the LVB coordinates of the cell that     */
                    /* will be drawn at the top left corner of the block. */
                    /******************************************************/
                    NoOfRows  = (VioPS->BufferRowCount - 1) -
                                 ((ScreenLVBOrigin.Row - AIxfer.bRow) +
                                                    VioPS->WindowOriginRow);

                    NoOfCells = (NoOfRows * VioPS->BufferColumnCount) +
                                 ((AIxfer.bColumn - ScreenLVBOrigin.Col) +
                                                 VioPS->WindowOriginColumn);

                    /******************************************************/
                    /* Calculate the offset of this cell from the start   */
                    /* of the LVB                                         */
                    /******************************************************/
                    Offset = NoOfCells * VioPS->CellByteSize;

                    /******************************************************/
                    /* Calculate the address of the first cell            */
                    /******************************************************/
                    AIxfer.pLVBChars = (PBYTE)(VioPS->pLVB) + Offset;

                    /******************************************************/
                    /* Both routines take their clip rectangle from       */
                    /* pFirstClipRect                                     */
                    /******************************************************/
                    AIxfer.pFirstClipRect = &(pdc->DCIClipRects[i]);

                    /******************************************************/
                    /* Draw the characters.                               */
                    /******************************************************/
                    if (VioPS->CellByteSize == 2)
                    {
                        CGAText();
                    }
                    else
                    {
                        MFIText();
                    }
                    /**************************************************/
                    /* adjust this value back to the value it had     */
                    /* when we scrolled since it is used in the       */
                    /* code which handles horizontal scrolls          */
                    /**************************************************/
                    AIxfer.bAcross = Savedacross;
                    AIxfer.bDown   = Saveddown;
                }

                /******************************************************/
                /* Now check for a horizontal scroll, and redraw      */
                /* any uncovered area.                                */
                /*                                                    */
                /* Note this code assumes that .across and .down      */
                /* are as set for the scroll                          */
                /******************************************************/
                if (ArgScrollRect->HorzMovement)
                {
                    /**************************************************/
                    /* We can use the values set up in the ASCROLL    */
                    /* parameter block.                               */
                    /**************************************************/
                    AIxfer.bRow = AIxfer.bDestRow;

                    if (ArgScrollRect->HorzMovement > 0)
                    {
                        /**********************************************/
                        /* The scroll is rightwards.                  */
                        /**********************************************/
                        AIxfer.bColumn = AIxfer.bSourceColumn;
                        AIxfer.bAcross = (BYTE)(HorzShift +
                                               PartialLeftCells);
                    }
                    else
                    {
                        /**************************************************/
                        /* The scroll is leftwards.                       */
                        /**************************************************/
                        AIxfer.bColumn = AIxfer.bDestColumn + AIxfer.bAcross;
                        AIxfer.bAcross =
                                     (BYTE)(HorzShift + PartialRightCells);
                    }
                    /**************************************************/
                    /* The height of the block is the same as the one */
                    /* that was scrolled so .down is already set up   */
                    /* (actually only true because of our fiddle above*/
                    /**************************************************/

                    /******************************************************/
                    /* Calculate the LVB coordinates of the cell that     */
                    /* will be drawn at the top left corner of the block. */
                    /******************************************************/
                    NoOfRows  = (VioPS->BufferRowCount - 1) -
                                 ((ScreenLVBOrigin.Row - AIxfer.bRow) +
                                                    VioPS->WindowOriginRow);
                    NoOfCells = (NoOfRows * VioPS->BufferColumnCount) +
                                 ((AIxfer.bColumn - ScreenLVBOrigin.Col) +
                                                 VioPS->WindowOriginColumn);

                    /******************************************************/
                    /* Calculate the offset of this cell from the start   */
                    /* of the LVB                                         */
                    /******************************************************/
                    Offset = NoOfCells * VioPS->CellByteSize;

                    /******************************************************/
                    /* Calculate the address of the first cell            */
                    /******************************************************/
                    AIxfer.pLVBChars = (PBYTE)(VioPS->pLVB) + Offset;

                    /******************************************************/
                    /* Both routines take their clip rectangle from       */
                    /* pFirstClipRect.                                    */
                    /******************************************************/
                    AIxfer.pFirstClipRect = &(pdc->DCIClipRects[i]);

                    /******************************************************/
                    /* Draw the characters.                               */
                    /******************************************************/
                    if (VioPS->CellByteSize == 2)
                    {
                        CGAText();
                    }
                    else
                    {
                        MFIText();
                    }
                }
            }
            else
            {
                /**********************************************************/
                /* The scroll distance is bigger than the size of the     */
                /* rectangle to be updated. Just repaint the whole block  */
                /* from the LVB.                                          */
                /**********************************************************/
                AIxfer.bRow    = (BYTE) ClippedRect[0].Row;
                AIxfer.bColumn = (BYTE) ClippedRect[0].Col;
                AIxfer.bAcross = (BYTE) (ClippedRect[1].Col -
                                                    ClippedRect[0].Col + 1);
                AIxfer.bDown   = (BYTE) (ClippedRect[1].Row -
                                                    ClippedRect[0].Row + 1);

                /**********************************************************/
                /* Calculate the LVB coordinates of the cell that         */
                /* will be drawn at the top left corner of the block.     */
                /**********************************************************/
                NoOfRows  = ((SHORT)VioPS->BufferRowCount - 1) -
                       ((ScreenLVBOrigin.Row - AIxfer.bRow) +
                                               (SHORT)VioPS->WindowOriginRow);

                NoOfCells = (NoOfRows * VioPS->BufferColumnCount) +
                       ((AIxfer.bColumn - ScreenLVBOrigin.Col) +
                                            (SHORT)VioPS->WindowOriginColumn);

                /**********************************************************/
                /* Calculate the offset of this cell from the start       */
                /* of the LVB                                             */
                /**********************************************************/
                Offset = NoOfCells * VioPS->CellByteSize;

                /**********************************************************/
                /* Calculate the address of the first cell                */
                /**********************************************************/
                AIxfer.pLVBChars = (PBYTE)(VioPS->pLVB) + Offset;

                /**********************************************************/
                /* Both routines take their clip rectangle from           */
                /* pFirstClipRect                                         */
                /**********************************************************/
                AIxfer.pFirstClipRect = &(pdc->DCIClipRects[i]);

                /**********************************************************/
                /* Draw the characters.                                   */
                /**********************************************************/
                if (VioPS->CellByteSize == 2)
                {
                    CGAText();
                }
                else
                {
                    MFIText();
                }
            }
        }

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


    /******************************************************************/
    /* reenable cursor updates                                        */
    /******************************************************************/
    reenable_cursor();

    /******************************************************************/
    /* If bounds required                                             */
    /******************************************************************/
    if (FunNTest(COM_BOUND | COM_ALT_BOUND))
    {
        /**************************************************************/
        /* update the global bounds                                   */
        /**************************************************************/
        eddg_AddBounds ((pDevRect)Bounds,
                        FunN,
                        COORD_DEVICE_WORD);
    }

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

    return(OK_ZERO);
}
