/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
* GNUPLOT - caca.trm
*
* This file uses UTF8 encoding and a tab size of 4.
*
*/
/*
Copyright (c) 2012-2014 Bastian Maerkisch. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef TERM_REGISTER
register_term(caca)
#endif
#ifdef TERM_PROTO
#define CACA_XMAX (80-1)
#define CACA_YMAX (25-1)
TERM_PUBLIC void CACA_options(void);
TERM_PUBLIC void CACA_init(void);
TERM_PUBLIC void CACA_graphics(void);
TERM_PUBLIC void CACA_text(void);
TERM_PUBLIC void CACA_reset(void);
TERM_PUBLIC void CACA_suspend(void);
TERM_PUBLIC void CACA_resume(void);
TERM_PUBLIC void CACA_linetype(int linetype);
TERM_PUBLIC int CACA_make_palette(t_sm_palette *palette);
TERM_PUBLIC void CACA_color(t_colorspec *colorspec);
TERM_PUBLIC void CACA_move(unsigned int x, unsigned int y);
TERM_PUBLIC void CACA_point(unsigned int x, unsigned int y, int point);
TERM_PUBLIC void CACA_vector(unsigned int x, unsigned int y);
TERM_PUBLIC void CACA_layer(t_termlayer layer);
TERM_PUBLIC void CACA_path(int p);
TERM_PUBLIC void CACA_put_text(unsigned int x, unsigned int y, const char *str);
TERM_PUBLIC void CACA_arrow(unsigned int sx, unsigned int sy,
unsigned int ex, unsigned int ey,
int head);
TERM_PUBLIC int CACA_text_angle(float ang);
TERM_PUBLIC void CACA_fillbox(int style, unsigned int x, unsigned int y, unsigned int w, unsigned int h);
#ifdef USE_MOUSE
TERM_PUBLIC int CACA_waitforinput(int);
TERM_PUBLIC void CACA_put_tmptext(int i, const char str[]);
TERM_PUBLIC void CACA_set_ruler(int, int);
TERM_PUBLIC void CACA_set_cursor(int, int, int);
#endif
TERM_PUBLIC void CACA_filled_polygon(int points, gpiPoint *corners);
TERM_PUBLIC void CACA_image(unsigned int M, unsigned int N, coordval *image,
gpiPoint *corner, t_imagecolor color_mode);
TERM_PUBLIC void CACA_enhanced_put_text(unsigned int x, unsigned int y, const char str[]);
TERM_PUBLIC void CACA_enhanced_open(char * fontname, double fontsize,
double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint);
TERM_PUBLIC void CACA_enhanced_flush(void);
TERM_PUBLIC void CACA_hypertext(int type, const char *text);
TERM_PUBLIC void CACA_boxed_text(unsigned int x, unsigned int y, int option);
TERM_PUBLIC void CACA_modify_plots(unsigned int operations, int plotno);
#endif /* TERM_PROTO */
/* This is a new section for public interfaces. */
#if defined(TERM_PUBLIC_PROTO) || defined (TERM_BODY)
typedef enum CACA_result { CACA_quit, CACA_loop, CACA_endpause } CACA_result;
extern CACA_result CACA_process_events(void);
extern TBOOLEAN CACA_window_opened(void);
#endif /* TERM_PUBLIC_PROTO */
#ifdef TERM_BODY
#include
#include
#include
#ifdef _WIN32
# ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0501
# endif
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include
#endif
#ifdef HAVE_SYS_SELECT_H
# include
#endif
/* Internal Functions */
static void CACA_update_options(void); /* update the term_options string */
static void CACA_refresh(void); /* redraw the screen */
static void CACA_check_resize(void); /* test if the screen size changed */
static void CACA_init_angles(void); /* init the array used for rotated text */
static void CACA_close_display(void);
static void CACA_add_path_point(int x, int y); /* add a new point to current path */
static void CACA_init_display(void);
#ifdef USE_MOUSE
static void CACA_print_status(void); /* draw the status line */
static int CACA_translate_key(int key); /* map libcaca key codes to gnuplot's */
static unsigned long CACA_event_time(void); /* get the event time */
static void CACA_discard_events(void); /* discard pending caca events */
#endif
static void CACA_update_keybox(unsigned plotno, unsigned x, unsigned y);
/* libcaca helper functions */
/* CACA_put_transparent_char() attempts to 'merge' line drawing characters and does not
change the background color. That way filled areas do not get overwritten. */
static void CACA_put_transparent_char(caca_canvas_t *cv, int x, int y, uint32_t c);
/* modified copies of libcaca functions which use CACA_put_transparent_char() */
static int CACA_put_str(caca_canvas_t *cv, int x, int y, char const *s);
static void CACA_draw_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2, uint32_t ch);
static void CACA_draw_thin_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2);
static void CACA_draw_thin_polyline(caca_canvas_t *cv, int const x[], int const y[], int n);
static int CACA_draw_cp437_box(caca_canvas_t *cv, int x, int y, int w, int h);
static int CACA_draw_thin_box(caca_canvas_t *cv, int x, int y, int w, int h);
static uint8_t CACA_rgb_to_ansi(uint32_t color); /* Convert RGB to ANSI background color */
static char const * CACA_get_color_name(uint8_t color); /* Find name of nearest matching predefined color */
static char * CACA_encode_text(const char *str); /* map strings according to current encoding */
static int gp_kbhit(void); /* kbhit() implementation */
/* Types, Enums */
enum CACA_charset {
CACA_ASCII = 0, CACA_BLOCKS, CACA_UNICODE
};
typedef struct {
int x;
int y;
} CACA_POINT;
typedef struct {
int left;
int right;
int top;
int bottom;
} CACA_RECT;
typedef struct CACA_HYPERTEXT_S {
CACA_POINT loc;
char * text;
struct CACA_HYPERTEXT_S * next;
} CACA_HYPERTEXT;
/* Internal Variables */
/* settings */
static int CACA_xmax = CACA_XMAX; /* terminal size, same as term->xmax */
static int CACA_ymax = CACA_YMAX;
static enum CACA_charset CACA_charset = CACA_BLOCKS;
static TBOOLEAN CACA_truecolor = FALSE; /* does backend support truecolor? */
static uint8_t CACA_background = CACA_WHITE; /* background color */
static char * CACA_driver = NULL; /* backend driver name */
static TBOOLEAN CACA_mono = FALSE; /* use greyscale only? */
static TBOOLEAN CACA_force_truecolor = FALSE; /* override the terminals hard-coded truecolor setting */
static TBOOLEAN CACA_inverted = FALSE; /* use inverterted color (black background) */
static char * CACA_title = NULL; /* windows title */
static caca_display_t * CACA_dp;
static caca_canvas_t * CACA_cv = NULL;
static caca_canvas_t * CACA_screen_cv;
static caca_canvas_t * CACA_suspend_cv = NULL;
static TBOOLEAN CACA_windowed = FALSE; /* graphics output in different window*/
static TBOOLEAN CACA_exportonly = FALSE; /* no output to screen but to file */
static TBOOLEAN CACA_processing_events = FALSE; /* flag processing of events in CACA_text() */
static TBOOLEAN CACA_nosuspend; /* no suspend for null and raw drivers */
#ifdef _WIN32
static unsigned CACA_codepage = 0;
#endif
/* drawing */
static char CACA_pen; /* current character used to draw point symbols */
static int CACA_x = -1; /* current X position */
static int CACA_y = -1; /* current Y position */
static int CACA_ang = 0; /* current text angle */
static uint32_t CACA_next_text_attr = 0; /* next text attribute (bold/italic support) */
/* newpath/closepath support */
static TBOOLEAN CACA_in_path = FALSE;
static int * CACA_path_x = NULL;
static int * CACA_path_y = NULL;
static int CACA_maxpath = 0;
static int CACA_polygon_points = 0;
static TBOOLEAN CACA_invertfill = TRUE; /* Switch foreground/background color for filling of polygons and boxes? */
/* mousing */
#ifdef USE_MOUSE
static const int CACA_status_height = 1; /* height of status line */
static CACA_POINT CACA_mouse; /* current mouse position */
static char * CACA_status_text = NULL; /* current status line text */
static int CACA_ruler_line = FALSE; /* status of ruler line */
static CACA_POINT CACA_ruler = {-1, -1}; /* ruler position */
static TBOOLEAN CACA_zooming = FALSE; /* mouse zooming */
static CACA_POINT CACA_zoom_from;
static char * CACA_zoom_from_text = NULL;
static CACA_POINT CACA_zoom_to;
static char * CACA_zoom_to_text = NULL;
#else
static const int CACA_status_height = 0;
#endif
/* boxed text */
static struct s_boxedtext {
TBOOLEAN boxed;
CACA_POINT margin;
CACA_RECT box;
int angle;
} CACA_boxedtext;
/* hypertext */
static CACA_HYPERTEXT * CACA_hypertext_list = NULL;
static CACA_HYPERTEXT * CACA_last_hypertext = NULL;
/* interactive hiding of plots */
static int CACA_plotno = 0;
static int CACA_maxhideplots = 0;
static TBOOLEAN * CACA_hideplot = NULL;
static int CACA_maxkeyboxes = 0;
static CACA_RECT * CACA_keyboxes;
static int CACA_skipplot = FALSE;
static int CACA_keysample = FALSE;
static TBOOLEAN CACA_zoom_or_replot = FALSE; /* do not reset above variables when zooming/replotting */
/* colors */
static const char CACA_AXIS_CONST = '\1';
static const char CACA_BORDER_CONST = '\2';
static const int CACA_colors[9] = {
CACA_BLACK, CACA_LIGHTGRAY,
CACA_LIGHTRED, CACA_LIGHTGREEN, CACA_LIGHTBLUE, CACA_LIGHTMAGENTA,
CACA_LIGHTCYAN, CACA_BROWN, CACA_BLACK
};
/* Unicode light box drawing characters */
enum CACA_box_drawing_chars {
BOX_HOR = 0x2500,
BOX_VERT = 0x2502,
BOX_UP_LEFT = 0x2518,
BOX_UP_HOR = 0x2534,
BOX_UP_RIGHT = 0x2514,
BOX_VERT_LEFT = 0x2524,
BOX_VERT_HOR = 0x253c,
BOX_VERT_RIGHT = 0x251c,
BOX_DOWN_LEFT = 0x2510,
BOX_DOWN_HOR = 0x252c,
BOX_DOWN_RIGHT = 0x250c
};
/* option names */
enum CACA_id { CACA_DEFAULTOPTIONS,
CACA_DRIVER, CACA_FORMAT,
CACA_SIZE, CACA_TITLE,
CACA_COLOR, CACA_MONOCHROME,
CACA_TRUECOLOR, CACA_NOTRUECOLOR,
CACA_INVERTED, CACA_NOINVERTED,
CACA_CHARSET, CACA_BACKGROUND,
CACA_ENH, CACA_NOENH,
CACA_OTHER };
static struct gen_table CACA_opts[] =
{
{ "def$ault", CACA_DEFAULTOPTIONS },
{ "driver", CACA_DRIVER },
{ "format", CACA_FORMAT },
{ "size", CACA_SIZE },
{ "ti$tle", CACA_TITLE },
{ "c$olor", CACA_COLOR },
{ "c$olour", CACA_COLOR },
{ "m$onochrome", CACA_MONOCHROME },
{ "inv$erted", CACA_INVERTED },
{ "noinv$erted", CACA_NOINVERTED },
{ "ch$arset", CACA_CHARSET },
{ "backg$round", CACA_BACKGROUND },
{ "enh$anced", CACA_ENH },
{ "noe$nhanced", CACA_NOENH },
{ "tru$ecolor", CACA_TRUECOLOR },
{ "notru$ecolor", CACA_NOTRUECOLOR },
{ NULL, CACA_OTHER }
};
TERM_PUBLIC void
CACA_options()
{
while (!END_OF_COMMAND) {
switch ((enum CACA_id) lookup_table(&CACA_opts[0], c_token)) {
case CACA_DEFAULTOPTIONS:
term->put_text = CACA_enhanced_put_text;
term->flags |= TERM_ENHANCED_TEXT;
term->flags |= TERM_NO_OUTPUTFILE;
term->flags &= ~TERM_MONOCHROME;
free(CACA_driver);
CACA_driver = NULL;
free(CACA_title);
CACA_title = NULL;
CACA_background = CACA_WHITE;
CACA_charset = CACA_BLOCKS;
term->xmax = CACA_xmax = CACA_XMAX;
term->ymax = CACA_ymax = CACA_YMAX;
CACA_mono = FALSE;
CACA_inverted = FALSE;
break;
case CACA_ENH:
c_token++;
term->put_text = CACA_enhanced_put_text;
term->flags |= TERM_ENHANCED_TEXT;
break;
case CACA_NOENH:
c_token++;
term->put_text = CACA_put_text;
term->flags &= ~TERM_ENHANCED_TEXT;
break;
case CACA_CHARSET: {
c_token++;
/* Character set to use for fills, lines and points */
if (equals(c_token, "ascii"))
CACA_charset = CACA_ASCII;
else if (equals(c_token, "blocks"))
CACA_charset = CACA_BLOCKS;
else if (equals(c_token, "unicode"))
CACA_charset = CACA_UNICODE;
else if (!END_OF_COMMAND)
int_error(c_token, "Unknown character set.");
else
int_error(c_token, "Option `charset` requires an argument, see `help caca`.");
c_token++;
break;
}
case CACA_BACKGROUND: {
uint32_t CACA_background_rgb;
c_token++;
CACA_background_rgb = parse_color_name();
CACA_background = CACA_rgb_to_ansi(CACA_background_rgb);
FPRINTF((stderr, "CACA: background color = %06x, bg = %0x name = %s\n",
CACA_background_rgb, CACA_background, CACA_get_color_name(CACA_background)));
break;
}
case CACA_FORMAT:
case CACA_DRIVER: {
char const * const * format_list;
int i;
c_token++;
if (equals(c_token, "default")) {
c_token++;
CACA_close_display();
free(CACA_driver);
CACA_driver = NULL;
} else if (equals(c_token, "list")) {
c_token++;
fprintf(stderr, "caca drivers: ");
format_list = caca_get_display_driver_list();
for (i = 0; format_list[i] != NULL; i += 2) {
/* suppress null and raw drivers */
if ((strcmp(format_list[i], "null") != 0) &&
(strcmp(format_list[i], "raw") != 0))
fprintf(stderr, "%s ", format_list[i]);
}
fprintf(stderr, "\ncaca export formats: ");
format_list = caca_get_export_list();
for (i = 0; format_list[i] != NULL; i += 2)
fprintf(stderr, "%s ", format_list[i]);
fprintf(stderr, "\n");
return;
} else {
if (!END_OF_COMMAND) {
char * driver = NULL;
m_capture(&driver, c_token, c_token);
c_token++;
/* Has driver changed? */
if ((CACA_driver != NULL) && (strcmp(CACA_driver, driver) != 0))
CACA_close_display();
free(CACA_driver);
CACA_driver = driver;
} else {
int_error(c_token, "Option `driver` requires an argument, see `help caca`.");
}
}
/* Test the list of export formats for a match. In that case
we do not display anything but save the graph to a file. */
CACA_exportonly = FALSE;
if (CACA_driver != NULL) {
format_list = caca_get_export_list();
for (i = 0; format_list[i] != NULL; i += 2) {
if (strcmp(format_list[i], CACA_driver) == 0) {
CACA_exportonly = TRUE;
break;
}
}
}
/* Verify that the driver is valid. If it is an export format
it was detected already above. */
if ((CACA_driver != NULL) && !CACA_exportonly) {
TBOOLEAN found = FALSE;
format_list = caca_get_display_driver_list();
for (i = 0; format_list[i] != NULL; i += 2) {
if (strcmp(format_list[i], CACA_driver) == 0) {
found = TRUE;
break;
}
}
if (!found) {
free(CACA_driver);
CACA_driver = NULL;
int_error(c_token, "Unknown driver or export format.");
}
}
/* raw and null drivers may not _suspend() */
if (strcmp(CACA_driver, "null") == 0 || strcmp(CACA_driver, "raw") == 0)
CACA_nosuspend = TRUE;
else
CACA_nosuspend = FALSE;
if (CACA_exportonly)
term->flags &= ~TERM_NO_OUTPUTFILE;
else
term->flags |= TERM_NO_OUTPUTFILE;
break;
}
case CACA_COLOR:
c_token++;
CACA_mono = FALSE;
term->flags &= ~TERM_MONOCHROME;
break;
case CACA_MONOCHROME:
c_token++;
CACA_mono = TRUE;
term->flags |= TERM_MONOCHROME;
break;
case CACA_INVERTED:
c_token++;
CACA_inverted = TRUE;
CACA_background = CACA_BLACK;
break;
case CACA_NOINVERTED:
c_token++;
CACA_inverted = FALSE;
CACA_background = CACA_WHITE;
break;
case CACA_TRUECOLOR:
c_token++;
CACA_force_truecolor = TRUE;
break;
case CACA_NOTRUECOLOR:
c_token++;
CACA_force_truecolor = FALSE;
break;
case CACA_SIZE: {
float width, height;
c_token++;
parse_term_size(&width, &height, PIXELS);
term->xmax = CACA_xmax = width - 1;
term->ymax = CACA_ymax = height - 1;
break;
}
case CACA_TITLE:
c_token++;
if (!END_OF_COMMAND) {
free(CACA_title);
CACA_title = strdup(try_to_get_string());
} else {
int_error(c_token, "Option `title` requires an argument, see `help caca`.");
}
break;
case CACA_OTHER:
default:
int_error(c_token, "extraneous argument in set terminal %s", term->name);
break;
}
}
CACA_update_options();
#ifdef DEBUG
{
int i;
for (i = 0; i < 16; i++) {
fprintf(stderr, "CACA: color #%i = %s\n", i, CACA_get_color_name(i));
}
}
#endif
}
static void
CACA_update_options(void)
{
char options[MAX_LINE_LEN + 1];
int n;
term_options[0] = NUL;
if (CACA_driver != NULL)
snprintf(term_options, MAX_LINE_LEN + 1, "%s %s ", CACA_exportonly ? "format" : "driver", CACA_driver);
if (CACA_title != NULL) {
n = snprintf(options, MAX_LINE_LEN, "title \"%s\" ", CACA_title);
if (n >= MAX_LINE_LEN) options[MAX_LINE_LEN] = NUL;
strcat(term_options, options);
}
n = snprintf(options, MAX_LINE_LEN + 1, "%senhanced size %d, %d background rgb \"%s\" %s %s ",
(term->flags & TERM_ENHANCED_TEXT) ? "" : "no",
CACA_xmax + 1, CACA_ymax + 1,
CACA_get_color_name(CACA_background),
CACA_mono ? "monochrome" : "color",
CACA_inverted ? "inverted" : "noinverted");
if (n >= MAX_LINE_LEN) options[MAX_LINE_LEN] = NUL;
strcat(term_options, options);
strcat(term_options, "charset ");
switch (CACA_charset) {
case CACA_ASCII:
strcat(term_options, "ascii");
break;
case CACA_BLOCKS:
strcat(term_options, "blocks");
break;
case CACA_UNICODE:
strcat(term_options, "unicode");
break;
}
}
TERM_PUBLIC void
CACA_init()
{
CACA_init_display();
if (CACA_cv != NULL)
caca_clear_canvas(CACA_cv);
if (CACA_dp != NULL) {
#ifdef USE_MOUSE
CACA_print_status();
#endif
caca_refresh_display(CACA_dp);
CACA_check_resize();
}
}
static void
CACA_refresh(void)
{
CACA_HYPERTEXT * hypertext = CACA_hypertext_list;
if ((CACA_dp == NULL) || (CACA_cv == NULL) || CACA_exportonly)
return;
/* copy graph to screen canvas */
caca_blit(CACA_screen_cv, 0, 0, CACA_cv, NULL);
#ifdef USE_MOUSE
/* hypertext */
while (hypertext) {
if ((CACA_mouse.x == hypertext->loc.x) && (CACA_mouse.y == hypertext->loc.y)) {
uint32_t attr = caca_get_attr(CACA_screen_cv, -1, -1);
caca_set_color_ansi(CACA_screen_cv, CACA_background, !CACA_inverted ? CACA_BLACK : CACA_WHITE);
caca_put_str(CACA_screen_cv,
CACA_mouse.x + 1, CACA_ymax - CACA_mouse.y,
hypertext->text);
caca_set_attr(CACA_screen_cv, attr);
break;
}
hypertext = hypertext->next;
}
/* draw ruler */
if (CACA_ruler.x >= 0) {
uint32_t attr = caca_get_attr(CACA_screen_cv, -1, -1);
caca_set_color_ansi(CACA_screen_cv, !CACA_inverted ? CACA_BLACK : CACA_WHITE, CACA_background);
if (CACA_charset == CACA_ASCII) {
CACA_draw_thin_line(CACA_screen_cv,
CACA_ruler.x, 0, CACA_ruler.x, CACA_ymax);
CACA_draw_thin_line(CACA_screen_cv, 0,
CACA_ymax - CACA_ruler.y, CACA_xmax,
CACA_ymax - CACA_ruler.y);
CACA_put_transparent_char(CACA_screen_cv, CACA_ruler.x, CACA_ymax - CACA_ruler.y, L'+');
} else { /* BLOCKS / UNICODE */
CACA_draw_line(CACA_screen_cv,
CACA_ruler.x, 0, CACA_ruler.x, CACA_ymax, BOX_VERT);
CACA_draw_line(CACA_screen_cv, 0,
CACA_ymax - CACA_ruler.y, CACA_xmax,
CACA_ymax - CACA_ruler.y, BOX_HOR);
CACA_put_transparent_char(CACA_screen_cv, CACA_ruler.x, CACA_ymax - CACA_ruler.y, BOX_VERT_HOR);
}
/* ruler line */
if (CACA_ruler_line) {
CACA_draw_thin_line(CACA_screen_cv,
CACA_ruler.x, CACA_ymax - CACA_ruler.y,
CACA_mouse.x, CACA_ymax - CACA_mouse.y);
}
caca_set_attr(CACA_screen_cv, attr);
}
/* draw zoom box */
if (CACA_zooming) {
if (CACA_charset == CACA_ASCII)
caca_draw_thin_box(CACA_screen_cv,
CACA_zoom_from.x, CACA_ymax - CACA_zoom_from.y,
CACA_zoom_to.x - CACA_zoom_from.x + 1,
CACA_zoom_from.y - CACA_zoom_to.y + 1);
else /* BLOCKS / UNICODE */
caca_draw_cp437_box(CACA_screen_cv,
CACA_zoom_from.x, CACA_ymax - CACA_zoom_from.y,
CACA_zoom_to.x - CACA_zoom_from.x + 1,
CACA_zoom_from.y - CACA_zoom_to.y + 1);
if (CACA_zoom_from_text) {
char *separator = strchr(CACA_zoom_from_text, '\r');
if (separator) {
*separator = NUL;
caca_put_str(CACA_screen_cv,
CACA_zoom_from.x + 1, CACA_ymax - CACA_zoom_from.y - 1,
CACA_zoom_from_text);
caca_put_str(CACA_screen_cv,
CACA_zoom_from.x + 1, CACA_ymax - CACA_zoom_from.y,
separator + 1);
*separator = '\r';
} else {
caca_put_str(CACA_screen_cv,
CACA_zoom_from.x + 1, CACA_ymax - CACA_zoom_from.y - 1,
CACA_zoom_from_text);
}
}
if (CACA_zoom_to_text) {
char *separator = strchr(CACA_zoom_to_text, '\r');
if (separator) {
*separator = NUL;
caca_put_str(CACA_screen_cv,
CACA_zoom_to.x + 1, CACA_ymax - CACA_zoom_to.y - 1,
CACA_zoom_to_text);
caca_put_str(CACA_screen_cv,
CACA_zoom_to.x + 1, CACA_ymax - CACA_zoom_to.y,
separator + 1);
*separator = '\r';
} else {
caca_put_str(CACA_screen_cv,
CACA_zoom_to.x + 1, CACA_ymax - CACA_zoom_to.y - 1,
CACA_zoom_to_text);
}
}
}
/* status line */
CACA_print_status();
#endif
/* update display */
caca_refresh_display(CACA_dp);
}
static void
CACA_check_resize()
{
caca_event_t ev;
if ((CACA_dp == NULL) || (CACA_cv == NULL) || CACA_exportonly)
return;
if (caca_get_event(CACA_dp, CACA_EVENT_RESIZE, &ev, 100)) {
FPRINTF((stderr, "CACA: resize event detected in check_resize().\n"));
term->xmax = CACA_xmax = GPMAX(caca_get_event_resize_width(&ev) - 1, 1);
term->ymax = CACA_ymax = GPMAX(caca_get_event_resize_height(&ev) - 1 - CACA_status_height, 1);
caca_set_canvas_size(CACA_cv, term->xmax + 1, term->ymax + 1);
CACA_update_options();
CACA_refresh();
} else if ((caca_get_canvas_width(CACA_screen_cv) != term->xmax + 1) ||
(caca_get_canvas_height(CACA_screen_cv) != term->ymax + 1 + CACA_status_height)) {
/* Did we miss a resize event? */
FPRINTF((stderr, "CACA: missed resize event in check_resize().\n"));
term->xmax = CACA_xmax = GPMAX(caca_get_canvas_width(CACA_screen_cv) - 1, 1);
term->ymax = CACA_ymax = GPMAX(caca_get_canvas_height(CACA_screen_cv) - 1 - CACA_status_height, 1);
caca_set_canvas_size(CACA_cv, term->xmax + 1, term->ymax + 1);
CACA_update_options();
CACA_refresh();
}
}
void
CACA_sigint_handler(int sig)
{
if (!CACA_windowed)
CACA_close_display();
interrupt_setup();
raise(SIGINT);
}
static void
CACA_init_display(void)
{
if ((CACA_dp == NULL) && !CACA_exportonly) {
if (CACA_driver == NULL)
CACA_dp = caca_create_display(NULL);
else if (!CACA_exportonly)
CACA_dp = caca_create_display_with_driver(NULL, CACA_driver);
}
if (CACA_dp != NULL) {
if (CACA_driver == NULL)
CACA_driver = strdup(caca_get_display_driver(CACA_dp));
CACA_windowed = (strcmp(CACA_driver, "x11") == 0);
CACA_windowed |= (strcmp(CACA_driver, "gl") == 0);
#if defined(_WIN32) && !defined(WGP_CONSOLE)
CACA_windowed |= (strcmp(CACA_driver, "win32") == 0);
#endif
#ifdef USE_MOUSE
if (CACA_windowed)
term->waitforinput = CACA_waitforinput;
else
term->waitforinput = NULL;
caca_set_mouse(CACA_dp, 1);
#endif
/* NOTE: Info on truecolor support is not available from libcaca and
is thus hard-coded here. The checks below have to be adopted
whenever libcaca evolves. */
CACA_truecolor = (strcmp(CACA_driver, "x11") == 0);
CACA_truecolor |= (strcmp(CACA_driver, "gl") == 0);
CACA_truecolor |= CACA_force_truecolor;
FPRINTF((stderr, "CACA: driver = '%s'\n", CACA_driver));
caca_set_display_title(CACA_dp,
(CACA_title != NULL) ? CACA_title : "gnuplot: caca terminal");
CACA_screen_cv = caca_get_canvas(CACA_dp);
term->xmax = CACA_xmax = GPMAX(caca_get_canvas_width(CACA_screen_cv) - 1, 1);
term->ymax = CACA_ymax = GPMAX(caca_get_canvas_height(CACA_screen_cv) - 1 - CACA_status_height, 1);
if (CACA_xmax waitforinput = NULL;
#endif
CACA_truecolor = FALSE;
}
FPRINTF((stderr, "CACA_driver = %s, truecolor = %i\n", CACA_driver, CACA_truecolor));
CACA_cv = caca_create_canvas(term->xmax + 1, term->ymax + 1);
if (CACA_cv != NULL) {
caca_set_color_ansi(CACA_cv, !CACA_inverted ? CACA_BLACK : CACA_WHITE, CACA_background);
}
}
static void
CACA_init_aspect_ratio(void)
{
int width = 6;
int height = 10;
if (CACA_dp) {
height = caca_get_display_height(CACA_dp) / (CACA_ymax + CACA_status_height + 1);
width = caca_get_display_width(CACA_dp) / (CACA_xmax + 1);
/* The caca man page says "these functions never fail". */
/* But in practice they can return 0. */
}
if (height == 0 || width == 0) {
term->h_tic = term->v_tic = 1;
} else if (height > width) {
term->h_tic = (height + width / 2) / width;
term->v_tic = 1;
} else {
term->h_tic = 1;
term->v_tic = (width + height / 2) / height;
}
}
TERM_PUBLIC void
CACA_graphics()
{
CACA_HYPERTEXT * hypertext;
if (CACA_dp == NULL) {
CACA_init();
if (CACA_cv == NULL)
fprintf(stderr, "ERROR: Failed to setup up libcaca canvas!\n");
}
if (CACA_cv == NULL)
return;
caca_set_color_ansi(CACA_cv, !CACA_inverted ? CACA_BLACK : CACA_WHITE, CACA_background);
caca_clear_canvas(CACA_cv);
if (CACA_dp != NULL) {
#ifdef USE_MOUSE
CACA_print_status();
#endif
CACA_check_resize();
}
FPRINTF((stderr, "CACA: terminal size %i x %i\n", CACA_xmax + 1, CACA_ymax + 1 + CACA_status_height));
CACA_init_angles();
CACA_init_aspect_ratio();
CACA_x = -1;
CACA_y = -1;
/* clear hypertext labels */
hypertext = CACA_hypertext_list;
while (hypertext != NULL) {
CACA_HYPERTEXT * next = hypertext->next;
free(hypertext->text);
free(hypertext);
hypertext = next;
};
CACA_hypertext_list = NULL;
CACA_last_hypertext = NULL;
/* Reset layer variables */
CACA_plotno = 0;
CACA_skipplot = FALSE;
CACA_keysample = FALSE;
if (CACA_zoom_or_replot) {
CACA_zoom_or_replot = FALSE;
} else {
int i;
for (i = 0; i < CACA_maxhideplots; i++)
CACA_hideplot[i] = FALSE;
for (i = 0; i < CACA_maxkeyboxes; i++) {
CACA_keyboxes[i].left = INT_MAX;
CACA_keyboxes[i].right = 0;
CACA_keyboxes[i].bottom = INT_MAX;
CACA_keyboxes[i].top = 0;
}
}
}
TERM_PUBLIC void
CACA_text()
{
if (CACA_cv == NULL)
return;
if (CACA_dp != NULL)
CACA_refresh();
#ifdef USE_MOUSE
exec_event(GE_plotdone, 0, 0, 0, 0, 0);
if (!CACA_windowed && !CACA_processing_events && !CACA_exportonly && !CACA_nosuspend) {
/* Test for input redirection */
if (isatty(fileno(stdin))) {
/* NOTE: This is a hack to force a call to _graphics() before redrawing.
The vgagl terminal uses the very same trick. */
term_graphics = FALSE;
CACA_processing_events = TRUE;
while (CACA_process_events() == CACA_loop);
/* Clean up remaining console events, as e.g. KEYUP events: */
CACA_discard_events();
CACA_processing_events = FALSE;
term_graphics = TRUE;
} else {
(void) getchar();
CACA_close_display();
}
}
#endif
/* Save canvas to file. */
if (CACA_exportonly) {
size_t bytes;
#ifndef USE_CACA_EXPORT_MEMORY
/* available since 0.99.beta17 */
void * data = caca_export_canvas_to_memory(CACA_cv, CACA_driver, &bytes);
#else
void * data = caca_export_memory(CACA_cv, CACA_driver, &bytes);
#endif
if (data != NULL) {
fwrite(data, 1, bytes, gpoutfile);
free(data);
}
}
#if defined(_WIN32) && !defined(WGP_CONSOLE)
/* Raise the console (graph!) window. Beware: */
/* we cannot call RaiseConsole() to do this. */
else {
HWND console = GetConsoleWindow();
if (console != NULL) {
ShowWindow(console, SW_SHOWNORMAL);
BringWindowToTop(console);
SetFocus(console);
}
}
#endif
#if defined(WGP_CONSOLE)
/* libcaca seems to leave stdout in wide mode */
fflush(stdout);
_setmode(fileno(stdout), _O_TEXT);
#endif
}
static void
CACA_close_display(void)
{
/* make a copy of the current canvas */
if ((CACA_dp != NULL) || (CACA_cv != NULL)) {
if (!CACA_windowed && !CACA_nosuspend) {
if (CACA_suspend_cv != NULL)
caca_free_canvas(CACA_suspend_cv);
CACA_suspend_cv = caca_create_canvas(CACA_xmax + 1, CACA_ymax + 1 + CACA_status_height);
if (CACA_cv != NULL && CACA_suspend_cv != NULL)
caca_blit(CACA_suspend_cv, 0, 0, CACA_cv, NULL);
}
if (CACA_cv != NULL) {
caca_free_canvas(CACA_cv);
CACA_cv = NULL;
}
if (CACA_dp != NULL) {
caca_set_display_title(CACA_dp, "gnuplot");
caca_free_display(CACA_dp);
CACA_dp = NULL;
}
#ifdef _WIN32
if (strcmp(CACA_driver, "win32") == 0) {
#ifndef WGP_CONSOLE
FreeConsole(); /* close the console window */
#endif
/* Revert to previous codepage if necessary */
if (CACA_codepage != 0) {
SetConsoleOutputCP(CACA_codepage);
CACA_codepage = 0;
}
}
#ifndef WGP_CONSOLE
WinPersistTextClose();
#endif
#endif
/* NOTE: The slang backend driver sets its own signal handler for SIGINT,
but does not seem to restore the previous handler on exit.
*/
if (strcmp(CACA_driver, "slang") == 0)
interrupt_setup();
}
}
TERM_PUBLIC void
CACA_suspend(void)
{
if (CACA_exportonly || CACA_nosuspend)
return;
CACA_text();
if (!CACA_windowed)
CACA_close_display();
}
TERM_PUBLIC void
CACA_resume(void)
{
if (CACA_exportonly || CACA_nosuspend)
return;
if (CACA_dp == NULL) {
CACA_init_display();
if (CACA_cv == NULL) {
fprintf(stderr, "ERROR: Failed to setup up libcaca canvas!\n");
return;
}
if (CACA_suspend_cv != NULL) {
caca_blit(CACA_cv, 0, 0, CACA_suspend_cv, NULL);
caca_free_canvas(CACA_suspend_cv);
CACA_suspend_cv = NULL;
}
}
#ifdef USE_MOUSE
CACA_print_status();
#endif
CACA_refresh();
CACA_check_resize();
}
TERM_PUBLIC void
CACA_reset()
{
#ifdef USE_MOUSE
free(CACA_status_text);
CACA_status_text = NULL;
free(CACA_zoom_from_text);
free(CACA_zoom_to_text);
CACA_zoom_from_text = CACA_zoom_to_text = NULL;
#endif
free(CACA_path_x);
free(CACA_path_y);
CACA_path_x = CACA_path_y = NULL;
CACA_polygon_points = CACA_maxpath = 0;
}
TERM_PUBLIC void
CACA_linetype(int linetype)
{
static const char pen_type[] = "*#$%@&=";
t_colorspec colorspec;
if (linetype == LT_BLACK)
CACA_pen = CACA_BORDER_CONST;
else if (linetype == LT_AXIS)
CACA_pen = CACA_AXIS_CONST;
else if (linetype type) {
case TC_LT: {
if (!CACA_mono) {
if (colorspec->lt >= LT_BLACK) {
uint16_t color = CACA_colors[(colorspec->lt + 2) % 9];
if (CACA_inverted && (color == CACA_BLACK))
color = CACA_WHITE;
caca_set_color_ansi(CACA_cv, color, CACA_background);
} else /* LT_BACKGROUND */
caca_set_color_ansi(CACA_cv, CACA_background, CACA_background);
} else {
if (colorspec->lt == LT_AXIS)
caca_set_color_ansi(CACA_cv, CACA_LIGHTGRAY, CACA_background);
else if (colorspec->lt >= LT_BLACK)
caca_set_color_ansi(CACA_cv, !CACA_inverted ? CACA_BLACK : CACA_WHITE, CACA_background);
else /* LT_BACKGROUND */
caca_set_color_ansi(CACA_cv, CACA_background, CACA_background);
}
break;
}
case TC_FRAC: {
uint32_t attr;
rgb255_color rgb255;
uint16_t alpha;
uint16_t fg_color;
uint16_t bg_color;
/* Immediately translate palette index to RGB colour */
rgb255maxcolors_from_gray(colorspec->value, &rgb255);
alpha = 0xf;
/* keep background color */
attr = caca_get_attr(CACA_cv, -1, -1); /* get current attribute */
if (!CACA_truecolor) {
uint32_t fg_color32;
bg_color = caca_attr_to_ansi_bg(attr);
if (CACA_mono) {
/* convert to gray */
int luma = (int)(rgb255.r * 0.3 + rgb255.g * 0.59 + rgb255.b * 0.11);
fg_color32 = (0xff > 4) > 4) > 4) > 4);
}
caca_set_color_argb(CACA_cv, fg_color, bg_color);
}
break;
}
case TC_RGB: {
uint32_t attr;
uint16_t alpha, red, green, blue;
uint16_t fg_color;
uint16_t bg_color;
/* keep background color */
attr = caca_get_attr(CACA_cv, -1, -1); /* get current attribute */
/* extract color components from colorspec.lt */
alpha = 0xff - ((colorspec->lt >> 24) & 0xff);
red = (colorspec->lt >> 16) & 0xff;
green = (colorspec->lt >> 8) & 0xff;
blue = (colorspec->lt) & 0xff;
if (!CACA_truecolor) {
uint32_t fg_color32 = colorspec->lt;
bg_color = caca_attr_to_ansi_bg(attr);
if (CACA_mono) {
/* convert to gray */
int luma = (int)(red * 0.3 + green * 0.59 + blue * 0.11);
fg_color32 = (0xff 4) > 4) > 4);
} else {
/* caca uses 4 bits per component only */
fg_color = ((alpha >> 4) > 4) > 4) > 4);
}
caca_set_color_argb(CACA_cv, fg_color, bg_color);
}
break;
}
default:
break;
}
}
TERM_PUBLIC void
CACA_move(unsigned int x, unsigned int y)
{
if (CACA_in_path && ((CACA_x != x) || (CACA_y != y)))
CACA_add_path_point(x, y);
CACA_x = x;
CACA_y = y;
}
TERM_PUBLIC void
CACA_point(unsigned int x, unsigned int y, int point)
{
const wchar_t usymbol[] = { /* 0x2022 */
0x2219 /* bullet */, L'+', 0x00d7 /* multiplication sign */,
0x0263c, 0x25a0 /* black square */, 0x25cb /* white circle */, 0x25ca /* lozenge */,
0x25b2 /* black up triangle */, 0x25bc /* black down triangle */,
0x2302 /* house */,
0x263a /* white smiling face */, 0x263b /* black smiling face */,
0x2640 /* female sign */, 0x2642 /* male sign */,
0x2660, 0x2663, 0x2665, 0x2666,
0x266a /* eighth note */, 0x266b /* beamed eighth notes */
};
const int nsymbols = (sizeof(usymbol) / sizeof(wchar_t)) - 1;
wchar_t symbol;
if (CACA_cv == NULL)
return;
if (CACA_skipplot && !CACA_keysample)
return;
if (CACA_charset == CACA_ASCII)
symbol = (point loc.x = x;
CACA_last_hypertext->loc.y = y;
CACA_last_hypertext = NULL;
}
if (CACA_keysample) {
CACA_update_keybox(CACA_plotno, x, y);
}
}
TERM_PUBLIC void
CACA_vector(unsigned int arg_x, unsigned int arg_y)
{
int x = arg_x;
int y = arg_y;
wchar_t pen = NUL;
if (CACA_cv == NULL)
return;
if (CACA_skipplot && !CACA_keysample)
return;
if (CACA_in_path) {
CACA_add_path_point(x, y);
CACA_x = x;
CACA_y = y;
return;
}
/* Use line drawing characters for border and axis. */
if ((CACA_charset != CACA_ASCII) &&
((CACA_pen == CACA_BORDER_CONST) || (CACA_pen == CACA_AXIS_CONST))) {
if (CACA_y == y) /* horizontal line */
pen = BOX_HOR;
else if (CACA_x == x) /* vertical line */
pen = BOX_VERT;
}
/* Use monochrome pen if applicable. */
if (CACA_mono &&
(CACA_pen != CACA_BORDER_CONST) && (CACA_pen != CACA_AXIS_CONST))
pen = CACA_pen;
if (pen != NUL)
CACA_draw_line(CACA_cv, CACA_x, CACA_ymax - CACA_y, x, CACA_ymax - y, pen);
else
CACA_draw_thin_line(CACA_cv, CACA_x, CACA_ymax - CACA_y, x, CACA_ymax - y);
if (pen != NUL && pen < ' ')
FPRINTF((stderr, "Warning pen = %02x\n", (int) pen));
if (CACA_keysample) {
CACA_update_keybox(CACA_plotno, CACA_x, CACA_y);
CACA_update_keybox(CACA_plotno, x, y);
}
CACA_x = x;
CACA_y = y;
}
static void
CACA_update_keybox(unsigned plotno, unsigned x, unsigned y)
{
CACA_RECT * bb;
if (plotno == 0)
return;
if (plotno > CACA_maxkeyboxes) {
int i;
CACA_maxkeyboxes += 10;
CACA_keyboxes = (CACA_RECT *) gp_realloc(CACA_keyboxes,
CACA_maxkeyboxes * sizeof(CACA_RECT), "key boxes");
for (i = plotno - 1; i < CACA_maxkeyboxes; i++) {
CACA_keyboxes[i].left = INT_MAX;
CACA_keyboxes[i].right = 0;
CACA_keyboxes[i].bottom = INT_MAX;
CACA_keyboxes[i].top = 0;
}
}
bb = CACA_keyboxes + plotno - 1;
if (x < bb->left) bb->left = x;
if (x > bb->right) bb->right = x;
if (y < bb->bottom) bb->bottom = y;
if (y > bb->top) bb->top = y;
}
void
CACA_layer(t_termlayer layer)
{
int idx;
switch (layer) {
case TERM_LAYER_BEFORE_ZOOM:
CACA_zoom_or_replot = TRUE;
break;
case TERM_LAYER_BEFORE_PLOT:
CACA_plotno++;
if (CACA_plotno >= CACA_maxhideplots) {
CACA_maxhideplots += 10;
CACA_hideplot = (TBOOLEAN *) gp_realloc(CACA_hideplot, CACA_maxhideplots * sizeof(TBOOLEAN), "hideplot");
for (idx = CACA_plotno - 1; idx < CACA_maxhideplots; idx++)
CACA_hideplot[idx] = FALSE;
}
if (CACA_plotno 0) {
/* Test for rectangles: */
if (((CACA_charset == CACA_BLOCKS) || (CACA_charset == CACA_UNICODE)) &&
(CACA_polygon_points == 5) &&
((CACA_path_x[0] == CACA_path_x[4]) && (CACA_path_y[0] == CACA_path_y[4])) &&
(((CACA_path_x[0] == CACA_path_x[4]) && (CACA_path_y[0] == CACA_path_y[4]) &&
(CACA_path_x[0] == CACA_path_x[1]) && (CACA_path_y[0] == CACA_path_y[3])) ||
((CACA_path_x[0] == CACA_path_x[3]) && (CACA_path_y[0] == CACA_path_y[1]) &&
(CACA_path_x[2] == CACA_path_x[1]) && (CACA_path_y[2] == CACA_path_y[3])))) {
/* skip empty boxes */
if ((CACA_path_x[0] == CACA_path_x[2]) || (CACA_path_y[0] == CACA_path_y[2]))
return;
CACA_draw_cp437_box(CACA_cv,
GPMIN(CACA_path_x[0], CACA_path_x[2]),
GPMIN(CACA_path_y[0], CACA_path_y[2]),
ABS(CACA_path_x[0] - CACA_path_x[2]) + 1,
ABS(CACA_path_y[0] - CACA_path_y[2]) + 1);
} else { /* Always use thin lines for ASCII. */
CACA_draw_thin_polyline(
CACA_cv, CACA_path_x, CACA_path_y, CACA_polygon_points - 1);
}
}
}
}
static void
CACA_add_path_point(int x, int y)
{
if (CACA_polygon_points >= CACA_maxpath) {
CACA_maxpath += 10;
CACA_path_x = (int *) gp_realloc(CACA_path_x, CACA_maxpath * sizeof(int), "path_x");
CACA_path_y = (int *) gp_realloc(CACA_path_y, CACA_maxpath * sizeof(int), "path_y");
}
CACA_path_x[CACA_polygon_points] = x;
CACA_path_y[CACA_polygon_points] = CACA_ymax - y;
CACA_polygon_points++;
FPRINTF((stderr, "CACA: new polygon point: %i %i\n", x, y));
}
struct angles {
int angle;
int xdiv;
int ydiv;
};
/* Table assumes aspect ratio of characters of 16:10. */
static struct angles CACA_angles[] = {
{ 0, 1, 1000},
{28, 1, 3},
{39, 1, 2},
{58, 1, 1},
{73, 2, 1},
{78, 3, 1},
{90, 1000, 1},
{-1, 0, 0}
};
static void
CACA_init_angles(void)
{
int i;
int width = 6;
int height = 10;
if (CACA_dp != NULL) {
height = caca_get_display_height(CACA_dp) / (CACA_ymax + CACA_status_height + 1);
width = caca_get_display_width(CACA_dp) / (CACA_xmax + 1);
}
for (i = 0; CACA_angles[i].angle >= 0; i++) {
CACA_angles[i].angle = (int) (0.5 + 180. / M_PI *
atan2l(CACA_angles[i].xdiv * height,
CACA_angles[i].ydiv * width));
}
}
static char *
CACA_encode_text(const char *str)
{
char * text = (char * ) str;
int i;
if (text == NULL)
return NULL;
if (((encoding == S_ENC_DEFAULT) || (encoding == S_ENC_UTF8))
&& (CACA_charset == CACA_ASCII)) {
/* convert from utf8 to ascii */
char const * s = str;
size_t len = strlen_utf8(str);
text = (char *) malloc(len + 1);
i = 0;
while ((*s != NUL) && (i < len)) {
size_t bytes;
uint32_t c = caca_utf8_to_utf32(s, &bytes);
if (bytes == 0) {
s++; /* skip illegal character */
} else {
text[i++] = caca_utf32_to_ascii(c);
s += bytes;
}
}
text[i] = NUL;
} else if ((encoding == S_ENC_CP437) && (CACA_charset == CACA_ASCII)) {
/* convert from cp437 to ascii */
text = (char *) gp_alloc(strlen(str) + 1, "text");
for (i = 0; i < strlen(str); i++) {
uint32_t c = caca_cp437_to_utf32(str[i]);
text[i] = caca_utf32_to_ascii(c);
}
text[strlen(str)] = NUL;
} else if (encoding == S_ENC_CP437) {
/* convert from cp437 to utf8 */
char * s = text = (char *) gp_alloc(4 * strlen(str) + 1, "text");
for (i = 0; i < strlen(str); i++) {
uint32_t c = caca_cp437_to_utf32(str[i]);
s += caca_utf32_to_utf8(s, c);
}
*s = NUL;
}
return text;
}
TERM_PUBLIC void
CACA_put_text(unsigned int x, unsigned int y, const char *str)
{
/* Convert text encoding if necessary. */
char * text = CACA_encode_text(str);
int xpos, ypos;
int xmax = x, ymax = y;
if (CACA_cv == NULL)
return;
if (CACA_skipplot && !CACA_keysample)
return;
if (CACA_next_text_attr != 0)
caca_set_attr(CACA_cv, CACA_next_text_attr);
if (CACA_ang == 0) {
/* Speed up the default case. */
int len = CACA_put_str(CACA_cv, x, CACA_ymax - y, text);
if (CACA_boxedtext.boxed) {
xpos = x; /* signed */
ypos = y;
if (CACA_boxedtext.box.left > xpos)
CACA_boxedtext.box.left = xpos;
if (CACA_boxedtext.box.right < (xpos + len))
CACA_boxedtext.box.right = xpos + len;
if (CACA_boxedtext.box.top < ypos)
CACA_boxedtext.box.top = ypos;
if (CACA_boxedtext.box.bottom > ypos)
CACA_boxedtext.box.bottom = ypos;
CACA_boxedtext.angle = 0;
}
if (CACA_keysample) {
CACA_update_keybox(CACA_plotno, x, y);
CACA_update_keybox(CACA_plotno, x + len, y);
}
} else {
int quadrant;
int angle = CACA_ang;
int xsign = 1, ysign = 1;
int i, n;
/* Table covers only first quadrant. */
quadrant = angle / 90;
switch (quadrant) {
case 0:
/* nothing to do */
break;
case 1:
angle = 90 - angle % 90;
xsign = -1;
break;
case 2:
angle %= 90;
xsign = ysign = -1;
break;
case 3:
angle = 90 - angle % 90;
ysign = -1;
break;
}
/* Find closest match in table to selected angle. */
i = 0;
while ((CACA_angles[i + 1].angle > 0) && (CACA_angles[i + 1].angle < angle)) i++;
if ((angle - CACA_angles[i].angle) > (CACA_angles[i + 1].angle - angle)) i++;
/* Print text */
if (CACA_charset == CACA_ASCII)
for (n = 0; n < strlen(text); n++) {
caca_put_char(CACA_cv, xmax = (x + xsign * n / CACA_angles[i].xdiv),
CACA_ymax - (ymax = (y + ysign * n / CACA_angles[i].ydiv)),
text[n]);
}
else {
int k = 0;
/* Text is encoded in utf8 */
for (n = k = 0; text[n] != 0; ) {
size_t bytes;
uint32_t c = caca_utf8_to_utf32(text + n, &bytes);
if (bytes > 0) {
n += bytes;
CACA_put_transparent_char(CACA_cv, xmax = (x + xsign * k / CACA_angles[i].xdiv),
CACA_ymax - (ymax = (y + ysign * k / CACA_angles[i].ydiv)), c);
k += caca_utf32_is_fullwidth(c) ? 2 : 1;
} else {
n++; /* skip invalid character */
}
}
}
if (CACA_boxedtext.boxed) {
xpos = x; /* signed */
ypos = y;
if (xmax > x) {
if (CACA_boxedtext.box.left > xpos)
CACA_boxedtext.box.left = xpos;
if (CACA_boxedtext.box.right < xmax)
CACA_boxedtext.box.right = xmax;
} else {
if (CACA_boxedtext.box.left > xmax)
CACA_boxedtext.box.left = xmax;
if (CACA_boxedtext.box.right < xpos)
CACA_boxedtext.box.right = xpos;
}
if (ymax > y) {
if (CACA_boxedtext.box.top < ymax)
CACA_boxedtext.box.top = ymax;
if (CACA_boxedtext.box.bottom > ypos)
CACA_boxedtext.box.bottom = ypos;
} else {
if (CACA_boxedtext.box.top < ypos)
CACA_boxedtext.box.top = ypos;
if (CACA_boxedtext.box.bottom > ymax)
CACA_boxedtext.box.bottom = ymax;
}
CACA_boxedtext.angle = CACA_ang;
}
if (CACA_keysample) {
CACA_update_keybox(CACA_plotno, x, y);
CACA_update_keybox(CACA_plotno, xmax, ymax);
}
}
if (CACA_next_text_attr != 0) {
caca_unset_attr(CACA_cv, CACA_next_text_attr);
CACA_next_text_attr = 0;
}
if (text != str)
free(text);
}
TERM_PUBLIC void
CACA_arrow(unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, int head)
{
if (CACA_cv == NULL)
return;
if (CACA_skipplot && !CACA_keysample)
return;
if ((head & HEADS_ONLY) == 0)
CACA_draw_thin_line(CACA_cv, sx, CACA_ymax - sy, ex, CACA_ymax - ey);
if ((head & BOTH_HEADS) != 0) {
const wchar_t ascii_arrows[] = L">+^+