2 static char *RCSid() { return RCSid("$Id: term.c,v 1.151.2.6 2008/12/12 06:57:50 sfeam Exp $"); }
8 * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
10 * Permission to use, copy, and distribute this software and its
11 * documentation for any purpose with or without fee is hereby granted,
12 * provided that the above copyright notice appear in all copies and
13 * that both that copyright notice and this permission notice appear
14 * in supporting documentation.
16 * Permission to modify the software is granted, but not the right to
17 * distribute the complete modified source code. Modifications are to
18 * be distributed as patches to the released version. Permission to
19 * distribute binaries produced by compiling modified sources is granted,
21 * 1. distribute the corresponding source modifications from the
22 * released version in the form of a patch file along with the binaries,
23 * 2. add special version identification to distinguish your version
24 * in addition to the base release version number,
25 * 3. provide your name and address as the primary contact for the
26 * support of your modified version, and
27 * 4. retain our contact information in regard to use of the base
29 * Permission to distribute the released version of the source code along
30 * with corresponding source modifications in the form of a patch file is
31 * granted with same provisions 2 through 4 for binary distributions.
33 * This software is provided "as is" without express or implied warranty
34 * to the extent permitted by applicable law.
38 /* This module is responsible for looking after the terminal
39 * drivers at the lowest level. Only this module (should)
40 * know about all the various rules about interpreting
41 * the terminal capabilities exported by the terminal
42 * drivers in the table.
44 * Note that, as far as this module is concerned, a
45 * terminal session lasts only until _either_ terminal
46 * or output file changes. Before either is changed,
47 * the terminal is shut down.
49 * Entry points : (see also term/README)
51 * term_set_output() : called when set output invoked
53 * term_initialise() : optional. Prepare the terminal for first
54 * use. It protects itself against subsequent calls.
56 * term_start_plot() : called at start of graph output. Calls term_init
59 * term_apply_lp_properties() : apply linewidth settings
61 * term_end_plot() : called at the end of a plot
63 * term_reset() : called during int_error handling, to shut
64 * terminal down cleanly
66 * term_start_multiplot() : called by set multiplot
68 * term_end_multiplot() : called by set nomultiplot
70 * term_check_multiplot_okay() : called just before an interactive
71 * prompt is issued while in multiplot mode,
72 * to allow terminal to suspend if necessary,
73 * Raises an error if interactive multiplot
96 static int save_mouse_state = 1;
100 FILE *open_printer __PROTO((void)); /* in wprinter.c */
101 void close_printer __PROTO((FILE * outfile));
107 #endif /* _Windows */
109 static int termcomp __PROTO((const generic * a, const generic * b));
111 /* Externally visible variables */
112 /* the central instance: the current terminal's interface structure */
113 struct termentry *term = NULL; /* unknown */
115 /* ... and its options string */
116 char term_options[MAX_LINE_LEN+1] = "";
118 /* the 'output' file name and handle */
119 char *outstr = NULL; /* means "STDOUT" */
122 /* Output file where the PostScript output goes to. See term_api.h for more
127 /* true if terminal has been initialized */
128 TBOOLEAN term_initialised;
130 /* true if in multiplot mode */
131 TBOOLEAN multiplot = FALSE;
133 /* text output encoding, for terminals that support it */
134 enum set_encoding_id encoding;
135 /* table of encoding names, for output of the setting */
136 const char *encoding_names[] = {
137 "default", "iso_8859_1", "iso_8859_2", "iso_8859_15",
138 "cp437", "cp850", "cp852", "cp1250", "koi8r", "koi8u", NULL };
139 /* 'set encoding' options */
140 const struct gen_table set_encoding_tbl[] =
142 { "def$ault", S_ENC_DEFAULT },
143 { "iso$_8859_1", S_ENC_ISO8859_1 },
144 { "iso_8859_2", S_ENC_ISO8859_2 },
145 { "iso_8859_15", S_ENC_ISO8859_15 },
146 { "cp4$37", S_ENC_CP437 },
147 { "cp850", S_ENC_CP850 },
148 { "cp852", S_ENC_CP852 },
149 { "cp1250", S_ENC_CP1250 },
150 { "koi8$r", S_ENC_KOI8_R },
151 { "koi8$u", S_ENC_KOI8_U },
152 { NULL, S_ENC_INVALID }
155 const char *arrow_head_names[4] =
156 {"nohead", "head", "backhead", "heads"};
158 /* HBB 20020225: moved here, from ipc.h, where it never should have
161 /* HBB 20020225: currently not used anywhere outside term.c --> make
163 static SELECT_TYPE_ARG1 ipc_back_fd = IPC_BACK_CLOSED;
166 /* resolution in dpi for converting pixels to size units */
167 int gp_resolution = 72;
169 /* Support for enhanced text mode. Declared extern in term_api.h */
170 char enhanced_text[MAX_LINE_LEN+1] = "";
171 char *enhanced_cur_text = NULL;
172 double enhanced_fontscale = 1.0;
173 char enhanced_escape_format[16] = "";
174 double enhanced_max_height = 0.0, enhanced_min_height = 0.0;
175 /* flag variable to disable enhanced output of filenames, mainly. */
176 TBOOLEAN ignore_enhanced_text = FALSE;
178 /* Internal variables */
180 /* true if terminal is in graphics mode */
181 static TBOOLEAN term_graphics = FALSE;
183 /* we have suspended the driver, in multiplot mode */
184 static TBOOLEAN term_suspended = FALSE;
187 static TBOOLEAN opened_binary = FALSE;
189 /* true if require terminal to be initialized */
190 static TBOOLEAN term_force_init = FALSE;
192 /* internal pointsize for do_point */
193 static double term_pointsize=1;
195 /* Internal prototypes: */
197 static void term_suspend __PROTO((void));
198 static void term_close_output __PROTO((void));
199 static struct termentry *change_term __PROTO((const char *name, int length));
201 static void null_linewidth __PROTO((double));
202 static void do_point __PROTO((unsigned int x, unsigned int y, int number));
203 static void do_pointsize __PROTO((double size));
204 static void line_and_point __PROTO((unsigned int x, unsigned int y, int number));
205 static void do_arrow __PROTO((unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, int head));
207 static void UP_redirect __PROTO((int called));
209 static int null_text_angle __PROTO((int ang));
210 static int null_justify_text __PROTO((enum JUSTIFY just));
211 static int null_scale __PROTO((double x, double y));
212 static void options_null __PROTO((void));
213 static void UNKNOWN_null __PROTO((void));
214 static void MOVE_null __PROTO((unsigned int, unsigned int));
215 static void LINETYPE_null __PROTO((int));
216 static void PUTTEXT_null __PROTO((unsigned int, unsigned int, const char *));
218 /* Used by terminals and by shared routine parse_term_size() */
224 static size_units parse_term_size __PROTO((float *xsize, float *ysize, size_units def_units));
227 * Bookkeeping and support routine for 'set multiplot layout <rows>, <cols>'
229 * Volker Dobler <v.dobler@web.de>
232 static void mp_layout_size_and_offset __PROTO((void));
234 enum set_multiplot_id {
236 S_MULTIPLOT_COLUMNSFIRST, S_MULTIPLOT_ROWSFIRST, S_MULTIPLOT_SCALE,
237 S_MULTIPLOT_DOWNWARDS, S_MULTIPLOT_UPWARDS,
238 S_MULTIPLOT_OFFSET, S_MULTIPLOT_TITLE, S_MULTIPLOT_INVALID
241 static const struct gen_table set_multiplot_tbl[] =
243 { "lay$out", S_MULTIPLOT_LAYOUT },
244 { "col$umnsfirst", S_MULTIPLOT_COLUMNSFIRST },
245 { "row$sfirst", S_MULTIPLOT_ROWSFIRST },
246 { "down$wards", S_MULTIPLOT_DOWNWARDS },
247 { "up$wards", S_MULTIPLOT_UPWARDS },
248 { "sca$le", S_MULTIPLOT_SCALE },
249 { "off$set", S_MULTIPLOT_OFFSET },
250 { "ti$tle", S_MULTIPLOT_TITLE },
251 { NULL, S_MULTIPLOT_INVALID }
254 # define MP_LAYOUT_DEFAULT { \
255 FALSE, /* auto_layout */ \
256 0, 0, /* num_rows, num_cols */ \
257 FALSE, /* row_major */ \
258 TRUE, /* downwards */ \
259 0, 0, /* act_row, act_col */ \
260 1, 1, /* xscale, yscale */ \
261 0, 0, /* xoffset, yoffset */ \
262 0,0,0,0, /* prev_ sizes and offsets */ \
263 EMPTY_LABELSTRUCT, 0.0 \
267 TBOOLEAN auto_layout; /* automatic layout if true */
268 int num_rows; /* number of rows in layout */
269 int num_cols; /* number of columns in layout */
270 TBOOLEAN row_major; /* row major mode if true, column major else */
271 TBOOLEAN downwards; /* prefer downwards or upwards direction */
272 int act_row; /* actual row in layout */
273 int act_col; /* actual column in layout */
274 double xscale; /* factor for horizontal scaling */
275 double yscale; /* factor for vertical scaling */
276 double xoffset; /* horizontal shift */
277 double yoffset; /* horizontal shift */
278 double prev_xsize, prev_ysize, prev_xoffset, prev_yoffset;
279 /* values before 'set multiplot layout' */
280 text_label title; /* goes above complete set of plots */
281 double title_height; /* fractional height reserved for title */
282 } mp_layout = MP_LAYOUT_DEFAULT;
293 void term_mode_tek();
294 void term_mode_native();
296 void term_nopasthru();
297 void fflush_binary();
298 # define FOPEN_BINARY(file) fopen(file, "wb", "rfm=fix", "bls=512", "mrs=512")
300 # define FOPEN_BINARY(file) fopen(file, "wb")
303 #if defined(MSDOS) || defined(WIN32) || defined(WIN16)
304 # if defined(__DJGPP__) || defined (__TURBOC__)
310 # define O_BINARY _O_BINARY
312 # define O_BINARY O_BINARY_is_not_defined
322 #if defined(__WATCOMC__) || defined(__MSC__)
323 # include <io.h> /* for setmode() */
326 /* This is needed because the unixplot library only writes to stdout,
327 * but GNU plotutils libplot.a doesn't */
328 #if defined(UNIXPLOT) && !defined(GNUGRAPH)
329 static FILE save_stdout;
331 static int unixplot = 0;
333 #if defined(MTOS) || defined(ATARI)
338 #define POINT_TYPES 6
341 # define DEFAULTTERM NULL
344 /* interface to the rest of gnuplot - the rules are getting
345 * too complex for the rest of gnuplot to be allowed in
349 static TBOOLEAN output_pipe_open = FALSE;
355 FPRINTF((stderr, "term_close_output\n"));
357 opened_binary = FALSE;
359 if (!outstr) /* ie using stdout */
363 if (output_pipe_open) {
364 (void) pclose(gpoutfile);
365 output_pipe_open = FALSE;
369 if (stricmp(outstr, "PRN") == 0)
370 close_printer(gpoutfile);
373 if (gpoutfile != gppsfile)
376 gpoutfile = stdout; /* Don't dup... */
386 # define POPEN_MODE ("wb")
388 # define POPEN_MODE ("w")
391 /* assigns dest to outstr, so it must be allocated or NULL
392 * and it must not be outstr itself !
395 term_set_output(char *dest)
399 FPRINTF((stderr, "term_set_output\n"));
400 assert(dest == NULL || dest != outstr);
403 fputs("In multiplotmode you can't change the output\n", stderr);
406 if (term && term_initialised) {
408 term_initialised = FALSE;
409 /* switch off output to special postscript file (if used) */
412 if (dest == NULL) { /* stdout */
419 if ((f = popen(dest + 1, POPEN_MODE)) == (FILE *) NULL)
420 os_error(c_token, "cannot create pipe; output not changed");
422 output_pipe_open = TRUE;
427 if (outstr && stricmp(outstr, "PRN") == 0) {
428 /* we can't call open_printer() while printer is open, so */
429 close_printer(gpoutfile); /* close printer immediately if open */
430 gpoutfile = stdout; /* and reset output to stdout */
434 if (stricmp(dest, "PRN") == 0) {
435 if ((f = open_printer()) == (FILE *) NULL)
436 os_error(c_token, "cannot open printer temporary file; output may have changed");
442 if (outstr && (0 == stricmp(outstr, dest))) {
443 /* On MSDOS, you cannot open the same file twice and
444 * then close the first-opened one and keep the second
445 * open, it seems. If you do, you get lost clusters
446 * (connection to the first version of the file is
447 * lost, it seems). */
448 /* FIXME: this is not yet safe enough. You can fool it by
449 * specifying the same output file in two different ways
450 * (relative vs. absolute path to file, e.g.) */
454 if (term && (term->flags & TERM_BINARY))
455 f = FOPEN_BINARY(dest);
457 f = fopen(dest, "w");
459 if (f == (FILE *) NULL)
460 os_error(c_token, "cannot open file; output not changed");
469 opened_binary = (term && (term->flags & TERM_BINARY));
477 FPRINTF((stderr, "term_initialise()\n"));
480 int_error(NO_CARET, "No terminal defined");
482 /* check if we have opened the output file in the wrong mode
483 * (text/binary), if set term comes after set output
484 * This was originally done in change_term, but that
485 * resulted in output files being truncated
488 if (outstr && (term->flags & TERM_NO_OUTPUTFILE)) {
490 fprintf(stderr,"Closing %s\n",outstr);
495 (((term->flags & TERM_BINARY) && !opened_binary) ||
496 ((!(term->flags & TERM_BINARY) && opened_binary)))) {
497 /* this is nasty - we cannot just term_set_output(outstr)
498 * since term_set_output will first free outstr and we
499 * end up with an invalid pointer. I think I would
500 * prefer to defer opening output file until first plot.
502 char *temp = gp_alloc(strlen(outstr) + 1, "temp file string");
504 FPRINTF((stderr, "term_initialise: reopening \"%s\" as %s\n",
505 outstr, term->flags & TERM_BINARY ? "binary" : "text"));
506 strcpy(temp, outstr);
507 term_set_output(temp); /* will free outstr */
508 if (temp != outstr) {
514 fputs("Cannot reopen output file in binary", stderr);
515 /* and carry on, hoping for the best ! */
517 #if defined(MSDOS) || defined (_Windows) || defined(OS2)
519 else if (!outstr && (term->flags & TERM_BINARY))
521 else if (!outstr && !interactive && (term->flags & TERM_BINARY))
524 /* binary to stdout in non-interactive session... */
526 setmode(fileno(stdout), O_BINARY);
531 if (!term_initialised || term_force_init) {
532 FPRINTF((stderr, "- calling term->init()\n"));
534 term_initialised = TRUE;
542 FPRINTF((stderr, "term_start_plot()\n"));
544 if (!term_initialised)
547 if (!term_graphics) {
548 FPRINTF((stderr, "- calling term->graphics()\n"));
549 (*term->graphics) ();
550 term_graphics = TRUE;
551 } else if (multiplot && term_suspended) {
553 FPRINTF((stderr, "- calling term->resume()\n"));
556 term_suspended = FALSE;
559 /* Sync point for epslatex text positioning */
561 (term->layer)(TERM_LAYER_RESET);
563 /* Because PostScript plots may be viewed out of order, make sure */
564 /* Each new plot makes no assumption about the previous palette. */
565 if (term->flags & TERM_IS_POSTSCRIPT)
566 invalidate_palette();
568 /* Set canvas size to full range of current terminal coordinates */
570 canvas.xright = term->xmax;
572 canvas.ytop = term->ymax;
579 FPRINTF((stderr, "term_end_plot()\n"));
581 if (!term_initialised)
584 /* Sync point for epslatex text positioning */
586 (term->layer)(TERM_LAYER_END_TEXT);
589 FPRINTF((stderr, "- calling term->text()\n"));
591 term_graphics = FALSE;
593 if (mp_layout.auto_layout) {
594 if (mp_layout.row_major) {
596 if (mp_layout.act_row == mp_layout.num_rows ) {
597 mp_layout.act_row = 0;
599 if (mp_layout.act_col == mp_layout.num_cols ) {
600 /* int_warn(NO_CARET,"will overplot first plot"); */
601 mp_layout.act_col = 0;
604 } else { /* column-major */
606 if (mp_layout.act_col == mp_layout.num_cols ) {
607 mp_layout.act_col = 0;
609 if (mp_layout.act_row == mp_layout.num_rows ) {
610 /* int_warn(NO_CARET,"will overplot first plot"); */
611 mp_layout.act_col = 0;
615 mp_layout_size_and_offset();
624 (void) fflush(gpoutfile);
633 term_start_multiplot()
635 FPRINTF((stderr, "term_start_multiplot()\n"));
639 term_end_multiplot();
643 mp_layout.auto_layout = FALSE;
645 /* Parse options (new in version 4.1 */
646 while (!END_OF_COMMAND) {
650 if (almost_equals(c_token, "ti$tle")) {
652 if ((s = try_to_get_string())) {
653 free(mp_layout.title.text);
654 mp_layout.title.text = s;
659 if (almost_equals(c_token, "lay$out")) {
660 if (mp_layout.auto_layout)
661 int_error(c_token, "too many layout commands");
663 mp_layout.auto_layout = TRUE;
666 if (END_OF_COMMAND) {
667 int_error(c_token,"expecting '<num_cols>,<num_rows>'");
671 mp_layout.num_rows = (int) real(const_express(&a));
672 if (END_OF_COMMAND || !equals(c_token,",") )
673 int_error(c_token, "expecting ', <num_cols>'");
677 int_error(c_token, "expecting <num_cols>");
678 mp_layout.num_cols = (int) real(const_express(&a));
680 /* remember current values of the plot size */
681 mp_layout.prev_xsize = xsize;
682 mp_layout.prev_ysize = ysize;
683 mp_layout.prev_xoffset = xoffset;
684 mp_layout.prev_yoffset = yoffset;
686 mp_layout.act_row = 0;
687 mp_layout.act_col = 0;
692 /* The remaining options are only valid for auto-layout mode */
693 if (!mp_layout.auto_layout)
694 int_error(c_token, "only valid as part of an auto-layout command");
696 switch(lookup_table(&set_multiplot_tbl[0],c_token)) {
697 case S_MULTIPLOT_COLUMNSFIRST:
698 mp_layout.row_major = TRUE;
701 case S_MULTIPLOT_ROWSFIRST:
702 mp_layout.row_major = FALSE;
705 case S_MULTIPLOT_DOWNWARDS:
706 mp_layout.downwards = TRUE;
709 case S_MULTIPLOT_UPWARDS:
710 mp_layout.downwards = FALSE;
713 case S_MULTIPLOT_SCALE:
715 mp_layout.xscale = real(const_express(&a));
716 mp_layout.yscale = mp_layout.xscale;
717 if (!END_OF_COMMAND && equals(c_token,",") ) {
719 if (END_OF_COMMAND) {
720 int_error(c_token, "expecting <yscale>");
722 mp_layout.yscale = real(const_express(&a));
725 case S_MULTIPLOT_OFFSET:
727 mp_layout.xoffset = real(const_express(&a));
728 mp_layout.yoffset = mp_layout.xoffset;
729 if (!END_OF_COMMAND && equals(c_token,",") ) {
731 if (END_OF_COMMAND) {
732 int_error(c_token, "expecting <yoffset>");
734 mp_layout.yoffset = real(const_express(&a));
738 int_error(c_token,"invalid or duplicate option");
743 /* If we reach here, then the command has been successfully parsed */
746 /* Place overall title before doing anything else */
747 if (mp_layout.title.text) {
750 char *p = mp_layout.title.text;
752 map_position_r(&(mp_layout.title.offset), &tmpx, &tmpy, "mp title");
753 x = term->xmax / 2 + tmpx;
754 y = term->ymax - term->v_char + tmpy;;
756 ignore_enhanced(mp_layout.title.noenhanced);
757 apply_pm3dcolor(&(mp_layout.title.textcolor), term);
758 write_multiline(x, y, mp_layout.title.text,
759 CENTRE, JUST_TOP, 0, mp_layout.title.font);
760 reset_textcolor(&(mp_layout.title.textcolor), term);
761 ignore_enhanced(FALSE);
763 /* Calculate fractional height of title compared to entire page */
764 /* If it would fill the whole page, forget it! */
768 mp_layout.title_height = (double)(y * term->v_char) / (double)term->ymax;
769 if (mp_layout.title_height > 0.9)
770 mp_layout.title_height = 0.05;
772 mp_layout.title_height = 0.0;
775 mp_layout_size_and_offset();
778 /* save the state of mouse_setting.on and
779 * disable mouse; call UpdateStatusline()
780 * to turn of eventually statusline */
781 save_mouse_state = mouse_setting.on;
782 mouse_setting.on = 0;
790 FPRINTF((stderr, "term_end_multiplot()\n"));
794 if (term_suspended) {
797 term_suspended = FALSE;
800 /* reset plot size and origin to values before 'set multiplot layout' */
801 if (mp_layout.auto_layout) {
802 xsize = mp_layout.prev_xsize;
803 ysize = mp_layout.prev_ysize;
804 xoffset = mp_layout.prev_xoffset;
805 yoffset = mp_layout.prev_yoffset;
807 /* reset automatic multiplot layout */
808 mp_layout.auto_layout = FALSE;
809 mp_layout.xscale = mp_layout.yscale = 1.0;
810 mp_layout.xoffset = mp_layout.yoffset = 0.0;
811 if (mp_layout.title.text) {
812 free(mp_layout.title.text);
813 mp_layout.title.text = NULL;
818 /* restore the state of mouse_setting.on;
819 * call UpdateStatusline() to turn on
820 * eventually statusline */
821 mouse_setting.on = save_mouse_state;
831 FPRINTF((stderr, "term_suspend()\n"));
832 if (term_initialised && !term_suspended && term->suspend) {
833 FPRINTF((stderr, "- calling term->suspend()\n"));
835 term_suspended = TRUE;
842 FPRINTF((stderr, "term_reset()\n"));
845 /* Make sure that ^C will break out of a wait for 'pause mouse' */
846 paused_for_mouse = 0;
849 if (!term_initialised)
852 if (term_suspended) {
854 FPRINTF((stderr, "- calling term->resume()\n"));
857 term_suspended = FALSE;
861 term_graphics = FALSE;
863 if (term_initialised) {
865 term_initialised = FALSE;
866 /* switch off output to special postscript file (if used) */
872 term_apply_lp_properties(struct lp_style_type *lp)
874 /* This function passes all the line and point properties to the
875 * terminal driver and issues the corresponding commands.
877 * Alas, sometimes it might be necessary to give some help to
878 * this function by explicitly issuing additional '(*term)(...)'
883 /* change points, too
884 * Currently, there is no 'pointtype' function. For points
885 * there is a special function also dealing with (x,y) co-
889 (*term->pointsize) (pointsize);
891 (*term->pointsize) (lp->p_size);
893 /* _first_ set the line width, _then_ set the line type !
895 * The linetype might depend on the linewidth in some terminals.
897 (*term->linewidth) (lp->l_width);
899 /* Apply "linetype", which can include both color and dot/dash */
900 (*term->linetype) (lp->l_type);
901 /* Possibly override the linetype color with a fancier colorspec */
903 apply_pm3dcolor(&lp->pm3d_color, term);
909 term_check_multiplot_okay(TBOOLEAN f_interactive)
911 FPRINTF((stderr, "term_multiplot_okay(%d)\n", f_interactive));
913 if (!term_initialised)
914 return; /* they've not started yet */
916 /* make sure that it is safe to issue an interactive prompt
918 * it is not an interactive read, or
919 * the terminal supports interactive multiplot, or
920 * we are not writing to stdout and terminal doesn't
921 * refuse multiplot outright
923 if (!f_interactive || (term->flags & TERM_CAN_MULTIPLOT) ||
924 ((gpoutfile != stdout) && !(term->flags & TERM_CANNOT_MULTIPLOT))
926 /* it's okay to use multiplot here, but suspend first */
930 /* uh oh: they're not allowed to be in multiplot here */
932 term_end_multiplot();
934 /* at this point we know that it is interactive and that the
935 * terminal can either only do multiplot when writing to
936 * to a file, or it does not do multiplot at all
939 if (term->flags & TERM_CANNOT_MULTIPLOT)
940 int_error(NO_CARET, "This terminal does not support multiplot");
942 int_error(NO_CARET, "Must set output to a file or put all multiplot commands on one input line");
948 unsigned int x, unsigned y,
950 JUSTIFY hor, /* horizontal ... */
951 VERT_JUSTIFY vert, /* ... and vertical just - text in hor direction despite angle */
952 int angle, /* assume term has already been set for this */
953 const char *font) /* NULL or "" means use default */
955 struct termentry *t = term;
961 /* EAM 9-Feb-2003 - Set font before calculating sizes */
962 if (font && *font && t->set_font)
963 (*t->set_font) (font);
965 if (vert != JUST_TOP) {
966 /* count lines and adjust y */
967 int lines = 0; /* number of linefeeds - one fewer than lines */
973 x -= (vert * lines * t->v_char) / 2;
975 y += (vert * lines * t->v_char) / 2;
978 for (;;) { /* we will explicitly break out */
980 if ((text != NULL) && (p = strchr(text, '\n')) != NULL)
981 *p = 0; /* terminate the string */
983 if ((*t->justify_text) (hor)) {
985 (*t->put_text) (x, y, text);
987 int fix = hor * t->h_char * estimate_strlen(text) / 2;
989 if (on_page(x, y - fix))
990 (*t->put_text) (x, y - fix, text);
993 if (on_page(x - fix, y))
994 (*t->put_text) (x - fix, y, text);
997 if (angle == TEXT_VERTICAL)
999 else if (-angle == TEXT_VERTICAL)
1012 } /* unconditional branch back to the for(;;) - just a goto ! */
1014 if (font && *font && t->set_font)
1015 (*t->set_font) ("");
1021 do_point(unsigned int x, unsigned int y, int number)
1024 struct termentry *t = term;
1026 if (number < 0) { /* do dot */
1028 (*t->vector) (x, y);
1031 number %= POINT_TYPES;
1032 /* should be in term_tbl[] in later version */
1033 htic = (term_pointsize * t->h_tic / 2);
1034 vtic = (term_pointsize * t->v_tic / 2);
1036 /* point types 1..4 are same as in postscript, png and x11
1037 point types 5..6 are "similar"
1038 (note that (number) equals (pointtype-1)
1041 case 4: /* do diamond */
1042 (*t->move) (x - htic, y);
1043 (*t->vector) (x, y - vtic);
1044 (*t->vector) (x + htic, y);
1045 (*t->vector) (x, y + vtic);
1046 (*t->vector) (x - htic, y);
1048 (*t->vector) (x, y);
1050 case 0: /* do plus */
1051 (*t->move) (x - htic, y);
1052 (*t->vector) (x - htic, y);
1053 (*t->vector) (x + htic, y);
1054 (*t->move) (x, y - vtic);
1055 (*t->vector) (x, y - vtic);
1056 (*t->vector) (x, y + vtic);
1058 case 3: /* do box */
1059 (*t->move) (x - htic, y - vtic);
1060 (*t->vector) (x - htic, y - vtic);
1061 (*t->vector) (x + htic, y - vtic);
1062 (*t->vector) (x + htic, y + vtic);
1063 (*t->vector) (x - htic, y + vtic);
1064 (*t->vector) (x - htic, y - vtic);
1066 (*t->vector) (x, y);
1069 (*t->move) (x - htic, y - vtic);
1070 (*t->vector) (x - htic, y - vtic);
1071 (*t->vector) (x + htic, y + vtic);
1072 (*t->move) (x - htic, y + vtic);
1073 (*t->vector) (x - htic, y + vtic);
1074 (*t->vector) (x + htic, y - vtic);
1076 case 5: /* do triangle */
1077 (*t->move) (x, y + (4 * vtic / 3));
1078 (*t->vector) (x - (4 * htic / 3), y - (2 * vtic / 3));
1079 (*t->vector) (x + (4 * htic / 3), y - (2 * vtic / 3));
1080 (*t->vector) (x, y + (4 * vtic / 3));
1082 (*t->vector) (x, y);
1084 case 2: /* do star */
1085 (*t->move) (x - htic, y);
1086 (*t->vector) (x - htic, y);
1087 (*t->vector) (x + htic, y);
1088 (*t->move) (x, y - vtic);
1089 (*t->vector) (x, y - vtic);
1090 (*t->vector) (x, y + vtic);
1091 (*t->move) (x - htic, y - vtic);
1092 (*t->vector) (x - htic, y - vtic);
1093 (*t->vector) (x + htic, y + vtic);
1094 (*t->move) (x - htic, y + vtic);
1095 (*t->vector) (x - htic, y + vtic);
1096 (*t->vector) (x + htic, y - vtic);
1102 do_pointsize(double size)
1104 term_pointsize = (size >= 0 ? size : 1);
1109 * general point routine
1112 line_and_point(unsigned int x, unsigned int y, int number)
1114 /* temporary(?) kludge to allow terminals with bad linetypes
1115 to make nice marks */
1117 (*term->linetype) (NICE_LINE);
1118 do_point(x, y, number);
1122 * general arrow routine
1124 * I set the angle between the arrowhead and the line 15 degree.
1125 * The length of arrowhead varies depending on the line length
1126 * within the the range [0.3*(the-tic-length), 2*(the-tic-length)].
1127 * No head is printed if the arrow length is zero.
1129 * Yasu-hiro Yamazaki(hiro@rainbow.physics.utoronto.ca)
1133 #define COS15 (0.96593) /* cos of 15 degree */
1134 #define SIN15 (0.25882) /* sin of 15 degree */
1136 #define HEAD_LONG_LIMIT (2.0) /* long limit of arrowhead length */
1137 #define HEAD_SHORT_LIMIT (0.3) /* short limit of arrowhead length */
1138 /* their units are the "tic" length */
1140 #define HEAD_COEFF (0.3) /* default value of head/line length ratio */
1142 int curr_arrow_headlength; /* access head length + angle without changing API */
1143 double curr_arrow_headangle; /* angle in degrees */
1144 double curr_arrow_headbackangle; /* angle in degrees */
1145 int curr_arrow_headfilled; /* arrow head filled or not */
1149 unsigned int usx, unsigned int usy, /* start point */
1150 unsigned int uex, unsigned int uey, /* end point (point of arrowhead) */
1153 /* Clipping and angle calculations do not work if coords are unsigned! */
1159 struct termentry *t = term;
1160 float len_tic = ((double) (t->h_tic + t->v_tic)) / 2.0;
1161 /* average of tic sizes */
1162 /* (dx,dy) : vector from end to start */
1163 double dx = sx - ex;
1164 double dy = sy - ey;
1165 double len_arrow = sqrt(dx * dx + dy * dy);
1166 gpiPoint filledhead[5];
1168 BoundingBox *clip_save;
1169 t_arrow_head head = (t_arrow_head)((headstyle < 0) ? -headstyle : headstyle);
1170 /* negative headstyle means draw heads only, no shaft */
1172 /* FIXME: The plan is to migrate calling routines to call via */
1173 /* draw_clip_arrow() in which case we would not need to clip again here. */
1174 clip_save = clip_area;
1175 if (term->flags & TERM_CAN_CLIP)
1178 clip_area = &canvas;
1180 /* Calculate and draw arrow heads.
1181 * Draw no head for arrows with length = 0, or, to be more specific,
1182 * length < DBL_EPSILON, because len_arrow will almost always be != 0.
1184 if ((head != NOHEAD) && fabs(len_arrow) >= DBL_EPSILON) {
1186 if (curr_arrow_headlength <= 0) {
1187 /* arrow head with the default size */
1188 /* now calc the head_coeff */
1189 double coeff_shortest = len_tic * HEAD_SHORT_LIMIT / len_arrow;
1190 double coeff_longest = len_tic * HEAD_LONG_LIMIT / len_arrow;
1191 double head_coeff = GPMAX(coeff_shortest,
1192 GPMIN(HEAD_COEFF, coeff_longest));
1193 /* we put the arrowhead marks at 15 degrees to line */
1194 x1 = (int) ((COS15 * dx - SIN15 * dy) * head_coeff);
1195 y1 = (int) ((SIN15 * dx + COS15 * dy) * head_coeff);
1196 x2 = (int) ((COS15 * dx + SIN15 * dy) * head_coeff);
1197 y2 = (int) ((-SIN15 * dx + COS15 * dy) * head_coeff);
1198 /* backangle defaults to 90 deg */
1199 xm = (int) ((x1 + x2)/2);
1200 ym = (int) ((y1 + y2)/2);
1202 /* the arrow head with the length + angle specified explicitly */
1203 double alpha = curr_arrow_headangle * DEG2RAD;
1204 double beta = curr_arrow_headbackangle * DEG2RAD;
1205 double phi = atan2(-dy,-dx); /* azimuthal angle of the vector */
1206 double backlen = curr_arrow_headlength * sin(alpha) / sin(beta);
1208 /* anticlock-wise head segment */
1209 x1 = -(int)(curr_arrow_headlength * cos( alpha - phi ));
1210 y1 = (int)(curr_arrow_headlength * sin( alpha - phi ));
1211 /* clock-wise head segment */
1212 dx2 = -curr_arrow_headlength * cos( phi + alpha );
1213 dy2 = -curr_arrow_headlength * sin( phi + alpha );
1217 xm = (int) (dx2 + backlen * cos( phi + beta ));
1218 ym = (int) (dy2 + backlen * sin( phi + beta ));
1221 if (head & END_HEAD) {
1222 if (curr_arrow_headfilled==2 && !clip_point(ex,ey)) {
1223 /* draw filled forward arrow head */
1224 filledhead[0].x = ex + xm;
1225 filledhead[0].y = ey + ym;
1226 filledhead[1].x = ex + x1;
1227 filledhead[1].y = ey + y1;
1228 filledhead[2].x = ex;
1229 filledhead[2].y = ey;
1230 filledhead[3].x = ex + x2;
1231 filledhead[3].y = ey + y2;
1232 filledhead[4].x = ex + xm;
1233 filledhead[4].y = ey + ym;
1234 filledhead->style = FS_OPAQUE;
1235 if (t->filled_polygon)
1236 (*t->filled_polygon) (5, filledhead);
1238 /* draw outline of forward arrow head */
1239 if (clip_point(ex,ey))
1241 else if (curr_arrow_headfilled!=0) {
1242 draw_clip_line(ex+xm, ey+ym, ex+x1, ey+y1);
1243 draw_clip_line(ex+x1, ey+y1, ex, ey);
1244 draw_clip_line(ex, ey, ex+x2, ey+y2);
1245 draw_clip_line(ex+x2, ey+y2, ex+xm, ey+ym);
1247 draw_clip_line(ex+x1, ey+y1, ex, ey);
1248 draw_clip_line(ex, ey, ex+x2, ey+y2);
1252 /* backward arrow head */
1253 if ((head & BACKHEAD) && !clip_point(sx,sy)) {
1254 if (curr_arrow_headfilled==2) {
1255 /* draw filled backward arrow head */
1256 filledhead[0].x = sx - xm;
1257 filledhead[0].y = sy - ym;
1258 filledhead[1].x = sx - x1;
1259 filledhead[1].y = sy - y1;
1260 filledhead[2].x = sx;
1261 filledhead[2].y = sy;
1262 filledhead[3].x = sx - x2;
1263 filledhead[3].y = sy - y2;
1264 filledhead[4].x = sx - xm;
1265 filledhead[4].y = sy - ym;
1266 filledhead->style = FS_OPAQUE;
1267 if (t->filled_polygon)
1268 (*t->filled_polygon) (5, filledhead);
1270 /* draw outline of backward arrow head */
1271 if (curr_arrow_headfilled!=0) {
1272 draw_clip_line(sx-xm, sy-ym, sx-x2, sy-y2);
1273 draw_clip_line(sx-x2, sy-y2, sx, sy);
1274 draw_clip_line(sx, sy, sx-x1, sy-y1);
1275 draw_clip_line(sx-x1, sy-y1, sx-xm, sy-ym);
1277 draw_clip_line(sx-x2, sy-y2, sx, sy);
1278 draw_clip_line(sx, sy, sx-x1, sy-y1);
1283 /* Draw the line for the arrow. */
1284 if (headstyle >= 0) {
1285 if ((head & BACKHEAD)
1286 && (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled!=0) ) {
1290 if ((head & END_HEAD)
1291 && (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled!=0) ) {
1295 if (clip_line(&sx, &sy, &ex, &ey))
1296 draw_clip_line(sx, sy, ex, ey);
1299 /* Restore previous clipping box */
1300 clip_area = clip_save;
1306 #define TERM_PUBLIC static
1315 /* Dummy functions for unavailable features */
1316 /* return success if they asked for default - this simplifies code
1317 * where param is passed as a param. Client can first pass it here,
1318 * and only if it fails do they have to see what was trying to be done
1321 /* change angle of text. 0 is horizontal left to right.
1322 * 1 is vertical bottom to top (90 deg rotate)
1325 null_text_angle(int ang)
1330 /* change justification of text.
1331 * modes are LEFT (flush left), CENTRE (centred), RIGHT (flush right)
1334 null_justify_text(enum JUSTIFY just)
1336 return (just == LEFT);
1340 /* Change scale of plot.
1341 * Parameters are x,y scaling factors for this plot.
1342 * Some terminals (eg latex) need to do scaling themselves.
1345 null_scale(double x, double y)
1347 (void) x; /* avoid -Wunused warning */
1349 return FALSE; /* can't be done */
1352 /* HBB 990829: unused --> commented out */
1355 do_scale(double x, double y)
1357 return TRUE; /* can be done */
1359 #endif /* commented out */
1364 term_options[0] = '\0'; /* we have no options */
1373 MOVE_null(unsigned int x, unsigned int y)
1375 (void) x; /* avoid -Wunused warning */
1380 LINETYPE_null(int t)
1382 (void) t; /* avoid -Wunused warning */
1386 PUTTEXT_null(unsigned int x, unsigned int y, const char *s)
1388 (void) s; /* avoid -Wunused warning */
1395 null_linewidth(double s)
1397 (void) s; /* avoid -Wunused warning */
1401 /* cast to get rid of useless warnings about UNKNOWN_null */
1402 /* HBB 20040619: unused --- commented out */
1403 /* typedef void (*void_fp) __PROTO((void)); */
1406 /* setup the magic macros to compile in the right parts of the
1407 * terminal drivers included by term.h
1410 #define TERM_TABLE_START(x) ,{
1411 #define TERM_TABLE_END(x) }
1415 * term_tbl[] contains an entry for each terminal. "unknown" must be the
1416 * first, since term is initialized to 0.
1418 static struct termentry term_tbl[] =
1420 {"unknown", "Unknown terminal type - not a plotting device",
1422 1, 1, options_null, UNKNOWN_null, UNKNOWN_null,
1423 UNKNOWN_null, null_scale, UNKNOWN_null, MOVE_null, MOVE_null,
1424 LINETYPE_null, PUTTEXT_null}
1430 #define TERMCOUNT (sizeof(term_tbl) / sizeof(term_tbl[0]))
1433 /* mainly useful for external code */
1445 char *line_buffer = gp_alloc(BUFSIZ, "list_terms");
1446 int sort_idxs[TERMCOUNT];
1448 /* sort terminal types alphabetically */
1449 for( i = 0; i < TERMCOUNT; i++ )
1451 qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
1452 /* now sort_idxs[] contains the sorted indices */
1455 strcpy(line_buffer, "\nAvailable terminal types:\n");
1456 OutLine(line_buffer);
1458 for (i = 0; i < TERMCOUNT; i++) {
1459 sprintf(line_buffer, " %15s %s\n",
1460 term_tbl[sort_idxs[i]].name,
1461 term_tbl[sort_idxs[i]].description);
1462 OutLine(line_buffer);
1470 termcomp(const generic *arga, const generic *argb)
1472 const int *a = arga;
1473 const int *b = argb;
1475 return( strcasecmp( term_tbl[*a].name, term_tbl[*b].name ) );
1478 /* set_term: get terminal number from name on command line
1479 * will change 'term' variable if successful
1482 set_term(int c_token_arg)
1484 struct termentry *t = NULL;
1487 if (!token[c_token_arg].is_token)
1488 int_error(c_token_arg, "terminal name expected");
1489 input_name = gp_input_line + token[c_token_arg].start_index;
1490 t = change_term(input_name, token[c_token_arg].length);
1492 int_error(c_token_arg, "unknown or ambiguous terminal type; type just 'set terminal' for a list");
1494 /* otherwise the type was changed */
1499 /* change_term: get terminal number from name and set terminal type
1501 * returns NULL for unknown or ambiguous, otherwise is terminal
1504 static struct termentry *
1505 change_term(const char *origname, int length)
1508 struct termentry *t = NULL;
1509 TBOOLEAN ambiguous = FALSE;
1511 /* For backwards compatibility only */
1512 char *name = (char *)origname;
1513 if (!strncmp(origname,"X11",length)) {
1518 for (i = 0; i < TERMCOUNT; i++) {
1519 if (!strncmp(name, term_tbl[i].name, length)) {
1523 /* Exact match is always accepted */
1524 if (length == strlen(term_tbl[i].name)) {
1531 if (!t || ambiguous)
1534 /* Success: set terminal type now */
1537 term_initialised = FALSE;
1539 if (term->scale != null_scale)
1540 fputs("Warning: scale interface is not null_scale - may not work with multiplot\n", stderr);
1542 /* check that optional fields are initialised to something */
1543 if (term->text_angle == 0)
1544 term->text_angle = null_text_angle;
1545 if (term->justify_text == 0)
1546 term->justify_text = null_justify_text;
1547 if (term->point == 0)
1548 term->point = do_point;
1549 if (term->arrow == 0)
1550 term->arrow = do_arrow;
1551 if (term->pointsize == 0)
1552 term->pointsize = do_pointsize;
1553 if (term->linewidth == 0)
1554 term->linewidth = null_linewidth;
1556 /* Special handling for unixplot term type */
1557 if (!strncmp("unixplot", term->name, 8)) {
1558 UP_redirect(2); /* Redirect actual stdout for unixplots */
1559 } else if (unixplot) {
1560 UP_redirect(3); /* Put stdout back together again. */
1563 fprintf(stderr, "Terminal type set to '%s'\n", term->name);
1565 /* Invalidate any terminal-specific structures that may be active */
1566 invalidate_palette();
1572 * Routine to detect what terminal is being used (or do anything else
1573 * that would be nice). One anticipated (or allowed for) side effect
1574 * is that the global ``term'' may be set.
1575 * The environment variable GNUTERM is checked first; if that does
1576 * not exist, then the terminal hardware is checked, if possible,
1577 * and finally, we can check $TERM for some kinds of terminals.
1578 * A default can be set with -DDEFAULTTERM=myterm in the Makefile
1579 * or #define DEFAULTTERM myterm in term.h
1581 /* thanks to osupyr!alden (Dave Alden) for the original GNUTERM code */
1585 char *term_name = DEFAULTTERM;
1586 #if (defined(__TURBOC__) && defined(MSDOS) && !defined(_Windows)) || defined(NEXT) || defined(SUN) || defined(X11)
1587 char *env_term = NULL; /* from TERM environment var */
1590 char *display = NULL;
1592 char *gnuterm = NULL;
1594 /* GNUTERM environment variable is primary */
1595 gnuterm = getenv("GNUTERM");
1596 if (gnuterm != (char *) NULL) {
1597 term_name = gnuterm;
1601 term_name = ztc_init();
1605 term_name = vms_init();
1609 env_term = getenv("TERM");
1610 if (term_name == (char *) NULL
1611 && env_term != (char *) NULL && strcmp(env_term, "next") == 0)
1616 env_term = getenv("TERM");
1617 if (term_name == (char *) NULL
1618 && env_term != (char *) NULL && strcmp(env_term, "beterm") == 0)
1623 env_term = getenv("TERM"); /* try $TERM */
1624 if (term_name == (char *) NULL
1625 && env_term != (char *) NULL && strcmp(env_term, "sun") == 0)
1630 if (term_name == (char *) NULL)
1635 /* let the wxWidgets terminal be the default when available */
1636 if (term_name == (char *) NULL)
1638 #endif /* _Windows */
1641 /* find out whether stdout is a DM pad. See term/gpr.trm */
1646 /* find out whether stdout is a DM pad. See term/apollo.trm */
1647 if (apollo_isa_pad())
1648 term_name = "apollo";
1649 # endif /* APOLLO */
1652 #if defined(__APPLE__) && defined(__MACH__) && defined(HAVE_LIBAQUATERM)
1653 /* Mac OS X with AquaTerm installed */
1658 env_term = getenv("TERM"); /* try $TERM */
1659 if (term_name == (char *) NULL
1660 && env_term != (char *) NULL && strcmp(env_term, "xterm") == 0)
1662 display = getenv("DISPLAY");
1663 if (term_name == (char *) NULL && display != (char *) NULL)
1670 term_name = "amiga";
1673 #if defined(ATARI) || defined(MTOS)
1674 term_name = "atari";
1678 if (iswind() == 0) {
1679 term_name = "unixpc";
1684 if (getenv("CGIDISP") || getenv("CGIPRNT"))
1693 term_name = "grass";
1697 /* amai: Note that we do some checks above and now overwrite any
1698 results. Perhaps we may disable checks above!? */
1700 /* WINDOWID is set in sessions like xterm, etc.
1701 DISPLAY is also mandatory. */
1702 env_term = getenv("WINDOWID");
1703 display = getenv("DISPLAY");
1704 if ((env_term != (char *) NULL) && (display != (char *) NULL))
1711 /* set linux terminal only if LINUX_setup was successfull, if we are on X11
1712 LINUX_setup has failed, also if we are logged in by network */
1714 if (LINUX_graphics_allowed)
1716 term_name = "vgagl";
1718 term_name = "linux";
1720 #endif /* LINUXVGA */
1723 /* We have a name, try to set term type */
1724 if (term_name != NULL && *term_name != '\0') {
1725 int namelength = strlen(term_name);
1726 #ifdef GP_STRING_VARS
1727 struct udvt_entry *name = add_udv_by_name("GNUTERM");
1728 Gstring(&name->udv_value, gp_strdup(term_name));
1729 name->udv_undef = FALSE;
1731 if (strchr(term_name,' '))
1732 namelength = strchr(term_name,' ') - term_name;
1734 /* Force the terminal to initialize default fonts, etc. This prevents */
1735 /* segfaults and other strangeness if you set GNUTERM to "post" or */
1736 /* "png" for example. However, calling X11_options() is expensive due */
1737 /* to the fork+execute of gnuplot_x11 and x11 can tolerate not being */
1738 /* initialized until later. */
1739 /* Note that gp_input_line[] is blank at this point. */
1740 if (change_term(term_name, namelength)) {
1741 if (strcmp(term->name,"x11"))
1745 fprintf(stderr, "Unknown or ambiguous terminal name '%s'\n", term_name);
1747 change_term("unknown", 7);
1756 char *term_name = NULL;
1762 fputs("Graphics card not detected or not supported.\n", stderr);
1765 term_name = "hercules";
1768 term_name = "egamono";
1771 term_name = "egalib";
1774 term_name = "vgamono";
1777 term_name = "vgalib";
1780 term_name = "svgalib";
1783 term_name = "ssvgalib";
1789 #endif /* __ZTC__ */
1793 * Unixplot can't really write to gpoutfile--it wants to write to stdout.
1794 * This is normally ok, but the original design of gnuplot gives us
1795 * little choice. Originally users of unixplot had to anticipate
1796 * their needs and redirect all I/O to a file... Not very gnuplot-like.
1798 * caller: 1 - called from SET OUTPUT "FOO.OUT"
1799 * 2 - called from SET TERM UNIXPLOT
1800 * 3 - called from SET TERM other
1801 * 4 - called from SET OUTPUT
1804 UP_redirect(int caller)
1806 #if defined(UNIXPLOT) && !defined(GNUGRAPH)
1809 /* Don't save, just replace stdout w/gpoutfile (save was already done). */
1811 *(stdout) = *(gpoutfile); /* Copy FILE structure */
1816 save_stdout = *(stdout);
1817 *(stdout) = *(gpoutfile); /* Copy FILE structure */
1822 /* New terminal in use--put stdout back to original. */
1823 /* closepl(); */ /* This is called by the term. */
1825 *(stdout) = save_stdout; /* Copy FILE structure */
1829 /* User really wants to go to normal output... */
1832 *(stdout) = save_stdout; /* Copy FILE structure */
1836 #else /* !UNIXPLOT || GNUGRAPH */
1837 (void) caller; /* avoid -Wunused warning */
1838 #endif /* !UNIXPLOT || GNUGRAPH */
1841 /* test terminal by drawing border and text */
1842 /* called from command test */
1846 struct termentry *t = term;
1848 int x, y, xl, yl, i;
1849 unsigned int xmax_t, ymax_t;
1850 char label[MAX_ID_LEN];
1851 int key_entry_height;
1856 xmax_t = (unsigned int) (t->xmax * xsize);
1857 ymax_t = (unsigned int) (t->ymax * ysize);
1859 p_width = pointsize * t->h_tic;
1860 key_entry_height = pointsize * t->v_tic * 1.25;
1861 if (key_entry_height < t->v_char)
1862 key_entry_height = t->v_char;
1864 /* Sync point for epslatex text positioning */
1866 (term->layer)(TERM_LAYER_FRONTTEXT);
1868 /* border linetype */
1869 (*t->linewidth) (1.0);
1870 (*t->linetype) (LT_BLACK);
1872 (*t->vector) (xmax_t - 1, 0);
1873 (*t->vector) (xmax_t - 1, ymax_t - 1);
1874 (*t->vector) (0, ymax_t - 1);
1875 (*t->vector) (0, 0);
1877 (void) (*t->justify_text) (LEFT);
1878 (*t->put_text) (t->h_char * 5, ymax_t - t->v_char * 1.5, "Terminal Test");
1881 (*t->put_text) (t->h_char * 5, ymax_t - t->v_char * 3, "Mouse and hotkeys are supported, hit: h r m 6");
1884 (*t->linetype)(LT_BLACK);
1885 (*t->linetype) (LT_AXIS);
1886 (*t->move) (xmax_t / 2, 0);
1887 (*t->vector) (xmax_t / 2, ymax_t - 1);
1888 (*t->move) (0, ymax_t / 2);
1889 (*t->vector) (xmax_t - 1, ymax_t / 2);
1890 /* test width and height of characters */
1892 (*t->move) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 + t->v_char / 2);
1893 (*t->vector) (xmax_t / 2 + t->h_char * 10, ymax_t / 2 + t->v_char / 2);
1894 (*t->vector) (xmax_t / 2 + t->h_char * 10, ymax_t / 2 - t->v_char / 2);
1895 (*t->vector) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 - t->v_char / 2);
1896 (*t->vector) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 + t->v_char / 2);
1897 (*t->put_text) (xmax_t / 2 - t->h_char * 10, ymax_t / 2,
1898 "12345678901234567890");
1899 (*t->put_text) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 + t->v_char * 1.4,
1900 "test of character width:");
1901 (*t->linetype) (LT_BLACK);
1902 /* test justification */
1903 (void) (*t->justify_text) (LEFT);
1904 (*t->put_text) (xmax_t / 2, ymax_t / 2 + t->v_char * 6, "left justified");
1905 str = "centre+d text";
1906 if ((*t->justify_text) (CENTRE))
1907 (*t->put_text) (xmax_t / 2,
1908 ymax_t / 2 + t->v_char * 5, str);
1910 (*t->put_text) (xmax_t / 2 - strlen(str) * t->h_char / 2,
1911 ymax_t / 2 + t->v_char * 5, str);
1912 str = "right justified";
1913 if ((*t->justify_text) (RIGHT))
1914 (*t->put_text) (xmax_t / 2,
1915 ymax_t / 2 + t->v_char * 4, str);
1917 (*t->put_text) (xmax_t / 2 - strlen(str) * t->h_char,
1918 ymax_t / 2 + t->v_char * 4, str);
1919 /* test text angle */
1921 str = "rotated ce+ntred text";
1922 if ((*t->text_angle) (TEXT_VERTICAL)) {
1923 if ((*t->justify_text) (CENTRE))
1924 (*t->put_text) (t->v_char,
1927 (*t->put_text) (t->v_char,
1928 ymax_t / 2 - strlen(str) * t->h_char / 2, str);
1929 (*t->justify_text) (LEFT);
1930 str = " rotated by +45 deg";
1931 (*t->text_angle)(45);
1932 (*t->put_text)(t->v_char * 3, ymax_t / 2, str);
1933 (*t->justify_text) (LEFT);
1934 str = " rotated by -45 deg";
1935 (*t->text_angle)(-45);
1936 (*t->put_text)(t->v_char * 2, ymax_t / 2, str);
1938 if (!strcmp(t->name, "png") || !strcmp(t->name, "gif") || !strcmp(t->name, "jpeg")) {
1939 (*t->text_angle)(0);
1940 str = "this terminal supports text rotation only for truetype fonts";
1941 (*t->put_text)(t->v_char * 2 + t->h_char * 4, ymax_t / 2 - t->v_char * 2, str);
1945 (void) (*t->justify_text) (LEFT);
1946 (*t->put_text) (t->h_char * 2, ymax_t / 2 - t->v_char * 2, "can't rotate text");
1948 (void) (*t->justify_text) (LEFT);
1949 (void) (*t->text_angle) (0);
1950 (*t->linetype)(LT_BLACK);
1954 (*t->move) ((unsigned int) (xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)), (unsigned int) ymax_t - 1);
1955 (*t->vector) ((unsigned int) (xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)),
1956 (unsigned int) (ymax_t - axis_array[FIRST_X_AXIS].ticscale * t->v_tic));
1957 (*t->move) ((unsigned int) (xmax_t / 2), (unsigned int) (ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
1958 (*t->vector) ((unsigned int) (xmax_t / 2 + axis_array[FIRST_X_AXIS].ticscale * t->h_tic),
1959 (unsigned int) (ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
1960 /* HBB 19990530: changed this to use right-justification, if possible... */
1961 str = "show ticscale";
1962 if ((*t->justify_text) (RIGHT))
1963 (*t->put_text) ((unsigned int) (xmax_t / 2 - 1* t->h_char),
1964 (unsigned int) (ymax_t - (t->v_tic * 2 + t->v_char / 2)),
1967 (*t->put_text) ((unsigned int) (xmax_t / 2 - (strlen(str)+1) * t->h_char),
1968 (unsigned int) (ymax_t - (t->v_tic * 2 + t->v_char / 2)),
1970 (void) (*t->justify_text) (LEFT);
1971 (*t->linetype)(LT_BLACK);
1973 /* test line and point types */
1974 x = xmax_t - t->h_char * 6 - p_width;
1975 y = ymax_t - key_entry_height;
1976 (*t->pointsize) (pointsize);
1977 for (i = -2; y > key_entry_height; i++) {
1979 /* (void) sprintf(label,"%d",i); Jorgen Lippert
1981 (void) sprintf(label, "%d", i + 1);
1982 if ((*t->justify_text) (RIGHT))
1983 (*t->put_text) (x, y, label);
1985 (*t->put_text) (x - strlen(label) * t->h_char, y, label);
1986 (*t->move) (x + t->h_char, y);
1987 (*t->vector) (x + t->h_char * 4, y);
1989 (*t->point) (x + t->h_char * 5 + p_width / 2, y, i);
1990 y -= key_entry_height;
1993 /* test some arrows */
1994 (*t->linewidth) (1.0);
2000 i = curr_arrow_headfilled;
2001 curr_arrow_headfilled = 0;
2002 (*t->arrow) (x, y, x + xl, y, END_HEAD);
2003 curr_arrow_headfilled = 1;
2004 (*t->arrow) (x, y, x - xl, y, END_HEAD);
2005 curr_arrow_headfilled = 2;
2006 (*t->arrow) (x, y, x, y + yl, END_HEAD);
2007 curr_arrow_headfilled = 1; /* Was 3, but no terminals implement it */
2008 (*t->arrow) (x, y, x, y - yl, END_HEAD);
2009 curr_arrow_headfilled = i;
2012 (*t->arrow) (x - xl, y - yl, x + xl, y + yl, END_HEAD | BACKHEAD);
2013 (*t->arrow) (x - xl, y + yl, x, y, NOHEAD);
2014 curr_arrow_headfilled = 1; /* Was 3, but no terminals implement it */
2015 (*t->arrow) (x, y, x + xl, y - yl, BACKHEAD);
2017 /* test line widths */
2018 (void) (*t->justify_text) (LEFT);
2024 for (i=1; i<7; i++) {
2025 (*t->linewidth) ((float)(i)); (*t->linetype)(LT_BLACK);
2026 (*t->move) (x, y); (*t->vector) (x+xl, y);
2027 sprintf(label," lw %1d%c",i,0);
2028 (*t->put_text) (x+xl, y, label);
2031 (*t->put_text) (x, y, "linewidth");
2033 /* test fill patterns */
2038 (*t->linewidth) ((float)(1));
2039 (*t->linetype)(LT_BLACK);
2040 (*t->justify_text) (CENTRE);
2041 (*t->put_text)(x+xl*7, yl+t->v_char*1.5, "pattern fill");
2042 for (i=0; i<10; i++) {
2043 int style = ((i<<4) + FS_PATTERN);
2045 (*t->fillbox) ( style, x, y, xl, yl );
2047 (*t->vector)(x,y+yl);
2048 (*t->vector)(x+xl,y+yl);
2049 (*t->vector)(x+xl,y);
2051 sprintf(label,"%2d",i);
2052 (*t->put_text)(x+xl/2, y+yl+t->v_char*0.5, label);
2057 int cen_x = (int)(0.75 * xmax_t);
2058 int cen_y = (int)(0.83 * ymax_t);
2059 int radius = xmax_t / 20;
2062 /* test pm3d -- filled_polygon(), but not set_color() */
2063 if (t->filled_polygon) {
2064 #define NUMBER_OF_VERTICES 6
2065 int n = NUMBER_OF_VERTICES;
2066 gpiPoint corners[NUMBER_OF_VERTICES+1];
2067 #undef NUMBER_OF_VERTICES
2070 for (i = 0; i < n; i++) {
2071 corners[i].x = cen_x + radius * cos(2*M_PI*i/n);
2072 corners[i].y = cen_y + radius * sin(2*M_PI*i/n);
2074 corners[n].x = corners[0].x;
2075 corners[n].y = corners[0].y;
2076 corners->style = FS_OPAQUE;
2077 term->filled_polygon(n+1, corners);
2078 str = "(color) filled polygon:";
2080 str = "filled polygons not supported";
2081 (*t->linetype)(LT_BLACK);
2082 i = ((*t->justify_text) (CENTRE)) ? 0 : t->h_char * strlen(str) / 2;
2083 (*t->put_text) (cen_x + i, cen_y + radius + t->v_char * 0.5, str);
2084 (*t->linetype)(LT_BLACK);
2091 # if defined(MSDOS)||defined(g)||defined(MTOS)||defined(OS2)||defined(_Windows)||defined(DOS386)
2093 /* output for some terminal types must be binary to stop non Unix computers
2094 changing \n to \r\n.
2095 If the output is not STDOUT, the following code reopens gpoutfile
2096 with binary mode. */
2101 (void) fclose(gpoutfile);
2103 if (!stricmp(outstr, "PRN")) {
2104 /* use temp file for windows */
2105 (void) strcpy(filename, win_prntmp);
2108 if ((gpoutfile = fopen(filename, "wb")) == (FILE *) NULL) {
2109 if ((gpoutfile = fopen(filename, "w")) == (FILE *) NULL) {
2110 os_error(NO_CARET, "cannot reopen file with binary type; output unknown");
2112 os_error(NO_CARET, "cannot reopen file with binary type; output reset to ascii");
2115 # if defined(__TURBOC__) && defined(MSDOS)
2117 if (!stricmp(outstr, "PRN")) {
2118 /* Put the printer into binary mode. */
2120 regs.h.ah = 0x44; /* ioctl */
2121 regs.h.al = 0; /* get device info */
2122 regs.x.bx = fileno(gpoutfile);
2123 intdos(®s, ®s);
2124 regs.h.dl |= 0x20; /* binary (no ^Z intervention) */
2126 regs.h.ah = 0x44; /* ioctl */
2127 regs.h.al = 1; /* set device info */
2128 intdos(®s, ®s);
2130 # endif /* !_Windows */
2131 # endif /* TURBOC && MSDOS */
2135 # endif /* MSDOS || g || MTOS || ... */
2139 /* these are needed to modify terminal characteristics */
2141 /* avoid duplicate warning; VWS includes these */
2142 # include <descrip.h>
2144 # endif /* !VWS_MAX */
2147 # include <tt2def.h>
2151 /* If you use WATCOM C or a very strict ANSI compiler, you may have to
2152 * delete or comment out the following 3 lines: */
2153 # ifndef TT2$M_DECCRT3 /* VT300 not defined as of VAXC v2.4 */
2154 # define TT2$M_DECCRT3 0X80000000
2156 static unsigned short chan;
2157 static int old_char_buf[3], cur_char_buf[3];
2158 $DESCRIPTOR(sysoutput_desc, "SYS$OUTPUT");
2160 /* Look first for decw$display (decterms do regis). Determine if we
2161 * have a regis terminal and save terminal characteristics */
2165 /* Save terminal characteristics in old_char_buf and
2166 initialise cur_char_buf to current settings. */
2168 if (getenv("DECW$DISPLAY"))
2171 sys$assign(&sysoutput_desc, &chan, 0, 0);
2172 sys$qiow(0, chan, IO$_SENSEMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2173 for (i = 0; i < 3; ++i)
2174 cur_char_buf[i] = old_char_buf[i];
2177 /* Test if terminal is regis */
2178 if ((cur_char_buf[2] & TT2$M_REGIS) == TT2$M_REGIS)
2183 /* set terminal to original state */
2189 sys$assign(&sysoutput_desc, &chan, 0, 0);
2190 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2191 for (i = 0; i < 3; ++i)
2192 cur_char_buf[i] = old_char_buf[i];
2196 /* set terminal mode to tektronix */
2202 if (gpoutfile != stdout)
2203 return; /* don't modify if not stdout */
2204 sys$assign(&sysoutput_desc, &chan, 0, 0);
2205 cur_char_buf[0] = 0x004A0000 | DC$_TERM | (TT$_TEK401X << 8);
2206 cur_char_buf[1] = (cur_char_buf[1] & 0x00FFFFFF) | 0x18000000;
2208 cur_char_buf[1] &= ~TT$M_CRFILL;
2209 cur_char_buf[1] &= ~TT$M_ESCAPE;
2210 cur_char_buf[1] &= ~TT$M_HALFDUP;
2211 cur_char_buf[1] &= ~TT$M_LFFILL;
2212 cur_char_buf[1] &= ~TT$M_MECHFORM;
2213 cur_char_buf[1] &= ~TT$M_NOBRDCST;
2214 cur_char_buf[1] &= ~TT$M_NOECHO;
2215 cur_char_buf[1] &= ~TT$M_READSYNC;
2216 cur_char_buf[1] &= ~TT$M_REMOTE;
2217 cur_char_buf[1] |= TT$M_LOWER;
2218 cur_char_buf[1] |= TT$M_TTSYNC;
2219 cur_char_buf[1] |= TT$M_WRAP;
2220 cur_char_buf[1] &= ~TT$M_EIGHTBIT;
2221 cur_char_buf[1] &= ~TT$M_MECHTAB;
2222 cur_char_buf[1] &= ~TT$M_SCOPE;
2223 cur_char_buf[1] |= TT$M_HOSTSYNC;
2225 cur_char_buf[2] &= ~TT2$M_APP_KEYPAD;
2226 cur_char_buf[2] &= ~TT2$M_BLOCK;
2227 cur_char_buf[2] &= ~TT2$M_DECCRT3;
2228 cur_char_buf[2] &= ~TT2$M_LOCALECHO;
2229 cur_char_buf[2] &= ~TT2$M_PASTHRU;
2230 cur_char_buf[2] &= ~TT2$M_REGIS;
2231 cur_char_buf[2] &= ~TT2$M_SIXEL;
2232 cur_char_buf[2] |= TT2$M_BRDCSTMBX;
2233 cur_char_buf[2] |= TT2$M_EDITING;
2234 cur_char_buf[2] |= TT2$M_INSERT;
2235 cur_char_buf[2] |= TT2$M_PRINTER;
2236 cur_char_buf[2] &= ~TT2$M_ANSICRT;
2237 cur_char_buf[2] &= ~TT2$M_AVO;
2238 cur_char_buf[2] &= ~TT2$M_DECCRT;
2239 cur_char_buf[2] &= ~TT2$M_DECCRT2;
2240 cur_char_buf[2] &= ~TT2$M_DRCS;
2241 cur_char_buf[2] &= ~TT2$M_EDIT;
2242 cur_char_buf[2] |= TT2$M_FALLBACK;
2244 status = sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2245 if (status == SS$_BADPARAM) {
2246 /* terminal fallback utility not installed on system */
2247 cur_char_buf[2] &= ~TT2$M_FALLBACK;
2248 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2250 if (status != SS$_NORMAL)
2251 lib$signal(status, 0, 0);
2256 /* set terminal mode back to native */
2262 if (gpoutfile != stdout)
2263 return; /* don't modify if not stdout */
2264 sys$assign(&sysoutput_desc, &chan, 0, 0);
2265 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2266 for (i = 0; i < 3; ++i)
2267 cur_char_buf[i] = old_char_buf[i];
2271 /* set terminal mode pasthru */
2275 if (gpoutfile != stdout)
2276 return; /* don't modify if not stdout */
2277 sys$assign(&sysoutput_desc, &chan, 0, 0);
2278 cur_char_buf[2] |= TT2$M_PASTHRU;
2279 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2283 /* set terminal mode nopasthru */
2287 if (gpoutfile != stdout)
2288 return; /* don't modify if not stdout */
2289 sys$assign(&sysoutput_desc, &chan, 0, 0);
2290 cur_char_buf[2] &= ~TT2$M_PASTHRU;
2291 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2298 typedef short int INT16; /* signed 16-bit integers */
2299 INT16 k; /* loop index */
2301 if (gpoutfile != stdout) {
2302 /* Stupid VMS fflush() raises error and loses last data block
2303 unless it is full for a fixed-length record binary file.
2304 Pad it here with NULL characters. */
2305 for (k = (INT16) ((*gpoutfile)->_cnt); k > 0; --k)
2306 putc('\0', gpoutfile);
2313 * This is an abstraction of the enhanced text mode originally written
2314 * for the postscript terminal driver by David Denholm and Matt Heffron.
2315 * I have split out a terminal-independent recursive syntax-parser
2316 * routine that can be shared by all drivers that want to add support
2317 * for enhanced text mode.
2319 * A driver that wants to make use of this common framework must provide
2320 * three new entries in TERM_TABLE:
2321 * void *enhanced_open (char *fontname, double fontsize, double base,
2322 * TBOOLEAN widthflag, TBOOLEAN showflag,
2324 * void *enhanced_writec (char c)
2325 * void *enhanced_flush ()
2327 * Each driver also has a separate ENHXX_put_text() routine that replaces
2328 * the normal (term->put_text) routine while in enhanced mode.
2329 * This routine must initialize the following globals used by the shared code:
2330 * enhanced_fontscale converts font size to device resolution units
2331 * enhanced_escape_format used to process octal escape characters \xyz
2333 * I bent over backwards to make the output of the revised code identical
2334 * to the output of the original postscript version. That means there is
2335 * some cruft left in here (enhanced_max_height for one thing, and all
2336 * the code relating to RememberFont) that is probably irrelevant to any
2337 * new drivers using the code.
2339 * Ethan A Merritt - November 2003
2343 #define ENH_DEBUG(x) printf x;
2345 #define ENH_DEBUG(x)
2349 do_enh_writec(int c)
2351 /* note: c is meant to hold a char, but is actually an int, for
2352 * the same reasons applying to putc() and friends */
2353 *enhanced_cur_text++ = c;
2357 * Process a bit of string, and return the last character used.
2358 * p is start of string
2359 * brace is TRUE to keep processing to }, FALSE to do one character only
2360 * fontname & fontsize are obvious
2361 * base is the current baseline
2362 * widthflag is TRUE if the width of this should count,
2363 * FALSE for zero width boxes
2364 * showflag is TRUE if this should be shown,
2365 * FALSE if it should not be shown (like TeX \phantom)
2366 * overprint is 0 for normal operation,
2367 * 1 for the underprinted text (included in width calculation),
2368 * 2 for the overprinted text (not included in width calc)
2369 * (overprinted text is centered horizontally on underprinted text
2383 ENH_DEBUG(("RECURSE WITH [%p] \"%s\", %d %s %.1f %.1f %d %d %d\n", p, p, brace, fontname, fontsize, base, widthflag, showflag, overprint));
2385 /* Start each recursion with a clean string */
2386 (term->enhanced_flush)();
2388 if (base + fontsize > enhanced_max_height) {
2389 enhanced_max_height = base + fontsize;
2390 ENH_DEBUG(("Setting max height to %.1f\n", enhanced_max_height));
2393 if (base < enhanced_min_height) {
2394 enhanced_min_height = base;
2395 ENH_DEBUG(("Setting min height to %.1f\n", enhanced_min_height));
2403 /*{{{ deal with it*/
2407 fputs("enhanced text parser - spurious }\n", stderr);
2413 /*{{{ deal with super/sub script*/
2414 shift = (*p == '^') ? 0.5 : -0.3;
2415 (term->enhanced_flush)();
2416 p = enhanced_recursion(p + 1, FALSE, fontname, fontsize * 0.8,
2417 base + shift * fontsize, widthflag,
2418 showflag, overprint);
2423 char *savepos = NULL, save = 0;
2424 char *localfontname = fontname, ch;
2426 float f = fontsize, ovp;
2428 /*{{{ recurse (possibly with a new font) */
2430 ENH_DEBUG(("Dealing with {\n"));
2432 /* get vertical offset (if present) for overprinted text */
2433 while (*++p == ' ');
2434 if (overprint == 2) {
2435 ovp = (float)strtod(p,&p);
2436 if (term->flags & TERM_IS_POSTSCRIPT)
2441 --p; /* HBB 20001021: bug fix: 10^{2} broken */
2444 /* then parse a fontname, optional fontsize */
2453 while ((ch = *p) > ' ' && ch != '=' && ch != '*' && ch != '}')
2455 save = *(savepos=p);
2458 /*{{{ get optional font size*/
2459 ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
2460 f = (float)strtod(p, &p);
2461 ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
2466 f *= enhanced_fontscale; /* remember the scaling */
2468 ENH_DEBUG(("Font size %.1f\n", f));
2470 } else if (ch == '*') {
2472 /*{{{ get optional font size scale factor*/
2473 ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
2474 f = (float)strtod(p, &p);
2475 ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
2478 f *= fontsize; /* apply the scale factor */
2482 ENH_DEBUG(("Font size %.1f\n", f));
2491 if (! *localfontname) {
2492 localfontname = fontname;
2493 #ifdef POSTSCRIPT_DRIVER
2494 } else if (!strncmp("postscript",term->name,9)) {
2495 /* FIXME - This cruft is left over from when the code */
2496 /* was part of post.trm. No one else needs it! */
2497 char *recodestring = (PS_RememberFont)(localfontname,
2498 recode && !ENHps_opened_string);
2499 if (recode && recodestring) {
2500 (term->enhanced_flush)();
2501 fprintf(gpoutfile, "/%s %s",
2502 localfontname, recodestring);
2509 ENH_DEBUG(("Before recursing, we are at [%p] \"%s\"\n", p, p));
2511 p = enhanced_recursion(p, TRUE, localfontname, f, base,
2512 widthflag, showflag, overprint);
2514 ENH_DEBUG(("BACK WITH \"%s\"\n", p));
2516 (term->enhanced_flush)();
2519 /* restore overwritten character */
2524 /*{{{ phantom box - prints next 'char', then restores currentpoint */
2525 (term->enhanced_flush)();
2526 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 3);
2527 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2528 widthflag, showflag, overprint);
2529 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 4);
2534 /*{{{ character skip - skips space equal to length of character(s) */
2535 (term->enhanced_flush)();
2537 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2538 widthflag, FALSE, overprint);
2543 /*{{{ overprinted text */
2544 /* the second string is overwritten on the first, centered
2545 * horizontally on the first and (optionally) vertically
2546 * shifted by an amount specified (as a fraction of the
2547 * current fontsize) at the beginning of the second string
2549 * Note that in this implementation neither the under- nor
2550 * overprinted string can contain syntax that would result
2551 * in additional recursions -- no subscripts,
2552 * superscripts, or anything else, with the exception of a
2553 * font definition at the beginning of the text */
2555 (term->enhanced_flush)();
2556 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2557 widthflag, showflag, 1);
2558 (term->enhanced_flush)();
2559 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2560 FALSE, showflag, 2);
2562 overprint = 0; /* may not be necessary, but just in case . . . */
2568 /*{{{ an escape and print it */
2570 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2571 if (term->flags & TERM_IS_POSTSCRIPT)
2572 (term->enhanced_writec)('\\');
2573 (term->enhanced_writec)(*p);
2578 if (p[1]=='\\' || p[1]=='(' || p[1]==')') {
2579 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2580 (term->enhanced_writec)('\\');
2582 /*{{{ The enhanced mode always uses \xyz as an octal character representation
2583 but each terminal type must give us the actual output format wanted.
2584 pdf.trm wants the raw character code, which is why we use strtol();
2585 most other terminal types want some variant of "\\%o". */
2586 } else if (p[1] >= '0' && p[1] <= '7') {
2587 char *e, escape[16], octal[4] = {'\0','\0','\0','\0'};
2589 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2591 if (p[1] >= '0' && p[1] <= '7') {
2593 if (p[1] >= '0' && p[1] <= '7')
2596 sprintf(escape, enhanced_escape_format, strtol(octal,NULL,8));
2597 for (e=escape; *e; e++) {
2598 (term->enhanced_writec)(*e);
2601 } else if (term->flags & TERM_IS_POSTSCRIPT) {
2602 /* Shigeharu TAKENO Aug 2004 - Needed in order for shift-JIS */
2603 /* encoding to work. If this change causes problems then we */
2604 /* need a separate flag for shift-JIS and certain other 8-bit */
2605 /* character sets. */
2606 /* EAM Nov 2004 - Nevertheless we must allow \ to act as an */
2607 /* escape for a few enhanced mode formatting characters even */
2608 /* though it corrupts certain Shift-JIS character sequences. */
2609 if (strchr("^_@&~{}",p[1]) == NULL) {
2610 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2611 (term->enhanced_writec)('\\');
2612 (term->enhanced_writec)('\\');
2618 /* HBB 20030122: Avoid broken output if there's a \
2619 * exactly at the end of the line */
2621 fputs("enhanced text parser -- spurious backslash\n", stderr);
2625 /* just go and print it (fall into the 'default' case) */
2629 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2630 (term->enhanced_writec)(*p);
2634 /* like TeX, we only do one character in a recursion, unless it's
2639 (term->enhanced_flush)();
2640 return(p); /* the ++p in the outer copy will increment us */
2643 if (*p) /* only not true if { not terminated, I think */
2647 (term->enhanced_flush)();
2651 /* Called after the end of recursion to check for errors */
2653 enh_err_check(const char *str)
2656 fputs("enhanced text mode parser - ignoring spurious }\n", stderr);
2658 fprintf(stderr, "enhanced text mode parsing error - *str=0x%x\n",
2662 /* Helper function for multiplot auto layout to issue size and offest cmds */
2664 mp_layout_size_and_offset(void)
2666 if (!mp_layout.auto_layout) return;
2668 /* fprintf(stderr,"col==%d row==%d\n",mp_layout.act_col,mp_layout.act_row); */
2669 /* the 'set size' command */
2670 xsize = mp_layout.xscale / mp_layout.num_cols;
2671 ysize = mp_layout.yscale / mp_layout.num_rows;
2673 /* the 'set origin' command */
2674 xoffset = (double)(mp_layout.act_col) / mp_layout.num_cols;
2675 if (mp_layout.downwards)
2676 yoffset = 1.0 - (double)(mp_layout.act_row+1) / mp_layout.num_rows;
2678 yoffset = (double)(mp_layout.act_row) / mp_layout.num_rows;
2679 /* fprintf(stderr,"xoffset==%g yoffset==%g\n", xoffset,yoffset); */
2681 /* Allow a little space at the top for a title */
2682 if (mp_layout.title.text) {
2683 ysize *= (1.0 - mp_layout.title_height);
2684 yoffset *= (1.0 - mp_layout.title_height);
2687 /* corrected for x/y-scaling factors and user defined offsets */
2688 xoffset -= (mp_layout.xscale-1)/(2*mp_layout.num_cols);
2689 yoffset -= (mp_layout.yscale-1)/(2*mp_layout.num_rows);
2690 /* fprintf(stderr," xoffset==%g yoffset==%g\n", xoffset,yoffset); */
2691 xoffset += mp_layout.xoffset;
2692 yoffset += mp_layout.yoffset;
2693 /* fprintf(stderr," xoffset==%g yoffset==%g\n", xoffset,yoffset); */
2697 * Text strings containing control information for enhanced text mode
2698 * contain more characters than will actually appear in the output.
2699 * This makes it hard to estimate how much horizontal space on the plot
2700 * (e.g. in the key box) must be reserved to hold them. To approximate
2701 * the eventually length we switch briefly to the dummy terminal driver
2702 * "estimate.trm" and then switch back to the current terminal.
2703 * If better, perhaps terminal-specific methods of estimation are
2704 * developed later they can be slotted into this one call site.
2707 estimate_strlen(char *text)
2712 if (term->flags & TERM_ENHANCED_TEXT) {
2713 struct termentry *tsave = term;
2715 term->put_text(0,0,text);
2718 FPRINTF((stderr,"Estimating length %d for enhanced text string \"%s\"\n",len,text));
2727 ignore_enhanced(TBOOLEAN flag)
2729 /* Force a return to the default font */
2730 if (flag && !ignore_enhanced_text) {
2731 ignore_enhanced_text = TRUE;
2735 ignore_enhanced_text = flag;
2738 /* Simple-minded test for whether the point (x,y) is in bounds for the current terminal.
2739 * Some terminals can do their own clipping, and can clip partial objects.
2740 * If the flag TERM_CAN_CLIP is set, we skip this relative crude test and let the
2741 * driver or the hardware handle clipping.
2744 on_page(int x, int y)
2746 if (term->flags & TERM_CAN_CLIP)
2749 if ((0 < x && x < term->xmax) && (0 < y && y < term->ymax))
2755 /* Utility routine for drivers to accept an explicit size for the
2759 parse_term_size( float *xsize, float *ysize, size_units default_units )
2761 size_units units = default_units;
2765 int_error(c_token, "size requires two numbers: xsize, ysize");
2766 *xsize = real(const_express(&a));
2767 if (almost_equals(c_token,"in$ches")) {
2770 } else if (equals(c_token,"cm")) {
2775 case INCHES: *xsize *= gp_resolution; break;
2776 case CM: *xsize *= (float)gp_resolution / 2.54; break;
2781 if (!equals(c_token++,","))
2782 int_error(c_token, "size requires two numbers: xsize, ysize");
2783 *ysize = real(const_express(&a));
2784 if (almost_equals(c_token,"in$ches")) {
2787 } else if (equals(c_token,"cm")) {
2792 case INCHES: *ysize *= gp_resolution; break;
2793 case CM: *ysize *= (float)gp_resolution / 2.54; break;
2798 if (*xsize < 1 || *ysize < 1)
2799 int_error(c_token, "size: out of range");
2805 * Wrappers for newpath and closepath