// Central dispatch facility for PLplot. // Also contains the PLplot main data structures, external access // routines, and initialization calls. // // This stuff used to be in "dispatch.h", "dispatch.c", and "base.c". // // // Copyright (C) 1993-2001 Geoffrey Furnish // Copyright (C) 1993-2006 Maurice LeBrun // Copyright (C) 1996 Rady Shouman // Copyright (C) 2000-2019 Alan W. Irwin // Copyright (C) 2001-2003 Joao Cardoso // Copyright (C) 2001-2005 Rafael Laboissiere // Copyright (C) 2004-2007 Andrew Roach // Copyright (C) 2004-2015 Andrew Ross // Copyright (C) 2005 Thomas Duck // Copyright (C) 2005-2015 Arjen Markus // Copyright (C) 2006-2011 Hazen Babcock // Copyright (C) 2008-2009 Werner Smekal // Copyright (C) 2009-2011 Hezekiah M. Carty // Copyright (C) 2015 Jim Dishaw // Copyright (C) 2015 jdishaw // Copyright (C) 2015-2017 Phil Rosenberg // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // #define DEBUG #define NEED_PLDEBUG #include "plcore.h" #ifdef ENABLE_DYNDRIVERS #ifndef LTDL_WIN32 #include #else #include "ltdl_win32.h" #endif #endif #if HAVE_DIRENT_H // The following conditional is a workaround for a bug in the MacOSX system. // When the dirent.h file will be fixed upstream by Apple Inc, this should // go away. # ifdef NEED_SYS_TYPE_H # include # endif # include # define NAMLEN( dirent ) strlen( ( dirent )->d_name ) #else # if defined ( _MSC_VER ) # include "dirent_msvc.h" # else # define dirent direct # define NAMLEN( dirent ) ( dirent )->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif # endif #endif // AM: getcwd has a somewhat strange status on Windows, its proper // name is _getcwd, this is a problem in the case of DLLs, like with // the Java bindings. The functions _getcwd() and chdir() are // declared in direct.h for Visual C++. Since chdir() is deprecated // (but still available) in Visual C++ we redefine chdir to _chdir. // #if defined ( _MSC_VER ) # include # define getcwd _getcwd # define chdir _chdir #endif #define BUFFER_SIZE 80 #define BUFFER2_SIZE 300 #define DRVSPEC_SIZE 400 #include int text2num( PLCHAR_VECTOR text, char end, PLUNICODE *num ); int text2fci( PLCHAR_VECTOR text, unsigned char *hexdigit, unsigned char *hexpower ); //-------------------------------------------------------------------------- // Driver Interface // // These routines are the low-level interface to the driver -- all calls to // driver functions must pass through here. For implementing driver- // specific functions, the escape function is provided. The command stream // gets duplicated to the plot buffer here. // // All functions that result in graphics actually being plotted (rather than // just a change of state) are filtered as necessary before being passed on. // The default settings do not require any filtering, i.e. PLplot physical // coordinates are the same as the device physical coordinates (currently // this can't be changed anyway), and a global view equal to the entire page // is used. // // The reason one wants to put view-specific filtering here is that if // enabled, the plot buffer should receive the unfiltered data stream. This // allows a specific view to be used from an interactive device (e.g. TCL/TK // driver) but be restored to the full view at any time merely by // reprocessing the contents of the plot buffer. // // The metafile, on the other hand, *should* be affected by changes in the // view, since this is a crucial editing capability. It is recommended that // the initial metafile be created without a restricted global view, and // modification of the view done on a per-plot basis as desired during // subsequent processing. // //-------------------------------------------------------------------------- // Initialize device. // The plot buffer must be called last. // The following array of chars is used both here and in plsym.c for // translating the Greek characters from the #g escape sequences into // the Hershey and Unicode codings // const char plP_greek_mnemonic[] = "ABGDEZYHIKLMNCOPRSTUFXQWabgdezyhiklmncoprstufxqw"; void plP_init( void ) { char * save_locale; plsc->page_status = AT_EOP; plsc->stream_closed = FALSE; save_locale = plsave_set_locale(); ( *plsc->dispatch_table->pl_init )( (struct PLStream_struct *) plsc ); plrestore_locale( save_locale ); if ( plsc->plbuf_write ) plbuf_init( plsc ); } // End of page // The plot buffer must be called first. // Ignore instruction if already at eop. void plP_eop( void ) { int skip_driver_eop = 0; if ( plsc->page_status == AT_EOP ) return; plsc->page_status = AT_EOP; if ( plsc->plbuf_write ) plbuf_eop( plsc ); // Call user eop handler if present. if ( plsc->eop_handler != NULL ) ( *plsc->eop_handler )( plsc->eop_data, &skip_driver_eop ); if ( !skip_driver_eop ) { char *save_locale = plsave_set_locale(); if ( !plsc->stream_closed ) { ( *plsc->dispatch_table->pl_eop )( (struct PLStream_struct *) plsc ); } plrestore_locale( save_locale ); } } // Set up new page. // The plot buffer must be called last. // Ignore if already at bop. // It's not actually necessary to be AT_EOP here, so don't check for it. void plP_bop( void ) { int skip_driver_bop = 0; plP_subpInit(); if ( plsc->page_status == AT_BOP ) return; plsc->page_status = AT_BOP; plsc->nplwin = 0; // Call user bop handler if present. if ( plsc->bop_handler != NULL ) ( *plsc->bop_handler )( plsc->bop_data, &skip_driver_bop ); if ( !skip_driver_bop ) { char *save_locale = plsave_set_locale(); if ( !plsc->stream_closed ) { ( *plsc->dispatch_table->pl_bop )( (struct PLStream_struct *) plsc ); } plrestore_locale( save_locale ); } if ( plsc->plbuf_write ) plbuf_bop( plsc ); } // Tidy up device (flush buffers, close file, etc). void plP_tidy( void ) { char * save_locale; if ( plsc->tidy ) { ( *plsc->tidy )( plsc->tidy_data ); plsc->tidy = NULL; plsc->tidy_data = NULL; } save_locale = plsave_set_locale(); ( *plsc->dispatch_table->pl_tidy )( (struct PLStream_struct *) plsc ); plrestore_locale( save_locale ); if ( plsc->plbuf_write ) { plbuf_tidy( plsc ); } plsc->OutFile = NULL; } // Change state. void plP_state( PLINT op ) { char * save_locale; if ( plsc->plbuf_write ) plbuf_state( plsc, op ); save_locale = plsave_set_locale(); if ( !plsc->stream_closed ) { ( *plsc->dispatch_table->pl_state )( (struct PLStream_struct *) plsc, op ); } plrestore_locale( save_locale ); } // Escape function, for driver-specific commands. void plP_esc( PLINT op, void *ptr ) { char * save_locale; PLINT clpxmi, clpxma, clpymi, clpyma; EscText* args; // The plot buffer must be called first if ( plsc->plbuf_write ) plbuf_esc( plsc, op, ptr ); // Text coordinates must pass through the driver interface filter if ( ( op == PLESC_HAS_TEXT && plsc->dev_unicode ) || ( op == PLESC_END_TEXT && plsc->alt_unicode ) ) { // Apply the driver interface filter if ( plsc->difilt ) { args = (EscText *) ptr; difilt( &( args->x ), &( args->y ), 1, &clpxmi, &clpxma, &clpymi, &clpyma ); } } save_locale = plsave_set_locale(); if ( !plsc->stream_closed ) { ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc, op, ptr ); } plrestore_locale( save_locale ); } // Set up plot window parameters. // The plot buffer must be called first // Some drivers (metafile, Tk) need access to this data void plP_swin( PLWindow *plwin ) { PLWindow *w; PLINT clpxmi, clpxma, clpymi, clpyma; // Provide plot buffer with unfiltered window data if ( plsc->plbuf_write ) plbuf_esc( plsc, PLESC_SWIN, (void *) plwin ); w = &plsc->plwin[plsc->nplwin++ % PL_MAXWINDOWS]; w->dxmi = plwin->dxmi; w->dxma = plwin->dxma; w->dymi = plwin->dymi; w->dyma = plwin->dyma; if ( plsc->difilt ) { xscl[0] = plP_dcpcx( w->dxmi ); xscl[1] = plP_dcpcx( w->dxma ); yscl[0] = plP_dcpcy( w->dymi ); yscl[1] = plP_dcpcy( w->dyma ); difilt( xscl, yscl, 2, &clpxmi, &clpxma, &clpymi, &clpyma ); w->dxmi = plP_pcdcx( xscl[0] ); w->dxma = plP_pcdcx( xscl[1] ); w->dymi = plP_pcdcy( yscl[0] ); w->dyma = plP_pcdcy( yscl[1] ); } w->wxmi = plwin->wxmi; w->wxma = plwin->wxma; w->wymi = plwin->wymi; w->wyma = plwin->wyma; // If the driver wants to process swin commands, call it now // It must use the filtered data, which it can get from *plsc if ( plsc->dev_swin ) { char *save_locale = plsave_set_locale(); if ( !plsc->stream_closed ) { ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc, PLESC_SWIN, NULL ); } plrestore_locale( save_locale ); } } // Calls the device specific wait for user input function. This // action depends on the state of the nopause flag and whether // user input is supported by the driver. void plP_wait( void ) { // If the nopause is disabled (which means pauses are wanted) and the // the device supports waiting for user input if ( !plsc->nopause && *plsc->dispatch_table->pl_wait != NULL ) { char *save_locale = plsave_set_locale(); if ( !plsc->stream_closed ) { ( *plsc->dispatch_table->pl_wait )( (struct PLStream_struct *) plsc ); } plrestore_locale( save_locale ); } } //-------------------------------------------------------------------------- // Drawing commands. //-------------------------------------------------------------------------- // Draw line between two points // The plot buffer must be called first so it gets the unfiltered data void plP_line( short *x, short *y ) { PLINT i, npts = 2, clpxmi, clpxma, clpymi, clpyma; plsc->page_status = DRAWING; if ( plsc->plbuf_write ) plbuf_line( plsc, x[0], y[0], x[1], y[1] ); if ( plsc->difilt ) { for ( i = 0; i < npts; i++ ) { xscl[i] = x[i]; yscl[i] = y[i]; } difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma ); plP_pllclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grline ); } else { grline( x, y, npts ); } } // Draw polyline // The plot buffer must be called first void plP_polyline( short *x, short *y, PLINT npts ) { PLINT i, clpxmi, clpxma, clpymi, clpyma; plsc->page_status = DRAWING; if ( plsc->plbuf_write ) plbuf_polyline( plsc, x, y, npts ); if ( plsc->difilt ) { for ( i = 0; i < npts; i++ ) { xscl[i] = x[i]; yscl[i] = y[i]; } difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma ); plP_pllclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grpolyline ); } else { grpolyline( x, y, npts ); } } // Fill polygon // The plot buffer must be called first // Here if the desired area fill capability isn't present, we mock up // something in software static int foo; void plP_fill( short *x, short *y, PLINT npts ) { PLINT i, clpxmi, clpxma, clpymi, clpyma; plsc->page_status = DRAWING; if ( plsc->plbuf_write ) { plsc->dev_npts = npts; plsc->dev_x = x; plsc->dev_y = y; plbuf_esc( plsc, PLESC_FILL, NULL ); } // Account for driver ability to do fills if ( plsc->patt == 0 && !plsc->dev_fill0 ) { if ( !foo ) { plwarn( "Driver does not support hardware solid fills, switching to software fill.\n" ); foo = 1; } plsc->patt = 8; plpsty( plsc->patt ); } if ( plsc->dev_fill1 ) { plsc->patt = -ABS( plsc->patt ); } // Perform fill. Here we MUST NOT allow the software fill to pass through the // driver interface filtering twice, else we get the infamous 2*rotation for // software fills on orientation swaps. // if ( plsc->patt > 0 ) plfill_soft( x, y, npts ); else { if ( plsc->difilt ) { for ( i = 0; i < npts; i++ ) { xscl[i] = x[i]; yscl[i] = y[i]; } difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma ); plP_plfclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grfill ); } else { grfill( x, y, npts ); } } } // Render a gradient // The plot buffer must be called first // N.B. plP_gradient is never called (see plgradient) unless the // device driver has set plsc->dev_gradient to true. void plP_gradient( short *x, short *y, PLINT npts ) { PLINT i, clpxmi, clpxma, clpymi, clpyma; plsc->page_status = DRAWING; if ( plsc->plbuf_write ) { plsc->dev_npts = npts; plsc->dev_x = x; plsc->dev_y = y; plbuf_esc( plsc, PLESC_GRADIENT, NULL ); } // Render gradient with driver. if ( plsc->difilt ) { for ( i = 0; i < npts; i++ ) { xscl[i] = x[i]; yscl[i] = y[i]; } difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma ); plP_plfclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grgradient ); } else { grgradient( x, y, npts ); } } // Account for driver ability to draw text itself // // #define DEBUG_TEXT // //-------------------------------------------------------------------------- // int text2num( char *text, char end, PLUNICODE *num) // char *text - pointer to the text to be parsed // char end - end character (i.e. ')' or ']' to stop parsing // PLUNICODE *num - pointer to an PLUNICODE to store the value // // Function takes a string, which can be either hex or decimal, // and converts it into an PLUNICODE, stopping at either a null, // or the character pointed to by 'end'. This implementation using // the C library strtoul replaces the original brain-dead version // and should be more robust to invalid control strings. //-------------------------------------------------------------------------- int text2num( PLCHAR_VECTOR text, char end, PLUNICODE *num ) { char *endptr; // This special base logic required to _avoid_ interpretation of // numbers with leading zeroes as octal numbers if base = 0. int base = 10; if ( !strncmp( text, "0x", 2 ) || !strncmp( text, "0X", 2 ) ) base = 16; *num = (PLUNICODE) strtoul( text, &endptr, base ); if ( end != endptr[0] ) { char msgbuf[BUFFER2_SIZE]; snprintf( msgbuf, BUFFER2_SIZE, "text2num: for base = %d, strtoul found invalid non-numeric character \"%c\" detected in string \"%s\" when looking for \"%c\" ", base, *endptr, text, end ); plwarn( msgbuf ); } return (int) ( endptr - text ); } //-------------------------------------------------------------------------- // int text2fci( char *text, unsigned char *hexdigit, unsigned char *hexpower) // char *text - pointer to the text to be parsed // unsigned char *hexdigit - pointer to hex value that is stored. // unsigned char *hexpower - pointer to hex power (left shift) that is stored. // // Function takes a pointer to a string, which is looked up in a table // to determine the corresponding FCI (font characterization integer) // hex digit value and hex power (left shift). All matched strings // start with "<" and end with the two characters "/>". // If the lookup succeeds, hexdigit and hexpower are set to the appropriate // values in the table, and the function returns the number of characters // in text that are consumed by the matching string in the table lookup. // // If the lookup fails, hexdigit is set to 0, hexpower is set to and // impossible value, and the function returns 0. //-------------------------------------------------------------------------- int text2fci( PLCHAR_VECTOR text, unsigned char *hexdigit, unsigned char *hexpower ) { typedef struct { PLCHAR_VECTOR ptext; unsigned char hexdigit; unsigned char hexpower; } TextLookupTable; // This defines the various font control commands and the corresponding // hexdigit and hexpower in the FCI. // #define N_TextLookupTable 10 const TextLookupTable lookup[N_TextLookupTable] = { { "", PL_FCI_SANS, PL_FCI_FAMILY }, { "", PL_FCI_SERIF, PL_FCI_FAMILY }, { "", PL_FCI_MONO, PL_FCI_FAMILY }, { "