1 /* Hello, Emacs, this is -*-C-*-
2 * $Id: apollo.trm,v 1.14 2006/07/21 02:35:45 sfeam Exp $
6 /* GNUPLOT - apollo.trm */
8 Apollo terminal driver for gnuplot.
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.
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
23 As of 13 January 1999, this file has been placed in the
24 public domain by Ray Lischner.
29 * adapted to the new terminal layout by Stefan Bodewig (Dec. 1995)
39 TERM_PUBLIC void APOLLO_init __PROTO((void));
40 TERM_PUBLIC void APOLLO_graphics __PROTO((void));
41 TERM_PUBLIC void APOLLO_linetype __PROTO((int ltype));
42 TERM_PUBLIC void APOLLO_move __PROTO((unsigned int x, unsigned int y));
43 TERM_PUBLIC void APOLLO_vector __PROTO((unsigned int x, unsigned int y));
44 TERM_PUBLIC void APOLLO_text __PROTO((void));
45 TERM_PUBLIC int APOLLO_text_angle __PROTO((int ang));
46 TERM_PUBLIC int APOLLO_justify_text __PROTO((enum JUSTIFY mode));
47 TERM_PUBLIC void APOLLO_put_text __PROTO((unsigned int x, unsigned int y, const char str[]));
48 TERM_PUBLIC void APOLLO_reset __PROTO((void));
49 /* default tick sizes for small windows */
54 #ifndef TERM_PROTO_ONLY
57 #include <apollo/base.h>
58 #include <apollo/error.h>
59 #include <apollo/pad.h>
60 #include <apollo/gpr.h>
62 int apollo_isa_pad __PROTO((void));
63 static void apollo_font_info __PROTO((struct termentry * tbl, char *fname));
64 static void apollo_gpr_init __PROTO((struct termentry * tbl, pad_$window_desc_t * window));
65 static void apollo_tic_sizes __PROTO((struct termentry * tbl));
66 static void apollo_gpr_terminate __PROTO((void));
67 static void apollo_redo_window __PROTO((pad_$window_desc_t * window));
68 static void apollo_xy_error __PROTO((char *s, int x, int y, status_$t status));
70 /* issue an error message, using additional text "s" */
71 #define apollo_error(s) error_$print_name(status, (s), strlen(s))
73 /* if "status" indicates an error, then issue an error message */
74 #define apollo_check(s) if (status.all != status_$ok) apollo_error(s)
76 static ios_$id_t stream = -1; /* the stream for the pad */
77 static gpr_$bitmap_desc_t screen_desc; /* the screen's bitmap */
78 static gpr_$bitmap_desc_t bitmap_desc; /* the graphics bitmap */
79 static gpr_$attribute_desc_t attr; /* attribute block for saved bitmap */
80 static int APOLLO_XMAX, APOLLO_YMAX; /* window size */
81 static short draw_width; /* default GPR draw width */
82 static name_$long_pname_t font_name; /* font path name */
83 static boolean use_bitmap; /* use a separate bitmap? */
85 /* return whether stdout is a DM pad */
91 return (status.all == status_$ok);
95 Find out what the default font is for the pad, and save the
96 character height and width information.
98 Note that we must save the font file name because we need
99 to reload the font file everytime the window changes size.
102 apollo_font_info(struct termentry *tbl, char *fname)
104 short fwidth, fheight, flen;
107 /* get the font size & update the termentry table */
108 pad_$inq_font(stream, &fwidth, &fheight, fname, name_$long_pnamlen_max,
110 apollo_check("inq_font");
113 tbl->v_char = fheight;
114 tbl->h_char = fwidth;
118 Initialize all the GPR stuff. To save time, we draw into a separate
119 bitmap, and then blt it onto the screen all at once. This results
120 in 5-10 times speed-up in the graphics, with only a little
121 complication. Most of the complication is right here, making sure
122 we allocate the right bitmaps, etc., in the right order. The rest
123 is in APOLLO_text(), where we actually BLT the bitmap onto the screen.
124 Everything else is the same.
126 The bitmaps have the same size as the window. If the window changes
127 size, then the bitmaps retain the same size, so the user sees part
128 of the plot or a lot of space around the plot. Drawing a new plot,
129 or replotting the previous one causes APOLLO_graphics() to see if
130 the window has changed size, in which case the GPR is terminated,
131 and this routine is called again. Thus, make sure any changes
132 preserve this ability. Anything that should only be done once
133 to the pad should be handled by APOLLO_init().
135 By the way, we save the current draw width, to be used later
136 for drawing extra wide lines. This way we don't need to know
137 anything about the current output device characteristics;
138 we can just draw the default width, or twice the default width, etc.
141 apollo_gpr_init(struct termentry *tbl, pad_$window_desc_t * window)
147 size.x_size = APOLLO_XMAX = tbl->xmax = window->width;
148 size.y_size = APOLLO_YMAX = tbl->ymax = window->height;
150 /* now initialize GPR */
151 gpr_$init(gpr_$frame, stream, size, 1, &screen_desc, &status);
152 apollo_check("gpr_$init");
155 /* allocate the bitmap and its attribute block */
156 gpr_$allocate_attribute_block(&attr, &status);
157 apollo_check("allocate_attribute_block");
159 gpr_$allocate_bitmap(size, 1, attr, &bitmap_desc, &status);
160 apollo_check("allocate_bitmap");
162 gpr_$set_bitmap(bitmap_desc, &status);
163 apollo_check("set_bitmap");
165 /* set the font file */
166 gpr_$load_font_file(font_name, strlen(font_name), &fontid, &status);
167 apollo_check(font_name);
169 gpr_$set_text_font(fontid, &status);
170 apollo_check("set_text_font");
172 gpr_$inq_draw_width(&draw_width, &status);
173 apollo_check("inq_draw_width");
177 Determine the tick sizes to be used for labelling borders.
178 By default, we use 1/50 of the window size, which looks nice to me.
179 If this makes the ticks too small, however, we use a minimum
180 size, to make sure they are visible. The minimum size was also
181 determined experimentally.
183 Feel free to changes the sizes to something you feel looks better.
185 This routine must be called after apollo_gpr_init(), because we
186 need to know the window size, as stored in the termentry table.
189 apollo_tic_sizes(struct termentry *tbl)
191 /* base the tick size on the window size */
192 tbl->v_tic = tbl->ymax / 50;
193 if (tbl->v_tic < APOLLO_VTIC)
194 tbl->v_tic = APOLLO_VTIC;
195 tbl->h_tic = tbl->xmax / 50;
196 if (tbl->h_tic < APOLLO_HTIC)
197 tbl->h_tic = APOLLO_HTIC;
201 Terminate the GPR. This is called whenever the window size
202 changes, and we need to reinitialize the GPR. I assume that
203 calling gpr_$terminate() also deallocates the bitmaps and
204 attribute blocks because deallocating the screen's bitmap
205 causes terminate() to think GPR has already been terminated.
207 Since this can be called many times, make sure nothing
208 drastic is done here, like closing the stream to the pad.
209 The only actions should be those that will be reinitialized
210 by apollo_gpr_init().
213 apollo_gpr_terminate()
217 gpr_$terminate(false, &status);
218 apollo_check("terminate");
222 Initialize the graphics. This is called once, so we do things
223 here that should be done exactly once, such as opening the window.
224 I like to give windows names, so it is obvious what the window's
225 contents are, but this causes a transcript to be kept in a file
226 whose name is the window's name. This might be nice in some
227 circumstances, but to me it is a nuisance, so the file is
228 deleted immediately. The name is unlikely to appear normally,
229 so there should be little interference with users' normal files.
230 If the user has explicitly set the output file, however, then
231 we use that name, and do not delete the file. Thus, the
232 user can get a metafile of the plot. We can tell if the
233 output file has been set because outstr is NULL. Otherwise,
234 outstr is the filename, in alloc-ed store.
236 The DM defaults are used for window sizes and positions. If
237 the user doesn't like it, he or she can change is and issue
238 a replot command (assuming a plot has already been generated).
240 Note, also, that we must call pad_$set_scale() or else
241 pad_$inq_windows() returns scaled values, which is not what
242 we want. Setting the scale to one (1) turns off the scaling,
243 so we get real pixel sizes.
245 Finally, we get the name and size of the default font. The
246 name is kept, as explained earlier. Then we can initialize
249 Note that there is a way that APOLLO_init() gets called more
250 than once. If the user issues the "set terminal apollo" command
251 more than once, then this is called, so we need to make sure
252 that we do not keep creating windows.
254 An alternative strategy would be to interpret multiple "set term
255 apollo"s to mean create multiple windows. The user could only
256 access the most recent window because gnuplot has no concept of
257 multiple terminals. The user would, in fact, have no way of
258 removing old windows because they are still active. We could try
259 catching keyboard events to see if the user presses EXIT, but I do
260 not want to start getting into that mess. If the user really
261 wants this kind of functionality, then he or she can run gnuplot
262 multiple times. I think that is a lot cleaner, don't you?
267 /* only initialize once */
269 struct termentry *tbl;
270 pad_$window_desc_t window;
271 name_$long_name_t wname;
272 short wnum; /* junk needed by pad_$inq_windows() */
273 boolean unlink_wname;
278 /* make the window name unique, with "gnuplot" in the label */
279 if (outstr == NULL) {
280 sprintf(wname, "gnuplot-%d", getpid());
283 strcpy(wname, outstr);
284 unlink_wname = false;
287 use_bitmap = unlink_wname;
289 /* use the default window position and size */
290 window.top = window.left = window.width = window.height = 0;
291 pad_$create_window(wname, strlen(wname), pad_$transcript, 1, window,
293 apollo_check("create_window");
295 /* if this is not the user's name, then delete the file */
299 /* remove all scaling, to revert to pixel units, not char. units */
300 pad_$set_scale(stream, 1, 1, &status);
301 apollo_check("set_scale");
303 /* get rid of the window when the program exits */
304 pad_$set_auto_close(stream, 1, true, &status);
305 apollo_check("set_auto_close");
307 /* now determine the window size & update the termentry table */
308 pad_$inq_windows(stream, &window, 1, &wnum, &status);
309 apollo_check("inq_windows");
311 /* the order of the next three calls is important */
312 apollo_font_info(tbl, font_name);
313 apollo_gpr_init(tbl, &window);
314 apollo_tic_sizes(tbl);
319 Prepare for graphics output. Since this is what the user wants to
320 do when preparing a new plot, this is a meaningful time to see if
321 the window has changed size. Thus, we avoid mucking about with
322 asynchronous traps, and we avoid the bigger problem of dealing
323 with a half-finished plot when the window changes size.
325 Simply put, get the current window size, and if it has changed,
326 then get rid of the old bitmaps, etc., and allocate new ones at
327 the new size. We also need to update the termentry table.
328 If the window stays the same size, then just clear it.
331 apollo_redo_window(pad_$window_desc_t * window)
333 struct termentry *tbl = term;
336 /* the order of the following calls is important */
337 apollo_gpr_terminate();
338 apollo_gpr_init(tbl, window);
339 apollo_tic_sizes(tbl);
345 pad_$window_desc_t window;
349 pad_$inq_windows(stream, &window, 1, &wnum, &status);
350 apollo_check("inq_windows");
352 if (window.width != APOLLO_XMAX || window.height != APOLLO_YMAX)
353 apollo_redo_window(&window);
355 gpr_$clear(0, &status);
356 apollo_check("clear");
361 -2 heavy, solid (border)
362 -1 heavy, dotted (axis)
364 1 dots (other curves)
369 Apparently, GPUplot draws a lot of short line segments, and each
370 one starts a new pattern. This makes the patterns somewhat useless,
371 but one can still tell the difference between solid, dotted, and
372 dashed lines. The utility of fancier styles is limited, however.
374 On a color workstation, we should use different colors, but I
379 To draw different line styles on an Apollo, we use two different
380 parameters. One is a line thickness, which is just an integral
381 multiple of the default line thickness. The second is a 16-bit
382 pattern that is repeated. We could use fancier patterns, since
383 GPR supports up to 64-bits, but, as I explained earlier, this
384 really does not buy us anything.
386 I used patterns that do not start with all bits on because
387 gnuplot seems to use lots of short line segments to draw
388 a curve, and this might make a very curvey plot seem like
389 a solid line, regardless of pattern. I don't want to start
390 with too many zeros, however, or else the curve might not
391 appear at all! All the patterns, therefore, start with one
392 bit on. The rest of the bits determine the true pattern.
394 By examining graphics.c, we see that linetype -2 is used exclusively
395 for the border, -1 for the axes, and the non-negative integers for
396 the curves. We use heavy lines for the border and axes, and normal
397 width lines for the curves.
399 Since C arrays start at zero, make sure all the offsets are correct,
400 so that it is easy to access the array with -2...n linetypes.
408 static APOLLO_LINE apollo_lines[] =
410 {2, ~0}, /* heavy, solid */
411 {2, 0x6666}, /* heavy, dotted */
412 {1, ~0}, /* normal */
413 {1, 0xAAAA}, /* dotted */
414 {1, 0xC3C3}, /* short dash */
415 {1, 0xE01F}, /* long dash */
416 {1, 0x87F8}, /* dash dot */
417 {1, 0x6666}, /* big dots */
420 #define BITS_PER_LINETYPE 16
422 /* apollo_line(-2) is the border style, etc. */
423 #define apollo_line(x) apollo_lines[(x)+2]
424 #define apollo_pattern(x) &apollo_line(x).pattern
425 #define apollo_width(x) apollo_line(x).width
427 #define APOLLO_MIN_LINE (-2)
428 #define APOLLO_MAX_LINE (sizeof(apollo_lines)/sizeof(*apollo_lines)-2)
430 /* set the line style */
432 APOLLO_linetype(int ltype)
436 if (ltype < APOLLO_MIN_LINE)
437 ltype = APOLLO_MIN_LINE;
438 if (ltype >= APOLLO_MAX_LINE)
439 ltype %= APOLLO_MAX_LINE;
441 gpr_$set_line_pattern(1, apollo_pattern(ltype), BITS_PER_LINETYPE, &status);
442 apollo_check("set_line_pattern");
444 gpr_$set_draw_width(draw_width * apollo_width(ltype), &status);
445 apollo_check("set_draw_width");
448 /* issue an error message that includes an (x, y) coordinate */
450 apollo_xy_error(char *s, int x, int y, status_$t status)
454 sprintf(buffer, "%s(%d, %d)", s, x, y);
455 apollo_error(buffer);
458 #define apollo_xy_check(s) \
459 if (status.all != status_$ok) apollo_xy_error((s), x, y, status)
462 Note that gnuplot and GPR have reversed ideas of where the Y origin is.
463 This means subtracting the Y coordinate from Y max.
465 #define plot_to_gpr(y) (APOLLO_YMAX - (y))
467 /* move to a new position */
469 APOLLO_move(unsigned int x, unsigned int y)
473 gpr_$move((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
474 apollo_xy_check("move");
477 /* draw a line to a new position */
479 APOLLO_vector(unsigned int x, unsigned int y)
483 gpr_$line((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
484 apollo_xy_check("line");
488 On terminals, this switches to text mode. The real meaning,
489 however, is that the graphics are finished. This means we can
490 now display the saved bitmap.
496 static gpr_$position_t pos; /* always zero */
497 gpr_$window_t window;
500 /* bitblt the entire bitmap to the entire window */
501 window.window_base.x_coord = 0;
502 window.window_base.y_coord = 0;
503 window.window_size.x_size = APOLLO_XMAX;
504 window.window_size.y_size = APOLLO_YMAX;
506 gpr_$set_bitmap(screen_desc, &status);
507 apollo_check("set_bitmap(screen_desc)");
509 gpr_$pixel_blt(bitmap_desc, window, pos, &status);
510 apollo_check("bitblt");
512 gpr_$set_bitmap(bitmap_desc, &status);
513 apollo_check("set_bitmap(bitmap_desc)");
518 APOLLO_text_angle(int ang)
522 gpr_$set_text_path(ang ? gpr_$up : gpr_$right, &status);
523 apollo_check("set_text_path");
527 static enum JUSTIFY apollo_text_mode;
530 APOLLO_justify_text(enum JUSTIFY mode)
532 apollo_text_mode = mode;
537 Write "str" right justified on row "row". A row is assumed to
538 have whatever height the current text has. Make sure the
539 text does not cover the tick marks.
542 APOLLO_put_text(unsigned int x, unsigned int y, const char str[])
547 gpr_$inq_text_extent(str, strlen(str), &size, &status);
548 apollo_check("inq_text_extent");
550 y -= size.y_size / 2; /* center around "y" */
551 switch (apollo_text_mode) {
555 x -= size.x_size / 2;
563 gpr_$text(str, strlen(str), &status);
564 apollo_check("put_text");
567 /* reset the graphics state and terminate */
572 apollo_gpr_terminate();
577 #endif /* TERM_BODY */
580 TERM_TABLE_START(apollo_driver)
582 "Apollo Graphics Primitive Resource, rescaling of subsequent plots after window resizing",
583 0, 0, 0, 0, /* APOLLO_XMAX, APOLLO_YMAX, APOLLO_VCHAR, APOLLO_HCHAR, are filled in at run-time */
584 APOLLO_VTIC, APOLLO_HTIC, options_null, APOLLO_init, APOLLO_reset,
585 APOLLO_text, null_scale, APOLLO_graphics, APOLLO_move, APOLLO_vector,
586 APOLLO_linetype, APOLLO_put_text, APOLLO_text_angle,
587 APOLLO_justify_text, line_and_point, do_arrow, set_font_null
588 TERM_TABLE_END(apollo_driver)
591 #define LAST_TERM apollo_driver
593 #endif /* TERM_TABLE */
594 #endif /* TERM_PROTO_ONLY */
599 "?commands set terminal apollo",
600 "?set terminal apollo",
605 " The `apollo` terminal driver supports the Apollo Graphics Primitive Resource",
606 " with rescaling after window resizing. It has no options.",
608 " If a fixed-size window is desired, the `gpr` terminal may be used instead."
610 #endif /* TERM_HELP */