#ifdef INCRCSDATA
static char RCSid[]="$Id: gclient.c%v 3.38.2.96 1993/03/15 21:39:29 woo Exp woo $" ;
#endif

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

    PROGRAM: Gnupmdrv
    
    MODULE:  gclient.c
        
    This file contains the client window procedures for Gnupmdrv
    
****************************************************************************/

/*
 * PM driver for GNUPLOT
 * Copyright (C) 1992   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 modified code.  Modifications are to be distributed 
 * as patches to released version.
 *  
 * This software is provided "as is" without express or implied warranty.
 * 
 *
 * 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 
 *  info-gnuplot-request@dartmouth.edu.  
 * Send bug reports to
 *  bug-gnuplot@dartmouth.edu.
**/

#define INCL_PM
#define INCL_WIN
#define INCL_SPL
#define INCL_SPLDOSPRINT
#define INCL_WINSTDFONT
#define INCL_DOSMEMMGR
#define INCL_DOSPROCESS
#define INCL_DOSERRORS
#define INCL_DOSFILEMGR
#define INCL_DOSNMPIPES
#define INCL_DOSSESMGR
#define INCL_DOSSEMAPHORES
#define INCL_DOSMISC
#define INCL_DOSQUEUES
#define INCL_WINSWITCHLIST
#include <os2.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <process.h>
#include "gnupmdrv.h"

/*==== g l o b a l    d a t a ================================================*/

extern char szIPCName[] ;       /* name used in IPC with gnuplot */

/*==== l o c a l    d a t a ==================================================*/

#define   GNUBUF    1024        /* buffer for gnuplot commands */
#define   PIPEBUF   4096        /* size of pipe buffers */
#define   CMDALLOC  4096        /* command buffer allocation increment (ints) */
#define   ENVSIZE   2048        /* size of environment */ 
#define   GNUPAGE   4096        /* size of gnuplot page in pixels (driver dependent) */

#define   PAUSE_DLG 1           /* pause handled in dialog box */
#define   PAUSE_BTN 2           /* pause handled by menu item */
#define   PAUSE_GNU 3           /* pause handled by Gnuplot */

static HWND     hWndstart ;     /* used for errors in startup */
static ULONG    pidGnu=0L ;      /* gnuplot session id */
static ULONG    ppidGnu=0L ;      /* gnuplot pid */
static HPS      hpsScreen ;     /* screen pres. space */

static HSWITCH hSwitch = 0 ;    /* switching between windows */
static SWCNTRL swGnu ;

static BOOL     bLineTypes = FALSE ;
static BOOL     bLineThick = FALSE ;
static BOOL     bColours = TRUE ;
static BOOL     bShellPos = FALSE ;
static BOOL     bPlotPos = FALSE ;
static ULONG    ulPlotPos[4] ;
static ULONG    ulShellPos[4] ;
static char     szFontNameSize[FONTBUF] ;
static char     achPrinterName[128] = "" ;
static PRQINFO3 infPrinter = { "" } ;
static HMTX     semCommands ;
static HEV      semStartSeq ;   /* semaphore to start things in right sequence */
static HEV      semPause ;
static ULONG    ulPauseReply = 1 ;
static ULONG    ulPauseMode  = PAUSE_DLG ;

            /* commands from gnuplot come via this ... */
            
static HPIPE    hRead = 0L ; 
           
            /* stuff for screen-draw thread control */
            
static BOOL     bExist ; 
static BOOL     bStopDraw ; 
static HEV      semDrawDone ;
static HEV      semStartDraw ;

            /* command buffer */
            
static int ncalloc = 0 ;
static int ic = 0 ;
static volatile int *commands = NULL ;

/*==== f u n c t i o n s =====================================================*/

int             DoPrint( HWND ) ;
MRESULT         WmClientCmdProc( HWND , ULONG, MPARAM, MPARAM ) ; 
void            ChangeCheck( HWND, USHORT, USHORT ) ;
BOOL            QueryIni( HAB ) ;
static void     SaveIni( void ) ;
static void     ThreadDraw( void ) ;
static void     DoPaint( HWND, HPS ) ;
static void     Display( void ) ;
void            SelectFont( HPS, char *, short );
static void     ReadGnu( void ) ;
static void     WaitEnd( void ) ;
static void     AllocMore() ;
static int      BufRead( HFILE, void*, int, PULONG ) ;
int             GetNewFont( HWND, HPS ) ;


/*==== c o d e ===============================================================*/

MRESULT EXPENTRY DisplayClientWndProc(HWND hWnd, ULONG message, MPARAM mp1, MPARAM mp2)
/*
**  Window proc for main window
**  -- passes most stuff to active child window via WmClientCmdProc
**  -- passes DDE messages to DDEProc
*/
{
    HDC     hdcScreen ;
    SIZEL   sizlPage ;
    TID     tidDraw, tidSpawn ;
    char    *pp ;
    ULONG   ulID ;
    ULONG   ulFlag ;
    char    szErrs[128] ;
    
    switch (message) {

        case WM_CREATE:

                // set initial values
            ChangeCheck( hWnd, IDM_LINES_THICK, bLineThick?IDM_LINES_THICK:0 ) ;
            ChangeCheck( hWnd, IDM_LINES_SOLID, bLineTypes?0:IDM_LINES_SOLID ) ;
            ChangeCheck( hWnd, IDM_COLOURS, bColours?IDM_COLOURS:0 ) ;
            hWndstart = hWnd ;  /* used in ReadGnu for errors */ 
                // disable close from system menu (close only from gnuplot)
            WinSendMsg( WinWindowFromID( WinQueryWindow( hWnd, QW_PARENT ), FID_SYSMENU ),
                        MM_SETITEMATTR,
                        MPFROM2SHORT(SC_CLOSE, TRUE ),
                        MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED ) ) ;
                // setup semaphores
            DosCreateMutexSem( NULL, &semCommands, 0L, 0L ) ;
            DosCreateEventSem( NULL, &semStartDraw, 0L, 0L ) ;
            DosCreateEventSem( NULL, &semDrawDone, 0L, 0L ) ;
            DosCreateEventSem( NULL, &semStartSeq, 0L, 0L ) ;
            DosCreateEventSem( NULL, &semPause, 0L, 0L ) ;
            bStopDraw = FALSE ;
            bExist = TRUE ;
                // create a dc and hps to draw on the screen
            hdcScreen = WinOpenWindowDC( hWnd ) ;
            sizlPage.cx = GNUPAGE ; sizlPage.cy = GNUPAGE ;
            hpsScreen = GpiCreatePS( hab, hdcScreen, &sizlPage,
                           PU_ARBITRARY|GPIT_MICRO|GPIA_ASSOC) ;
                // spawn a thread to do the drawing
            DosCreateThread( &tidDraw, (PFNTHREAD)ThreadDraw, 0L, 0L, 8192L ) ;
                // then spawn server for GNUPLOT ...
            DosCreateThread( &tidSpawn, (PFNTHREAD)ReadGnu, 0L, 0L, 8192L ) ;
            DosWaitEventSem( semStartSeq, SEM_INDEFINITE_WAIT ) ;
                // get details of command-line window
            hSwitch = WinQuerySwitchHandle( 0, ppidGnu ) ;
            WinQuerySwitchEntry( hSwitch, &swGnu ) ;
                // set size of this window
            WinSetWindowPos( WinQueryWindow( hWnd, QW_PARENT ), 
                             HWND_TOP,
                             ulShellPos[0],
                             ulShellPos[1],
                             ulShellPos[2],
                             ulShellPos[3], 
                             bShellPos?SWP_SIZE|SWP_MOVE|SWP_SHOW|SWP_ACTIVATE:SWP_SHOW|SWP_ACTIVATE ) ;
                //  clear screen 
            DosPostEventSem( semDrawDone ) ;  
            break ;
            

        case WM_COMMAND:

            return WmClientCmdProc( hWnd , message , mp1 , mp2 ) ;

        case WM_CLOSE:

            if( WinSendMsg( hWnd, WM_USER_PRINT_QBUSY, 0L, 0L ) != 0L ) {
                WinMessageBox( HWND_DESKTOP,
                               hWnd, 
                               "Still printing - not closed",
                               APP_NAME,
                               0,
                               MB_OK | MB_ICONEXCLAMATION ) ;
                return 0L ;
                }
      	    return (WinDefWindowProc(hWnd, message, mp1, mp2));

        case WM_PAINT:

            DoPaint( hWnd, hpsScreen ) ;
            break ;     

        case WM_SIZE :
            
            WinInvalidateRect( hWnd, NULL, TRUE ) ;
            break ;

        case WM_PRESPARAMCHANGED:
        
            pp = malloc(FONTBUF) ;
            if( WinQueryPresParam( hWnd, 
                                   PP_FONTNAMESIZE, 
                                   0, 
                                   &ulID, 
                                   FONTBUF, 
                                   pp, 
                                   QPF_NOINHERIT ) != 0L ) {
                strcpy( szFontNameSize, pp ) ;
                WinInvalidateRect( hWnd, NULL, TRUE ) ;
                }
            free(pp) ; 
            break ;
            
        case WM_USER_PRINT_BEGIN:
        case WM_USER_PRINT_OK :
        case WM_USER_DEV_ERROR :
        case WM_USER_PRINT_ERROR :
        case WM_USER_PRINT_QBUSY :

            return( PrintCmdProc( hWnd, message, mp1, mp2 ) ) ;

        case WM_GNUPLOT:
                // display the plot         
            WinSetWindowPos( hwndFrame, HWND_TOP, 0,0,0,0, SWP_ACTIVATE|SWP_ZORDER ) ;
            WinInvalidateRect( hWnd, NULL, TRUE ) ;
            return 0L ;

        case WM_PAUSEPLOT:
                /* put pause message on screen, or enable 'continue' button */
            if( ulPauseMode == PAUSE_DLG ) {
                WinLoadDlg( HWND_DESKTOP,
                            hWnd,
                            (PFNWP)PauseMsgDlgProc,
                            0L,
                            IDD_PAUSEBOX,
                            (char*)mp1 ) ; 
                }
            else { 
                WinEnableMenuItem( WinWindowFromID( 
                                   WinQueryWindow( hWnd, QW_PARENT ), FID_MENU ),
                                   IDM_CONTINUE,
                                   TRUE ) ;            
                }
            return 0L ;

        case WM_PAUSEEND:
            /* resume plotting */
            ulPauseReply = (ULONG) mp1 ;
            DosPostEventSem( semPause ) ;
            return 0L ;
    
	default:         /* Passes it on if unproccessed    */
	    return (WinDefWindowProc(hWnd, message, mp1, mp2));
    }
    return (NULL);
}

MRESULT WmClientCmdProc(HWND hWnd, ULONG message, MPARAM mp1, MPARAM mp2)
/*
**   Handle client window command (menu) messages
**   -- mostly passed on to active child window
**
*/
    {
    ULONG usDlg ;
    extern HWND hApp ;
    static ulPauseItem = IDM_PAUSEDLG ;

    switch( (USHORT) SHORT1FROMMP( mp1 ) ) {
                
        case IDM_ABOUT :    /* show the 'About' box */
             
            WinDlgBox( HWND_DESKTOP,
                       hWnd , 
                       (PFNWP)About ,
                       0L,
                       ID_ABOUT, 
                       NULL ) ;
            break ;


        case IDM_PRINT :    /* print plot */

            if( SetupPrinter( hWnd, achPrinterName, &infPrinter ) )
                WinPostMsg( hWnd, WM_USER_PRINT_BEGIN, (MPARAM)&infPrinter, 0L ) ; 
            break ;

        case IDM_PRINTSETUP :    /* select printer */

            WinDlgBox( HWND_DESKTOP,
                       hWnd ,
                       (PFNWP)QPrintersDlgProc,
                       0L,
                       IDD_QUERYPRINT, 
                       achPrinterName ) ;
            break ;

        case IDM_LINES_THICK:
                // change line setting
            bLineThick = !bLineThick ;
            ChangeCheck( hWnd, IDM_LINES_THICK, bLineThick?IDM_LINES_THICK:0 ) ;
            WinInvalidateRect( hWnd, NULL, TRUE ) ;
            break ;

        case IDM_LINES_SOLID:
                // change line setting
            bLineTypes = !bLineTypes ;
            ChangeCheck( hWnd, IDM_LINES_SOLID, bLineTypes?0:IDM_LINES_SOLID ) ;
            WinInvalidateRect( hWnd, NULL, TRUE ) ;
            break ;

        case IDM_COLOURS:
                // change colour setting
            bColours = !bColours ;        
            ChangeCheck( hWnd, IDM_COLOURS, bColours?IDM_COLOURS:0 ) ;
            WinInvalidateRect( hWnd, NULL, TRUE ) ;
            break ;

        case IDM_FONTS:
        
            if( GetNewFont( hWnd, hpsScreen ) ) 
                WinInvalidateRect( hWnd, NULL, TRUE ) ;
            break ;
            
        case IDM_SAVE :
            SaveIni() ;
            break ;
            
        case IDM_COMMAND:       /* go back to GNUPLOT command window */            
            WinSwitchToProgram( hSwitch ) ;
            break ;

        case IDM_CONTINUE:
            WinPostMsg( hWnd, WM_PAUSEEND, 1L, 0L ) ; 
            WinEnableMenuItem( WinWindowFromID( 
                               WinQueryWindow( hWnd, QW_PARENT ), FID_MENU ),
                               IDM_CONTINUE,
                               FALSE ) ;            
            break ;
            
        case IDM_PAUSEGNU:  /* gnuplot handles pause */
            ChangeCheck( hWnd, ulPauseItem, IDM_PAUSEGNU ) ;
            ulPauseItem = IDM_PAUSEGNU ;
            ulPauseMode = PAUSE_GNU ;
            break ;
            
        case IDM_PAUSEDLG:  /* pause message in dlg box */
            ChangeCheck( hWnd, ulPauseItem, IDM_PAUSEDLG ) ;
            ulPauseItem = IDM_PAUSEDLG ;
            ulPauseMode = PAUSE_DLG ;
            break ;
            
        case IDM_PAUSEBTN:  /* pause uses menu button, no message */
            ChangeCheck( hWnd, ulPauseItem, IDM_PAUSEBTN ) ;
            ulPauseItem = IDM_PAUSEBTN ;
            ulPauseMode = PAUSE_BTN ;
            break ;
          
        case IDM_HELPFORHELP:
            WinSendMsg(WinQueryHelpInstance(hWnd),
                       HM_DISPLAY_HELP, 0L, 0L ) ;
            return 0L ;

        case IDM_EXTENDEDHELP:
            WinSendMsg(WinQueryHelpInstance(hWnd),
                        HM_EXT_HELP, 0L, 0L);
            return 0L ;

        case IDM_KEYSHELP:
            WinSendMsg(WinQueryHelpInstance(hWnd),
                       HM_KEYS_HELP, 0L, 0L);
            return 0L ;

        case IDM_HELPINDEX:
            WinSendMsg(WinQueryHelpInstance(hWnd),
                       HM_HELP_INDEX, 0L, 0L);
            return 0L ;

        default : 
    
            return WinDefWindowProc( hWnd, message, mp1, mp2 ) ;

        }  
    return( NULL ) ;
    }                      

void ChangeCheck( HWND hWnd , USHORT wItem1 , USHORT wItem2 )
/*
**  Utility function:
**
**  move check mark from menu item 1 to item 2
*/
    {
    HWND hMenu ;

    hMenu = WinWindowFromID( WinQueryWindow( hWnd, QW_PARENT ), 
                             FID_MENU ) ;            
    if( wItem1 != 0 )
        WinSendMsg( hMenu,
                    MM_SETITEMATTR,
                    MPFROM2SHORT( wItem1, TRUE ),
                    MPFROM2SHORT( MIA_CHECKED, 0 ) ) ;
    if( wItem2 != 0 )
        WinSendMsg( hMenu,
                    MM_SETITEMATTR,
                    MPFROM2SHORT( wItem2, TRUE ),
                    MPFROM2SHORT( MIA_CHECKED, MIA_CHECKED ) ) ;
    }       

BOOL QueryIni( HAB hab )
/*
** Query INI file
*/
    {
    BOOL         bPos, bData ;
    ULONG        ulOpts[4] ;
    HINI         hini ;
    ULONG        ulCB ;
    char         *p ;
    
            // get default printer name    
            
    PrfQueryProfileString( HINI_PROFILE,
                           "PM_SPOOLER",
                           "PRINTER",
                           ";",
                           achPrinterName,
                           (long) sizeof achPrinterName ) ;
    if( (p=strchr( achPrinterName, ';' )) != NULL ) *p = '\0' ;
    
            // read gnuplot ini file

    hini = PrfOpenProfile( hab, GNUINI ) ;
    ulCB = sizeof( ulShellPos ) ;
    bPos = PrfQueryProfileData( hini, APP_NAME, INISHELLPOS, &ulShellPos, &ulCB ) ;
    ulCB = sizeof( ulOpts ) ;
    bData = PrfQueryProfileData( hini, APP_NAME, INIOPTS, &ulOpts, &ulCB ) ;
    if( bData ) {
        bLineTypes = (BOOL)ulOpts[0] ;
        bLineThick = (BOOL)ulOpts[1] ;
        bColours = (BOOL)ulOpts[2] ;
        ulPauseMode = ulOpts[3] ;
        }
    else {
        bLineTypes = FALSE ;  /* default values */
        bLineThick = FALSE ;
        bColours = TRUE ;
        ulPauseMode = 1 ;
        }
    PrfQueryProfileString( hini, APP_NAME, INIFONT, INITIAL_FONT, 
                                 szFontNameSize, FONTBUF ) ;
    PrfCloseProfile( hini ) ;
    bShellPos = bPos ;
    return bPos ;
    }

static void SaveIni( )
/*
** save data in ini file
*/
    {
    SWP swp ;
    HINI  hini ;
    ULONG ulOpts[4] ;
    
    hini = PrfOpenProfile( hab, GNUINI ) ;
    WinQueryWindowPos( hwndFrame, &swp ) ;
    ulPlotPos[0] = swp.x ;
    ulPlotPos[1] = swp.y ;
    ulPlotPos[2] = swp.cx ;
    ulPlotPos[3] = swp.cy ;
    PrfWriteProfileData( hini, APP_NAME, INISHELLPOS, &ulPlotPos, sizeof(ulPlotPos) ) ;
/*
    WinQueryWindowPos( swGnu.hwnd, &swp ) ;
    ulPlotPos[0] = swp.x ;
    ulPlotPos[1] = swp.y ;
    ulPlotPos[2] = swp.cx ;
    ulPlotPos[3] = swp.cy ;
    PrfWriteProfileData( hini, APP_NAME, INIPLOTPOS, &ulPlotPos, sizeof(ulPlotPos) ) ;
*/
    ulOpts[0] = (ULONG)bLineTypes ;
    ulOpts[1] = (ULONG)bLineThick ; 
    ulOpts[2] = (ULONG)bColours ;
    ulOpts[3] = ulPauseMode ; 
    PrfWriteProfileData( hini, APP_NAME, INIOPTS, &ulOpts, sizeof(ulOpts) ) ;
    PrfWriteProfileString( hini, APP_NAME, INIFONT, szFontNameSize ) ;
    PrfCloseProfile( hini ) ;
    }

static void DoPaint( HWND hWnd, HPS hps  ) 
/*
**  Paint the screen with current data 
*/
    {
    RECTL rectClient ;
    ULONG ulCount ;    
    bStopDraw = TRUE ;    // stop any drawing in progress and wait for 
                          // thread to signal completion 
    DosWaitEventSem( semDrawDone, SEM_INDEFINITE_WAIT ) ;
    DosResetEventSem( semDrawDone, &ulCount ) ;
    WinBeginPaint( hWnd , hps, NULL ) ;
    DosPostEventSem( semStartDraw ) ;  // start drawing
    }

static void ThreadDraw( )
/*
**  Thread to draw plot
*/
    {
    HAB  hab ;
    RECTL rectClient ;
    ULONG ulCount ;

        /* initialize and wait until ready to draw */
    
    hab = WinInitialize( 0 ) ;

        /* ok - draw until window is destroyed */

    while( bExist )  {

                // indicate access to window 
        DosWaitEventSem( semStartDraw, SEM_INDEFINITE_WAIT ) ;
        DosResetEventSem( semStartDraw, &ulCount ) ;

                // will be set TRUE if we decide to stop in the middle, but now
        bStopDraw = FALSE ;         
        
        GpiResetPS( hpsScreen, GRES_ALL ) ;        
        WinQueryWindowRect( hApp, (PRECTL)&rectClient ) ;
        GpiSetPageViewport( hpsScreen, &rectClient ) ;

        WinFillRect( hpsScreen, &rectClient, CLR_WHITE ) ;

        ScalePS( hpsScreen, &rectClient, 0 ) ;

        PlotThings( hpsScreen, 0L ) ;
                // ok, say that we did it

        WinEndPaint( hpsScreen ) ;
        DosPostEventSem( semDrawDone ) ;
        }
   
    WinTerminate( hab ) ;
    }


enum JUSTIFY { LEFT, CENTRE, RIGHT } jmode;

void PlotThings( HPS hps, long lColour ) 
/*
**  Plot a spectrum and related graphs on the designated presentation space
**
**  Input:
**          HPS hps         -- presentation space handle of plot ps
**          long lColour    -- number of physical colours, used mainly by
**                             printer drivers to set black & white mode.
**                             If 0, assume screen display
**
**  Note: use semaphore to prevent access to command list while
**        pipe thread is reallocating the list.
*/
    {
    int i, lt, ta, col, sl, n, x, y, cx, cy, width ;
    int icnow ;
    int cmd ;
    char *str, *buf ;
    long sw ;
    POINTL ptl ;
    POINTL aptl[4] ;    
    FONTMETRICS fm ;
    HDC hdc ;
    long lVOffset ;
    long yDeviceRes ;
    long lCurCol ;
    long lOldLine = 0 ;
    BOOL bBW ;
    GRADIENTL grdl ;
    BOOL bHorz = TRUE ;
    SIZEF sizHor, sizVer ;
        /* sometime, make these user modifiable... */
    static long lLineTypes[7] = { LINETYPE_SOLID,
                                  LINETYPE_SHORTDASH,
                                  LINETYPE_DOT,
                                  LINETYPE_DASHDOT,
                                  LINETYPE_LONGDASH,
                                  LINETYPE_DOUBLEDOT,
                                  LINETYPE_DASHDOUBLEDOT } ;
    static long lCols[15] =     { CLR_BLACK,
                                  CLR_DARKGRAY,
                                  CLR_BLUE,
                                  CLR_RED,
                                  CLR_GREEN,
                                  CLR_CYAN,
                                  CLR_PINK,
                                  CLR_YELLOW,
                                  CLR_DARKBLUE,
                                  CLR_DARKRED,
                                  CLR_DARKGREEN,
                                  CLR_DARKCYAN,
                                  CLR_DARKPINK,
                                  CLR_BROWN,
                                  CLR_PALEGRAY } ;

    if( commands == NULL ) return ;

        /* check for colourless devices */

    if( lColour== 1 || lColour == 2 ) bBW = TRUE ;
    else bBW = FALSE ;

        /* get vertical offset for horizontal text strings */
        /* (0.5 em height, so string in vertically centered
           about plot position */

    GpiQueryFontMetrics( hps, sizeof( FONTMETRICS ), &fm ) ;
    lVOffset = fm.lEmHeight ;

    
   /* loop over accumulated commands from inboard driver */

    DosRequestMutexSem( semCommands, SEM_INDEFINITE_WAIT ) ;

    GpiSetLineWidth( hps, bLineThick?LINEWIDTH_THICK:LINEWIDTH_NORMAL ) ;
    for( i=0; bExist && i<ic; ) {

            if( bStopDraw ) break ;

            cmd = commands[i++];

      /*   PM_vector(x,y) - draw vector  */
            if (cmd == 'V') { 
                 ptl.x = (LONG)commands[i++] ; ptl.y = (LONG)commands[i++] ;
                 GpiLine( hps, &ptl ) ;
	         }

      /*   PM_move(x,y) - move  */
            else if (cmd == 'M')  {
                ptl.x = (LONG)commands[i++] ; ptl.y = (LONG)commands[i++] ;
                GpiMove( hps, &ptl ) ;
                }
        
      /*   PM_put_text(x,y,str) - draw text   */
            else if (cmd == 'T') { 
                x = commands[i++] ;
                y = commands[i++] ;
	        str = (char*)&commands[i] ; 
	        sl = strlen(str) ;
	        i += 1+sl/sizeof(int) ;
                lCurCol = GpiQueryColor( hps ) ;
                GpiSetColor( hps, CLR_BLACK ) ;
                GpiQueryTextBox( hps, (LONG)strlen( str ), str, 4L, aptl ) ;
                if( bHorz ) sw = aptl[3].x ;
                else sw = aptl[3].y ;
                switch(jmode) {
	            case LEFT:   sw = 0;     break;
	            case CENTRE: sw = -sw/2; break;
	            case RIGHT:  sw = -sw;   break;
                    }
                if( bHorz ) {
                    ptl.x = (LONG)(x+sw) ; ptl.y = (LONG)(y-lVOffset/4) ;
                    }
                else {
                    ptl.x = (LONG)x ; ptl.y = (LONG)(y+sw) ;
                    }
                GpiCharStringAt( hps, &ptl, (LONG) strlen( str ) , str ) ;
                GpiSetColor( hps, lCurCol ) ;
	        }

      /*   PM_justify_text(mode) - set text justification mode  */
            else if (cmd == 'J') 
    	        jmode = commands[i++] ;

      /*   PM_linetype(type) - set line type  */
      /* mapped to colour */
            else if (cmd == 'L') { 
    	        lt = commands[i++] ;
	    /* linetype = -2 axes, -1 border, 0 arrows, all to 0 */
	        col = lt ;
                if( lt == -1 ) GpiSetLineWidth( hps, LINEWIDTH_NORMAL ) ;
                else GpiSetLineWidth( hps, bLineThick?LINEWIDTH_THICK:LINEWIDTH_NORMAL ) ;
                if( lt < 0 ) lt = 0 ;
	        lt = (lt%8);
	        col = (col+2)%16 ;
                if( bLineTypes || bBW ) {
                    GpiSetLineType( hps, lLineTypes[lt] ) ;
                    }
                if( !bBW )  /* maintain some flexibility here in case we don't want
                           the model T option */ 
                    if( bColours ) GpiSetColor( hps, lCols[col] ) ;
                    else GpiSetColor( hps, CLR_BLACK ) ;
                }

            else if (cmd == 'D') { /* point/dot mode - may need colour change */
    	        lt = commands[i++] ;  /* 1: enter point mode, 0: exit */
                if( bLineTypes || bBW ) {
                    if( lt == 1 ) lOldLine = GpiSetLineType( hps, lLineTypes[0] ) ;
                    else GpiSetLineType( hps, lOldLine ) ;
                    }
                }

      /*   PM_text_angle(ang) - set text angle, 0 horz, 1 vert  */
            else if (cmd == 'A') { 
    	        ta = commands[i++] ;
                if( ta == 0 ) {
                    grdl.x = 0L ; grdl.y = 0L ;
                    GpiSetCharAngle( hps, &grdl ) ;
                    if( !bHorz ) {
                        GpiQueryCharBox( hps, &sizVer ) ;
                        sizHor.cx = sizVer.cy ; sizHor.cy = sizVer.cx ;
                        GpiSetCharBox( hps, &sizHor ) ;
                        bHorz = TRUE ;
                        }
                    }
                else if( ta == 1 ) {
                    grdl.x = 0L ; grdl.y = 1L ;
                    GpiSetCharAngle( hps, &grdl ) ;
                    if( bHorz ) {
                        GpiQueryCharBox( hps, &sizHor ) ;
                        sizVer.cx = sizHor.cy ; sizVer.cy = sizHor.cx ;
                        GpiSetCharBox( hps, &sizVer ) ;
                        bHorz = FALSE ;
                        }
                    }
                else continue ;            
                }
            }
        DosReleaseMutexSem( semCommands ) ;
    }
    
short ScalePS( HPS hps, PRECTL prect, USHORT usFlags )
/*
**  Get a font to use
**  Scale the plot area to world coords for subsequent plotting
*/
    {
    RECTL rectView, rectClient ;
    SIZEL sizePage ;
    static char *szFontName ;
    static short shFontSize ;
        
    rectClient = *prect ;
    sizePage.cx = GNUPAGE ; 
    sizePage.cy = GNUPAGE ; 

    sscanf( szFontNameSize, "%d", &shFontSize ) ;
    szFontName = strchr( szFontNameSize, '.' ) + 1 ;
    rectView.xLeft = 0L ;
    rectView.xRight = sizePage.cx ;
    rectView.yBottom = 0L ; rectView.yTop = sizePage.cy ;
    GpiSetPS( hps, &sizePage, PU_ARBITRARY ) ;
    GpiSetPageViewport( hps, &rectClient ) ;
    SelectFont( hps, szFontName, shFontSize ) ;
    GpiSetGraphicsField( hps, &rectView ) ;
    return 0 ;
    }

void SelectFont( HPS hps, char *szFont, short shPointSize )
/*
**  Select a named and sized outline (adobe) font
*/
    {
     HDC         hdc ;
     static FATTRS fat ;
     LONG   xDeviceRes, yDeviceRes ;
     POINTL ptlFont ;
     SIZEF  sizfx ;
     static LONG lcid = 0L ;

     fat.usRecordLength  = sizeof fat ;
     fat.fsSelection     = 0 ;
     fat.lMatch          = 0 ;
     fat.idRegistry      = 0 ;
     fat.usCodePage      = GpiQueryCp (hps) ;
     fat.lMaxBaselineExt = 0 ;
     fat.lAveCharWidth   = 0 ;
     fat.fsType          = 0 ;
     fat.fsFontUse       = FATTR_FONTUSE_OUTLINE |
                           FATTR_FONTUSE_TRANSFORMABLE ;

     strcpy (fat.szFacename, szFont) ;

     if( lcid == 0L ) lcid = 1L ;
     else {
        GpiSetCharSet( hps, 0L) ;
        GpiDeleteSetId( hps, lcid ) ;
        }
     GpiCreateLogFont (hps, NULL, lcid, &fat) ;
     GpiSetCharSet( hps, lcid ) ;

     hdc = GpiQueryDevice (hps) ;

     DevQueryCaps (hdc, CAPS_HORIZONTAL_RESOLUTION, 1L, &xDeviceRes) ;
     DevQueryCaps (hdc, CAPS_VERTICAL_RESOLUTION,   1L, &yDeviceRes) ;

                         // Find desired font size in pixels

     ptlFont.x = 254L * (long)shPointSize * xDeviceRes / 720000L ;
     ptlFont.y = 254L * (long)shPointSize * yDeviceRes / 720000L ;

                         // Convert to page units

     GpiConvert (hps, CVTC_DEVICE, CVTC_PAGE, 1L, &ptlFont) ;

                         // Set the character box

     sizfx.cx = MAKEFIXED (ptlFont.x, 0) ;
     sizfx.cy = MAKEFIXED (ptlFont.y, 0) ;

     GpiSetCharBox (hps, &sizfx) ;
    }

static void ReadGnu()
/*
** Thread to read plot commands from  GNUPLOT pm driver.
** Opens named pipe, then clears semaphore to allow GNUPLOT driver to proceed.
** Reads commands and builds a command list.
*/
    {
    char *szEnv ;
    char *szFileBuf ;
    ULONG rc; 
    USHORT usErr ;
    ULONG cbR ;
    STARTDATA start ;
    USHORT i ;
    PID ppid ;
    unsigned char buff[2] ;
    int len ;
    HEV hev ;
    static char *szPauseText = NULL ;
    ULONG ulPause ;
    char *pszPipeName, *pszSemName ;
    
#ifdef USEOWNALLOC
    DosAllocMem( &commands, 64*1024*1024, PAG_READ|PAG_WRITE ) ;
#endif

    DosEnterCritSec() ;
    pszPipeName = malloc( 256 ) ;
    pszSemName  = malloc( 256 ) ;
    DosExitCritSec() ;
    strcpy( pszPipeName, "\\pipe\\" ) ;
    strcpy( pszSemName, "\\sem32\\" ) ;
    strcat( pszPipeName, szIPCName ) ;
    strcat( pszSemName, szIPCName ) ;

            /* open a named pipe for communication with gnuplot */
 
    rc = DosCreateNPipe( pszPipeName,
                         &hRead, 
                         NP_ACCESS_DUPLEX|NP_NOINHERIT|NP_NOWRITEBEHIND ,
                         1|NP_WAIT|NP_READMODE_MESSAGE|NP_TYPE_MESSAGE,
                         PIPEBUF,
                         PIPEBUF,
                         0xFFFFFFFF) ;
    hev = 0 ;       /* OK, gnuplot can try to open npipe ... */
    DosOpenEventSem( pszSemName, &hev ) ;
    DosPostEventSem( hev ) ;
    

    
        /* attach to gnuplot */

    if( DosConnectNPipe( hRead ) == 0L ) {

        /* store graphics commands */
        /* use semaphore to prevent problems with drawing while reallocating
           the command buffers */

        DosRead( hRead, &ppidGnu, 4, &cbR ) ;
        DosPostEventSem( semStartSeq ) ;         /* once we've got pidGnu */


        while (1) {
        
            usErr=BufRead(hRead,buff, 1, &cbR) ;
            if( usErr != 0 ) break ;
 
            switch( *buff ) {
                case 'G' :    /* enter graphics mode */
    	            /* wait for access to command list and lock it */
                    DosRequestMutexSem( semCommands, SEM_INDEFINITE_WAIT ) ;
                    DosEnterCritSec() ;
#ifdef USEOWNALLOC
                    if( ncalloc > 0 ) {
                        DosSetMem( commands, ncalloc*sizeof(int), PAG_DECOMMIT ) ;
#else
       	            if (commands!=NULL) {    // delete all old commands and prepare for new
    	                free(commands);
#endif
    	                }
                    ic = 0 ;
                    ncalloc = CMDALLOC ;
#ifdef USEOWNALLOC
                    DosSetMem( commands, ncalloc*sizeof(int), PAG_COMMIT|PAG_DEFAULT ) ;
#else
                    commands = (int*)malloc(ncalloc*sizeof(int)) ;
#endif
                    DosExitCritSec() ;
                    DosReleaseMutexSem( semCommands ) ;
                    break ;
                    
                case 'E' :     /* leave graphics mode (graph completed) */
                    Display() ; /* plot graph */
                    break ;
                    
                case 'R' :
                    /* gnuplot has reset drivers, we do nothing */
                    break ;

                case 'M' :   /* move */
                case 'V' :   /* draw vector */
                    commands[ ic++ ] = (int)*buff ; 
                    if( ic+2 >= ncalloc ) AllocMore() ;
                    BufRead(hRead,&commands[ic], 2*sizeof(int), &cbR) ;
                    ic+=2 ;
                    break ;
                  
                case 'P' :   /* pause */
                    BufRead(hRead,&len, sizeof(int), &cbR) ;
                    len = (len+sizeof(int)-1)/sizeof(int) ;
                    if( len > 0 ){  /* get pause text */
                        szPauseText = malloc( len*sizeof(int) ) ;
                        BufRead(hRead,szPauseText, len*sizeof(int), &cbR) ;
                        }
                    if( ulPauseMode != PAUSE_GNU ) {
                             /* pause and wait for semaphore to be cleared */
                        DosResetEventSem( semPause, &ulPause ) ;
                        WinPostMsg( hApp, WM_PAUSEPLOT, (MPARAM) szPauseText, 0L ) ;
                        DosWaitEventSem( semPause, SEM_INDEFINITE_WAIT ) ;
                        }
                    else { /* gnuplot handles pause */
                        ulPauseReply = 2 ;
                        }
                    if( szPauseText != NULL ) free( szPauseText ) ;
                    szPauseText = NULL ;
                             /* reply to gnuplot so it can continue */
                    DosWrite( hRead, &ulPauseReply, sizeof(int), &cbR ) ;
                    break ; 
                       
                case 'T' :   /* write text */
                    commands[ ic++ ] = (int)*buff ; 
                    if( ic+1 >= ncalloc ) AllocMore() ;
                        /* read x, y, len */
                    BufRead(hRead,&commands[ic++], sizeof(int), &cbR) ;
                    BufRead(hRead,&commands[ic++], sizeof(int), &cbR) ;
                    BufRead(hRead,&len, sizeof(int), &cbR) ;
                    if( ic+1+((len+sizeof(int)-1)/sizeof(int)) >= ncalloc ) AllocMore() ;
                    BufRead(hRead,&commands[ic], len, &cbR) ;
                    if( len == 0 ) len = 1 ;
                    ic += (len+sizeof(int)-1)/sizeof(int) ;
                    break ;
                    
                case 'J' :   /* justify */
                case 'A' :   /* text angle */
                case 'L' :   /* line type */
                case 'D' :   /* points mode */
                
                    commands[ ic++ ] = (int)*buff ; 
                    if( ic+1 >= ncalloc ) AllocMore() ;
                    BufRead(hRead,&commands[ic++], sizeof(int), &cbR) ;
                    break ;

                 default :  /* should handle error */
                    break ;
                 }
             }
        }
    DosEnterCritSec() ;
    free( szFileBuf ) ;
    free( szEnv ) ;
    DosExitCritSec() ;
    pidGnu = 0 ; /* gnuplot has shut down (?) */
    WinPostMsg( hApp, WM_CLOSE, 0L, 0L ) ;    
    }

static int BufRead( HFILE hfile, void *buf, int nBytes, ULONG *pcbR )
/*
** pull next plot command out of buffer read from GNUPLOT
*/
    {
    ULONG ulR, ulRR ;
    static char buffer[GNUBUF] ;
    static char *pbuffer = buffer+GNUBUF, *ebuffer = buffer+GNUBUF ;
    
    for( ; nBytes > 0 ; nBytes-- ) {
        if( pbuffer >= ebuffer ) {
            ulR = GNUBUF ;
            DosRead( hfile, buffer, ulR, &ulRR ) ;
            pbuffer = buffer ;
            ebuffer = pbuffer+ulRR ;
            }
        *(char*)buf++ = *pbuffer++ ;
        }
    return 0L ;
    } 

static void AllocMore()
/*
**  Allocate more memory for plot commands
*/
    {
    DosRequestMutexSem( semCommands, SEM_INDEFINITE_WAIT ) ;
    DosEnterCritSec() ;
#ifdef USEOWNALLOC
    DosSetMem( commands+ncalloc, CMDALLOC*sizeof(int), PAG_COMMIT|PAG_DEFAULT ) ;
#endif
    ncalloc = ncalloc + CMDALLOC ;
#ifndef USEOWNALLOC
    commands = (int*)realloc(commands, ncalloc*sizeof(int)) ;
#endif
    DosExitCritSec() ;
    DosReleaseMutexSem( semCommands ) ;
    }

static void Display()
/*
**  Display gnuplot results 
**  -- must post message as this thread is not drawing thread
*/
    {
    WinPostMsg( hApp, WM_GNUPLOT, 0L, 0L ) ;    
    }

 
int GetNewFont( HWND hwnd, HPS hps ) 
/*
** Get a new font using standard font dialog
*/
    {
    static FONTDLG pfdFontdlg;      /* Font dialog info structure */
    static int i1 =1 ;
    static int     iSize ;
    char szPtList[64] ;
    HWND    hwndFontDlg;     /* Font dialog window handle */
    char    *p ; 
    char szFamilyname[FACESIZE];
 
    if( i1 ) {
        strcpy( pfdFontdlg.fAttrs.szFacename, strchr( szFontNameSize, '.' ) + 1 ) ;
        strcpy( szFamilyname, strchr( szFontNameSize, '.' ) + 1 ) ;
        sscanf( szFontNameSize, "%d", &iSize ) ;
        memset(&pfdFontdlg, 0, sizeof(FONTDLG));
 
        pfdFontdlg.cbSize = sizeof(FONTDLG);
        pfdFontdlg.hpsScreen = hps;
 /*   szFamilyname[0] = 0;*/
        pfdFontdlg.pszFamilyname = szFamilyname;
        pfdFontdlg.usFamilyBufLen = FACESIZE;
        pfdFontdlg.fl = FNTS_HELPBUTTON | FNTS_CENTER | FNTS_VECTORONLY | FNTS_INITFROMFATTRS ; 
        pfdFontdlg.clrFore = CLR_BLACK;
        pfdFontdlg.clrBack = CLR_WHITE;
        pfdFontdlg.usWeight = 5 ;
        pfdFontdlg.fAttrs.usCodePage = 0;
        i1=0; 
        }
    sprintf( szPtList, "%d 8 10 12 14 18 24", iSize ) ;
    pfdFontdlg.pszPtSizeList = szPtList ;
    pfdFontdlg.fxPointSize = MAKEFIXED(iSize,0); 
    hwndFontDlg = WinFontDlg(HWND_DESKTOP, hwnd, &pfdFontdlg);
 
    if (hwndFontDlg && (pfdFontdlg.lReturn == DID_OK)) {
        iSize = FIXEDINT( pfdFontdlg.fxPointSize ) ;
        sprintf( szFontNameSize, "%d.%s", iSize, pfdFontdlg.fAttrs.szFacename ) ;
        return 1 ;
        }
    else return 0 ;
    }
