/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = PATH.C
 *
 * DESCRIPTIVE NAME = Path and area related routines for Postscript driver.
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION :
 *
 * This module contains PartialArc, Arc, FullArcBoundary,
 * FullArcInterior, FullArcBoth, and support routines.  Several
 * ASM routines are called externally, and are located in ARC.ASM
 * and CIRCCTR.ASM.
 *
 *
 * FUNCTIONS :              prdl_BeginArea
 *                          prdl_EndArea
 *                          prdl_BeginPath
 *                          prdl_EndPath
 *                          prdl_CloseFigure
 *                          prdl_ModifyPath
 *                          prdl_StrokePath
 *                          prdl_OutlinePath
 *                          play_clip_rects
 *                          rclIsEqual
 *                          prdl_NotifyClipChange
 *                          prdl_SelectClipPath
 *                          prdl_SavePath
 *                          prdl_RestorePath
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/
/*#define TESTING                                                           */

#define  INCL_GPIPATHS
#define  INCL_GPIREGIONS       /* needed for RGNRECT structure                */
#include "inc\prdinclt.h"
#include "inc\prdgextf.h"
#include "inc\utl.h"
#include "inc\prdmath.h"
#include "inc\prdlextf.h" /*  prdlsubr.c*/
#include "inc\pspagtun.h"             /* V2.174057  Page Tuning */
#define  INCL_GENPLIB_ERROR
#include <genplib.h>

#define  OD_MEMORY  8L

extern void  prdl_PenGray(PDDC ); /*  patfill.c */
extern ULONG ps_patfill(HDC,PDDC,ULONG,BOOL,PBITBLTATTRS,BOOL );
extern PDDC EnterDriver(PDDC );
extern VOID ExitDriver(PDDC );
extern VOID _Optlink ps_CosmeticLine( PDDC );

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_BeginArea
 *
 * DESCRIPTION   =  Sets the proper flags in the ddc and outputs
 *                  a newpath command to the postscript printer
 *                  to indicate we are starting a new area definition.
 *
 * INPUT         = HDC   hdc
 *                 ULONG ulFlags
 *                 PDDC  pddc
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

ULONG prdl_BeginArea( HDC hdc, ULONG ulFlags, PDDC pddc, ULONG FunN )
{
  ULONG tl;

  EnterDriver( pddc  );

  #if      DEBUG
    LogCall( "prdl_BeginArea(%08lx, %08lx, %lp, %08lx)", ((PB)&hdc)+sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  /*
  ** check for invalid flags
  */
  if (ulFlags & (~( BA_WINDING + BA_BOUNDARY )))
  {
    RIP( "BeginArea: invalid flags" );
    GplErrSetError( PMERR_INV_AREA_CONTROL );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** Fail if a path is already open because paths and areas can't
  ** be mixed.
  */
  if (pddc->pddcb->path.fPathIsOpen)
  {
    RIP( "BeginArea: path already exists." );
    GplErrSetError( PMERR_INV_IN_PATH );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (pddc->pddcb->path.fAreaIsOpen)
  {
    RIP( "BeginArea: area already exists." );
    GplErrSetError( PMERR_ALREADY_IN_PATH );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (FunN&COM_DRAW)
  {
    PrintChannel( pddc, (PSZ)"n\n" );

    /*
    ** force the move to because 'newpath' clears current PS position
    */
    PrintChannel( pddc, (PSZ)"%ld %ld m\n", pddc->pddcb->pen.ptlCur.x,
                  pddc->pddcb->pen.ptlCur.y );
  }
  pddc->pddcb->path.fPathOrArea = TRUE;
  pddc->pddcb->path.fAreaIsOpen = TRUE;
  pddc->pddcb->path.usAreaFlags = (SHORT) ulFlags;

  /*
  ** indicate there are no curves in the current path.
  */
  pddc->pddcb->cgs.fCurvesInPath = FALSE;
  pddc->pddcb->cgs.fFullArcInPath = FALSE;

  /*
  ** save the current position, so the corresponding EndArea has
  ** something to update to.
  */
  pddc->pddcb->path.ptlAreaCP.x = pddc->pddcb->pen.ptlCur.x;
  pddc->pddcb->path.ptlAreaCP.y = pddc->pddcb->pen.ptlCur.y;

  /*
  ** save the current area color so the corresponding EndArea will
  ** fill with the area color set at begin area time
  */
  pddc->pddcb->path.ulAreaFgColor = pddc->pddcb->pat.ulFgColor;
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_EndArea
 *
 * DESCRIPTION   = If the cancel flags is set, the EndArea discards
 *                 the current path without drawing anything.
 *                 Otherwise the area defined since the last BeginArea call
 *                 is filled, and if requested, stroked.  If the area is
 *                 empty, then we do nothing.
 *
 * INPUT         = HDC   hdc
 *                 ULONG fCancel
 *                 PDDC  pddc
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 * RETURN-ERROR  = FAILURE
 *                                                                    
 ****************************************************************************/

ULONG prdl_EndArea( HDC hdc, ULONG fCancel, PDDC pddc, ULONG FunN )
{
  ULONG ulReturn;
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_EndArea(%08lx, %ld, %lp, %08lx)", ((PB)&hdc)+sizeof(hdc) );
    PrintLog( (PSZ)"pddc->pddcb->path.fAreaIsOpen = %b\n",
              pddc->pddcb->path.fAreaIsOpen );
  #endif                       /* DEBUG                                       */

  /*
  ** do nothing if an area has not yet been started.
  */
  if (!pddc->pddcb->path.fAreaIsOpen)
  {
    ExitDriver( pddc );
    return( SUCCESS );
  }

  /*
  ** Either cancel the current area or fill it.
  */
  if (fCancel)
  {
    if (FunN&COM_DRAW)
    {
      ps_clearpath( pddc );
      ps_movetoCP( pddc );     /* validate the current pos                    */
    }
  }
  else
  {
    /*
    ** update the current position to where it was at the start
    ** of the figure.
    */
    pddc->pddcb->pen.ptlCur.x = pddc->pddcb->path.ptlAreaCP.x;
    pddc->pddcb->pen.ptlCur.y = pddc->pddcb->path.ptlAreaCP.y;

    /*
    ** !! we need to close the area if needed...
    **  do nothing if the draw bit is not set
    */
    if (!(FunN & COM_DRAW))
    {
      pddc->pddcb->path.fAreaIsOpen = FALSE;
      pddc->pddcb->path.fPathOrArea = FALSE;
      ExitDriver( pddc );
      return( SUCCESS );
    }

    /*
    ** close the path so we have nice smooth connections
    */
    PrintChannel( pddc, (PSZ)"cp\n" );

    if (pddc->pddcb->path.usAreaFlags & BA_BOUNDARY)
    {

      #if      DEBUG
        PrintLog( (PSZ) "Fill + stroke\n" );
      #endif                  /* DEBUG                                       */

      /*
      ** Control comes here to both fill the area and
      ** stroke the border.  A gsave is done so that the
      ** path can be restored after the fill and then
      ** stroked.
      */
      ps_gsave( pddc );

      if (pddc->pddcb->path.usAreaFlags&BA_WINDING)
      {
        ulReturn = ps_patfill( hdc, pddc, BA_WINDING, PF_ENDAREA, NULL, TRUE );
      }
      else
      {
        ulReturn = ps_patfill( hdc, pddc, BA_ALTERNATE, PF_ENDAREA, NULL, TRUE);
      }
      ps_grestore( pddc );
      pddc->pddcb->cgs.fPathExists = TRUE;
      ps_strokecurrentpath( pddc );
    }
    else
    {
      #if      DEBUG
            PrintLog((PSZ)"Fill only\n" );
      #endif                       /* DEBUG                                       */

      /*
      ** Control comes here if it is an area fill only.
      **                                                        
      ** Removed gsave and grestore around patfill.
      ** Patfill function will do this if necessary.
      */
      if (pddc->pddcb->path.usAreaFlags&BA_WINDING)
      {
        ulReturn = ps_patfill( hdc, pddc, BA_WINDING, PF_ENDAREA, NULL, FALSE );
      }
      else
      {
        ulReturn = ps_patfill( hdc, pddc, BA_ALTERNATE, PF_ENDAREA, NULL, FALSE );
      }
    }

    /*
    ** make current position valid.
    */
    ps_moveto( pddc, &pddc->pddcb->path.ptlAreaCP );
  }

  /*
  ** Indicate that we're done with the current path.
  */
  pddc->pddcb->path.fAreaIsOpen = FALSE;
  pddc->pddcb->path.fPathOrArea = FALSE;

  /*
  ** this return code depends on the fact that ps_status returns
  ** SUCCESS or FAILURE.
  */
  tl =  ps_status( pddc ) & ulReturn;
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_BeginPath
 *
 * DESCRIPTION   =  Sets the proper flags in the ddc and outputs
 *                  a newpath command to the postscript printer to
 *                  indicate we are starting a new path definition.
 *
 * INPUT         = HDC   hdc
 *                 LONG  lPathId
 *                 PDDC  pddc
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

ULONG prdl_BeginPath( HDC hdc, LONG lPathId, PDDC pddc, ULONG FunN )
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_BeginPath(%08lx, %ld, %lp, %08lx)", ((PB)&hdc)+sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  #ifdef   TESTING
    PrintChannel( pddc, (PSZ)"BeginPath\n" );
  #endif                       /* TESTING                                     */

  /*
  ** for the first release 1 is the only valid path ID
  */
  if (lPathId != 1L)
  {
    RIP( "BeginPath: invalid path id" );
    GplErrSetError( PMERR_INV_PATH_ID );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** fail if a path or area is already open
  */
  if (pddc->pddcb->path.fAreaIsOpen)
  {
    RIP( "BeginPath: Area is already open." );
    GplErrSetError( PMERR_INV_IN_AREA );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (pddc->pddcb->path.fPathIsOpen)
  {
    RIP( "BeginPath: Path is already open." );
    GplErrSetError( PMERR_ALREADY_IN_PATH );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** Defect 75994
  ** If the Path has not been discarded do it now
  */
  if ( pddc->pddcb->lPathBufCount != (LONG) PATH_BUF_INIT_SIZE )
  {
    discard_path_buf( pddc );
  }
  init_path_buf( pddc );
  pddc->pddcb->path.fPathOrArea = TRUE;
  pddc->pddcb->path.fPathIsOpen = TRUE;
  ps_clearpath( pddc );

  /*
  ** we have just wiped out the current position in the postscript
  ** printer, so reset it now.
  */
  ps_movetoCP( pddc );

  /*
  ** indicate there are no curves in the current path
  */
  pddc->pddcb->cgs.fCurvesInPath = FALSE;
  pddc->pddcb->cgs.fFullArcInPath = FALSE;

  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_EndPath
 *
 * DESCRIPTION   =  If cancel flag is set, the EndPath discards the
 *                  current path. Otherwise the definition of the
 *                  current path is ended.
 *
 * INPUT         = HDC   hdc
 *                 ULONG fCancel
 *                 PDDC  pddc
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

ULONG prdl_EndPath( HDC hdc, ULONG fCancel, PDDC pddc, ULONG FunN )
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_EndPath(%08lx, %ld, %lp, %08lx)", ((PB)&hdc)+sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  #ifdef   TESTING
    PrintChannel( pddc, (PSZ) "EndPath\n" );
  #endif                       /* TESTING                                     */

  if (!(pddc->pddcb->path.fPathIsOpen))
  {
    #ifdef   TESTING
      PrintChannel( pddc, (PSZ) "Leaving EndPath\n" );
    #endif                       /* TESTING                                     */

    ExitDriver( pddc );
    return( GPI_OK );
  }

  /*
  ** An end path call has an effect only if there is an open
  ** path.
  */
  if ((pddc->pddcb->path.fPathIsOpen) && (FunN&COM_DRAW) && (fCancel))
  {
    PrintChannel( pddc, (PSZ) "n\n" );
  }
  pddc->pddcb->path.fPathIsOpen = FALSE;
  pddc->pddcb->cgs.fPathExists = TRUE;

  /*
  ** CR!! If there is a begin path ..xxx.. End path, PolyLine, FillPath
  ** repeated sequence, the PolyLine needs a position update.
  ** because it will be put in the postscript file as
  ** PolyLine Newpath ..xxx.. strokepath PolyLine NewPath ..xx..  etc
  **                                    ^ we need the moveto.
  ** and ps_movetocp checks the fPathExit flag as says we don't need
  ** a move, so this will force it!
  */
  pddc->pddcb->cgs.fUpdateCP = TRUE;

  #ifdef   TESTING
    PrintChannel( pddc, (PSZ) "Leaving EndPath\n" );
  #endif                       /* TESTING                                     */

  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_CloseFigure
 *
 * DESCRIPTION   =  Closes a figure within a path definition. The current
 *                  figure is closed by means of appending a straight line
 *                  from the current position to the start point of the
 *                  figure.
 *
 * INPUT         = HDC   hdc
 *                 PDDC  pddc
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

ULONG prdl_CloseFigure( HDC hdc, PDDC pddc, ULONG FunN)
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_CloseFigure(%08lx, %lp, %08lx)", ((PB)&hdc)+sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  if (!(pddc->pddcb->path.fPathIsOpen))
  {
    RIP( "CloseFigure: no path exists to close." );
    GplErrSetError( PMERR_NOT_IN_PATH );
    ExitDriver( pddc );
    return( FALSE );
  }
  PrintChannel( pddc, (PSZ) "cp\n" );
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_ModifyPath
 *
 * DESCRIPTION   =  Replaces the current path with a path enclosing the
 *                  shape produced by stroking the path using the current
 *                  geometric wide line attribute. Any open figures within
 *                  the path are not closed.
 *
 * INPUT         =  HDC hdc
 *                  ULONG lPathId
 *                  ULONG lMode
 *                  PDDC pddc
 *                  ULONG FunN
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = SUCCESS
 *
 *
 * RETURN-ERROR  = FAILURE
 *                                                                    
 *
 *
 ****************************************************************************/

ULONG prdl_ModifyPath( HDC hdc, ULONG lPathId, ULONG lMode, PDDC pddc,
                       ULONG FunN)
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_ModifyPath(%08lx, %ld, %ld, %lp, %08lx)", ((PB)&hdc)+sizeof
             (hdc) );
  #endif                       /* DEBUG                                       */

  /*
  ** For the first version of presentation manager, the path
  ** id must be one.
  */
  if (lPathId != 1)
  {
    RIP( "ModifyPath: invalid path id." );
    GplErrSetError( PMERR_INV_PATH_ID );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (lMode != MPATH_STROKE)
  {
    /*
    ** lMode must equal MPATH_STROKE in the first release
    */
    RIP( "ModifyPath: invalid lMode." );
    GplErrSetError( PMERR_INV_PATH_MODE );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** output a postscript strokepath command.  set the fPathIsOpen flag
  ** so PrintChannel will add the strokepath command to the path buffer.
  **
  **
  ** Working on stroke path. Deciding if to stroke alone or if to stroke
  ** the path depends on the path format. If we have any curves in the path
  ** we wait until next call is made. Since only two are possible it give us
  ** the ability to do so. So, if it is a FillPath we only stroke and if it
  ** is a ClipPath  then we strokepath. Though if no curves then strokepath
  ** here.                                                          
  */
  if (pddc->pddcb->cgs.fCurvesInPath)
  {
    pddc->pddcb->path.fModifiedPath = TRUE;
  }
  else
  {
    pddc->pddcb->path.fPathIsOpen = TRUE;
    PrintChannel( pddc, (PSZ) "%ld w strokepath ",
                  pddc->pddcb->pen.lGeomWidth );
    ps_CosmeticLine( pddc );
    pddc->pddcb->path.fPathIsOpen = FALSE;

    /*
    ** let the world know a path exists.
    */
    pddc->pddcb->path.fPathOrArea = TRUE;
  }
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_StrokePath
 *
 * DESCRIPTION   =  Strokes the current path with a line width as set by
 *                  the geometric line width.
 *
 * INPUT         =  HDC hdc
 *                  ULONG lPathId
 *                  ULONG lOptions
 *                  PDDC pddc
 *                  ULONG FunN
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *                                                                    
 *                                                                    
 *                                                          @104627
 ****************************************************************************/

ULONG prdl_StrokePath( HDC hdc, ULONG lPathId, ULONG lOptions,
                       PDDC pddc, ULONG FunN)
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_StrokePath(%08lx, %ld, %ld, %lp, %08lx)", ((PB)&hdc)+sizeof
             (hdc) );
    PrintLog( (PSZ) "pddc->pddcb->path.fPathIsOpen = %b\n",
     pddc->pddcb->path.fPathIsOpen );
  #endif                       /* DEBUG                                       */

  /* For the first version of presentation manager, the path
  ** id must be one.
  */
  if (lPathId != 1)
  {
    RIP( "StrokePath: invalid path id." );
    GplErrSetError( PMERR_INV_PATH_ID );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** option bits are reserved for now
  */
  if (lOptions)
  {
    RIP( "StrokePath: invalid options." );
    GplErrSetError( PMERR_INV_OR_INCOMPAT_OPTIONS );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (!(FunN&COM_DRAW))
  { /* STD fix */
    discard_path_buf( pddc );
    pddc->pddcb->path.fPathOrArea = FALSE;
    ExitDriver( pddc );
    return( SUCCESS );
  }

  /*
  ** set the line color to the current pattern foreground color,
  ** save the linewidth, set it to the geometric linewidth,
  ** do a stroke, restore the linewidth.  Also, set the linetype
  ** to solid, and restore it when done.
  */
  if (pddc->pddcb->pat.usFgMix == FM_LEAVEALONE)
  {
    discard_path_buf( pddc );
  }
  else
  {
    play_path_buf( pddc, FALSE );

    prdl_PatGray( pddc );
    /*
    ** this is a change     to get around a postscript limitation.
    ** if there are no curves in the path, then we output the line
    ** filled with the current fill pattern.  if there are curves
    ** in the path, we just fill it with the solid color.
    */
    /*
    ** first modify the path to be the outline of the wide line
    ** descriped by the geometric line width, then fill this
    ** path with the pattern attributes.
    **                          Moved the linwidth fix
    **                          from the end of the if block to here.
    **                          Linewidth Fix
    **                                                        
    ** After drawing in geometric width, reset to cosmetic width.
    */

    /* @104627 - path for pathfilling will become too complex on
    **           some pscript printers. (checked IBM 4029, Panasonic
    **           4455, and Kyocera FS-1500 A), mjones
    */

    PrintChannel( pddc, (PSZ) "%ld w {flattenpath strokepath ",
                  pddc->pddcb->pen.lGeomWidth );

    /*
    ** Added gsave and grestore around patfill.
    **                                                        
    ** Removed gsave and grestore around patfill.
    ** Patfill function will do this if necessary.
    */
    ps_patfill( hdc, pddc, FPATH_WINDING, 0L, NULL, FALSE );

    /*
    ** If the strokepath fails do a stroke.  GS/GR bracket the call
    */
    PrintChannel( pddc, "} stopped {gs " );
    ps_setdash( pddc, LINETYPE_SOLID );
    PrintChannel( pddc, (PSZ) "stroke gr} if\n" );
    ps_CosmeticLine( pddc );

    pddc->pddcb->cgs.fPathExists = FALSE;
    pddc->pddcb->cgs.fCurvesInPath = FALSE;
    pddc->pddcb->cgs.fFullArcInPath = FALSE;
  }

  pddc->pddcb->path.fPathOrArea = FALSE;
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_OutlinePath
 *
 * DESCRIPTION   =  Closes a figure within a path definition. The current
 *                  figure is closed by means of appending a straight line
 *                  from the current position to the start point of the
 *                  figure.
 *
 * INPUT         =  HDC hdc
 *                  LONG lPathId
 *                  LONG lOptions
 *                  PDDC pddc
 *                  ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *                                                                    
 ****************************************************************************/

ULONG prdl_OutlinePath( HDC hdc,LONG lPathId,LONG lOptions,PDDC pddc,
                        ULONG FunN)
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_OutlinePath(%lp, %ld, %ld, %lp, %lp", ((PB)&hdc)+sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  /*
  ** Make sure that the path id is 1 in the first release.
  */
  if (lPathId != 1L)
  {
    RIP( "OutlinePath: invalid path id." );
    GplErrSetError( PMERR_INV_PATH_ID );
    ExitDriver( pddc );
    return( FAILURE );
  }

   /*
   ** all option bits are reserved for now
   */
  if (lOptions)
  {
    RIP( "OutlinePath: invalid options." );
    GplErrSetError( PMERR_INV_OR_INCOMPAT_OPTIONS );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (!(FunN&COM_DRAW))
  {
    discard_path_buf( pddc );
    pddc->pddcb->path.fPathOrArea = FALSE;
    ExitDriver( pddc );
    return( SUCCESS );
  }

  if (pddc->pddcb->pen.usFgMix == FM_LEAVEALONE || pddc->pddcb->pen.usType ==
     LINETYPE_INVISIBLE)
  {
    discard_path_buf( pddc );
  }
  else
  {
    play_path_buf( pddc, FALSE );

    /*
    ** stroke the path using the current line attributes
    **
    **                              Linewidth Fix
    **                                                            
    ** PrintChannel( pddc, (PSZ) "%f w\n", pddc->pddcb->pen.fxWidth );
    */
    ps_setlinewidth( pddc, pddc->pddcb->pen.fxWidth, FALSE );
    prdl_PenGray( pddc );
    PrintChannel( pddc, (PSZ) "s\n" );
  }
  pddc->pddcb->path.fPathOrArea = FALSE;
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_FillPath
 *
 * DESCRIPTION   =  This takes a path and fills the interior of the closed
 *                  figures defined in the path using the specified rule
 *                  (even odd or winding) and the current pattern attributes.
 *                  Before filling, this function closes any open figures
 *                  within the current path.
 *
 * INPUT         =  HDC hdc
 *                  ULONG lPathId
 *                  ULONG lOptions
 *                  PDDC pddc
 *                  ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *                                                                    
 *                                                                    
 *                                                                    
 *
 ****************************************************************************/

ULONG prdl_FillPath( HDC hdc, ULONG lPathId, ULONG lOptions, PDDC pddc,
                     ULONG FunN)
{
  ULONG ulReturn;
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_FillPath(%08lx, %ld, %ld, %lp, %08lx)", ((PB)&hdc)+sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  /*
  ** Decide stroke.  If the path was modified that indicates curves in the path.
  ** If there is a foreground mix the we want to stroke here instead of in
  ** ModifyPath. Once finished, reset the flags and return.
  ** We actually don't want to fill the path since it would be redundant
  ** to the stroke block.                                           
  */
  if (pddc->pddcb->path.fModifiedPath)
  {
    if (pddc->pddcb->pat.usFgMix != FM_LEAVEALONE)
    {
      play_path_buf( pddc, FALSE );
      prdl_PatGray( pddc );
      ps_setdash( pddc, LINETYPE_SOLID );
      PrintChannel( pddc, (PSZ) "%ld w stroke ", pddc->pddcb->pen.lGeomWidth );
      ps_CosmeticLine( pddc );
      pddc->pddcb->path.fPathIsOpen = FALSE;
      ps_setdash( pddc, pddc->pddcb->pen.usType );
    }
    else
    {
      discard_path_buf( pddc );
    }
    PrintChannel( pddc, (PSZ) "n\n" );

    /* we have just wiped out the current position in the postscript
    ** printer, so reset it now.
    */

    pddc->pddcb->cgs.fPathExists = FALSE;
    pddc->pddcb->path.fPathOrArea = FALSE;
    pddc->pddcb->cgs.fCurvesInPath = FALSE;
    ps_movetoCP( pddc );
    pddc->pddcb->path.fModifiedPath = FALSE;

    /*
    ** this return code is based on the fact that ps_status
    ** returns SUCCESS or FAILURE.
    */
    tl = ps_status( pddc );
    ExitDriver( pddc );
    return( tl );
  }

  /*
  ** If there isn't an open path do nothing.
  */
  if (!pddc->pddcb->path.fPathOrArea)
  {
    RIP( "FillPath: no path exists to fill." );
    GplErrSetError( PMERR_PATH_UNKNOWN );
    ExitDriver( pddc );
    return( GPI_ERROR );
  }

  if (pddc->pddcb->path.fPathIsOpen)
  {
    RIP( "FillPath: invalid in a path." );
    GplErrSetError( PMERR_INV_IN_PATH );
    ExitDriver( pddc );
    return( GPI_ERROR );
  }

  if (pddc->pddcb->path.fAreaIsOpen)
  {
    RIP( "FillPath: invalid in an area." );
    GplErrSetError(  PMERR_INV_IN_AREA );
    ExitDriver( pddc );
    return( GPI_ERROR );
  }

  /*
  ** For the first version of presentation manager, the path
  ** id must be one.
  */
  if (lPathId != 1)
  {
    RIP( "FillPath: invalid path id." );
    GplErrSetError( PMERR_INV_PATH_ID );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** make sure we have valid options
  */
  if (lOptions & (~(FPATH_WINDING)))
  {
    RIP( "FillPath: invalid options." );
    GplErrSetError( PMERR_INV_OR_INCOMPAT_OPTIONS );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (!(FunN & COM_DRAW))
  {
    discard_path_buf( pddc );
    pddc->pddcb->cgs.fPathExists = FALSE;
    pddc->pddcb->path.fPathOrArea = FALSE;
    ExitDriver( pddc );
    return( SUCCESS );
  }

  if (pddc->pddcb->pat.usFgMix != FM_LEAVEALONE)
  {
    play_path_buf(pddc, FALSE );

    /*
    **  Added gsave and grestore around patfill.                  
    **                                                            
    ** Removed gsave and grestore around patfill.
    ** Patfill function will do this if necessary.
    */

    if (lOptions & FPATH_WINDING)
    {
      ulReturn = ps_patfill(hdc, pddc, FPATH_WINDING, 0L, NULL, FALSE );
    }
    else
    {
      ulReturn = ps_patfill(hdc, pddc, FPATH_ALTERNATE, 0L, NULL, FALSE );
    }
  }
  discard_path_buf( pddc );
  PrintChannel( pddc, (PSZ) "n\n" );

  /*
  ** we have just wiped out the current position in the postscript
  ** printer, so reset it now.
  */
  pddc->pddcb->cgs.fPathExists = FALSE;
  pddc->pddcb->path.fPathOrArea = FALSE;
  ps_movetoCP( pddc );

  /*
  ** this return code is based on the fact that ps_status
  ** returns SUCCESS or FAILURE.
  */
  tl = ps_status( pddc ) &ulReturn;
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME =  play_clip_rects
 *
 * DESCRIPTION   =  Replays all of the clip rects to the current path.
 *
 * INPUT         =  HDC hdc
 *                  PDDC pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

void play_clip_rects( HDC hdc, PDDC pddc )
{
  RECTL   rcl;
  RGNRECT ctl;

  ctl.crc = 1;
  ctl.ulDirection = RECTDIR_LFRT_BOTTOP;

  /*
  **                                                                
  ** Since the clipping rectangles are played in device coordinates,
  ** save the CTM
  ** Start a new path for playing the clipping rectangles.
  */
  PrintChannel( pddc, (PSZ) "/msbeforeclp mx cm def n [1 0 0 1 0 0] setmx\n" );

  for (ctl.ircStart = 1 ; ctl.ircStart <= pddc->pddcb->path.uscClipRects ;
       ++ctl.ircStart)
  {
    /*
    ** Enumerate the clipping rectangles one at a time.
    */
    GreGetClipRects( hdc, NULL, &ctl, &rcl );

    /*
    ** the PM engine and PostScript printers handle clipping differently.
    ** the PM engine clips inclusively at the left and bottom, exclusively
    ** at the right and top.  PostScript clips inclusively all around.
    ** therefore, when we send the output to the printer, bring in the
    ** right and top by one pel.
    **                                                            
    */
    PrintChannel( pddc, (PSZ) "%ld %ld m\n", rcl.xLeft, rcl.yBottom );
    PrintChannel( pddc, (PSZ) "%ld %ld l\n", rcl.xRight-1L, rcl.yBottom );
    PrintChannel( pddc, (PSZ) "%ld %ld l\n", rcl.xRight-1L, rcl.yTop-1L );
    PrintChannel( pddc, (PSZ) "%ld %ld l cp\n", rcl.xLeft, rcl.yTop-1L );
  }

  /*
  ** clip to the path we just set.  remember to do a newpath,
  ** since clip leaves the path intact.
  **                                                                
  ** Restore the CTM.
  */
  PrintChannel( pddc, (PSZ) "clip n msbeforeclp SM\n" );
  pddc->pddcb->cgs.fValidCP = FALSE;
}

/***************************************************************************
 *
 * FUNCTION NAME =  rclIsEqual
 *
 * DESCRIPTION   =  Compares two rectangles and returns TRUE if they
 *                  are equal.
 * INPUT         =  PRECTL prcl1
 *                  PRECTL prcl2
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE   if equal - FALSE if not equal.
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

BOOL rclIsEqual( PRECTL prcl1, PRECTL prcl2)
{

  if (prcl1->xLeft == prcl2->xLeft && prcl1->yTop == prcl2->yTop &&
     prcl1->xRight == prcl2->xRight && prcl1->yBottom == prcl2->yBottom)
  {
    return( TRUE );
  }
  else
  {
    return( FALSE );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_NotifyClipChange
 *
 * DESCRIPTION   =  This function adds rectangles from the clipping region
 *                  to the current clip path.
 *
 * INPUT         = HDC hdc
 *                 PRECTL prcl
 *                 LONG nrc
 *                 ULONG fClipPath
 *                 PDDC pddc
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *                                                                    
 *
 ****************************************************************************/

ULONG   prdl_NotifyClipChange( HDC hdc, PRECTL prcl, LONG nrc,
                               ULONG fClipPath, PDDC pddc, ULONG FunN)
{
  ULONG ulRet;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_NotifyClipChange(%08lx, %lp, %ld, %lb, %lp, %08lx)", ((PB)&hdc ) +
             sizeof(hdc) );
  #endif /* DEBUG                                     */

  #ifdef TESTING
    PrintChannel( pddc, (PSZ) "NotifyClipChange\n" );
  #endif /* TESTING                                   */

  if (pddc->iType == OD_MEMORY)
  {
    if (ulRet = prdl_MemClipChange (pddc, FunN & 0xFFFF0000))
    {
      ulRet = (*pfnlNotifyClipChange)( hdc, prcl, nrc, fClipPath, pddc, FunN );
    }
    ExitDriver( pddc );
    return( ulRet );
  }

  /*
  ** Fail if there is an open path since region functions are no
  ** allowed in paths.
  */
  if (pddc->pddcb->path.fPathIsOpen)
  {
    RIP( "NotifyClipChange: invalid in path." );
    GplErrSetError( PMERR_INV_IN_PATH );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** If there is only a single clip rectangle, then check to see if
  ** its the same one that was given to us last time.  If it is,
  ** then no action is required.  This is done as an optimization
  ** to prevent excessive clipping calls from being generated.
  */
  if ((nrc == 1) && (pddc->pddcb->path.uscClipRects == 1))
  {
    if (pddc->pddcb->path.fDidClipRect)
    {
      if (rclIsEqual(prcl, &pddc->pddcb->path.rclClip))
      {

        #ifdef   TESTING
          PrintChannel( pddc, (PSZ) "Leaving NotifyClipChange: no change.\n" );
        #endif                       /* TESTING                                     */

        ExitDriver( pddc );
        return( SUCCESS );
      }
    }
  }

  /*
  ** we are about to change the clip region.  in order to do this
  ** we need to destroy all clipping, get back the clip path, then
  ** intersect that with the new clip region.
  */
  PrintChannel( pddc, (PSZ) "ic\n" );

  /*
  **                                                                
  ** If there was a clip path,
  ** play back the clip path buffer.
  */
  if (pddc->pddcb->path.fDidClipPath)
  {
    /*
    ** Save the CTM, line width, line joint and color because
    ** the played back path may change them.
    */
    PrintChannel( pddc, (PSZ) "/msbeforeclp mx cm def\n" );
    PrintChannel( pddc, (PSZ)
                  "/lwidth cw def /ljoint currentlinejoin def /gray currentgray def\n" );

    /*
    ** Start a new path and play back the clip path buffer.
    */
    ps_newpath( pddc );
    play_clip_path_buf( pddc );

    if (pddc->pddcb->ulClipPathOptions&SCP_WINDING)
    {
      PrintChannel( pddc, (PSZ) "clip n\n" );
    }
    else
    {
      PrintChannel( pddc, (PSZ) "eoclip n\n" );
    }

    /*
    ** Restore the saved CTM, linewidth and color.
    */
    PrintChannel( pddc, (PSZ) "msbeforeclp SM lwidth w ljoint j gray g\n" );
    pddc->pddcb->cgs.fValidCP = FALSE;
  }

  /*
  ** remember information about the clip region.
  */
  utl_memcopy((PSZ) &pddc->pddcb->path.rclClip, (PSZ) prcl, sizeof (RECTL) );
  pddc->pddcb->path.uscClipRects = (SHORT)nrc;

  /*
  ** intersect the current clip path with the clip region.
  */
  play_clip_rects( hdc, pddc );

  /*
  ** set a flag stating the clip region exists.
  */
  pddc->pddcb->path.fDidClipRect = TRUE;

  #ifdef   TESTING
    PrintChannel( pddc, (PSZ) "Leaving NotifyClipChange\n" );
  #endif                       /* TESTING                                     */

  ulRet = (*pfnlNotifyClipChange)( hdc, prcl, nrc, fClipPath, pddc, FunN );
  ExitDriver( pddc );
  return( ulRet );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prdl_SelectClipPath
 *
 * DESCRIPTION   =
 * The GpiSetClipPath function sets the clip path. The clip path specifies a
 * path in device space that the system uses to clip output. The clip path
 * includes all points inside and on the boundary of the path specified by
 * idPath. Since the path coordinates are assumed to be device coordinates, no
 * conversion is applied.
 *
 * The function creates the clip path by closing any open figures. It then
 * releases any existing clip path (deleting the previous path, if any), and
 * sets the specified path as the clip path. After a path is set as the clip
 * path, it cannot be used again. However, its identifier is free to use for
 * another path.
 *
 * Parameter   Description
 * --------------------------------------------------------------------------
 * hps         Identifies the presentation space.
 *
 * idPath      Specifies the identifier of the path to set to the clip path. It
 *             can be 1 to specify a path or zero to specify no clip path.
 *
 * cmdOptions  Specifies the filling and combining modes. It can be one or two
 *             of the following values:
 *
 *             Value          Meaning
 *             ----------------------------------------------------------------
 *             SCP_ALTERNATE  Computes the interior of the clip path, using
 *                            alternate mode. This is the default if neither
 *                            SCP_ALTERNATE nor SCP_WINDING is given.
 *
 *             SCP_AND        Intersects the specified path with the current
 *                            clip path. This value must be specified if the
 *                            idPath parameter is 1.
 *
 *             SCP_RESET      Resets the clip path, releasing the current clip
 *                            path if any. This value must be specified if the
 *                            idPath parameter is 0. This is the default if
 *                            neither SCP_AND nor SCP_RESET is given.
 *
 *             SCP_WINDING    Computes the interior of the clip path, using
 *                            winding mode.
 *
 * INPUT         = HDC hdc           The DC handle
 *                 ULONG ulId        The path id
 *                 ULONG ulOptions   The clipping mode
 *                 PDDC pddc         Ptr to the DC instance data
 *                 ULONG FunN        The engine function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 *
 * The return value is GPI_OK if the function is successful or GPI_ERROR if an
 * error occurred.
 *
 * RETURN-ERROR  = GPI_ERROR
 *
 * Use the WinGetLastError function to retrieve the error value, which may be
 * one of the following:
 *
 *      PMERR_INV_CLIP_PATH_OPTIONS
 *      PMERR_INV_HPS
 *      PMERR_INV_PATH_ID
 *      PMERR_INV_PATH_MODE
 *      PMERR_PATH_UNKNOWN
 *      PMERR_PS_BUSY
 *
 * Intersects the current path with the current clip path or it replaces
 * the clip path with the current path depending on the options parameter.
 *
 *                                                                    
 *                                                                    
 *
 ****************************************************************************/

ULONG prdl_SelectClipPath( HDC hdc, ULONG ulId, ULONG ulOptions,
                           PDDC pddc, ULONG FunN)
  /*
  ** HDC hdc;             The DC handle
  ** ULONG ulId;          The path id
  ** ULONG ulOptions;     The clipping mode
  ** PDDC pddc;           Ptr to the DC instance data
  ** ULONG FunN;          The engine function number
  */

{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_SelectClipPath(%lp, %ld, %08lx, %lp, %lp)", ((PB)&hdc) +
             sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  #ifdef   TESTING
    PrintChannel( pddc, (PSZ) "SelectClipPath ulId = %d ulOptions = %lx\n", ulId,
                  ulOptions );
  #endif                       /* TESTING                                     */

  /*
  ** make sure we have valid path id
  */
  if ((ulId != 0) && (ulId != 1))
  {
    RIP( "SelectClipPath: invalid path id" );
    GplErrSetError( PMERR_INV_PATH_ID );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** check options
  */
  if ((ulOptions &~(SCP_WINDING | SCP_AND)) || (ulId == 0 && (ulOptions & SCP_AND))
     || (ulId == 1 && !(ulOptions & SCP_AND)))
  {
    RIP( "SelectClipPath: invalid path options" );
    GplErrSetError( PMERR_INV_CLIP_PATH_OPTIONS );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** make sure we are not inside a path
  */
  if (pddc->pddcb->path.fPathIsOpen)
  {
    RIP( "SelectClipPath: invalid inside a path." );
    GplErrSetError( PMERR_INV_IN_PATH );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** If the path was modified with curves in it we then want to stroke the
  ** here before we proceed with the clipping.                      
  */
  if (pddc->pddcb->path.fModifiedPath)
  {
    pddc->pddcb->path.fPathIsOpen = TRUE;
    PrintChannel( pddc, (PSZ) "%ld w strokepath ",
                  pddc->pddcb->pen.lGeomWidth );
    ps_CosmeticLine( pddc );
    pddc->pddcb->path.fPathIsOpen = FALSE;

    /*
    ** let the world know a path exists.
    */
    pddc->pddcb->path.fPathOrArea = TRUE;
    pddc->pddcb->path.fModifiedPath = FALSE;
  }

  /*
  ** if the path id == 0 this means we are being asked to
  ** destroy the clip path, ie no clipping.  however, the
  ** clip region is still in effect.
  */
  if (ulId == 0)
  {
    /*
    ** the only way to increase the size of the clipped
    ** area on the printer is to do an initclip.
    **
    ** CR!!! if we already have a clip path do we need to keep the old
    ** CR!!! one; we are eating stack space if we do
    */
    if (pddc->pddcb->path.fDidClipPath)
    {
      /*
      **            Discard the clip path buffer.
      */
      discard_clip_path_buf( pddc );
      PrintChannel( pddc, (PSZ) "ic\n" );

      /*
      ** now that we set clipping to the full page size,
      ** bring the clip region back into play.
      */
      play_clip_rects( hdc, pddc );

      /*
      ** set a flag stating that no clip path currently exists.
      */
      pddc->pddcb->path.fDidClipPath = FALSE;
    }
    goto SCPexit;
  }

  /* make sure we have a path to set as the clip path.  Return
  ** an error if not.
  */
  if (!pddc->pddcb->cgs.fPathExists)
  {
    RIP( "SelectCliPath: path not found." );
    GplErrSetError( PMERR_PATH_UNKNOWN );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  **                                                                
  ** Save the current path buffer into the clip path buffer.
  ** We need this clip path to play back,
  ** when the clip region is modified later.
  */
  init_clip_path_buf( pddc );

  /*
  ** Save the ulOptions for play back.
  */
  pddc->pddcb->ulClipPathOptions = ulOptions;

  /*
  ** Start a virgin clip path.
  */
  PrintChannel( pddc, (PSZ) "ic\n" );

  /*
  ** now get the new path from the path buffer and clip to it.
  **                                                                
  ** Restore the CTM at the beginning of the path.
  */
  PrintChannel( pddc, (PSZ) "/msbeforeclp mx cm def msclp SM\n" );
  ps_newpath(pddc );
  play_path_buf(pddc, FALSE );

  /*
  **                                                                
  ** Clip the path and restore the CTM and start a new path.
  */

  if (ulOptions & SCP_WINDING)
  {
    PrintChannel( pddc, (PSZ) "clip n msbeforeclp SM\n" );
  }
  else
  {
    PrintChannel( pddc, (PSZ) "eoclip n msbeforeclp SM\n" );
  }
  pddc->pddcb->cgs.fValidCP = FALSE;

  /*
  ** indicate that a clip path now exists.
  */
  pddc->pddcb->path.fDidClipPath = TRUE;

  /*
  ** now that we have the clip path set up and saved, it is
  ** time to intersect is with the current clip region , if
  ** one exists (besides the full page).
  */
  if (pddc->pddcb->path.fDidClipRect)
  {
    play_clip_rects( hdc, pddc );
  }

SCPexit:

  #ifdef   TESTING
    PrintChannel( pddc, (PSZ)
                  "Leaving SelectClipPath fDidClipPath = %d fDidClipRect = %d\n",
                  pddc->pddcb->path.fDidClipPath, pddc->pddcb->path.fDidClipRect );
  #endif                       /* TESTING                                     */

  pddc->pddcb->path.fPathIsOpen = FALSE;    /* Should already be clear        */
  pddc->pddcb->path.fPathOrArea = FALSE;
  pddc->pddcb->cgs.fPathExists = FALSE;
  pddc->pddcb->cgs.fCurvesInPath = FALSE;
  pddc->pddcb->cgs.fFullArcInPath = FALSE;
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_SavePath
 *
 * DESCRIPTION   = The driver handles all the path stuff itself, so this
 *                 function is hooked out so that the engine is pacified
 *                 (prevented from doing random things).
 *
 * INPUT         = HDC hdc         The DC handle
 *                 ULONG nLevel    The save/restore level
 *                 PDDC pddc       Ptr to the DC instance data
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

ULONG prdl_SavePath( HDC hdc, ULONG nLevel, PDDC pddc, ULONG FunN)
  /*
  ** HDC hdc;         The DC handle
  ** ULONG nLevel;    The save/restore level
  ** PDDC pddc;       Ptr to the DC instance data
  ** ULONG FunN;
  */
{
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_RestorePath
 *
 * DESCRIPTION   = The driver handles all the path stuff itself, so this
 *                 function is hooked out so that the engine is pacified
 *                 (prevented from doing random things).
 *
 * INPUT         = HDC hdc         The DC handle
 *                 ULONG nLevel    The save/restore level
 *                 PDDC pddc       Ptr to the DC instance data
 *                 ULONG FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

ULONG prdl_RestorePath( HDC hdc, LONG nLevel, PDDC pddc, ULONG FunN)
  /*
  ** HDC hdc;         The DC handle
  ** ULONG nLevel;    The save/restore level
  ** PDDC pddc;       Ptr to the DC instance data
  ** ULONG FunN;
  */
{
  EnterDriver( pddc );

  /*
  ** This is the ultimate change.         We are updating
  ** the transform matrix here to get around an engine oddity
  ** with RestoreDC.  See RestoreDC in ENABLE.C for more
  ** details.                                                       
  **
  ** we only want to modify te transform matrix if we have
  ** been called by RestoreDC.
  */
  if (nLevel > 0L)
  {
    ps_restorematrix( pddc );
  }

  ExitDriver( pddc );
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_MemClipChange
 *
 * DESCRIPTION   = Set the clipping region  of the shadow memory DC.
 *                It copies the DC region  of the DC and
 *                set the same in the shadow memory DC.
 *                Hence both will be in sync.
 *
 * INPUT                = pddc  The DC handle
 *
 * OUTPUT            = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  =
 *
 * 03-01-93   Wrote it.  Ravisankar.
 ****************************************************************************/

BOOL prdl_MemClipChange( PDDC pddc, ULONG ulComFlags)
{
  HRGN  hrgnOld;
  RECTL rclBounds;
  ULONG ulRet = TRUE;

  /*
  ** Release the current clip region of the shadow memory.
  */
  if (InnerGreSelectClipRegion( pddc->hdcMemory, (HRGN) NULL, &hrgnOld,
                                ulComFlags | NGreSelectClipRegion) == RGN_ERROR)
  {
    RIP( "MemClipChange: GreSelectClipRegion(  returns RGN_ERROR");
    ulRet = FALSE;
  }
  /*
  ** Copy the DC region
  */
  else if (InnerGreCopyClipRegion( pddc->hdc, pddc->hrgnMemory, &rclBounds,
                                   COPYCRGN_ALLINTERSECT,
                                   ulComFlags | NGreCopyClipRegion ) == RGN_ERROR)
  {
    RIP( "MemClipChange: GreCopyClipRegion(  returns RGN_ERROR") ;
    ulRet = FALSE;
  }
  else if (InnerGreSelectClipRegion( pddc->hdcMemory, pddc->hrgnMemory,
                                     &hrgnOld,
                                     ulComFlags | NGreSelectClipRegion ) == RGN_ERROR)
  {
    RIP ( "MemClipChange: GreSelectClipRegion( returns RGN_ERROR") ;
    ulRet = FALSE;
  }

    return( ulRet );
}
