1 /* GNUPLOT - term.c */
2
3 /*[
4 * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
5 *
6 * Permission to use, copy, and distribute this software and its
7 * documentation for any purpose with or without fee is hereby granted,
8 * provided that the above copyright notice appear in all copies and
9 * that both that copyright notice and this permission notice appear
10 * in supporting documentation.
11 *
12 * Permission to modify the software is granted, but not the right to
13 * distribute the complete modified source code. Modifications are to
14 * be distributed as patches to the released version. Permission to
15 * distribute binaries produced by compiling modified sources is granted,
16 * provided you
17 * 1. distribute the corresponding source modifications from the
18 * released version in the form of a patch file along with the binaries,
19 * 2. add special version identification to distinguish your version
20 * in addition to the base release version number,
21 * 3. provide your name and address as the primary contact for the
22 * support of your modified version, and
23 * 4. retain our contact information in regard to use of the base
24 * software.
25 * Permission to distribute the released version of the source code along
26 * with corresponding source modifications in the form of a patch file is
27 * granted with same provisions 2 through 4 for binary distributions.
28 *
29 * This software is provided "as is" without express or implied warranty
30 * to the extent permitted by applicable law.
31 ]*/
32
33
34 /* This module is responsible for looking after the terminal
35 * drivers at the lowest level. Only this module (should)
36 * know about all the various rules about interpreting
37 * the terminal capabilities exported by the terminal
38 * drivers in the table.
39 *
40 * Note that, as far as this module is concerned, a
41 * terminal session lasts only until _either_ terminal
42 * or output file changes. Before either is changed,
43 * the terminal is shut down.
44 *
45 * Entry points : (see also term/README)
46 *
47 * term_set_output() : called when set output invoked
48 *
49 * term_initialise() : optional. Prepare the terminal for first
50 * use. It protects itself against subsequent calls.
51 *
52 * term_start_plot() : called at start of graph output. Calls term_init
53 * if necessary
54 *
55 * term_apply_lp_properties() : apply linewidth settings
56 *
57 * term_end_plot() : called at the end of a plot
58 *
59 * term_reset() : called during int_error handling, to shut
60 * terminal down cleanly
61 *
62 * term_start_multiplot() : called by set multiplot
63 *
64 * term_end_multiplot() : called by set nomultiplot
65 *
66 * term_check_multiplot_okay() : called just before an interactive
67 * prompt is issued while in multiplot mode,
68 * to allow terminal to suspend if necessary,
69 * Raises an error if interactive multiplot
70 * is not supported.
71 */
72
73 #include "term_api.h"
74
75 #include "alloc.h"
76 #include "axis.h"
77 #ifndef NO_BITMAP_SUPPORT
78 #include "bitmap.h"
79 #endif
80 #include "command.h"
81 #include "driver.h"
82 #include "graphics.h"
83 #include "help.h"
84 #include "plot.h"
85 #include "tables.h"
86 #include "getcolor.h"
87 #include "term.h"
88 #include "util.h"
89 #include "version.h"
90 #include "misc.h"
91 #include "multiplot.h"
92 #include "readline.h"
93
94 #ifdef USE_MOUSE
95 #include "mouse.h"
96 #else
97 /* Some terminals (svg canvas) can provide mousing information */
98 /* even if the interactive gnuplot session itself cannot. */
99 long mouse_mode = 0;
100 char* mouse_alt_string = NULL;
101 #endif
102
103 #ifdef _WIN32
104 # include "win/winmain.h"
105 # include "win/wcommon.h"
106 # ifdef __MSC__
107 # include <malloc.h>
108 # include <io.h>
109 # else
110 # include <alloc.h>
111 # endif /* MSC */
112 #endif /* _WIN32 */
113
114 static int termcomp __PROTO((const generic * a, const generic * b));
115
116 /* Externally visible variables */
117 /* the central instance: the current terminal's interface structure */
118 struct termentry *term = NULL; /* unknown */
119
120 /* ... and its options string */
121 char term_options[MAX_LINE_LEN+1] = "";
122
123 /* the 'output' file name and handle */
124 char *outstr = NULL; /* means "STDOUT" */
125 FILE *gpoutfile;
126
127 /* Output file where the PostScript output goes to. See term_api.h for more
128 details.
129 */
130 FILE *gppsfile = 0;
131 char *PS_psdir = NULL;
132
133 /* true if terminal has been initialized */
134 TBOOLEAN term_initialised;
135
136 /* The qt and wxt terminals cannot be used in the same session. */
137 /* Whichever one is used first to plot, this locks out the other. */
138 void *term_interlock = NULL;
139
140 /* true if "set monochrome" */
141 TBOOLEAN monochrome = FALSE;
142
143 /* true if in multiplot mode */
144 TBOOLEAN multiplot = FALSE;
145
146 /* text output encoding, for terminals that support it */
147 enum set_encoding_id encoding;
148 /* table of encoding names, for output of the setting */
149 const char *encoding_names[] = {
150 "default", "iso_8859_1", "iso_8859_2", "iso_8859_9", "iso_8859_15",
151 "cp437", "cp850", "cp852", "cp950", "cp1250", "cp1251", "cp1252", "cp1254",
152 "koi8r", "koi8u", "sjis", "utf8", NULL };
153 /* 'set encoding' options */
154 const struct gen_table set_encoding_tbl[] =
155 {
156 { "def$ault", S_ENC_DEFAULT },
157 { "utf$8", S_ENC_UTF8 },
158 { "iso$_8859_1", S_ENC_ISO8859_1 },
159 { "iso_8859_2", S_ENC_ISO8859_2 },
160 { "iso_8859_9", S_ENC_ISO8859_9 },
161 { "iso_8859_15", S_ENC_ISO8859_15 },
162 { "cp4$37", S_ENC_CP437 },
163 { "cp850", S_ENC_CP850 },
164 { "cp852", S_ENC_CP852 },
165 { "cp950", S_ENC_CP950 },
166 { "cp1250", S_ENC_CP1250 },
167 { "cp1251", S_ENC_CP1251 },
168 { "cp1252", S_ENC_CP1252 },
169 { "cp1254", S_ENC_CP1254 },
170 { "koi8$r", S_ENC_KOI8_R },
171 { "koi8$u", S_ENC_KOI8_U },
172 { "sj$is", S_ENC_SJIS },
173 { NULL, S_ENC_INVALID }
174 };
175
176 const char *arrow_head_names[4] =
177 {"nohead", "head", "backhead", "heads"};
178
179 enum { IPC_BACK_UNUSABLE = -2, IPC_BACK_CLOSED = -1 };
180 #ifdef PIPE_IPC
181 /* HBB 20020225: currently not used anywhere outside term.c */
182 static SELECT_TYPE_ARG1 ipc_back_fd = IPC_BACK_CLOSED;
183 #endif
184
185 /* resolution in dpi for converting pixels to size units */
186 int gp_resolution = 72;
187
188 /* Support for enhanced text mode. Declared extern in term_api.h */
189 char enhanced_text[MAX_LINE_LEN+1] = "";
190 char *enhanced_cur_text = NULL;
191 double enhanced_fontscale = 1.0;
192 char enhanced_escape_format[16] = "";
193 double enhanced_max_height = 0.0, enhanced_min_height = 0.0;
194 /* flag variable to disable enhanced output of filenames, mainly. */
195 TBOOLEAN ignore_enhanced_text = FALSE;
196
197 /* Recycle count for user-defined linetypes */
198 int linetype_recycle_count = 0;
199 int mono_recycle_count = 0;
200
201
202 /* Internal variables */
203
204 /* true if terminal is in graphics mode */
205 static TBOOLEAN term_graphics = FALSE;
206
207 /* we have suspended the driver, in multiplot mode */
208 static TBOOLEAN term_suspended = FALSE;
209
210 /* true if? */
211 static TBOOLEAN opened_binary = FALSE;
212
213 /* true if require terminal to be initialized */
214 static TBOOLEAN term_force_init = FALSE;
215
216 /* internal pointsize for do_point */
217 static double term_pointsize=1;
218
219 /* Internal prototypes: */
220
221 static void term_suspend __PROTO((void));
222 static void term_close_output __PROTO((void));
223
224 static void null_linewidth __PROTO((double));
225 static void do_point __PROTO((unsigned int x, unsigned int y, int number));
226 static void do_pointsize __PROTO((double size));
227 static void line_and_point __PROTO((unsigned int x, unsigned int y, int number));
228 static void do_arrow __PROTO((unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, int head));
229 static void null_dashtype __PROTO((int type, t_dashtype *custom_dash_pattern));
230
231 static int null_text_angle __PROTO((int ang));
232 static int null_justify_text __PROTO((enum JUSTIFY just));
233 static int null_scale __PROTO((double x, double y));
234 static void null_layer __PROTO((t_termlayer layer));
235 static int null_set_font __PROTO((const char *font));
236 static void null_set_color __PROTO((struct t_colorspec *colorspec));
237 static void options_null __PROTO((void));
238 static void graphics_null __PROTO((void));
239 static void UNKNOWN_null __PROTO((void));
240 static void MOVE_null __PROTO((unsigned int, unsigned int));
241 static void LINETYPE_null __PROTO((int));
242 static void PUTTEXT_null __PROTO((unsigned int, unsigned int, const char *));
243
244 static int strlen_tex __PROTO((const char *));
245
246 static char *stylefont __PROTO((const char *fontname, TBOOLEAN isbold, TBOOLEAN isitalic));
247
248 /* Used by terminals and by shared routine parse_term_size() */
249 typedef enum {
250 PIXELS,
251 INCHES,
252 CM
253 } size_units;
254 static size_units parse_term_size __PROTO((float *xsize, float *ysize, size_units def_units));
255
256
257 #ifdef VMS
258 char *vms_init();
259 void vms_reset();
260 void term_mode_tek();
261 void term_mode_native();
262 void term_pasthru();
263 void term_nopasthru();
264 void fflush_binary();
265 # define FOPEN_BINARY(file) fopen(file, "wb", "rfm=fix", "bls=512", "mrs=512")
266 #else /* !VMS */
267 # define FOPEN_BINARY(file) fopen(file, "wb")
268 #endif /* !VMS */
269
270 #if defined(MSDOS) || defined(_WIN32)
271 # if defined(__DJGPP__)
272 # include <io.h>
273 # endif
274 # include <fcntl.h>
275 # ifndef O_BINARY
276 # ifdef _O_BINARY
277 # define O_BINARY _O_BINARY
278 # else
279 # define O_BINARY O_BINARY_is_not_defined
280 # endif
281 # endif
282 #endif
283
284 #ifdef __EMX__
285 #include <io.h>
286 #include <fcntl.h>
287 #endif
288
289 #if defined(__WATCOMC__) || defined(__MSC__)
290 # include <io.h> /* for setmode() */
291 #endif
292
293 #define NICE_LINE 0
294 #define POINT_TYPES 6
295
296 #ifndef DEFAULTTERM
297 # define DEFAULTTERM NULL
298 #endif
299
300 /* interface to the rest of gnuplot - the rules are getting
301 * too complex for the rest of gnuplot to be allowed in
302 */
303
304 #if defined(PIPES)
305 static TBOOLEAN output_pipe_open = FALSE;
306 #endif /* PIPES */
307
308 static void
term_close_output()309 term_close_output()
310 {
311 FPRINTF((stderr, "term_close_output\n"));
312
313 opened_binary = FALSE;
314
315 if (!outstr) /* ie using stdout */
316 return;
317
318 #if defined(PIPES)
319 if (output_pipe_open) {
320 (void) pclose(gpoutfile);
321 output_pipe_open = FALSE;
322 } else
323 #endif /* PIPES */
324 #ifdef _WIN32
325 if (stricmp(outstr, "PRN") == 0)
326 close_printer(gpoutfile);
327 else
328 #endif
329 if (gpoutfile != gppsfile)
330 fclose(gpoutfile);
331
332 gpoutfile = stdout; /* Don't dup... */
333 free(outstr);
334 outstr = NULL;
335
336 if (gppsfile)
337 fclose(gppsfile);
338 gppsfile = NULL;
339 }
340
341 /* assigns dest to outstr, so it must be allocated or NULL
342 * and it must not be outstr itself !
343 */
344 void
term_set_output(char * dest)345 term_set_output(char *dest)
346 {
347 FILE *f = NULL;
348
349 FPRINTF((stderr, "term_set_output\n"));
350 assert(dest == NULL || dest != outstr);
351
352 if (multiplot) {
353 fputs("In multiplot mode you can't change the output\n", stderr);
354 return;
355 }
356 if (term && term_initialised) {
357 (*term->reset) ();
358 term_initialised = FALSE;
359 /* switch off output to special postscript file (if used) */
360 gppsfile = NULL;
361 }
362 if (dest == NULL) { /* stdout */
363 term_close_output();
364 } else {
365
366 #if defined(PIPES)
367 if (*dest == '|') {
368 restrict_popen();
369 #ifdef _WIN32
370 if (term && (term->flags & TERM_BINARY))
371 f = popen(dest + 1, "wb");
372 else
373 f = popen(dest + 1, "w");
374 #else
375 f = popen(dest + 1, "w");
376 #endif
377 if (f == (FILE *) NULL)
378 os_error(c_token, "cannot create pipe; output not changed");
379 else
380 output_pipe_open = TRUE;
381 } else {
382 #endif /* PIPES */
383
384 #ifdef _WIN32
385 if (outstr && stricmp(outstr, "PRN") == 0) {
386 /* we can't call open_printer() while printer is open, so */
387 close_printer(gpoutfile); /* close printer immediately if open */
388 gpoutfile = stdout; /* and reset output to stdout */
389 free(outstr);
390 outstr = NULL;
391 }
392 if (stricmp(dest, "PRN") == 0) {
393 if ((f = open_printer()) == (FILE *) NULL)
394 os_error(c_token, "cannot open printer temporary file; output may have changed");
395 } else
396 #endif
397
398 {
399 #if defined (MSDOS)
400 if (outstr && (0 == stricmp(outstr, dest))) {
401 /* On MSDOS, you cannot open the same file twice and
402 * then close the first-opened one and keep the second
403 * open, it seems. If you do, you get lost clusters
404 * (connection to the first version of the file is
405 * lost, it seems). */
406 /* FIXME: this is not yet safe enough. You can fool it by
407 * specifying the same output file in two different ways
408 * (relative vs. absolute path to file, e.g.) */
409 term_close_output();
410 }
411 #endif
412 if (term && (term->flags & TERM_BINARY))
413 f = FOPEN_BINARY(dest);
414 else
415 f = fopen(dest, "w");
416
417 if (f == (FILE *) NULL)
418 os_error(c_token, "cannot open file; output not changed");
419 }
420 #if defined(PIPES)
421 }
422 #endif
423
424 term_close_output();
425 gpoutfile = f;
426 outstr = dest;
427 opened_binary = (term && (term->flags & TERM_BINARY));
428 }
429 }
430
431 void
term_initialise()432 term_initialise()
433 {
434 FPRINTF((stderr, "term_initialise()\n"));
435
436 if (!term)
437 int_error(NO_CARET, "No terminal defined");
438
439 /* check if we have opened the output file in the wrong mode
440 * (text/binary), if set term comes after set output
441 * This was originally done in change_term, but that
442 * resulted in output files being truncated
443 */
444
445 if (outstr && (term->flags & TERM_NO_OUTPUTFILE)) {
446 if (interactive)
447 fprintf(stderr,"Closing %s\n",outstr);
448 term_close_output();
449 }
450
451 if (outstr &&
452 (((term->flags & TERM_BINARY) && !opened_binary) ||
453 ((!(term->flags & TERM_BINARY) && opened_binary)))) {
454 /* this is nasty - we cannot just term_set_output(outstr)
455 * since term_set_output will first free outstr and we
456 * end up with an invalid pointer. I think I would
457 * prefer to defer opening output file until first plot.
458 */
459 char *temp = gp_alloc(strlen(outstr) + 1, "temp file string");
460 if (temp) {
461 FPRINTF((stderr, "term_initialise: reopening \"%s\" as %s\n",
462 outstr, term->flags & TERM_BINARY ? "binary" : "text"));
463 strcpy(temp, outstr);
464 term_set_output(temp); /* will free outstr */
465 if (temp != outstr) {
466 if (temp)
467 free(temp);
468 temp = outstr;
469 }
470 } else
471 fputs("Cannot reopen output file in binary", stderr);
472 /* and carry on, hoping for the best ! */
473 }
474 #if defined(MSDOS) || defined (_WIN32) || defined(OS2)
475 # ifdef _WIN32
476 else if (!outstr && (term->flags & TERM_BINARY))
477 # else
478 else if (!outstr && !interactive && (term->flags & TERM_BINARY))
479 # endif
480 {
481 #if defined(_WIN32) && !defined(WGP_CONSOLE)
482 #ifdef PIPES
483 if (!output_pipe_open)
484 #endif
485 if (outstr == NULL && !(term->flags & TERM_NO_OUTPUTFILE))
486 int_error(c_token, "cannot output binary data to wgnuplot text window");
487 #endif
488 /* binary to stdout in non-interactive session... */
489 fflush(stdout);
490 setmode(fileno(stdout), O_BINARY);
491 }
492 #endif
493
494 if (!term_initialised || term_force_init) {
495 FPRINTF((stderr, "- calling term->init()\n"));
496 (*term->init) ();
497 term_initialised = TRUE;
498 #ifdef HAVE_LOCALE_H
499 /* This is here only from an abundance of caution (a.k.a. paranoia).
500 * Some terminals (wxt qt caca) are known to change the locale when
501 * initialized. Others have been implicated (gd). Rather than trying
502 * to catch all such offenders one by one, cover for all of them here.
503 */
504 setlocale(LC_NUMERIC, "C");
505 #endif
506 }
507 }
508
509
510 void
term_start_plot()511 term_start_plot()
512 {
513 FPRINTF((stderr, "term_start_plot()\n"));
514
515 if (!term_initialised)
516 term_initialise();
517
518 if (!term_graphics) {
519 FPRINTF((stderr, "- calling term->graphics()\n"));
520 (*term->graphics) ();
521 term_graphics = TRUE;
522 } else if (multiplot && term_suspended) {
523 if (term->resume) {
524 FPRINTF((stderr, "- calling term->resume()\n"));
525 (*term->resume) ();
526 }
527 term_suspended = FALSE;
528 }
529
530 /* Sync point for epslatex text positioning */
531 (*term->layer)(TERM_LAYER_RESET);
532
533 /* Because PostScript plots may be viewed out of order, make sure */
534 /* Each new plot makes no assumption about the previous palette. */
535 if (term->flags & TERM_IS_POSTSCRIPT)
536 invalidate_palette();
537
538 /* Set canvas size to full range of current terminal coordinates */
539 canvas.xleft = 0;
540 canvas.xright = term->xmax - 1;
541 canvas.ybot = 0;
542 canvas.ytop = term->ymax - 1;
543
544 }
545
546 void
term_end_plot()547 term_end_plot()
548 {
549 FPRINTF((stderr, "term_end_plot()\n"));
550
551 if (!term_initialised)
552 return;
553
554 /* Sync point for epslatex text positioning */
555 (*term->layer)(TERM_LAYER_END_TEXT);
556
557 if (!multiplot) {
558 FPRINTF((stderr, "- calling term->text()\n"));
559 (*term->text) ();
560 term_graphics = FALSE;
561 } else {
562 multiplot_next();
563 }
564 #ifdef VMS
565 if (opened_binary)
566 fflush_binary();
567 else
568 #endif /* VMS */
569
570 (void) fflush(gpoutfile);
571
572 #ifdef USE_MOUSE
573 recalc_statusline();
574 update_ruler();
575 #endif
576 }
577
578
579 static void
term_suspend()580 term_suspend()
581 {
582 FPRINTF((stderr, "term_suspend()\n"));
583 if (term_initialised && !term_suspended && term->suspend) {
584 FPRINTF((stderr, "- calling term->suspend()\n"));
585 (*term->suspend) ();
586 term_suspended = TRUE;
587 }
588 }
589
590 void
term_reset()591 term_reset()
592 {
593 FPRINTF((stderr, "term_reset()\n"));
594
595 #ifdef USE_MOUSE
596 /* Make sure that ^C will break out of a wait for 'pause mouse' */
597 paused_for_mouse = 0;
598 #ifdef _WIN32
599 kill_pending_Pause_dialog();
600 #endif
601 #endif
602
603 if (!term_initialised)
604 return;
605
606 if (term_suspended) {
607 if (term->resume) {
608 FPRINTF((stderr, "- calling term->resume()\n"));
609 (*term->resume) ();
610 }
611 term_suspended = FALSE;
612 }
613 if (term_graphics) {
614 (*term->text) ();
615 term_graphics = FALSE;
616 }
617 if (term_initialised) {
618 (*term->reset) ();
619 term_initialised = FALSE;
620 /* switch off output to special postscript file (if used) */
621 gppsfile = NULL;
622 }
623 }
624
625 void
term_apply_lp_properties(struct lp_style_type * lp)626 term_apply_lp_properties(struct lp_style_type *lp)
627 {
628 /* This function passes all the line and point properties to the
629 * terminal driver and issues the corresponding commands.
630 *
631 * Alas, sometimes it might be necessary to give some help to
632 * this function by explicitly issuing additional '(*term)(...)'
633 * commands.
634 */
635 int lt = lp->l_type;
636 int dt = lp->d_type;
637 t_dashtype custom_dash_pattern = lp->custom_dash_pattern;
638 t_colorspec colorspec = lp->pm3d_color;
639
640 if ((lp->flags & LP_SHOW_POINTS)) {
641 /* change points, too
642 * Currently, there is no 'pointtype' function. For points
643 * there is a special function also dealing with (x,y) co-
644 * ordinates.
645 */
646 if (lp->p_size < 0)
647 (*term->pointsize) (pointsize);
648 else
649 (*term->pointsize) (lp->p_size);
650 }
651 /* _first_ set the line width, _then_ set the line type !
652 * The linetype might depend on the linewidth in some terminals.
653 */
654 (*term->linewidth) (lp->l_width);
655
656 /* LT_DEFAULT (used only by "set errorbars"?) means don't change it */
657 /* FIXME: If this causes problems, test also for LP_ERRORBAR_SET */
658 if (lt == LT_DEFAULT)
659 ;
660 else
661
662 /* The paradigm for handling linetype and dashtype in version 5 is */
663 /* linetype < 0 (e.g. LT_BACKGROUND, LT_NODRAW) means some special */
664 /* category that will be handled directly by term->linetype(). */
665 /* linetype > 0 is now redundant. It used to encode both a color */
666 /* and a dash pattern. Now we have separate mechanisms for those. */
667 if (LT_COLORFROMCOLUMN < lt && lt < 0)
668 (*term->linetype) (lt);
669 else if (term->set_color == null_set_color) {
670 (*term->linetype) (lt-1);
671 return;
672 } else /* All normal lines will be solid unless a dashtype is given */
673 (*term->linetype) (LT_SOLID);
674
675 /* Apply dashtype or user-specified dash pattern, which may override */
676 /* the terminal-specific dot/dash pattern belonging to this linetype. */
677 if (lt == LT_AXIS)
678 ; /* LT_AXIS is a special linetype that may incorporate a dash pattern */
679 else if (dt == DASHTYPE_CUSTOM)
680 (*term->dashtype) (dt, &custom_dash_pattern);
681 else if (dt == DASHTYPE_SOLID)
682 (*term->dashtype) (dt, NULL);
683 else if (dt >= 0)
684 /* The null_dashtype() routine or a version 5 terminal's private */
685 /* dashtype routine converts this into a call to term->linetype() */
686 /* yielding the same result as in version 4 except possibly for a */
687 /* different line width. */
688 (*term->dashtype) (dt, NULL);
689
690 /* Finally adjust the color of the line */
691 apply_pm3dcolor(&colorspec);
692 }
693
694 void
term_start_multiplot()695 term_start_multiplot()
696 {
697 FPRINTF((stderr, "term_start_multiplot()\n"));
698 multiplot_start();
699 #ifdef USE_MOUSE
700 UpdateStatusline();
701 #endif
702 }
703
704 void
term_end_multiplot()705 term_end_multiplot()
706 {
707 FPRINTF((stderr, "term_end_multiplot()\n"));
708 if (!multiplot)
709 return;
710
711 if (term_suspended) {
712 if (term->resume)
713 (*term->resume) ();
714 term_suspended = FALSE;
715 }
716
717 multiplot_end();
718
719 term_end_plot();
720 #ifdef USE_MOUSE
721 UpdateStatusline();
722 #endif
723 }
724
725 void
term_check_multiplot_okay(TBOOLEAN f_interactive)726 term_check_multiplot_okay(TBOOLEAN f_interactive)
727 {
728 FPRINTF((stderr, "term_multiplot_okay(%d)\n", f_interactive));
729
730 if (!term_initialised)
731 return; /* they've not started yet */
732
733 /* make sure that it is safe to issue an interactive prompt
734 * it is safe if
735 * it is not an interactive read, or
736 * the terminal supports interactive multiplot, or
737 * we are not writing to stdout and terminal doesn't
738 * refuse multiplot outright
739 */
740 if (!f_interactive || (term->flags & TERM_CAN_MULTIPLOT) ||
741 ((gpoutfile != stdout) && !(term->flags & TERM_CANNOT_MULTIPLOT))
742 ) {
743 /* it's okay to use multiplot here, but suspend first */
744 term_suspend();
745 return;
746 }
747 /* uh oh: they're not allowed to be in multiplot here */
748
749 term_end_multiplot();
750
751 /* at this point we know that it is interactive and that the
752 * terminal can either only do multiplot when writing to
753 * to a file, or it does not do multiplot at all
754 */
755
756 if (term->flags & TERM_CANNOT_MULTIPLOT)
757 int_error(NO_CARET, "This terminal does not support multiplot");
758 else
759 int_error(NO_CARET, "Must set output to a file or put all multiplot commands on one input line");
760 }
761
762
763 void
write_multiline(unsigned int x,unsigned int y,char * text,JUSTIFY hor,VERT_JUSTIFY vert,int angle,const char * font)764 write_multiline(
765 unsigned int x, unsigned int y,
766 char *text,
767 JUSTIFY hor, /* horizontal ... */
768 VERT_JUSTIFY vert, /* ... and vertical just - text in hor direction despite angle */
769 int angle, /* assume term has already been set for this */
770 const char *font) /* NULL or "" means use default */
771 {
772 struct termentry *t = term;
773 char *p = text;
774
775 if (!p)
776 return;
777
778 /* EAM 9-Feb-2003 - Set font before calculating sizes */
779 if (font && *font)
780 (*t->set_font) (font);
781
782 if (vert != JUST_TOP) {
783 /* count lines and adjust y */
784 int lines = 0; /* number of linefeeds - one fewer than lines */
785 while (*p) {
786 if (*p++ == '\n')
787 ++lines;
788 }
789 if (angle)
790 x -= (vert * lines * t->v_char) / 2;
791 else
792 y += (vert * lines * t->v_char) / 2;
793 }
794
795 for (;;) { /* we will explicitly break out */
796
797 if ((text != NULL) && (p = strchr(text, '\n')) != NULL)
798 *p = 0; /* terminate the string */
799
800 if ((*t->justify_text) (hor)) {
801 if (on_page(x, y))
802 (*t->put_text) (x, y, text);
803 } else {
804 int len = estimate_strlen(text);
805 int hfix, vfix;
806
807 if (angle == 0) {
808 hfix = hor * t->h_char * len / 2;
809 vfix = 0;
810 } else {
811 /* Attention: This relies on the numeric values of enum JUSTIFY! */
812 hfix = hor * t->h_char * len * cos(angle * DEG2RAD) / 2 + 0.5;
813 vfix = hor * t->v_char * len * sin(angle * DEG2RAD) / 2 + 0.5;
814 }
815 if (on_page(x - hfix, y - vfix))
816 (*t->put_text) (x - hfix, y - vfix, text);
817 }
818 if (angle == 90 || angle == TEXT_VERTICAL)
819 x += t->v_char;
820 else if (angle == -90 || angle == -TEXT_VERTICAL)
821 x -= t->v_char;
822 else
823 y -= t->v_char;
824
825 if (!p)
826 break;
827 else {
828 /* put it back */
829 *p = '\n';
830 }
831
832 text = p + 1;
833 } /* unconditional branch back to the for(;;) - just a goto ! */
834
835 if (font && *font)
836 (*t->set_font) ("");
837
838 }
839
840
841 static void
do_point(unsigned int x,unsigned int y,int number)842 do_point(unsigned int x, unsigned int y, int number)
843 {
844 int htic, vtic;
845 struct termentry *t = term;
846
847 /* use solid lines for point symbols */
848 if (term->dashtype != null_dashtype)
849 term->dashtype(DASHTYPE_SOLID, NULL);
850
851 if (number < 0) { /* do dot */
852 (*t->move) (x, y);
853 (*t->vector) (x, y);
854 return;
855 }
856 number %= POINT_TYPES;
857 /* should be in term_tbl[] in later version */
858 htic = (term_pointsize * t->h_tic / 2);
859 vtic = (term_pointsize * t->v_tic / 2);
860
861 /* point types 1..4 are same as in postscript, png and x11
862 point types 5..6 are "similar"
863 (note that (number) equals (pointtype-1)
864 */
865 switch (number) {
866 case 4: /* do diamond */
867 (*t->move) (x - htic, y);
868 (*t->vector) (x, y - vtic);
869 (*t->vector) (x + htic, y);
870 (*t->vector) (x, y + vtic);
871 (*t->vector) (x - htic, y);
872 (*t->move) (x, y);
873 (*t->vector) (x, y);
874 break;
875 case 0: /* do plus */
876 (*t->move) (x - htic, y);
877 (*t->vector) (x - htic, y);
878 (*t->vector) (x + htic, y);
879 (*t->move) (x, y - vtic);
880 (*t->vector) (x, y - vtic);
881 (*t->vector) (x, y + vtic);
882 break;
883 case 3: /* do box */
884 (*t->move) (x - htic, y - vtic);
885 (*t->vector) (x - htic, y - vtic);
886 (*t->vector) (x + htic, y - vtic);
887 (*t->vector) (x + htic, y + vtic);
888 (*t->vector) (x - htic, y + vtic);
889 (*t->vector) (x - htic, y - vtic);
890 (*t->move) (x, y);
891 (*t->vector) (x, y);
892 break;
893 case 1: /* do X */
894 (*t->move) (x - htic, y - vtic);
895 (*t->vector) (x - htic, y - vtic);
896 (*t->vector) (x + htic, y + vtic);
897 (*t->move) (x - htic, y + vtic);
898 (*t->vector) (x - htic, y + vtic);
899 (*t->vector) (x + htic, y - vtic);
900 break;
901 case 5: /* do triangle */
902 (*t->move) (x, y + (4 * vtic / 3));
903 (*t->vector) (x - (4 * htic / 3), y - (2 * vtic / 3));
904 (*t->vector) (x + (4 * htic / 3), y - (2 * vtic / 3));
905 (*t->vector) (x, y + (4 * vtic / 3));
906 (*t->move) (x, y);
907 (*t->vector) (x, y);
908 break;
909 case 2: /* do star */
910 (*t->move) (x - htic, y);
911 (*t->vector) (x - htic, y);
912 (*t->vector) (x + htic, y);
913 (*t->move) (x, y - vtic);
914 (*t->vector) (x, y - vtic);
915 (*t->vector) (x, y + vtic);
916 (*t->move) (x - htic, y - vtic);
917 (*t->vector) (x - htic, y - vtic);
918 (*t->vector) (x + htic, y + vtic);
919 (*t->move) (x - htic, y + vtic);
920 (*t->vector) (x - htic, y + vtic);
921 (*t->vector) (x + htic, y - vtic);
922 break;
923 }
924 }
925
926 static void
do_pointsize(double size)927 do_pointsize(double size)
928 {
929 term_pointsize = (size >= 0 ? size : 1);
930 }
931
932
933 /*
934 * general point routine
935 */
936 static void
line_and_point(unsigned int x,unsigned int y,int number)937 line_and_point(unsigned int x, unsigned int y, int number)
938 {
939 /* temporary(?) kludge to allow terminals with bad linetypes
940 to make nice marks */
941
942 (*term->linetype) (NICE_LINE);
943 do_point(x, y, number);
944 }
945
946 /*
947 * general arrow routine
948 *
949 * I set the angle between the arrowhead and the line 15 degree.
950 * The length of arrowhead varies depending on the line length
951 * within the the range [0.3*(the-tic-length), 2*(the-tic-length)].
952 * No head is printed if the arrow length is zero.
953 *
954 * Yasu-hiro Yamazaki(hiro@rainbow.physics.utoronto.ca)
955 * Jul 1, 1993
956 */
957
958 #define COS15 (0.96593) /* cos of 15 degree */
959 #define SIN15 (0.25882) /* sin of 15 degree */
960
961 #define HEAD_LONG_LIMIT (2.0) /* long limit of arrowhead length */
962 #define HEAD_SHORT_LIMIT (0.3) /* short limit of arrowhead length */
963 /* their units are the "tic" length */
964
965 #define HEAD_COEFF (0.3) /* default value of head/line length ratio */
966
967 int curr_arrow_headlength; /* access head length + angle without changing API */
968 double curr_arrow_headangle; /* angle in degrees */
969 double curr_arrow_headbackangle; /* angle in degrees */
970 arrowheadfill curr_arrow_headfilled; /* arrow head filled or not */
971 TBOOLEAN curr_arrow_headfixedsize; /* Adapt the head size for short arrows or not */
972
973 static void
do_arrow(unsigned int usx,unsigned int usy,unsigned int uex,unsigned int uey,int headstyle)974 do_arrow(
975 unsigned int usx, unsigned int usy, /* start point */
976 unsigned int uex, unsigned int uey, /* end point (point of arrowhead) */
977 int headstyle)
978 {
979 /* Clipping and angle calculations do not work if coords are unsigned! */
980 int sx = (int)usx;
981 int sy = (int)usy;
982 int ex = (int)uex;
983 int ey = (int)uey;
984
985 struct termentry *t = term;
986 float len_tic = ((double) (t->h_tic + t->v_tic)) / 2.0;
987 /* average of tic sizes */
988 /* (dx,dy) : vector from end to start */
989 double dx = sx - ex;
990 double dy = sy - ey;
991 double len_arrow = sqrt(dx * dx + dy * dy);
992 gpiPoint head_points[5];
993 int xm = 0, ym = 0;
994 BoundingBox *clip_save;
995 t_arrow_head head = (t_arrow_head)((headstyle < 0) ? -headstyle : headstyle);
996 /* negative headstyle means draw heads only, no shaft */
997
998 /* The arrow shaft was clipped already in do_clip_arrow() but we still */
999 /* need to clip the head here. */
1000 clip_save = clip_area;
1001 if (term->flags & TERM_CAN_CLIP)
1002 clip_area = NULL;
1003 else
1004 clip_area = &canvas;
1005
1006 /* Calculate and draw arrow heads.
1007 * Draw no head for arrows with length = 0, or, to be more specific,
1008 * length < DBL_EPSILON, because len_arrow will almost always be != 0.
1009 */
1010 if ((head != NOHEAD) && fabs(len_arrow) >= DBL_EPSILON) {
1011 int x1, y1, x2, y2;
1012 if (curr_arrow_headlength <= 0) {
1013 /* An arrow head with the default size and angles */
1014 double coeff_shortest = len_tic * HEAD_SHORT_LIMIT / len_arrow;
1015 double coeff_longest = len_tic * HEAD_LONG_LIMIT / len_arrow;
1016 double head_coeff = GPMAX(coeff_shortest,
1017 GPMIN(HEAD_COEFF, coeff_longest));
1018 /* we put the arrowhead marks at 15 degrees to line */
1019 x1 = (int) ((COS15 * dx - SIN15 * dy) * head_coeff);
1020 y1 = (int) ((SIN15 * dx + COS15 * dy) * head_coeff);
1021 x2 = (int) ((COS15 * dx + SIN15 * dy) * head_coeff);
1022 y2 = (int) ((-SIN15 * dx + COS15 * dy) * head_coeff);
1023 /* backangle defaults to 90 deg */
1024 xm = (int) ((x1 + x2)/2);
1025 ym = (int) ((y1 + y2)/2);
1026 } else {
1027 /* An arrow head with the length + angle specified explicitly. */
1028 /* Assume that if the arrow is shorter than the arrowhead, this is */
1029 /* because of foreshortening in a 3D plot. */
1030 double alpha = curr_arrow_headangle * DEG2RAD;
1031 double beta = curr_arrow_headbackangle * DEG2RAD;
1032 double phi = atan2(-dy,-dx); /* azimuthal angle of the vector */
1033 double backlen, effective_length;
1034 double dx2, dy2;
1035
1036 effective_length = curr_arrow_headlength;
1037 if (!curr_arrow_headfixedsize && (curr_arrow_headlength > len_arrow/2.)) {
1038 effective_length = len_arrow/2.;
1039 alpha = atan(tan(alpha)*((double)curr_arrow_headlength/effective_length));
1040 beta = atan(tan(beta)*((double)curr_arrow_headlength/effective_length));
1041 }
1042 backlen = sin(alpha) / sin(beta);
1043
1044 /* anticlock-wise head segment */
1045 x1 = -(int)(effective_length * cos( alpha - phi ));
1046 y1 = (int)(effective_length * sin( alpha - phi ));
1047 /* clock-wise head segment */
1048 dx2 = -effective_length * cos( phi + alpha );
1049 dy2 = -effective_length * sin( phi + alpha );
1050 x2 = (int) (dx2);
1051 y2 = (int) (dy2);
1052 /* back point */
1053 xm = (int) (dx2 + backlen*effective_length * cos( phi + beta ));
1054 ym = (int) (dy2 + backlen*effective_length * sin( phi + beta ));
1055 }
1056
1057 if ((head & END_HEAD) && !clip_point(ex, ey)) {
1058 head_points[0].x = ex + xm;
1059 head_points[0].y = ey + ym;
1060 head_points[1].x = ex + x1;
1061 head_points[1].y = ey + y1;
1062 head_points[2].x = ex;
1063 head_points[2].y = ey;
1064 head_points[3].x = ex + x2;
1065 head_points[3].y = ey + y2;
1066 head_points[4].x = ex + xm;
1067 head_points[4].y = ey + ym;
1068 if (curr_arrow_headfilled >= AS_FILLED) {
1069 /* draw filled forward arrow head */
1070 head_points->style = FS_OPAQUE;
1071 if (t->filled_polygon)
1072 (*t->filled_polygon) (5, head_points);
1073 }
1074 /* draw outline of forward arrow head */
1075 if (curr_arrow_headfilled == AS_NOFILL) {
1076 draw_clip_polygon(3, head_points+1);
1077 } else if (curr_arrow_headfilled != AS_NOBORDER) {
1078 draw_clip_polygon(5, head_points);
1079 }
1080 }
1081
1082 /* backward arrow head */
1083 if ((head & BACKHEAD) && !clip_point(sx,sy)) {
1084 head_points[0].x = sx - xm;
1085 head_points[0].y = sy - ym;
1086 head_points[1].x = sx - x1;
1087 head_points[1].y = sy - y1;
1088 head_points[2].x = sx;
1089 head_points[2].y = sy;
1090 head_points[3].x = sx - x2;
1091 head_points[3].y = sy - y2;
1092 head_points[4].x = sx - xm;
1093 head_points[4].y = sy - ym;
1094 if (curr_arrow_headfilled >= AS_FILLED) {
1095 /* draw filled backward arrow head */
1096 head_points->style = FS_OPAQUE;
1097 if (t->filled_polygon)
1098 (*t->filled_polygon) (5, head_points);
1099 }
1100 /* draw outline of backward arrow head */
1101 if (curr_arrow_headfilled == AS_NOFILL) {
1102 draw_clip_polygon(3, head_points+1);
1103 } else if (curr_arrow_headfilled != AS_NOBORDER) {
1104 draw_clip_polygon(5, head_points);
1105 }
1106 }
1107 }
1108
1109 /* Draw the line for the arrow. */
1110 if (headstyle >= 0) {
1111 if ((head & BACKHEAD)
1112 && (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
1113 sx -= xm;
1114 sy -= ym;
1115 }
1116 if ((head & END_HEAD)
1117 && (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
1118 ex += xm;
1119 ey += ym;
1120 }
1121 draw_clip_line(sx, sy, ex, ey);
1122 }
1123
1124 /* Restore previous clipping box */
1125 clip_area = clip_save;
1126
1127 }
1128
1129 #ifdef EAM_OBJECTS
1130 /* Generic routine for drawing circles or circular arcs. */
1131 /* If this feature proves useful, we can add a new terminal entry */
1132 /* point term->arc() to the API and let terminals either provide */
1133 /* a private implemenation or use this generic one. */
1134
1135 void
do_arc(int cx,int cy,double radius,double arc_start,double arc_end,int style,TBOOLEAN wedge)1136 do_arc(
1137 int cx, int cy, /* Center */
1138 double radius, /* Radius */
1139 double arc_start, double arc_end, /* Limits of arc in degress */
1140 int style, TBOOLEAN wedge)
1141 {
1142 gpiPoint vertex[250];
1143 int i, segments;
1144 double aspect;
1145 TBOOLEAN complete_circle;
1146
1147 /* Protect against out-of-range values */
1148 while (arc_start < 0)
1149 arc_start += 360.;
1150 while (arc_end > 360.)
1151 arc_end -= 360.;
1152
1153 /* Always draw counterclockwise */
1154 while (arc_end < arc_start)
1155 arc_end += 360.;
1156
1157 /* Choose how finely to divide this arc into segments */
1158 /* FIXME: INC=2 causes problems for gnuplot_x11 */
1159 # define INC 3.
1160 segments = (arc_end - arc_start) / INC;
1161 if (segments < 1)
1162 segments = 1;
1163
1164 /* Calculate the vertices */
1165 aspect = (double)term->v_tic / (double)term->h_tic;
1166 for (i=0; i<segments; i++) {
1167 vertex[i].x = cx + cos(DEG2RAD * (arc_start + i*INC)) * radius;
1168 vertex[i].y = cy + sin(DEG2RAD * (arc_start + i*INC)) * radius * aspect;
1169 }
1170 # undef INC
1171 vertex[segments].x = cx + cos(DEG2RAD * arc_end) * radius;
1172 vertex[segments].y = cy + sin(DEG2RAD * arc_end) * radius * aspect;
1173
1174 if (fabs(arc_end - arc_start) > .1
1175 && fabs(arc_end - arc_start) < 359.9) {
1176 vertex[++segments].x = cx;
1177 vertex[segments].y = cy;
1178 vertex[++segments].x = vertex[0].x;
1179 vertex[segments].y = vertex[0].y;
1180 complete_circle = FALSE;
1181 } else
1182 complete_circle = TRUE;
1183
1184 if (style) { /* Fill in the center */
1185 gpiPoint fillarea[250];
1186 int in;
1187
1188 clip_polygon(vertex, fillarea, segments, &in);
1189 fillarea[0].style = style;
1190 if (term->filled_polygon)
1191 term->filled_polygon(in, fillarea);
1192
1193 } else { /* Draw the arc */
1194 if (!wedge && !complete_circle)
1195 segments -= 2;
1196 draw_clip_polygon(segments+1, vertex);
1197 }
1198 }
1199 #endif /* EAM_OBJECTS */
1200
1201
1202
1203 #define TERM_PROTO
1204 #define TERM_BODY
1205 #define TERM_PUBLIC static
1206
1207 #include "term.h"
1208
1209 #undef TERM_PROTO
1210 #undef TERM_BODY
1211 #undef TERM_PUBLIC
1212
1213
1214 /* Dummy functions for unavailable features */
1215 /* return success if they asked for default - this simplifies code
1216 * where param is passed as a param. Client can first pass it here,
1217 * and only if it fails do they have to see what was trying to be done
1218 */
1219
1220 /* change angle of text. 0 is horizontal left to right.
1221 * 1 is vertical bottom to top (90 deg rotate)
1222 */
1223 static int
null_text_angle(int ang)1224 null_text_angle(int ang)
1225 {
1226 return (ang == 0);
1227 }
1228
1229 /* change justification of text.
1230 * modes are LEFT (flush left), CENTRE (centred), RIGHT (flush right)
1231 */
1232 static int
null_justify_text(enum JUSTIFY just)1233 null_justify_text(enum JUSTIFY just)
1234 {
1235 return (just == LEFT);
1236 }
1237
1238
1239 /*
1240 * Deprecated terminal function (pre-version 3)
1241 */
1242 static int
null_scale(double x,double y)1243 null_scale(double x, double y)
1244 {
1245 (void) x; /* avoid -Wunused warning */
1246 (void) y;
1247 int_error(NO_CARET, "Attempt to call deprecated terminal function");
1248 return FALSE; /* can't be done */
1249 }
1250
1251 static void
null_layer(t_termlayer layer)1252 null_layer(t_termlayer layer)
1253 {
1254 (void) layer; /* avoid -Wunused warning */
1255 }
1256
1257 static void
options_null()1258 options_null()
1259 {
1260 term_options[0] = '\0'; /* we have no options */
1261 }
1262
1263 static void
graphics_null()1264 graphics_null()
1265 {
1266 fprintf(stderr,
1267 "WARNING: Plotting with an 'unknown' terminal.\n"
1268 "No output will be generated. Please select a terminal with 'set terminal'.\n");
1269 }
1270
1271 static void
UNKNOWN_null()1272 UNKNOWN_null()
1273 {
1274 }
1275
1276 static void
MOVE_null(unsigned int x,unsigned int y)1277 MOVE_null(unsigned int x, unsigned int y)
1278 {
1279 (void) x; /* avoid -Wunused warning */
1280 (void) y;
1281 }
1282
1283 static void
LINETYPE_null(int t)1284 LINETYPE_null(int t)
1285 {
1286 (void) t; /* avoid -Wunused warning */
1287 }
1288
1289 static void
PUTTEXT_null(unsigned int x,unsigned int y,const char * s)1290 PUTTEXT_null(unsigned int x, unsigned int y, const char *s)
1291 {
1292 (void) s; /* avoid -Wunused warning */
1293 (void) x;
1294 (void) y;
1295 }
1296
1297
1298 static void
null_linewidth(double s)1299 null_linewidth(double s)
1300 {
1301 (void) s; /* avoid -Wunused warning */
1302 }
1303
1304 static int
null_set_font(const char * font)1305 null_set_font(const char *font)
1306 {
1307 (void) font; /* avoid -Wunused warning */
1308 return FALSE; /* Never used!! */
1309 }
1310
1311 static void
null_set_color(struct t_colorspec * colorspec)1312 null_set_color(struct t_colorspec *colorspec)
1313 {
1314 if (colorspec->type == TC_LT)
1315 term->linetype(colorspec->lt);
1316 }
1317
1318 static void
null_dashtype(int type,t_dashtype * custom_dash_pattern)1319 null_dashtype(int type, t_dashtype *custom_dash_pattern)
1320 {
1321 (void) custom_dash_pattern; /* ignore */
1322 /*
1323 * If the terminal does not support user-defined dashtypes all we can do
1324 * do is fall through to the old (pre-v5) assumption that the dashtype,
1325 * if any, is part of the linetype. We also assume that the color will
1326 * be adjusted after this.
1327 */
1328 if (type <= 0)
1329 type = LT_SOLID;
1330 term->linetype(type);
1331 }
1332
1333 /* setup the magic macros to compile in the right parts of the
1334 * terminal drivers included by term.h
1335 */
1336 #define TERM_TABLE
1337 #define TERM_TABLE_START(x) ,{
1338 #define TERM_TABLE_END(x) }
1339
1340
1341 /*
1342 * term_tbl[] contains an entry for each terminal. "unknown" must be the
1343 * first, since term is initialized to 0.
1344 */
1345 static struct termentry term_tbl[] =
1346 {
1347 {"unknown", "Unknown terminal type - not a plotting device",
1348 100, 100, 1, 1,
1349 1, 1, options_null, UNKNOWN_null, UNKNOWN_null,
1350 UNKNOWN_null, null_scale, graphics_null, MOVE_null, MOVE_null,
1351 LINETYPE_null, PUTTEXT_null}
1352
1353 #include "term.h"
1354
1355 };
1356
1357 #define TERMCOUNT (sizeof(term_tbl) / sizeof(term_tbl[0]))
1358
1359 void
list_terms()1360 list_terms()
1361 {
1362 int i;
1363 char *line_buffer = gp_alloc(BUFSIZ, "list_terms");
1364 int sort_idxs[TERMCOUNT];
1365
1366 /* sort terminal types alphabetically */
1367 for( i = 0; i < TERMCOUNT; i++ )
1368 sort_idxs[i] = i;
1369 qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
1370 /* now sort_idxs[] contains the sorted indices */
1371
1372 StartOutput();
1373 strcpy(line_buffer, "\nAvailable terminal types:\n");
1374 OutLine(line_buffer);
1375
1376 for (i = 0; i < TERMCOUNT; i++) {
1377 sprintf(line_buffer, " %15s %s\n",
1378 term_tbl[sort_idxs[i]].name,
1379 term_tbl[sort_idxs[i]].description);
1380 OutLine(line_buffer);
1381 }
1382
1383 EndOutput();
1384 free(line_buffer);
1385 }
1386
1387 /* Return string with all terminal names.
1388 Note: caller must free the returned names after use.
1389 */
1390 char*
get_terminals_names()1391 get_terminals_names()
1392 {
1393 int i;
1394 char *buf = gp_alloc(TERMCOUNT*15, "all_term_names"); /* max 15 chars per name */
1395 char *names;
1396 int sort_idxs[TERMCOUNT];
1397
1398 /* sort terminal types alphabetically */
1399 for( i = 0; i < TERMCOUNT; i++ )
1400 sort_idxs[i] = i;
1401 qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
1402 /* now sort_idxs[] contains the sorted indices */
1403
1404 strcpy(buf, " "); /* let the string have leading and trailing " " in order to search via strstrt(GPVAL_TERMINALS, " png "); */
1405 for (i = 0; i < TERMCOUNT; i++)
1406 sprintf(buf+strlen(buf), "%s ", term_tbl[sort_idxs[i]].name);
1407 names = gp_alloc(strlen(buf)+1, "all_term_names2");
1408 strcpy(names, buf);
1409 free(buf);
1410 return names;
1411 }
1412
1413 static int
termcomp(const generic * arga,const generic * argb)1414 termcomp(const generic *arga, const generic *argb)
1415 {
1416 const int *a = arga;
1417 const int *b = argb;
1418
1419 return( strcasecmp( term_tbl[*a].name, term_tbl[*b].name ) );
1420 }
1421
1422 /* set_term: get terminal number from name on command line
1423 * will change 'term' variable if successful
1424 */
1425 struct termentry *
set_term()1426 set_term()
1427 {
1428 struct termentry *t = NULL;
1429 char *input_name = NULL;
1430
1431 if (!END_OF_COMMAND) {
1432 input_name = gp_input_line + token[c_token].start_index;
1433 t = change_term(input_name, token[c_token].length);
1434 if (!t && isstringvalue(c_token) && (input_name = try_to_get_string())) {
1435 if (strchr(input_name, ' '))
1436 *strchr(input_name, ' ') = '\0';
1437 t = change_term(input_name, strlen(input_name));
1438 free(input_name);
1439 } else {
1440 c_token++;
1441 }
1442 }
1443
1444 if (!t) {
1445 change_term("unknown", 7);
1446 int_error(c_token-1, "unknown or ambiguous terminal type; type just 'set terminal' for a list");
1447 }
1448
1449 /* otherwise the type was changed */
1450 return (t);
1451 }
1452
1453 /* change_term: get terminal number from name and set terminal type
1454 *
1455 * returns NULL for unknown or ambiguous, otherwise is terminal
1456 * driver pointer
1457 */
1458 struct termentry *
change_term(const char * origname,int length)1459 change_term(const char *origname, int length)
1460 {
1461 int i;
1462 struct termentry *t = NULL;
1463 TBOOLEAN ambiguous = FALSE;
1464
1465 /* For backwards compatibility only */
1466 char *name = (char *)origname;
1467 if (!strncmp(origname,"X11",length)) {
1468 name = "x11";
1469 length = 3;
1470 }
1471
1472 #ifdef HAVE_CAIROPDF
1473 /* To allow "set term eps" as short for "set term epscairo" */
1474 if (!strncmp(origname,"eps",length)) {
1475 name = "epscairo";
1476 length = 8;
1477 }
1478 #endif
1479
1480 for (i = 0; i < TERMCOUNT; i++) {
1481 if (!strncmp(name, term_tbl[i].name, length)) {
1482 if (t)
1483 ambiguous = TRUE;
1484 t = term_tbl + i;
1485 /* Exact match is always accepted */
1486 if (length == strlen(term_tbl[i].name)) {
1487 ambiguous = FALSE;
1488 break;
1489 }
1490 }
1491 }
1492
1493 if (!t || ambiguous)
1494 return (NULL);
1495
1496 /* Success: set terminal type now */
1497
1498 term = t;
1499 term_initialised = FALSE;
1500
1501 /* check that optional fields are initialised to something */
1502 if (term->text_angle == 0)
1503 term->text_angle = null_text_angle;
1504 if (term->justify_text == 0)
1505 term->justify_text = null_justify_text;
1506 if (term->point == 0)
1507 term->point = do_point;
1508 if (term->arrow == 0)
1509 term->arrow = do_arrow;
1510 if (term->pointsize == 0)
1511 term->pointsize = do_pointsize;
1512 if (term->linewidth == 0)
1513 term->linewidth = null_linewidth;
1514 if (term->layer == 0)
1515 term->layer = null_layer;
1516 if (term->tscale <= 0)
1517 term->tscale = 1.0;
1518 if (term->set_font == 0)
1519 term->set_font = null_set_font;
1520 if (term->set_color == 0) {
1521 term->set_color = null_set_color;
1522 term->flags |= TERM_NULL_SET_COLOR;
1523 }
1524 if (term->dashtype == 0)
1525 term->dashtype = null_dashtype;
1526
1527 if (interactive)
1528 fprintf(stderr, "\nTerminal type is now '%s'\n", term->name);
1529
1530 /* Invalidate any terminal-specific structures that may be active */
1531 invalidate_palette();
1532
1533 return (t);
1534 }
1535
1536 /*
1537 * Find an appropriate initial terminal type.
1538 * The environment variable GNUTERM is checked first; if that does
1539 * not exist, then the terminal hardware is checked, if possible,
1540 * and finally, we can check $TERM for some kinds of terminals.
1541 * A default can be set with -DDEFAULTTERM=myterm in the Makefile
1542 * or #define DEFAULTTERM myterm in term.h
1543 */
1544 /* thanks to osupyr!alden (Dave Alden) for the original GNUTERM code */
1545 void
init_terminal()1546 init_terminal()
1547 {
1548 char *term_name = DEFAULTTERM;
1549 #if (defined(MSDOS) && !defined(_WIN32)) || defined(SUN) || defined(X11)
1550 char *env_term = NULL; /* from TERM environment var */
1551 #endif
1552 #ifdef X11
1553 char *display = NULL;
1554 #endif
1555 char *gnuterm = NULL;
1556
1557 /* GNUTERM environment variable is primary */
1558 gnuterm = getenv("GNUTERM");
1559 if (gnuterm != (char *) NULL) {
1560 /* April 2017 - allow GNUTERM to include terminal options */
1561 char *set_term = "set term ";
1562 char *set_term_command = gp_alloc(strlen(set_term) + strlen(gnuterm) + 4, NULL);
1563 strcpy(set_term_command, set_term);
1564 strcat(set_term_command, gnuterm);
1565 do_string(set_term_command);
1566 free(set_term_command);
1567 /* replicate environmental variable GNUTERM for internal use */
1568 Gstring(&(add_udv_by_name("GNUTERM")->udv_value), gp_strdup(gnuterm));
1569 return;
1570
1571 } else {
1572
1573 #ifdef VMS
1574 term_name = vms_init();
1575 #endif /* VMS */
1576
1577 if (term_name == (char *) NULL
1578 && getenv ("DOMTERM") != NULL)
1579 term_name = "domterm";
1580
1581 #ifdef __BEOS__
1582 env_term = getenv("TERM");
1583 if (term_name == (char *) NULL
1584 && env_term != (char *) NULL && strcmp(env_term, "beterm") == 0)
1585 term_name = "be";
1586 #endif /* BeOS */
1587
1588 #if defined(WXWIDGETS) && defined(_WIN32)
1589 if (term_name == (char *) NULL)
1590 term_name = "wxt";
1591 #endif
1592
1593 #ifdef QTTERM
1594 if (term_name == (char *) NULL)
1595 term_name = "qt";
1596 #endif
1597
1598 #ifdef WXWIDGETS
1599 if (term_name == (char *) NULL)
1600 term_name = "wxt";
1601 #endif
1602
1603 #ifdef _WIN32
1604 if (term_name == (char *) NULL)
1605 term_name = "win";
1606 #endif /* _WIN32 */
1607
1608 #if defined(__APPLE__) && defined(__MACH__) && defined(HAVE_FRAMEWORK_AQUATERM)
1609 /* Mac OS X with AquaTerm installed */
1610 term_name = "aqua";
1611 #endif
1612
1613 #ifdef X11
1614 env_term = getenv("TERM"); /* try $TERM */
1615 if (term_name == (char *) NULL
1616 && env_term != (char *) NULL && strcmp(env_term, "xterm") == 0)
1617 term_name = "x11";
1618 display = getenv("DISPLAY");
1619 if (term_name == (char *) NULL && display != (char *) NULL)
1620 term_name = "x11";
1621 if (X11_Display)
1622 term_name = "x11";
1623 #endif /* x11 */
1624
1625 #ifdef DJGPP
1626 term_name = "svga";
1627 #endif
1628
1629 #ifdef GRASS
1630 term_name = "grass";
1631 #endif
1632
1633 #ifdef OS2
1634 /* amai: Note that we do some checks above and now overwrite any
1635 results. Perhaps we may disable checks above!? */
1636 #ifdef X11
1637 /* WINDOWID is set in sessions like xterm, etc.
1638 DISPLAY is also mandatory. */
1639 env_term = getenv("WINDOWID");
1640 display = getenv("DISPLAY");
1641 if ((env_term != (char *) NULL) && (display != (char *) NULL))
1642 term_name = "x11";
1643 else
1644 #endif /* X11 */
1645 term_name = "pm";
1646 #endif /*OS2 */
1647
1648 /* set linux terminal only if LINUX_setup was successfull, if we are on X11
1649 LINUX_setup has failed, also if we are logged in by network */
1650 #ifdef LINUXVGA
1651 if (LINUX_graphics_allowed)
1652 #if defined(VGAGL) && defined (THREEDKIT)
1653 term_name = "vgagl";
1654 #else
1655 term_name = "linux";
1656 #endif
1657 #endif /* LINUXVGA */
1658 }
1659
1660 /* We have a name, try to set term type */
1661 if (term_name != NULL && *term_name != '\0') {
1662 int namelength = strlen(term_name);
1663 struct udvt_entry *name = add_udv_by_name("GNUTERM");
1664
1665 Gstring(&name->udv_value, gp_strdup(term_name));
1666
1667 if (strchr(term_name,' '))
1668 namelength = strchr(term_name,' ') - term_name;
1669
1670 /* Force the terminal to initialize default fonts, etc. This prevents */
1671 /* segfaults and other strangeness if you set GNUTERM to "post" or */
1672 /* "png" for example. However, calling X11_options() is expensive due */
1673 /* to the fork+execute of gnuplot_x11 and x11 can tolerate not being */
1674 /* initialized until later. */
1675 /* Note that gp_input_line[] is blank at this point. */
1676 if (change_term(term_name, namelength)) {
1677 if (strcmp(term->name,"x11"))
1678 term->options();
1679 return;
1680 }
1681 fprintf(stderr, "Unknown or ambiguous terminal name '%s'\n", term_name);
1682 }
1683 change_term("unknown", 7);
1684 }
1685
1686
1687 /* test terminal by drawing border and text */
1688 /* called from command test */
1689 void
test_term()1690 test_term()
1691 {
1692 struct termentry *t = term;
1693 const char *str;
1694 int x, y, xl, yl, i;
1695 int xmax_t, ymax_t, x0, y0;
1696 char label[MAX_ID_LEN];
1697 int key_entry_height;
1698 int p_width;
1699 TBOOLEAN already_in_enhanced_text_mode;
1700 static t_colorspec black = BLACK_COLORSPEC;
1701
1702 already_in_enhanced_text_mode = t->flags & TERM_ENHANCED_TEXT;
1703 if (!already_in_enhanced_text_mode)
1704 do_string("set termopt enh");
1705
1706 term_start_plot();
1707 screen_ok = FALSE;
1708 xmax_t = (t->xmax * xsize);
1709 ymax_t = (t->ymax * ysize);
1710 x0 = (xoffset * t->xmax);
1711 y0 = (yoffset * t->ymax);
1712
1713 p_width = pointsize * t->h_tic;
1714 key_entry_height = pointsize * t->v_tic * 1.25;
1715 if (key_entry_height < t->v_char)
1716 key_entry_height = t->v_char;
1717
1718 /* Sync point for epslatex text positioning */
1719 (*t->layer)(TERM_LAYER_FRONTTEXT);
1720
1721 /* border linetype */
1722 (*t->linewidth) (1.0);
1723 (*t->linetype) (LT_BLACK);
1724 newpath();
1725 (*t->move) (x0, y0);
1726 (*t->vector) (x0 + xmax_t - 1, y0);
1727 (*t->vector) (x0 + xmax_t - 1, y0 + ymax_t - 1);
1728 (*t->vector) (x0, y0 + ymax_t - 1);
1729 (*t->vector) (x0, y0);
1730 closepath();
1731
1732 /* Echo back the current terminal type */
1733 if (!strcmp(term->name,"unknown"))
1734 int_error(NO_CARET, "terminal type is unknown");
1735 else {
1736 char tbuf[64];
1737 (void) (*t->justify_text) (LEFT);
1738 sprintf(tbuf,"%s terminal test", term->name);
1739 (*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t - t->v_char, tbuf);
1740 sprintf(tbuf, "gnuplot version %s.%s ", gnuplot_version, gnuplot_patchlevel);
1741 (*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t - t->v_char * 2.25, tbuf);
1742 }
1743
1744 (*t->linetype) (LT_AXIS);
1745 (*t->move) (x0 + xmax_t / 2, y0);
1746 (*t->vector) (x0 + xmax_t / 2, y0 + ymax_t - 1);
1747 (*t->move) (x0, y0 + ymax_t / 2);
1748 (*t->vector) (x0 + xmax_t - 1, y0 + ymax_t / 2);
1749
1750 /* How well can we estimate width and height of characters?
1751 * Textbox fill shows true size, surrounding box shows the generic estimate
1752 * used to reserve space during plot layout.
1753 */
1754 #ifdef EAM_BOXED_TEXT
1755 if (TRUE) {
1756 struct text_label sample = EMPTY_LABELSTRUCT;
1757 struct textbox_style save_opts = textbox_opts;
1758 sample.text = "12345678901234567890";
1759 sample.pos = CENTRE;
1760 sample.boxed = 1;
1761 textbox_opts.opaque = TRUE;
1762 textbox_opts.noborder = TRUE;
1763 textbox_opts.fillcolor.type = TC_RGB;
1764 textbox_opts.fillcolor.lt = 0xccccee;
1765
1766 (*t->linetype) (LT_SOLID);
1767 write_label(xmax_t/2, ymax_t/2, &sample);
1768 textbox_opts = save_opts;
1769
1770 sample.boxed = 0;
1771 sample.text = "true vs. estimated text dimensions";
1772 write_label(xmax_t/2, ymax_t/2 + 1.5 * t->v_char, &sample);
1773
1774 newpath();
1775 (*t->move) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
1776 (*t->vector) (x0 + xmax_t / 2 + t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
1777 (*t->vector) (x0 + xmax_t / 2 + t->h_char * 10, y0 + ymax_t / 2 - t->v_char / 2);
1778 (*t->vector) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 - t->v_char / 2);
1779 (*t->vector) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
1780 closepath();
1781 }
1782 #endif
1783
1784 /* Test for enhanced text */
1785 (*t->linetype) (LT_BLACK);
1786 if (t->flags & TERM_ENHANCED_TEXT) {
1787 char *tmptext1 = "Enhanced text: {x@_{0}^{n+1}}";
1788 char *tmptext2 = "&{Enhanced text: }{/:Bold Bold}{/:Italic Italic}";
1789 (*t->put_text) (x0 + xmax_t * 0.5, y0 + ymax_t * 0.40, tmptext1);
1790 (*t->put_text) (x0 + xmax_t * 0.5, y0 + ymax_t * 0.35, tmptext2);
1791 (*t->set_font)("");
1792 if (!already_in_enhanced_text_mode)
1793 do_string("set termopt noenh");
1794 }
1795
1796 /* test justification */
1797 (void) (*t->justify_text) (LEFT);
1798 (*t->put_text) (x0 + xmax_t / 2, y0 + ymax_t / 2 + t->v_char * 6, "left justified");
1799 str = "centre+d text";
1800 if ((*t->justify_text) (CENTRE))
1801 (*t->put_text) (x0 + xmax_t / 2,
1802 y0 + ymax_t / 2 + t->v_char * 5, str);
1803 else
1804 (*t->put_text) (x0 + xmax_t / 2 - strlen(str) * t->h_char / 2,
1805 y0 + ymax_t / 2 + t->v_char * 5, str);
1806 str = "right justified";
1807 if ((*t->justify_text) (RIGHT))
1808 (*t->put_text) (x0 + xmax_t / 2,
1809 y0 + ymax_t / 2 + t->v_char * 4, str);
1810 else
1811 (*t->put_text) (x0 + xmax_t / 2 - strlen(str) * t->h_char,
1812 y0 + ymax_t / 2 + t->v_char * 4, str);
1813
1814 /* test tic size */
1815 (*t->linetype)(2);
1816 (*t->move) ((unsigned int) (x0 + xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)), y0 + (unsigned int) ymax_t - 1);
1817 (*t->vector) ((unsigned int) (x0 + xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)),
1818 (unsigned int) (y0 + ymax_t - axis_array[FIRST_X_AXIS].ticscale * t->v_tic));
1819 (*t->move) ((unsigned int) (x0 + xmax_t / 2), y0 + (unsigned int) (ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
1820 (*t->vector) ((unsigned int) (x0 + xmax_t / 2 + axis_array[FIRST_X_AXIS].ticscale * t->h_tic),
1821 (unsigned int) (y0 + ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
1822 (void) (*t->justify_text) (RIGHT);
1823 (*t->put_text) (x0 + (unsigned int) (xmax_t / 2 - 1* t->h_char),
1824 y0 + (unsigned int) (ymax_t - t->v_char),
1825 "show ticscale");
1826 (void) (*t->justify_text) (LEFT);
1827 (*t->linetype)(LT_BLACK);
1828
1829 /* test line and point types */
1830 x = x0 + xmax_t - t->h_char * 7 - p_width;
1831 y = y0 + ymax_t - key_entry_height;
1832 (*t->pointsize) (pointsize);
1833 for (i = -2; y > y0 + key_entry_height; i++) {
1834 struct lp_style_type ls = DEFAULT_LP_STYLE_TYPE;
1835 ls.l_width = 1;
1836 load_linetype(&ls,i+1);
1837 term_apply_lp_properties(&ls);
1838
1839 (void) sprintf(label, "%d", i + 1);
1840 if ((*t->justify_text) (RIGHT))
1841 (*t->put_text) (x, y, label);
1842 else
1843 (*t->put_text) (x - strlen(label) * t->h_char, y, label);
1844 (*t->move) (x + t->h_char, y);
1845 (*t->vector) (x + t->h_char * 5, y);
1846 if (i >= -1)
1847 (*t->point) (x + t->h_char * 6 + p_width / 2, y, i);
1848 y -= key_entry_height;
1849 }
1850
1851 /* test arrows (should line up with rotated text) */
1852 (*t->linewidth) (1.0);
1853 (*t->linetype) (0);
1854 (*t->dashtype) (DASHTYPE_SOLID, NULL);
1855 x = x0 + 2. * t->v_char;
1856 y = y0 + ymax_t/2;
1857 xl = t->h_tic * 7;
1858 yl = t->v_tic * 7;
1859 i = curr_arrow_headfilled;
1860 curr_arrow_headfilled = AS_NOBORDER;
1861 (*t->arrow) (x, y-yl, x, y+yl, BOTH_HEADS);
1862 curr_arrow_headfilled = AS_EMPTY;
1863 (*t->arrow) (x, y, x + xl, y + yl, END_HEAD);
1864 curr_arrow_headfilled = AS_NOFILL;
1865 (*t->arrow) (x, y, x + xl, y - yl, END_HEAD);
1866 curr_arrow_headfilled = i;
1867
1868 /* test text angle (should match arrows) */
1869 (*t->linetype)(0);
1870 str = "rotated ce+ntred text";
1871 if ((*t->text_angle) (TEXT_VERTICAL)) {
1872 if ((*t->justify_text) (CENTRE))
1873 (*t->put_text) (x0 + t->v_char,
1874 y0 + ymax_t / 2, str);
1875 else
1876 (*t->put_text) (x0 + t->v_char,
1877 y0 + ymax_t / 2 - strlen(str) * t->h_char / 2, str);
1878 (*t->justify_text) (LEFT);
1879 str = " rotate by +45";
1880 (*t->text_angle)(45);
1881 (*t->put_text)(x0 + t->v_char * 3, y0 + ymax_t / 2, str);
1882 (*t->justify_text) (LEFT);
1883 str = " rotate by -45";
1884 (*t->text_angle)(-45);
1885 (*t->put_text)(x0 + t->v_char * 3, y0 + ymax_t / 2, str);
1886 } else {
1887 (void) (*t->justify_text) (LEFT);
1888 (*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t / 2, "cannot rotate text");
1889 }
1890 (void) (*t->justify_text) (LEFT);
1891 (void) (*t->text_angle) (0);
1892
1893 /* test line widths */
1894 (void) (*t->justify_text) (LEFT);
1895 xl = xmax_t / 10;
1896 yl = ymax_t / 25;
1897 x = x0 + xmax_t * .075;
1898 y = y0 + yl;
1899
1900 for (i=1; i<7; i++) {
1901 (*t->linewidth) ((float)(i)); (*t->linetype)(LT_BLACK);
1902 (*t->move) (x, y); (*t->vector) (x+xl, y);
1903 sprintf(label," lw %1d", i);
1904 (*t->put_text) (x+xl, y, label);
1905 y += yl;
1906 }
1907 (*t->put_text) (x, y, "linewidth");
1908
1909 /* test native dashtypes (_not_ the 'set mono' sequence) */
1910 (void) (*t->justify_text) (LEFT);
1911 xl = xmax_t / 10;
1912 yl = ymax_t / 25;
1913 x = x0 + xmax_t * .3;
1914 y = y0 + yl;
1915
1916 for (i=0; i<5; i++) {
1917 (*t->linewidth) (1.0);
1918 (*t->linetype) (LT_SOLID);
1919 (*t->dashtype) (i, NULL);
1920 (*t->set_color)(&black);
1921 (*t->move) (x, y); (*t->vector) (x+xl, y);
1922 sprintf(label," dt %1d", i+1);
1923 (*t->put_text) (x+xl, y, label);
1924 y += yl;
1925 }
1926 (*t->put_text) (x, y, "dashtype");
1927
1928 /* test fill patterns */
1929 x = x0 + xmax_t * 0.5;
1930 y = y0;
1931 xl = xmax_t / 40;
1932 yl = ymax_t / 8;
1933 (*t->linewidth) ((float)(1));
1934 (*t->linetype)(LT_BLACK);
1935 (*t->justify_text) (CENTRE);
1936 (*t->put_text)(x+xl*7, y + yl+t->v_char*1.5, "pattern fill");
1937 for (i=0; i<9; i++) {
1938 int style = ((i<<4) + FS_PATTERN);
1939 if (t->fillbox)
1940 (*t->fillbox) ( style, x, y, xl, yl );
1941 newpath();
1942 (*t->move) (x,y);
1943 (*t->vector)(x,y+yl);
1944 (*t->vector)(x+xl,y+yl);
1945 (*t->vector)(x+xl,y);
1946 (*t->vector)(x,y);
1947 closepath();
1948 sprintf(label, "%2d", i);
1949 (*t->put_text)(x+xl/2, y+yl+t->v_char*0.5, label);
1950 x += xl * 1.5;
1951 }
1952
1953 {
1954 int cen_x = x0 + (int)(0.70 * xmax_t);
1955 int cen_y = y0 + (int)(0.83 * ymax_t);
1956 int radius = xmax_t / 20;
1957
1958 /* test pm3d -- filled_polygon(), but not set_color() */
1959 if (t->filled_polygon) {
1960 int i, j;
1961 #define NUMBER_OF_VERTICES 6
1962 int n = NUMBER_OF_VERTICES;
1963 gpiPoint corners[NUMBER_OF_VERTICES+1];
1964 #undef NUMBER_OF_VERTICES
1965
1966 for (j=0; j<=1; j++) {
1967 int ix = cen_x + j*radius;
1968 int iy = cen_y - j*radius/2;
1969 for (i = 0; i < n; i++) {
1970 corners[i].x = ix + radius * cos(2*M_PI*i/n);
1971 corners[i].y = iy + radius * sin(2*M_PI*i/n);
1972 }
1973 corners[n].x = corners[0].x;
1974 corners[n].y = corners[0].y;
1975 if (j == 0) {
1976 (*t->linetype)(2);
1977 corners->style = FS_OPAQUE;
1978 } else {
1979 (*t->linetype)(1);
1980 corners->style = FS_TRANSPARENT_SOLID + (50<<4);
1981 }
1982 term->filled_polygon(n+1, corners);
1983 }
1984 str = "filled polygons:";
1985 } else
1986 str = "No filled polygons";
1987 (*t->linetype)(LT_BLACK);
1988 i = ((*t->justify_text) (CENTRE)) ? 0 : t->h_char * strlen(str) / 2;
1989 (*t->put_text) (cen_x - i, cen_y + radius + t->v_char * 0.5, str);
1990 }
1991
1992 term_end_plot();
1993 }
1994
1995
1996 #ifdef VMS
1997 /* these are needed to modify terminal characteristics */
1998 # ifndef VWS_XMAX
1999 /* avoid duplicate warning; VWS includes these */
2000 # include <descrip.h>
2001 # include <ssdef.h>
2002 # endif /* !VWS_MAX */
2003 # include <iodef.h>
2004 # include <ttdef.h>
2005 # include <tt2def.h>
2006 # include <dcdef.h>
2007 # include <stat.h>
2008 # include <fab.h>
2009 /* If you use WATCOM C or a very strict ANSI compiler, you may have to
2010 * delete or comment out the following 3 lines: */
2011 # ifndef TT2$M_DECCRT3 /* VT300 not defined as of VAXC v2.4 */
2012 # define TT2$M_DECCRT3 0X80000000
2013 # endif
2014 static unsigned short chan;
2015 static int old_char_buf[3], cur_char_buf[3];
2016 $DESCRIPTOR(sysoutput_desc, "SYS$OUTPUT");
2017
2018 /* Look first for decw$display (decterms do regis). Determine if we
2019 * have a regis terminal and save terminal characteristics */
2020 char *
vms_init()2021 vms_init()
2022 {
2023 /* Save terminal characteristics in old_char_buf and
2024 initialise cur_char_buf to current settings. */
2025 int i;
2026 #ifdef X11
2027 if (getenv("DECW$DISPLAY"))
2028 return ("x11");
2029 #endif
2030 atexit(vms_reset);
2031 sys$assign(&sysoutput_desc, &chan, 0, 0);
2032 sys$qiow(0, chan, IO$_SENSEMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2033 for (i = 0; i < 3; ++i)
2034 cur_char_buf[i] = old_char_buf[i];
2035 sys$dassgn(chan);
2036
2037 /* Test if terminal is regis */
2038 if ((cur_char_buf[2] & TT2$M_REGIS) == TT2$M_REGIS)
2039 return ("regis");
2040 return (NULL);
2041 }
2042
2043 /* set terminal to original state */
2044 void
vms_reset()2045 vms_reset()
2046 {
2047 int i;
2048
2049 sys$assign(&sysoutput_desc, &chan, 0, 0);
2050 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2051 for (i = 0; i < 3; ++i)
2052 cur_char_buf[i] = old_char_buf[i];
2053 sys$dassgn(chan);
2054 }
2055
2056 /* set terminal mode to tektronix */
2057 void
term_mode_tek()2058 term_mode_tek()
2059 {
2060 long status;
2061
2062 if (gpoutfile != stdout)
2063 return; /* don't modify if not stdout */
2064 sys$assign(&sysoutput_desc, &chan, 0, 0);
2065 cur_char_buf[0] = 0x004A0000 | DC$_TERM | (TT$_TEK401X << 8);
2066 cur_char_buf[1] = (cur_char_buf[1] & 0x00FFFFFF) | 0x18000000;
2067
2068 cur_char_buf[1] &= ~TT$M_CRFILL;
2069 cur_char_buf[1] &= ~TT$M_ESCAPE;
2070 cur_char_buf[1] &= ~TT$M_HALFDUP;
2071 cur_char_buf[1] &= ~TT$M_LFFILL;
2072 cur_char_buf[1] &= ~TT$M_MECHFORM;
2073 cur_char_buf[1] &= ~TT$M_NOBRDCST;
2074 cur_char_buf[1] &= ~TT$M_NOECHO;
2075 cur_char_buf[1] &= ~TT$M_READSYNC;
2076 cur_char_buf[1] &= ~TT$M_REMOTE;
2077 cur_char_buf[1] |= TT$M_LOWER;
2078 cur_char_buf[1] |= TT$M_TTSYNC;
2079 cur_char_buf[1] |= TT$M_WRAP;
2080 cur_char_buf[1] &= ~TT$M_EIGHTBIT;
2081 cur_char_buf[1] &= ~TT$M_MECHTAB;
2082 cur_char_buf[1] &= ~TT$M_SCOPE;
2083 cur_char_buf[1] |= TT$M_HOSTSYNC;
2084
2085 cur_char_buf[2] &= ~TT2$M_APP_KEYPAD;
2086 cur_char_buf[2] &= ~TT2$M_BLOCK;
2087 cur_char_buf[2] &= ~TT2$M_DECCRT3;
2088 cur_char_buf[2] &= ~TT2$M_LOCALECHO;
2089 cur_char_buf[2] &= ~TT2$M_PASTHRU;
2090 cur_char_buf[2] &= ~TT2$M_REGIS;
2091 cur_char_buf[2] &= ~TT2$M_SIXEL;
2092 cur_char_buf[2] |= TT2$M_BRDCSTMBX;
2093 cur_char_buf[2] |= TT2$M_EDITING;
2094 cur_char_buf[2] |= TT2$M_INSERT;
2095 cur_char_buf[2] |= TT2$M_PRINTER;
2096 cur_char_buf[2] &= ~TT2$M_ANSICRT;
2097 cur_char_buf[2] &= ~TT2$M_AVO;
2098 cur_char_buf[2] &= ~TT2$M_DECCRT;
2099 cur_char_buf[2] &= ~TT2$M_DECCRT2;
2100 cur_char_buf[2] &= ~TT2$M_DRCS;
2101 cur_char_buf[2] &= ~TT2$M_EDIT;
2102 cur_char_buf[2] |= TT2$M_FALLBACK;
2103
2104 status = sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2105 if (status == SS$_BADPARAM) {
2106 /* terminal fallback utility not installed on system */
2107 cur_char_buf[2] &= ~TT2$M_FALLBACK;
2108 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2109 } else {
2110 if (status != SS$_NORMAL)
2111 lib$signal(status, 0, 0);
2112 }
2113 sys$dassgn(chan);
2114 }
2115
2116 /* set terminal mode back to native */
2117 void
term_mode_native()2118 term_mode_native()
2119 {
2120 int i;
2121
2122 if (gpoutfile != stdout)
2123 return; /* don't modify if not stdout */
2124 sys$assign(&sysoutput_desc, &chan, 0, 0);
2125 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2126 for (i = 0; i < 3; ++i)
2127 cur_char_buf[i] = old_char_buf[i];
2128 sys$dassgn(chan);
2129 }
2130
2131 /* set terminal mode pasthru */
2132 void
term_pasthru()2133 term_pasthru()
2134 {
2135 if (gpoutfile != stdout)
2136 return; /* don't modify if not stdout */
2137 sys$assign(&sysoutput_desc, &chan, 0, 0);
2138 cur_char_buf[2] |= TT2$M_PASTHRU;
2139 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2140 sys$dassgn(chan);
2141 }
2142
2143 /* set terminal mode nopasthru */
2144 void
term_nopasthru()2145 term_nopasthru()
2146 {
2147 if (gpoutfile != stdout)
2148 return; /* don't modify if not stdout */
2149 sys$assign(&sysoutput_desc, &chan, 0, 0);
2150 cur_char_buf[2] &= ~TT2$M_PASTHRU;
2151 sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2152 sys$dassgn(chan);
2153 }
2154
2155 void
fflush_binary()2156 fflush_binary()
2157 {
2158 typedef short int INT16; /* signed 16-bit integers */
2159 INT16 k; /* loop index */
2160
2161 if (gpoutfile != stdout) {
2162 /* Stupid VMS fflush() raises error and loses last data block
2163 unless it is full for a fixed-length record binary file.
2164 Pad it here with NULL characters. */
2165 for (k = (INT16) ((*gpoutfile)->_cnt); k > 0; --k)
2166 putc('\0', gpoutfile);
2167 fflush(gpoutfile);
2168 }
2169 }
2170 #endif /* VMS */
2171
2172 /*
2173 * This is an abstraction of the enhanced text mode originally written
2174 * for the postscript terminal driver by David Denholm and Matt Heffron.
2175 * I have split out a terminal-independent recursive syntax-parser
2176 * routine that can be shared by all drivers that want to add support
2177 * for enhanced text mode.
2178 *
2179 * A driver that wants to make use of this common framework must provide
2180 * three new entries in TERM_TABLE:
2181 * void *enhanced_open (char *fontname, double fontsize, double base,
2182 * TBOOLEAN widthflag, TBOOLEAN showflag,
2183 * int overprint)
2184 * void *enhanced_writec (char c)
2185 * void *enhanced_flush ()
2186 *
2187 * Each driver also has a separate ENHXX_put_text() routine that replaces
2188 * the normal (term->put_text) routine while in enhanced mode.
2189 * This routine must initialize the following globals used by the shared code:
2190 * enhanced_fontscale converts font size to device resolution units
2191 * enhanced_escape_format used to process octal escape characters \xyz
2192 *
2193 * I bent over backwards to make the output of the revised code identical
2194 * to the output of the original postscript version. That means there is
2195 * some cruft left in here (enhanced_max_height for one thing) that is
2196 * probably irrelevant to any new drivers using the code.
2197 *
2198 * Ethan A Merritt - November 2003
2199 */
2200
2201 #ifdef DEBUG_ENH
2202 #define ENH_DEBUG(x) printf x;
2203 #else
2204 #define ENH_DEBUG(x)
2205 #endif
2206
2207 void
do_enh_writec(int c)2208 do_enh_writec(int c)
2209 {
2210 /* note: c is meant to hold a char, but is actually an int, for
2211 * the same reasons applying to putc() and friends */
2212 *enhanced_cur_text++ = c;
2213 }
2214
2215 /*
2216 * Process a bit of string, and return the last character used.
2217 * p is start of string
2218 * brace is TRUE to keep processing to }, FALSE to do one character only
2219 * fontname & fontsize are obvious
2220 * base is the current baseline
2221 * widthflag is TRUE if the width of this should count,
2222 * FALSE for zero width boxes
2223 * showflag is TRUE if this should be shown,
2224 * FALSE if it should not be shown (like TeX \phantom)
2225 * overprint is 0 for normal operation,
2226 * 1 for the underprinted text (included in width calculation),
2227 * 2 for the overprinted text (not included in width calc)
2228 * (overprinted text is centered horizontally on underprinted text
2229 */
2230
2231 const char *
enhanced_recursion(const char * p,TBOOLEAN brace,char * fontname,double fontsize,double base,TBOOLEAN widthflag,TBOOLEAN showflag,int overprint)2232 enhanced_recursion(
2233 const char *p,
2234 TBOOLEAN brace,
2235 char *fontname,
2236 double fontsize,
2237 double base,
2238 TBOOLEAN widthflag,
2239 TBOOLEAN showflag,
2240 int overprint)
2241 {
2242 TBOOLEAN wasitalic, wasbold;
2243
2244 /* Keep track of the style of the font passed in at this recursion level */
2245 wasitalic = (strstr(fontname, ":Italic") != NULL);
2246 wasbold = (strstr(fontname, ":Bold") != NULL);
2247
2248 FPRINTF((stderr, "RECURSE WITH \"%s\", %d %s %.1f %.1f %d %d %d",
2249 p, brace, fontname, fontsize, base, widthflag, showflag, overprint));
2250
2251 /* Start each recursion with a clean string */
2252 (term->enhanced_flush)();
2253
2254 if (base + fontsize > enhanced_max_height) {
2255 enhanced_max_height = base + fontsize;
2256 ENH_DEBUG(("Setting max height to %.1f\n", enhanced_max_height));
2257 }
2258
2259 if (base < enhanced_min_height) {
2260 enhanced_min_height = base;
2261 ENH_DEBUG(("Setting min height to %.1f\n", enhanced_min_height));
2262 }
2263
2264 while (*p) {
2265 float shift;
2266
2267 /*
2268 * EAM Jun 2009 - treating bytes one at a time does not work for multibyte
2269 * encodings, including utf-8. If we hit a byte with the high bit set, test
2270 * whether it starts a legal UTF-8 sequence and if so copy the whole thing.
2271 * Other multibyte encodings are still a problem.
2272 * Gnuplot's other defined encodings are all single-byte; for those we
2273 * really do want to treat one byte at a time.
2274 */
2275 if ((*p & 0x80) && (encoding == S_ENC_DEFAULT || encoding == S_ENC_UTF8)) {
2276 unsigned long utf8char;
2277 const char *nextchar = p;
2278
2279 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2280 if (utf8toulong(&utf8char, &nextchar)) { /* Legal UTF8 sequence */
2281 while (p < nextchar)
2282 (term->enhanced_writec)(*p++);
2283 p--;
2284 } else { /* Some other multibyte encoding? */
2285 (term->enhanced_writec)(*p);
2286 }
2287 /* shige : for Shift_JIS */
2288 } else if ((*p & 0x80) && (encoding == S_ENC_SJIS)) {
2289 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2290 (term->enhanced_writec)(*(p++));
2291 (term->enhanced_writec)(*p);
2292 } else
2293
2294 switch (*p) {
2295 case '}' :
2296 /*{{{ deal with it*/
2297 if (brace)
2298 return (p);
2299
2300 int_warn(NO_CARET, "enhanced text parser - spurious }");
2301 break;
2302 /*}}}*/
2303
2304 case '_' :
2305 case '^' :
2306 /*{{{ deal with super/sub script*/
2307 shift = (*p == '^') ? 0.5 : -0.3;
2308 (term->enhanced_flush)();
2309 p = enhanced_recursion(p + 1, FALSE, fontname, fontsize * 0.8,
2310 base + shift * fontsize, widthflag,
2311 showflag, overprint);
2312 break;
2313 /*}}}*/
2314 case '{' :
2315 {
2316 TBOOLEAN isitalic = FALSE, isbold = FALSE, isnormal = FALSE;
2317 const char *start_of_fontname = NULL;
2318 const char *end_of_fontname = NULL;
2319 char *localfontname = NULL;
2320 char ch;
2321 float f = fontsize, ovp;
2322
2323 /* Mar 2014 - this will hold "fontfamily{:Italic}{:Bold}" */
2324 char *styledfontname = NULL;
2325
2326 /*{{{ recurse (possibly with a new font) */
2327
2328 ENH_DEBUG(("Dealing with {\n"));
2329
2330 /* 30 Sep 2016: Remove incorrect whitespace-eating loop going */
2331 /* waaay back to 31-May-2000 */ /* while (*++p == ' '); */
2332 ++p;
2333 /* get vertical offset (if present) for overprinted text */
2334 if (overprint == 2) {
2335 char *end;
2336 ovp = (float)strtod(p,&end);
2337 p = end;
2338 if (term->flags & TERM_IS_POSTSCRIPT)
2339 base = ovp*f;
2340 else
2341 base += ovp*f;
2342 }
2343 --p;
2344
2345 if (*++p == '/') {
2346 /* then parse a fontname, optional fontsize */
2347 while (*++p == ' ')
2348 ; /* do nothing */
2349 if (*p=='-') {
2350 while (*++p == ' ')
2351 ; /* do nothing */
2352 }
2353 start_of_fontname = p;
2354
2355 /* Allow font name to be in quotes.
2356 * This makes it possible to handle font names containing spaces.
2357 */
2358 if (*p == '\'' || *p == '"') {
2359 ++p;
2360 while (*p != '\0' && *p != '}' && *p != *start_of_fontname)
2361 ++p;
2362 if (*p != *start_of_fontname) {
2363 int_warn(NO_CARET, "cannot interpret font name %s", start_of_fontname);
2364 p = start_of_fontname;
2365 }
2366 start_of_fontname++;
2367 end_of_fontname = p++;
2368 ch = *p;
2369 } else {
2370
2371 /* Normal unquoted font name */
2372 while ((ch = *p) > ' ' && ch != '=' && ch != '*' && ch != '}' && ch != ':')
2373 ++p;
2374 end_of_fontname = p;
2375 }
2376
2377 do {
2378 if (ch == '=') {
2379 /* get optional font size */
2380 char *end;
2381 p++;
2382 ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
2383 f = (float)strtod(p, &end);
2384 p = end;
2385 ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
2386
2387 if (f == 0)
2388 f = fontsize;
2389 else
2390 f *= enhanced_fontscale; /* remember the scaling */
2391
2392 ENH_DEBUG(("Font size %.1f\n", f));
2393 } else if (ch == '*') {
2394 /* get optional font size scale factor */
2395 char *end;
2396 p++;
2397 ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
2398 f = (float)strtod(p, &end);
2399 p = end;
2400 ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
2401
2402 if (f)
2403 f *= fontsize; /* apply the scale factor */
2404 else
2405 f = fontsize;
2406
2407 ENH_DEBUG(("Font size %.1f\n", f));
2408 } else if (ch == ':') {
2409 /* get optional style markup attributes */
2410 p++;
2411 if (!strncmp(p,"Bold",4))
2412 isbold = TRUE;
2413 if (!strncmp(p,"Italic",6))
2414 isitalic = TRUE;
2415 if (!strncmp(p,"Normal",6))
2416 isnormal = TRUE;
2417 while (isalpha((unsigned char)*p)) {p++;}
2418 }
2419 } while (((ch = *p) == '=') || (ch == ':') || (ch == '*'));
2420
2421 if (ch == '}')
2422 int_warn(NO_CARET,"bad syntax in enhanced text string");
2423
2424 if (*p == ' ') /* Eat up a single space following a font spec */
2425 ++p;
2426 if (!start_of_fontname || (start_of_fontname == end_of_fontname)) {
2427 /* Use the font name passed in to us */
2428 localfontname = gp_strdup(fontname);
2429 } else {
2430 /* We found a new font name {/Font ...} */
2431 int len = end_of_fontname - start_of_fontname;
2432 localfontname = gp_alloc(len+1,"localfontname");
2433 strncpy(localfontname, start_of_fontname, len);
2434 localfontname[len] = '\0';
2435 }
2436 }
2437 /*}}}*/
2438
2439 /* Collect cumulative style markup before passing it in the font name */
2440 isitalic = (wasitalic || isitalic) && !isnormal;
2441 isbold = (wasbold || isbold) && !isnormal;
2442
2443 styledfontname = stylefont(localfontname ? localfontname : fontname,
2444 isbold, isitalic);
2445
2446 p = enhanced_recursion(p, TRUE, styledfontname, f, base,
2447 widthflag, showflag, overprint);
2448
2449 (term->enhanced_flush)();
2450
2451 free(styledfontname);
2452 free(localfontname);
2453
2454 break;
2455 } /* case '{' */
2456 case '@' :
2457 /*{{{ phantom box - prints next 'char', then restores currentpoint */
2458 (term->enhanced_flush)();
2459 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 3);
2460 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2461 widthflag, showflag, overprint);
2462 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 4);
2463 break;
2464 /*}}}*/
2465
2466 case '&' :
2467 /*{{{ character skip - skips space equal to length of character(s) */
2468 (term->enhanced_flush)();
2469
2470 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2471 widthflag, FALSE, overprint);
2472 break;
2473 /*}}}*/
2474
2475 case '~' :
2476 /*{{{ overprinted text */
2477 /* the second string is overwritten on the first, centered
2478 * horizontally on the first and (optionally) vertically
2479 * shifted by an amount specified (as a fraction of the
2480 * current fontsize) at the beginning of the second string
2481
2482 * Note that in this implementation neither the under- nor
2483 * overprinted string can contain syntax that would result
2484 * in additional recursions -- no subscripts,
2485 * superscripts, or anything else, with the exception of a
2486 * font definition at the beginning of the text */
2487
2488 (term->enhanced_flush)();
2489 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2490 widthflag, showflag, 1);
2491 (term->enhanced_flush)();
2492 if (!*p)
2493 break;
2494 p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2495 FALSE, showflag, 2);
2496
2497 overprint = 0; /* may not be necessary, but just in case . . . */
2498 break;
2499 /*}}}*/
2500
2501 case '(' :
2502 case ')' :
2503 /*{{{ an escape and print it */
2504 /* special cases */
2505 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2506 if (term->flags & TERM_IS_POSTSCRIPT)
2507 (term->enhanced_writec)('\\');
2508 (term->enhanced_writec)(*p);
2509 break;
2510 /*}}}*/
2511
2512 case '\\' :
2513 /*{{{ Enhanced mode always uses \xyz as an octal character representation
2514 but each terminal type must give us the actual output format wanted.
2515 pdf.trm wants the raw character code, which is why we use strtol();
2516 most other terminal types want some variant of "\\%o". */
2517 if (p[1] >= '0' && p[1] <= '7') {
2518 char *e, escape[16], octal[4] = {'\0','\0','\0','\0'};
2519
2520 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2521 octal[0] = *(++p);
2522 if (p[1] >= '0' && p[1] <= '7') {
2523 octal[1] = *(++p);
2524 if (p[1] >= '0' && p[1] <= '7')
2525 octal[2] = *(++p);
2526 }
2527 sprintf(escape, enhanced_escape_format, strtol(octal,NULL,8));
2528 for (e=escape; *e; e++) {
2529 (term->enhanced_writec)(*e);
2530 }
2531 break;
2532 /* This was the original (prior to version 4) enhanced text code specific */
2533 /* to the reserved characters of PostScript. Some of it was mis-applied */
2534 /* to other terminal types until fixed in Mar 2012. */
2535 } else if (term->flags & TERM_IS_POSTSCRIPT) {
2536 if (p[1]=='\\' || p[1]=='(' || p[1]==')') {
2537 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2538 (term->enhanced_writec)('\\');
2539 } else if (strchr("^_@&~{}",p[1]) == NULL) {
2540 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2541 (term->enhanced_writec)('\\');
2542 (term->enhanced_writec)('\\');
2543 break;
2544 }
2545 }
2546 ++p;
2547
2548 /* HBB 20030122: Avoid broken output if there's a \
2549 * exactly at the end of the line */
2550 if (*p == '\0') {
2551 int_warn(NO_CARET, "enhanced text parser -- spurious backslash");
2552 break;
2553 }
2554
2555 /* SVG requires an escaped '&' to be passed as something else */
2556 /* FIXME: terminal-dependent code does not belong here */
2557 if (*p == '&' && encoding == S_ENC_DEFAULT && !strcmp(term->name, "svg")) {
2558 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2559 (term->enhanced_writec)('\376');
2560 break;
2561 }
2562
2563 /* just go and print it (fall into the 'default' case) */
2564 /*}}}*/
2565 default:
2566 /*{{{ print it */
2567 (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2568 (term->enhanced_writec)(*p);
2569 /*}}}*/
2570 } /* switch (*p) */
2571
2572 /* like TeX, we only do one character in a recursion, unless it's
2573 * in braces
2574 */
2575
2576 if (!brace) {
2577 (term->enhanced_flush)();
2578 return(p); /* the ++p in the outer copy will increment us */
2579 }
2580
2581 if (*p) /* only not true if { not terminated, I think */
2582 ++p;
2583 } /* while (*p) */
2584
2585 (term->enhanced_flush)();
2586 return p;
2587 }
2588
2589 /* Strip off anything trailing the requested font name,
2590 * then add back markup requests.
2591 */
2592 char *
stylefont(const char * fontname,TBOOLEAN isbold,TBOOLEAN isitalic)2593 stylefont(const char *fontname, TBOOLEAN isbold, TBOOLEAN isitalic)
2594 {
2595 char *div;
2596 char *markup = gp_alloc( strlen(fontname) + 16, "font markup");
2597 strcpy(markup, fontname);
2598 if ((div = strchr(markup,':')))
2599 *div = '\0';
2600 if (isbold)
2601 strcat(markup, ":Bold");
2602 if (isitalic)
2603 strcat(markup, ":Italic");
2604
2605 FPRINTF((stderr, "MARKUP FONT: %s -> %s\n", fontname, markup));
2606 return markup;
2607 }
2608
2609 /* Called after the end of recursion to check for errors */
2610 void
enh_err_check(const char * str)2611 enh_err_check(const char *str)
2612 {
2613 if (*str == '}')
2614 int_warn(NO_CARET, "enhanced text mode parser - ignoring spurious }");
2615 else
2616 int_warn(NO_CARET, "enhanced text mode parsing error");
2617 }
2618
2619 /*
2620 * Text strings containing control information for enhanced text mode
2621 * contain more characters than will actually appear in the output.
2622 * This makes it hard to estimate how much horizontal space on the plot
2623 * (e.g. in the key box) must be reserved to hold them. To approximate
2624 * the eventual length we switch briefly to the dummy terminal driver
2625 * "estimate.trm" and then switch back to the current terminal.
2626 * If better, perhaps terminal-specific methods of estimation are
2627 * developed later they can be slotted into this one call site.
2628 */
2629 int
estimate_strlen(const char * text)2630 estimate_strlen(const char *text)
2631 {
2632 int len;
2633
2634 if ((term->flags & TERM_IS_LATEX))
2635 len = strlen_tex(text);
2636 else
2637
2638 #ifdef GP_ENH_EST
2639 if (strchr(text,'\n') || (term->flags & TERM_ENHANCED_TEXT)) {
2640 struct termentry *tsave = term;
2641 term = &ENHest;
2642 term->put_text(0,0,text);
2643 len = term->xmax;
2644 FPRINTF((stderr,"Estimating length %d height %g for enhanced text string \"%s\"\n",
2645 len, (double)(term->ymax)/10., text));
2646 term = tsave;
2647 } else if (encoding == S_ENC_UTF8)
2648 len = strwidth_utf8(text);
2649 else
2650 #endif
2651 len = strlen(text);
2652
2653 return len;
2654 }
2655
2656 /*
2657 * Use estimate.trm to mock up a non-enhanced approximation of the
2658 * original string.
2659 */
2660 char *
estimate_plaintext(char * enhancedtext)2661 estimate_plaintext(char *enhancedtext)
2662 {
2663 if (enhancedtext == NULL)
2664 return NULL;
2665 estimate_strlen(enhancedtext);
2666 return ENHest_plaintext;
2667 }
2668
2669 void
ignore_enhanced(TBOOLEAN flag)2670 ignore_enhanced(TBOOLEAN flag)
2671 {
2672 #if (0)
2673 /* Apr 2018: This code was introduced long ago (2005; Bug #266) to address
2674 * a glitch in the postscript terminal that left the last-used font in
2675 * an enhanced text string active afterwards.
2676 * We now deal with this in ENHPS_put_text() instead.
2677 */
2678 if (flag && !ignore_enhanced_text) {
2679 ignore_enhanced_text = TRUE;
2680 term->set_font("");
2681 }
2682 #endif
2683 ignore_enhanced_text = flag;
2684 }
2685
2686 /* Simple-minded test for whether the point (x,y) is in bounds for the current terminal.
2687 * Some terminals can do their own clipping, and can clip partial objects.
2688 * If the flag TERM_CAN_CLIP is set, we skip this relative crude test and let the
2689 * driver or the hardware handle clipping.
2690 */
2691 TBOOLEAN
on_page(int x,int y)2692 on_page(int x, int y)
2693 {
2694 if (term->flags & TERM_CAN_CLIP)
2695 return TRUE;
2696
2697 if ((0 < x && x < term->xmax) && (0 < y && y < term->ymax))
2698 return TRUE;
2699
2700 return FALSE;
2701 }
2702
2703 /* Utility routine for drivers to accept an explicit size for the
2704 * output image.
2705 */
2706 size_units
parse_term_size(float * xsize,float * ysize,size_units default_units)2707 parse_term_size( float *xsize, float *ysize, size_units default_units )
2708 {
2709 size_units units = default_units;
2710
2711 if (END_OF_COMMAND)
2712 int_error(c_token, "size requires two numbers: xsize, ysize");
2713 *xsize = real_expression();
2714 if (almost_equals(c_token,"in$ches")) {
2715 c_token++;
2716 units = INCHES;
2717 } else if (equals(c_token,"cm")) {
2718 c_token++;
2719 units = CM;
2720 }
2721 switch (units) {
2722 case INCHES: *xsize *= gp_resolution; break;
2723 case CM: *xsize *= (float)gp_resolution / 2.54; break;
2724 case PIXELS:
2725 default: break;
2726 }
2727
2728 if (!equals(c_token++,","))
2729 int_error(c_token, "size requires two numbers: xsize, ysize");
2730 *ysize = real_expression();
2731 if (almost_equals(c_token,"in$ches")) {
2732 c_token++;
2733 units = INCHES;
2734 } else if (equals(c_token,"cm")) {
2735 c_token++;
2736 units = CM;
2737 }
2738 switch (units) {
2739 case INCHES: *ysize *= gp_resolution; break;
2740 case CM: *ysize *= (float)gp_resolution / 2.54; break;
2741 case PIXELS:
2742 default: break;
2743 }
2744
2745 if (*xsize < 1 || *ysize < 1)
2746 int_error(c_token, "size: out of range");
2747
2748 return units;
2749 }
2750
2751 /*
2752 * Wrappers for newpath and closepath
2753 */
2754
2755 void
newpath()2756 newpath()
2757 {
2758 if (term->path)
2759 (*term->path)(0);
2760 }
2761
2762 void
closepath()2763 closepath()
2764 {
2765 if (term->path)
2766 (*term->path)(1);
2767 }
2768
2769 /* Squeeze all fill information into the old style parameter.
2770 * The terminal drivers know how to extract the information.
2771 * We assume that the style (int) has only 16 bit, therefore we take
2772 * 4 bits for the style and allow 12 bits for the corresponding fill parameter.
2773 * This limits the number of styles to 16 and the fill parameter's
2774 * values to the range 0...4095, which seems acceptable.
2775 */
2776 int
style_from_fill(struct fill_style_type * fs)2777 style_from_fill(struct fill_style_type *fs)
2778 {
2779 int fillpar, style;
2780
2781 switch( fs->fillstyle ) {
2782 case FS_SOLID:
2783 case FS_TRANSPARENT_SOLID:
2784 fillpar = fs->filldensity;
2785 style = ((fillpar & 0xfff) << 4) + fs->fillstyle;
2786 break;
2787 case FS_PATTERN:
2788 case FS_TRANSPARENT_PATTERN:
2789 fillpar = fs->fillpattern;
2790 style = ((fillpar & 0xfff) << 4) + fs->fillstyle;
2791 break;
2792 case FS_EMPTY:
2793 default:
2794 /* solid fill with background color */
2795 style = FS_EMPTY;
2796 break;
2797 }
2798
2799 return style;
2800 }
2801
2802
2803 /*
2804 * Load dt with the properties of a user-defined dashtype.
2805 * Return: DASHTYPE_SOLID or DASHTYPE_CUSTOM or a positive number
2806 * if no user-defined dashtype was found.
2807 */
2808 int
load_dashtype(struct t_dashtype * dt,int tag)2809 load_dashtype(struct t_dashtype *dt, int tag)
2810 {
2811 struct custom_dashtype_def *this;
2812 struct t_dashtype loc_dt = DEFAULT_DASHPATTERN;
2813
2814 this = first_custom_dashtype;
2815 while (this != NULL) {
2816 if (this->tag == tag) {
2817 *dt = this->dashtype;
2818 memcpy(dt->dstring, this->dashtype.dstring, sizeof(dt->dstring));
2819 return this->d_type;
2820 } else {
2821 this = this->next;
2822 }
2823 }
2824
2825 /* not found, fall back to default, terminal-dependent dashtype */
2826 *dt = loc_dt;
2827 return tag - 1;
2828 }
2829
2830
2831 void
lp_use_properties(struct lp_style_type * lp,int tag)2832 lp_use_properties(struct lp_style_type *lp, int tag)
2833 {
2834 /* This function looks for a linestyle defined by 'tag' and copies
2835 * its data into the structure 'lp'.
2836 */
2837
2838 struct linestyle_def *this;
2839 int save_flags = lp->flags;
2840
2841 this = first_linestyle;
2842 while (this != NULL) {
2843 if (this->tag == tag) {
2844 *lp = this->lp_properties;
2845 lp->flags = save_flags;
2846 return;
2847 } else {
2848 this = this->next;
2849 }
2850 }
2851
2852 /* No user-defined style with this tag; fall back to default line type. */
2853 load_linetype(lp, tag);
2854 }
2855
2856
2857 /*
2858 * Load lp with the properties of a user-defined linetype
2859 */
2860 void
load_linetype(struct lp_style_type * lp,int tag)2861 load_linetype(struct lp_style_type *lp, int tag)
2862 {
2863 struct linestyle_def *this;
2864 TBOOLEAN recycled = FALSE;
2865
2866 recycle:
2867
2868 if ((tag > 0)
2869 && (monochrome || (term && (term->flags & TERM_MONOCHROME)))) {
2870 for (this = first_mono_linestyle; this; this = this->next) {
2871 if (tag == this->tag) {
2872 *lp = this->lp_properties;
2873 return;
2874 }
2875 }
2876
2877 /* This linetype wasn't defined explicitly. */
2878 /* Should we recycle one of the first N linetypes? */
2879 if (tag > mono_recycle_count && mono_recycle_count > 0) {
2880 tag = (tag-1) % mono_recycle_count + 1;
2881 goto recycle;
2882 }
2883
2884 return;
2885 }
2886
2887 this = first_perm_linestyle;
2888 while (this != NULL) {
2889 if (this->tag == tag) {
2890 /* Always load color, width, and dash properties */
2891 lp->l_type = this->lp_properties.l_type;
2892 lp->l_width = this->lp_properties.l_width;
2893 lp->pm3d_color = this->lp_properties.pm3d_color;
2894 lp->d_type = this->lp_properties.d_type;
2895 lp->custom_dash_pattern = this->lp_properties.custom_dash_pattern;
2896
2897 /* Needed in version 5.0 to handle old terminals (pbm hpgl ...) */
2898 /* with no support for user-specified colors */
2899 if (term && term->set_color == null_set_color)
2900 lp->l_type = tag;
2901
2902 /* Do not recycle point properties. */
2903 /* FIXME: there should be a separate command "set pointtype cycle N" */
2904 if (!recycled) {
2905 lp->p_type = this->lp_properties.p_type;
2906 lp->p_interval = this->lp_properties.p_interval;
2907 lp->p_size = this->lp_properties.p_size;
2908 memcpy(lp->p_char, this->lp_properties.p_char, sizeof(lp->p_char));
2909 }
2910 return;
2911 } else {
2912 this = this->next;
2913 }
2914 }
2915
2916 /* This linetype wasn't defined explicitly. */
2917 /* Should we recycle one of the first N linetypes? */
2918 if (tag > linetype_recycle_count && linetype_recycle_count > 0) {
2919 tag = (tag-1) % linetype_recycle_count + 1;
2920 recycled = TRUE;
2921 goto recycle;
2922 }
2923
2924 /* No user-defined linetype with this tag; fall back to default line type. */
2925 /* NB: We assume that the remaining fields of lp have been initialized. */
2926 lp->l_type = tag - 1;
2927 lp->pm3d_color.type = TC_LT;
2928 lp->pm3d_color.lt = lp->l_type;
2929 lp->d_type = DASHTYPE_SOLID;
2930 lp->p_type = (tag <= 0) ? -1 : tag - 1;
2931 }
2932
2933 /*
2934 * Version 5 maintains a parallel set of linetypes for "set monochrome" mode.
2935 * This routine allocates space and initializes the default set.
2936 */
2937 void
init_monochrome()2938 init_monochrome()
2939 {
2940 struct lp_style_type mono_default[] = DEFAULT_MONO_LINETYPES;
2941
2942 if (first_mono_linestyle == NULL) {
2943 int i, n = sizeof(mono_default) / sizeof(struct lp_style_type);
2944 struct linestyle_def *new;
2945 /* copy default list into active list */
2946 for (i=n; i>0; i--) {
2947 new = gp_alloc(sizeof(struct linestyle_def), NULL);
2948 new->next = first_mono_linestyle;
2949 new->lp_properties = mono_default[i-1];
2950 new->tag = i;
2951 first_mono_linestyle = new;
2952 }
2953 }
2954 }
2955
2956 /*
2957 * Totally bogus estimate of TeX string lengths.
2958 * Basically
2959 * - don't count anything inside square braces
2960 * - count regexp \[a-zA-z]* as a single character
2961 * - ignore characters {}$^_
2962 */
2963 int
strlen_tex(const char * str)2964 strlen_tex(const char *str)
2965 {
2966 const char *s = str;
2967 int len = 0;
2968
2969 if (!strpbrk(s, "{}$[]\\")) {
2970 len = strlen(s);
2971 FPRINTF((stderr,"strlen_tex(\"%s\") = %d\n",s,len));
2972 return len;
2973 }
2974
2975 while (*s) {
2976 switch (*s) {
2977 case '[':
2978 while (*s && *s != ']') s++;
2979 if (*s) s++;
2980 break;
2981 case '\\':
2982 s++;
2983 while (*s && isalpha((unsigned char)*s)) s++;
2984 len++;
2985 break;
2986 case '{':
2987 case '}':
2988 case '$':
2989 case '_':
2990 case '^':
2991 s++;
2992 break;
2993 default:
2994 s++;
2995 len++;
2996 }
2997 }
2998
2999
3000 FPRINTF((stderr,"strlen_tex(\"%s\") = %d\n",str,len));
3001 return len;
3002 }
3003
3004 /* The check for asynchronous events such as hotkeys and mouse clicks is
3005 * normally done in term->waitforinput() while waiting for the next input
3006 * from the command line. If input is currently coming from a file or
3007 * pipe instead, as with a "load" command, then this path would not be
3008 * triggered automatically and these events would back up until input
3009 * returned to the command line. These code paths can explicitly call
3010 * check_for_mouse_events() so that event processing is handled sooner.
3011 */
3012 void
check_for_mouse_events()3013 check_for_mouse_events()
3014 {
3015 #ifdef USE_MOUSE
3016 if (term_initialised && term->waitforinput) {
3017 term->waitforinput(TERM_ONLY_CHECK_MOUSING);
3018 }
3019 #endif
3020 #ifdef _WIN32
3021 /* Process windows GUI events (e.g. for text window, or wxt and windows terminals) */
3022 WinMessageLoop();
3023 /* On Windows, Ctrl-C only sets this flag. */
3024 /* The next block duplicates the behaviour of inter(). */
3025 if (ctrlc_flag) {
3026 ctrlc_flag = FALSE;
3027 term_reset();
3028 putc('\n', stderr);
3029 fprintf(stderr, "Ctrl-C detected!\n");
3030 bail_to_command_line(); /* return to prompt */
3031 }
3032 #endif
3033 }
3034