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