--- /dev/null
+/* 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 */