The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * $Id: atariaes.trm,v 1.13 2001/02/01 17:56:05 broeker Exp $
 *
 */

/* GNUPLOT - atari.trm */

/*[
 * Copyright 1992, 1993, 1998
 *
 * 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:
 *   Atari Screens working with the normal VDI
 *     (this should include TT and big screens)
 *
 * AUTHORS
 *  Alexander Lehmann
 *  HE Koechling
 *
 * send your comments or suggestions to (info-gnuplot@dartmouth.edu).
 *
 * ATARI-related comments please to alexlehm@iti.informatik.th-darmstadt.de
 *
 */

#include "driver.h"

#ifdef TERM_REGISTER
register_term(atari)
#endif

#ifdef TERM_PROTO

/* function-prototypes */
TERM_PUBLIC void ATARI_options __PROTO((void));
TERM_PUBLIC void ATARI_init __PROTO((void));
TERM_PUBLIC void ATARI_reset __PROTO((void));
TERM_PUBLIC void ATARI_graphics __PROTO((void));
TERM_PUBLIC void ATARI_text __PROTO((void));
TERM_PUBLIC void ATARI_move __PROTO((unsigned int x, unsigned int y));
TERM_PUBLIC void ATARI_vector __PROTO((unsigned int x, unsigned int y));
TERM_PUBLIC void ATARI_linetype __PROTO((int lt));
TERM_PUBLIC void ATARI_put_text __PROTO((unsigned int x, unsigned int y, const char *str));
TERM_PUBLIC int ATARI_text_angle __PROTO((int ang));
TERM_PUBLIC int ATARI_justify_text __PROTO((enum JUSTIFY mode));
TERM_PUBLIC void ATARI_point __PROTO((unsigned int x, unsigned int y, int number));

/* default to hi-res */
#define ATARI_XMAX 640
#define ATARI_YMAX 400
#define ATARI_VCHAR 16
#define ATARI_HCHAR 8
#define ATARI_HTIC (ATARI_XMAX/100)
#define ATARI_VTIC ATARI_HTIC

#endif /* TERM_PROTO */

#ifndef TERM_PROTO_ONLY
#ifdef TERM_BODY

#ifdef __PUREC__
/* why did they have to change these names ??? */
# include <aes.h>
# include <vdi.h>
# include <tos.h>
#else /* !__PUREC__ i.e. __GNUC__, maybe others */
# include <aesbind.h>
# include <vdibind.h>
# include <osbind.h>
extern short _global[];
#endif

#include <ctype.h>

static void open_window __PROTO((void));
static void close_window __PROTO((void));
static void text_mode __PROTO((void));
static void mouse_mode __PROTO((void));
static void application_exit __PROTO((void));
void application_init __PROTO((void));
static int intersect __PROTO((int *x1, int *y1, int *w1, int *h1, int x2, int y2, int w2, int h2));
static void walk_rects __PROTO((void (*draw) (int, int, int, int), int x1, int y1, int w1, int h1));
static void clear __PROTO((int x, int y, int w, int h));
static void process_message __PROTO((int *msg));
long poll_events __PROTO((int waitkey));
static void f_line __PROTO((int x, int y, int w, int h));
static void flush_line __PROTO((void));
static void put_text __PROTO((int x, int y, int w, int h));

static int multi_aes;
static int window_id = -1;
static int vdi_handle = -1;
static int win_xpos, win_ypos;
static int win_xsize, win_ysize;

#define ATARI_yc(y) (ATARI_maxycoord-(y))
static int ATARI_linetypes[] =
{
    0xffff, 0x1111,
    0xffff, 0x5555, 0x3333, 0x7777,
    0x3f3f, 0x0f0f, 0x5f5f, 0xe4e4, 0x55f5
};

#define ATARI_LINETYPES_MAX     (sizeof(ATARI_linetypes)/sizeof(int))

static int ATARI_lt;
static int ATARI_maxycoord;
static int ATARI_rotation;
static int ATARI_numcolors;
static int pxy[128];		/* Maximum of 64 pixels per v_pline */
static int pxy_index;
static int ATARI_colors[16];
static int ATARI_savecolors[16][3];
static int ATARI_numpalette;

#define ATARI_c_height_default 6	/* well, well ...               */

static int ATARI_c_height = ATARI_c_height_default;

static int cursor_is_on = FALSE;
static int put_text_x;
static int put_text_y;
static char *put_text_str;

/* don't change this without changing process_message() below */
#define WINTYPE (NAME|CLOSER|FULLER|MOVER|SIZER)

static void
open_window(void)
{
    int x, y, w, h;

    if (window_id == -1) {
	wind_calc(WC_BORDER, WINTYPE, win_xpos, win_ypos,
		  win_xsize, win_ysize, &x, &y, &w, &h);
	window_id = wind_create(WINTYPE, x, y, w, h);

	if (window_id < 0) {
	    window_id = -1;
	    int_error(NO_CARET, "Can't create window");
	}
	wind_set(window_id, WF_NAME, "gnuplot output");
	wind_open(window_id, x, y, w, h);
    }
}

static void
close_window(void)
{
    if (window_id != -1) {
	wind_close(window_id);
	wind_delete(window_id);
	window_id = -1;
    }
}

static void
text_mode(void)
{
    if (!multi_aes) {
	if (!cursor_is_on) {
	    cursor_is_on = TRUE;
	    graf_mouse(M_OFF, NULL);
	    fputs("\033e", stderr);
	}
	fputs("\033E", stderr);
    }
}

static void
mouse_mode(void)
{
    if (!multi_aes) {
	if (cursor_is_on) {
	    cursor_is_on = FALSE;
	    graf_mouse(M_ON, NULL);
	    fputs("\033f", stderr);
	}
    }
}

static void
application_exit(void)
{
    close_window();

    if (vdi_handle != -1) {
	v_clsvwk(vdi_handle);
    }
    mouse_mode();
}

void
application_init(void)
{
    if (aesid < 0) {
	if ((aesid = appl_init()) < 0)
	    int_error(NO_CARET, "APPL_INIT failed !");
    }
#ifdef __PUREC__
    multi_aes = _GemParBlk.global[1] == -1;
#else
    /* tested with gcc only */
    multi_aes = _global[1] == -1;
#endif

    if (!multi_aes)
	graf_mouse(ARROW, NULL);
    else
	menu_register(aesid, "  Terminal: atari");

    text_mode();
    atexit(application_exit);
}

static int
intersect(int *x1, int *y1, int *w1, int *h1, int x2, int y2, int w2, int h2)
{
    if (*x1 > x2) {
	w2 -= *x1 - x2;
	x2 = *x1;
    } else {
	(*w1) -= x2 - *x1;
	*x1 = x2;
    }
    if (*y1 > y2) {
	h2 -= *y1 - y2;
	y2 = *y1;
    } else {
	(*h1) -= y2 - *y1;
	*y1 = y2;
    }
    if (*w1 > w2) {
	*w1 = w2;
    } else {
	w2 = *w1;
    }
    if (*h1 > h2) {
	*h1 = h2;
    } else {
	h2 = *h1;
    }

    return (*w1) > 0 && (*h1) > 0;
}

static void
walk_rects(void (*draw) (int, int, int, int), int x1, int y1, int w1, int h1)
{
    int x, y, w, h;
    int pxy[4];

    wind_update(BEG_UPDATE);
    graf_mouse(M_OFF, NULL);

    wind_get(window_id, WF_FIRSTXYWH, &x, &y, &w, &h);
    while (w > 0 && h > 0) {
	if (intersect(&x, &y, &w, &h, x1, y1, w1, h1)) {
	    pxy[0] = x;
	    pxy[1] = y;
	    pxy[2] = x + w - 1;
	    pxy[3] = y + h - 1;
	    vs_clip(vdi_handle, 1, pxy);
	    (*draw) (x, y, w, h);
	}
	wind_get(window_id, WF_NEXTXYWH, &x, &y, &w, &h);
    }
    vs_clip(vdi_handle, 0, pxy);
    graf_mouse(M_ON, NULL);
    wind_update(END_UPDATE);
}

static void
clear(int x, int y, int w, int h)
{
    static MFDB mfdb = { NULL };
    int pxy[8];

    pxy[0] = pxy[4] = x;
    pxy[1] = pxy[5] = y;
    pxy[2] = pxy[6] = x + w - 1;
    pxy[3] = pxy[7] = y + h - 1;

    vro_cpyfm(vdi_handle, ALL_WHITE /*0 */ , pxy, &mfdb, &mfdb);
}

static void
process_message(int *msg)
{
    static int is_fulled = FALSE;
    static int small_xpos, small_ypos, small_xsize, small_ysize;
    int x, y, w, h;

    if (window_id == -1 || msg[3] != window_id)
	return;

    switch (msg[0]) {
    case WM_REDRAW:
	walk_rects(clear, msg[4], msg[5], msg[6], msg[7]);
	break;

    case WM_CLOSED:
	close_window();
	break;

    case WM_TOPPED:
	wind_set(window_id, WF_TOP, window_id);
	break;

    case WM_MOVED:
    case WM_SIZED:
	is_fulled = 0;
	wind_calc(WC_WORK, WINTYPE, msg[4], msg[5], msg[6], msg[7],
		  &win_xpos, &win_ypos, &win_xsize, &win_ysize);
	wind_set(window_id, WF_CURRXYWH, msg[4], msg[5], msg[6], msg[7]);
	break;

    case WM_FULLED:
	if (!is_fulled) {
	    is_fulled = TRUE;
	    small_xpos = win_xpos;
	    small_ypos = win_ypos;
	    small_xsize = win_xsize;
	    small_ysize = win_ysize;

	    wind_get(0, WF_WORKXYWH, &x, &y, &w, &h);
	    wind_calc(WC_WORK, WINTYPE, x, y, w, h, &win_xpos, &win_ypos,
		      &win_xsize, &win_ysize);
	} else {
	    is_fulled = FALSE;
	    win_xpos = small_xpos;
	    win_ypos = small_ypos;
	    win_xsize = small_xsize;
	    win_ysize = small_ysize;
	}
	wind_calc(WC_BORDER, WINTYPE, win_xpos, win_ypos, win_xsize,
		  win_ysize, &x, &y, &w, &h);
	wind_set(window_id, WF_CURRXYWH, x, y, w, h);
	break;
    }
}

long
poll_events(int waitkey)
{
    int msg[8];
    int dummy;
    int which;
    int key = 0;
    /* 1/10th second when waiting for key, poll otherwise */
    unsigned long time = (waitkey ? 100L : 1L);

    do {
	which = evnt_multi(
			      (waitkey ? (MU_MESAG | MU_KEYBD | MU_TIMER) : (MU_MESAG | MU_TIMER)),
			      0, 0, 0,
			      0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0,
			      msg,
#ifdef __PUREC__
			      time, 0,
#else
			      time,
#endif
			      &dummy, &dummy, &dummy, &dummy, &key, &dummy);

	if (which & MU_MESAG) {
	    process_message(msg);
	}
    } while (which & MU_MESAG);

    if (which & MU_KEYBD) {
	return ((Kbshift(-1) & 7) << 24) | ((long) (key & 0xff00) << 8) | (key & 0xff);
    } else {
	return 0;
    }
}

static void
f_line(int x, int y, int w, int h)
{
    v_pline(vdi_handle, pxy_index, pxy);
}

static void
flush_line()
{
    int line_type;
    int color_index;
    int i;

    if (pxy_index >= 2) {
	if (ATARI_numcolors == 2) {	/* Monochrome */
	    color_index = 1;
	    line_type = ATARI_lt;
	    if (line_type >= 0)
		line_type %= (ATARI_LINETYPES_MAX - 2);
	} else {		/* Color */
	    if (ATARI_lt < 0) {
		color_index = 1;
		line_type = ATARI_lt;
	    } else {
		color_index = 2 + ATARI_lt % (ATARI_numcolors - 2);
		line_type = (ATARI_lt / (ATARI_numcolors - 2)) % (ATARI_LINETYPES_MAX - 2);
	    }
	}

	vswr_mode(vdi_handle, MD_TRANS);
	vsl_color(vdi_handle, color_index);

	vsl_type(vdi_handle, 7);
	vsl_udsty(vdi_handle, ATARI_linetypes[line_type + 2]);
	walk_rects(f_line, win_xpos, win_ypos, win_xsize, win_ysize);
    }
    if (pxy_index >= 1) {
	pxy[0] = pxy[2 * (pxy_index - 1)];
	pxy[1] = pxy[2 * (pxy_index - 1) + 1];
	pxy_index = 1;
    }
}

static void
put_text(int x, int y, int w, int h)
{
    v_gtext(vdi_handle, put_text_x, put_text_y, put_text_str);
}

TERM_PUBLIC void
ATARI_options()
{
#define ATARIHEXERROR "palette values 3 hex digits, please"
#define ATARIHEIGHTERROR "expecting a character height"
    char opt[6];		/* maximum: 'fff'\0 */
    int i;
    char *tok_end;

    term_options[0] = NUL;
    ATARI_c_height = ATARI_c_height_default;

    for (i = 0; i < 17; i++) {
	if (END_OF_COMMAND)
	    break;
	if (token[c_token].length > 5) {
	    ATARI_numpalette = 0;
	    ATARI_c_height = ATARI_c_height_default;
	    term_options[0] = NUL;
	    int_error(c_token, ATARIHEXERROR);
	}
	capture(opt, c_token, c_token, 6);
	if (!i) {
	    ATARI_c_height = strtoul(opt, &tok_end, 10);
	    if (*tok_end != NUL) {
		ATARI_numpalette = 0;
		ATARI_c_height = ATARI_c_height_default;
		term_options[0] = NUL;
		int_error(c_token, ATARIHEIGHTERROR);
	    }
	    if (ATARI_c_height > 999)
		ATARI_c_height = 999;	/* avoid opt length overflow */
	    sprintf(opt, "%d ", ATARI_c_height);
	} else {
	    if (*opt == '"' || *opt == '\'') {
		opt[strlen(opt) - 1] = NUL;
		strcpy(opt, opt + 1);
	    }
	    ATARI_colors[i - 1] = strtoul(opt, &tok_end, 16);
	    if (*tok_end != NUL) {
		ATARI_numpalette = 0;
		ATARI_c_height = ATARI_c_height_default;
		term_options[0] = NUL;
		int_error(c_token, ATARIHEXERROR);
	    }
	    sprintf(opt, "%X ", ATARI_colors[i - 1]);

	    /* do we need to quote? */
	    if (isdigit((unsigned char) *opt) && strpbrk(opt, "ABCDEF")) {
		sprintf(opt, "\"%X\" ", ATARI_colors[i - 1]);
	    }
	}
	strcat(term_options, opt);
	c_token++;
    }
    ATARI_numpalette = (i == 0 ? 0 : i - 1);
    /* printf("Number of linetypes:%d\n", ATARI_LINETYPES_MAX); */
}

TERM_PUBLIC void
ATARI_init()
{
    int work_in[11];
    int work_out[57];
    int i;
    int hchar, wchar, dummy;
    int rgb[3];
    int num_save;
    char *colors, *tok_end;
    int x, y, w, h;
    static int have_win_size = 0;

    application_init();

    if (ATARI_numpalette == 0 && (colors = getenv("GNUCOLORS")) && *colors) {
	for (i = 0; i < 17; i++) {
	    if (!i) {
		ATARI_c_height = strtoul(colors, &tok_end, 10);
		if (colors == tok_end) {
		    i = 0;
		    ATARI_c_height = ATARI_c_height_default;
		    break;
		}
	    } else {
		if (*colors == '\0')
		    break;
		ATARI_colors[i] = strtoul(colors, &tok_end, 16);
		if (colors == tok_end || (unsigned) ATARI_colors[i] > 0xfff) {
		    i = 0;
		    break;
		}
	    }
	    colors = tok_end;

	    while (*colors == ' ')
		colors++;
	}
	ATARI_numpalette = (i == 0 ? 0 : i - 1);
    }
    vdi_handle = graf_handle(&dummy, &dummy, &dummy, &dummy);

    if (!vdi_handle)
	int_error(NO_CARET, "Fatal error opening virtual workstation");

    for (i = 0; i < 10; work_in[i++] = 1);
    work_in[10] = 2;		/* use raster coordinates */
    v_opnvwk(work_in, &vdi_handle, work_out);

    if (!vdi_handle)
	int_error(NO_CARET, "Fatal error opening virtual workstation");

    if (!have_win_size) {
	wind_get(0, WF_WORKXYWH, &x, &y, &w, &h);
	wind_calc(WC_WORK, WINTYPE, x, y, w, h, &win_xpos, &win_ypos,
		  &win_xsize, &win_ysize);
	have_win_size = 1;
    }
    term->xmax = win_xsize;
    term->ymax = win_ysize;

    vst_height(vdi_handle, ATARI_c_height, &dummy, &dummy, &wchar, &hchar);

    term->h_char = wchar;
    term->v_char = hchar;	/* hchar stands for height this time */
    term->h_tic = win_xsize / 100;
    term->v_tic = term->h_tic;

    ATARI_maxycoord = win_ysize - 1;
    ATARI_numcolors = work_out[13];

    for (i = 0; i < ATARI_numpalette; i++) {
	vq_color(vdi_handle, i, 1, ATARI_savecolors[i]);

	rgb[0] = 1000 * (ATARI_colors[i] >> 8);
	rgb[0] /= 15;
	rgb[1] = 1000 * ((ATARI_colors[i] >> 4) & 15);
	rgb[1] /= 15;
	rgb[2] = 1000 * (ATARI_colors[i] & 15);
	rgb[2] /= 15;
	vs_color(vdi_handle, i, rgb);
    }
    pxy_index = 0;
}

TERM_PUBLIC void
ATARI_reset()
{
    int i;

    close_window();
    if (vdi_handle != -1) {
	for (i = 0; i < ATARI_numpalette; i++) {
	    vs_color(vdi_handle, i, ATARI_savecolors[i]);
	}
	v_clsvwk(vdi_handle);
	vdi_handle = -1;
    }
}

TERM_PUBLIC void
ATARI_graphics()
{
    ATARI_maxycoord = win_ysize - 1;

    term->xmax = win_xsize;
    term->ymax = win_ysize;

    mouse_mode();
    open_window();
    poll_events(0);
    walk_rects(clear, win_xpos, win_ypos, win_xsize, win_ysize);

    pxy_index = 0;
}

TERM_PUBLIC void
ATARI_text()
{
    flush_line();
    if (!multi_aes) {
	while (window_id != -1 && !poll_events(1));
	close_window();
    }
    text_mode();
}

TERM_PUBLIC void
ATARI_move(unsigned int x, unsigned int y)
{
    flush_line();

    pxy[0] = x + win_xpos;
    pxy[1] = ATARI_yc(y) + win_ypos;
    pxy_index = 1;
}

TERM_PUBLIC void
ATARI_vector(unsigned int x, unsigned int y)
{
    pxy[2 * pxy_index] = x + win_xpos;
    pxy[2 * pxy_index + 1] = ATARI_yc(y) + win_ypos;
    pxy_index++;

    if (pxy_index == 64) {	/* we're all full */
	flush_line();
    }
}

TERM_PUBLIC void
ATARI_linetype(int lt)
{
    flush_line();

    ATARI_lt = lt;
}

TERM_PUBLIC void
ATARI_put_text(unsigned int x, unsigned int y, const char *str)
{
    int vchar = term->v_char;
    int dummy;

    if (!strlen(str))
	return;

    if (x < 0)
	x = 0;
    if (y < 0)
	y = 0;

    /* align text left and to middle of char height */
    vst_alignment(vdi_handle, 0, 5, &dummy, &dummy);
    vst_rotation(vdi_handle, (ATARI_rotation ? 900 : 0));
    if (ATARI_rotation) {
	put_text_x = x - vchar / 2 + 1 + win_xpos;
	put_text_y = ATARI_yc(y) - 1 + win_ypos;
	put_text_str = str;
    } else {
	put_text_x = x + 1 + win_xpos;
	put_text_y = ATARI_yc(y) - vchar / 2 + 1 + win_ypos;
	put_text_str = str;
    }
    walk_rects(put_text, win_xpos, win_ypos, win_xsize, win_ysize);
}

TERM_PUBLIC int
ATARI_text_angle(int ang)
{
    ATARI_rotation = ang;

    return TRUE;
}

TERM_PUBLIC int
ATARI_justify_text(enum JUSTIFY mode)
{
    return FALSE;
}

TERM_PUBLIC void
ATARI_point(unsigned int x, unsigned int y, int number)
{
    int old_linetype;

    if (ATARI_numcolors == 2) {
	line_and_point(x, y, number);	/* monochrome */
    } else {
	/* we map colors that exceed our limit to dotted lines, but we can't do
	   that with the markers (sortof a generalized line_and_point) */
	old_linetype = ATARI_lt;
	if (ATARI_lt > ATARI_numcolors - 2)
	    ATARI_linetype(ATARI_lt % (ATARI_numcolors - 2));	/* same color, but no dots */
	do_point(x, y, number);
	ATARI_linetype(old_linetype);
    }
}

#endif /* TERM_BODY */

#ifdef TERM_TABLE

TERM_TABLE_START(atari_driver)
    "atari", "Atari AES-Terminal",
    ATARI_XMAX, ATARI_YMAX, ATARI_VCHAR, ATARI_HCHAR,
    ATARI_VTIC, ATARI_HTIC, ATARI_options, ATARI_init, ATARI_reset,
    ATARI_text, null_scale, ATARI_graphics, ATARI_move, ATARI_vector,
    ATARI_linetype, ATARI_put_text, ATARI_text_angle,
    ATARI_justify_text, ATARI_point, do_arrow, set_font_null,
    0, TERM_CAN_MULTIPLOT, 0, 0
TERM_TABLE_END(atari_driver)

#undef LAST_TERM
#define LAST_TERM atari_driver

#endif /* TERM_TABLE */

#endif /* TERM_PROTO_ONLY */

#ifdef TERM_HELP
START_HELP(atari)
"1 atari ST (via AES)",
"?commands set terminal atari",
"?set terminal atari",
"?set term atari",
"?terminal atari",
"?term atari",
"?atari",
" The `atari` terminal has options to set the character size and the screen",
" colors.",
"",
" Syntax:",
"       set terminal atari {<fontsize>} {<col0> <col1> ... <col15.}",
"",
" The character size must appear if any colors are to be specified.  Each of",
" the (up to 16) colors is given as a three-digit hex number, where the digits",
" represent RED, GREEN and BLUE (in that order).  The range of 0--15 is scaled",
" to whatever color range the screen actually has.  On a normal ST screen, odd",
" and even intensities are the same.",
"",
" Examples:",
"       set terminal atari 4    # use small (6x6) font",
"       set terminal atari 6 0  # set monochrome screen to white on black",
"       set terminal atari 13 0 fff f00 f0 f ff f0f",
"                  # set first seven colors to black, white, green, blue,",
"                  # cyan, purple, and yellow and use large font (8x16).",
"",
" Additionally, if an environment variable GNUCOLORS exists, its contents are",
" interpreted as an options string, but an explicit terminal option takes",
" precedence."
END_HELP(atari)
#endif /* TERM_HELP */