#include "precomp.h"


//
// OE.C
// Order Encoder, display driver side
//
// Copyright(c) Microsoft 1997-
//

//
// Number of entries in the font alias table.
//
#define NUM_ALIAS_FONTS 3

//
// Define entries in the Font Alias table.  This table is used to convert
// non-existant fonts (used by certain widely used applications) into
// something we can use as a local font.
//
// The font names that we alias are:
//
// "Helv"
// This is used by Excel. It is mapped directly onto "MS Sans Serif".
//
// "MS Dialog"
// This is used by Word. It is the same as an 8pt bold MS Sans Serif.
// We actually map it to a "MS Sans Serif" font that is one pel narrower
// than the metrics specify (because all matching is done on non-bold
// fonts) - hence the 1 value in the charWidthAdjustment field.
//
// "MS Dialog Light"
// Added as part of the Win95 performance enhancements...Presumably for
// MS-Word...
//
//
FONT_ALIAS_TABLE fontAliasTable[NUM_ALIAS_FONTS] =
{
    { "Helv",            "MS Sans Serif", 0 },
    { "MS Dialog",       "MS Sans Serif", 1 },
    { "MS Dialog Light", "MS Sans Serif", 0 }
};




//
// FUNCTION: OE_SendAsOrder see oe.h
//
BOOL  OE_SendAsOrder(DWORD order)
{
    BOOL  rc = FALSE;

    DebugEntry(OE_SendAsOrder);

    //
    // Only check the order if we are allowed to send orders in the first
    // place!
    //
    if (g_oeSendOrders)
    {
        TRACE_OUT(("Orders enabled"));

        //
        // We are sending some orders, so check individual flags.
        //
        rc = (BOOL)g_oeOrderSupported[HIWORD(order)];
        TRACE_OUT(("Send order %lx HIWORD %hu", order, HIWORD(order)));
    }

    DebugExitDWORD(OE_SendAsOrder, rc);
    return(rc);
}


//
// OE_RectIntersectsSDA - see oe.h
//
BOOL  OE_RectIntersectsSDA(LPRECT pRect)
{
    RECT  rectVD;
    BOOL  fIntersection = FALSE;
    UINT  i;

    DebugEntry(OE_RectIntersectsSDA);

    //
    // Copy the supplied rectangle, converting to inclusive Virtual
    // Desktop coords.
    //
    rectVD.left   = pRect->left;
    rectVD.top    = pRect->top;
    rectVD.right  = pRect->right - 1;
    rectVD.bottom = pRect->bottom - 1;

    //
    // Loop through each of the bounding rectangles checking for
    // an intersection with the supplied rectangle.
    //
    for (i = 0; i <= BA_NUM_RECTS; i++)
    {
        if ( (g_baBounds[i].InUse) &&
             (g_baBounds[i].Coord.left <= rectVD.right) &&
             (g_baBounds[i].Coord.top <= rectVD.bottom) &&
             (g_baBounds[i].Coord.right >= rectVD.left) &&
             (g_baBounds[i].Coord.bottom >= rectVD.top) )
        {
            TRACE_OUT(("Rect(%d,%d)(%d,%d) intersects SDA(%d,%d)(%d,%d)",
                          rectVD.left, rectVD.top,
                          rectVD.right, rectVD.bottom,
                          g_baBounds[i].Coord.left, g_baBounds[i].Coord.top,
                          g_baBounds[i].Coord.right, g_baBounds[i].Coord.bottom));
            fIntersection = TRUE;
            break;
        }
    }

    DebugExitDWORD(OE_RectIntersectsSDA, fIntersection);
    return(fIntersection);
}


//
// DrvBitBlt - see NT DDK documentation.
//
BOOL DrvBitBlt( SURFOBJ  *psoDst,
                      SURFOBJ  *psoSrc,
                      SURFOBJ  *psoMask,
                      CLIPOBJ  *pco,
                      XLATEOBJ *pxlo,
                      RECTL    *prclDst,
                      POINTL   *pptlSrc,
                      POINTL   *pptlMask,
                      BRUSHOBJ *pbo,
                      POINTL   *pptlBrush,
                      ROP4      rop4 )
{
    LPOSI_PDEV               ppdev = (LPOSI_PDEV)psoDst->dhpdev;
    BOOL                    rc = TRUE;
    UINT                orderType = 0;
    BYTE                 rop3;
    LPINT_ORDER              pOrder = NULL;
    LPDSTBLT_ORDER          pDstBlt;
    LPSCRBLT_ORDER          pScrBlt;
    LPMEMBLT_ORDER          pMemBlt;
    LPMEM3BLT_ORDER         pMem3Blt;
    BOOL                  fSendOrder  = FALSE;
    BOOL                  fAccumulate = FALSE;
    UINT                fOrderFlags = OF_SPOILABLE;
    RECT                  bounds;
    RECT                  intersectRect;
    POINT                 origin;
    POE_BRUSH_DATA          pCurrentBrush;
    MEMBLT_ORDER_EXTRA_INFO memBltExtraInfo;

    DebugEntry(DrvBitBlt);

    //
    // DO THIS _BEFORE_ TAKING LOCKS
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;


    OE_SHM_START_WRITING;

    //
    // Get the bounding rectangle for the operation.
    //
    RECT_FROM_RECTL(bounds, (*prclDst));

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(psoDst, pco, &bounds);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert the data to virtual coordinates.
    //
    OELRtoVirtual(&bounds, 1);

    //
    // Check if this 4-way ROP simplifies to a 3-way ROP.  A 4-way ROP
    // contains two 3-way ROPS, one for each setting of a mask bit - the
    // high ROP3 corresponds to a value of zero in the mask bit.
    //
    // If the two 3-way ROPs are the same, we know the 4-way ROP is a 3-way
    // ROP.
    //
    if (ROP3_LOW_FROM_ROP4(rop4) == ROP3_HIGH_FROM_ROP4(rop4))
    {
        //
        // Take the high byte as the 3-way ROP.
        //
        rop3 = ROP3_HIGH_FROM_ROP4(rop4);
        TRACE_OUT(( "4-way ROP %04x is really 3-way %02x", rop4, rop3));
    }
    else
    {
        TRACE_OUT(( "4-way ROP %08x", rop4));
        DC_QUIT;
    }

    //
    // Determine the command type.  It can be one of the following.
    //
    // DSTBLT  - A destination only BLT (no source, or pattern)
    // PATBLT  - a pattern BLT (no source)
    // SCRBLT  - a screen to screen BLT
    // MEMBLT  - a memory to screen BLT (no pattern)
    // MEM3BLT - a memory to screen 3-way BLT
    //

    //
    // Check for destination only BLTs (ie. independent of source bits).
    //
    if ((psoSrc == NULL) || ROP3_NO_SOURCE(rop3))
    {
        //
        // Check for a pattern or true destination BLT.
        //
        if (ROP3_NO_PATTERN(rop3))
        {
            TRACE_OUT(( "DSTBLT"));
            orderType = ORD_DSTBLT;
        }
        else
        {
            TRACE_OUT(( "PATBLT"));
            orderType = ORD_PATBLT;
        }
    }
    else
    {
        //
        // We have a source BLT, check whether we have screen or memory
        // BLTs.
        //
        if (psoSrc->hsurf != ppdev->hsurfScreen)
        {
            if (psoDst->hsurf != ppdev->hsurfScreen)
            {
                ERROR_OUT(( "MEM to MEM blt!"));
            }
            else
            {
                //
                // We have a memory to screen BLT, check which type.
                //
                if ((ppdev->cBitsPerPel == 4) && (rop3 != 0xcc))
                {
                    //
                    // No order -- the result depends on the palette
                    // which is dicy in VGA
                    //
                    TRACE_OUT(("No order on VGA for rop 0x%02x", rop3));
                    DC_QUIT;
                }

                if (ROP3_NO_PATTERN(rop3))
                {
                    TRACE_OUT(( "MEMBLT"));
                    orderType = ORD_MEMBLT;
                }
                else
                {
                    TRACE_OUT(( "MEM3BLT"));
                    orderType = ORD_MEM3BLT;
                }
            }
        }
        else
        {
            if (psoDst->hsurf != ppdev->hsurfScreen)
            {
                TRACE_OUT(( "SCR to MEM blt!"));
            }
            else
            {
                //
                // We only support destination only screen BLTs (ie.  no
                // patterns allowed).
                //
                if (ROP3_NO_PATTERN(rop3))
                {
                    TRACE_OUT(( "SCRBLT"));
                    orderType = ORD_SCRBLT;
                }
                else
                {
                    TRACE_OUT(( "Unsupported screen ROP %x", rop3));
                }
            }
        }
    }

    //
    // Check if we have a supported order.
    //
    if (orderType == 0)
    {
        TRACE_OUT(( "Unsupported BLT"));
        fAccumulate = FALSE;
        DC_QUIT;
    }

    //
    // Check if we are allowed to send this order (determined by the
    // negotiated capabilities of all the machines in the conference).
    //
    if (!OE_SendAsOrder(orderType))
    {
        TRACE_OUT(( "Order %d not allowed", orderType));
        DC_QUIT;
    }

    //
    // Check if we are allowed to send the ROP.
    //
    if (!OESendRop3AsOrder(rop3))
    {
        TRACE_OUT(( "Cannot send ROP %d", rop3));
        DC_QUIT;
    }

    //
    // Check for overcomplicated clipping.
    //
    if (OEClippingIsComplex(pco))
    {
        TRACE_OUT(( "Clipping is too complex"));
        DC_QUIT;
    }

    //
    // If this is a Memblt, do an initial check on whether it is cachable
    //
    if ((orderType == ORD_MEMBLT) || (orderType == ORD_MEM3BLT))
    {
        //
        // We have to fill in a structure containing extra into
        // specifically for a MEM(3)BLT order.
        //
        memBltExtraInfo.pSource   = psoSrc;
        memBltExtraInfo.pDest     = psoDst;
        memBltExtraInfo.pXlateObj = pxlo;

        if (!SBC_DDIsMemScreenBltCachable(&memBltExtraInfo))
        {
            TRACE_OUT(( "MemBlt is not cachable"));
            DC_QUIT;
        }

        //
        // It is cachable.  Before we get SBC to do the caching, we have to
        // allow it to queue a color table (if required).
        //
        if (!SBC_DDMaybeQueueColorTable(ppdev))
        {
            TRACE_OUT(( "Unable to queue color table for MemBlt"));
            DC_QUIT;
        }
    }

    //
    // We have a recognised order - do the specific checks for each order.
    //
    switch (orderType)
    {
        case ORD_DSTBLT:
            //
            // Allocate the memory for the order.
            //
            pOrder = OA_DDAllocOrderMem(sizeof(DSTBLT_ORDER),0);
            if (pOrder == NULL)
            {
                TRACE_OUT(( "Failed to alloc order"));
                DC_QUIT;
            }
            pDstBlt = (LPDSTBLT_ORDER)pOrder->abOrderData;

            //
            // Set the spoiler flag if the rop is opaque.
            //
            if (ROP3_IS_OPAQUE(rop3))
            {
                fOrderFlags |= OF_SPOILER;
            }

            //
            // Store the order type.
            //
            pDstBlt->type = LOWORD(orderType);

            //
            // Virtual desktop co-ordinates.
            //
            pDstBlt->nLeftRect  = bounds.left;
            pDstBlt->nTopRect   = bounds.top;
            pDstBlt->nWidth     = bounds.right  - bounds.left + 1;
            pDstBlt->nHeight    = bounds.bottom - bounds.top  + 1;
            pDstBlt->bRop       = rop3;

            TRACE_OUT(( "DstBlt X %d Y %d w %d h %d rop %02X",
                    pDstBlt->nLeftRect,
                    pDstBlt->nTopRect,
                    pDstBlt->nWidth,
                    pDstBlt->nHeight,
                    pDstBlt->bRop));
            break;

        case ORD_PATBLT:
            if ( !OEEncodePatBlt(ppdev,
                                 pbo,
                                 pptlBrush,
                                 rop3,
                                 &bounds,
                                 &pOrder) )
            {
                //
                // Something went wrong with the encoding, so skip to the
                // end to add this operation to the SDA.
                //
                DC_QUIT;
            }

            fOrderFlags = pOrder->OrderHeader.Common.fOrderFlags;
            break;

        case ORD_SCRBLT:
            //
            // Check for a SCRBLT as a result of a Desktop Scroll.  We must
            // ignore these as they will stuff the remote desktop.
            //
            // The check is simple - if the virtual position of the source
            // is the same as the virual position of the target for a
            // SRCCOPY type SCRBLT, we have a hit...
            //
            POINT_FROM_POINTL(origin, (*pptlSrc));

            //
            // Allocate the memory for the order.
            //
            pOrder = OA_DDAllocOrderMem(sizeof(SCRBLT_ORDER),0);
            if (pOrder == NULL)
            {
                TRACE_OUT(( "Failed to alloc order"));
                DC_QUIT;
            }
            pScrBlt = (LPSCRBLT_ORDER)pOrder->abOrderData;

            //
            // Store the order type.
            //
            pScrBlt->type = LOWORD(orderType);

            //
            // All data which is sent over the wire must be in virtual
            // desktop co-ordinates.  OELRtoVirtual has already converted
            // bounds to an inclusive rectangle in virtual co-ordinates.
            //
            pScrBlt->nLeftRect  = bounds.left;
            pScrBlt->nTopRect   = bounds.top;
            pScrBlt->nWidth     = bounds.right  - bounds.left + 1;
            pScrBlt->nHeight    = bounds.bottom - bounds.top  + 1;
            pScrBlt->bRop       = rop3;

            //
            // Source point on the screen.
            //
            OELPtoVirtual(&origin, 1);
            pScrBlt->nXSrc = origin.x;
            pScrBlt->nYSrc = origin.y;

            //
            // Screen to screen blts are Blocking orders (i.e.  they
            // prevent any previous orders from being spoilt).
            //
            // We do not mark Screen to Screen blts as SPOILER orders.  If
            // the ROP is opaque we could spoil the destination rect, but
            // only the area that does not overlap with the src rectangle.
            // The most common use of Screen to Screen blts is scrolling,
            // where the src and dst rects almost completely overlap,
            // giving only a small "spoiler" region.  The spoiler region
            // could also be complex (more that 1 rect).
            //
            // Consequently, the potential gains of trying to spoil using
            // these orders are small compared to the complexity of the
            // code required.
            //
            //
            fOrderFlags |= OF_BLOCKER;

            //
            // If the blt is screen to screen and the source overlaps the
            // destination and the clipping is not simple (> 1 rect) then
            // we do not want to send this as an order.
            //
            // (This is because we would need some complex code to
            // calculate the order in which to blt through each of the clip
            // rects.  As this case is pretty rare, it seems reasonable to
            // just send it as Screen Data).
            //
            if (!OEClippingIsSimple(pco))
            {
                //
                // Calculate the overlapping rectangle.
                //
                intersectRect.left  = max(pScrBlt->nLeftRect, pScrBlt->nXSrc);

                intersectRect.right = min(
                          pScrBlt->nLeftRect + pScrBlt->nWidth-1,
                          pScrBlt->nXSrc     + pScrBlt->nWidth-1 );

                intersectRect.top   = max(pScrBlt->nTopRect, pScrBlt->nYSrc);

                intersectRect.bottom = min(
                               pScrBlt->nTopRect + pScrBlt->nHeight-1,
                               pScrBlt->nYSrc    + pScrBlt->nHeight-1 );

                //
                // Check for a src / dst overlap.  If they overlap, the
                // intersection is a well-ordered non-trivial rectangle.
                //
                if ( (intersectRect.left <= intersectRect.right ) &&
                     (intersectRect.top  <= intersectRect.bottom) )
                {
                    //
                    // The src & dest overlap.  Free up the order memory
                    // and skip out now.  The destination rectangle will be
                    // added to the Screen Data Area.
                    //
                    OA_DDFreeOrderMem(pOrder);
                    DC_QUIT;
                }
            }

            TRACE_OUT(( "ScrBlt x %d y %d w %d h %d sx %d sy %d rop %02X",
                   pScrBlt->nLeftRect,
                   pScrBlt->nTopRect,
                   pScrBlt->nWidth,
                   pScrBlt->nHeight,
                   pScrBlt->nXSrc,
                   pScrBlt->nYSrc,
                   pScrBlt->bRop));
            break;

        case ORD_MEMBLT:
            //
            // Allocate the memory for the order - don't use OA as we are
            // only going to tile this order immediately.  Instead, we have
            // a static buffer to receive the template order data.
            //
            pOrder  = (LPINT_ORDER)g_oeTmpOrderBuffer;
            pMemBlt = (LPMEMBLT_ORDER)pOrder->abOrderData;
            pOrder->OrderHeader.Common.cbOrderDataLength
                                                    = sizeof(MEMBLT_R2_ORDER);

            //
            // Store the order type.
            //
            pMemBlt->type = LOWORD(orderType);

            //
            // Any data which is sent over the wire must be in virtual
            // desktop co-ordinates.  The bounding rectangle has already
            // been converted by OELRtoScreen.
            //
            pMemBlt->nLeftRect  = bounds.left;
            pMemBlt->nTopRect   = bounds.top;
            pMemBlt->nWidth     = bounds.right  - bounds.left + 1;
            pMemBlt->nHeight    = bounds.bottom - bounds.top  + 1;
            pMemBlt->bRop       = rop3;

            //
            // We need to store the source bitmap origin.  This is a memory
            // object, so screen/virtual conversions are unnecessary.
            //
            pMemBlt->nXSrc = pptlSrc->x;
            pMemBlt->nYSrc = pptlSrc->y;

            //
            // Mark the order as opaque if necessary.
            //
            if (ROP3_IS_OPAQUE(rop3))
            {
                fOrderFlags |= OF_SPOILER;
            }

            //
            // Store the src bitmap handle in the order.
            //
            pMemBlt->cacheId = 0;

            TRACE_OUT(( "MemBlt dx %d dy %d w %d h %d sx %d sy %d rop %04X",
                   pMemBlt->nLeftRect,
                   pMemBlt->nTopRect,
                   pMemBlt->nWidth,
                   pMemBlt->nHeight,
                   pMemBlt->nXSrc,
                   pMemBlt->nYSrc,
                   pMemBlt->bRop));
            break;

        case ORD_MEM3BLT:
            //
            // Check that the brush pattern is simple.
            //
            if (!OECheckBrushIsSimple(ppdev, pbo, &pCurrentBrush))
            {
                TRACE_OUT(( "Brush is not simple"));
                orderType = 0;
                DC_QUIT;
            }

            //
            // Allocate the memory for the order - don't use OA as we are
            // only going to tile this order immediately.  Instead, we have
            // a static buffer to receive the template order data.
            //
            pOrder   = (LPINT_ORDER)g_oeTmpOrderBuffer;
            pMem3Blt = (LPMEM3BLT_ORDER)pOrder->abOrderData;
            pOrder->OrderHeader.Common.cbOrderDataLength
                                                   = sizeof(MEM3BLT_R2_ORDER);

            //
            // Store the order type.
            //
            pMem3Blt->type = LOWORD(orderType);

            //
            // All data which is sent over the wire must be in virtual
            // desktop co-ordinates.  OELRtoVirtual has already done this
            // conversion for us.
            //
            pMem3Blt->nLeftRect  = bounds.left;
            pMem3Blt->nTopRect   = bounds.top;
            pMem3Blt->nWidth     = bounds.right  - bounds.left + 1;
            pMem3Blt->nHeight    = bounds.bottom - bounds.top  + 1;
            pMem3Blt->bRop       = rop3;

            //
            // We need to store the source bitmap origin.  This is a memory
            // object, so screen/virtual conversions are unnecessary.
            //
            pMem3Blt->nXSrc = pptlSrc->x;
            pMem3Blt->nYSrc = pptlSrc->y;

            //
            // Mark the order as opaque if necessary.
            //
            if (ROP3_IS_OPAQUE(rop3))
            {
                fOrderFlags |= OF_SPOILER;
            }

            //
            // Store the src bitmap handle in the order.
            //
            pMem3Blt->cacheId = 0;

            //
            // Set up the information required for the pattern.
            //
            pMem3Blt->BackColor = pCurrentBrush->back;
            pMem3Blt->ForeColor = pCurrentBrush->fore;

            //
            // The protocol brush origin is the point on the screen where
            // we want the brush to start being drawn from (tiling where
            // necessary).  This must be in virtual coordinates.
            //
            pMem3Blt->BrushOrgX  = pptlBrush->x;
            pMem3Blt->BrushOrgY  = pptlBrush->y;
            OELPtoVirtual((LPPOINT)&pMem3Blt->BrushOrgX, 1);

            //
            // Extra brush data from the data when we realised the brush.
            //
            pMem3Blt->BrushStyle = pCurrentBrush->style;
            pMem3Blt->BrushHatch = pCurrentBrush->style;

            RtlCopyMemory(pMem3Blt->BrushExtra,
                          pCurrentBrush->brushData,
                          sizeof(pMem3Blt->BrushExtra));

            TRACE_OUT(( "Mem3Blt brush %02X %02X dx %d dy %d w %d h %d "
                         "sx %d sy %d rop %04X",
                    pMem3Blt->BrushStyle,
                    pMem3Blt->BrushHatch,
                    pMem3Blt->nLeftRect,
                    pMem3Blt->nTopRect,
                    pMem3Blt->nWidth,
                    pMem3Blt->nHeight,
                    pMem3Blt->nXSrc,
                    pMem3Blt->nYSrc,
                    pMem3Blt->bRop));
            break;

        default:
            ERROR_OUT(( "New unsupported order %08lx", orderType));
            orderType = 0;
            break;
    }

    //
    // We have generated an order so make sure we send it.
    //
    if (orderType != 0)
    {
        fSendOrder = TRUE;
    }

DC_EXIT_POINT:
    //
    // If we did not send an order, we must accumulate the output in the
    // Screen Data Area.
    //
    if (fSendOrder)
    {
        //
        // Check if the ROP has a dependency on the destination.
        //
        if (!ROP3_NO_TARGET(rop3))
        {
            TRACE_OUT(( "ROP has a target dependency"));
            fOrderFlags |= OF_DESTROP;
        }

        //
        // Store the general order data.  The bounding rectagle
        // co-ordinates must be virtual desktop.  OELRtoVirtual has already
        // converted rect for us.
        //
        pOrder->OrderHeader.Common.fOrderFlags   = (TSHR_UINT16)fOrderFlags;

        TSHR_RECT16_FROM_RECT(&pOrder->OrderHeader.Common.rcsDst, bounds);

        //
        // Add the order to the cache.  Note that we have the new tiled
        // processing for MEMBLT and MEM3BLT orders.
        //
        if ((orderType == ORD_MEMBLT) || (orderType == ORD_MEM3BLT))
        {
            OETileBitBltOrder(pOrder, &memBltExtraInfo, pco);
        }
        else
        {
            OEClipAndAddOrder(pOrder, NULL, pco);
        }
    }
    else
    {
        if (fAccumulate)
        {
            OEClipAndAddScreenData(&bounds, pco);
        }

    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvBitBlt, rc);
    return(rc);
}


//
// DrvStretchBlt - see NT DDK documentation.
//
BOOL DrvStretchBlt(SURFOBJ         *psoDst,
                         SURFOBJ         *psoSrc,
                         SURFOBJ         *psoMask,
                         CLIPOBJ         *pco,
                         XLATEOBJ        *pxlo,
                         COLORADJUSTMENT *pca,
                         POINTL          *pptlHTOrg,
                         RECTL           *prclDst,
                         RECTL           *prclSrc,
                         POINTL          *pptlMask,
                         ULONG            iMode)
{
    BOOL    rc = TRUE;
    RECT  rectSrc;
    RECT  rectDst;
    BOOL  fAccumulate = FALSE;
    POINTL  ptlSrc;
    BOOL    usedBitBlt  = FALSE;

    DebugEntry(DrvStretchBlt);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;


    //
    // Get the source and destination rectangles
    //
    RECT_FROM_RECTL(rectSrc, (*prclSrc));
    RECT_FROM_RECTL(rectDst, (*prclDst));

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(psoDst, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Check that we have a valid ROP code.  The NT DDK states that the ROP
    // code for the StretchBlt is implicit in the mask specification.  If a
    // mask is specified, we have an implicit ROP4 of 0xCCAA, otherwise the
    // code is 0xCCCC.
    //
    // Our BitBlt code only encodes orders for ROP3s, so we must throw any
    // StretchBlts with a mask.
    //
    if (psoMask != NULL)
    {
        TRACE_OUT(( "Mask specified"));
        DC_QUIT;
    }

    //
    // Check for overcomplicated clipping.
    //
    if (OEClippingIsComplex(pco))
    {
        TRACE_OUT(( "Clipping is too complex"));
        DC_QUIT;
    }

    //
    // Rectangles are now well-ordered, check if we have a degenerate (ie.
    // no stretch) case.
    //
    if ( (rectSrc.right  - rectSrc.left == rectDst.right  - rectDst.left) &&
         (rectSrc.bottom - rectSrc.top  == rectDst.bottom - rectDst.top ) )
    {
        //
        // This can be passed on to the BitBlt code.
        //
        usedBitBlt = TRUE;

        ptlSrc.x = prclSrc->left;
        ptlSrc.y = prclSrc->top;

        rc = DrvBitBlt(psoDst,
                       psoSrc,
                       psoMask,
                       pco,
                       pxlo,
                       prclDst,
                       &ptlSrc,
                       pptlMask,
                       NULL,
                       NULL,
                       0xCCCC);

        //
        // We have stored this object in the BitBlt, so don't store the
        // data again.
        //
        fAccumulate = FALSE;
    }

DC_EXIT_POINT:
    if (fAccumulate)
    {
        //
        // Convert the data to virtual coordinates.
        //
        OELRtoVirtual(&rectDst, 1);

        //
        // Update the screen data area
        //
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvStretchBlt, rc);
    return(rc);
}


//
// DrvCopyBits - see NT DDK documentation.
//
BOOL DrvCopyBits(SURFOBJ  *psoDst,
                       SURFOBJ  *psoSrc,
                       CLIPOBJ  *pco,
                       XLATEOBJ *pxlo,
                       RECTL    *prclDst,
                       POINTL   *pptlSrc)
{
    //
    // CopyBits is a fast path for the NT display drivers.  In our case it
    // can always be processed as a BITBLT.
    //
    return(DrvBitBlt( psoDst,
                    psoSrc,
                    NULL,
                    pco,
                    pxlo,
                    prclDst,
                    pptlSrc,
                    NULL,
                    NULL,
                    NULL,
                    0xCCCC));
}


//
// DrvTextOut - see NT DDK documentation.
//
BOOL DrvTextOut(SURFOBJ  *pso,
                      STROBJ   *pstro,
                      FONTOBJ  *pfo,
                      CLIPOBJ  *pco,
                      RECTL    *prclExtra,
                      RECTL    *prclOpaque,
                      BRUSHOBJ *pboFore,
                      BRUSHOBJ *pboOpaque,
                      POINTL   *pptlOrg,
                      MIX       mix)
{
    LPOSI_PDEV           ppdev = (LPOSI_PDEV)pso->dhpdev;
    BOOL                rc = TRUE;
    RECT              rectDst;
    RECT              rectText;
    LPINT_ORDER          pOrder;
    LPINT_ORDER          pOpaqueOrder;
    LPTEXTOUT_ORDER     pTextOut;
    LPEXTTEXTOUT_ORDER  pExtTextOut;
    BOOL              fSendOrder  = FALSE;
    BOOL              fAccumulate = FALSE;
    char              ansiString[ORD_MAX_STRING_LEN_WITHOUT_DELTAS+2];
    ULONG               ansiLen;
    ULONG               tempLen;
    UINT            orderType = 0;
    ULONG               maxLength;
    LPSTR             lpVariable;
    BOOL                fMoreData;
    ULONG               count;
    ULONG               i;
    GLYPHPOS*           pGlyphData;
    int                 currentDelta;
    LPVARIABLE_DELTAX   lpDeltaPos;
    UINT                fontFlags;
    UINT                fontAscender;
    UINT                fontHeight;
    UINT                fontWidth;
    UINT                fontWeight;
    UINT                fontIndex;
    POINTL              lastPtl;
    LPCOMMON_TEXTORDER   pCommon;
    POINT               startPoint;
    BOOL              sendDeltaX = FALSE;

    DebugEntry(DrvTextOut);

    //
    // DO THIS _BEFORE_ TAKING LOCKS
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;

    //
    // Get bounding rectangle and convert to a RECT.
    //
    if (prclOpaque != NULL)
    {
        RECT_FROM_RECTL(rectDst, (*prclOpaque));
    }
    else
    {
        RECT_FROM_RECTL(rectDst, pstro->rclBkGround);
        TRACE_OUT(( "Using STROBJ bgd for size"));
    }

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(pso, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert to virtual coordinates
    //
    OELRtoVirtual(&rectDst, 1);

    //
    // Determine which order we will generate
    //
    if ( ((pstro->flAccel & SO_FLAG_DEFAULT_PLACEMENT) != 0) &&
         (prclOpaque == NULL) )
    {
        orderType = ORD_TEXTOUT;
        maxLength = ORD_MAX_STRING_LEN_WITHOUT_DELTAS;
    }
    else
    {
        orderType = ORD_EXTTEXTOUT;
        maxLength = ORD_MAX_STRING_LEN_WITH_DELTAS;
    }

    //
    // Check if we are allowed to send this order (determined by the
    // negotiated capabilities of all the machines in the conference).
    //
    if (!OE_SendAsOrder(orderType))
    {
        TRACE_OUT(( "Text order %x not allowed", orderType));
        DC_QUIT;
    }

    //
    // Check for a valid brush for the test operation.
    //
    if (pboFore->iSolidColor == -1)
    {
        TRACE_OUT(( "Bad brush for text fg"));
        DC_QUIT;
    }
    if (pboOpaque->iSolidColor == -1)
    {
        TRACE_OUT(( "Bad brush for text bg"));
        DC_QUIT;
    }

    //
    // Check that we don't have any modifiers rects on the font
    //
    if (prclExtra != NULL)
    {
        TRACE_OUT(( "Unsupported extra rects"));
        DC_QUIT;
    }

    //
    // Check that text orientation is OK.
    //
    if (pstro->flAccel & OE_BAD_TEXT_MASK)
    {
        TRACE_OUT(("DrvTextOut - unsupported flAccel 0x%08x", pstro->flAccel));
        DC_QUIT;
    }

    //
    // Check we have a valid string.
    //
    if (pstro->pwszOrg == NULL)
    {
        TRACE_OUT(( "No string - opaque %x", prclOpaque));
        DC_QUIT;
    }

    //
    // Check for overcomplicated clipping.
    //
    if (OEClippingIsComplex(pco))
    {
        TRACE_OUT(( "Clipping is too complex"));
        DC_QUIT;
    }

    //
    // Convert the string to an ANSI representation.
    //
    RtlFillMemory(ansiString, sizeof(ansiString), 0);
    EngUnicodeToMultiByteN(ansiString,
                           maxLength,
                           &ansiLen,
                           pstro->pwszOrg,
                           pstro->cGlyphs * sizeof(WCHAR));


    //
    // The conversion claims it never fails, but we have seen results that
    // are completely different on the remote box.  So we convert the ANSI
    // string back to UNICODE and check that we still have what we started
    // with.
    //
    EngMultiByteToUnicodeN(g_oeTempString,
                           sizeof(g_oeTempString),
                           &tempLen,
                           ansiString,
                           ansiLen);

    //
    // Check we don't have too much data, or that the translation failed to
    // give the correct data.  This happens when we try to translate
    // UNICODE text.
    //
    if ( (tempLen != pstro->cGlyphs * sizeof(WCHAR))           ||
         (memcmp(pstro->pwszOrg, g_oeTempString, tempLen) != 0) )
    {
        TRACE_OUT(( "String not translated"));
        DC_QUIT;
    }

    //
    // Check that the font is valid.
    //
    if (!OECheckFontIsSupported(pfo, ansiString, ansiLen,
                                &fontHeight,
                                &fontAscender,
                                &fontWidth,
                                &fontWeight,
                                &fontFlags,
                                &fontIndex,
                                &sendDeltaX))
    {
        TRACE_OUT(( "Unsupported font for '%s'", ansiString));
        //
        // Check if there is an opaque rectangle. If so it is worth
        // splitting this out. Word can output an entire line comprising a
        // single character followed by background, eg bullets, where the
        // line is blanked by drawing the bullet character at the start of
        // the line followed by a large - >1000 pixel - opaque rect.
        // Splitting the opaque rect from the text means we can send a
        // small area of SD for the unmatched font char while encoding the
        // large opaque rectangle.
        //
        if ( (prclOpaque != NULL) &&
             (pstro->cGlyphs == 1) &&
             (pstro->flAccel & SO_HORIZONTAL) &&
             OE_SendAsOrder(ORD_PATBLT))
        {
            //
            // There is an opaque rectangle and a single char.
            // Encode the opaque rectangle. First get a copy of the target
            // rect so we can use it later (and flip it into screen
            // coordinates).
            //
            TRACE_OUT(( "Have 1 char + opaque rect"));
            rectText.left = rectDst.left;
            rectText.top = rectDst.top;
            rectText.right = rectDst.right + 1;
            rectText.bottom = rectDst.bottom + 1;

            //
            // Call into the PATBLT encoding function.
            //
            if ( !OEEncodePatBlt(ppdev,
                                 pboOpaque,
                                 pptlOrg,
                                 OE_COPYPEN_ROP,
                                 &rectDst,
                                 &pOpaqueOrder) )
            {
                //
                // Something went wrong with the encoding, so skip to the
                // end to add this operation to the SDA.
                //
                TRACE_OUT(( "Failed to encode opaque rect"));
                DC_QUIT;
            }

            //
            // Store the general order data.  The bounding rectagle
            // co-ordinates must be virtual desktop.  OELRtoVirtual has
            // already converted rect for us.
            //
            TSHR_RECT16_FROM_RECT(&pOpaqueOrder->OrderHeader.Common.rcsDst, rectDst);

            //
            // Add the order to the cache.
            //
            OEClipAndAddOrder(pOpaqueOrder, NULL, pco);

            //
            // Calculate the bounds of the text. Get the glyph positions
            // for the left and right, and assume the top and bottom equate
            // approximately to the opaque rectangle.
            //
            if ( pstro->pgp == NULL)
            {
                //
                // The string object doesn't contain the GLYPHPOS info, so
                // enumerate the glyphs.
                //
                TRACE_OUT(( "Enumerate glyphs"));
                STROBJ_vEnumStart(pstro);
                STROBJ_bEnum(pstro, &count, &pGlyphData);
            }
            else
            {
                //
                // The string object already contains the GLYPHPOS info, so
                // just grab the pointer to it.
                //
                pGlyphData = pstro->pgp;
            }

            rectDst = rectText;
            rectDst.left = max(rectDst.left, pGlyphData[0].ptl.x);
            if ( pstro->ulCharInc == 0 )
            {
                //
                // No character increment for this string object. Just use
                // the maximum glyph width to calculate the right bounding
                // edge.
                //
                TRACE_OUT(( "no charinc glyph %d trg %d left %d maxX %d",
                                                   pGlyphData[0].ptl.x,
                                                   rectDst.right,
                                                   rectDst.left,
                                                   pfo->cxMax));
                rectDst.right = min(rectDst.right, (int)(pGlyphData[0].ptl.x +
                                                              pfo->cxMax - 1));
            }
            else
            {
                //
                // The string object has a character increment, so use it
                // to determine the right bounding edge.
                //
                TRACE_OUT(( "charinc %x glyph %d trg %d left %d",
                                                    pstro->ulCharInc,
                                                    pGlyphData[0].ptl.x,
                                                    rectDst.right,
                                                    rectDst.left));
                rectDst.right = min(rectDst.right, (int)(pGlyphData[0].ptl.x +
                                                        pstro->ulCharInc - 1));
            }

            //
            // Flip the target rectangle back to virtual coordinates.
            //
            rectDst.right -= 1;
            rectDst.bottom -= 1;
        }

        //
        // Skip to the end to add to the SDA.
        //
        DC_QUIT;
    }

    //
    // It is possible that the font matching blows our previous decision to
    // generate a TextOut order and we need to generate an ExtTextOut order
    // instead.  We need to reverify our parameters if this is the case.
    //
    if ((sendDeltaX) && (orderType != ORD_EXTTEXTOUT))
    {
        TRACE_OUT(( "Text order must be EXTTEXTOUT"));

        //
        // Set up for ExtTexOut orders.
        //
        orderType = ORD_EXTTEXTOUT;
        maxLength = ORD_MAX_STRING_LEN_WITH_DELTAS;

        //
        // Check if we are allowed to send this order (determined by the
        // negotiated capabilities of all the machines in the conference).
        //
        if (!OE_SendAsOrder(orderType))
        {
            TRACE_OUT(( "Text order %x not allowed", orderType));
            DC_QUIT;
        }

        //
        // Make sure we haven't blown the order size.
        //
        if (pstro->cGlyphs > maxLength)
        {
            TRACE_OUT(( "Text limit blown", pstro->cGlyphs));
            DC_QUIT;
        }
    }

    //
    // Get the proper start position for the text.
    //
    if ( pstro->pgp == NULL)
    {
        STROBJ_vEnumStart(pstro);
        STROBJ_bEnum(pstro, &count, &pGlyphData);
        if (count == 0)
        {
            WARNING_OUT(( "No glyphs"));
            DC_QUIT;
        }
    }
    else
    {
        pGlyphData = pstro->pgp;
    }

    startPoint.x = pGlyphData[0].ptl.x;

    //
    // Check if we should be using baseline alignment for the y
    // coordinate.  If we should be, the value in the glyph data is
    // correct.  If not, we the y coordinate is for the top of the
    // text, and we have to calculate it.
    //
    if (g_oeBaselineTextEnabled)
    {
        startPoint.y = pGlyphData[0].ptl.y;
        fontFlags   |= NF_BASELINE;
    }
    else
    {
        startPoint.y = pGlyphData[0].ptl.y - fontAscender;
    }

    //
    // Allocate memory for the order
    //
    switch (orderType)
    {
        case ORD_TEXTOUT:
        {
            //
            // Allocate the memory
            //
            pOrder = OA_DDAllocOrderMem((UINT)( sizeof(TEXTOUT_ORDER)
                                          - ORD_MAX_STRING_LEN_WITHOUT_DELTAS
                                          + ansiLen ),
                                      0);
            if (pOrder == NULL)
            {
                TRACE_OUT(( "Failed to alloc order"));
                DC_QUIT;
            }
            pTextOut = (LPTEXTOUT_ORDER)pOrder->abOrderData;

            //
            // Set up the order type.
            //
            pTextOut->type    = ORD_TEXTOUT_TYPE;

            //
            // Get a pointer to the fields which are common to both TextOut
            // and ExtTextOut
            //
            pCommon           = &pTextOut->common;
        }
        break;


        case ORD_EXTTEXTOUT:
        {
            //
            // BOGUS LAURABU
            // This allocates space for a deltax array whether or not one is needed
            //
            //
            // Allocate the memory
            //
            pOrder = OA_DDAllocOrderMem((UINT)( sizeof(EXTTEXTOUT_ORDER)
                                      -  ORD_MAX_STRING_LEN_WITHOUT_DELTAS
                                      - (ORD_MAX_STRING_LEN_WITH_DELTAS
                                                            * sizeof(TSHR_INT32))
                                      + ansiLen * (sizeof(TSHR_INT32) + 1)
                                      + 4),   // Allow for internal padding
                                      0);
            if (pOrder == NULL)
            {
                TRACE_OUT(( "Failed to alloc order"));
                DC_QUIT;
            }
            pExtTextOut = (LPEXTTEXTOUT_ORDER)pOrder->abOrderData;

            //
            // Set up the order type.
            //
            pExtTextOut->type = ORD_EXTTEXTOUT_TYPE;

            //
            // Get a pointer to the fields which are common to both TextOut
            // and ExtTextOut
            //
            pCommon           = &pExtTextOut->common;
        }
        break;

        default:
        {
            ERROR_OUT(( "Unknown order %x", orderType));
            DC_QUIT;
        }
        break;
    }

    //
    // Fill in the fields which are common to both TextOut and ExtTextOut
    //
    // Convert to virtual coordinates
    //
    OELPtoVirtual(&startPoint, 1);

    //
    // The x and y values are available in virtual coords from the bounds
    // rectangle.
    //
    pCommon->nXStart = startPoint.x;
    pCommon->nYStart = startPoint.y;

    //
    // Get the text colours.
    //
    OEConvertColor(ppdev,
                   &pCommon->BackColor,
                   pboOpaque->iSolidColor,
                   NULL);
    OEConvertColor(ppdev,
                   &pCommon->ForeColor,
                   pboFore->iSolidColor,
                   NULL);

    //
    // The transparency of the operation is determined by whether we have
    // an opaque rectangle or not.
    //
    pCommon->BackMode    = (prclOpaque == NULL) ? TRANSPARENT : OPAQUE;

    //
    // NT has a character extra spacing, not a generic for every character
    // spacing.  So, we always set this value to 0.
    //
    pCommon->CharExtra   = 0;

    //
    // NT does not provide a break of any sorts.
    //
    pCommon->BreakExtra  = 0;
    pCommon->BreakCount  = 0;

    //
    // Copy the font details
    //
    pCommon->FontHeight  = fontHeight;
    pCommon->FontWidth   = fontWidth;
    pCommon->FontWeight  = fontWeight;
    pCommon->FontFlags   = fontFlags;
    pCommon->FontIndex   = fontIndex;

    //
    // Now fill in the order specific data
    //
    switch (orderType)
    {
        case ORD_TEXTOUT:

            //
            // Copy across the text string.
            //
            pTextOut->variableString.len = (BYTE)ansiLen;
            RtlCopyMemory(pTextOut->variableString.string,
                          ansiString,
                          ansiLen);

            //
            // Make sure we send the order
            //
            fSendOrder = TRUE;

            TRACE_OUT(( "TEXTOUT: X %u Y %u bm %u FC %02X%02X%02X "
                         "BC %02X%02X%02X",
                         pTextOut->common.nXStart,
                         pTextOut->common.nYStart,
                         pTextOut->common.BackMode,
                         pTextOut->common.ForeColor.red,
                         pTextOut->common.ForeColor.green,
                         pTextOut->common.ForeColor.blue,
                         pTextOut->common.BackColor.red,
                         pTextOut->common.BackColor.green,
                         pTextOut->common.BackColor.blue));

            TRACE_OUT(( "Font: fx %u fy %u fw %u ff %04x fh %u len %u",
                         pTextOut->common.FontWidth,
                         pTextOut->common.FontHeight,
                         pTextOut->common.FontWeight,
                         pTextOut->common.FontFlags,
                         pTextOut->common.FontIndex,
                         ansiLen));

            TRACE_OUT(( "String '%s'", ansiString));
            break;

        case ORD_EXTTEXTOUT:
            //
            // Since our text is only ever fully contained within the
            // opaque rectangle, we only set the opaque flag (and ignore
            // the clipping).
            //
            pExtTextOut->fuOptions = (prclOpaque == NULL) ? 0 : ETO_OPAQUE;

            //
            // Set up the bounding rectangle for the operation.
            // EXT_TEXT_OUT orders use TSHR_RECT32s, hence we can't directly
            // assign rectDst to it.
            //
            pExtTextOut->rectangle.left     = rectDst.left;
            pExtTextOut->rectangle.top      = rectDst.top;
            pExtTextOut->rectangle.right    = rectDst.right;
            pExtTextOut->rectangle.bottom   = rectDst.bottom;

            //
            // Copy across the text string.
            //
            pExtTextOut->variableString.len = ansiLen;
            RtlCopyMemory(pExtTextOut->variableString.string,
                          ansiString,
                          ansiLen);

            //
            // WHOOP WHOOP WHOOP - Prepare to shut your eyes...
            //
            // Although we have a defined fixed length structure for
            // storing ExtTextOut orders, we must not send the full
            // structure over the network as the text will only be, say, 10
            // characters while the structure contains room for 127.
            //
            // Hence we pack the structure now to remove all the blank data
            // BUT we must maintain the natural alignment of the variables.
            //
            // So we know the length of the string which we can use to
            // start the new delta structure at the next 4-byte boundary.
            //
            lpVariable = ((LPBYTE)(&pExtTextOut->variableString))
                       + ansiLen
                       + sizeof(pExtTextOut->variableString.len);

            lpVariable = (LPSTR)
                         DC_ROUND_UP_4((UINT_PTR)lpVariable);

            lpDeltaPos = (LPVARIABLE_DELTAX)lpVariable;

            //
            // Do we need a delta array, or are the chars at their default
            // positions.
            //
            if ( sendDeltaX ||
                 ((pstro->flAccel & SO_FLAG_DEFAULT_PLACEMENT) == 0) )
            {
                //
                // Store the length of the position deltas.
                //
                lpDeltaPos->len = ansiLen * sizeof(TSHR_INT32);

                //
                // Set up the position deltas.
                //
                STROBJ_vEnumStart(pstro);
                fMoreData    = TRUE;
                currentDelta = 0;
                while (fMoreData)
                {
                    //
                    // Get the next set of glyph data
                    //
                    fMoreData = STROBJ_bEnum(pstro, &count, &pGlyphData);
                    for (i = 0; i < count; i++)
                    {
                        //
                        // The first time through we must set up the first
                        // glyph position.
                        //
                        if ((currentDelta == 0) && (i == 0))
                        {
                            lastPtl.x = pGlyphData[0].ptl.x;
                            lastPtl.y = pGlyphData[0].ptl.y;

                            TRACE_OUT(( "First Pos %d", lastPtl.x));
                        }
                        else
                        {
                            //
                            // For subsequent entries, we need to add the
                            // delta on the X position to the array.
                            //
                            if (pstro->ulCharInc == 0)
                            {
                                 lpDeltaPos->deltaX[currentDelta]
                                                         = pGlyphData[i].ptl.x
                                                         - lastPtl.x;

                                //
                                // Check for delta Y's - which we can't
                                // encode
                                //
                                if (pGlyphData[i].ptl.y - lastPtl.y)
                                {
                                    WARNING_OUT(( "New Y %d",
                                                 pGlyphData[i].ptl.y));
                                    OA_DDFreeOrderMem(pOrder);
                                    DC_QUIT;
                                }

                                //
                                // Store the last position for the next
                                // time round.
                                //
                                lastPtl.x = pGlyphData[i].ptl.x;
                                lastPtl.y = pGlyphData[i].ptl.y;

                                TRACE_OUT(( "Next Pos %d %d", i, lastPtl.x));
                            }
                            else
                            {
                                lpDeltaPos->deltaX[currentDelta]
                                                           = pstro->ulCharInc;
                            }

                            currentDelta++;
                        }
                    }
                }

                //
                // For the last entry, we need to set up the data by hand
                // (there are only n-1 deltas for n chars)
                //
                // This is done for compatibility with Windows 95 which
                // requires the last delta to be the delta to the place
                // where the next char would be if there were n+1 chars in
                // the string.
                //
                if (pstro->ulCharInc == 0)
                {
                    //
                    // No characters left - fudge a value of the width of
                    // the last character.
                    //
                    lpDeltaPos->deltaX[currentDelta] =
                                 pGlyphData[count-1].pgdf->pgb->sizlBitmap.cx;
                }
                else
                {
                    //
                    // All chars are evenly spaced, so just stick the value
                    // in.
                    //
                    lpDeltaPos->deltaX[currentDelta] = pstro->ulCharInc;
                }

                //
                // WHOOP WHOOP WHOOP - You can open your eyes now...
                //

                //
                // We must indicate the presence of this field to the
                // receiver.
                //
                pExtTextOut->fuOptions |= ETO_LPDX;
            }
            else
            {
                //
                // Mark the delta array as empty.
                //
                lpDeltaPos->len = 0;
            }

            //
            // WHOOP WHOOP WHOOP - You can open your eyes now...
            //


            //
            // Make sure we send the order
            //
            fSendOrder = TRUE;

            TRACE_OUT(( "EXTTEXTOUT: X %u Y %u bm %u FC %02X%02X%02X "
                         "BC %02X%02X%02X",
                         pExtTextOut->common.nXStart,
                         pExtTextOut->common.nYStart,
                         pExtTextOut->common.BackMode,
                         pExtTextOut->common.ForeColor.red,
                         pExtTextOut->common.ForeColor.green,
                         pExtTextOut->common.ForeColor.blue,
                         pExtTextOut->common.BackColor.red,
                         pExtTextOut->common.BackColor.green,
                         pExtTextOut->common.BackColor.blue));

            TRACE_OUT(( "Extra: Opt %x X1 %d Y1 %d X2 %d Y2 %d",
                         pExtTextOut->fuOptions,
                         pExtTextOut->rectangle.left,
                         pExtTextOut->rectangle.top,
                         pExtTextOut->rectangle.right,
                         pExtTextOut->rectangle.bottom));

            TRACE_OUT(( "Font: fx %u fy %u fw %u ff %04x fh %u len %u",
                         pExtTextOut->common.FontWidth,
                         pExtTextOut->common.FontHeight,
                         pExtTextOut->common.FontWeight,
                         pExtTextOut->common.FontFlags,
                         pExtTextOut->common.FontIndex,
                         ansiLen));

            TRACE_OUT(( "String '%s'", ansiString));
            break;

        default:
            ERROR_OUT(( "Unknown order %x", orderType));
            break;
    }

DC_EXIT_POINT:
    //
    // If we did not send an order, we must accumulate the output in the
    // Screen Data Area.
    //
    if (fSendOrder)
    {
        //
        // Store the general order data.  The bounding rectangle position
        // must be in virtual desktop co-ordinates.  OELRtoVirtual has
        // already done this.
        //
        pOrder->OrderHeader.Common.fOrderFlags   = OF_SPOILABLE;
        TSHR_RECT16_FROM_RECT(&pOrder->OrderHeader.Common.rcsDst, rectDst);

        //
        // Add the order to the cache.
        //
        OEClipAndAddOrder(pOrder, NULL, pco);
    }
    else
    {
        if (fAccumulate)
        {
            OEClipAndAddScreenData(&rectDst, pco);
        }
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvTextOut, rc);
    return(rc);
}


//
// DrvLineTo - see NT DDK documentation.
//
BOOL DrvLineTo(SURFOBJ   *pso,
                     CLIPOBJ   *pco,
                     BRUSHOBJ  *pbo,
                     LONG       x1,
                     LONG       y1,
                     LONG       x2,
                     LONG       y2,
                     RECTL     *prclBounds,
                     MIX        mix)
{
    LPOSI_PDEV      ppdev = (LPOSI_PDEV)pso->dhpdev;
    BOOL           rc = TRUE;
    RECT         rectDst;
    POINT           startPoint;
    POINT           endPoint;
    BOOL          fAccumulate = FALSE;

    DebugEntry(DrvLineTo);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;


    OE_SHM_START_WRITING;


    //
    // Get bounding rectangle and convert to a RECT.
    //
    RECT_FROM_RECTL(rectDst, (*prclBounds));

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(pso, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert the data to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

    //
    // Check if we are allowed to send this order (determined by the
    // negotiated capabilities of all the machines in the conference).
    //
    if (!OE_SendAsOrder(ORD_LINETO))
    {
        TRACE_OUT(( "LineTo order not allowed"));
        DC_QUIT;
    }

    //
    // Check for a valid brush for the test operation.
    //
    if (pbo->iSolidColor == -1)
    {
        TRACE_OUT(( "Bad brush for line"));
        DC_QUIT;
    }

    //
    // Check for overcomplicated clipping.
    //
    if (OEClippingIsComplex(pco))
    {
        TRACE_OUT(( "Clipping is too complex"));
        DC_QUIT;
    }

    //
    // Set up data for order
    //
    startPoint.x = x1;
    startPoint.y = y1;
    endPoint.x   = x2;
    endPoint.y   = y2;

    //
    // Store that order.
    //
    if (!OEAddLine(ppdev,
              &startPoint,
              &endPoint,
              &rectDst,
              mix & 0x1F,
              1,
              pbo->iSolidColor,
              pco))
    {
        TRACE_OUT(( "Failed to add order - use SDA"));
        DC_QUIT;
    }

    //
    // We have stored this object, so don't store the data in the SDA
    // again.
    //
    fAccumulate = FALSE;

DC_EXIT_POINT:
    if (fAccumulate)
    {
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvLineTo, rc);
    return(rc);
}


//
// DrvStrokePath - see NT DDK documentation.
//
BOOL DrvStrokePath(SURFOBJ   *pso,
                         PATHOBJ   *ppo,
                         CLIPOBJ   *pco,
                         XFORMOBJ  *pxo,
                         BRUSHOBJ  *pbo,
                         POINTL    *pptlBrushOrg,
                         LINEATTRS *plineattrs,
                         MIX        mix)
{
    LPOSI_PDEV      ppdev = (LPOSI_PDEV)pso->dhpdev;
    BOOL           rc = TRUE;
    RECTFX         rectfxTrg;
    RECT         rectDst;
    BOOL           fMore = TRUE;
    PATHDATA       pathData;
    POINT        startPoint;
    POINT        nextPoint;
    POINT        endPoint;
    BOOL         fAccumulate = FALSE;
    UINT         i;

    DebugEntry(DrvStrokePath);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;


    //
    // Get bounding rectangle and convert to a RECT.
    //
    PATHOBJ_vGetBounds(ppo, &rectfxTrg);
    RECT_FROM_RECTFX(rectDst, rectfxTrg);

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(pso, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Check if we are allowed to send this order (determined by the
    // negotiated capabilities of all the machines in the conference).
    //
    if (!OE_SendAsOrder(ORD_LINETO))
    {
        TRACE_OUT(( "LineTo order not allowed"));
        DC_QUIT;
    }

    //
    // Check for a valid brush for the test operation.
    //
    if (pbo->iSolidColor == -1)
    {
        TRACE_OUT(( "Bad brush for line"));
        DC_QUIT;
    }

    //
    // Check for overcomplicated clipping.
    //
    if (OEClippingIsComplex(pco))
    {
        TRACE_OUT(( "Clipping is too complex"));
        DC_QUIT;
    }

    //
    // See if we can optimize the path...
    //
    // We cannot send:
    //
    // - curvy lines (i.e. beziers)
    // - lines with funny end patterns (i.e. geometric lines)
    // - non Windows standard patterns
    //
    if ( ((ppo->fl        & PO_BEZIERS)   == 0) &&
         ((plineattrs->fl & LA_GEOMETRIC) == 0) &&
         (plineattrs->pstyle              == NULL) )
    {
        //
        // This is a set of solid cosmetic (i.e.  no fancy end styles)
        // lines.  Let's send those orders.
        //
        // NT stores all paths as a set of independent sub-paths.  Each
        // sub-path can start at a new point that is NOT linked to the
        // previous sub-path.
        //
        // Paths used for this function (as opposed to DrvFillPath or
        // DrvStrokeAndFillPath) do not need to be closed.
        //
        PATHOBJ_vEnumStart(ppo);

        while (fMore)
        {
            //
            // Get the next set of lines
            //
            fMore = PATHOBJ_bEnum(ppo, &pathData);

            TRACE_OUT(( "PTS: %lu FLAG: %08lx",
                         pathData.count,
                         pathData.flags));

            //
            // If this is the start of a path, remember the point in case
            // we need to close the path at the end.
            //
            if (pathData.flags & PD_BEGINSUBPATH)
            {
                POINT_FROM_POINTFIX(startPoint, pathData.pptfx[0]);
                POINT_FROM_POINTFIX(nextPoint,  pathData.pptfx[0]);
            }

            //
            // Generate orders for each line in the path.
            //
            for (i = 0; i < pathData.count; i++)
            {
                POINT_FROM_POINTFIX(endPoint, pathData.pptfx[i]);

                if ( (nextPoint.x != endPoint.x) ||
                     (nextPoint.y != endPoint.y) )
                {
                    if (!OEAddLine(ppdev,
                                   &nextPoint,
                                   &endPoint,
                                   &rectDst,
                                   mix & 0x1f,
                                   plineattrs->elWidth.l,
                                   pbo->iSolidColor,
                                   pco))
                    {
                        DC_QUIT;
                    }
                }

                nextPoint.x = endPoint.x;
                nextPoint.y = endPoint.y;
            }

            //
            // Close the path if necessary.
            //
            if ((pathData.flags & PD_CLOSEFIGURE) != 0)
            {
                if (!OEAddLine(ppdev,
                               &endPoint,
                               &startPoint,
                               &rectDst,
                               mix & 0x1f,
                               plineattrs->elWidth.l,
                               pbo->iSolidColor,
                               pco))
                {
                    DC_QUIT;
                }
            }
        }

        //
        // We have processed the entire thing as orders - don't send screen
        // data.
        //
        fAccumulate = FALSE;
    }

DC_EXIT_POINT:
    if (fAccumulate)
    {
        //
        // Convert the bounds to virtual coordinates.
        //
        OELRtoVirtual(&rectDst, 1);
        TRACE_OUT(( "SDA: (%d,%d)(%d,%d)",
                     rectDst.left,
                     rectDst.top,
                     rectDst.right,
                     rectDst.bottom));

        //
        // Update the Screen Data Area
        //
        OEClipAndAddScreenData(&rectDst, pco);

    }
    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvStrokePath, rc);
    return(rc);
}


//
// DrvFillPath - see NT DDK documentation.
//
BOOL DrvFillPath(SURFOBJ  *pso,
                       PATHOBJ  *ppo,
                       CLIPOBJ  *pco,
                       BRUSHOBJ *pbo,
                       POINTL   *pptlBrushOrg,
                       MIX       mix,
                       FLONG     flOptions)
{
    BOOL    rc = TRUE;
    RECTFX  rectfxTrg;
    RECT  rectDst;

    DebugEntry(DrvFillPath);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;


    OE_SHM_START_WRITING;


    //
    // Get bounding rectangle and convert to a RECT.
    //
    PATHOBJ_vGetBounds(ppo, &rectfxTrg);
    RECT_FROM_RECTFX(rectDst, rectfxTrg);

    //
    // Check if we are accumulating data for this function
    //
    if (!OEAccumulateOutput(pso, pco, &rectDst))
    {
        DC_QUIT;
    }

    //
    // Convert the bounds to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);
    TRACE_OUT(( "SDA: (%d,%d)(%d,%d)",
                 rectDst.left,
                 rectDst.top,
                 rectDst.right,
                 rectDst.bottom));

    //
    // Update the Screen Data Area
    //
    OEClipAndAddScreenData(&rectDst, pco);

DC_EXIT_POINT:
    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvFillPath, rc);
    return(rc);
}


//
// DrvStrokeAndFillPath - see NT DDK documentation.
//
BOOL DrvStrokeAndFillPath(SURFOBJ   *pso,
                                PATHOBJ   *ppo,
                                CLIPOBJ   *pco,
                                XFORMOBJ  *pxo,
                                BRUSHOBJ  *pboStroke,
                                LINEATTRS *plineattrs,
                                BRUSHOBJ  *pboFill,
                                POINTL    *pptlBrushOrg,
                                MIX        mixFill,
                                FLONG      flOptions)
{
    BOOL    rc = TRUE;
    RECTFX  rectfxTrg;
    RECT  rectDst;

    DebugEntry(DrvStrokeAndFillPath);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;


    OE_SHM_START_WRITING;


    //
    // Get bounding rectangle and convert to a RECT.
    //
    PATHOBJ_vGetBounds(ppo, &rectfxTrg);
    RECT_FROM_RECTFX(rectDst, rectfxTrg);

    //
    // Check if we are accumulating data for this function
    //
    if (!OEAccumulateOutput(pso, pco, &rectDst))
    {
        DC_QUIT;
    }

    //
    // Convert the bounds to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

    //
    // Update the Screen Data Area
    //
    OEClipAndAddScreenData(&rectDst, pco);

DC_EXIT_POINT:
    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvStrokeAndFillPath, rc);
    return(rc);
}


//
// DrvPaint - see NT DDK documentation.
//
BOOL DrvPaint(SURFOBJ  *pso,
                    CLIPOBJ  *pco,
                    BRUSHOBJ *pbo,
                    POINTL   *pptlBrushOrg,
                    MIX       mix)
{
    BOOL    rc = TRUE;
    RECT  rectDst;
    BOOL  fAccumulate = FALSE;
    ROP4    rop4;

    DebugEntry(DrvPaint);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;


    OE_SHM_START_WRITING;


    //
    // Get bounding rectangle and convert to a RECT.
    //
    RECT_FROM_RECTL(rectDst, pco->rclBounds);

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(pso, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

    //
    // Check for overcomplicated clipping.
    //
    if (OEClippingIsComplex(pco))
    {
        TRACE_OUT(( "Clipping is too complex"));
        DC_QUIT;
    }

    //
    // The low byte of the mix represents a ROP2.  We need a ROP4 for
    // BitBlt, so convert the mix as follows.
    //
    // Remember the definitions of 2, 3 & 4 way ROP codes.
    //
    //  Msk Pat Src Dst
    //
    //  1   1   1   1    ÍÍÍÍÍÍÍÍ¸ÍÍÍÍÍÍ»         ROP2 uses P & D only
    //  1   1   1   0            ³      º
    //  1   1   0   1    Ä¿      ³      º         ROP3 uses P, S & D
    //  1   1   0   0     ³ROP2-1³ROP3  ºROP4
    //  1   0   1   1     ³(see  ³      º         ROP4 uses M, P, S & D
    //  1   0   1   0    ÄÙ note)³      º
    //  1   0   0   1            ³      º
    //  1   0   0   0    ÍÍÍÍÍÍÍÍ¾      º
    //  0   1   1   1                   º
    //  0   1   1   0                   º         NOTE: Windows defines its
    //  0   1   0   1                   º         ROP2 codes as the bitwise
    //  0   1   0   0                   º         value calculated here
    //  0   0   1   1                   º         plus one.  All other ROP
    //  0   0   1   0                   º         codes are the straight
    //  0   0   0   1                   º         bitwise value.
    //  0   0   0   0    ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼
    //
    // Or, algorithmically...
    //
    // ROP3 = (ROP2 & 0x3) | ((ROP2 & 0xC) << 4) | (ROP2 << 2)
    //
    // ROP4 = (ROP3 << 8) | ROP3
    //
    mix  = (mix & 0x1F) - 1;
    rop4 = (mix & 0x3) | ((mix & 0xC) << 4) | (mix << 2);
    rop4 = (rop4 << 8) | rop4;

    //
    // This can be passed on to the BitBlt code.
    //
    rc = DrvBitBlt( pso,
                    NULL,
                    NULL,
                    pco,
                    NULL,
                    &pco->rclBounds,
                    NULL,
                    NULL,
                    pbo,
                    pptlBrushOrg,
                    rop4 );

    //
    // We have stored this object in the BitBlt, so don't store the data
    // again.
    //
    fAccumulate = FALSE;

DC_EXIT_POINT:
    if (fAccumulate)
    {
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitDWORD(DrvPaint, rc);
    return(rc);
}


//
// OE_DDProcessRequest - see oe.h
//
ULONG OE_DDProcessRequest
(
    SURFOBJ* pso,
    UINT    cjIn,
    void *  pvIn,
    UINT    cjOut,
    void *  pvOut
)
{
    BOOL                    rc = TRUE;
    LPOSI_ESCAPE_HEADER      pHeader;

    DebugEntry(OE_DDProcessRequest);

    //
    // Get the request number.
    //
    pHeader = pvIn;
    switch (pHeader->escapeFn)
    {
        case OE_ESC_NEW_FONTS:
        {
            if ((cjIn != sizeof(OE_NEW_FONTS)) ||
                (cjOut != sizeof(OE_NEW_FONTS)))
            {
                ERROR_OUT(("OE_DDProcessRequest:  Invalid sizes %d, %d for OE_ESC_NEW_FONTS",
                    cjIn, cjOut));
                rc = FALSE;
                DC_QUIT;
            }

            //
            // Get new local font data
            //
            OEDDSetNewFonts(pvIn);
        }
        break;

        case OE_ESC_NEW_CAPABILITIES:
        {
            if ((cjIn != sizeof(OE_NEW_CAPABILITIES)) ||
                (cjOut != sizeof(OE_NEW_CAPABILITIES)))
            {
                ERROR_OUT(("OE_DDProcessRequest:  Invalid sizes %d, %d for OE_ESC_NEW_CAPABILITIES",
                    cjIn, cjOut));
                rc = FALSE;
                DC_QUIT;
            }

            //
            // The capabilities have changed - take the new copy.
            //
            OEDDSetNewCapabilities(pvIn);
        }
        break;

        default:
        {
            ERROR_OUT(("Unrecognised OE escape"));
            rc = FALSE;
        }
        break;
    }

DC_EXIT_POINT:
    DebugExitDWORD(OE_DDProcessRequest, rc);
    return((ULONG)rc);
}


//
// OE_DDTerm()
// This cleans up objects used
//
void OE_DDTerm(void)
{
    DebugEntry(OE_DDTerm);

    //
    // Free font list
    //
    if (g_poeLocalFonts)
    {
        TRACE_OUT(("OE_DDLocalHosting: freeing font block since we're done sharing"));
        EngFreeMem(g_poeLocalFonts);

        g_poeLocalFonts = NULL;
        g_oeNumFonts = 0;
    }

    DebugExitVOID(OE_DDTerm);
}


//
// DrvRealizeBrush - see NT DDK documentation.
//
BOOL DrvRealizeBrush(BRUSHOBJ *pbo,
                           SURFOBJ  *psoTarget,
                           SURFOBJ  *psoPattern,
                           SURFOBJ  *psoMask,
                           XLATEOBJ *pxlo,
                           ULONG    iHatch)
{
    LPOSI_PDEV ppdev = (LPOSI_PDEV)psoTarget->dhpdev;
    BOOL      rc    = TRUE;
    LPBYTE  pData;
    BYTE   brushBits[8];
    UINT  color1;
    UINT  color2;
    int     i;
    int     j;
    BOOL    monochromeBrush = TRUE;

    DebugEntry(DrvRealizeBrush);

    //
    // This function only sets up local data, so shared memory protection
    // is not required.
    //

    //
    // Since this function is called only when we call BRUSHOBJ_pvGetRBrush
    // and we don't do any processing until we are in a share we don't need
    // an explicit check for hosting here ('coz that happened before the
    // call to realise the brush).
    //

    //
    // A valid brush satisfies either of the following criteria.
    //
    //  1) It is a standard hatch brush (as passed by DrvEnablePDEV)
    //  2) It is an 8x8 monochrome bitmap
    //

    //
    // Check for a Windows standard hatch
    //
    if (iHatch < HS_DDI_MAX)
    {
        TRACE_OUT(( "Standard hatch %lu", iHatch));
        rc = OEStoreBrush(ppdev,
                          pbo,
                          BS_HATCHED,
                          NULL,
                          pxlo,
                          (BYTE)iHatch,
                          0,
                          1);
        DC_QUIT;
    }

    //
    // If the driver has been passed a dither color brush we can support
    // this by sending a solid color brush definition
    //
    if ((iHatch & RB_DITHERCOLOR) != 0)
    {
        TRACE_OUT(( "Standard hatch %lu", iHatch));
        rc = OEStoreBrush(ppdev,
                          pbo,
                          BS_SOLID,
                          NULL,
                          NULL,
                          (BYTE)iHatch,
                          iHatch & 0xFFFFFF,
                          0);
        DC_QUIT;
    }


    //
    // Check for a simple 8x8 brush
    //
    if ( (psoPattern->sizlBitmap.cx == 8) &&
         (psoPattern->sizlBitmap.cy == 8) )
    {
        //
        // Check for 2 colours only in the bitmap.
        //
        // NOTE: There's a flag (BMF_TOPDOWN) in psoPattern->fjBitmap
        // that's supposed to indicate whether the bitmap is top-down or
        // bottom-up, but it is not always set up correctly.  In fact, the
        // bitmaps are always the wrong way up for our protocol, so we have
        // to flip them regardless of the flag.  Hence the row numbers are
        // reversed ('i' loops) in all the conversions below.
        //
        pData = psoPattern->pvScan0;
        switch (psoPattern->iBitmapFormat)
        {
            case BMF_1BPP:
            {
                //
                // 1 bpp MUST be 2 colours maximum.
                //
                color1 = 1;
                color2 = 0;
                for (i = 7; i >= 0; i--)
                {
                    brushBits[i] = *pData;
                    pData       += psoPattern->lDelta;
                }
            }
            break;

            case BMF_4BPP:
            {
                //
                // See if it is really a 2 colour brush.  Start off with
                // both colours the same.
                //
                color1 = pData[0] & 15;
                color2 = color1;

                //
                // Iterate through each row of the bitmap.
                //
                for (i = 7; (i >= 0) && (monochromeBrush); i--)
                {
                    brushBits[i] = 0;

                    //
                    // Check each pixel in the row: 4bpp->2 pixels per byte
                    //
                    for (j = 0; (j < 4) && (monochromeBrush); j++)
                    {
                        //
                        // Check the 1st pixel color
                        //
                        if ( (color1 != (UINT)(pData[j] & 0x0F)) &&
                             (color2 != (UINT)(pData[j] & 0x0F)) )
                        {
                            if (color1 == color2)
                            {
                                color2 = (pData[j] & 0x0F);
                            }
                            else
                            {
                                monochromeBrush = FALSE;
                            }
                        }

                        //
                        // Check the 2nd pixel color
                        //
                        if ( (color1 != (UINT)((pData[j] & 0xF0) >> 4)) &&
                             (color2 != (UINT)((pData[j] & 0xF0) >> 4)) )
                        {
                            if (color1 == color2)
                            {
                                color2 = (pData[j] & 0xF0) >> 4;
                            }
                            else
                            {
                                monochromeBrush = FALSE;
                            }
                        }

                        //
                        // Set up the brush data.  High bit is leftmost.
                        //
                        if ((UINT)(pData[j] & 0x0F) == color1)
                        {
                            brushBits[i] |= 0x40 >> (j * 2);
                        }
                        if ((UINT)(pData[j] & 0xF0) >> 4  == color1)
                        {
                            brushBits[i] |= 0x80 >> (j * 2);
                        }
                    }

                    //
                    // Get start of next row.
                    //
                    pData += psoPattern->lDelta;
                }
            }
            break;

            case BMF_8BPP:
            {
                //
                // See if it is really a 2 colour brush.  Start off with
                // both colours the same.
                //
                color1 = pData[0];
                color2 = color1;

                //
                // Iterate through each row of the bitmap.
                //
                for (i = 7; (i >= 0) && (monochromeBrush); i--)
                {
                    brushBits[i] = 0;

                    //
                    // Check each pixel in the row: 8bpp->1 pixel per byte
                    //
                    for (j = 0; (j < 8) && (monochromeBrush); j++)
                    {
                        //
                        // Check each pixel.
                        //
                        if ( (color1 != pData[j]) &&
                             (color2 != pData[j]) )
                        {
                            if (color1 == color2)
                            {
                                color2 = pData[j];
                            }
                            else
                            {
                                monochromeBrush = FALSE;
                            }
                        }

                        //
                        // Update the brush data.  High bit is leftmost.
                        //
                        if (pData[j] == color1)
                        {
                           brushBits[i] |= 0x80 >> j;
                        }
                    }

                    //
                    // Get start of next row.
                    //
                    pData += psoPattern->lDelta;
                }
            }
            break;

            default:
            {
                //
                // Unsupported colour depth.
                //
                monochromeBrush = FALSE;
            }
            break;
        }
    }
    else
    {
        //
        // The brush is the wrong size or requires dithering and so cannot
        // be sent over the wire.
        //
        monochromeBrush = FALSE;
    }

    //
    // Store that brush.
    //
    if (monochromeBrush)
    {
        //
        // Store the brush - note that we have a monochrome brush where the
        // color bit is set up so that 0 = color2 and 1 = color1.  This
        // actually corresponds to 0 = fg and 1 = bg for the protocol
        // colors.
        //
        TRACE_OUT(( "Storing brush: type %d bg %x fg %x",
                     psoPattern->iBitmapFormat,
                     color1,
                     color2));

        rc = OEStoreBrush(ppdev,
                          pbo,
                          BS_PATTERN,
                          brushBits,
                          pxlo,
                          0,
                          color2,
                          color1);
    }
    else
    {
        TRACE_OUT(( "Rejected brush h %08lx s (%ld, %ld) fmt %lu",
                     iHatch,
                     psoPattern != NULL ? psoPattern->sizlBitmap.cx : 0,
                     psoPattern != NULL ? psoPattern->sizlBitmap.cy : 0,
                     psoPattern != NULL ? psoPattern->iBitmapFormat : 0));
        rc = OEStoreBrush(ppdev, pbo, BS_NULL, NULL, pxlo, 0, 0, 0);
    }

DC_EXIT_POINT:

    DebugExitDWORD(DrvRealizeBrush, rc);
    return(rc);
}


//
// DrvSaveScreenBits - see NT DDK documentation.
//
ULONG_PTR DrvSaveScreenBits(SURFOBJ *pso,
                              ULONG    iMode,
                              ULONG_PTR    ident,
                              RECTL   *prcl)
{
    BOOL    rc;
    UINT  ourMode;
    RECT  rectDst;

    DebugEntry(DrvSaveScreenBits);

    TRACE_OUT(("DrvSaveScreenBits:  %s",
        ((iMode == SS_SAVE) ? "SAVE" :
            ((iMode == SS_RESTORE) ? "RESTORE" : "DISCARD"))));
    TRACE_OUT(("      rect        {%04ld, %04ld, %04ld, %04ld}",
        prcl->left, prcl->top, prcl->right, prcl->bottom));
    //
    // Default is TRUE, let SaveBits happen if we don't care.  Which we don't
    // if we have no shared memory (NetMeeting isn't running), no window list
    // (no shared apps), or the operation isn't intersecting a window we
    // care about.
    //
    // Note that if we return TRUE on a save, and FALSE on a restore later
    // (because we are now sharing that area for example), USER+GRE handle
    // that.  So it's ok.
    //
    rc = TRUE;

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    //
    // If we have no shared memory (NetMeeting isn't running), this will bail
    // out immediately.
    //

    OE_SHM_START_WRITING;


    //
    // Get the bounding rectangle for the operation.  NOTE that this is
    // meaningless for SS_FREE.
    //
    RECT_FROM_RECTL(rectDst, (*prcl));
    if (iMode != SS_FREE)
    {
        //
        // Check if we are accumulating data for this area, ONLY FOR
        // SAVEs.  We may get notified after a window is gone to
        // restore or discard bits we had saved.
        //
        if (!OEAccumulateOutputRect(pso, &rectDst))
        {
            TRACE_OUT(("DrvSaveScreenBits:  save/restore in area we don't care about"));
            DC_QUIT;
        }
    }

    //
    // Convert the NT orders to our generic save/restore types.
    //
    switch (iMode)
    {
        case SS_SAVE:
        {
            ourMode = ONBOARD_SAVE;
        }
        break;

        case SS_RESTORE:
        {
            ourMode = ONBOARD_RESTORE;
        }
        break;

        case SS_FREE:
        {
            ourMode = ONBOARD_DISCARD;
        }
        break;

        default:
        {
            ERROR_OUT(( "Unknown type %lu", iMode));
            DC_QUIT;
        }
    }

    //
    // Call through to the SSI handler.
    //
    rc = SSI_SaveScreenBitmap(&rectDst, ourMode);

DC_EXIT_POINT:
    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    TRACE_OUT(("DrvSaveScreenBits returning %d", rc));
    DebugExitDWORD(DrvSaveScreenBits, rc);
    return(rc);
}



//
// Function:    OEUnicodeStrlen
//
// Description: Get the length of a unicode string in bytes.
//
// Parameters:  pString - Unicode string to be read
//
// Returns:     Length of the Unicode string in bytes
//
int  OEUnicodeStrlen(PWSTR pString)
{
    int i;

    for (i = 0; pString[i] != 0; i++)
        ;

    return((i + 1) * sizeof(WCHAR));
}



//
// Function:    OEExpandColor
//
// Description: Converts a generic bitwise representation of an RGB color
//              index into an 8-bit color index as used by the line
//              protocol.
//
//
void  OEExpandColor
(
    LPBYTE  lpField,
    ULONG   srcColor,
    ULONG   mask
)
{
    ULONG   colorTmp;

    DebugEntry(OEExpandColor);

    //
    // Different example bit masks:
    //
    // Normal 24-bit:
    //      0x000000FF  (red)
    //      0x0000FF00  (green)
    //      0x00FF0000  (blue)
    //
    // True color 32-bits:
    //      0xFF000000  (red)
    //      0x00FF0000  (green)
    //      0x0000FF00  (blue)
    //
    // 5-5-5 16-bits
    //      0x0000001F  (red)
    //      0x000003E0  (green)
    //      0x00007C00  (blue)
    //
    // 5-6-5 16-bits
    //      0x0000001F  (red)
    //      0x000007E0  (green)
    //      0x0000F800  (blue)
    //
    //
    // Convert the color using the following algorithm.
    //
    // <new color> = <old color> * <new bpp mask> / <old bpp mask>
    //
    // where:
    //
    // new bpp mask = mask for all bits at new setting (0xFF for 8bpp)
    //
    // This way maximal (eg.  0x1F) and minimal (eg.  0x00) settings are
    // converted into the correct 8-bit maximum and minimum.
    //
    // Rearranging the above equation we get:
    //
    // <new color> = (<old color> & <old bpp mask>) * 0xFF / <old bpp mask>
    //
    // where:
    //
    // <old bpp mask> = mask for the color
    //

    //
    // LAURABU BOGUS:
    // We need to avoid overflow caused by the multiply.  NOTE:  in theory
    // we should use a double, but that's painfully slow.  So for now hack
    // it.  If the HIBYTE is set, just right shift 24 bits.
    //
    colorTmp = srcColor & mask;
    if (colorTmp & 0xFF000000)
        colorTmp >>= 24;
    else
        colorTmp = (colorTmp * 0xFF) / mask;
    *lpField = (BYTE)colorTmp;

    TRACE_OUT(( "0x%lX -> 0x%X", srcColor, *lpField));

    DebugExitVOID(OEExpandColor);
}


//
// Function:    OEConvertColor
//
// Description: Convert a color from the NT Display Driver into a TSHR_COLOR
//
// Parameters:  pDCColor  - (returned) color in protocol format
//              osColor   - color from the NT display driver
//              pxlo      - XLATEOBJ for the color to be converted
//                          (NULL if no translation is required)
//
// Returns:     (none)
//
void  OEConvertColor(LPOSI_PDEV ppdev, LPTSHR_COLOR pTshrColor,
                                         ULONG     osColor,
                                         XLATEOBJ* pxlo)
{
    ULONG    realIndex;

    DebugEntry(OEConvertColor);

    //
    // Make sure we have a default setting.
    //
    RtlFillMemory(pTshrColor, sizeof(TSHR_COLOR), 0);

    //
    // Check if color translation is required.
    //
    if ((pxlo != NULL) && (pxlo->flXlate != XO_TRIVIAL))
    {
        //
        // Convert from BMP to device color.
        //
        realIndex = XLATEOBJ_iXlate(pxlo, osColor);
        if (realIndex == -1)
        {
            ERROR_OUT(( "Failed to convert color 0x%lx", osColor));
            DC_QUIT;
        }
    }
    else
    {
        //
        // Use the OS color without translation
        //
        realIndex = osColor;
    }

    TRACE_OUT(( "Device color 0x%lX", realIndex));

    //
    // We now have the device specific version of the color.  Time to
    // convert it into a 24-bit RGB color as used by the line protocol.
    //
    switch (ppdev->iBitmapFormat)
    {
        case BMF_1BPP:
        case BMF_4BPP:
        case BMF_4RLE:
        case BMF_8BPP:
        case BMF_8RLE:
            //
            // Palette type device - use the device color as an index into
            // our palette array.
            //
            pTshrColor->red  = (BYTE)ppdev->pPal[realIndex].peRed;
            pTshrColor->green= (BYTE)ppdev->pPal[realIndex].peGreen;
            pTshrColor->blue = (BYTE)ppdev->pPal[realIndex].peBlue;
            break;

        case BMF_16BPP:
        case BMF_24BPP:
        case BMF_32BPP:
            //
            // Generic colour masks (could be eg.  5-6-5 for 16 or 8-8-8
            // for 24 bits per pel).  We must mask off the other bits and
            // shift down to bit 0.
            //
            OEExpandColor(&(pTshrColor->red),
                          realIndex,
                          ppdev->flRed);

            OEExpandColor(&(pTshrColor->green),
                          realIndex,
                          ppdev->flGreen);

            OEExpandColor(&(pTshrColor->blue),
                          realIndex,
                          ppdev->flBlue);
            break;

        default:
            ERROR_OUT(( "Unrecognised BMP color depth %lu",
                                                       ppdev->iBitmapFormat));
            break;
    }

    TRACE_OUT(( "Red %x green %x blue %x", pTshrColor->red,
                                            pTshrColor->green,
                                            pTshrColor->blue));

DC_EXIT_POINT:
    DebugExitVOID(OEConvertColor);
}


//
// Function:    OEStoreBrush
//
// Description: Store the brush data required for pattern realted orders.
//              This function is called by DrvRealiseBrush when it has data
//              to be stored about a brush.
//
// Parameters:  pbo        - BRUSHOBJ of the brush to be stored
//              style      - Style of the brush (as defined in the DC-Share
//                           protocol)
//              pBits      - Pointer to the bits which are used to define
//                           a BS_PATTERN brush.
//              pxlo       - XLATEOBJ for the brush.
//              hatch      - Standard Windows hatch pattern index for a
//                           BS_HATCHED brush.
//              color1     - index into XLATEOBJ for bit set color
//                           OR exact 24bpp color to use (pxlo == NULL)
//              color2     - index into XLATEOBJ for bit clear color
//                           OR exact 24bpp color to use (pxlo == NULL)
//
// Returns:     (none)
//
BOOL  OEStoreBrush(LPOSI_PDEV ppdev,
                                       BRUSHOBJ* pbo,
                                       BYTE   style,
                                       LPBYTE  pBits,
                                       XLATEOBJ* pxlo,
                                       BYTE   hatch,
                                       UINT  color1,
                                       UINT  color2)
{
    BOOL         rc = FALSE;
    int          i;
    LPBYTE       pData;
    ULONG*         pColorTable;
    POE_BRUSH_DATA pBrush;

    DebugEntry(OEStoreBrush);

    //
    // Allocate the space for the brush data.
    //
    pBrush = (POE_BRUSH_DATA)BRUSHOBJ_pvAllocRbrush(pbo,
                                                    sizeof(OE_BRUSH_DATA));
    if (pBrush == NULL)
    {
        ERROR_OUT(( "No memory"));
        DC_QUIT;
    }

    //
    // Reset the brush definition
    //
    RtlFillMemory(pBrush, sizeof(OE_BRUSH_DATA), 0);

    //
    // Set the new brush data.
    //
    pBrush->style = style;
    pBrush->hatch = hatch;

    TRACE_OUT(( " Style: %d Hatch: %d", style, hatch));

    //
    // For pattern brushes, copy the brush specific data.
    //
    if (style == BS_PATTERN)
    {
        //
        // Copy the brush bits.  Since this is an 8x8 mono bitmap, we can
        // copy the first byte of the brush data for each scan line.
        //
        // NOTE however that the brush structures sent over the wire
        // re-use the hatching variable as the first byte of the brush data.
        //
        pData         = pBits;
        pBrush->hatch = *pData;
        TRACE_OUT(( " Hatch: %d", *pData));

        pData++;

        for (i = 0; i < 7; i++)
        {
            pBrush->brushData[i] = pData[i];
            TRACE_OUT(( " Data[%d]: %d", i, pData[i]));
        }

        //
        // Get pointer to the bitmap color table.
        //
        pColorTable = pxlo->pulXlate;
        if (pColorTable == NULL)
        {
            pColorTable = XLATEOBJ_piVector(pxlo);
        }
    }

    //
    // Store the foreground and background colours for the brush.
    //
    if (pxlo != NULL)
    {
        //
        // Conversion required.
        //
        OEConvertColor(ppdev,
                       &pBrush->fore,
                       color1,
                       pxlo);

        OEConvertColor(ppdev,
                       &pBrush->back,
                       color2,
                       pxlo);
    }
    else
    {
        //
        // We have been passed an exact 24bpp color - this only happens for
        // solid brushes so we don't need to convert color2.
        //
        pBrush->fore.red   = (BYTE) (color1 & 0x0000FF);
        pBrush->fore.green = (BYTE)((color1 & 0x00FF00) >> 8);
        pBrush->fore.blue  = (BYTE)((color1 & 0xFF0000) >> 16);
    }

    rc = TRUE;

DC_EXIT_POINT:
    DebugExitDWORD(OEStoreBrush, rc);
    return(rc);
}


//
// Function:    OECheckBrushIsSimple
//
// Description: Check that the brush is a 'simple' object we can transfer
//              over the DC-Share protocol.
//
// Parameters:  pbo - BRUSHOBJ of the brush to be checked.
//
// Returns:     TRUE  - brush can be sent as DC-Share order
//              FALSE - brush is too complicated.
//
BOOL  OECheckBrushIsSimple(LPOSI_PDEV       ppdev,
                                               BRUSHOBJ*       pbo,
                                               POE_BRUSH_DATA* ppBrush)
{
    BOOL         rc     = FALSE;
    POE_BRUSH_DATA pBrush = NULL;

    DebugEntry(OECheckBrushIsSimple);

    //
    // A 'simple' brush satisfies any of the following.
    //
    //  1) It is a solid color.
    //  2) It is a valid brush as stored by DrvRealizeBrush.
    //

    //
    // Check for a simple solid colour.
    //
    if (pbo->iSolidColor != -1)
    {
        //
        // Use the reserved brush definition to set up the solid colour.
        //
        TRACE_OUT(( "Simple solid colour %08lx", pbo->iSolidColor));
        pBrush = &g_oeBrushData;

        //
        // Set up the specific data for this brush.
        //
        OEConvertColor(ppdev, &pBrush->fore, pbo->iSolidColor, NULL);

        pBrush->back.red   = 0;
        pBrush->back.green = 0;
        pBrush->back.blue  = 0;

        pBrush->style      = BS_SOLID;
        pBrush->hatch      = 0;

        RtlFillMemory(pBrush->brushData, 7, 0);

        //
        // We have a valid brush - return true.
        //
        rc = TRUE;
        DC_QUIT;
    }

    //
    // Check brush definition (which was stored when we realized the
    // brush).
    //
    pBrush = (POE_BRUSH_DATA)pbo->pvRbrush;
    if (pBrush == NULL)
    {
        pBrush = (POE_BRUSH_DATA)BRUSHOBJ_pvGetRbrush(pbo);
        if (pBrush == NULL)
        {
            //
            // We can get NULL returned from BRUSHOBJ_pvGetRbrush when the
            // brush is NULL or in low-memory situations (when the brush
            // realization may fail).
            //
            TRACE_OUT(( "NULL returned from BRUSHOBJ_pvGetRbrush"));
            DC_QUIT;
        }
    }

    //
    // Check it is an encodable brush.
    //
    if (pBrush->style == BS_NULL)
    {
        TRACE_OUT(( "Complex brush"));
        DC_QUIT;
    }

    //
    // Evrything passed - let's use this brush.
    //
    rc = TRUE;

DC_EXIT_POINT:
    //
    // Return the brush definition
    //
    *ppBrush = pBrush;

    TRACE_OUT(( "Returning %d - 0x%08lx", rc, pBrush));

    DebugExitDWORD(OECheckBrushIsSimple, rc);
    return(rc);
}


//
// Function:    OEClippingIsSimple
//
// Description: Check to see if the clipping on the graphics object is
//              trivial
//
// Parameters:  pco - CLIPOBJ of the graphics object to be checked.
//
// Returns:     TRUE  - Clipping is trivial
//              FALSE - Clipping is complex
//
BOOL  OEClippingIsSimple(CLIPOBJ* pco)
{
    BOOL rc = TRUE;

    DebugEntry(OEClippingIsSimple);

    //
    // Check for a valid clip object
    //
    if (pco == NULL)
    {
        TRACE_OUT(( "No clipobj"));
        DC_QUIT;
    }

    //
    // Check for complexity of clipping
    //
    switch (pco->iDComplexity)
    {
        case DC_TRIVIAL:
        case DC_RECT:
            //
            // Trivial (ignore clipping) or simple (one square) clipping -
            // no worries.
            //
            TRACE_OUT(( "Simple clipping"));
            DC_QUIT;

        default:
            TRACE_OUT(( "Clipping is complex"));
            break;
    }

    //
    // Failed all tests - must be too complicated.
    //
    rc = FALSE;

DC_EXIT_POINT:
    DebugExitDWORD(OEClippingIsSimple, rc);
    return(rc);
}


//
// Function:    OEClippingIsComplex
//
// Description: Check to see if the clipping on the graphics object is too
//              complicated to be sent as an order or multiple orders.
//
// Parameters:  pco - CLIPOBJ of the graphics object to be checked.
//
// Returns:     TRUE  - Clipping is too complicated
//              FALSE - Clipping is sufficiently simple to send as orders
//
BOOL  OEClippingIsComplex(CLIPOBJ* pco)
{
    BOOL       rc         = FALSE;
    BOOL       fMoreRects;
    OE_ENUMRECTS clip;
    UINT       numRects = 0;

    DebugEntry(OEClippingIsComplex);

    //
    // If the any of the following are true, the clipping is not too
    // complicated.
    //
    //  1) The clip object does not exist.
    //  2) The clipping is trivial (the object exists, but there are no
    //     clipping rectangles).
    //  3) The clipping is a single rectangle.
    //  4) The object enumerates to less than 'n' rectangles.
    //

    //
    // Check for a valid clip object
    //
    if (pco == NULL)
    {
        TRACE_OUT(( "No clipobj"));
        DC_QUIT;
    }

    //
    // Check for complexity of clipping
    //
    switch (pco->iDComplexity)
    {
        case DC_TRIVIAL:
        case DC_RECT:
            //
            // Trivial or simple clipping - no worries.
            //
            TRACE_OUT(( "Simple clipping"));
            DC_QUIT;

        case DC_COMPLEX:
            //
            // Lots of rectangles - make sure that it is less than the
            // acceptable limit.
            // The documentation for this function incorrectly states that
            // the returned value is the total number of rectangles
            // comprising the clip region. In fact, -1 is always returned,
            // even when the final parameter is non-zero. This means we
            // have to enumerate to get the number of rects.
            //
            CLIPOBJ_cEnumStart(pco,
                               FALSE,
                               CT_RECTANGLES,
                               CD_ANY,
                               0);

            //
            // MSDN: It is possible for CLIPOBJ_bEnum to return TRUE with
            // the number of clipping rectangles equal to zero. In such
            // cases, the driver should call CLIPOBJ_bEnum again without
            // taking any action. Get as many rectangles as we permit for
            // order encoding - this loop should execute once only.
            // If the number of rects equals COMPLEX_CLIP_RECT_COUNT the
            // 1st invocation of CLIPOBJ_bEnum returns that there are more
            // rects and a second call returns there are no more without
            // returning any in addition to those returned on the first
            // call. Our buffer has space for COMPLEX_CLIP_RECT_COUNT+1
            // rects so we should never have to execute the loop more than
            // once.
            //
            do
            {
                fMoreRects = CLIPOBJ_bEnum(pco,
                                           sizeof(clip),
                                           (ULONG *)&clip.rects);
                numRects += clip.rects.c;
            } while ( fMoreRects && (numRects <= COMPLEX_CLIP_RECT_COUNT) );

            //
            // If there are no more rectangles in the clip region then the
            // clipping complexity is within our limits for order encoding.
            //
            if ( numRects <= COMPLEX_CLIP_RECT_COUNT )
            {
                TRACE_OUT(( "Acceptable clipping %u", numRects));
                DC_QUIT;
            }
            break;

        default:
            ERROR_OUT(( "Unknown clipping"));
            break;
    }

    //
    // Failed all tests - must be too complicated.
    //
    TRACE_OUT(( "Complex clipping"));
    rc = TRUE;

DC_EXIT_POINT:
    DebugExitDWORD(OEClippingIsComplex, rc);
    return(rc);
}


//
// Function:    OEAccumulateOutput
//
// Description: Check to see if we should accumulate this output for
//              sending to the remote machine.
//
// Parameters:  pso   - Pointer to the target surface
//              pco   - Pointer to the clip object (may be NULL)
//              pRect - Pointer to the bounding rectangle of the operation
//
// Returns:     TRUE  - We should accumulate the output
//              FALSE - ignore the output
//
BOOL   OEAccumulateOutput(SURFOBJ* pso, CLIPOBJ *pco, LPRECT pRect)
{
    BOOL    rc = FALSE;
    POINT   pt = {0,0};
    ENUMRECTS clipRect;
    LPOSI_PDEV ppdev = ((LPOSI_PDEV)pso->dhpdev);

    DebugEntry(OEAccumulateOutput);

    //
    // Validate we have valid parameters to access the surface.
    //
    if (ppdev == NULL)
    {
        TRACE_OUT(( "NULL PDEV"));
        DC_QUIT;
    }

    //
    // Check for the screen surface, which will be a bitmap in the hosting
    // only code.
    //
    if (ppdev->hsurfScreen != pso->hsurf)
    {
        TRACE_OUT(( "Dest is not our surface"));
        DC_QUIT;
    }

    if (pso->dhsurf == NULL)
    {
        ERROR_OUT(( "NULL hSurf"));
        DC_QUIT;
    }

    //
    // Extract a single point from the clip object
    //
    if (pco == NULL)
    {
        //
        // No clip object - use a point from the bounding rectangle
        //
        pt.x = pRect->left;
        pt.y = pRect->top;
        TRACE_OUT(( "No clip object, point is %d, %d", pt.x, pt.y));
    }
    else if (pco->iDComplexity == DC_TRIVIAL)
    {
        //
        // Trivial clip object - use a point from the bounding rectangle
        //
        pt.x = pRect->left;
        pt.y = pRect->top;
        TRACE_OUT(( "Trivial clip object, point is %d, %d", pt.x, pt.y));
    }
    else if (pco->iDComplexity == DC_RECT)
    {
        //
        // Single clip rectangle - use a point from it
        //
        // It appears that the clip rectangle is frequantly the entire
        // display.  This is about as much use as a chocolate teapot.  If
        // this is the case, use a point from the bounding rectangle
        // instead.
        //
        if ((pco->rclBounds.left == 0) && (pco->rclBounds.top == 0))
        {
            pt.x = pRect->left;
            pt.y = pRect->top;
            TRACE_OUT(( "Meaningless clip rect, point is %d, %d",
                    pt.x, pt.y));
        }
        else
        {
            pt.x = pco->rclBounds.left;
            pt.y = pco->rclBounds.top;
            TRACE_OUT(( "Single clip rect, point is %d, %d", pt.x, pt.y));
        }
    }
    else
    {
        //
        // Complex clip object - enumerate its first rectangle and use a
        // point from that.
        //
        TRACE_OUT(( "Complex clip rect - call cEnumStart"));
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        clipRect.c = 1;
        memset(clipRect.arcl, 0, sizeof(RECTL));
        TRACE_OUT(( "Complex clip rect - call bEnum"));
        CLIPOBJ_bEnum(pco, sizeof(clipRect), (ULONG *)(&clipRect));

        pt.x = clipRect.arcl[0].left;
        pt.y = clipRect.arcl[0].top;
        TRACE_OUT(( "Complex clip rect, point is %d, %d", pt.x, pt.y));
    }

    //
    // Check if we are accumulating this window.
    //
    rc = HET_DDOutputIsHosted(pt);

DC_EXIT_POINT:
    TRACE_OUT(("OEAccumulateOutput:  point {%d, %d} is %sshared",
        pt.x, pt.y, (rc ? "" : "NOT ")));
    DebugExitBOOL(OEAccumulateOutput, rc);
    return(rc);
}


//
// Function:    OEAccumulateOutputRect
//
// Description: Check to see if we should accumulate the given output rect
//              for sending to the remote machine.
//
//              Most drawing functions will use OEAccumulateOutput, which
//              just checks for a single point within the hosted area.
//              This function checks for any part of the given rectangle
//              intersecting with the hosted area.  It is currently only
//              used by DrvSaveScreenBitmap - operations which may not
//              lie completetely within the hosted area.
//
// Parameters:  pso   - Pointer to the target surface
//              pRect - Pointer to the bounding rectangle of the operation
//
// Returns:     TRUE  - We should accumulate the output
//              FALSE - ignore the output
//
BOOL   OEAccumulateOutputRect( SURFOBJ* pso, LPRECT pRect)
{
    BOOL    rc = FALSE;
    LPOSI_PDEV ppdev = ((LPOSI_PDEV)pso->dhpdev);

    DebugEntry(OEAccumulateOutputRect);

    //
    // Validate we have valid parameters to access the surface.
    //
    if (ppdev == NULL)
    {
        TRACE_OUT(( "NULL PDEV"));
        DC_QUIT;
    }

    //
    // Check for the screen surface, which will be a bitmap in the hosting
    // only code.
    //
    if (ppdev->hsurfScreen != pso->hsurf)
    {
        TRACE_OUT(( "Dest is not our surface"));
        DC_QUIT;
    }

    if (pso->dhsurf == NULL)
    {
        ERROR_OUT(( "NULL hSurf"));
        DC_QUIT;
    }

    //
    // Check if we are accumulating this window.
    //
    rc = HET_DDOutputRectIsHosted(pRect);

DC_EXIT_POINT:
    TRACE_OUT(("OEAccumulateOutputRect:  rect {%d, %d, %d, %d} is %sshared",
        pRect->left, pRect->top, pRect->right, pRect->bottom,
        (rc ? "" : "NOT ")));
    DebugExitBOOL(OEAccumulateOutputRect, rc);
    return(rc);
}


//
// Function:    OESendRop3AsOrder
//
// Description: Check if we are allowed to send this 3-way ROP.  A ROP may
//              be disallowed if it relies on the destination data.
//
// Parameters:  rop3 - the 3-way ROP to be checked.
//
// Returns:     TRUE  - We are allowed to send this ROP
//              FALSE - We can't send this ROP
//
BOOL  OESendRop3AsOrder(BYTE rop3)
{
    BOOL   rc = TRUE;

    DebugEntry(OESendRop3AsOrder);

    //
    // Rop 0x5F is used by MSDN to highlight search keywords.  This XORs
    // a pattern with the destination, producing markedly different (and
    // sometimes unreadable) shadow output.  We special-case no-encoding for
    // it.
    //
    if (rop3 == 0x5F)
    {
        TRACE_OUT(("Rop3 0x5F never encoded"));
        rc = FALSE;
    }

    DebugExitBOOL(OESendRop3AsOrder, rc);
    return(rc);
}




//
// Function:    OECheckFontIsSupported
//
// Description: Check if we are allowed to send this font.  Fonts are
//              disallowed while they are being negotiated on a new entry
//              to the share.
//
// Parameters:  pfo           - (IN)  the font to be checked
//              pFontText     - (IN)  text message to be sent
//              textLen       - (IN)  length of text message
//              pFontHeight   - (OUT) font height in points
//              pFontAscender - (OUT) font ascender in points
//              pFontWidth    - (OUT) ave font width in points
//              pFontWeight   - (OUT) font weight
//              pFontFlags    - (OUT) font style flags
//              pFontIndex    - (OUT) font table index
//              pSendDeltaX   - (OUT) Do we need to send delta X coords?
//
// Returns:     TRUE  - We are allowed to send this font
//              FALSE - We can't send this font
//
BOOL   OECheckFontIsSupported
(
    FONTOBJ*    pfo,
    LPSTR       pFontText,
    UINT        textLen,
    LPUINT      pFontHeight,
    LPUINT      pFontAscender,
    LPUINT      pFontWidth,
    LPUINT      pFontWeight,
    LPUINT      pFontFlags,
    LPUINT      pFontIndex,
    LPBOOL      pSendDeltaX
)
{
    BOOL            rc = FALSE;
    PIFIMETRICS     pFontMetrics;
    UINT            codePage;
    UINT            i;
    UINT            iLocal;
    UINT            matchQuality;
    UINT            charWidthAdjustment = 0;
    char            fontName[FH_FACESIZE];
    ULONG           fontNameLen;
    PWSTR           pUnicodeString;
    XFORMOBJ*       pxform;
    POINTL          xformSize[3];
    int             compareResult;
    FLOATOBJ_XFORM  xformFloatData;

    DebugEntry(OECheckFontIsSupported);

    //
    // Set up default return values
    //
    *pSendDeltaX = FALSE;

    //
    // Check that we have a valid list of font data from the remotes.
    //
    if (!g_oeTextEnabled)
    {
        TRACE_OUT(( "Fonts unavailable"));
        DC_QUIT;
    }

    //
    // Check for valid font attributes
    //
    pFontMetrics = FONTOBJ_pifi(pfo);
    if (pFontMetrics->fsSelection & FM_SEL_OUTLINED)
    {
        TRACE_OUT(( "Unsupported font style"));
        DC_QUIT;
    }

    //
    // The current protocol cannot apply a general 2-D transform to text
    // orders, so we must reject any weird ones such as:
    //
    // - rotations
    // - X or Y shears
    // - X or Y reflections
    // - scaling with a negative value.
    //
    // Or put another way, we only allow:
    //
    // - the identity transformation
    // - scaling with a positive value.
    //
    pxform = FONTOBJ_pxoGetXform(pfo);
    if (pxform != NULL)
    {
        //
        // Get the details of the transformation.  Note we can ignore the
        // translation vector as it does not affect the font sizing /
        // orientation, so we are only interested in the matrix values...
        //

        //
        // NOTE:  Do NOT use floating point explicitly!
        // Can't do float ops in ring 0 with normal lib for x86.
        // Use FLOATOBJs instead and corresponding Eng services.
        // On alpha, these are macros and are way fast in any case.
        //

        if (XFORMOBJ_iGetFloatObjXform(pxform, &xformFloatData) != DDI_ERROR)
        {
            //
            // Rotations and shears will have cross dependencies on the x
            // and y components.
            //
            if ( (!FLOATOBJ_EqualLong(&xformFloatData.eM12, 0)) ||
                 (!FLOATOBJ_EqualLong(&xformFloatData.eM21, 0)) )
            {
                TRACE_OUT(( "Rejected rotn/shear"));
                DC_QUIT;
            }

            //
            // Reflections and scaling operations with negative scale
            // factors will have negative values on the leading diagonal of
            // the matrix.
            //
            if ( (FLOATOBJ_LessThanLong(&xformFloatData.eM11, 0)) ||
                 (FLOATOBJ_LessThanLong(&xformFloatData.eM22, 0)) )
            {
                TRACE_OUT(( "Rejected refln/-ive"));
                DC_QUIT;
            }
        }
    }

    //
    // Get the current font code page for font matching.
    //
    switch (pFontMetrics->jWinCharSet)
    {
        case ANSI_CHARSET:
            TRACE_OUT(( "ANSI font"));
            codePage = NF_CP_WIN_ANSI;
            break;

        case OEM_CHARSET:
            TRACE_OUT(( "OEM font"));
            codePage = NF_CP_WIN_OEM;
            break;

        case SYMBOL_CHARSET:
            TRACE_OUT(("Symbol font"));
            codePage = NF_CP_WIN_SYMBOL;
            break;

        default:
            TRACE_OUT(( "Unknown CP %d", pFontMetrics->jWinCharSet));
            codePage = NF_CP_UNKNOWN;
            break;
    }

    //
    // Get the name of the font.
    //
    pUnicodeString = (PWSTR)( (LPBYTE)pFontMetrics +
                                        pFontMetrics->dpwszFamilyName );
    EngUnicodeToMultiByteN(fontName,
                           sizeof(fontName),
                           &fontNameLen,
                           pUnicodeString,
                           OEUnicodeStrlen(pUnicodeString));

    //
    // Search our Font Alias Table for the current family name.  If we find
    // it, replace it with the alias name from the table.
    //
    for (i = 0; i < NUM_ALIAS_FONTS; i++)
    {
        if (!strcmp((LPSTR)fontName,
                        (LPSTR)(fontAliasTable[i].pszOriginalFontName)))
        {
            TRACE_OUT(( "Alias name: %s -> %s",
                              (LPSTR)fontName,
                              (LPSTR)(fontAliasTable[i].pszAliasFontName)));
            strcpy((LPSTR)fontName,
                   (LPSTR)(fontAliasTable[i].pszAliasFontName));
            charWidthAdjustment = fontAliasTable[i].charWidthAdjustment;
            break;
        }
    }

    TRACE_OUT(( "Font name: '%s'", fontName));

    //
    // We have a font name to match with those we know to be available
    // remotely.  Try to jump straight to the first entry in the local font
    // table starting with the same character as this font.  If this index
    // slot is empty (i.e.  has a value of USHRT_MAX) then the loop will
    // immediately exit
    //
    TRACE_OUT(( "Looking for matching fonts"));

    for (iLocal = g_oeLocalFontIndex[(BYTE)fontName[0]];
         iLocal < g_oeNumFonts;
         iLocal++)
    {
        TRACE_OUT(( "Trying font number %hd", iLocal));

        //
        // If this font is not supported remotely then skip it.
        //
        ASSERT(g_poeLocalFonts);
        matchQuality = g_poeLocalFonts[iLocal].SupportCode;
        if (matchQuality == FH_SC_NO_MATCH)
        {
            continue;
        }

        //
        // See if we've got a facename match
        //
        compareResult =
                 strcmp(g_poeLocalFonts[iLocal].Details.nfFaceName, fontName);

        if (compareResult < 0)
        {
            //
            // We haven't found a match yet, but we haven't gone far enough
            // into this list.
            //
            continue;
        }
        else if (compareResult > 0)
        {
            //
            // We're past the part of the local font array that's applicable.
            // We didn't find a match, it must not exist.
            //
            break;
        }

        //
        // The font names match.  Now see if the other attributes do...
        //

        //
        // This is looking promising - a font with the right name is
        // supported on the remote system.
        //
        // Start building up the details in the global variables while
        // making further checks...
        //
        *pFontFlags  = 0;
        *pFontIndex = iLocal;
        *pFontWeight = pFontMetrics->usWinWeight;

        //
        // Check for a fixed pitch font.
        //
        if ((pFontMetrics->jWinPitchAndFamily & FIXED_PITCH) != 0)
        {
            *pFontFlags |= NF_FIXED_PITCH;
        }

        //
        // Is it a TrueType font?
        //
        if ((pfo->flFontType & TRUETYPE_FONTTYPE) != 0)
        {
            *pFontFlags |= NF_TRUE_TYPE;
        }

        //
        // Get the basic width and height.
        //
        xformSize[0].y = 0;
        xformSize[0].x = 0;
        xformSize[1].y = pFontMetrics->fwdUnitsPerEm;
        xformSize[1].x = pFontMetrics->fwdAveCharWidth;
        xformSize[2].y = pFontMetrics->fwdWinAscender;
        xformSize[2].x = 0;

        //
        // We now need to convert these sizes if the GDI has provided a
        // transform object.
        //
        if (pxform != NULL)
        {
            if (!XFORMOBJ_bApplyXform(pxform,
                                      XF_LTOL,
                                      3,
                                      &xformSize,
                                      &xformSize))
            {
                ERROR_OUT(( "Xform failed"));
                continue;
            }
        }

        //
        // Calculate the font width and height.
        //
        *pFontHeight = (UINT)(xformSize[1].y - xformSize[0].y);
        *pFontWidth  = (UINT)(xformSize[1].x - xformSize[0].x
                                                 - charWidthAdjustment);

        TRACE_OUT(( "Device font size %hdx%hd", *pFontWidth, *pFontHeight));

        //
        // Get the offset to the start of the text cell.
        //
        *pFontAscender = (UINT)(xformSize[2].y - xformSize[0].y);

        //
        // Check that we have a matching pair - where we require that the
        // fonts (ie the one being used by the application and the one
        // we've matched with the remote system) are the same pitch (ie
        // variable or fixed) and use the same technology (ie TrueType or
        // not).
        //
        if ((g_poeLocalFonts[iLocal].Details.nfFontFlags & NF_FIXED_PITCH) !=
                ((TSHR_UINT16)(*pFontFlags) & NF_FIXED_PITCH))
        {
            TRACE_OUT(( "Fixed pitch mismatch"));
            continue;
        }
        if ((g_poeLocalFonts[iLocal].Details.nfFontFlags & NF_TRUE_TYPE) !=
                ((TSHR_UINT16)*pFontFlags & NF_TRUE_TYPE))
        {
            TRACE_OUT(( "True type mismatch"));
            continue;
        }

        //
        // We have a pair of fonts with the same attributes - either both
        // fixed pitch or both variable pitch - and using the same font
        // technology.
        //
        // If the font is fixed pitch then we must also check that this
        // particular size matches.
        //
        // If the font is not fixed pitch (scalable) then we assume that it
        // is matchable.
        //
        if (g_poeLocalFonts[iLocal].Details.nfFontFlags & NF_FIXED_SIZE)
        {
            //
            // The font is fixed size, so we must check that this
            // particular size is matchable.
            //
            if ( (*pFontHeight != g_poeLocalFonts[iLocal].Details.nfAveHeight) ||
                 (*pFontWidth  != g_poeLocalFonts[iLocal].Details.nfAveWidth)  )
            {
                //
                // The sizes differ, so we must fail this match.
                //
                TRACE_OUT(( "Size mismatch"));
                continue;
            }
        }

        //
        // Hey! We've got a matched pair!
        //
        rc = TRUE;
        TRACE_OUT(( "Found match at local font %hd", iLocal));
        break;
    }

    if (rc != TRUE)
    {
        TRACE_OUT(( "Couldn't find matching font in table"));
        DC_QUIT;
    }

    //
    // Build up the rest of the font flags.  We have already put the pitch
    // flag in place.
    //
    if ( ((pFontMetrics->fsSelection & FM_SEL_ITALIC) != 0) ||
         ((pfo->flFontType           & FO_SIM_ITALIC) != 0) )
    {
        TRACE_OUT(( "Italic"));
        *pFontFlags |= NF_ITALIC;
    }
    if ((pFontMetrics->fsSelection & FM_SEL_UNDERSCORE) != 0)
    {
        TRACE_OUT(( "Underline"));
        *pFontFlags |= NF_UNDERLINE;
    }
    if ((pFontMetrics->fsSelection & FM_SEL_STRIKEOUT) != 0)
    {
        TRACE_OUT(( "Strikeout"));
        *pFontFlags |= NF_STRIKEOUT;
    }

    //
    // It is possible to have a font made bold by Windows, i.e.  the
    // standard font definition is not bold, but windows manipulates the
    // font data to create a bold effect.  This is marked by the
    // FO_SIM_BOLD flag.
    //
    // In this case we need to ensure that the font flags are marked as
    // bold according to the weight.
    //
    if ( ((pfo->flFontType & FO_SIM_BOLD) != 0)       &&
         ( pFontMetrics->usWinWeight      <  FW_BOLD) )
    {
        TRACE_OUT(( "Upgrading weight for a bold font"));
        *pFontWeight = FW_BOLD;
    }

    //
    // If the font is an exact match, or if it is an approximate match for
    // its entire range (0x00 to 0xFF) then send it happily.  If not...only
    // send chars within the range 0x20->0x7F ("true ASCII").
    //
    ASSERT(g_poeLocalFonts);
    if (codePage != g_poeLocalFonts[iLocal].Details.nfCodePage)
    {
        TRACE_OUT(( "Using different CP: downgrade to APPROX_ASC"));
        matchQuality = FH_SC_APPROX_ASCII_MATCH;
    }

    //
    // If we don't have an exact match, check the individual characters.
    //
    if ( (matchQuality != FH_SC_EXACT_MATCH ) &&
         (matchQuality != FH_SC_APPROX_MATCH) )
    {
        //
        // The approximate match is only valid if we use a font that
        // supports the ANSI character set.
        //
        if ((pFontMetrics->jWinCharSet & ANSI_CHARSET) != 0)
        {
            TRACE_OUT(( "Cannot do match without ANSI support"));
            DC_QUIT;
        }

        //
        // This font is not a good match across its entire range.  Check
        // that all chars are within the desired range.
        //
        for (i = 0; i < textLen; i++)
        {
            if ( (pFontText[i] == 0) ||
                 ( (pFontText[i] >= NF_ASCII_FIRST) &&
                   (pFontText[i] <= NF_ASCII_LAST)  )  )
            {
                continue;
            }

            //
            // Can only get here by finding a char outside our acceptable
            // range.
            //
            TRACE_OUT(( "found non ASCII char %x", pFontText[i]));
            DC_QUIT;
        }

    }

    //
    // We have a valid font. Now sort out delta X issues.
    //

    //
    // If we do not need to send delta X arrays then exit now.
    //
    if (!(g_oeFontCaps & CAPS_FONT_NEED_X_ALWAYS))
    {
        if (!(g_oeFontCaps & CAPS_FONT_NEED_X_SOMETIMES))
        {
            //
            // CAPS_FONT_NEED_X_SOMETIMES and CAPS_FONT_NEED_X_ALWAYS are
            // both not set so we can exit now.  (We do not need a delta X
            // array).
            //
            TRACE_OUT(( "Capabilities eliminated delta X"));
            DC_QUIT;
        }

        //
        // CAPS_FONT_NEED_X_SOMETIMES is set and CAPS_FONT_NEED_X_ALWAYS is
        // not set.  In this case whether we need a delta X is determined
        // by whether the font is an exact match or an approximate match
        // (because of either approximation of name, signature, or aspect
        // ratio).  We can only find this out after we have extracted the
        // font handle from the existing order.
        //
    }

    //
    // If the string is a single character (or less) then we can just
    // return.
    //
    if (textLen <= 1)
    {
        TRACE_OUT(( "String only %lu long", textLen));
        DC_QUIT;
    }

    //
    // Capabilities allow us to ignore delta X position if we have an exact
    // match.
    //
    if ((matchQuality & FH_SC_EXACT) != 0)
    {
        //
        // Exit immediately, providing that there is no override to always
        // send increments.
        //
        if (!(g_oeFontCaps & CAPS_FONT_NEED_X_ALWAYS))
        {
            TRACE_OUT(( "Font has exact match"));
            DC_QUIT;
        }
    }

    //
    // We have passed all the checks - we must send a delta X array.
    //
    TRACE_OUT(( "Must send delta X"));
    *pSendDeltaX = TRUE;

DC_EXIT_POINT:
    DebugExitDWORD(OECheckFontIsSupported, rc);
    return(rc);
}


//
// Function:    OELPtoVirtual
//
// Description: Adjusts window coordinates to virtual desktop coordinates.
//              Clips the result to [+32766, -32768].
//
// Parameters:  pPoints - Array of points to be converted
//              cPoints - Number of points to be converted
//
// Returns:     (none)
//
void  OELPtoVirtual
(
    LPPOINT aPts,
    UINT    cPts
)
{
    int         l;
    TSHR_INT16  s;

    DebugEntry(OELPtoVirtual);

    //
    // Convert to screen coordinates
    //
    while (cPts > 0)
    {
        //
        // Look for int16 overflow in the X coordinate
        //
        l = aPts->x;
        s = (TSHR_INT16)l;

        if (l == (int)s)
        {
            aPts->x = s;
        }
        else
        {
            //
            // HIWORD(l) will be 1 for positive overflow, 0xFFFF for
            // negative overflow.  Therefore we will get 0x7FFE or 0x8000
            // (+32766 or -32768).
            //
            aPts->x = 0x7FFF - HIWORD(l);
            TRACE_OUT(("adjusted X from %ld to %d", l, aPts->x));
        }

        //
        // Look for int16 overflow in the Y coordinate
        //
        l = aPts->y;
        s = (TSHR_INT16)l;

        if (l == (int)s)
        {
            aPts->y = s;
        }
        else
        {
            //
            // HIWORD(l) will be 1 for positive overflow, 0xFFFF for
            // negative overflow.  Therefore we will get 0x7FFE or 0x8000
            // (+32766 or -32768).
            //
            aPts->y = 0x7FFF - HIWORD(l);
            TRACE_OUT(("adjusted Y from %ld to %d", l, aPts->y));
        }

        //
        // Move on to the next point
        //
        --cPts;
        ++aPts;
    }

    DebugExitVOID(OELPtoVirtual);
}


//
// Function:    OELRtoVirtual
//
// Description: Adjusts RECT in window coordinates to virtual coordinates.
//              Clips the result to [+32766, -32768].
//
// Parameters:  pRects  - Array of rects to be converted
//              numRects  - Number of rects to be converted
//
// Returns:     (none)
//
// NB.  This function takes a Windows rectangle (exclusive coords) and
//      returns a DC-Share rectangle (inclusive coords).
//
void OELRtoVirtual
(
    LPRECT  aRects,
    UINT    cRects
)
{
    DebugEntry(OELRtoVirtual);

    //
    // Convert the points to screen coords, clipping to INT16s
    //
    OELPtoVirtual((LPPOINT)aRects, 2 * cRects);

    //
    // Make each rectangle inclusive
    //
    while (cRects > 0)
    {
        aRects->right--;
        aRects->bottom--;

        //
        // Move on to the next rect
        //
        cRects--;
        aRects++;
    }

    DebugExitVOID(OELRtoVirtual);
}


//
// Function:    OEClipAndAddOrder
//
// Description: Adds the order to the order buffer, splitting it up into
//              multiple orders if the clipping is complicated.  If we fail
//              to send the full order, we accumulate it in the SDA instead
//
// Parameters:  pOrder     - Order to be stored.
//              pExtraInfo - Pointer to extra data associated with the
//                           order.  This data depends on the order type,
//                           and may be NULL.
//              pco        - Clipping object for the area
//
// Returns:     (none)
//
void   OEClipAndAddOrder(LPINT_ORDER pOrder,
                                             void *    pExtraInfo,
                                             CLIPOBJ*   pco)
{
    BOOL             fOrderClipped;
    BOOL             fMoreRects;
    RECT             clippedRect;
    RECT             orderRect;
    LPINT_ORDER         pNewOrder;
    LPINT_ORDER         pLastOrder = NULL;
    OE_ENUMRECTS       clip;
    UINT             i;
    UINT             numRects = 0;

    DebugEntry(OEClipAndAddOrder);

    //
    // Convert the order rectangle passed in (in virtual co-ordinates) back
    // to screen co-ordinates.  It is going to be clipped against clip
    // rectangles returned to us in screen co-ordinates.
    //
    // Note that we also convert to exclusive coords here to make
    // comparison with the exclusive Windows coords easier.
    //
    orderRect.left   = pOrder->OrderHeader.Common.rcsDst.left;
    orderRect.top    = pOrder->OrderHeader.Common.rcsDst.top;
    orderRect.right  = pOrder->OrderHeader.Common.rcsDst.right + 1;
    orderRect.bottom = pOrder->OrderHeader.Common.rcsDst.bottom + 1;
    fOrderClipped    = FALSE;

    TRACE_OUT(( "orderRect: (%d,%d)(%d,%d)",
                 orderRect.left,
                 orderRect.top,
                 orderRect.right,
                 orderRect.bottom));

    //
    // Check if we have a clipping object at all.
    //
    if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
    {
        //
        // No clipping object - just use the bounds
        //
        clippedRect   = orderRect;
        fOrderClipped = TRUE;
        pLastOrder    = pOrder;
    }
    else if (pco->iDComplexity == DC_RECT)
    {
        //
        // One clipping rectangle - use it directly.
        //
        RECT_FROM_RECTL(clippedRect, pco->rclBounds);
        clippedRect.left   = max(clippedRect.left,   orderRect.left);
        clippedRect.bottom = min(clippedRect.bottom, orderRect.bottom);
        clippedRect.right  = min(clippedRect.right,  orderRect.right);
        clippedRect.top    = max(clippedRect.top,    orderRect.top);
        fOrderClipped = TRUE;
        pLastOrder     = pOrder;
    }
    else
    {
        //
        // OA can only cope as long as the orders are added in the same
        // order that they were allocated, so we need to do a little
        // shuffling here.
        //
        // We always keep one order outstanding (pLastOrder) and a flag to
        // indicate if it is valid (fOrderClipped).  The first time we find
        // a valid clipping rectangle, we set up pLastOrder and
        // fOrderClipped.  If we find we need to allocate a new order, we
        // request the memory for the new order (pNewOrder), add pLastOrder
        // and store pNewOrder in pLastOrder.
        //
        // Once we have finished enumerating the clipping rectangles, if
        // pLastOrder is valid, we add it in.
        //
        // Also, while we are adding all these orders, OA must not purge
        // the order heap otherwise we'll be left holding an invalid
        // pointer.
        //
        pNewOrder = pOrder;
        g_oaPurgeAllowed = FALSE;

        //
        // Multiple clipping rectangles - Enumerate all the rectangles
        // involved in this drawing operation.
        // The documentation for this function incorrectly states that
        // the returned value is the total number of rectangles
        // comprising the clip region. In fact, -1 is always returned,
        // even when the final parameter is non-zero.
        //
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        //
        // Get the clip rectangles. We fetch these into the clip buffer
        // which is big enough to get all the clip rectangles we expect + 1.
        // If the order runs across this number of clip rects or more then
        // we will already have decided to send it as screen data.
        // The clip rectangle fetching is contained within a loop because,
        // while we expect to call CLIPOBJ_bEnum once only, it is possible
        // for this functio to return zero rects and report that there are
        // more to fetch (according to MSDN).
        //
        do
        {
            fMoreRects = CLIPOBJ_bEnum(pco,
                                       sizeof(clip),
                                       (ULONG *)&clip.rects);

            //
            // The clipping object can decide that there are no more
            // rectangles and that this query has returned no rectangles,
            // so we must check for any valid data in the returned
            // rectangle list.
            //
            if (clip.rects.c == 0)
            {
                //
                // We didn't get any rects this time so go round again - if
                // we're finished, the loop termination condition will take
                // us out. CLIPOBJ_bEnum can return a count of zero when
                // there are still more rects.
                //
                TRACE_OUT(( "No rects this time, more %u", fMoreRects));
                continue;
            }

            //
            // To get to here we expect to have fetched all the rects and
            // no more. Do a quick check.
            //
            numRects += clip.rects.c;
            ASSERT( (numRects <= COMPLEX_CLIP_RECT_COUNT) );

            //
            // Process each clip rectangle by clipping the drawing order to
            // it.
            //
            for ( i = 0; i < clip.rects.c; i++ )
            {
                TRACE_OUT(( "  (%d,%d)(%d,%d)",
                             clip.rects.arcl[i].left,
                             clip.rects.arcl[i].top,
                             clip.rects.arcl[i].right,
                             clip.rects.arcl[i].bottom));

                //
                // Check for an intersection
                //
                if ( (clip.rects.arcl[i].left >= orderRect.right)  ||
                     (clip.rects.arcl[i].bottom <= orderRect.top)    ||
                     (clip.rects.arcl[i].right <= orderRect.left)   ||
                     (clip.rects.arcl[i].top >= orderRect.bottom) )
                {
                    //
                    // No intersection, move on to next clip rect.
                    //
                    continue;
                }

                //
                // There is an intersection, so we may need to add a new
                // order to the buffer to cater for this rectangle.
                //
                if (fOrderClipped)
                {
                    //
                    // The order has already been clipped once, so it
                    // actually intersects more than one clip rect, ie
                    // fOrderClipped is always FALSE for at least the first
                    // clip rectangle in the clip.rects buffer.  We cope
                    // with this by duplicating the order and clipping it
                    // again.
                    //
                    pNewOrder = OA_DDAllocOrderMem(
                         pLastOrder->OrderHeader.Common.cbOrderDataLength, 0);

                    if (pNewOrder == NULL)
                    {
                        WARNING_OUT(( "Order memory allocation failed" ));
                        goto CLIP_ORDER_FAILED;
                    }

                    //
                    // Copy the header & data from the original order to
                    // the new order (making sure that we don't overwrite
                    // the list information at the start of the header).
                    //
                    memcpy((LPBYTE)pNewOrder
                                    + FIELD_SIZE(INT_ORDER, OrderHeader.list),
                              (LPBYTE)pLastOrder
                                    + FIELD_SIZE(INT_ORDER, OrderHeader.list),
                              pLastOrder->OrderHeader.Common.cbOrderDataLength
                                    + sizeof(INT_ORDER_HEADER)
                                    - FIELD_SIZE(INT_ORDER, OrderHeader.list));

                    //
                    // Set the destination (clip) rectangle (in virtual
                    // desktop coordinates).
                    //
                    TSHR_RECT16_FROM_RECT(
                                       &pLastOrder->OrderHeader.Common.rcsDst,
                                       clippedRect);

                    pLastOrder->OrderHeader.Common.rcsDst.right -= 1;
                    pLastOrder->OrderHeader.Common.rcsDst.bottom -= 1;

                    TRACE_OUT(( "Adding duplicate order  (%d,%d) (%d,%d)",
                               pLastOrder->OrderHeader.Common.rcsDst.left,
                               pLastOrder->OrderHeader.Common.rcsDst.top,
                               pLastOrder->OrderHeader.Common.rcsDst.right,
                               pLastOrder->OrderHeader.Common.rcsDst.bottom));

                    //
                    // Add the order to the Order List.
                    //
                    OA_DDAddOrder(pLastOrder, pExtraInfo);
                }

                //
                // Update the clipping rectangle for the order to be sent.
                //
                clippedRect.left  = max(clip.rects.arcl[i].left,
                                           orderRect.left);
                clippedRect.bottom= min(clip.rects.arcl[i].bottom,
                                           orderRect.bottom);
                clippedRect.right = min(clip.rects.arcl[i].right,
                                           orderRect.right);
                clippedRect.top   = max(clip.rects.arcl[i].top,
                                           orderRect.top);
                fOrderClipped     = TRUE;
                pLastOrder        = pNewOrder;
            }
        } while (fMoreRects);
    }

    //
    // Check whether the clipping has removed the order entirely.
    //
    if (fOrderClipped)
    {
        TSHR_RECT16_FROM_RECT(&pLastOrder->OrderHeader.Common.rcsDst,
                                clippedRect);

        pLastOrder->OrderHeader.Common.rcsDst.right -= 1;
        pLastOrder->OrderHeader.Common.rcsDst.bottom -= 1;

        TRACE_OUT(( "Adding order  (%d,%d) (%d,%d)",
                    pLastOrder->OrderHeader.Common.rcsDst.left,
                    pLastOrder->OrderHeader.Common.rcsDst.top,
                    pLastOrder->OrderHeader.Common.rcsDst.right,
                    pLastOrder->OrderHeader.Common.rcsDst.bottom));

        //
        // Add the order to the Order List.
        //
        OA_DDAddOrder(pLastOrder, pExtraInfo);
    }
    else
    {

        TRACE_OUT(( "Order clipped completely"));
        OA_DDFreeOrderMem(pOrder);
    }

    DC_QUIT;


CLIP_ORDER_FAILED:
    //
    // Allocation of memory for a duplicate order failed.  Just add the
    // original order's destination rect into the SDA and free the order.
    //
    // The order rectangle is already in inclusive virtual coordinates.
    //
    TRACE_OUT(( "Order add failed, add to SDA"));
    RECT_FROM_TSHR_RECT16(&orderRect,pLastOrder->OrderHeader.Common.rcsDst);
    OA_DDFreeOrderMem(pLastOrder);
    BA_AddScreenData(&orderRect);

DC_EXIT_POINT:
    //
    // Make sure that we always re-enable heap purging.
    //
    g_oaPurgeAllowed = TRUE;

    DebugExitVOID(OEClipAndAddOrder);
}


//
// Function:    OEClipAndAddScreenData
//
// Description: Determines if we need to accumulate any screen data for the
//              specified area.  If so, it is added to the SDA.
//
// Parameters:  pRect - Bounding rectangle of area to be accumulated
//              pco   - Clipping object for the area
//
// Returns:     (none)
//
void   OEClipAndAddScreenData(LPRECT pRect, CLIPOBJ* pco)
{
    RECT    SDACandidate;
    BOOL    fMoreRects;
    RECT    clippedRect;
    OE_ENUMRECTS clip;
    UINT    i;

    DebugEntry(OEClipAndAddScreenData);

    //
    // Convert the order rectangle passed in (in virtual co-ordinates) back
    // to screen co-ordinates.  It is going to be clipped against clip
    // rectangles returned to us in screen co-ordinates.
    //
    // Note that we also convert to exclusive coords here to make
    // comparison with the exclusive Windows coords easier.
    //
    SDACandidate.left   = pRect->left;
    SDACandidate.top    = pRect->top;
    SDACandidate.right  = pRect->right + 1;
    SDACandidate.bottom = pRect->bottom + 1;

    TRACE_OUT(( "SDACandidate: (%d,%d)(%d,%d)",
                 SDACandidate.left,
                 SDACandidate.top,
                 SDACandidate.right,
                 SDACandidate.bottom));

    //
    // Check if we have a clipping object at all.
    //
    if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
    {
        //
        // Convert the clipped rect into Virtual Desktop coords.
        //
        clippedRect         = SDACandidate;
        clippedRect.right  -= 1;
        clippedRect.bottom -= 1;

        //
        // Add the clipped rect into the SDA.
        //
        TRACE_OUT(( "Adding SDA (%d,%d)(%d,%d)", clippedRect.left,
                                                  clippedRect.top,
                                                  clippedRect.right,
                                                  clippedRect.bottom));

        BA_AddScreenData(&clippedRect);
    }
    else if (pco->iDComplexity == DC_RECT)
    {
        //
        // One clipping rectangle - use it directly, converting into
        // Virtual Desktop coords. Make sure the rectangle is valid before
        // adding to the SDA.
        //
        RECT_FROM_RECTL(clippedRect, pco->rclBounds);
        clippedRect.left = max(clippedRect.left, SDACandidate.left);
        clippedRect.right = min(clippedRect.right, SDACandidate.right) + -1;

        if ( clippedRect.left <= clippedRect.right )
        {
            clippedRect.bottom = min(clippedRect.bottom,
                                        SDACandidate.bottom) + -1;
            clippedRect.top = max(clippedRect.top, SDACandidate.top);

            if ( clippedRect.bottom >= clippedRect.top )
            {
                //
                // Add the clipped rect into the SDA.
                //
                TRACE_OUT(( "Adding SDA RECT (%d,%d)(%d,%d)",
                                                         clippedRect.left,
                                                         clippedRect.top,
                                                         clippedRect.right,
                                                         clippedRect.bottom));
                BA_AddScreenData(&clippedRect);
            }
        }
    }
    else
    {
        //
        // Enumerate all the rectangles involved in this drawing operation.
        // The documentation for this function incorrectly states that
        // the returned value is the total number of rectangles
        // comprising the clip region. In fact, -1 is always returned,
        // even when the final parameter is non-zero.
        //
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        do
        {
            //
            // Get the next batch of clipping rectangles
            //
            fMoreRects = CLIPOBJ_bEnum(pco,
                                       sizeof(clip),
                                       (ULONG *)&clip.rects);

            for ( i = 0; i < clip.rects.c; i++ )
            {
                TRACE_OUT(( "  (%d,%d)(%d,%d)",
                             clip.rects.arcl[i].left,
                             clip.rects.arcl[i].top,
                             clip.rects.arcl[i].right,
                             clip.rects.arcl[i].bottom));

                //
                // Intersect the SDA rect with the clip rect, checking for
                // no intersection.
                //
                clippedRect.left  = max( clip.rects.arcl[i].left,
                                            SDACandidate.left );
                clippedRect.right = min( clip.rects.arcl[i].right,
                                            SDACandidate.right );

                if (clippedRect.left >= clippedRect.right)
                {
                    //
                    // No horizontal intersection.
                    //
                    continue;
                }

                clippedRect.bottom = min( clip.rects.arcl[i].bottom,
                                             SDACandidate.bottom );
                clippedRect.top    = max( clip.rects.arcl[i].top,
                                             SDACandidate.top );

                if (clippedRect.top >= clippedRect.bottom)
                {
                    //
                    // No vertical intersection.
                    //
                    continue;
                }

                //
                // Convert the clipped rect into Virtual Desktop coords.
                //
                clippedRect.right  -= 1;
                clippedRect.bottom -= 1;

                //
                // Add the clipped rect into the SDA.
                //
                TRACE_OUT(( "Adding SDA (%d,%d)(%d,%d)",
                             clippedRect.left,
                             clippedRect.top,
                             clippedRect.right,
                             clippedRect.bottom));

                BA_AddScreenData(&clippedRect);
            }
        } while (fMoreRects);
    }

    DebugExitVOID(OEClipAndAddScreenData);
}





//
// FUNCTION:    OEDDSetNewFonts
//
// DESCRIPTION:
//
// Set the new font handling information to be used by the display driver.
//
// RETURNS:
//
// NONE
//
//
void  OEDDSetNewFonts(LPOE_NEW_FONTS pRequest)
{
    UINT    cbNewSize;

    DebugEntry(OEDDSetNewFonts);

    TRACE_OUT(( "New fonts %d", pRequest->countFonts));

    //
    // Initialize new number of fonts to zero in case an error happens.
    // We don't want to use stale font info if so.
    //
    g_oeNumFonts = 0;

    g_oeFontCaps = pRequest->fontCaps;

    //
    // Free our previous font block if we had one.
    //
    if (g_poeLocalFonts)
    {
        EngFreeMem(g_poeLocalFonts);
        g_poeLocalFonts = NULL;
    }

    //
    // Alloc a new one, the size of the new font block.
    //
    cbNewSize = pRequest->countFonts * sizeof(LOCALFONT);
    g_poeLocalFonts = EngAllocMem(0, cbNewSize, OSI_ALLOC_TAG);
    if (! g_poeLocalFonts)
    {
        ERROR_OUT(("OEDDSetNewFonts: can't allocate space for font info"));
        DC_QUIT;
    }

    //
    // OK, if we're here, this is going to succeed.  Copy the info over.
    //
    g_oeNumFonts = pRequest->countFonts;

    memcpy(g_poeLocalFonts, pRequest->fontData, cbNewSize);

    memcpy(g_oeLocalFontIndex, pRequest->fontIndex,
              sizeof(g_oeLocalFontIndex[0]) * FH_LOCAL_INDEX_SIZE);

DC_EXIT_POINT:
    DebugExitVOID(OEDDSetNewFonts);
}


//
// FUNCTION:    OEDDSetNewCapabilities
//
// DESCRIPTION:
//
// Set the new OE related capabilities
//
// RETURNS:
//
// NONE
//
// PARAMETERS:
//
// pDataIn  - pointer to the input buffer
//
//
void  OEDDSetNewCapabilities(LPOE_NEW_CAPABILITIES pCapabilities)
{
    DebugEntry(OEDDSetNewCapabilities);

    //
    // Copy the data from the Share Core.
    //
    g_oeBaselineTextEnabled = pCapabilities->baselineTextEnabled;

    g_oeSendOrders          = pCapabilities->sendOrders;

    g_oeTextEnabled         = pCapabilities->textEnabled;

    //
    // The share core has passed down a pointer to it's copy of the order
    // support array.  We take a copy for the kernel here.
    //
    memcpy(g_oeOrderSupported,
              pCapabilities->orderSupported,
              sizeof(g_oeOrderSupported));

    TRACE_OUT(( "OE caps: BLT %c Orders %c Text %c",
                 g_oeBaselineTextEnabled ? 'Y': 'N',
                 g_oeSendOrders ? 'Y': 'N',
                 g_oeTextEnabled ? 'Y': 'N'));

    DebugExitVOID(OEDDSetNewCapabilities);
}


//
// Function:    OETileBitBltOrder
//
// Description: Divides a single large BitBlt order into a series of small,
//              "tiled" BitBlt orders, each of which is added to the order
//              queue.
//
// Parameters:  pOrder     - Template order to be tiled
//              pExtraInfo - Structure containing pointers to the source
//                           and destination surface objects, and a pointer
//                           to the color translation object for the Blt
//              pco        - Clipping object for the operation
//
// Returns:     TRUE - Stored in orders (and possibly some SDA)
//              FALSE- Stored in SDA (or contained bad data)
//
//
void   OETileBitBltOrder
(
    LPINT_ORDER                 pOrder,
    LPMEMBLT_ORDER_EXTRA_INFO   pExtraInfo,
    CLIPOBJ*                    pco
)
{
    UINT        tileWidth;
    UINT        tileHeight;
    int         srcLeft;
    int         srcTop;
    int         srcRight;
    int         srcBottom;
    int         xFirstTile;
    int         yFirstTile;
    int         xTile;
    int         yTile;
    UINT        type;
    int         bmpWidth, bmpHeight;
    RECT        destRect;

    DebugEntry(OETileBitBltOrder);

    //
    // Extract the src bitmap handle from the Order - if the order is not a
    // memory to screen blit, we get out now.
    //
    type = ((LPMEMBLT_ORDER)pOrder->abOrderData)->type;
    switch (type)
    {
        case ORD_MEMBLT_TYPE:
        {
            srcLeft   = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nXSrc;
            srcTop    = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nYSrc;
            srcRight  = srcLeft +
                       ((LPMEMBLT_ORDER)pOrder->abOrderData)->nWidth;
            srcBottom = srcTop +
                       ((LPMEMBLT_ORDER)pOrder->abOrderData)->nHeight;
            destRect.left  = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nLeftRect;
            destRect.top   = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nTopRect;
            destRect.right = destRect.left +
                ((LPMEMBLT_ORDER)pOrder->abOrderData)->nWidth;
            destRect.bottom= destRect.top +
                ((LPMEMBLT_ORDER)pOrder->abOrderData)->nHeight;
        }
        break;

        case ORD_MEM3BLT_TYPE:
        {
            srcLeft   = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nXSrc;
            srcTop    = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nYSrc;
            srcRight  = srcLeft +
                       ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nWidth;
            srcBottom = srcTop +
                       ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nHeight;

            destRect.left = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nLeftRect;
            destRect.top  = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nTopRect;
            destRect.right= destRect.left +
                            ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nWidth;
            destRect.bottom = destRect.top +
                            ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nHeight;
        }
        break;

        default:
        {
            ERROR_OUT(( "Invalid order type %u", type));
        }
        break;
    }

    //
    // Fetch the bitmap details.
    //
    bmpWidth  = (int)pExtraInfo->pSource->sizlBitmap.cx;
    bmpHeight = (int)pExtraInfo->pSource->sizlBitmap.cy;

    if (!SBC_DDQueryBitmapTileSize(bmpWidth, bmpHeight, &tileWidth, &tileHeight))
    {
        //
        // This could happen if some 2.x user joins the share.
        //
        TRACE_OUT(("Bitmap is not tileable"));
        OEClipAndAddScreenData(&destRect, pco);
    }
    else
    {
        //
        // Tile the order.  If an individual tile fails to get queued as an
        // order, OEAddTiledBitBltOrder() will add it as screen data.  Hence
        // no return value to be checked.
        //
        xFirstTile = srcLeft - (srcLeft % tileWidth);
        yFirstTile = srcTop - (srcTop % tileHeight);

        for (yTile = yFirstTile; yTile < srcBottom; yTile += tileHeight)
        {
            for (xTile = xFirstTile; xTile < srcRight; xTile += tileWidth)
            {
                OEAddTiledBitBltOrder(pOrder, pExtraInfo, pco, xTile, yTile,
                    tileWidth,  tileHeight);
            }
        }
    }

    DebugExitVOID(OETileBitBltOrder);
}



//
// Function:    OEAddTiledBitBltOrder
//
// Description: Takes an unmodified "large" BitBlt and a tile rectangle,
//              makes a copy of the order and modifies the copied order's
//              src/dest so it applies to the source tile only. The order
//              is added to the order queue.  If the allocation of the
//              "tiled" order fails, the destination rect is added to SDA
//
// Parameters:  pOrder     - Template order to be added
//              pExtraInfo - Pointer to the extra BitBlt info
//              pco        - Clipping object for the BitBlt
//              xTile      - X position of the tile
//              yTile      - Y position of the tile
//              tileWidth  - tile width
//              tileHeight - tile height
//
// Returns:     none
//
//
void   OEAddTiledBitBltOrder(
                                         LPINT_ORDER               pOrder,
                                         LPMEMBLT_ORDER_EXTRA_INFO pExtraInfo,
                                         CLIPOBJ*                 pco,
                                         int                  xTile,
                                         int                  yTile,
                                         UINT                 tileWidth,
                                         UINT                 tileHeight)
{
    LPINT_ORDER pTileOrder;
    LPINT  pXSrc   = NULL;
    LPINT  pYSrc   = NULL;
    LPINT  pLeft   = NULL;
    LPINT  pTop    = NULL;
    LPINT  pWidth  = NULL;
    LPINT  pHeight = NULL;
    RECT    srcRect;
    RECT    destRect;
    UINT  type;

    DebugEntry(OETileAndAddBitBltOrder);

    //
    // This is a trusted interface - assume the type is correct
    //
    type = ((LPMEMBLT_ORDER)pOrder->abOrderData)->type;
    ASSERT(((type == ORD_MEMBLT_TYPE) || (type == ORD_MEM3BLT_TYPE)));

    //
    // Do processing which depends on the type of bit blt being tiled:
    // - save existing src and dest rects
    // - make a copy of the order (which will be the tile order)
    // - save pointers to the fields in the tile order which we're likely
    //   to change.
    //
    if (type == ORD_MEMBLT_TYPE)
    {
        srcRect.left  = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nXSrc;
        srcRect.top   = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nYSrc;
        srcRect.right = srcRect.left +
                        ((LPMEMBLT_ORDER)pOrder->abOrderData)->nWidth;
        srcRect.bottom = srcRect.top +
                        ((LPMEMBLT_ORDER)pOrder->abOrderData)->nHeight;
        destRect.left = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nLeftRect;
        destRect.top  = ((LPMEMBLT_ORDER)pOrder->abOrderData)->nTopRect;

        //
        // We must allocate enough space for the maximum size order that
        // SBC may use (i.e.  an R2 order).  We default to filling in the
        // data as an R1 order.
        //
        pTileOrder = OA_DDAllocOrderMem(sizeof(MEMBLT_R2_ORDER),0);
        if (pTileOrder == NULL)
        {
            TRACE_OUT(( "No space for tile order"));
            DC_QUIT;
        }

        //
        // We must not mess up the linked list data in the orders.
        //
        RtlCopyMemory(((LPBYTE)pTileOrder) +
                                       FIELD_SIZE(INT_ORDER, OrderHeader.list),
                      ((LPBYTE)pOrder)     +
                                       FIELD_SIZE(INT_ORDER, OrderHeader.list),
                      sizeof(INT_ORDER_HEADER)
                                    + sizeof(MEMBLT_R2_ORDER)
                                    - FIELD_SIZE(INT_ORDER, OrderHeader.list));

        pXSrc   = &((LPMEMBLT_ORDER)pTileOrder->abOrderData)->nXSrc;
        pYSrc   = &((LPMEMBLT_ORDER)pTileOrder->abOrderData)->nYSrc;
        pWidth  = &((LPMEMBLT_ORDER)pTileOrder->abOrderData)->nWidth;
        pHeight = &((LPMEMBLT_ORDER)pTileOrder->abOrderData)->nHeight;
        pLeft   = &((LPMEMBLT_ORDER)pTileOrder->abOrderData)->nLeftRect;
        pTop    = &((LPMEMBLT_ORDER)pTileOrder->abOrderData)->nTopRect;
    }
    else
    {
        srcRect.left  = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nXSrc;
        srcRect.top   = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nYSrc;
        srcRect.right = srcRect.left +
                        ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nWidth;
        srcRect.bottom = srcRect.top +
                        ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nHeight;
        destRect.left = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nLeftRect;
        destRect.top  = ((LPMEM3BLT_ORDER)pOrder->abOrderData)->nTopRect;

        //
        // We must allocate enough space for the maximum size order that
        // SBC may use (i.e.  an R2 order).  We default to filling in the
        // data as an R1 order.
        //
        pTileOrder = OA_DDAllocOrderMem(sizeof(MEM3BLT_R2_ORDER),0);
        if (pTileOrder == NULL)
        {
            TRACE_OUT(( "No space for tile order"));
            DC_QUIT;
        }

        //
        // We must not mess up the linked list data in the orders.
        //
        RtlCopyMemory(((LPBYTE)pTileOrder) +
                                       FIELD_SIZE(INT_ORDER, OrderHeader.list),
                      ((LPBYTE)pOrder)     +
                                       FIELD_SIZE(INT_ORDER, OrderHeader.list),
                      sizeof(INT_ORDER_HEADER)
                                    + sizeof(MEM3BLT_R2_ORDER)
                                    - FIELD_SIZE(INT_ORDER, OrderHeader.list));

        pXSrc   = &((LPMEM3BLT_ORDER)pTileOrder->abOrderData)->nXSrc;
        pYSrc   = &((LPMEM3BLT_ORDER)pTileOrder->abOrderData)->nYSrc;
        pWidth  = &((LPMEM3BLT_ORDER)pTileOrder->abOrderData)->nWidth;
        pHeight = &((LPMEM3BLT_ORDER)pTileOrder->abOrderData)->nHeight;
        pLeft   = &((LPMEM3BLT_ORDER)pTileOrder->abOrderData)->nLeftRect;
        pTop    = &((LPMEM3BLT_ORDER)pTileOrder->abOrderData)->nTopRect;
    }

    TRACE_OUT(( "Tiling order, orig srcLeft=%hd, srcTop=%hd, srcRight=%hd, "
           "srcBottom=%hd, destX=%hd, destY=%hd; "
           "xTile=%hd, yTile=%hd, tileW=%hd, tileH=%hd",
           srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
           destRect.left, destRect.top,
           xTile, yTile, tileWidth, tileHeight));

DC_EXIT_POINT:
    //
    // NOTE: ALL THE POINTERS MAY BE NULL AT THIS POINT - DO NOT USE THEM
    // UNTIL YOU VERIFY PTILEORDER IS NON-NULL.
    //
    // Intersect source and tile rects, and set up destination rect
    // accordingly - we need to do this even if we failed to copy the
    // order, because the tiled source rect will have to be added to the
    // screen data area.
    //
    if (xTile > srcRect.left)
    {
        destRect.left += (xTile - srcRect.left);
        srcRect.left = xTile;
    }

    if (yTile > srcRect.top)
    {
        destRect.top += (yTile - srcRect.top);
        srcRect.top = yTile;
    }

    srcRect.right  = min((UINT)srcRect.right, xTile + tileWidth);
    srcRect.bottom = min((UINT)srcRect.bottom, yTile + tileHeight);

    destRect.right  = destRect.left + (srcRect.right - srcRect.left);
    destRect.bottom = destRect.top + (srcRect.bottom - srcRect.top);

    //
    // If the order was successfully copied above, then modify the order
    // to contain the tiled coordinates, and add it to the order list.
    // Otherwise, send the dest rect as screen data.
    //
    if (pTileOrder != NULL)
    {
        TRACE_OUT(( "Tile order originally: srcX=%hd, srcY=%hd, destX=%hd, "
               "destY=%hd, w=%hd, h=%hd",
               *pXSrc, *pYSrc, *pLeft, *pTop, *pWidth, *pHeight));

        *pXSrc = srcRect.left;
        *pYSrc = srcRect.top;
        *pLeft = destRect.left;
        *pTop  = destRect.top;
        *pWidth = srcRect.right - srcRect.left;
        *pHeight = srcRect.bottom - srcRect.top;

        pTileOrder->OrderHeader.Common.rcsDst.left = (TSHR_INT16)destRect.left;
        pTileOrder->OrderHeader.Common.rcsDst.right = (TSHR_INT16)destRect.right;
        pTileOrder->OrderHeader.Common.rcsDst.top = (TSHR_INT16)destRect.top;
        pTileOrder->OrderHeader.Common.rcsDst.bottom =
                                                     (TSHR_INT16)destRect.bottom;

        TRACE_OUT(( "Adding order srcX=%hd, srcY=%hd, destX=%hd, destY=%hd,"
               " w=%hd, h=%hd",
               *pXSrc, *pYSrc, *pLeft, *pTop, *pWidth, *pHeight));
        OEClipAndAddOrder(pTileOrder, pExtraInfo, pco);
    }
    else
    {
        TRACE_OUT(( "Failed to allocate order - sending as screen data"));
        OEClipAndAddScreenData(&destRect, pco);
    }

    DebugExitVOID(OETileAndAddBitBltOrder);
}



// NAME:      OEAddLine
//
// PURPOSE:
//
// Add a LineTo order to the order heap.
//
// RETURNS:
//
// TRUE  - Attempted to add to heap
// FALSE - No room left to allocate an order
//
// PARAMS:
//
// ppdev      - display driver PDEV
// startPoint - start point of line
// endPoint   - end point of line
// rectDst    - bounding rectangle
// rop2       - ROP2 to use with line
// width      - width of line to add
// color      - color of line to add
// pco        - clipping object for drawing operation
//
BOOL  OEAddLine(LPOSI_PDEV ppdev,
                    LPPOINT  startPoint,
                    LPPOINT  endPoint,
                                    LPRECT   rectDst,
                                    UINT  rop2,
                                    UINT  width,
                                    UINT  color,
                                    CLIPOBJ*  pco)
{
    BOOL         rc = FALSE;
    LPLINETO_ORDER pLineTo;
    LPINT_ORDER     pOrder;

    DebugEntry(OEAddLine);

    //
    // Allocate the memory for the order.
    //
    pOrder = OA_DDAllocOrderMem(sizeof(LINETO_ORDER),0);
    if (pOrder == NULL)
    {
        TRACE_OUT(( "Failed to alloc order"));
        DC_QUIT;
    }
    pLineTo = (LPLINETO_ORDER)pOrder->abOrderData;

    //
    // Mark this order type.
    //
    pLineTo->type = ORD_LINETO_TYPE;

    //
    // Store the line end coordinates.
    //
    pLineTo->nXStart   = startPoint->x;
    pLineTo->nYStart   = startPoint->y;
    pLineTo->nXEnd     = endPoint->x;
    pLineTo->nYEnd     = endPoint->y;

    //
    // We must convert these values to virtual coords.
    //
    OELPtoVirtual((LPPOINT)&pLineTo->nXStart, 2);

    //
    // Always do solid lines, so it does not matter what we specify as the
    // back color.
    //
    RtlFillMemory(&pLineTo->BackColor,
                  sizeof(pLineTo->BackColor),
                  0);

    //
    // We only draw solid lines with no option as to what we do to the
    // background, so this is always transparent.
    //
    pLineTo->BackMode  = TRANSPARENT;

    //
    // Get the ROP value.
    //
    pLineTo->ROP2      = rop2;

    //
    // The NT Display Driver is only called to accelerate simple solid
    // lines.  So we only support pen styles of PS_SOLID.
    //
    pLineTo->PenStyle  = PS_SOLID;

    //
    // Get the pen width.
    //
    pLineTo->PenWidth = width;

    //
    // Set up the color.
    //
    OEConvertColor(ppdev,
                   &pLineTo->PenColor,
                   color,
                   NULL);

    TRACE_OUT(( "LineTo BC %02x%02x%02x BM %04X rop2 %02X "
                 "pen %04X %04X %02x%02x%02x x1 %d y1 %d x2 %d y2 %d",
            pLineTo->BackColor.red,
            pLineTo->BackColor.green,
            pLineTo->BackColor.blue,
            pLineTo->BackMode,
            pLineTo->ROP2,
            pLineTo->PenStyle,
            pLineTo->PenWidth,
            pLineTo->PenColor.red,
            pLineTo->PenColor.green,
            pLineTo->PenColor.blue,
            pLineTo->nXStart,
            pLineTo->nYStart,
            pLineTo->nXEnd,
            pLineTo->nYEnd));

    //
    // Store the general order data.  The bounding rectangle must be in to
    // virtual desktop co-ordinates.  OELRtoVirtual has already done this.
    //
    pOrder->OrderHeader.Common.fOrderFlags   = OF_SPOILABLE;
    pOrder->OrderHeader.Common.rcsDst.left   = (TSHR_INT16)rectDst->left;
    pOrder->OrderHeader.Common.rcsDst.right  = (TSHR_INT16)rectDst->right;
    pOrder->OrderHeader.Common.rcsDst.top    = (TSHR_INT16)rectDst->top;
    pOrder->OrderHeader.Common.rcsDst.bottom = (TSHR_INT16)rectDst->bottom;

    //
    // Store that order!
    //
    OEClipAndAddOrder(pOrder, NULL, pco);
    rc = TRUE;

DC_EXIT_POINT:
    DebugExitDWORD(OEAddLine, rc);
    return(rc);
}


// NAME:      OEEncodePatBlt
//
// PURPOSE:
//
// Attempts to encode a PatBlt order. This function allocates the memory
// for the encoded order (pointer returned in ppOrder). If the function
// completes successfully, it is the caller's responsibility to free this
// memory.
//
// RETURNS:
//
// TRUE  - Order encoded
// FALSE - Order not encoded (so add to SDA)
//
// PARAMS:
//
// ppdev       - display driver PDEV
// pbo         - brush object for the blt
// pptlBrush   - brush origin
// rop3        - 3-way rop to use
// pBounds     - bounding rectangle
// ppOrder     - the encoded order
//
BOOL  OEEncodePatBlt(LPOSI_PDEV   ppdev,
                                         BRUSHOBJ   *pbo,
                                         POINTL     *pptlBrush,
                                         BYTE     rop3,
                                         LPRECT     pBounds,
                                         LPINT_ORDER *ppOrder)
{
    BOOL rc = FALSE;
    POE_BRUSH_DATA pCurrentBrush;
    LPPATBLT_ORDER pPatBlt;
    UINT orderFlags = OF_SPOILABLE;

    DebugEntry(OEEncodePatBlt);

    //
    // Check for a simple brush pattern.
    //
    if ( OECheckBrushIsSimple(ppdev, pbo, &pCurrentBrush) )
    {
        //
        // Allocate the memory for the order.
        //
        *ppOrder = OA_DDAllocOrderMem(sizeof(PATBLT_ORDER),0);
        if (*ppOrder != NULL)
        {
            pPatBlt = (LPPATBLT_ORDER)((*ppOrder)->abOrderData);

            //
            // Set the opaque flag if the rop is opaque.
            //
            if (ROP3_IS_OPAQUE(rop3))
            {
               orderFlags |= OF_SPOILER;
            }

            //
            // Set up order type.
            //
            pPatBlt->type = LOWORD(ORD_PATBLT);

            //
            // Virtual desktop co-ordinates.
            //
            pPatBlt->nLeftRect  = pBounds->left;
            pPatBlt->nTopRect   = pBounds->top;
            pPatBlt->nWidth     = pBounds->right  - pBounds->left + 1;
            pPatBlt->nHeight    = pBounds->bottom - pBounds->top  + 1;
            pPatBlt->bRop       = rop3;

            //
            // Pattern colours.
            //
            pPatBlt->BackColor  = pCurrentBrush->back;
            pPatBlt->ForeColor  = pCurrentBrush->fore;

            //
            // The protocol brush origin is the point on the screen where
            // we want the brush to start being drawn from (tiling where
            // necessary).  This must be in virtual coordinates.
            //
            pPatBlt->BrushOrgX  = pptlBrush->x;
            pPatBlt->BrushOrgY  = pptlBrush->y;
            OELPtoVirtual((LPPOINT)&pPatBlt->BrushOrgX, 1);

            //
            // Extra brush data from the data when we realised the brush.
            //
            pPatBlt->BrushStyle = pCurrentBrush->style;
            pPatBlt->BrushHatch = pCurrentBrush->hatch;

            RtlCopyMemory(pPatBlt->BrushExtra,
                          pCurrentBrush->brushData,
                          sizeof(pPatBlt->BrushExtra));

            TRACE_OUT(( "PatBlt BC %02x%02x%02x FC %02x%02x%02x "
                         "Brush %02X %02X X %d Y %d w %d h %d rop %02X",
                    pPatBlt->BackColor.red,
                    pPatBlt->BackColor.green,
                    pPatBlt->BackColor.blue,
                    pPatBlt->ForeColor.red,
                    pPatBlt->ForeColor.green,
                    pPatBlt->ForeColor.blue,
                    pPatBlt->BrushStyle,
                    pPatBlt->BrushHatch,
                    pPatBlt->nLeftRect,
                    pPatBlt->nTopRect,
                    pPatBlt->nWidth,
                    pPatBlt->nHeight,
                    pPatBlt->bRop));

            //
            // Copy any order flags into the encoded order structure.
            //
            (*ppOrder)->OrderHeader.Common.fOrderFlags = (TSHR_UINT16)orderFlags;

            rc = TRUE;
        }
        else
        {
            TRACE_OUT(( "Failed to alloc order"));
        }
    }
    else
    {
        TRACE_OUT(( "Brush is not simple"));
    }

    DebugExitDWORD(OEEncodePatBlt, rc);
    return(rc);
}




//
// DrvTransparentBlt()
// NEW FOR NT5
//
BOOL DrvTransparentBlt
(
    SURFOBJ *   psoDst,
    SURFOBJ *   psoSrc,
    CLIPOBJ *   pco,
    XLATEOBJ *  pxlo,
    RECTL *     prclDst,
    RECTL *     prclSrc,
    ULONG       iTransColor,
    ULONG       ulReserved
)
{
    BOOL        rc = TRUE;
    RECT        rectSrc;
    RECT        rectDst;
    BOOL        fAccumulate = FALSE;

    DebugEntry(DrvTransparentBlt);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;

    //
    // Get bounding rectangle and convert to a RECT.
    //
    RECT_FROM_RECTL(rectSrc, (*prclSrc));
    RECT_FROM_RECTL(rectDst, (*prclDst));


    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(psoDst, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

DC_EXIT_POINT:
    if (fAccumulate)
    {
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitBOOL(DrvTransparentBlt, rc);
    return(rc);
}



//
// DrvAlphaBlend()
// NEW FOR NT5
//
BOOL DrvAlphaBlend
(
    SURFOBJ *   psoDst,
    SURFOBJ *   psoSrc,
    CLIPOBJ *   pco,
    XLATEOBJ *  pxlo,
    RECTL *     prclDst,
    RECTL *     prclSrc,
    BLENDOBJ *  pBlendObj
)
{
    BOOL        rc = TRUE;
    RECT        rectSrc;
    RECT        rectDst;
    BOOL        fAccumulate = FALSE;

    DebugEntry(DrvAlphaBlend);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;

    //
    // Get bounding rectangle and convert to a RECT.
    //
    RECT_FROM_RECTL(rectSrc, (*prclSrc));
    RECT_FROM_RECTL(rectDst, (*prclDst));


    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(psoDst, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

DC_EXIT_POINT:
    if (fAccumulate)
    {
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitBOOL(DrvAlphaBlend, rc);
    return(rc);
}



//
// DrvPlgBlt()
// NEW FOR NT5
//
BOOL DrvPlgBlt
(
    SURFOBJ *           psoDst,
    SURFOBJ *           psoSrc,
    SURFOBJ *           psoMsk,
    CLIPOBJ *           pco,
    XLATEOBJ *          pxlo,
    COLORADJUSTMENT *   pca,
    POINTL *            pptlBrushOrg,
    POINTFIX *          pptfx,
    RECTL *             prclDst,
    POINTL *            pptlSrc,
    ULONG               iMode
)
{
    BOOL        rc = TRUE;
    RECT        rectDst;
    BOOL        fAccumulate = FALSE;

    DebugEntry(DrvPlgBlt);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;

    //
    // Get bounding rectangle and convert to a RECT.
    //
    RECT_FROM_RECTL(rectDst, (*prclDst));

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(psoDst, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

DC_EXIT_POINT:
    if (fAccumulate)
    {
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitBOOL(DrvPlgBlt, rc);
    return(rc);
}



//
// DrvStretchBltROP()
// NEW FOR NT5
//
BOOL DrvStretchBltROP
(
    SURFOBJ *           psoDst,
    SURFOBJ *           psoSrc,
    SURFOBJ *           psoMask,
    CLIPOBJ *           pco,
    XLATEOBJ *          pxlo,
    COLORADJUSTMENT *   pca,
    POINTL *            pptlHTOrg,
    RECTL *             prclDst,
    RECTL *             prclSrc,
    POINTL *            pptlMask,
    ULONG               iMode,
    BRUSHOBJ *          pbo,
    DWORD               rop4
)
{
    BOOL        rc = TRUE;
    RECT        rectSrc;
    RECT        rectDst;
    BOOL        fAccumulate = FALSE;

    DebugEntry(DrvStretchBltROP);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;

    //
    // Get bounding rectangle and convert to a RECT.
    //
    RECT_FROM_RECTL(rectSrc, (*prclSrc));
    RECT_FROM_RECTL(rectDst, (*prclDst));

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(psoDst, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

DC_EXIT_POINT:
    if (fAccumulate)
    {
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitBOOL(DrvStretchBltROP, rc);
    return(rc);
}



//
// DrvGradientFill()
// NEW FOR NT5
//
BOOL DrvGradientFill
(
    SURFOBJ *       psoDst,
    CLIPOBJ *       pco,
    XLATEOBJ *      pxlo,
    TRIVERTEX *     pVertex,
    ULONG           nVertex,
    PVOID           pMesh,
    ULONG           nMesh,
    RECTL *         prclExtents,
    POINTL *        pptlDitherOrg,
    ULONG           ulMode
)
{
    BOOL        rc = TRUE;
    RECT        rectDst;
    BOOL        fAccumulate = FALSE;

    DebugEntry(DrvGradientFill);

    //
    // DO THIS _BEFORE_ TAKING LOCK
    //
    if (!g_oeViewers)
        goto NO_LOCK_EXIT;

    OE_SHM_START_WRITING;

    //
    // Get bounding rectangle and convert to a RECT.
    //
    RECT_FROM_RECTL(rectDst, pco->rclBounds);

    //
    // Check if we are accumulating data for this function
    //
    fAccumulate = OEAccumulateOutput(psoDst, pco, &rectDst);
    if (!fAccumulate)
    {
        DC_QUIT;
    }

    //
    // Convert to virtual coordinates.
    //
    OELRtoVirtual(&rectDst, 1);

DC_EXIT_POINT:
    if (fAccumulate)
    {
        OEClipAndAddScreenData(&rectDst, pco);
    }

    OE_SHM_STOP_WRITING;

NO_LOCK_EXIT:
    DebugExitBOOL(DrvGradientFill, rc);
    return(rc);
}







