1/* Hello, Emacs, this is -*-C-*- */
2
3/* GNUPLOT - dumb.trm */
4
5/*[
6 * Copyright 1991 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
7 *
8 * Permission to use, copy, and distribute this software and its
9 * documentation for any purpose with or without fee is hereby granted,
10 * provided that the above copyright notice appear in all copies and
11 * that both that copyright notice and this permission notice appear
12 * in supporting documentation.
13 *
14 * Permission to modify the software is granted, but not the right to
15 * distribute the complete modified source code.  Modifications are to
16 * be distributed as patches to the released version.  Permission to
17 * distribute binaries produced by compiling modified sources is granted,
18 * provided you
19 *   1. distribute the corresponding source modifications from the
20 *    released version in the form of a patch file along with the binaries,
21 *   2. add special version identification to distinguish your version
22 *    in addition to the base release version number,
23 *   3. provide your name and address as the primary contact for the
24 *    support of your modified version, and
25 *   4. retain our contact information in regard to use of the base
26 *    software.
27 * Permission to distribute the released version of the source code along
28 * with corresponding source modifications in the form of a patch file is
29 * granted with same provisions 2 through 4 for binary distributions.
30 *
31 * This software is provided "as is" without express or implied warranty
32 * to the extent permitted by applicable law.
33]*/
34
35/*
36 * This file is included by ../term.c.
37 *
38 * This terminal driver supports:
39 *   DUMB terminals
40 *
41 * AUTHORS
42 *   Francois Pinard, 91-04-03
43 *           INTERNET: pinard@iro.umontreal.ca
44 *
45 *   Ethan A Merritt Nov 2003
46 *	Added support for enhanced text mode.
47 *	Yes, this is frivolous, but it serves as an example for
48 *	adding enhanced text to other terminals.  You can disable
49 *	it by adding a line
50 *	#define NO_DUMB_ENHANCED_SUPPORT
51 *
52 *   Bastian Maerkisch Nov 2016
53 *	ANSI color support.  Filled polygons.
54 *
55 * send your comments or suggestions to (gnuplot-info@lists.sourceforge.net).
56 *
57 */
58#include "driver.h"
59
60#ifdef TERM_REGISTER
61register_term(dumb_driver)
62#endif
63
64#ifdef TERM_PROTO
65TERM_PUBLIC void DUMB_options(void);
66TERM_PUBLIC void DUMB_init(void);
67TERM_PUBLIC void DUMB_graphics(void);
68TERM_PUBLIC void DUMB_text(void);
69TERM_PUBLIC void DUMB_reset(void);
70TERM_PUBLIC void DUMB_linetype(int linetype);
71TERM_PUBLIC void DUMB_move(unsigned int x, unsigned int y);
72TERM_PUBLIC void DUMB_point(unsigned int x, unsigned int y, int point);
73TERM_PUBLIC void DUMB_vector(unsigned int x, unsigned int y);
74TERM_PUBLIC void DUMB_put_text(unsigned int x, unsigned int y, const char *str);
75TERM_PUBLIC void DUMB_arrow(unsigned int sx, unsigned int sy,
76			    unsigned int ex, unsigned int ey,
77			    int head);
78
79#ifndef NO_DUMB_ENHANCED_SUPPORT
80/* To support "set term dumb enhanced" (don't ask why!) */
81TERM_PUBLIC void ENHdumb_put_text(unsigned int x, unsigned int y, const char str[]);
82TERM_PUBLIC void ENHdumb_OPEN(char * fontname, double fontsize,
83	                      double base, TBOOLEAN widthflag, TBOOLEAN showflag,
84			      int overprint);
85TERM_PUBLIC void ENHdumb_FLUSH(void);
86#else
87#define ENHdumb_put_text NULL
88#endif
89#ifndef NO_DUMB_COLOR_SUPPORT
90TERM_PUBLIC int dumb_make_palette(t_sm_palette *palette);
91TERM_PUBLIC void dumb_set_color(t_colorspec *);
92#endif
93
94#define DUMB_XMAX 79
95#define DUMB_YMAX 24
96
97#endif /* TERM_PROTO */
98
99#ifdef TERM_BODY
100
101#define DUMB_AXIS_CONST '\1'
102#define DUMB_BORDER_CONST '\2'
103#define DUMB_FILL_CONST '\3'
104#define DUMB_NODRAW_CONST '\4'
105
106#ifdef HAVE_STDINT_H
107#include <stdint.h>
108#endif
109#include "readline.h"
110
111/* UTF-8 support */
112#ifdef HAVE_STDLIB_H
113#define DUMB_UTF8
114 typedef int32_t charcell;
115#else
116 typedef char charcell;
117#endif
118
119/* matrix of characters */
120static charcell *dumb_matrix = NULL;
121#ifndef NO_DUMB_COLOR_SUPPORT
122/* matrix of colors */
123static t_colorspec *dumb_colors = NULL;
124static t_colorspec dumb_color;
125static t_colorspec dumb_prev_color;
126#endif
127/* current character used to draw */
128static char dumb_pen;
129/* current X position */
130static int dumb_x;
131/* current Y position */
132static int dumb_y;
133static int dumb_xmax = DUMB_XMAX;
134static int dumb_ymax = DUMB_YMAX;
135static TBOOLEAN dumb_feed = TRUE;
136static int dumb_colormode = 0;
137
138#define DUMB_PIXEL(x,y) dumb_matrix[dumb_xmax*(y)+(x)]
139
140static void dumb_set_pixel(int x, int y, int v);
141
142enum DUMB_id { DUMB_FEED, DUMB_NOFEED, DUMB_ENH, DUMB_NOENH,
143               DUMB_SIZE, DUMB_ASPECT,
144               DUMB_ANSI, DUMB_ANSI256, DUMB_ANSIRGB, DUMB_NOCOLOR,
145               DUMB_OTHER };
146
147static struct gen_table DUMB_opts[] =
148{
149    { "f$eed", DUMB_FEED },
150    { "nof$eed", DUMB_NOFEED },
151    { "enh$anced", DUMB_ENH },
152    { "noe$nhanced", DUMB_NOENH },
153    { "size", DUMB_SIZE },
154    { "aspect", DUMB_ASPECT },
155    { "ansi", DUMB_ANSI },
156    { "ansi256", DUMB_ANSI256 },
157    { "ansirgb", DUMB_ANSIRGB },
158    { "mono", DUMB_NOCOLOR },
159    { NULL, DUMB_OTHER }
160};
161
162TERM_PUBLIC void
163DUMB_options()
164{
165    int x, y;
166    int cmd;
167    TBOOLEAN set_size = FALSE;
168
169    while (!END_OF_COMMAND) {
170	switch ((cmd = lookup_table(&DUMB_opts[0], c_token))) {
171	case DUMB_FEED:
172	    c_token++;
173	    dumb_feed = TRUE;
174	    break;
175	case DUMB_NOFEED:
176	    c_token++;
177	    dumb_feed = FALSE;
178	    break;
179#ifndef NO_DUMB_ENHANCED_SUPPORT
180	case DUMB_ENH:
181	    c_token++;
182	    term->put_text = ENHdumb_put_text;
183	    term->flags |= TERM_ENHANCED_TEXT;
184	    break;
185	case DUMB_NOENH:
186	    c_token++;
187	    term->put_text = DUMB_put_text;
188	    term->flags &= ~TERM_ENHANCED_TEXT;
189	    break;
190#endif
191	case DUMB_ASPECT:
192	    c_token++;
193	    x = int_expression();
194	    y = 1;
195	    if (!END_OF_COMMAND && equals(c_token, ",")) {
196		c_token++;
197		y = int_expression();
198	    }
199	    if (x <= 0) x = 1;
200	    if (y <= 0) y = 1;
201	    term->h_tic = x;
202	    term->v_tic = y;
203 	    break;
204	case DUMB_ANSI:
205	case DUMB_ANSI256:
206	case DUMB_ANSIRGB:
207	    c_token++;
208	    dumb_colormode = cmd;
209#ifndef NO_DUMB_COLOR_SUPPORT
210	    term->make_palette = dumb_make_palette;
211	    term->set_color = dumb_set_color;
212#endif
213	    break;
214	case DUMB_NOCOLOR:
215	    c_token++;
216	    dumb_colormode = 0;
217	    term->make_palette = NULL;
218	    term->set_color = null_set_color;
219	    break;
220	case DUMB_SIZE:
221	    c_token++;
222	    /* Fall through */
223	case DUMB_OTHER:
224	default:
225	    if (set_size) {
226		int_warn(c_token++,"unrecognized option");
227		break;
228	    }
229	    x = int_expression();
230	    if (x <= 0 || x > 1024)
231		x = DUMB_XMAX;
232	    if (!END_OF_COMMAND) {
233		if (equals(c_token,","))
234		    c_token++;
235		y = int_expression();
236		if (y <= 0 || y > 1024)
237		    y = DUMB_YMAX;
238		dumb_xmax = term->xmax = x;
239		dumb_ymax = term->ymax = y;
240	    }
241	    set_size = TRUE;
242 	    break;
243	}
244    }
245
246    {
247	const char * coloropts[] = {"mono", "ansi", "ansi256", "ansirgb"};
248
249	sprintf(term_options, "%sfeed %s size %d, %d aspect %i, %i %s",
250	    dumb_feed ? "" : "no",
251	    term->put_text == ENHdumb_put_text ? "enhanced" : "",
252	    dumb_xmax, dumb_ymax,
253	    term->h_tic, term->v_tic,
254	    coloropts[dumb_colormode == 0 ? 0 : dumb_colormode - DUMB_ANSI + 1]
255	    );
256    }
257}
258
259
260static void
261dumb_set_pixel(int x, int y, int v)
262{
263    char *charpixel;
264
265    if ((unsigned int) x <= dumb_xmax 	/* ie x>=0 && x<=dumb_xmax */
266    &&  (unsigned int) y <= dumb_ymax
267    )  {
268	charpixel = (char *)(&dumb_matrix[dumb_xmax * y + x]);
269	/* null-terminate single ascii character (needed for UTF-8) */
270	dumb_matrix[dumb_xmax * y + x] = 0;
271	*charpixel = v;
272#ifndef NO_DUMB_COLOR_SUPPORT
273	memcpy(&dumb_colors[dumb_xmax * y + x], &dumb_color, sizeof(t_colorspec));
274#endif
275    }
276}
277
278
279TERM_PUBLIC void
280DUMB_init()
281{
282    int size = (dumb_xmax+1) * (dumb_ymax+1);
283
284    dumb_matrix = gp_realloc(dumb_matrix, size*sizeof(charcell), "dumb terminal");
285#ifndef NO_DUMB_COLOR_SUPPORT
286    dumb_colors = gp_realloc(dumb_colors, size*sizeof(t_colorspec), "dumb terminal");
287#endif
288}
289
290
291TERM_PUBLIC void
292DUMB_graphics()
293{
294    int i;
295    int size = (dumb_xmax+1) * (dumb_ymax+1);
296    charcell *pm = dumb_matrix;
297
298    memset(dumb_matrix, 0, size * sizeof(charcell));
299#ifndef NO_DUMB_COLOR_SUPPORT
300    memset(dumb_colors, 0, size * sizeof(t_colorspec));
301#endif
302
303    for (i=0; i<size; i++) {
304	char *c = (char *)pm++;
305	*c = ' ';
306    }
307}
308
309
310#ifndef NO_DUMB_COLOR_SUPPORT
311/* code snippet adopted from libcaca:  WTFPL license */
312/* RGB colours for the ANSI palette. There is no real standard, so we
313 * use the same values as gnome-terminal. The 7th colour (brown) is a bit
314 * special: 0xfa50 instead of 0xfaa0. */
315static const unsigned ansitab16[16] =
316{
317    0xf000, 0xf00a, 0xf0a0, 0xf0aa, 0xfa00, 0xfa0a, 0xfa50, 0xfaaa,
318    0xf555, 0xf55f, 0xf5f5, 0xf5ff, 0xff55, 0xff5f, 0xfff5, 0xffff,
319};
320
321static unsigned
322nearest_ansi(rgb255_color rgb255)
323{
324    unsigned int i, best, dist;
325
326    best = 0;
327    dist = 0x3fff;
328    for (i = 0; i < 16; i++) {
329	unsigned int d = 0;
330	int a, b;
331
332	a = (ansitab16[i] >> 0) & 0xf;
333	b = (rgb255.r >> 4) & 0xf;
334	d += (a - b) * (a - b);
335
336	a = (ansitab16[i] >> 4) & 0xf;
337	b = (rgb255.g >> 4) & 0xf;
338	d += (a - b) * (a - b);
339
340	a = (ansitab16[i] >> 8) & 0xf;
341	b = (rgb255.b >> 4) & 0xf;
342	d += (a - b) * (a - b);
343
344	if (d < dist) {
345	    dist = d;
346	    best = i;
347	}
348    }
349    return best;
350}
351/* end of code libcaca code */
352
353
354static void
355DUMB_rgb_color(rgb255_color rgb255)
356{
357    switch (dumb_colormode) {
358    case DUMB_ANSI: {
359	unsigned color = nearest_ansi(rgb255);
360	fprintf(gpoutfile, "\033[%i;%im", color >= 8 ? 22 : 1, 30 + (color % 8));
361	break;
362    }
363    case DUMB_ANSI256:
364	if ((rgb255.r / 11 == rgb255.g / 11) && (rgb255.r / 11 == rgb255.b / 11)) {
365	    /* gray level */
366	    fprintf(gpoutfile, "\033[38;5;%im", 241 + rgb255.r / 11);
367	} else {
368	    rgb255.r /= 43;
369	    rgb255.g /= 43;
370	    rgb255.b /= 43;
371	    fprintf(gpoutfile, "\033[38;5;%im", 16 + 36 * rgb255.r + 6 * rgb255.g + rgb255.b);
372	}
373	break;
374    case DUMB_ANSIRGB:
375	fprintf(gpoutfile, "\033[38;2;%i;%i;%im", rgb255.r, rgb255.g, rgb255.b);
376	break;
377    }
378}
379#endif
380
381
382TERM_PUBLIC void
383DUMB_text()
384{
385    int x, y;
386
387    putc('\f', gpoutfile);
388    for (y = dumb_ymax - 1; y >= 0; y--) {
389#ifndef NO_DUMB_COLOR_SUPPORT
390	if (dumb_colormode > 0) {
391	    fputs("\033[0;39m", gpoutfile); /* reset colors to default */
392	    memset(&dumb_prev_color, 0, sizeof(t_colorspec));
393	}
394#endif
395    for (x = 0; x < dumb_xmax; x++) {
396#ifdef DUMB_UTF8
397	    char *c;
398#endif
399#ifndef NO_DUMB_COLOR_SUPPORT
400	    t_colorspec * color = &dumb_colors[dumb_xmax*y + x];
401	    switch (color->type) {
402	    case TC_LT: {
403		int n;
404
405		if (dumb_colormode < DUMB_ANSI)
406		    break;
407		if (dumb_prev_color.type == TC_LT && dumb_prev_color.lt == color->lt)
408		    break;
409		n = color->lt + 1;
410		/* map line type to colors */
411		if (n <= 0) {
412		    fprintf(gpoutfile, "\033[0;39m");  /* normal foreground color */
413		} else {
414		    if (n > 15) n = ((n - 1) % 15) + 1;
415		    fprintf(gpoutfile, "\033[%i;%im", n > 8 ? 22 : 1, 30 + (n % 8));
416		}
417		memcpy(&dumb_prev_color, color, sizeof(t_colorspec));
418		break;
419	    }
420	    case TC_FRAC: {
421		rgb255_color rgb255;
422
423		if (dumb_prev_color.type == TC_FRAC && dumb_prev_color.value == color->value)
424		    break;
425		rgb255maxcolors_from_gray(color->value, &rgb255);
426		DUMB_rgb_color(rgb255);
427		memcpy(&dumb_prev_color, color, sizeof(t_colorspec));
428		break;
429	    }
430	    case TC_RGB: {
431		rgb255_color rgb255;
432
433		if (dumb_prev_color.type == TC_RGB && dumb_prev_color.lt == color->lt)
434		    break;
435		rgb255.r = (color->lt >> 16) & 0xff;
436		rgb255.g = (color->lt >>  8) & 0xff;
437		rgb255.b = (color->lt >>  0) & 0xff;
438		DUMB_rgb_color(rgb255);
439		memcpy(&dumb_prev_color, color, sizeof(t_colorspec));
440		break;
441	    }
442	    default:
443		break;
444	    }
445#endif
446#ifdef DUMB_UTF8
447	    c = (char *)(&dumb_matrix[dumb_xmax*y + x]);
448	    fputs(c, gpoutfile);
449#else
450	    fputc(DUMB_PIXEL(x, y), gpoutfile);
451#endif
452	}
453	if (dumb_feed || y > 0)
454	    putc('\n', gpoutfile);
455    }
456#ifndef NO_DUMB_COLOR_SUPPORT
457    if (dumb_colormode > 0) {
458	fputs("\033[0;39;49m", gpoutfile); /* reset colors to default */
459    }
460#endif
461    fflush(gpoutfile);
462}
463
464
465TERM_PUBLIC void
466DUMB_reset()
467{
468    free(dumb_matrix);
469    dumb_matrix = NULL;
470#ifndef NO_DUMB_COLOR_SUPPORT
471    free(dumb_colors);
472    dumb_colors = NULL;
473#endif
474}
475
476
477TERM_PUBLIC void
478DUMB_linetype(int linetype)
479{
480    static char pen_type[7] = { '*', '#', '$', '%', '@', '&', '=' };
481
482    if (linetype == LT_BLACK)
483	dumb_pen = DUMB_BORDER_CONST;
484    else if (linetype == LT_AXIS)
485	dumb_pen = DUMB_AXIS_CONST;
486    else if (linetype == LT_NODRAW)
487	dumb_pen = DUMB_NODRAW_CONST;
488    else if (linetype <= LT_NODRAW)
489	dumb_pen = ' ';
490    else {
491	linetype = linetype % 7;
492	dumb_pen = pen_type[linetype];
493    }
494
495#ifndef NO_DUMB_COLOR_SUPPORT
496    dumb_color.type = TC_LT;
497    dumb_color.lt = linetype;
498#endif
499}
500
501
502TERM_PUBLIC void
503DUMB_move(unsigned int x, unsigned int y)
504{
505    dumb_x = x;
506    dumb_y = y;
507}
508
509
510TERM_PUBLIC void
511DUMB_point(unsigned int x, unsigned int y, int point)
512{
513    dumb_set_pixel(x, y, point == -1 ? '.' : point % 26 + 'A');
514}
515
516
517TERM_PUBLIC void
518DUMB_vector(unsigned int arg_x, unsigned int arg_y)
519{
520    int x = arg_x;		/* we need signed int, since
521				 * unsigned-signed=unsigned and */
522    int y = arg_y;		/* abs and cast to double wouldn't work */
523    char pen, pen1;
524    int delta;
525
526    if (dumb_pen == DUMB_NODRAW_CONST) {
527	DUMB_move(x, y);
528	return;
529    }
530
531    if (ABS(y - dumb_y) > ABS(x - dumb_x)) {
532	switch (dumb_pen) {
533	case DUMB_AXIS_CONST:
534	    pen = ':';
535	    pen1 = '+';
536	    break;
537
538	case DUMB_BORDER_CONST:
539	    pen = '|';
540	    pen1 = '+';
541	    break;
542
543	case DUMB_FILL_CONST:
544	    pen = pen1 = 'X';
545	    break;
546
547	default:
548	    pen = dumb_pen;
549	    pen1 = dumb_pen;
550	    break;
551	}
552	dumb_set_pixel(dumb_x, dumb_y, pen1);
553	for (delta = 1; delta < ABS(y - dumb_y); delta++) {
554	    dumb_set_pixel(dumb_x  + (int) ((double) (x - dumb_x) *
555					    delta / ABS(y - dumb_y) + 0.5),
556			   dumb_y + delta * sign(y - dumb_y), pen);
557	}
558	dumb_set_pixel(x, y, pen1);
559    } else if (ABS(x - dumb_x) > ABS(y - dumb_y)) {
560	switch (dumb_pen) {
561	case DUMB_AXIS_CONST:
562	    pen = '.';
563	    pen1 = '+';
564	    break;
565
566	case DUMB_BORDER_CONST:
567	    pen = '-';
568	    pen1 = '+';
569	    break;
570
571	case DUMB_FILL_CONST:
572	    pen = pen1 = 'X';
573	    break;
574
575	default:
576	    pen = dumb_pen;
577	    pen1 = dumb_pen;
578	    break;
579	}
580	dumb_set_pixel(dumb_x, dumb_y, pen1);
581	for (delta = 1; delta < ABS(x - dumb_x); delta++)
582	    dumb_set_pixel(dumb_x + delta * sign(x - dumb_x),
583			   dumb_y +
584			   (int) ((double) (y - dumb_y) * delta / ABS(x - dumb_x) + 0.5),
585			   pen);
586	dumb_set_pixel(x, y, pen1);
587    } else {
588	switch (dumb_pen) {
589	case DUMB_AXIS_CONST:	/* zero length axis */
590	    pen = '+';
591	    break;
592
593	case DUMB_BORDER_CONST:	/* zero length border */
594	    pen = '+';
595	    break;
596
597	case DUMB_FILL_CONST:
598	    pen = '#';
599	    break;
600
601	default:
602	    pen = dumb_pen;
603	    break;
604	}
605	for (delta = 0; delta <= ABS(x - dumb_x); delta++)
606	    dumb_set_pixel(dumb_x + delta * sign(x - dumb_x),
607			   dumb_y + delta * sign(y - dumb_y),
608			   pen);
609    }
610    dumb_x = x;
611    dumb_y = y;
612}
613
614static void
615utf8_copy_one(char *dest, const char *orig)
616{
617    const char *nextchar = orig;
618    unsigned long wch;
619    *((charcell *)dest) = 0;	/* zero-fill */
620
621    if (encoding != S_ENC_UTF8) {
622	*dest = *orig;
623	return;
624    }
625
626    /* Valid UTF8 byte sequence */
627    if (utf8toulong(&wch, &nextchar)) {
628	while (orig < nextchar)
629	    *dest++ = *orig++;
630    } else {
631	int_warn(NO_CARET, "invalid UTF-8 byte sequence");
632	*dest++ = *orig++;
633    }
634}
635
636TERM_PUBLIC void
637DUMB_put_text(unsigned int x, unsigned int y, const char *str)
638{
639    int i, length;
640
641    if (y > dumb_ymax)
642	return;
643
644    length = gp_strlen(str);
645    if (x + length > dumb_xmax)
646	x = GPMAX(0, dumb_xmax - length);
647
648#ifdef DUMB_UTF8
649    for (i = 0; i < length && x < dumb_xmax; i++, x++) {
650	utf8_copy_one( (char *)(&DUMB_PIXEL(x, y)), gp_strchrn(str,i));
651#ifndef NO_DUMB_COLOR_SUPPORT
652	memcpy(&dumb_colors[dumb_xmax * y + x], &dumb_color, sizeof(t_colorspec));
653#endif
654    }
655#else
656    for (; x < dumb_xmax && *str; x++, str++)
657	dumb_set_pixel(x, y, *str, 5);
658#endif
659}
660
661
662TERM_PUBLIC void
663DUMB_arrow(
664    unsigned int usx, unsigned int usy,
665    unsigned int uex, unsigned int uey,
666    int head)	/* mostly ignored */
667{
668    /* we have GOT to ditch this unsigned coord madness! */
669    int sx = (int)(usx);
670    int sy = (int)(usy);
671    int ex = (int)(uex);
672    int ey = (int)(uey);
673
674    char saved_pen;
675    char saved_x;
676    char saved_y;
677
678    saved_pen = dumb_pen;
679    saved_x = dumb_x;
680    saved_y = dumb_y;
681
682    /* Arrow shaft */
683    if (ex == sx) dumb_pen = '|';
684    else if (ey == sy) dumb_pen = '-';
685    else dumb_pen = '.';
686    dumb_x = sx;
687    dumb_y = sy;
688    if (!(head & HEADS_ONLY))
689	DUMB_vector(ex, ey);
690
691    /* Arrow tail */
692    if ((head & BACKHEAD)) {
693	char tailsym;
694	if (ex > sx) tailsym = '<';
695	else if (ex < sx) tailsym = '>';
696	else if (ey > sy) tailsym = 'v';
697	else tailsym = '^';
698	dumb_set_pixel(sx, sy, tailsym);
699    }
700
701    /* Arrow head */
702    if ((head & END_HEAD)) {
703	char headsym;
704	if (ex > sx) headsym = '>';
705	else if (ex < sx) headsym = '<';
706	else if (ey > sy) headsym = '^';
707	else headsym = 'v';
708	dumb_set_pixel(ex, ey, headsym);
709    }
710
711    dumb_pen = saved_pen;
712    dumb_x = saved_x;
713    dumb_y = saved_y;
714}
715
716
717#ifndef NO_DUMB_ENHANCED_SUPPORT
718/*
719 * The code from here on serves as an example of how to
720 * add enhanced text mode support to even a dumb driver.
721 */
722
723static TBOOLEAN ENHdumb_opened_string;
724static TBOOLEAN ENHdumb_show = TRUE;
725static int ENHdumb_overprint = 0;
726static TBOOLEAN ENHdumb_widthflag = TRUE;
727static unsigned int ENHdumb_xsave, ENHdumb_ysave;
728#define ENHdumb_fontsize 1
729#define ENHdumb_font ""
730static double ENHdumb_base;
731
732TERM_PUBLIC void
733ENHdumb_OPEN(
734    char *fontname,
735    double fontsize, double base,
736    TBOOLEAN widthflag, TBOOLEAN showflag,
737    int overprint)
738{
739    /* There are two special cases:
740     * overprint = 3 means save current position
741     * overprint = 4 means restore saved position
742     */
743    if (overprint == 3) {
744	ENHdumb_xsave = dumb_x;
745	ENHdumb_ysave = dumb_y;
746	return;
747    } else if (overprint == 4) {
748	DUMB_move(ENHdumb_xsave, ENHdumb_ysave);
749	return;
750    }
751
752
753    if (!ENHdumb_opened_string) {
754	ENHdumb_opened_string = TRUE;
755	/* Start new text fragment */
756	    enhanced_cur_text = &enhanced_text[0];
757	/* Scale fractional font height to vertical units of display */
758	    ENHdumb_base = base * 2 / fontsize;
759	/* Keep track of whether we are supposed to show this string */
760	    ENHdumb_show = showflag;
761	/* 0/1/2  no overprint / 1st pass / 2nd pass */
762	    ENHdumb_overprint = overprint;
763	/* widthflag FALSE means do not update text position after printing */
764	    ENHdumb_widthflag = widthflag;
765	/* Many drivers will need to do something about font selection here */
766	    /* but dumb is dumb */
767    }
768}
769
770TERM_PUBLIC void
771ENHdumb_FLUSH()
772{
773    char *str = enhanced_text;	/* The fragment to print */
774    int x = dumb_x;		/* The current position  */
775    int y = dumb_y + (int)ENHdumb_base;
776    int i, len;
777
778    if (ENHdumb_opened_string) {
779	*enhanced_cur_text = '\0';
780	len = gp_strlen(str);
781
782	/* print the string fragment, perhaps invisibly */
783	/* NB: base expresses offset from current y pos */
784	if (ENHdumb_show && y < dumb_ymax) {
785#ifdef DUMB_UTF8
786	    for (i = 0; i < len && x < dumb_xmax; i++, x++) {
787		utf8_copy_one( (char *)(&DUMB_PIXEL(x, y)), gp_strchrn(str,i));
788#ifndef NO_DUMB_COLOR_SUPPORT
789		memcpy(&dumb_colors[dumb_xmax * y + x], &dumb_color, sizeof(t_colorspec));
790#endif
791	    }
792#else
793	    for (; x < dumb_xmax && *str; x++, str++)
794		dumb_set_pixel(x, y, *str, 5);
795#endif
796	}
797
798	if (!ENHdumb_widthflag)
799	    /* don't update position */
800	    ;
801	else if (ENHdumb_overprint == 1)
802	    /* First pass of overprint, leave position in center of fragment */
803	    dumb_x += len / 2;
804	else
805	    /* Normal case is to update position to end of fragment */
806	    dumb_x += len;
807
808	ENHdumb_opened_string = FALSE;
809    }
810}
811
812TERM_PUBLIC void
813ENHdumb_put_text(unsigned int x, unsigned int y, const char *str)
814{
815    int length;
816
817    /* If no enhanced text processing is needed, we can use the plain  */
818    /* vanilla put_text() routine instead of this fancy recursive one. */
819    if (ignore_enhanced_text || (!strpbrk(str, "{}^_@&~") && !contains_unicode(str))) {
820	DUMB_put_text(x,y,str);
821	return;
822    }
823
824    length = estimate_strlen(str);
825    if (x + length > dumb_xmax)
826	x = GPMAX(0, dumb_xmax - length);
827    if (y > dumb_ymax)
828	return;
829
830    /* Set up global variables needed by enhanced_recursion() */
831    enhanced_fontscale = 1.0;
832    ENHdumb_opened_string = FALSE;
833    strncpy(enhanced_escape_format,"%c",sizeof(enhanced_escape_format));
834
835    DUMB_move(x,y);
836
837    /* Set the recursion going. We say to keep going until a
838     * closing brace, but we don't really expect to find one.
839     * If the return value is not the nul-terminator of the
840     * string, that can only mean that we did find an unmatched
841     * closing brace in the string. We increment past it (else
842     * we get stuck in an infinite loop) and try again.
843     */
844    while (*(str = enhanced_recursion((char *)str, TRUE,
845    			ENHdumb_font, ENHdumb_fontsize,
846			0.0, TRUE, TRUE, 0))) {
847	(term->enhanced_flush)();
848
849	/* I think we can only get here if *str == '}' */
850	    enh_err_check(str);
851
852	if (!*++str)
853	    break; /* end of string */
854
855	/* else carry on and process the rest of the string */
856    }
857}
858#endif /* NO_DUMB_ENHANCED_SUPPORT */
859
860
861#ifndef NO_DUMB_COLOR_SUPPORT
862TERM_PUBLIC int
863dumb_make_palette(t_sm_palette *palette)
864{
865    /* report continuous colors */
866    return 0;
867}
868
869
870TERM_PUBLIC void
871dumb_set_color(t_colorspec *colorspec)
872{
873    memcpy(&dumb_color, colorspec, sizeof(t_colorspec));
874}
875#endif
876
877
878static int
879dumb_float_compare(const void * elem1, const void * elem2)
880{
881	int val = *(float *)elem1 - *(float *)elem2;
882	return (0 < val) - (val < 0);
883}
884
885/* adopted copy from caca.trm */
886TERM_PUBLIC void
887dumb_filled_polygon(int points, gpiPoint *corners)
888{
889    char save_pen;
890
891    /* Eliminate duplicate polygon points. */
892    if ((corners[0].x == corners[points - 1].x) && (corners[0].y == corners[points - 1].y))
893	points--;
894    /* Need at least three remaining points */
895    if (points < 3)
896	return;
897
898    /* temporarily change pen */
899    save_pen = dumb_pen;
900    dumb_pen = DUMB_FILL_CONST;
901
902    {
903	/* ----------------------------------------------------------------
904	 * Derived from
905	 *  public-domain code by Darel Rex Finley, 2007
906	 *  http://alienryderflex.com/polygon_fill/
907	 * ---------------------------------------------------------------- */
908	int nodes;
909	float * nodeX;
910	int pixelY;
911	int i, j;
912	int ymin = dumb_ymax, ymax = 0;
913	int xmin = dumb_xmax, xmax = 0;
914
915	/* Find bounding box */
916	for (i = 0; i < points; i++) {
917	     if (corners[i].x < xmin) xmin = corners[i].x;
918	     if (corners[i].x > xmax) xmax = corners[i].x;
919	     if (corners[i].y < ymin) ymin = corners[i].y;
920	     if (corners[i].y > ymax) ymax = corners[i].y;
921	}
922
923	/* Dynamically allocate node list. */
924	nodeX = (float *) gp_alloc(sizeof(* nodeX) * points, "nodeX");
925
926	/* Loop through the rows of the image. */
927	for (pixelY = ymin; pixelY <= ymax + 1; pixelY++) {
928	    /* Build a sorted list of nodes. */
929	    nodes = 0;
930	    j = points - 1;
931	    for (i = 0; i < points; i++) {
932		if (((corners[i].y < pixelY) && (corners[j].y >= pixelY)) ||
933		    ((corners[j].y < pixelY) && (corners[i].y >= pixelY))) {
934			nodeX[nodes++] = (corners[i].x +
935			                  + (double) (pixelY - corners[i].y)
936			                  / (double) (corners[j].y - corners[i].y)
937			                  * (double) (corners[j].x - corners[i].x));
938		}
939		j = i;
940	    }
941	    qsort(nodeX, nodes, sizeof(float), dumb_float_compare);
942
943	   /* Fill the pixels between node pairs. */
944	   for (i = 0; i < nodes; i += 2) {
945		if (nodeX[i] > xmax)
946		    break;
947		if (nodeX[i + 1] >= 0) {
948		    /* TODO: Are these checks ever required? */
949		     if (nodeX[i] < xmin)
950			nodeX[i] = xmin;
951		    if (nodeX[i + 1] > xmax)
952			nodeX[i + 1] = xmax;
953		    /* skip lines with zero length */
954		    if (nodeX[i + 1] - nodeX[i] < 0.5)
955			continue;
956		    DUMB_move((int)(nodeX[i] + 0.5), pixelY);
957		    DUMB_vector((int)(nodeX[i + 1]), pixelY);
958		}
959	    }
960	}
961
962	/* cleanup */
963	free(nodeX);
964	/* ---------------------------------------------------------------- */
965    }
966
967    /* restore pen */
968    dumb_pen = save_pen;
969}
970
971
972#endif /* TERM_BODY */
973
974#ifdef TERM_TABLE
975TERM_TABLE_START(dumb_driver)
976    "dumb", "ascii art for anything that prints text",
977    DUMB_XMAX, DUMB_YMAX, 1, 1,
978    1, 2, /* account for typical aspect ratio of characters */
979    DUMB_options, DUMB_init, DUMB_reset,
980    DUMB_text, null_scale, DUMB_graphics, DUMB_move, DUMB_vector,
981    DUMB_linetype, DUMB_put_text, null_text_angle,
982    null_justify_text, DUMB_point, DUMB_arrow, set_font_null,
983    0,				/* pointsize */
984    TERM_CAN_MULTIPLOT,
985    NULL, NULL, NULL, NULL,
986#ifdef USE_MOUSE
987    NULL, NULL, NULL, NULL, NULL,
988#endif
989    NULL,	/* Color support sets this to dumb_make_palette */
990    NULL, 	/* previous_palette */
991    NULL,	/* Color support sets this to dumb_set_color */
992    dumb_filled_polygon, /* filled_polygon */
993    NULL, /* image */
994#ifndef NO_DUMB_ENHANCED_SUPPORT
995    ENHdumb_OPEN, ENHdumb_FLUSH, do_enh_writec
996#endif /* NO_DUMB_ENHANCED_SUPPORT */
997TERM_TABLE_END(dumb_driver)
998
999#undef LAST_TERM
1000#define LAST_TERM dumb_driver
1001
1002#endif /* TERM_TABLE */
1003
1004#ifdef TERM_HELP
1005START_HELP(dumb)
1006"1 dumb",
1007"?commands set terminal dumb",
1008"?set terminal dumb",
1009"?set term dumb",
1010"?terminal dumb",
1011"?term dumb",
1012"?dumb",
1013" The `dumb` terminal driver plots into a text block using ascii characters.",
1014" It has an optional size specification and a trailing linefeed flag.",
1015"",
1016" Syntax:",
1017"       set terminal dumb {size <xchars>,<ychars>} {[no]feed}",
1018"                         {aspect <htic>{,<vtic>}}",
1019#ifndef NO_DUMB_ENHANCED_SUPPORT
1020"                         {[no]enhanced}",
1021#endif
1022#ifndef NO_DUMB_COLOR_SUPPORT
1023"                         {mono|ansi|ansi256|ansirgb}",
1024#endif
1025"",
1026" where <xchars> and <ychars> set the size of the text block. The default is",
1027" 79 by 24. The last newline is printed only if `feed` is enabled.",
1028"",
1029" The `aspect` option can be used to control the aspect ratio of the plot by",
1030" setting the length of the horizontal and vertical tic marks. Only integer",
1031" values are allowed. Default is 2,1 -- corresponding to the aspect ratio of",
1032" common screen fonts.",
1033"",
1034#ifndef NO_DUMB_COLOR_SUPPORT
1035" The `ansi`, `ansi256`, and `ansirgb` options will include escape",
1036" sequences in the output to handle colors.  Note that these might",
1037" not be handled by your terminal.  Default is `mono`.",
1038" To obtain the best color match in `ansi` mode, you should use",
1039" `set colorsequence classic`.",
1040" Depending on the mode, the `dumb` terminal will emit the",
1041" following sequences (without the additional whitespace):",
1042"",
1043"       ESC [ 0 m           reset attributes to defaults",
1044"       foreground color:",
1045"       ESC [ 1 m           set intense/bold",
1046"       ESC [ 22 m          intense/bold off",
1047"       ESC [ <fg> m        with color code 30 <= <fg> <= 37",
1048"       ESC [ 39 m          reset to default",
1049"       ESC [ 38; 5; <c> m  with palette index 16 <= <c> <= 255",
1050"       ESC [ 38; 2; <r>; <g>; <b> m  with components 0 <= <r,g,b> <= 255",
1051"       background color:",
1052"       ESC [ <bg> m        with color code 40 <= <bg> <= 47",
1053"       ESC [ 49 m          reset to default",
1054"       ESC [ 48; 5; <c> m  with palette index 16 <= <c> <= 231",
1055"       ESC [ 48; 2; <r>; <g>; <b> m  with components 0 <= <r,g,b> <= 255",
1056"",
1057" See also e.g. the description at",
1058"^ <a href=\"https://en.wikipedia.org/wiki/ANSI_escape_code#Colors\">",
1059"           https://en.wikipedia.org/wiki/ANSI_escape_code#Colors",
1060"^ </a>",
1061"",
1062#endif
1063" Example:",
1064"       set term dumb mono size 60,15 aspect 1",
1065"       set tics nomirror scale 0.5",
1066"       plot [-5:6.5] sin(x) with impulse ls -1",
1067"",
1068"           1 +-------------------------------------------------+",
1069"         0.8 +|||++                   ++||||++                 |",
1070"         0.6 +|||||+                 ++|||||||+  sin(x) +----+ |",
1071"         0.4 +||||||+               ++|||||||||+               |",
1072"         0.2 +|||||||+             ++|||||||||||+             +|",
1073"           0 ++++++++++++++++++++++++++++++++++++++++++++++++++|",
1074"        -0.2 +        +|||||||||||+              +|||||||||||+ |",
1075"        -0.4 +         +|||||||||+                +|||||||||+  |",
1076"        -0.6 +          +|||||||+                  +|||||||+   |",
1077"        -0.8 +           ++||||+                    ++||||+    |",
1078"          -1 +---+--------+--------+-------+--------+--------+-+",
1079"                -4       -2        0       2        4        6  "
1080END_HELP(dumb)
1081#endif /* TERM_HELP */
1082