/*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 = PRDEVECT.C
 *
 * DESCRIPTIVE NAME = POSTSCRIPT DRIVER SOURCE FILE
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION POSTSCRIPT DRIVER SOURCE
 *
 *
 * FUNCTIONS   fnCentralDispatch()      generic processing
 *             EnterDriver              This function locates DDC structure
 *             ExitDriver()             This routine does a clean up before
 *                                      exiting the driver.
 *
 *             InstallDispatchVectors() This routine installs the vectors to the
 *                                      printer function handler routines into
 *                                      the engine's
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define  INCL_GRE_BITMAPS
#define  INCL_GRE_STRINGS
#define  INCL_GRE_XFORMS
#define  INCL_DEV
#define  INCL_DOSINFOSEG
#include "inc\prdinclt.h"
#include <pmdev.h>
#include "inc\utl.h"
#include "inc\prdgextf.h"
#include "inc\prdeextf.h"
#include "inc\prdvect.h"
#include "inc\mandvect.h"
#include "inc\pspagtun.h"             /* V2.174057  Page Tuning */
#define  INCL_GENPLIB_ERROR
#include <genplib.h>

#define  OD_MEMORY     8L              /* !!!CR include pmddi.h (or
                                          whereever it is)!!!               */

#define  IQueryDeviceCaps ((SHORT) NGreQueryDeviceCaps & 0x01FF)
#define  IQueryHardcopyCaps ((SHORT) NGreQueryHardcopyCaps & 0x01FF)
#define  IDeviceCreateBitmap ((SHORT) NGreDeviceCreateBitmap & 0x01FF)
#define  IDeviceDeleteBitmap ((SHORT) NGreDeviceDeleteBitmap & 0x01FF)
#define  ICharStringPos ((SHORT) NGreCharStringPos & 0x01FF)
#define  ICharString   ((SHORT) NGreCharString & 0x01FF)
#define  IQueryTextBox ((SHORT) NGreQueryTextBox & 0x01FF)
#define  IQueryCharPositions ((SHORT) NGreQueryCharPositions & 0x01FF)
#define  lGetPairKerningTable ((SHORT) NGreGetPairKerningTable & 0x01FF)
#define  IQueryWidthTable ((SHORT) NGreQueryWidthTable & 0x01FF)
#define  INotifyTransformChange ((SHORT) 0x000040A5L & 0x01FF)
#define  INotifyClipChange ((SHORT) NGreNotifyClipChange & 0x01FF)
#define  IGetBitmapBits ((SHORT) NGreGetBitmapBits & 0x01FF)
#define  ISetBitmapBits ((SHORT) NGreSetBitmapBits & 0x01FF)
#define  IPolyLine   ((SHORT) NGrePolyLine & 0x01FF)
#define  IPolyMarker   ((SHORT) NGrePolyMarker & 0x01FF)
#define  IBitBlt       ((SHORT) NGreBitblt & 0x01FF)
#define  ISetCurrentPosition ((SHORT) NGreSetCurrentPosition & 0x01FF)

#define  ExitWith(result) {                                                \
                       usResult= result;                                  \
                       goto CleanUpAndExit;                               \
                       }


/*
** The following vectors point the the engine's default
** text function handlers.  The driver calls them in case
** the currently selected font is an outline font and the
** engine will handle converting the text into line drawing
** calls.
*/
PFNL pfnlCharStringPos;
PFNL pfnlCharString;
PFNL pfnlQueryTextBox;
PFNL pfnlQueryCharPositions;
PFNL pfnlGetPairKerningTable;
PFNL pfnlQueryWidthTable;
PFNL pfnlNotifyTransformChange;
PFNL pfnlNotifyClipChange;
PFNL pfnlPolyMarker;
PFNL pfnlPolyLine;
PFNL pfnlBitBlt;
PFNL pfnlSetCurrentPosition ;
PDDC EnterDriver(PDDC pddc);
void ExitDriver(PDDC pddc);
extern HMTX tsemDriver; /* Global Driver semaphore           */
#define  NEW_RET       0
#define  ORG_RET       1
#define  COPYFUNN      2
#define  DISPHDDC      3

/*            */
extern LONG APIENTRY DevicePalette( HDC, PDEVICESURFACE, PDDC, ULONG );
extern ULONG ULGreVersion;

/***************************************************************************
 *
 * FUNCTION NAME = fnCentralDispatch()
 *
 * DESCRIPTION   = Action:
 *                    All function calls to the engine which are (now known
 *                    and) device driver hookable are dispatched by the
 *                    engine directly to this device driver dispatching
 *                    routine.
 *
 *                    This Central Dispatching Function (CDF) does all the
 *                    generic processing before passing the call on to the
 *                    intended GreFun.  That includes checking that the DDC
 *                    is valid, checking for DC busy (is another thread
 *                    already using this DDC?), etc.
 *
 *
 *                 General Description and Comments:
 *
 *                 The CDF has a variable number of arguments, because it
 *                 could be any of the standard Gre Functions (GreFuns).
 *                 However, the Function Number (FunN) and the hddc will
 *                 always be the first two args, so we can look at those
 *                 -- in fact we must look at FunN in order to know where
 *                 to dispatch.
 *
 *                 We will check that the hddc is valid, as it must be in all
 *                 cases except DeviceDeleteBitmap.  In that call the hddc
 *                 must be NULL.  (Bitmaps are independant objects not
 *                 associated with a DC when deleted).
 *
 *                 This driver does not handle memory DDCs itself.  Instead
 *                 it passes all memory DC related calls to the Display
 *                 Driver.  That means all bitmap stuff is done by the
 *                 display driver.
 *
 *                 We keep a minimal DDC for memory DDCs.  We keep a pointer
 *                 to it
 *                  which we call "pddc".  The display
 *                 driver's handle for the corresponding
 *                  DDC we call
 *                 "hddc".  Because the display driver and engine are
 *                 both unaware that we use the display (we call it by
 *                 pretending
 *                 WE are the ENGINE) and because sometimes
 *                 the display and engine will talk
 *                 to each other
 *                 directly (circumventing us -- e.g.  Bitblt) WE MUST
 *                 pass back to the engine the HDDC rather than our PDDC
 *                 (on the enable
 *                 DDC call).  Therefore the CDF
 *                 arg hddc is the display hddc, not our DDC call).
 *                 So we must have a mapping from hddc to pddc.  This
 *                 is currently done with a hashing scheme in
 *                 EnterDriver (called by CDF).
 *
 *                 CDF ultimatly calls "InsertReturn" which does a lot of
 *                 stack manipulation.  One consequence is that the RET
 *                 at the end of CDF will actually dispatch to the
 *                 GreFun, rather than return to the calling APP.
 *                 Another consequence is that the RET done by the GreFun
 *                 will return to asmExitDriver which calls ExitDriver.
 *                 The primary reason for the ExitDriver routine is to
 *                 unlock the DDC, which was locked in EnterDriver
 *                 (called by CDF).
 *
 *
 *                 Another consequence is that asmExitDriver has two args
 *                 -- our pddc and the number of args this GreFun has.
 *                 There is some      trickery going on just before/after
 *                 and during InsertReturn.  See the comments there
 *                 (prddyna.asm) for details.
 *
 * INPUT         = FunN, pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void fnCentralDispatch( ULONG FunN, PDDC pddc )
{
  PVCTD pvctd;                         /* Ptr to the function descriptor
                                          table                             */
  /*
  **  BOOL fUseDisplay;  SCT
  **  HDDC hddcDisplay;
  */
  ULONG             ulFunN;
  SHORT             usFunN;
  ULONG            *pulStack;
  SHORT             cArgs;
  extern ARGERRRET *aAER;

  #if 0
    ** PDDCENTRY ptagDDCE;
  #endif

  usFunN = (SHORT) (FunN & 0x01FF );
  PrintLog( (PSZ)"\nfnCentralDispatch: usFunN = %xH, ",
            (short)usFunN );

  /*
  ** avctd is tbl of FunN addrs and Param Counts
  */
  pvctd = &avctd[usFunN];

  if ((usFunN <= NGREMAX) && (pvctd->nParams > 0))
  {
    /*
    ** we know of this call
    */
    cArgs = pvctd->nParams;
  }
  else
  {
    RIP( "Bug in Engine: Can't call old display driver with new call" );
  }

  /*
  ** i.e. Is this call DeleteBitmap?
  ** (takes a NULL DC)
  */
  if (pddc)
  {
    /*
    ** find and verify our pDDC !!! why
    ** is this a call?
    */
    EnterDriver( pddc );

    if (pddc == (PDDC) INVALID_ADDRESS)
    {
      if (usFunN != (SHORT) (NGreEscape&0x01FF))
      {
        goto Failure;
      }
      else if ((LONG) *(&FunN + 6) != DEVESC_ABORTDOC)
      {
        goto Failure;
      }
    }

    #if 0
    **    /* we want all calls to fail, except DEVESC_ABORTDOC.
    **    ** if we have a DEVESC_ABORTDOC and EnterDriver failed
    **    ** because the ddc was busy, let the call go through. */
    **
    **    if (usFunN == (SHORT)(NGreEscape&0x01FF))
    **    {
    **      /* we have a DEVESC call.  check to see if its ABORTDOC.
    **      ** remember, the parameters are on the stack. */
    **
    **      if ((LONG)*(&FunN+6) == DEVESC_ABORTDOC)
    **      {
    **
    **        /*
    **        ** Find our pDDC from the display driver's hDDC
    **        */
    **
    **        ptagDDCE = (PDDCENTRY)FindEntry(DDC_MAP,
    **                                        sizeof(HDDC),
    **                                        (PBYTE)&hddc );
    **        pddc = ptagDDCE->pddc;
    **      }
    **
    **      else                         /* not an ABORTDOC.                  */
    **        goto Failure;
    **    }
    **
    **    else                           /* not DEVESC call.                  */
    **      goto Failure;
    **  }
    #endif

    PrintLog( (PSZ)"iType = %ld, nParams = %d  ", pddc->iType,
              pvctd->nParams );

    /* SCT
    **  fUseDisplay = (pddc->iType == OD_MEMORY );
    */
  }

#if 0
//  else
//  {
//    /*
//    ** must be DeviceDeleteBitmap, which
//       will fall through.
//    */
//    /*
//    ** !!! set busy bit ??? there is no DDC
//    */
//    pddc = NULL;
//
//    /*
//    ** !!! Do we need a usage count on bitmaps?
//    */
//
//    /*  SCT
//    **  fUseDisplay = TRUE;
//    */
//  }
#endif

  PrintLog( (PSZ)"    pddc = %lp\n",
            pddc );

  #if 0
  ** switch (usFunN)
  ** {
  **
  ** /*
  ** ** !!!CR  We should not be passing color table functions or color selections
  ** ** !!!CR  on to the display driver directly; we should do all color mapping
  ** ** !!!CR  ourselves and massage the caller's parameters so that the display
  ** ** !!!CR  driver only works in black and white.  This is because DCR 24009
  ** ** !!!CR  tells us that display driver mapping algorithms will not be
  ** ** !!!CR  consistent, and may not work the way we want them to, anyway.
  ** */
  ** /*
  ** **  !!!CR                                                                    
  ** */
  ** /*
  ** ** dispatch these to us even if
  ** **     Memory DC
  ** */
  ** case IQueryHardcopyCaps:
  ** case IQueryDeviceCaps:
  **
  ** /* SCT
  ** ** fUseDisplay = FALSE;
  ** */
  **      break;
  **
  ** /*
  ** ** dispatch these to display even if
  ** **   Device DC
  ** */
  ** case IDeviceCreateBitmap:
  ** case IDeviceDeleteBitmap:
  ** case IGetBitmapBits:
  ** case ISetBitmapBits:
  ** /* SCT
  ** **    fUseDisplay = TRUE;
  ** */
  **      break;
  **
  ** default:
  **      break;
  ** }
  #endif

  /* SCT
  ** hddcDisplay = hddc;
  */

  #if 0
  ** /*
  ** ** Dispatch there.
  ** ** Replace the display hddc with our pddc.
  ** */
  ** hddc = (HDDC)pddc;
  #endif

  ulFunN = FunN;

  /*
  ** This and pddc (above) are args to ExitDriver
  */
  FunN = (ULONG)cArgs;

  #if 0
  ** /*
  ** ** Munge stack (see prddyna.asm)
  ** */
  ** pulStack = InsertReturn(pvctd->nParams );
  ** pulStack[ORG_RET] = (ULONG)asmExitDriver;
  #endif

  pulStack[COPYFUNN] = ulFunN;

  /* SCT
  ** if (fUseDisplay)
  ** {
  **   pulStack[DISPHDDC] = (ULONG) hddcDisplay;
  **   pulStack[NEW_RET] = (ULONG) apfnlDisplay[usFunN];
  **   PrintLog( (PSZ)"DISPLAY DRIVER dispatch\n" );
  ** }
  **
  ** else
  */

  {
    /*
    ** Dispatch address
    */
    pulStack[NEW_RET] = (ULONG) pvctd->pfnl;

    PrintLog( (PSZ)"POSTSCRIPT DRIVER dispatch\n" );

    /*
    ** Does the Engine care that we have changed hddc to pddc?
    ** If it runs a simulation will it reuse the "ddc" we leave there?
    */
  }
  return ;

Failure:

  RIP( "Central Dispatch Failure" );

  #if 0
  ** /*
  ** ** Set up args for ExitDriver.
  ** */
  ** hddc = 0L;
  #endif

  FunN = (ULONG) cArgs;

  /*
  ** Yes, SS != DS, that's the way, uh
  ** huh, we like it (that is, for 16-bit).
  ** 32-bit won't have this.
  */
  /*  relay_to_asmExitDriver(&FunN ); */
}

/***************************************************************************
 *
 * FUNCTION NAME = EnterDriver
 *
 * DESCRIPTION   = This function locates our DDC structure corresponding
 *                 to the display hddc which the engine placed on the
 *                 stack.  It validates the DDC structure and attempts to
 *                 lock the DDC on behalf of the calling thread.  If the
 *                 DDC is already locked on behalf of some other pid/tid,
 *                 it will fail.
 *
 * INPUT         = hddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = The address of a DDC, or INVALID_ADDRESS for failure.
 *
 * RETURN-ERROR  = INVALID_ADDRESS
 *
 **************************************************************************/

PDDC EnterDriver( PDDC pddc )
{
  SHORT usResult;
  PPIB ppib;
  PTIB ptib;

  if (pddc)
  {
    if (pddc->iType == OD_MEMORY)
    {
      return (pddc );
    }

    DosRequestMutexSem( tsemDriver, SEM_INDEFINITE_WAIT );

    #if 0
    ** /*
    ** ** Find our pDDC from the display driver's hDDC
    ** */
    ** ptagDDCE = (PDDCENTRY)FindEntry(DDC_MAP,
    **                                 sizeof(HDDC),
    **                                 (PBYTE)&hddc );
    **
    ** if (ptagDDCE != (PDDCENTRY)INVALID_OFFSET)
    ** {
    **   /*
    **   ** got our pddc!
    **   */
    **   pddc = ptagDDCE->pddc;
    ** }
    ** else
    ** {
    **   RIP( "Can't find DDC Entry for display hddc" );
    **   ExitWith (PMERR_INV_HDC );
    ** }
    **
    #endif

    /*
    ** Verify that this IS a DDC of ours.
    */
    if (pddc->id != DRIVER_ID)
    {
      RIP( "Invalid DDC handle in EnterDriver" );
      ExitWith( PMERR_INV_HDC );
    }

    DosGetInfoBlocks( &ptib, &ppib );

    if (!pddc->cLockCount)
    {
      pddc->pid = ppib->pib_ulpid;
      pddc->tid = ptib->tib_ptib2->tib2_ultid;
    }
    else if (pddc->pid != ppib->pib_ulpid || pddc->tid !=
             ptib->tib_ptib2->tib2_ultid)
    {
      RIP( "HDC is busy in EnterDriver, ignore if ABORTDOC" );
      ExitWith( PMERR_HDC_BUSY );
    }

    ++( pddc->cLockCount );
    usResult = 0;

CleanUpAndExit:

    DosReleaseMutexSem( tsemDriver );

    if (usResult)
    {
      GplErrSetError( usResult );
      return( (PDDC)INVALID_ADDRESS );
    }

    return( pddc );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ExitDriver()
 *
 * DESCRIPTION   = When a GreFun does it's return it returns to asmExitDriver.
 *                 That is because our dispatcher munged the stack to make this
 *                 happen.  We need an assembly routine because ExitDriver
 *                 thinks it is being called, not returned to.  Thus C does not
 *                 provide a way for us to pick up the return code in DX:AX.
 *                 Hence we return to asmExitDriver which then CALLS ExitDriver
 *                 after saving DX:AX.  When we return to asmExitDriver it will
 *                 do a Ret N, poping the correct number of GreFun args off the
 *                 stack, sailing thru the call gate on its way back to the
 *                 APP.
 *
 *                 See additional comments in asmExitDriver.  ( prddyna.asm )
 *
 *                 This routine does all necessary cleanup before
 *                 exiting the driver.  Presently that consists of
 *                 decrementing the usage count for the current
 *                 DDC, and unlocking the DDC if the count becomes
 *                 zero.
 *
 * INPUT         = pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ExitDriver(PDDC pddc)
{
  PPIB ppib;
  PTIB ptib;

  /*
  ** pddc is NULL if error occured in
  ** if GreFun is DeleteBitmap
  */
  if (pddc)
  {
    if (pddc->iType == OD_MEMORY)
    {
      return;
    }

    /*
    ** DDC of ours?
    */
    ASSERT (pddc->id == DRIVER_ID );

    DosRequestMutexSem( tsemDriver, SEM_INDEFINITE_WAIT );

    DosGetInfoBlocks( &ptib, &ppib );

    if (pddc->cLockCount)
    {
      if (pddc->pid != ppib->pib_ulpid || pddc->tid !=
          ptib->tib_ptib2->tib2_ultid)
      {
        RIP( "DDC busy for a different thread, ignore if ABORTDOC" );
      }
      else
      {
        --pddc->cLockCount;
      }
    }
    else
    {
      RIP( "Attempt to unlock an unbusy DDC" );
    }

    DosReleaseMutexSem( tsemDriver );
  }
}

/*           
**
** These are required for any DC with the new engine
*/
VOID Hook22Calls( PFNL *apfnDispatch )
{
  /*
  ** For the new raster engine (2.2 and later), hook the following functions.
  */
  apfnDispatch[ NGreEscape             & 0x00FF ] = (PFNL) prdq_Escape;
  apfnDispatch[ NGreQueryHardcopyCaps  & 0x00FF ] = (PFNL) prdq_QueryHardcopyCaps;
  apfnDispatch[ 0x4040                 & 0x00FF ] = (PFNL) DevicePalette;
}

/*           
**
** The engine will take care of these even for direct/queued DCs
*/
VOID UnHookBitmapCalls( PFNL *apfnDispatch )
{
  /*
  ** For 2.2 unhook the bitmap calls excpet bitblt
  */
  apfnDispatch[ NGreDeviceCreateBitmap    & 0x00FF ] =
                     od_memory_vect[ NGreDeviceCreateBitmap    & 0x00FF ].pfnl;
  apfnDispatch[ NGreDeviceDeleteBitmap    & 0x00FF ] =
                     od_memory_vect[ NGreDeviceDeleteBitmap    & 0x00FF ].pfnl;
  apfnDispatch[ NGreDeviceSelectBitmap    & 0x00FF ] =
                     od_memory_vect[ NGreDeviceSelectBitmap    & 0x00FF ].pfnl;
  apfnDispatch[ NGreGetBitmapBits         & 0x00FF ] =
                     od_memory_vect[ NGreGetBitmapBits         & 0x00FF ].pfnl;
  apfnDispatch[ NGreSetBitmapBits         & 0x00FF ] =
                     od_memory_vect[ NGreSetBitmapBits         & 0x00FF ].pfnl;

  apfnDispatch[ NGreQueryDeviceCaps       & 0x00FF ] =
                     od_memory_vect[ NGreQueryDeviceCaps       & 0x00FF ].pfnl;
}


/***************************************************************************
 *
 * FUNCTION NAME = InstallDispatchVectors()
 *
 * DESCRIPTION   = Action: This routine installs the vectors to the printer
 *                         function handler routines into the engine's
 *                         dispatch table.  It also gets a copy of the
 *                         vectors to the display driver's function handler
 *                         routines.  These will be used when performing
 *                         operations on a memory DC.
 *
 *                 Explanation:
 *
 *                   The engine gives us a pointer to its dispatch table
 *                   which, at this time, points to all the engine's
 *                   default routines.  The engine expects us to
 *                   overwrite its pointers with ours for the routines
 *                   which we want to handle.  We, in fact, want to
 *                   handle ALL of them.  We overwrite every pointer in
 *                   the engine's table with the address of our dispatch
 *                   function.
 *
 *                   When I say "all" functions, I mean all of them that
 *                   are known to exist now, at compile time.  If this
 *                   driver is around with some future version of the
 *                   Engine there may be calls defined by the new engine
 *                   we don't know about.  We wont have hooked these
 *                   out.  The engine will detect that we are an old
 *                   driver and do simulations as necessary to get the
 *                   job done.  The new currently unknown functions
 *                   which the engine might add will have function
 *                   numbers at the end of the current table, cause the
 *                   table to be bigger in the future, OR, they may fill
 *                   in holes in the current table.
 *
 *                   We will use the display driver to process all
 *                   memory DC calls.  So below we load the display
 *                   driver and call its enable functions in exactly
 *                   the same way the engine is doing now to us.  We
 *                   pass it a pointer to a copy of the engine's
 *                   default dispatch table (copied before we altered
 *                   it).  Our dispatcher will use this table (in which
 *                   the display driver has placed its function
 *                   pointers) when dispatching calls using memory DCs.
 *
 *                   We also (like the engine) have a table (avctd) of
 *                   pointers to all our routines.  However, some
 *                   functions we have decided to let the engine handle.
 *                   Our table has NULL pointers (at compile time) in
 *                   these locations.  Here we overwrite those NULL
 *                   pointers with the engine addresses given us in the
 *                   table the engine passed us.  Then avctd can be used
 *                   as our dispatch table.
 *
 *                   Thus our dispatcher will send calls which don't use
 *                   memory DCs thru our table (avctd).
 *
 *                   You might think that we could put the display
 *                   drivers addresses in the engine's table and
 *                   dispatch directly the display driver.
 *                   Unfortunately we can't because we don't know if
 *                   it's a memory DC until we actually get the call and
 *                   examine the args.
 *
 *                 SomeDay: !!!
 *
 *                   We could leave the engine's default address for
 *                   funtions that neither we nor display hook out.  It
 *                   would speed up such calls since they could avoid
 *                   our      dispatching scheme.
 *
 * INPUT         = apfnDispatch, FLPlist
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *
 **************************************************************************/

/* HMODULE hmodDisplay; */
SHORT InstallDispatchVectors( PFNL *apfnDispatch, FLParamsLst *FLPlist )

/*  PFNL *apfnDispatch;            Ptr to the base of the dispatch table */
                                /* !!! which I think is unneeded because */
/*  FLParamsLst *FLPlist;             !!! it is an internal call         */

{
  static BOOL fGotProcAddr = FALSE;
  SHORT       usFunN;                 /* Index into the engine's function
                                         table                             */
  SHORT       usError;

  #if 0
  ** HMODULE hmodTmp;
  ** FLReturnsLst param2;
  ** ULONG ulTableSize;
  ** SHORT usFlags;
  **
  ** /*
  ** ** We must call DosLoadModule for each process so that the display
  ** ** driver's selectors are made valid for each process.  We write to a
  ** ** Tmp variable so we don't destroy the real hmodDisplay if the "Load"
  ** ** Fails for some reason.
  ** */
  ** if (DosLoadModule((PSZ)NULL,
  **                   (SHORT)0,
  **                   "DISPLAY",
  **                   &hmodTmp))
  ** {
  **   RIP( "InstallDispatchVectors: DosLoadModule failed" );
  **   return  FAILURE;
  ** }
  **
  ** hmodDisplay = hmodTmp;               /* it's cheaper to repeat this than
  **                                         "if" it.                        */
  **
  ** if (!fGotProcAddr)
  ** {
  **   /*
  **   ** Find out where the display driver's enable function is located.
  **   ** It will not change, so we only need do this once (first process).
  **   */
  **   if (DosQueryProcAddr (hmodDisplay,
  **                         0L,
  **                         (PSZ) "OS2_PM_DRV_ENABLE",
  **                         (PFN *) &pfnlPMDispEnable))
  **   {
  **     RIP( "InstallDispatchVectors: DosGetProcAddr failed" );
  **     return FAILURE;
  **   }
  **   else
  **   {
  **     fGotProcAddr = TRUE;
  **   }
  ** }
  #endif

  /*
  ** the engine passes us a NULL pointer if we are not supposed to
  ** overwrite the dispatch table.
  */
  if (!apfnDispatch)
  {
    return( SUCCESS );
  }

  /*
  ** if this assertion fails, it means that the engine's dispatch
  ** table we are about to overwrite is a different size than
  ** we think it is.  NGREMAX is defined in inc\prdvect.h, which
  ** is created by running makevect.exe.  if this assertion fails,
  ** delete inc\prdvect.h and remake the driver.
  **
  */
  ASSERT( FLPlist->cTableSize == (NGREMAX+1) );

  /*
  ** The engine has initialized the dispatch table with pointers to all
  ** of its default handler routines.  We make a duplicate copy of this
  ** list so that we may hand it to the display driver.
  */

  /* SCT
    ulTableSize = FLPlist->cTableSize;

    for (usFunN = 0; usFunN <= NGREMAX; ++usFunN)
    {
      PrintLog((PSZ)"apfnlDisplay[%d] = %ld\n",
                usFunN,
                apfnDispatch[usFunN] );
      apfnlDisplay[usFunN] = apfnDispatch[usFunN];
    }
  */
  /*
  ** Call the display driver's enable function with subfunction one in
  ** order to get a list of all the display driver's function handler
  ** vectors.
  */

  /* SCT
    usFlags = 0;
    param2.pFlags = &usFlags;
    param2.apfn = apfnlDisplay;

    if ((*pfnlPMDispEnable)((ULONG)1L,
                            (PB)FLPlist,
                            (PB)&param2))
    {
      RIP("InstallDispatchVectors: Display driver's enable failed" );
      return  FAILURE;
    }
  */

  /*
  ** Save the engine's default dispatch vectors for the text functions.
  ** The driver will have to call them in case an outline font is used.
  */
  pfnlCharStringPos = apfnDispatch[ICharStringPos];
  pfnlCharString = apfnDispatch[ICharString];
  pfnlQueryTextBox = apfnDispatch[IQueryTextBox];
  pfnlQueryCharPositions = apfnDispatch[IQueryCharPositions];
  pfnlGetPairKerningTable = apfnDispatch[lGetPairKerningTable];
  pfnlQueryWidthTable = apfnDispatch[IQueryWidthTable];
  pfnlNotifyTransformChange = apfnDispatch[INotifyTransformChange];
  pfnlNotifyClipChange = apfnDispatch[INotifyClipChange];
  pfnlPolyLine = apfnDispatch[IPolyLine];
  pfnlPolyMarker = apfnDispatch[IPolyMarker];
  pfnlBitBlt = apfnDispatch[IBitBlt];
  pfnlSetCurrentPosition = apfnDispatch[ISetCurrentPosition];

  /*            */
  if (ULGreVersion < RASTER_ENGINE_22)
  {
    /* Copy all the vectors that are not mandatory for the driver
    ** to support and also the functions we should copy back for
    ** od_memory into our od_memory_vect table. We can use this
    ** table later in prde_FillPdb to copy back all these vectors
    ** into the dispatch table for OD_MEMORY DCtype.
    */
    for (usFunN = 0 ; usFunN <= NGREMAX ; ++usFunN)
    {
      if (!od_memory_vect[usFunN].pfnl)
      {
        od_memory_vect[usFunN].pfnl = apfnDispatch[usFunN];
      }
    }
  }

  /*
  ** First copy the vectors that the driver doesn't hook out
  ** into the driver's dispatch table.  The end result is a
  ** dispatch table containing vectors to the "hooked-out"
  ** driver functions + the engine's default vectors for the
  ** functions that weren't hooked out.
  */
  for (usFunN = 0 ; usFunN <= NGREMAX ; ++usFunN)
  {
    /*           
    ** For 2.2 OD_memory we want to unhook all entry points so record all
    ** points
    */
    if (ULGreVersion >= RASTER_ENGINE_22)
    {
      od_memory_vect[usFunN].pfnl = apfnDispatch[usFunN];
    }
    /*
    ** If the driver doesn't hook out a vector, then copy
    ** the default engine vector into the driver's dispatch
    ** table.  This is necessary because all of the
    ** engines dispatch vectors are changed so that control
    ** passes through the driver first.
    */
    if (!avctd[usFunN].pfnl)
    {
      avctd[usFunN].pfnl = apfnDispatch[usFunN];
    }

    if (avctd[usFunN].nParams > 0)     /* do we know this fn?               */
    {
      apfnDispatch[usFunN] = avctd[usFunN].pfnl;
    }

  }

  /*            */
  if ( ULGreVersion >= RASTER_ENGINE_22 )
  {
    Hook22Calls( apfnDispatch );
    UnHookBitmapCalls( apfnDispatch );
  }

  return  SUCCESS;
}

