/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
// escape.c

#define INCL_DOS
#define INCL_SPL
#define INCL_SPLFSE
#define INCL_PM
#include <os2.h>

#define INCL_GRE_DEVICE
#define INCL_DDIMISC
#include <pmddi.h>

// c includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <builtin.h>

#include "def.h"
#include "driver.h"
#include "funcs.h"
#include "assert.h"



//----------------------------------------------------------------------------------------

LONG ENGENTRY Escape(              HDC       hdc,
                                   LONG      lEscape,
                                   LONG      cInCount,
                                   PBYTE     pInData,
                                   PLONG     pcOutCount,
                                   PBYTE     pOutData,
                                   PDDC      pddc,
                                   ULONG     ulFunction )
{
  BOOL        fOK;
  REGREC      regrec;
  ULONG       ulException;
  LONG        lrc;
  LONG        lWork;
  ULONG       ulWork;
  APIRET      rc;
  HSTD        hstd;
  LONG        lStdLength;
  LONG        lStart;
  LONG        lLength;
  PCHAR       pchSplBuffer = NULL;




  REGISTERHANDLER( regrec );
  ulException = setjmp( regrec.jmp );
  if( ulException ) {
    // clean up
    if( pchSplBuffer ) {
      FreeMem( pchSplBuffer );
    }
    CheckForTermination( &regrec, ulException, pddc );
    // error result
    lrc = DEVESC_ERROR;
    goto depart;
  }



  // not appropriate to use ENTERDDC in this case, especially in the case of ABORTDOC
  rc = DosRequestMutexSem( pddc->pdb->hmtxDCSem, SEM_INDEFINITE_WAIT );
  assert( rc == 0 );



  // assume success
  lrc = DEV_OK;



  switch( lEscape ) {


  case DEVESC_STARTDOC:
    DBPRINTF(( "DEVESC_STARTDOC\n" ));
    // new document for this DC
    pddc->pdb->fAbortDocCalled  = FALSE;
    // ignore multiple startdocs; it's not an error
    if( pddc->pdb->fStartDocCalled ) break;

    // pInData is document name; copy if non null
    assert( cInCount ? (BOOL)pInData:TRUE );
    strncpy( pddc->pdb->szDocumentName,  pInData ? pInData:"",  sizeof( pddc->pdb->szDocumentName )-1 );

    switch( pddc->pdb->ulType ) {
    case OD_DIRECT:
      DBPRINTF(( "  OD_DIRECT case\n" ));
      pddc->fGenerateOutput = TRUE;
      switch( pddc->pdb->ulDataType ) {
      case PM_Q_RAW:
        rc  =  PrtOpen( pddc->pdb->szLogAddress,
                        &pddc->pdb->hspl,
                        &ulWork,
                        0,
                        FILE_NORMAL,
                        OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
                        OPEN_ACCESS_WRITEONLY         | OPEN_SHARE_DENYREADWRITE,
                        0 );
        assert( 0 == rc );
        assert( pddc->pdb->hspl );
        break;
      case PM_Q_STD:
        // impossible
        assert( FALSE );
        break;
      }
      break;

    case OD_QUEUED:
      DBPRINTF(( "  OD_QUEUED case\n" ));
      assert( pddc->pdb->ulDataType == PM_Q_RAW  ||  pddc->pdb->ulDataType == PM_Q_STD );

      // this allocates a queued print job; need only be done once per DC instance
      // closed in enable.c at BEGIN_CLOSE_DC time
      if( !pddc->pdb->hspl ) {
        pddc->pdb->hspl = SplQmOpen( "*", 9, (PQMOPENDATA)&pddc->pdb->dop );
        assert( pddc->pdb->hspl );
      }

      // queue manager start document
      assert( pddc->pdb->hspl );
      assert( pddc->pdb->szDocumentName );
      assert( *pddc->pdb->szDocumentName  );
      fOK = SplQmStartDoc( pddc->pdb->hspl, pddc->pdb->szDocumentName  );
      assert( fOK );


      switch( pddc->pdb->ulDataType ) {
      case PM_Q_RAW:
        pddc->fGenerateOutput = TRUE;
        break;
      case PM_Q_STD:
        // starts the recording of GPI calls into a metafile
        fOK = SplStdStart( pddc->pdb->hdc );
        assert( fOK );
        break;
      }
      break;



    case OD_MEMORY:
    case OD_METAFILE:
    case OD_INFO:
      assert( FALSE );
      break;
    }

    // this app clearly indicated start of document; other apps may not be so tidy
    pddc->pdb->fStartDocCalled = TRUE;
    break;






  case DEVESC_RAWDATA:
    DBPRINTF(( "DEVESC_RAWDATA\n" ));

    if( !pddc->pdb->fStartDocCalled ) {
      // error to do DEVESC_RAWDATA before DEVESC_STARTDOC
      RAISEEXCEPTION( XCPT_USER );
    }

    switch( pddc->pdb->ulType ) {
    case OD_DIRECT:
      DBPRINTF(( "  OD_DIRECT case\n" ));
      switch( pddc->pdb->ulDataType ) {
      case PM_Q_RAW:
        // check hspl handle or fAbortDocCalled when deciding to call PrtWrite
        if( pddc->pdb->hspl ) {
          rc = PrtWrite( pddc->pdb->hspl, pInData, cInCount, &ulWork );
          assert( 0 == rc );
          assert( cInCount == ulWork );
        } else {
          // must have called abortdoc if hspl is zero
          assert( pddc->pdb->fAbortDocCalled );
        }
        break;
      case PM_Q_STD:
        // impossible case: this situation subjected to override in enable.c
        assert( FALSE );
        break;
      }
      break;

    case OD_QUEUED:
      DBPRINTF(( "  OD_QUEUED case\n" ));
      switch( pddc->pdb->ulDataType ) {
      case PM_Q_RAW:
        assert( pddc->pdb->hspl );
        assert( cInCount );
        assert( pInData  );
        fOK = SplQmWrite( pddc->pdb->hspl, cInCount, pInData );
        assert( fOK );
        break;
      case PM_Q_STD:
        break;
      default:
        assert( FALSE );
        break;
      }
      break;

    default:
      assert( FALSE );
      break;
    }
    break;






  case DEVESC_ENDDOC:
    DBPRINTF(( "DEVESC_ENDDOC\n" ));

    // ignore enddoc if no startdoc; it's not an error
    if( ! pddc->pdb->fStartDocCalled ) break;

    if( pddc->fGenerateOutput ) {
      fOK = NewFrame( pddc );
      assert( fOK );
    }

    switch( pddc->pdb->ulType ) {
    case OD_DIRECT:
      DBPRINTF(( "  OD_DIRECT case\n" ));

      switch( pddc->pdb->ulDataType ) {
      case PM_Q_RAW:
        assert(  pddc->pdb->hspl );
        rc = PrtClose( pddc->pdb->hspl );
        assert( 0 == rc );
        pddc->pdb->hspl = 0;
        break;
      case PM_Q_STD:
        // impossible
        assert( FALSE );
        break;
      }
      break;

    case OD_QUEUED:
      DBPRINTF(( "  OD_QUEUED case\n" ));
      switch( pddc->pdb->ulDataType ) {
      case PM_Q_RAW:
        lWork = (LONG)SplQmEndDoc( pddc->pdb->hspl );
        assert( lWork != SPL_ERROR );
        break;
      case PM_Q_STD:
        // stop recording and get handle to job
        hstd = SplStdStop( pddc->pdb->hdc );
        assert( hstd );

        // get length of metafile as a count of bytes
        lStdLength = SplStdQueryLength( hstd );

        // allocate some DDC heap for use as a buffer
        #define MAX_LEN_SPLBITBUFFER    0x1000
        lLength = min( MAX_LEN_SPLBITBUFFER, lStdLength );
        pchSplBuffer = (PCHAR) AllocMem( pddc->pdb->pvHeap,  lLength );
        assert( pchSplBuffer );

        // loop as necessary
        lStart = 0;
        while( lStart < lStdLength ) {
          fOK = SplStdGetBits( hstd, lStart, lLength, pchSplBuffer );
          assert( fOK );

          fOK = SplQmWrite( pddc->pdb->hspl, lLength, pchSplBuffer );
          assert( fOK );

          lStart += lLength;
          lLength = min( lLength, lStdLength-lStart );
        }

        // delete the spooler metafile data
        fOK = SplStdDelete( hstd );
        assert( fOK );

        // returns job ID
        lWork = (LONG)SplQmEndDoc( pddc->pdb->hspl );
        assert( lWork != SPL_ERROR );

        // if there is room at the pOutData pointer, then give the caller the job id (lWork)
        if( pcOutCount &&  *pcOutCount >= sizeof( lWork )) {
          // there is room
          assert( pOutData );
          *(PLONG)pOutData = lWork;
          *pcOutCount = sizeof( lWork );
        }

        FreeMem( pchSplBuffer );
        pchSplBuffer = NULL;

        break;
      }
      break;
    default:
      assert( FALSE );
      break;
    }

    // this doc is over
    // clean up this ddc in case there is another startdoc/enddoc pair down the road
    pddc->pdb->fStartDocCalled  = FALSE;
    pddc->pdb->fAbortDocCalled  = FALSE;
    pddc->pdb->ulPage           = 0;
    memset( pddc->pdb->szDocumentName, 0, sizeof( pddc->pdb->szDocumentName ));


    break;






  case DEVESC_NEWFRAME:
    DBPRINTF(( "DEVESC_NEWFRAME\n" ));
    // call routine to dump shadow DC bits to printer

    if( pddc->fGenerateOutput ) {
      fOK = NewFrame( pddc );
      assert( fOK );
    }

    switch( pddc->pdb->ulType ) {
    case OD_DIRECT:
      break;
    case OD_QUEUED:
      // this is being recorded or metafiled
      break;
    default:
      assert( FALSE );
      break;
    }
    break;






  case DEVESC_ABORTDOC:
    DBPRINTF(( "DEVESC_ABORTDOC\n" ));
    pddc->pdb->fStartDocCalled = FALSE;

    // abortdocs can arrive more than once
    if( !pddc->pdb->fAbortDocCalled ) {
      pddc->pdb->fAbortDocCalled  = TRUE;
      switch( pddc->pdb->ulType ) {
      case OD_DIRECT:
        // DEVICE_SPECIFIC: send escape to reset printer
        rc = PrtClose( pddc->pdb->hspl );
        assert( rc ==  0 );
        pddc->pdb->hspl = 0;
        break;
      case OD_QUEUED:
        fOK = SplQmAbortDoc( pddc->pdb->hspl );
        assert( fOK );
        switch( pddc->pdb->ulDataType ) {
        case PM_Q_STD:
          hstd = SplStdStop( hdc );
          assert( hstd );
          fOK = SplStdDelete( hstd );
          assert( fOK );
          break;
        case PM_Q_RAW:
          break;
        }
        break;
      }
    }
    break;



  // devescapes not implemented
  case DEVESC_BREAK_EXTRA:
  case DEVESC_CHAR_EXTRA:
  case DEVESC_DBE_FIRST:
  case DEVESC_DBE_LAST:
  case DEVESC_DRAFTMODE:
  case DEVESC_ERROR:
  case DEVESC_FLUSHOUTPUT:
  case DEVESC_GETCP:
  case DEVESC_GETSCALINGFACTOR:
  case DEVESC_NEXTBAND:
  case DEVESC_QUERYVIOCELLSIZES:
  case DEVESC_STD_JOURNAL:
    assert( FALSE );
    lrc = DEVESC_NOTIMPLEMENTED;
    break;


  case DEVESC_SETMODE:
    // pmspool.exe sends this escape during raw-data-only jobs.
    // pInData points to two adjacent LONGs. The 2nd is a codepage.
    lrc = DEVESC_NOTIMPLEMENTED;
    break;



  case DEVESC_QUERYESCSUPPORT:
    assert( pInData );
    assert( cInCount == sizeof( LONG ));
    switch( *(PLONG)pInData  ) {
    case DEVESC_BREAK_EXTRA:
    case DEVESC_CHAR_EXTRA:
    case DEVESC_DBE_FIRST:
    case DEVESC_DBE_LAST:
    case DEVESC_DRAFTMODE:
    case DEVESC_ERROR:
    case DEVESC_FLUSHOUTPUT:
    case DEVESC_GETCP:
    case DEVESC_GETSCALINGFACTOR:
    case DEVESC_NEXTBAND:
    case DEVESC_QUERYVIOCELLSIZES:
    case DEVESC_SETMODE:
    case DEVESC_STD_JOURNAL:
      lrc = DEVESC_NOTIMPLEMENTED;
      break;
    default:
      lrc = DEV_OK;
      break;
    }
    break;

  default:
    assert( FALSE );
    break;
  }

depart:
  rc = DosReleaseMutexSem( pddc->pdb->hmtxDCSem );
  assert( rc == 0 );
  UNREGISTERHANDLER( regrec );
  return lrc;
}

