home *** CD-ROM | disk | FTP | other *** search
/ CICA 1995 May / cica_0595_4.zip / cica_0595_4 / UTIL / GPT34SRC / TERM / APOLLO.TRM < prev    next >
Text File  |  1993-05-11  |  18KB  |  540 lines

  1. /*
  2.  * $Id: apollo.trm 3.38.2.5 1992/11/10 02:08:00 woo Exp $
  3.  *
  4.  */
  5.  
  6. /* GNUPLOT - apollo.trm */
  7. /*
  8.     Apollo terminal driver for GNUplot.
  9.  
  10.     Open a pad for the graphics, and use GPR routines.  For additional
  11.     speed, we do the graphics to a separate bitmap, and the blt the
  12.     entire bitmap to the display.  When the user specifies an output
  13.     file name, however, we draw directly to the screen, so the graphics
  14.     are written to the file correctly.  Thus, the user can save the
  15.     graphics in a file, to be viewed later.  If we try the bitmap
  16.     trick, it looks funny.
  17.  
  18.     Ray Lischner (uunet!mntgfx!lisch)
  19.     4 October 1989    file created for GNUplot 1.1
  20.     26 March 1990    updated for GNUplot 2.0
  21.     30 October 1991    fixed minor problem in apollo_tic_sizes
  22. */
  23.  
  24. #include <apollo/base.h>
  25. #include <apollo/error.h>
  26. #include <apollo/pad.h>
  27. #include <apollo/gpr.h>
  28.  
  29. /* default tick sizes for small windows */
  30. #define APOLLO_VTIC     6
  31. #define APOLLO_HTIC     6    
  32.  
  33. /* issue an error message, using additional text "s" */
  34. #define apollo_error(s)    error_$print_name(status, (s), strlen(s))
  35.  
  36. /* if "status" indicates an error, then issue an error message */
  37. #define apollo_check(s)    if (status.all != status_$ok) apollo_error(s)
  38.  
  39. static ios_$id_t stream = -1;           /* the stream for the pad */
  40. static gpr_$bitmap_desc_t screen_desc;  /* the screen's bitmap */
  41. static gpr_$bitmap_desc_t bitmap_desc;  /* the graphics bitmap */
  42. static gpr_$attribute_desc_t attr;      /* attribute block for saved bitmap */
  43. static short draw_width;                /* default GPR draw width */
  44. static name_$long_pname_t font_name;    /* font path name */
  45. static int APOLLO_XMAX, APOLLO_YMAX;    /* window size */
  46. static boolean use_bitmap;              /* use a separate bitmap? */
  47.  
  48. /* return whether stdout is a DM pad */
  49. apollo_isa_pad()
  50. {
  51.     status_$t status;
  52.     pad_$isa(1, &status);
  53.     return (status.all == status_$ok);
  54. }
  55.  
  56. /*
  57.     Find out what the default font is for the pad, and save the
  58.     character height and width information.
  59.  
  60.     Note that we must save the font file name because we need
  61.     to reload the font file everytime the window changes size.
  62. */
  63. static void apollo_font_info(struct termentry* tbl, char* fname)
  64. {
  65.     short fwidth, fheight, flen;
  66.     status_$t status;
  67.  
  68.     /* get the font size & update the termentry table */
  69.     pad_$inq_font(stream, &fwidth, &fheight, fname, name_$long_pnamlen_max,
  70.           &flen, &status);
  71.     apollo_check("inq_font");
  72.     fname[flen] = '\0';
  73.  
  74.     tbl->v_char = fheight;
  75.     tbl->h_char = fwidth;
  76. }
  77.  
  78. /*
  79.     Initialize all the GPR stuff.  To save time, we draw into a separate
  80.     bitmap, and then blt it onto the screen all at once.  This results
  81.     in 5-10 times speed-up in the graphics, with only a little
  82.     complication.  Most of the complication is right here, making sure
  83.     we allocate the right bitmaps, etc., in the right order.  The rest
  84.     is in APOLLO_text(), where we actually BLT the bitmap onto the screen.
  85.     Everything else is the same.
  86.  
  87.     The bitmaps have the same size as the window.  If the window changes
  88.     size, then the bitmaps retain the same size, so the user sees part
  89.     of the plot or a lot of space around the plot.  Drawing a new plot,
  90.     or replotting the previous one causes APOLLO_graphics() to see if
  91.     the window has changed size, in which case the GPR is terminated,
  92.     and this routine is called again.  Thus, make sure any changes
  93.     preserve this ability.  Anything that should only be done once
  94.     to the pad should be handled by APOLLO_init().
  95.  
  96.     By the way, we save the current draw width, to be used later
  97.     for drawing extra wide lines.  This way we don't need to know
  98.     anything about the current output device characteristics;
  99.     we can just draw the default width, or twice the default width, etc.
  100. */
  101. static void apollo_gpr_init(struct termentry* tbl, pad_$window_desc_t* window)
  102. {
  103.     gpr_$offset_t size;
  104.     short fontid;
  105.     status_$t status;
  106.  
  107.     size.x_size = APOLLO_XMAX = tbl->xmax = window->width;
  108.     size.y_size = APOLLO_YMAX = tbl->ymax = window->height;
  109.  
  110.     /* now initialize GPR */
  111.     gpr_$init(gpr_$frame, stream, size, 1, &screen_desc, &status);
  112.     apollo_check("gpr_$init");
  113.  
  114.     if (use_bitmap)
  115.     {
  116.     /* allocate the bitmap and its attribute block */
  117.     gpr_$allocate_attribute_block(&attr, &status);
  118.     apollo_check("allocate_attribute_block");
  119.  
  120.     gpr_$allocate_bitmap(size, 1, attr, &bitmap_desc, &status);
  121.     apollo_check("allocate_bitmap");
  122.  
  123.     gpr_$set_bitmap(bitmap_desc, &status);
  124.     apollo_check("set_bitmap");
  125.     }
  126.  
  127.     /* set the font file */
  128.     gpr_$load_font_file(font_name, strlen(font_name), &fontid, &status);
  129.     apollo_check(font_name);
  130.  
  131.     gpr_$set_text_font(fontid, &status);
  132.     apollo_check("set_text_font");
  133.  
  134.     gpr_$inq_draw_width(&draw_width, &status);
  135.     apollo_check("inq_draw_width");
  136. }
  137.  
  138. /*
  139.     Determine the tick sizes to be used for labelling borders.
  140.     By default, we use 1/50 of the window size, which looks nice to me.
  141.     If this makes the ticks too small, however, we use a minimum
  142.     size, to make sure they are visible.  The minimum size was also
  143.     determined experimentally.
  144.  
  145.     Feel free to changes the sizes to something you feel looks better.
  146.  
  147.     This routine must be called after apollo_gpr_init(), because we
  148.     need to know the window size, as stored in the termentry table.
  149. */
  150. static void apollo_tic_sizes(struct termentry* tbl)
  151. {
  152.     /* base the tick size on the window size */
  153.     tbl->v_tic = tbl->ymax / 50;
  154.     if (tbl->v_tic < APOLLO_VTIC)
  155.     tbl->v_tic = APOLLO_VTIC;
  156.     tbl->h_tic = tbl->xmax / 50;
  157.     if (tbl->h_tic < APOLLO_HTIC)
  158.     tbl->h_tic = APOLLO_HTIC;
  159. }
  160.  
  161. /*
  162.     Terminate the GPR.  This is called whenever the window size
  163.     changes, and we need to reinitialize the GPR.  I assume that
  164.     calling gpr_$terminate() also deallocates the bitmaps and
  165.     attribute blocks because deallocating the screen's bitmap
  166.     causes terminate() to think GPR has already been terminated.
  167.  
  168.     Since this can be called many times, make sure nothing
  169.     drastic is done here, like closing the stream to the pad.
  170.     The only actions should be those that will be reinitialized
  171.     by apollo_gpr_init().
  172. */
  173. static void apollo_gpr_terminate()
  174. {
  175.     status_$t status;
  176.  
  177.     gpr_$terminate(false, &status);
  178.     apollo_check("terminate");
  179. }
  180.  
  181. /*
  182.     Initialize the graphics.  This is called once, so we do things
  183.     here that should be done exactly once, such as opening the window.
  184.     I like to give windows names, so it is obvious what the window's
  185.     contents are, but this causes a transcript to be kept in a file
  186.     whose name is the window's name.  This might be nice in some
  187.     circumstances, but to me it is a nuisance, so the file is
  188.     deleted immediately.  The name is unlikely to appear normally,
  189.     so there should be little interference with users' normal files.
  190.     If the user has explicitly set the output file, however, then
  191.     we use that name, and do not delete the file.  Thus, the
  192.     user can get a metafile of the plot.  We can tell if the
  193.     output file has been set because outstr is "STDOUT".  Otherwise,
  194.     outstr is the filename, in single quotes.  We need to strip
  195.     the quotes to make the file name.
  196.  
  197.     The DM defaults are used for window sizes and positions.  If
  198.     the user doesn't like it, he or she can change is and issue
  199.     a replot command (assuming a plot has already been generated).
  200.  
  201.     Note, also, that we must call pad_$set_scale() or else
  202.     pad_$inq_windows() returns scaled values, which is not what
  203.     we want.  Setting the scale to one (1) turns off the scaling,
  204.     so we get real pixel sizes.
  205.  
  206.     Finally, we get the name and size of the default font.  The
  207.     name is kept, as explained earlier.  Then we can initialize
  208.     the GPR stuff.
  209.  
  210.     Note that there is a way that APOLLO_init() gets called more
  211.     than once.  If the user issues the "set terminal apollo" command
  212.     more than once, then this is called, so we need to make sure
  213.     that we do not keep creating windows.
  214.  
  215.     An alternative strategy would be to interpret multiple "set term
  216.     apollo"s to mean create multiple windows.  The user could only
  217.     access the most recent window because GNUplot has no concept of
  218.     multiple terminals.  The user would, in fact, have no way of
  219.     removing old windows because they are still active.  We could try
  220.     catching keyboard events to see if the user presses EXIT, but I do
  221.     not want to start getting into that mess.  If the user really
  222.     wants this kind of functionality, then he or she can run gnuplot
  223.     multiple times.  I think that is a lot cleaner, don't you?
  224. */
  225. APOLLO_init()
  226. {
  227.     /* only initialize once */
  228.     if (stream == -1)
  229.     {
  230.     extern char outstr[];
  231.     struct termentry* tbl;
  232.     pad_$window_desc_t window;
  233.     name_$long_name_t wname;
  234.     short wnum;            /* junk needed by pad_$inq_windows() */
  235.     boolean unlink_wname;
  236.     status_$t status;
  237.  
  238.     tbl = &term_tbl[term];
  239.  
  240.     /* make the window name unique, with "gnuplot" in the label */
  241.     if (strcmp(outstr, "STDOUT") == 0)
  242.     {
  243.         sprintf(wname, "gnuplot-%d", getpid());
  244.         unlink_wname = true;
  245.     }
  246.     else
  247.     {
  248.         /* strip the single quotes around the file name */
  249.         strcpy(wname, outstr + 1);
  250.         wname[strlen(wname) - 1] = '\0';
  251.         unlink_wname = false;
  252.     }
  253.  
  254.     use_bitmap = unlink_wname;
  255.  
  256.     /* use the default window position and size */
  257.     window.top = window.left = window.width = window.height = 0;
  258.     pad_$create_window(wname, strlen(wname), pad_$transcript, 1, window,
  259.                &stream, &status);
  260.     apollo_check("create_window");
  261.  
  262.     /* if this is not the user's name, then delete the file */
  263.     if (unlink_wname)
  264.         unlink(wname);
  265.  
  266.     /* remove all scaling, to revert to pixel units, not char. units */
  267.     pad_$set_scale(stream, 1, 1, &status);
  268.     apollo_check("set_scale");
  269.  
  270.     /* get rid of the window when the program exits */
  271.     pad_$set_auto_close(stream, 1, true, &status);
  272.     apollo_check("set_auto_close");
  273.  
  274.     /* now determine the window size & update the termentry table */
  275.     pad_$inq_windows(stream, &window, 1, &wnum, &status);
  276.     apollo_check("inq_windows");
  277.  
  278.     /* the order of the next three calls is important */
  279.     apollo_font_info(tbl, font_name);
  280.     apollo_gpr_init(tbl, &window);
  281.     apollo_tic_sizes(tbl);
  282.     }
  283. }
  284.  
  285. /*
  286.     Prepare for graphics output.  Since this is what the user wants to
  287.     do when preparing a new plot, this is a meaningful time to see if
  288.     the window has changed size.  Thus, we avoid mucking about with
  289.     asynchronous traps, and we avoid the bigger problem of dealing
  290.     with a half-finished plot when the window changes size.
  291.  
  292.     Simply put, get the current window size, and if it has changed,
  293.     then get rid of the old bitmaps, etc., and allocate new ones at
  294.     the new size.  We also need to update the termentry table.
  295.     If the window stays the same size, then just clear it.
  296. */
  297. static void apollo_redo_window(pad_$window_desc_t* window)
  298. {
  299.     struct termentry* tbl = &term_tbl[term];
  300.     status_$t status;
  301.  
  302.     /* the order of the following calls is important */
  303.     apollo_gpr_terminate();
  304.     apollo_gpr_init(tbl, window);
  305.     apollo_tic_sizes(tbl);
  306. }
  307.  
  308. APOLLO_graphics()
  309. {
  310.     pad_$window_desc_t window;
  311.     short wnum;
  312.     status_$t status;
  313.  
  314.     pad_$inq_windows(stream, &window, 1, &wnum, &status);
  315.     apollo_check("inq_windows");
  316.  
  317.     if (window.width != APOLLO_XMAX || window.height != APOLLO_YMAX)
  318.     apollo_redo_window(&window);
  319.     else
  320.     {
  321.     gpr_$clear(0, &status);
  322.     apollo_check("clear");
  323.     }
  324. }
  325.  
  326. /* set a line type:
  327.    -2 heavy, solid    (border)
  328.    -1 heavy, dotted    (axis)
  329.    0  solid        (normal)
  330.    1  dots        (other curves)
  331.    2  short dash
  332.    3  long dash
  333.    4  dash dot
  334.  
  335.    Apparently, GPUplot draws a lot of short line segments, and each
  336.    one starts a new pattern.  This makes the patterns somewhat useless,
  337.    but one can still tell the difference between solid, dotted, and
  338.    dashed lines.  The utility of fancier styles is limited, however.
  339.  
  340.    On a color workstation, we should use different colors, but I
  341.    don't have one.
  342. */
  343.  
  344. /*
  345.     To draw different line styles on an Apollo, we use two different
  346.     parameters.  One is a line thickness, which is just an integral
  347.     multiple of the default line thickness.  The second is a 16-bit
  348.     pattern that is repeated.  We could use fancier patterns, since
  349.     GPR supports up to 64-bits, but, as I explained earlier, this
  350.     really does not buy us anything.
  351.  
  352.     I used patterns that do not start with all bits on because
  353.     GNUplot seems to use lots of short line segments to draw
  354.     a curve, and this might make a very curvey plot seem like
  355.     a solid line, regardless of pattern.  I don't want to start
  356.     with too many zeros, however, or else the curve might not
  357.     appear at all!  All the patterns, therefore, start with one
  358.     bit on.  The rest of the bits determine the true pattern.
  359.  
  360.     By examining graphics.c, we see that linetype -2 is used exclusively
  361.     for the border, -1 for the axes, and the non-negative integers for
  362.     the curves.  We use heavy lines for the border and axes, and normal
  363.     width lines for the curves.
  364.  
  365.     Since C arrays start at zero, make sure all the offsets are correct,
  366.     so that it is easy to access the array with -2...n linetypes.
  367. */
  368.  
  369. typedef struct {
  370.     short width;
  371.     short pattern;
  372. } APOLLO_LINE;
  373.  
  374. static APOLLO_LINE apollo_lines[] = {
  375.     { 2, ~0 },        /* heavy, solid */
  376.     { 2, 0x6666 },    /* heavy, dotted */
  377.     { 1, ~0 },        /* normal */
  378.     { 1, 0xAAAA },    /* dotted */
  379.     { 1, 0xC3C3 },    /* short dash */
  380.     { 1, 0xE01F },    /* long dash */
  381.     { 1, 0x87F8 },    /* dash dot */
  382.     { 1, 0x6666 },    /* big dots */
  383. };
  384.  
  385. #define BITS_PER_LINETYPE    16
  386.  
  387. /* apollo_line(-2) is the border style, etc. */
  388. #define apollo_line(x)        apollo_lines[(x)+2]
  389. #define apollo_pattern(x)    &apollo_line(x).pattern
  390. #define apollo_width(x)        apollo_line(x).width
  391.  
  392. #define APOLLO_MIN_LINE        (-2)
  393. #define APOLLO_MAX_LINE        (sizeof(apollo_lines)/sizeof(*apollo_lines)-2)
  394.  
  395. /* set the line style */
  396. APOLLO_linetype(ltype)
  397. int ltype;
  398. {
  399.     status_$t status;
  400.  
  401.     if (ltype < APOLLO_MIN_LINE)
  402.     ltype = APOLLO_MIN_LINE;
  403.     if (ltype >= APOLLO_MAX_LINE)
  404.     ltype %= APOLLO_MAX_LINE;
  405.  
  406.     gpr_$set_line_pattern(1, apollo_pattern(ltype), BITS_PER_LINETYPE, &status);
  407.     apollo_check("set_line_pattern");
  408.  
  409.     gpr_$set_draw_width(draw_width * apollo_width(ltype), &status);
  410.     apollo_check("set_draw_width");
  411. }
  412.  
  413. /* issue an error message that includes an (x, y) coordinate */
  414. static void apollo_xy_error(char* s, int x, int y, status_$t status)
  415. {
  416.     char buffer[128];
  417.  
  418.     sprintf(buffer, "%s(%d, %d)", s, x, y);
  419.     apollo_error(buffer);
  420. }
  421.  
  422. #define apollo_xy_check(s)    \
  423.     if (status.all != status_$ok) apollo_xy_error((s), x, y, status)
  424.  
  425. /*
  426.     Note that GNUplot and GPR have reversed ideas of where the Y origin is.
  427.     This means subtracting the Y coordinate from Y max.
  428. */
  429. #define plot_to_gpr(y)        (APOLLO_YMAX - (y))
  430.  
  431. /* move to a new position */
  432. APOLLO_move(unsigned int x, unsigned int y)
  433. {
  434.     status_$t status;
  435.  
  436.     gpr_$move((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
  437.     apollo_xy_check("move");
  438. }
  439.  
  440. /* draw a line to a new position */
  441. APOLLO_vector(unsigned int x, unsigned int y)
  442. {
  443.     status_$t status;
  444.  
  445.     gpr_$line((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
  446.     apollo_xy_check("line");
  447. }
  448.  
  449. /*
  450.     On terminals, this switches to text mode.  The real meaning,
  451.     however, is that the graphics are finished.  This means we can
  452.     now display the saved bitmap.
  453. */
  454. APOLLO_text()
  455. {
  456.     if (use_bitmap)
  457.     {
  458.     static gpr_$position_t pos;        /* always zero */
  459.     gpr_$window_t window;
  460.     status_$t status;
  461.  
  462.     /* bitblt the entire bitmap to the entire window */
  463.     window.window_base.x_coord = 0;
  464.     window.window_base.y_coord = 0;
  465.     window.window_size.x_size = APOLLO_XMAX;
  466.     window.window_size.y_size = APOLLO_YMAX;
  467.  
  468.     gpr_$set_bitmap(screen_desc, &status);
  469.     apollo_check("set_bitmap(screen_desc)");
  470.  
  471.     gpr_$pixel_blt(bitmap_desc, window, pos, &status);
  472.     apollo_check("bitblt");
  473.  
  474.     gpr_$set_bitmap(bitmap_desc, &status);
  475.     apollo_check("set_bitmap(bitmap_desc)");
  476.     }
  477. }
  478.  
  479. APOLLO_text_angle(ang)
  480. int ang;
  481. {
  482.     status_$t status;
  483.  
  484.     gpr_$set_text_path(ang ? gpr_$up : gpr_$right, &status);
  485.     apollo_check("set_text_path");
  486.     return TRUE;
  487. }
  488.  
  489. static enum JUSTIFY apollo_text_mode;
  490.  
  491. APOLLO_justify_text(mode)
  492. enum JUSTIFY mode;
  493. {
  494.     apollo_text_mode = mode;
  495.     return TRUE;
  496. }
  497.  
  498. /*
  499.     Write "str" right justified on row "row".  A row is assumed to
  500.     have whatever height the current text has.  Make sure the
  501.     text does not cover the tick marks.
  502. */
  503. APOLLO_put_text(x, y, str)
  504. unsigned int x, y;
  505. char str[];
  506. {
  507.     gpr_$offset_t size;
  508.     status_$t status;
  509.  
  510.     gpr_$inq_text_extent(str, strlen(str), &size, &status);
  511.     apollo_check("inq_text_extent");
  512.  
  513.     y -= size.y_size / 2;    /* center around "y" */
  514.     switch (apollo_text_mode)
  515.     {
  516.     case LEFT:
  517.     break;
  518.     case CENTRE:
  519.     x -= size.x_size / 2;
  520.     break;
  521.     case RIGHT:
  522.     x -= size.x_size;
  523.     break;
  524.     }
  525.     APOLLO_move(x, y);
  526.  
  527.     gpr_$text(str, strlen(str), &status);
  528.     apollo_check("put_text");
  529. }
  530.  
  531. /* reset the graphics state and terminate */
  532. APOLLO_reset()
  533. {
  534.     if (stream != -1)
  535.     {
  536.     apollo_gpr_terminate();
  537.     stream = -1;
  538.     }
  539. }
  540.