/* Hello, Emacs, this is -*-C-*- */
/* GNUPLOT - dumb.trm */
/*[
* Copyright 1991 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
]*/
/*
* This file is included by ../term.c.
*
* This terminal driver supports:
* DUMB terminals
*
* AUTHORS
* Francois Pinard, 91-04-03
* INTERNET: pinard@iro.umontreal.ca
*
* Ethan A Merritt Nov 2003
* Added support for enhanced text mode.
* Yes, this is frivolous, but it serves as an example for
* adding enhanced text to other terminals. You can disable
* it by adding a line
* #define NO_DUMB_ENHANCED_SUPPORT
*
* Bastian Maerkisch Nov 2016
* ANSI color support. Filled polygons.
*
* Bastian Maerkisch Jul 2021
* Filled boxes.
* Bold/Italic escape sequences.
* (Partial) wide character support.
* Wide characters are problematic as they would destroy the layout. We
* handle this by marking subsequent cells as "occupied" with NUL. These
* cells do not get printed. Subsequent calls to set_pixel() also blank
* the cell to the left, if the cell is marked as occupied, and the one to
* the right if they overwrite a wide character.
* Note that is_wide_char() uses the same incomplete logic as mbwidth in
* readline.c and strwidth_utf8() in estimate.trm.
*
* Ethan A Merritt Dec 2021
* fillchar
*
* send your comments or suggestions to (gnuplot-info@lists.sourceforge.net).
*
*/
#include "driver.h"
#ifdef TERM_REGISTER
register_term(dumb_driver)
#endif
#ifdef TERM_PROTO
TERM_PUBLIC void DUMB_options(void);
TERM_PUBLIC void DUMB_init(void);
TERM_PUBLIC void DUMB_graphics(void);
TERM_PUBLIC void DUMB_text(void);
TERM_PUBLIC void DUMB_reset(void);
TERM_PUBLIC void DUMB_linetype(int linetype);
TERM_PUBLIC void DUMB_move(unsigned int x, unsigned int y);
TERM_PUBLIC void DUMB_point(unsigned int x, unsigned int y, int point);
TERM_PUBLIC void DUMB_vector(unsigned int x, unsigned int y);
TERM_PUBLIC void DUMB_put_text(unsigned int x, unsigned int y, const char *str);
TERM_PUBLIC void DUMB_arrow(unsigned int sx, unsigned int sy,
unsigned int ex, unsigned int ey,
int head);
TERM_PUBLIC int DUMB_set_font(const char *s);
#ifndef NO_DUMB_ENHANCED_SUPPORT
/* To support "set term dumb enhanced" (don't ask why!) */
TERM_PUBLIC void ENHdumb_put_text(unsigned int x, unsigned int y, const char str[]);
TERM_PUBLIC void ENHdumb_OPEN(char * fontname, double fontsize,
double base, TBOOLEAN widthflag, TBOOLEAN showflag,
int overprint);
TERM_PUBLIC void ENHdumb_FLUSH(void);
#else
#define ENHdumb_put_text NULL
#endif
#ifndef NO_DUMB_COLOR_SUPPORT
TERM_PUBLIC int DUMB_make_palette(t_sm_palette *palette);
TERM_PUBLIC void DUMB_set_color(t_colorspec *);
#endif
TERM_PUBLIC void DUMB_fillbox(int style, unsigned int x, unsigned int y,
unsigned int w, unsigned int h);
TERM_PUBLIC void DUMB_filled_polygon(int points, gpiPoint *corners);
#define DUMB_XMAX 79
#define DUMB_YMAX 24
#endif /* TERM_PROTO */
#ifdef TERM_BODY
#define DUMB_AXIS_CONST '\1'
#define DUMB_BORDER_CONST '\2'
#define DUMB_FILL_CONST '\3'
#define DUMB_NODRAW_CONST '\4'
#ifdef HAVE_STDINT_H
#include
#endif
#include "readline.h"
/* UTF-8 support */
typedef int32_t charcell;
/* text attribute support */
typedef struct _text_attr {
unsigned char bold : 1;
unsigned char italic : 1;
unsigned char superscript : 1;
unsigned char subscript : 1;
unsigned char font : 4;
} text_attr;
/* matrix of characters */
static charcell *dumb_matrix = NULL;
#ifndef NO_DUMB_COLOR_SUPPORT
/* matrix of colors */
static t_colorspec *dumb_colors = NULL;
static t_colorspec dumb_color;
static t_colorspec dumb_prev_color;
#endif
static text_attr *dumb_attributes = NULL;
static text_attr dumb_attr;
/* current character used to draw */
static char dumb_pen;
/* current X position */
static int dumb_x;
/* current Y position */
static int dumb_y;
static int dumb_xmax = DUMB_XMAX;
static int dumb_ymax = DUMB_YMAX;
static TBOOLEAN dumb_feed = TRUE;
static int dumb_colormode = 0;
static TBOOLEAN dumb_text_attributes = FALSE;
static char *dumb_fillchar = NULL;
#define DUMB_PIXEL(x,y) dumb_matrix[dumb_xmax*(y)+(x)]
const char * dumb_attrstring(text_attr * attr, text_attr * prev_text_attr);
const char * ansi_colorstring(t_colorspec * color, t_colorspec * prev_color);
static void dumb_set_pixel(int x, int y, int v);
static unsigned nearest_ansi(rgb255_color rgb255);
static void dumb_rgb_color(rgb255_color rgb255, char * colorstring);
enum DUMB_id { DUMB_FEED, DUMB_NOFEED, DUMB_ENH, DUMB_NOENH,
DUMB_SIZE, DUMB_ASPECT,
DUMB_ANSI, DUMB_ANSI256, DUMB_ANSIRGB, DUMB_NOCOLOR,
DUMB_ATTRIBUTES, DUMB_NOATTRIBUTES,
DUMB_FILLCHAR, DUMB_OTHER };
static struct gen_table DUMB_opts[] =
{
{ "f$eed", DUMB_FEED },
{ "nof$eed", DUMB_NOFEED },
{ "enh$anced", DUMB_ENH },
{ "noe$nhanced", DUMB_NOENH },
{ "size", DUMB_SIZE },
{ "aspect", DUMB_ASPECT },
{ "ansi", DUMB_ANSI },
{ "ansi256", DUMB_ANSI256 },
{ "ansirgb", DUMB_ANSIRGB },
{ "attr$ibutes", DUMB_ATTRIBUTES },
{ "noattr$ibutes", DUMB_NOATTRIBUTES },
{ "mono", DUMB_NOCOLOR },
{ "fill$char", DUMB_FILLCHAR },
{ NULL, DUMB_OTHER }
};
TERM_PUBLIC void
DUMB_options()
{
int x, y;
int cmd;
TBOOLEAN set_size = FALSE;
while (!END_OF_COMMAND) {
switch ((cmd = lookup_table(&DUMB_opts[0], c_token))) {
case DUMB_FEED:
c_token++;
dumb_feed = TRUE;
break;
case DUMB_NOFEED:
c_token++;
dumb_feed = FALSE;
break;
#ifndef NO_DUMB_ENHANCED_SUPPORT
case DUMB_ENH:
c_token++;
term->put_text = ENHdumb_put_text;
term->flags |= TERM_ENHANCED_TEXT;
break;
case DUMB_NOENH:
c_token++;
term->put_text = DUMB_put_text;
term->flags &= ~TERM_ENHANCED_TEXT;
break;
#endif
case DUMB_ASPECT:
c_token++;
x = int_expression();
y = 1;
if (!END_OF_COMMAND && equals(c_token, ",")) {
c_token++;
y = int_expression();
}
if (x v_tic = y;
break;
case DUMB_ANSI:
case DUMB_ANSI256:
case DUMB_ANSIRGB:
c_token++;
dumb_colormode = cmd;
#ifndef NO_DUMB_COLOR_SUPPORT
term->make_palette = DUMB_make_palette;
term->set_color = DUMB_set_color;
#endif
break;
case DUMB_NOCOLOR:
c_token++;
dumb_colormode = 0;
term->make_palette = NULL;
term->set_color = null_set_color;
break;
case DUMB_ATTRIBUTES:
c_token++;
dumb_text_attributes = TRUE;
break;
case DUMB_NOATTRIBUTES:
c_token++;
dumb_text_attributes = FALSE;
break;
case DUMB_FILLCHAR:
c_token++;
free(dumb_fillchar);
dumb_fillchar = NULL;
if (equals(c_token, "solid")) {
c_token++;
/* We mimic the way a user would have typed it in gnuplot syntax,
* not the C standard form "\u2588".
*/
dumb_fillchar = strdup("\\U+2588");
} else {
dumb_fillchar = try_to_get_string();
}
if (dumb_fillchar)
truncate_to_one_utf8_char(dumb_fillchar);
break;
case DUMB_SIZE:
c_token++;
/* Fall through */
case DUMB_OTHER:
default:
if (set_size) {
int_warn(c_token++,"unrecognized option");
break;
}
x = int_expression();
if (x 1024)
x = DUMB_XMAX;
if (!END_OF_COMMAND) {
if (equals(c_token,","))
c_token++;
y = int_expression();
if (y 1024)
y = DUMB_YMAX;
dumb_xmax = term->xmax = x;
dumb_ymax = term->ymax = y;
}
set_size = TRUE;
break;
}
}
{
const char * coloropts[] = {"mono", "ansi", "ansi256", "ansirgb"};
char fillopt[24];
sprintf(term_options, "%sfeed %s size %d, %d aspect %i, %i %s %sattributes",
dumb_feed ? "" : "no",
term->put_text == ENHdumb_put_text ? "enhanced" : "",
dumb_xmax, dumb_ymax,
term->h_tic, term->v_tic,
coloropts[dumb_colormode == 0 ? 0 : dumb_colormode - DUMB_ANSI + 1],
dumb_text_attributes ? "" : "no"
);
if (dumb_fillchar && *dumb_fillchar) {
sprintf(fillopt, " fillchar \"%s\"", dumb_fillchar);
strcat(term_options, fillopt);
}
}
}
static TBOOLEAN
is_wide_char(unsigned char *charpixel)
{
/* see also mbwidth() in readline.c */
if (encoding == S_ENC_UTF8)
return (*charpixel >= 0xe3);
else
return FALSE;
}
static void
dumb_set_pixel(int x, int y, int v)
{
unsigned char *charpixel;
if ((unsigned int) x =0 && x 0) & 0xf;
b = (rgb255.r >> 4) & 0xf;
d += (a - b) * (a - b);
a = (ansitab16[i] >> 4) & 0xf;
b = (rgb255.g >> 4) & 0xf;
d += (a - b) * (a - b);
a = (ansitab16[i] >> 8) & 0xf;
b = (rgb255.b >> 4) & 0xf;
d += (a - b) * (a - b);
if (d < dist) {
dist = d;
best = i;
}
}
return best;
}
/* end of libcaca code */
static unsigned
to_ansi256(rgb255_color *c)
{
if ((c->r - 8) / 10 == (c->b - 8) / 10 && (c->r - 8) / 10 == (c->g - 8) / 10) {
/* gray scale */
if (c->g < 8) /* black */
return 16;
if (c->g >= 238) /* white */
return 231;
return (c->g - 8) / 10 + 232; /* like XTerm, Mintty */
} else {
/* 6x6x6 color cube */
#define RMAPCUBE6(n) ((n >= 55) ? ((n) - 35) / 40 : 0)
return (((unsigned) RMAPCUBE6(c->r) * 36) +
((unsigned) RMAPCUBE6(c->g) * 6) +
((unsigned) RMAPCUBE6(c->b))) + 16;
}
}
static void
dumb_rgb_color(rgb255_color rgb255, char * colorstring)
{
switch (dumb_colormode) {
case DUMB_ANSI: {
unsigned color = nearest_ansi(rgb255);
sprintf(colorstring, "\033[%i;%im", color >= 8 ? 1 : 22, 30 + (color % 8));
break;
}
case DUMB_ANSI256:
sprintf(colorstring, "\033[38;5;%im", to_ansi256(&rgb255));
break;
case DUMB_ANSIRGB:
sprintf(colorstring, "\033[38;2;%i;%i;%im", rgb255.r, rgb255.g, rgb255.b);
break;
}
}
const char *
ansi_colorstring(t_colorspec * color, t_colorspec * prev_color)
{
static char colorstring[256];
colorstring[0] = NUL;
switch (color->type) {
case TC_LT: {
int n;
if (dumb_colormode < DUMB_ANSI)
break;
if (prev_color != NULL && prev_color->type == TC_LT && prev_color->lt == color->lt)
break;
n = color->lt + 1;
/* map line type to colors */
if (n 15) n = ((n - 1) % 15) + 1;
sprintf(colorstring, "\033[%i;%im", n >= 8 ? 1 : 22, 30 + (n % 8));
}
break;
}
case TC_FRAC: {
rgb255_color rgb255;
if (prev_color != NULL && prev_color->type == TC_FRAC && prev_color->value == color->value)
break;
rgb255maxcolors_from_gray(color->value, &rgb255);
dumb_rgb_color(rgb255, colorstring);
break;
}
case TC_RGB: {
rgb255_color rgb255;
if (prev_color != NULL && prev_color->type == TC_RGB && prev_color->lt == color->lt)
break;
rgb255.r = (color->lt >> 16) & 0xff;
rgb255.g = (color->lt >> 8) & 0xff;
rgb255.b = (color->lt >> 0) & 0xff;
dumb_rgb_color(rgb255, colorstring);
break;
}
default:
break;
}
return colorstring;
}
#endif
const char *
dumb_attrstring(text_attr * attr, text_attr * prev_text_attr)
{
static char attrstr[64];
attrstr[0] = NUL;
if ((attr->bold != prev_text_attr->bold) && (attr->italic != prev_text_attr->italic))
sprintf(attrstr, "\033[%i;%im", attr->bold ? 1 : 22, attr->italic ? 3 : 23);
else if (attr->bold != prev_text_attr->bold)
sprintf(attrstr, "\033[%im", attr->bold ? 1 : 22);
else if (attr->italic != prev_text_attr->italic)
sprintf(attrstr, "\033[%im", attr->italic ? 3 : 23);
return attrstr;
}
TERM_PUBLIC void
DUMB_text()
{
int x, y, i;
text_attr prev_text_attr;
putc('\f', gpoutfile);
#ifndef NO_DUMB_COLOR_SUPPORT
if (dumb_colormode > 0) {
fputs("\033[0;39m", gpoutfile); /* reset colors to default */
memset(&dumb_prev_color, 0, sizeof(t_colorspec));
}
#endif
if (dumb_text_attributes) {
fputs("\033[22;23m", gpoutfile); /* reset attributes */
memset(&prev_text_attr, 0, sizeof(text_attr));
}
for (y = dumb_ymax - 1; y >= 0; y--) {
for (x = 0; x < dumb_xmax; x++) {
char *c;
#ifndef NO_DUMB_COLOR_SUPPORT
t_colorspec * color = &dumb_colors[dumb_xmax*y + x];
const char * colorstring = ansi_colorstring(color, &dumb_prev_color);
if (colorstring[0] != NUL) {
fputs(colorstring, gpoutfile);
memcpy(&dumb_prev_color, color, sizeof(t_colorspec));
}
#endif
c = (char *)(&DUMB_PIXEL(x, y));
if (*c != NUL) { /* Do not print cells occupied by wide characters. */
/* character attributes */
if (dumb_text_attributes) {
text_attr * attr = &dumb_attributes[dumb_xmax*y + x];
const char * attrstring = dumb_attrstring(attr, &prev_text_attr);
if (attrstring[0] != NUL) {
fputs(attrstring, gpoutfile);
memcpy(&prev_text_attr, attr, sizeof(text_attr));
}
}
/* The UTF-8 character might be four bytes long and so there's
no guarantee that the charcell ends in a NUL. */
for (i = 0; i < sizeof(charcell) && *c != NUL; i++, c++)
fputc(*c, gpoutfile);
}
}
if (dumb_feed || y > 0)
putc('\n', gpoutfile);
}
#ifndef NO_DUMB_COLOR_SUPPORT
if (dumb_text_attributes)
fputs("\033[22;23m", gpoutfile); /* reset attributes */
if (dumb_colormode > 0)
fputs("\033[0;39;49m", gpoutfile); /* reset colors to default */
#endif
fflush(gpoutfile);
}
TERM_PUBLIC void
DUMB_reset()
{
free(dumb_matrix);
dumb_matrix = NULL;
#ifndef NO_DUMB_COLOR_SUPPORT
free(dumb_colors);
dumb_colors = NULL;
#endif
free(dumb_attributes);
dumb_attributes = NULL;
}
TERM_PUBLIC void
DUMB_linetype(int linetype)
{
static char pen_type[7] = { '*', '#', '$', '%', '@', '&', '=' };
if (linetype == LT_BLACK)
dumb_pen = DUMB_BORDER_CONST;
else if (linetype == LT_AXIS)
dumb_pen = DUMB_AXIS_CONST;
else if (linetype == LT_NODRAW)
dumb_pen = DUMB_NODRAW_CONST;
else if (linetype ';
else if (ex < sx) headsym = ' dumb_xmax)
x = GPMAX(0, dumb_xmax - length);
if (y > dumb_ymax)
return;
/* Set up global variables needed by enhanced_recursion() */
enhanced_fontscale = 1.0;
ENHdumb_opened_string = FALSE;
strncpy(enhanced_escape_format, "%c", sizeof(enhanced_escape_format));
DUMB_move(x,y);
/* Set the recursion going. We say to keep going until a
* closing brace, but we don't really expect to find one.
* If the return value is not the nul-terminator of the
* string, that can only mean that we did find an unmatched
* closing brace in the string. We increment past it (else
* we get stuck in an infinite loop) and try again.
*/
while (*(str = enhanced_recursion((char *)str, TRUE,
ENHdumb_font, ENHdumb_fontsize,
0.0, TRUE, TRUE, 0))) {
(term->enhanced_flush)();
/* I think we can only get here if *str == '}' */
enh_err_check(str);
if (!*++str)
break; /* end of string */
/* else carry on and process the rest of the string */
}
DUMB_set_font("");
}
#endif /* NO_DUMB_ENHANCED_SUPPORT */
TERM_PUBLIC int
DUMB_set_font(const char *s)
{
/* reset current attributes */
if (dumb_text_attributes)
memset(&dumb_attr, 0, sizeof(text_attr));
if (s == NULL || *s == NUL || !dumb_text_attributes)
return TRUE;
/* test for attributes */
if ((strstr(s, ":Bold") != NULL))
dumb_attr.bold = 1;
if ((strstr(s, ":Italic") != NULL))
dumb_attr.italic = 1;
return TRUE;
}
#ifndef NO_DUMB_COLOR_SUPPORT
TERM_PUBLIC int
DUMB_make_palette(t_sm_palette *palette)
{
/* report continuous colors */
return 0;
}
TERM_PUBLIC void
DUMB_set_color(t_colorspec *colorspec)
{
memcpy(&dumb_color, colorspec, sizeof(t_colorspec));
}
#endif
TERM_PUBLIC void
DUMB_fillbox(int style, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
{
unsigned int nx, ny;
char *fillchar = (dumb_fillchar) ? dumb_fillchar : "#";
for (ny = y; ny < y + GPMAX(h, 1); ny++) {
for(nx = x; nx xmax) xmax = corners[i].x;
if (corners[i].y < ymin) ymin = corners[i].y;
if (corners[i].y > ymax) ymax = corners[i].y;
}
/* Dynamically allocate node list. */
nodeX = (float *) gp_alloc(sizeof(* nodeX) * points, "nodeX");
/* Loop through the rows of the image. */
for (pixelY = ymin; pixelY = pixelY)) ||
((corners[j].y < pixelY) && (corners[i].y >= pixelY))) {
nodeX[nodes++] = (corners[i].x +
+ (double) (pixelY - corners[i].y)
/ (double) (corners[j].y - corners[i].y)
* (double) (corners[j].x - corners[i].x));
}
j = i;
}
qsort(nodeX, nodes, sizeof(float), dumb_float_compare);
/* Fill the pixels between node pairs. */
for (i = 0; i < nodes; i += 2) {
unsigned int nx;
if (nodeX[i] > xmax)
break;
if (nodeX[i + 1] >= 0) {
/* TODO: Are these checks ever required? */
if (nodeX[i] < xmin)
nodeX[i] = xmin;
if (nodeX[i + 1] > xmax)
nodeX[i + 1] = xmax;
/* skip lines with zero length */
if (nodeX[i + 1] - nodeX[i] < 0.5)
continue;
for (nx = (int)(nodeX[i] + 0.5); nx