The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#ifdef INCRCSDATA
static char RCSid[]="$Id: print.c,v 1.1 1999/03/26 22:15:54 lhecking Exp $" ;
#endif

/****************************************************************************

    PROGRAM: gnupmdrv
    
    Outboard PM driver for GNUPLOT 3.3

    MODULE:  print.c -- support for printing graphics under OS/2 
        
****************************************************************************/

/* PM driver for GNUPLOT */

/*[
 * Copyright 1992, 1993, 1998 Roger Fearick
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
]*/

/*
 * AUTHOR
 * 
 *   Gnuplot driver for OS/2:  Roger Fearick
 * 
 * Send your comments or suggestions to 
 *  info-gnuplot@dartmouth.edu.
 * This is a mailing list; to join it send a note to 
 *  majordomo@dartmouth.edu.  
 * Send bug reports to
 *  bug-gnuplot@dartmouth.edu.
**/

#define INCL_SPLDOSPRINT
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DEV
#define INCL_SPL
#define INCL_PM
#define INCL_WIN
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include "gnupmdrv.h"

#define   GNUPAGE   4096        /* size of gnuplot page in pixels (driver dependent) */


typedef struct {            /* for print thread parameters */
    HWND  hwnd ;
    HDC   hdc ;                 /* printer device context */
    HPS   hps ;                 /* screen PS to be printed */
    char  szPrintFile[256] ;    /* file for printer output if not to printer */
    PQPRINT pqp ;       /* print queue info */
    } PRINTPARAMS ;

static struct { 
    long    lTech ;     // printer technology
    long    lVer ;      // driver version
    long    lWidth ;    // page width in pels
    long    lHeight ;   // page height in pels
    long    lWChars ;   // page width in chars    
    long    lHChars ;   // page height in chars    
    long    lHorRes ;   // horizontal resolution pels / metre
    long    lVertRes ;  // vertical resolution pels / metre
    } prCaps ;

//static PDRIVDATA    pdriv = NULL ;
static DRIVDATA     driv = {sizeof( DRIVDATA) } ;
static char         szPrintFile[CCHMAXPATHCOMP] = {0} ;
static DEVOPENSTRUC devop ;

ULONG GetPrinters( PPRQINFO3 *, ULONG *  ) ;
int FindPrinter( char *, PPRQINFO3  ) ;
HMF     CopyToMetaFile( HPS ) ; 
static void    ThreadPrintPage( ) ;

MPARAM PrintCmdProc( HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
/*
**  Handle messages for print commands for 1- and 2-d spectra
** (i.e for the appropriate 1-and 2-d child windows )
*/
    {
    static BYTE abStack[4096] ;  
    static PRINTPARAMS tp ;
    static char szBusy[] = "Busy - try again later" ;
    static char szStart[] = "Printing started" ;
    static HEV semPrint = 0L ;
    static HWND hwndCancel = NULLHANDLE ;
    char szTemp[32] ;
    unsigned short lErr ;
    PBYTE pStack = abStack;
    TID tid ;
    char *pszMess, *szPrinter ;

    if( semPrint == 0L ) {
        DosCreateMutexSem( NULL, &semPrint, 0L, 0L ) ;
        }

    switch( msg ) {
        
        case WM_USER_PRINT_BEGIN:
        
            if( DosRequestMutexSem( semPrint, SEM_IMMEDIATE_RETURN ) != 0 ) {
                pszMess = szBusy ;
                WinMessageBox( HWND_DESKTOP,
                               hWnd, 
                               pszMess,
                               APP_NAME,
                               0,
                               MB_OK | MB_ICONEXCLAMATION ) ;
                }
            else {
                pszMess = szStart ;
                tp.hwnd = hWnd ;
                tp.pqp = (PQPRINT) mp1 ;
                tp.hps = (HPS) mp2 ;
                strcpy( tp.szPrintFile, szPrintFile ) ;
                tid = _beginthread( ThreadPrintPage, NULL, 32768, &tp ) ;
                hwndCancel = WinLoadDlg( HWND_DESKTOP,
                                         hWnd,
                                         (PFNWP)CancelPrintDlgProc,
                                         0L,
                                         ID_PRINTSTOP,
                                         NULL ) ; 
                }
            break ;


        case WM_USER_PRINT_OK :

            if( hwndCancel != NULLHANDLE ) {
                WinDismissDlg( hwndCancel, 0 ) ; 
                hwndCancel = NULLHANDLE ;
                }
             DosReleaseMutexSem( semPrint ) ;
             break ;

        case WM_USER_DEV_ERROR :

            if( hwndCancel != NULLHANDLE ) {
                WinDismissDlg( hwndCancel, 0 ) ; 
                hwndCancel = NULLHANDLE ;
                }
            lErr = ERRORIDERROR( (ERRORID) mp1 ) ;
            sprintf( szTemp, "Dev error: %d %x", lErr, lErr ) ;
            WinMessageBox( HWND_DESKTOP,
                           hWnd, 
                           szTemp,
                           APP_NAME,
                           0,
                           MB_OK | MB_ICONEXCLAMATION ) ;
             DosReleaseMutexSem( semPrint ) ;
             break ;

        case WM_USER_PRINT_ERROR :
        
            if( hwndCancel != NULLHANDLE ) {
                WinDismissDlg( hwndCancel, 0 ) ; 
                hwndCancel = NULLHANDLE ;
                }
            lErr = ERRORIDERROR( (ERRORID) mp1 ) ;
            sprintf( szTemp, "Print error: %d %x", lErr, lErr ) ;
            WinMessageBox( HWND_DESKTOP,
                           hWnd, 
                           szTemp,
                           APP_NAME,
                           0,
                           MB_OK | MB_ICONEXCLAMATION ) ;
             DosReleaseMutexSem( semPrint ) ;
             break ;

        case WM_USER_PRINT_CANCEL :

             DevEscape( tp.hdc, DEVESC_ABORTDOC, 0L, NULL, NULL, NULL ) ;
             break ;


        case WM_USER_PRINT_QBUSY :

            return( (MPARAM)DosRequestMutexSem( semPrint, SEM_IMMEDIATE_RETURN ) ) ;
            
        default : break ;
        }
        
    return 0L ;
    }

int SetupPrinter( HWND hwnd, PQPRINT pqp )
/*
**  Set up the printer
**
*/
    {
    HDC hdc ;
    float flXFrac, flYFrac;
          /* check that printer is still around .. */
    if( FindPrinter( pqp->szPrinterName, pqp->piPrinter ) != 0 ) return 0 ;
          /* get printer capabilities */
    if( (hdc = OpenPrinterDC( WinQueryAnchorBlock( hwnd ), pqp, OD_INFO, NULL )) != DEV_ERROR ) {
        DevQueryCaps( hdc, CAPS_TECHNOLOGY, (long)sizeof(prCaps)/sizeof(long), (PLONG)&prCaps ) ;
        DevCloseDC( hdc ) ;
        pqp->xsize = (float)100.0* (float) prCaps.lWidth / (float) prCaps.lHorRes ; // in cm
        pqp->ysize = (float)100.0* (float) prCaps.lHeight / (float) prCaps.lVertRes ; // in cm
        flXFrac = pqp->xfrac ;
        flYFrac = pqp->yfrac ;
        pqp->szFilename[0] = 0 ;
        szPrintFile[0] = 0 ;
        pqp->caps  = prCaps.lTech & (CAPS_TECH_VECTOR_PLOTTER|CAPS_TECH_POSTSCRIPT) ?
                   QP_CAPS_FILE : QP_CAPS_NORMAL ;        
        if( WinDlgBox( HWND_DESKTOP,
                      hwnd,
                    (PFNWP)QPrintDlgProc,
                    0L,
                    ID_QPRINT,
                    pqp ) == DID_OK ) {
          if( pqp->caps & QP_CAPS_FILE ) {
              if( pqp->szFilename[0] != 0 ) strcpy( szPrintFile, pqp->szFilename ) ;
              }
          return 1 ;
          }    
        pqp->xfrac = flXFrac ;
        pqp->yfrac = flYFrac ;
        }
    
    return 0 ;
    }

int SetPrinterMode( HWND hwnd, PQPRINT pqp )
/*
**  call up printer driver's own setup dialog box
**
**  returns :  -1 if error
**              0 if no settable modes
**              1 if OK
*/
    {
    HAB hab ;
    LONG lBytes ;
    PPRQINFO3 pinfo = pqp->piPrinter ;

    hab = WinQueryAnchorBlock( hwnd ) ;
    driv.szDeviceName[0]='\0' ;
    lBytes = DevPostDeviceModes( hab,
                                 NULL,
                                 devop.pszDriverName,
                                 pinfo->pDriverData->szDeviceName,
                                 //driv.szDeviceName,
                                 NULL,
                                 DPDM_POSTJOBPROP ) ;
    if( lBytes > 0L ) {
            /* if we have old pdriv data, and if it's for the same printer,
               keep it to retain user's current settings, else get new */
        if( pqp->pdriv != NULL
        && strcmp( pqp->pdriv->szDeviceName, pinfo->pDriverData->szDeviceName ) != 0 ) {
            free( pqp->pdriv ) ;
            pqp->pdriv = NULL ;
            }
        if( pqp->pdriv == NULL ) {
            if( lBytes < pinfo->pDriverData->cb ) lBytes = pinfo->pDriverData->cb ;
            pqp->pdriv = malloc( lBytes ) ;
            pqp->cbpdriv = lBytes ;
            memcpy( pqp->pdriv, pinfo->pDriverData, lBytes ) ;
            }
        strcpy( driv.szDeviceName, pqp->pdriv->szDeviceName ) ;
//        pqp->pdriv->szDeviceName[0] = '\0' ;  /* to check if 'cancel' selected */
        lBytes = DevPostDeviceModes( hab,
                                     pqp->pdriv,
                                     devop.pszDriverName,
                                     driv.szDeviceName,
                                     NULL,
                                     DPDM_POSTJOBPROP ) ;
        if( lBytes != 1 /*pqp->pdriv->szDeviceName[0] == '\0'*/ ) {  /* could be: 'cancel' selected */
            pqp->cbpdriv = lBytes = 0 ;
            free(pqp->pdriv ) ;   /* is this right ???? */
            pqp->pdriv = NULL ;
            }
        }
    return ( (int) lBytes ) ;
    }

static void ThreadPrintPage( PRINTPARAMS *ptp )
/*
**  thread to set up printer DC and print page 
**
**  Input: THREADPARAMS *ptp -- pointer to thread data passed by beginthread
**
*/
    {    
    HAB         hab ;       // thread anchor block nandle
    HDC         hdc ;       // printer device context handle
    HPS         hps ;       // presentation space handle
    HDC         hdcOld ;    // old hdc associated with hps
    SHORT       msgRet ;    // message posted prior to return (end of thread)
    SIZEL       sizPage ;   // size of page for creation of presentation space
    LONG        alPage[2] ; // actual size of printer page in pixels
    RECTL       rectPage ;  // viewport on page into which we draw
    LONG        lColors ;
    char        *szPrintFile ;
    HMF         hmf ;
    LONG        alOpt[9] ;
    HPS         hpsSc ;    
    hab = WinInitialize( 0 ) ;
    
    szPrintFile = ptp->szPrintFile[0] == '\0' ? NULL : ptp->szPrintFile ;
    
    if( (hdc = OpenPrinterDC( hab, ptp->pqp, 0L, szPrintFile )) != DEV_ERROR ) {
    
            // create presentation space for printer

        ptp->hdc = hdc ;
        hmf = CopyToMetaFile( ptp->hps ) ;
        hpsSc = ptp->hps ;

        sizPage.cx = GNUXPAGE;
        sizPage.cy = GNUYPAGE;
        hps = GpiCreatePS( hab,
                           hdc,
                           &sizPage,
                           PU_HIMETRIC | GPIF_DEFAULT | GPIT_NORMAL | GPIA_ASSOC ) ;

        DevQueryCaps( hdc, CAPS_WIDTH, 2L, alPage ) ;
        DevQueryCaps( hdc, CAPS_PHYS_COLORS, 1L, &lColors ) ;
        rectPage.xLeft = 0L ;
        rectPage.xRight = alPage[0] ;
        rectPage.yTop = alPage[1] ;//alPage[1]*(1.0-flYFrac) ;
        rectPage.yBottom = 0L ; //  = alPage[1] ;
       
        {
        double ratio = 1.560 ;
        double xs = rectPage.xRight - rectPage.xLeft ;
        double ys = rectPage.yTop - rectPage.yBottom ;
        if( ys > xs/ratio ) { /* reduce ys to fit */
            rectPage.yTop = rectPage.yBottom + (int)(xs/ratio) ; 
            }
        else if( ys < xs/ratio ) { /* reduce xs to fit */
            rectPage.xRight = rectPage.xLeft + (int)(ys*ratio) ;
            }
        }
       
        rectPage.xRight = rectPage.xRight*ptp->pqp->xfrac ;
        rectPage.yTop = rectPage.yTop*ptp->pqp->yfrac ;//alPage[1]*(1.0-flYFrac) ;

        {
        double ratio = 1.560 ;
        double xs = rectPage.xRight - rectPage.xLeft ;
        double ys = rectPage.yTop - rectPage.yBottom ;
        if( ys > xs/ratio ) { /* reduce ys to fit */
            rectPage.yTop = rectPage.yBottom + (int)(xs/ratio) ; 
            }
        else if( ys < xs/ratio ) { /* reduce xs to fit */
            rectPage.xRight = rectPage.xLeft + (int)(ys*ratio) ;
            }
        }


            // start printing
                    
        if( DevEscape( hdc, 
                       DEVESC_STARTDOC,
                       7L,
                       APP_NAME,
                       NULL,
                       NULL ) != DEVESC_ERROR ) {
            char buff[256] ;
            int rc;

            rc = GpiSetPageViewport( hps, &rectPage ) ;

            alOpt[0] = 0L ;
            alOpt[1] = LT_ORIGINALVIEW ;
            alOpt[2] = 0L ;
            alOpt[3] = LC_LOADDISC ;
            alOpt[4] = RES_DEFAULT ;
            alOpt[5] = SUP_DEFAULT ;
            alOpt[6] = CTAB_DEFAULT ;
            alOpt[7] = CREA_DEFAULT ;
            alOpt[8] = DDEF_DEFAULT ;
            if (rc) rc=GpiPlayMetaFile( hps, hmf, 9L, alOpt, NULL, 255, buff ) ;

            if (rc) {
              DevEscape( hdc, DEVESC_ENDDOC, 0L, NULL, NULL, NULL ) ;
              msgRet = WM_USER_PRINT_OK ;
              }
            else
              msgRet = WM_USER_PRINT_ERROR;
              
            }
        else
            msgRet = WM_USER_PRINT_ERROR ;
        
        GpiDestroyPS( hps ) ;
        DevCloseDC( hdc ) ;
        }
    else
        msgRet = WM_USER_DEV_ERROR ;
        
    DosEnterCritSec() ;
    WinPostMsg( ptp->hwnd, msgRet, (MPARAM)WinGetLastError(hab), 0L ) ;
    WinTerminate( hab ) ;
    }

HDC OpenPrinterDC( HAB hab, PQPRINT pqp, LONG lMode, char *szPrintFile )
/*
** get printer info from os2.ini and set up DC
**
** Input:  HAB hab  -- handle of anchor block of printing thread
**         PQPRINT-- pointer to data of current selected printer
**         LONG lMode -- mode in which device context is opened = OD_QUEUED, OD_DIRECT, OD_INFO
**         char *szPrintFile -- name of file for printer output, NULL
**                  if to printer (only used for devices that support file
**                  output eg plotter, postscript)
**
** Return: HDC      -- handle of printer device context
**                   = DEV_ERROR (=0) if error
*/
    {
    CHAR   *pchDelimiter ;
    LONG   lType ;
    static CHAR   achPrinterData[256] ;

    if( pqp->piPrinter == NULL ) return DEV_ERROR ;
        
    strcpy( achPrinterData, pqp->piPrinter->pszDriverName ) ;
    achPrinterData[ strcspn(achPrinterData,".") ] = '\0' ;

    devop.pszDriverName = achPrinterData ;
    devop.pszLogAddress = pqp->piPrinter->pszName ;

    if( pqp->pdriv != NULL 
        && strcmp( pqp->pdriv->szDeviceName, pqp->piPrinter->pDriverData->szDeviceName ) == 0 ) {
        devop.pdriv = pqp->pdriv ;
        }
    else devop.pdriv = pqp->piPrinter->pDriverData ;

    if( szPrintFile != NULL )  devop.pszLogAddress = szPrintFile ;
        
            // set data type to RAW
            
    devop.pszDataType = "PM_Q_RAW" ;
    
            // open device context
    if( lMode != 0L ) 
        lType = lMode ;
    else
        lType = (szPrintFile == NULL) ? OD_QUEUED: OD_DIRECT ;

    return DevOpenDC( hab, //  WinQueryAnchorBlock( hwnd ),
                      lType,
                      "*",
                      4L,
                      (PDEVOPENDATA) &devop,
                      NULLHANDLE ) ;
    }

int FindPrinter( char *szName, PPRQINFO3 piPrinter )
/*
**  Find a valid printer
*/
    {
    PPRQINFO3 pprq = NULL ;
    PDRIVDATA pdriv = NULL ;
    LONG np ;
    
    if( *szName && (strcmp( szName, piPrinter->pszName ) == 0) ) return 0 ;
    if( GetPrinters( &pprq , &np ) == 0 ) return 1 ;
    for( --np; np>=0; np-- ) {
        if( strcmp( szName, pprq[np].pszName ) == 0 ) {
            if( piPrinter->pDriverData != NULL ) free( piPrinter->pDriverData ) ;
            pdriv = malloc( pprq[np].pDriverData->cb ) ;
            memcpy( piPrinter, &pprq[np], sizeof( PRQINFO3 ) ) ;
            piPrinter->pDriverData = pdriv ;
            memcpy( pdriv, pprq[np].pDriverData, pprq[np].pDriverData->cb ) ;
            free( pprq ) ;
            return 0 ;
            }
        }
    memcpy( piPrinter, &pprq[0], sizeof( PRQINFO3 ) ) ;
    free( pprq ) ;
    return 0 ;
    }

MRESULT EXPENTRY CancelPrintDlgProc ( HWND hwnd, ULONG usMsg, MPARAM mp1, MPARAM mp2 )
/*
**  Cancel printing dialog box proc
*/
    {
    switch ( usMsg ) {

        case WM_COMMAND :
            switch ( SHORT1FROMMP(mp1) ) {
                case DID_CANCEL:
                    WinSendMsg( WinQueryWindow( hwnd, QW_OWNER ),
                                    WM_USER_PRINT_CANCEL,
                                    0L,
                                    0L ) ;
                    WinDismissDlg( hwnd, 0 ) ; 
                    break ;
                default:
                    break ;
                }
        default:
            break ;
        }
        /* fall through to the default control processing */
    return WinDefDlgProc ( hwnd , usMsg , mp1 , mp2 ) ;
    }