Initial release of Maemo 5 port of gnuplot
[gnuplot] / term / apollo.trm
diff --git a/term/apollo.trm b/term/apollo.trm
new file mode 100644 (file)
index 0000000..e03ac93
--- /dev/null
@@ -0,0 +1,610 @@
+/* Hello, Emacs, this is -*-C-*-
+ * $Id: apollo.trm,v 1.14 2006/07/21 02:35:45 sfeam Exp $
+ *
+ */
+
+/* GNUPLOT - apollo.trm */
+/*
+    Apollo terminal driver for gnuplot.
+
+    Open a pad for the graphics, and use GPR routines.  For additional
+    speed, we do the graphics to a separate bitmap, and the blt the
+    entire bitmap to the display.  When the user specifies an output
+    file name, however, we draw directly to the screen, so the graphics
+    are written to the file correctly.  Thus, the user can save the
+    graphics in a file, to be viewed later.  If we try the bitmap
+    trick, it looks funny.
+
+    Ray Lischner (uunet!mntgfx!lisch)
+    4 October 1989     file created for gnuplot 1.1
+    26 March 1990      updated for gnuplot 2.0
+    30 October 1991    fixed minor problem in apollo_tic_sizes
+
+    As of 13 January 1999, this file has been placed in the
+    public domain by Ray Lischner.
+
+*/
+
+/*
+ * adapted to the new terminal layout by Stefan Bodewig (Dec. 1995)
+ */
+
+#include "driver.h"
+
+#ifdef TERM_REGISTER
+register_term(apollo)
+#endif
+
+#ifdef TERM_PROTO
+TERM_PUBLIC void APOLLO_init __PROTO((void));
+TERM_PUBLIC void APOLLO_graphics __PROTO((void));
+TERM_PUBLIC void APOLLO_linetype __PROTO((int ltype));
+TERM_PUBLIC void APOLLO_move __PROTO((unsigned int x, unsigned int y));
+TERM_PUBLIC void APOLLO_vector __PROTO((unsigned int x, unsigned int y));
+TERM_PUBLIC void APOLLO_text __PROTO((void));
+TERM_PUBLIC int APOLLO_text_angle __PROTO((int ang));
+TERM_PUBLIC int APOLLO_justify_text __PROTO((enum JUSTIFY mode));
+TERM_PUBLIC void APOLLO_put_text __PROTO((unsigned int x, unsigned int y, const char str[]));
+TERM_PUBLIC void APOLLO_reset __PROTO((void));
+/* default tick sizes for small windows */
+#define APOLLO_VTIC     6
+#define APOLLO_HTIC     6
+#endif
+
+#ifndef TERM_PROTO_ONLY
+#ifdef TERM_BODY
+
+#include <apollo/base.h>
+#include <apollo/error.h>
+#include <apollo/pad.h>
+#include <apollo/gpr.h>
+
+int apollo_isa_pad __PROTO((void));
+static void apollo_font_info __PROTO((struct termentry * tbl, char *fname));
+static void apollo_gpr_init __PROTO((struct termentry * tbl, pad_$window_desc_t * window));
+static void apollo_tic_sizes __PROTO((struct termentry * tbl));
+static void apollo_gpr_terminate __PROTO((void));
+static void apollo_redo_window __PROTO((pad_$window_desc_t * window));
+static void apollo_xy_error __PROTO((char *s, int x, int y, status_$t status));
+
+/* issue an error message, using additional text "s" */
+#define apollo_error(s)        error_$print_name(status, (s), strlen(s))
+
+/* if "status" indicates an error, then issue an error message */
+#define apollo_check(s)        if (status.all != status_$ok) apollo_error(s)
+
+static ios_$id_t stream = -1;  /* the stream for the pad */
+static gpr_$bitmap_desc_t screen_desc; /* the screen's bitmap */
+static gpr_$bitmap_desc_t bitmap_desc; /* the graphics bitmap */
+static gpr_$attribute_desc_t attr;     /* attribute block for saved bitmap */
+static int APOLLO_XMAX, APOLLO_YMAX;   /* window size */
+static short draw_width;       /* default GPR draw width */
+static name_$long_pname_t font_name;   /* font path name */
+static boolean use_bitmap;     /* use a separate bitmap? */
+
+/* return whether stdout is a DM pad */
+int
+apollo_isa_pad()
+{
+    status_$t status;
+    pad_$isa(1, &status);
+    return (status.all == status_$ok);
+}
+
+/*
+    Find out what the default font is for the pad, and save the
+    character height and width information.
+
+    Note that we must save the font file name because we need
+    to reload the font file everytime the window changes size.
+*/
+static void
+apollo_font_info(struct termentry *tbl, char *fname)
+{
+    short fwidth, fheight, flen;
+    status_$t status;
+
+    /* get the font size & update the termentry table */
+    pad_$inq_font(stream, &fwidth, &fheight, fname, name_$long_pnamlen_max,
+                 &flen, &status);
+    apollo_check("inq_font");
+    fname[flen] = NUL;
+
+    tbl->v_char = fheight;
+    tbl->h_char = fwidth;
+}
+
+/*
+    Initialize all the GPR stuff.  To save time, we draw into a separate
+    bitmap, and then blt it onto the screen all at once.  This results
+    in 5-10 times speed-up in the graphics, with only a little
+    complication.  Most of the complication is right here, making sure
+    we allocate the right bitmaps, etc., in the right order.  The rest
+    is in APOLLO_text(), where we actually BLT the bitmap onto the screen.
+    Everything else is the same.
+
+    The bitmaps have the same size as the window.  If the window changes
+    size, then the bitmaps retain the same size, so the user sees part
+    of the plot or a lot of space around the plot.  Drawing a new plot,
+    or replotting the previous one causes APOLLO_graphics() to see if
+    the window has changed size, in which case the GPR is terminated,
+    and this routine is called again.  Thus, make sure any changes
+    preserve this ability.  Anything that should only be done once
+    to the pad should be handled by APOLLO_init().
+
+    By the way, we save the current draw width, to be used later
+    for drawing extra wide lines.  This way we don't need to know
+    anything about the current output device characteristics;
+    we can just draw the default width, or twice the default width, etc.
+*/
+static void
+apollo_gpr_init(struct termentry *tbl, pad_$window_desc_t * window)
+{
+    gpr_$offset_t size;
+    short fontid;
+    status_$t status;
+
+    size.x_size = APOLLO_XMAX = tbl->xmax = window->width;
+    size.y_size = APOLLO_YMAX = tbl->ymax = window->height;
+
+    /* now initialize GPR */
+    gpr_$init(gpr_$frame, stream, size, 1, &screen_desc, &status);
+    apollo_check("gpr_$init");
+
+    if (use_bitmap) {
+       /* allocate the bitmap and its attribute block */
+       gpr_$allocate_attribute_block(&attr, &status);
+       apollo_check("allocate_attribute_block");
+
+       gpr_$allocate_bitmap(size, 1, attr, &bitmap_desc, &status);
+       apollo_check("allocate_bitmap");
+
+       gpr_$set_bitmap(bitmap_desc, &status);
+       apollo_check("set_bitmap");
+    }
+    /* set the font file */
+    gpr_$load_font_file(font_name, strlen(font_name), &fontid, &status);
+    apollo_check(font_name);
+
+    gpr_$set_text_font(fontid, &status);
+    apollo_check("set_text_font");
+
+    gpr_$inq_draw_width(&draw_width, &status);
+    apollo_check("inq_draw_width");
+}
+
+/*
+    Determine the tick sizes to be used for labelling borders.
+    By default, we use 1/50 of the window size, which looks nice to me.
+    If this makes the ticks too small, however, we use a minimum
+    size, to make sure they are visible.  The minimum size was also
+    determined experimentally.
+
+    Feel free to changes the sizes to something you feel looks better.
+
+    This routine must be called after apollo_gpr_init(), because we
+    need to know the window size, as stored in the termentry table.
+*/
+static void
+apollo_tic_sizes(struct termentry *tbl)
+{
+    /* base the tick size on the window size */
+    tbl->v_tic = tbl->ymax / 50;
+    if (tbl->v_tic < APOLLO_VTIC)
+       tbl->v_tic = APOLLO_VTIC;
+    tbl->h_tic = tbl->xmax / 50;
+    if (tbl->h_tic < APOLLO_HTIC)
+       tbl->h_tic = APOLLO_HTIC;
+}
+
+/*
+    Terminate the GPR.  This is called whenever the window size
+    changes, and we need to reinitialize the GPR.  I assume that
+    calling gpr_$terminate() also deallocates the bitmaps and
+    attribute blocks because deallocating the screen's bitmap
+    causes terminate() to think GPR has already been terminated.
+
+    Since this can be called many times, make sure nothing
+    drastic is done here, like closing the stream to the pad.
+    The only actions should be those that will be reinitialized
+    by apollo_gpr_init().
+*/
+static void
+apollo_gpr_terminate()
+{
+    status_$t status;
+
+    gpr_$terminate(false, &status);
+    apollo_check("terminate");
+}
+
+/*
+    Initialize the graphics.  This is called once, so we do things
+    here that should be done exactly once, such as opening the window.
+    I like to give windows names, so it is obvious what the window's
+    contents are, but this causes a transcript to be kept in a file
+    whose name is the window's name.  This might be nice in some
+    circumstances, but to me it is a nuisance, so the file is
+    deleted immediately.  The name is unlikely to appear normally,
+    so there should be little interference with users' normal files.
+    If the user has explicitly set the output file, however, then
+    we use that name, and do not delete the file.  Thus, the
+    user can get a metafile of the plot.  We can tell if the
+    output file has been set because outstr is NULL.  Otherwise,
+    outstr is the filename, in alloc-ed store.
+
+    The DM defaults are used for window sizes and positions.  If
+    the user doesn't like it, he or she can change is and issue
+    a replot command (assuming a plot has already been generated).
+
+    Note, also, that we must call pad_$set_scale() or else
+    pad_$inq_windows() returns scaled values, which is not what
+    we want.  Setting the scale to one (1) turns off the scaling,
+    so we get real pixel sizes.
+
+    Finally, we get the name and size of the default font.  The
+    name is kept, as explained earlier.  Then we can initialize
+    the GPR stuff.
+
+    Note that there is a way that APOLLO_init() gets called more
+    than once.  If the user issues the "set terminal apollo" command
+    more than once, then this is called, so we need to make sure
+    that we do not keep creating windows.
+
+    An alternative strategy would be to interpret multiple "set term
+    apollo"s to mean create multiple windows.  The user could only
+    access the most recent window because gnuplot has no concept of
+    multiple terminals.  The user would, in fact, have no way of
+    removing old windows because they are still active.  We could try
+    catching keyboard events to see if the user presses EXIT, but I do
+    not want to start getting into that mess.  If the user really
+    wants this kind of functionality, then he or she can run gnuplot
+    multiple times.  I think that is a lot cleaner, don't you?
+*/
+TERM_PUBLIC void
+APOLLO_init()
+{
+    /* only initialize once */
+    if (stream == -1) {
+       struct termentry *tbl;
+       pad_$window_desc_t window;
+       name_$long_name_t wname;
+       short wnum;             /* junk needed by pad_$inq_windows() */
+       boolean unlink_wname;
+       status_$t status;
+
+       tbl = term;
+
+       /* make the window name unique, with "gnuplot" in the label */
+       if (outstr == NULL) {
+           sprintf(wname, "gnuplot-%d", getpid());
+           unlink_wname = true;
+       } else {
+           strcpy(wname, outstr);
+           unlink_wname = false;
+       }
+
+       use_bitmap = unlink_wname;
+
+       /* use the default window position and size */
+       window.top = window.left = window.width = window.height = 0;
+       pad_$create_window(wname, strlen(wname), pad_$transcript, 1, window,
+                          &stream, &status);
+       apollo_check("create_window");
+
+       /* if this is not the user's name, then delete the file */
+       if (unlink_wname)
+           unlink(wname);
+
+       /* remove all scaling, to revert to pixel units, not char. units */
+       pad_$set_scale(stream, 1, 1, &status);
+       apollo_check("set_scale");
+
+       /* get rid of the window when the program exits */
+       pad_$set_auto_close(stream, 1, true, &status);
+       apollo_check("set_auto_close");
+
+       /* now determine the window size & update the termentry table */
+       pad_$inq_windows(stream, &window, 1, &wnum, &status);
+       apollo_check("inq_windows");
+
+       /* the order of the next three calls is important */
+       apollo_font_info(tbl, font_name);
+       apollo_gpr_init(tbl, &window);
+       apollo_tic_sizes(tbl);
+    }
+}
+
+/*
+    Prepare for graphics output.  Since this is what the user wants to
+    do when preparing a new plot, this is a meaningful time to see if
+    the window has changed size.  Thus, we avoid mucking about with
+    asynchronous traps, and we avoid the bigger problem of dealing
+    with a half-finished plot when the window changes size.
+
+    Simply put, get the current window size, and if it has changed,
+    then get rid of the old bitmaps, etc., and allocate new ones at
+    the new size.  We also need to update the termentry table.
+    If the window stays the same size, then just clear it.
+*/
+static void
+apollo_redo_window(pad_$window_desc_t * window)
+{
+    struct termentry *tbl = term;
+    status_$t status;
+
+    /* the order of the following calls is important */
+    apollo_gpr_terminate();
+    apollo_gpr_init(tbl, window);
+    apollo_tic_sizes(tbl);
+}
+
+TERM_PUBLIC void
+APOLLO_graphics()
+{
+    pad_$window_desc_t window;
+    short wnum;
+    status_$t status;
+
+    pad_$inq_windows(stream, &window, 1, &wnum, &status);
+    apollo_check("inq_windows");
+
+    if (window.width != APOLLO_XMAX || window.height != APOLLO_YMAX)
+       apollo_redo_window(&window);
+    else {
+       gpr_$clear(0, &status);
+       apollo_check("clear");
+    }
+}
+
+/* set a line type:
+   -2 heavy, solid     (border)
+   -1 heavy, dotted    (axis)
+   0  solid            (normal)
+   1  dots             (other curves)
+   2  short dash
+   3  long dash
+   4  dash dot
+
+   Apparently, GPUplot draws a lot of short line segments, and each
+   one starts a new pattern.  This makes the patterns somewhat useless,
+   but one can still tell the difference between solid, dotted, and
+   dashed lines.  The utility of fancier styles is limited, however.
+
+   On a color workstation, we should use different colors, but I
+   don't have one.
+*/
+
+/*
+    To draw different line styles on an Apollo, we use two different
+    parameters.  One is a line thickness, which is just an integral
+    multiple of the default line thickness.  The second is a 16-bit
+    pattern that is repeated.  We could use fancier patterns, since
+    GPR supports up to 64-bits, but, as I explained earlier, this
+    really does not buy us anything.
+
+    I used patterns that do not start with all bits on because
+    gnuplot seems to use lots of short line segments to draw
+    a curve, and this might make a very curvey plot seem like
+    a solid line, regardless of pattern.  I don't want to start
+    with too many zeros, however, or else the curve might not
+    appear at all!  All the patterns, therefore, start with one
+    bit on.  The rest of the bits determine the true pattern.
+
+    By examining graphics.c, we see that linetype -2 is used exclusively
+    for the border, -1 for the axes, and the non-negative integers for
+    the curves.  We use heavy lines for the border and axes, and normal
+    width lines for the curves.
+
+    Since C arrays start at zero, make sure all the offsets are correct,
+    so that it is easy to access the array with -2...n linetypes.
+*/
+
+typedef struct {
+    short width;
+    short pattern;
+} APOLLO_LINE;
+
+static APOLLO_LINE apollo_lines[] =
+{
+    {2, ~0},                   /* heavy, solid */
+    {2, 0x6666},               /* heavy, dotted */
+    {1, ~0},                   /* normal */
+    {1, 0xAAAA},               /* dotted */
+    {1, 0xC3C3},               /* short dash */
+    {1, 0xE01F},               /* long dash */
+    {1, 0x87F8},               /* dash dot */
+    {1, 0x6666},               /* big dots */
+};
+
+#define BITS_PER_LINETYPE      16
+
+/* apollo_line(-2) is the border style, etc. */
+#define apollo_line(x)         apollo_lines[(x)+2]
+#define apollo_pattern(x)      &apollo_line(x).pattern
+#define apollo_width(x)                apollo_line(x).width
+
+#define APOLLO_MIN_LINE                (-2)
+#define APOLLO_MAX_LINE                (sizeof(apollo_lines)/sizeof(*apollo_lines)-2)
+
+/* set the line style */
+TERM_PUBLIC void
+APOLLO_linetype(int ltype)
+{
+    status_$t status;
+
+    if (ltype < APOLLO_MIN_LINE)
+       ltype = APOLLO_MIN_LINE;
+    if (ltype >= APOLLO_MAX_LINE)
+       ltype %= APOLLO_MAX_LINE;
+
+    gpr_$set_line_pattern(1, apollo_pattern(ltype), BITS_PER_LINETYPE, &status);
+    apollo_check("set_line_pattern");
+
+    gpr_$set_draw_width(draw_width * apollo_width(ltype), &status);
+    apollo_check("set_draw_width");
+}
+
+/* issue an error message that includes an (x, y) coordinate */
+static void
+apollo_xy_error(char *s, int x, int y, status_$t status)
+{
+    char buffer[128];
+
+    sprintf(buffer, "%s(%d, %d)", s, x, y);
+    apollo_error(buffer);
+}
+
+#define apollo_xy_check(s)     \
+    if (status.all != status_$ok) apollo_xy_error((s), x, y, status)
+
+/*
+    Note that gnuplot and GPR have reversed ideas of where the Y origin is.
+    This means subtracting the Y coordinate from Y max.
+*/
+#define plot_to_gpr(y)         (APOLLO_YMAX - (y))
+
+/* move to a new position */
+TERM_PUBLIC void
+APOLLO_move(unsigned int x, unsigned int y)
+{
+    status_$t status;
+
+    gpr_$move((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
+    apollo_xy_check("move");
+}
+
+/* draw a line to a new position */
+TERM_PUBLIC void
+APOLLO_vector(unsigned int x, unsigned int y)
+{
+    status_$t status;
+
+    gpr_$line((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
+    apollo_xy_check("line");
+}
+
+/*
+    On terminals, this switches to text mode.  The real meaning,
+    however, is that the graphics are finished.  This means we can
+    now display the saved bitmap.
+*/
+TERM_PUBLIC void
+APOLLO_text()
+{
+    if (use_bitmap) {
+       static gpr_$position_t pos;     /* always zero */
+       gpr_$window_t window;
+       status_$t status;
+
+       /* bitblt the entire bitmap to the entire window */
+       window.window_base.x_coord = 0;
+       window.window_base.y_coord = 0;
+       window.window_size.x_size = APOLLO_XMAX;
+       window.window_size.y_size = APOLLO_YMAX;
+
+       gpr_$set_bitmap(screen_desc, &status);
+       apollo_check("set_bitmap(screen_desc)");
+
+       gpr_$pixel_blt(bitmap_desc, window, pos, &status);
+       apollo_check("bitblt");
+
+       gpr_$set_bitmap(bitmap_desc, &status);
+       apollo_check("set_bitmap(bitmap_desc)");
+    }
+}
+
+TERM_PUBLIC int
+APOLLO_text_angle(int ang)
+{
+    status_$t status;
+
+    gpr_$set_text_path(ang ? gpr_$up : gpr_$right, &status);
+    apollo_check("set_text_path");
+    return TRUE;
+}
+
+static enum JUSTIFY apollo_text_mode;
+
+TERM_PUBLIC int
+APOLLO_justify_text(enum JUSTIFY mode)
+{
+    apollo_text_mode = mode;
+    return TRUE;
+}
+
+/*
+    Write "str" right justified on row "row".  A row is assumed to
+    have whatever height the current text has.  Make sure the
+    text does not cover the tick marks.
+*/
+TERM_PUBLIC void
+APOLLO_put_text(unsigned int x, unsigned int y, const char str[])
+{
+    gpr_$offset_t size;
+    status_$t status;
+
+    gpr_$inq_text_extent(str, strlen(str), &size, &status);
+    apollo_check("inq_text_extent");
+
+    y -= size.y_size / 2;      /* center around "y" */
+    switch (apollo_text_mode) {
+    case LEFT:
+       break;
+    case CENTRE:
+       x -= size.x_size / 2;
+       break;
+    case RIGHT:
+       x -= size.x_size;
+       break;
+    }
+    APOLLO_move(x, y);
+
+    gpr_$text(str, strlen(str), &status);
+    apollo_check("put_text");
+}
+
+/* reset the graphics state and terminate */
+TERM_PUBLIC void
+APOLLO_reset()
+{
+    if (stream != -1) {
+       apollo_gpr_terminate();
+       stream = -1;
+    }
+}
+
+#endif /* TERM_BODY */
+
+#ifdef TERM_TABLE
+TERM_TABLE_START(apollo_driver)
+    "apollo",
+    "Apollo Graphics Primitive Resource, rescaling of subsequent plots after window resizing",
+    0, 0, 0, 0,        /* APOLLO_XMAX, APOLLO_YMAX, APOLLO_VCHAR, APOLLO_HCHAR, are filled in at run-time */
+    APOLLO_VTIC, APOLLO_HTIC, options_null, APOLLO_init, APOLLO_reset,
+    APOLLO_text, null_scale, APOLLO_graphics, APOLLO_move, APOLLO_vector,
+    APOLLO_linetype, APOLLO_put_text, APOLLO_text_angle,
+    APOLLO_justify_text, line_and_point, do_arrow, set_font_null
+TERM_TABLE_END(apollo_driver)
+
+#undef LAST_TERM
+#define LAST_TERM apollo_driver
+
+#endif /* TERM_TABLE */
+#endif /* TERM_PROTO_ONLY */
+
+#ifdef TERM_HELP
+START_HELP(apollo)
+"1 apollo",
+"?commands set terminal apollo",
+"?set terminal apollo",
+"?set term apollo",
+"?terminal apollo",
+"?term apollo",
+"?apollo",
+" The `apollo` terminal driver supports the Apollo Graphics Primitive Resource",
+" with rescaling after window resizing.  It has no options.",
+"",
+" If a fixed-size window is desired, the `gpr` terminal may be used instead."
+END_HELP(apollo)
+#endif /* TERM_HELP */