Changed russian description a little bit
[gnuplot] / term / apollo.trm
1 /* Hello, Emacs, this is -*-C-*-
2  * $Id: apollo.trm,v 1.14 2006/07/21 02:35:45 sfeam 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     As of 13 January 1999, this file has been placed in the
24     public domain by Ray Lischner.
25
26 */
27
28 /*
29  * adapted to the new terminal layout by Stefan Bodewig (Dec. 1995)
30  */
31
32 #include "driver.h"
33
34 #ifdef TERM_REGISTER
35 register_term(apollo)
36 #endif
37
38 #ifdef TERM_PROTO
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 */
50 #define APOLLO_VTIC      6
51 #define APOLLO_HTIC      6
52 #endif
53
54 #ifndef TERM_PROTO_ONLY
55 #ifdef TERM_BODY
56
57 #include <apollo/base.h>
58 #include <apollo/error.h>
59 #include <apollo/pad.h>
60 #include <apollo/gpr.h>
61
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));
69
70 /* issue an error message, using additional text "s" */
71 #define apollo_error(s) error_$print_name(status, (s), strlen(s))
72
73 /* if "status" indicates an error, then issue an error message */
74 #define apollo_check(s) if (status.all != status_$ok) apollo_error(s)
75
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? */
84
85 /* return whether stdout is a DM pad */
86 int
87 apollo_isa_pad()
88 {
89     status_$t status;
90     pad_$isa(1, &status);
91     return (status.all == status_$ok);
92 }
93
94 /*
95     Find out what the default font is for the pad, and save the
96     character height and width information.
97
98     Note that we must save the font file name because we need
99     to reload the font file everytime the window changes size.
100 */
101 static void
102 apollo_font_info(struct termentry *tbl, char *fname)
103 {
104     short fwidth, fheight, flen;
105     status_$t status;
106
107     /* get the font size & update the termentry table */
108     pad_$inq_font(stream, &fwidth, &fheight, fname, name_$long_pnamlen_max,
109                   &flen, &status);
110     apollo_check("inq_font");
111     fname[flen] = NUL;
112
113     tbl->v_char = fheight;
114     tbl->h_char = fwidth;
115 }
116
117 /*
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.
125
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().
134
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.
139 */
140 static void
141 apollo_gpr_init(struct termentry *tbl, pad_$window_desc_t * window)
142 {
143     gpr_$offset_t size;
144     short fontid;
145     status_$t status;
146
147     size.x_size = APOLLO_XMAX = tbl->xmax = window->width;
148     size.y_size = APOLLO_YMAX = tbl->ymax = window->height;
149
150     /* now initialize GPR */
151     gpr_$init(gpr_$frame, stream, size, 1, &screen_desc, &status);
152     apollo_check("gpr_$init");
153
154     if (use_bitmap) {
155         /* allocate the bitmap and its attribute block */
156         gpr_$allocate_attribute_block(&attr, &status);
157         apollo_check("allocate_attribute_block");
158
159         gpr_$allocate_bitmap(size, 1, attr, &bitmap_desc, &status);
160         apollo_check("allocate_bitmap");
161
162         gpr_$set_bitmap(bitmap_desc, &status);
163         apollo_check("set_bitmap");
164     }
165     /* set the font file */
166     gpr_$load_font_file(font_name, strlen(font_name), &fontid, &status);
167     apollo_check(font_name);
168
169     gpr_$set_text_font(fontid, &status);
170     apollo_check("set_text_font");
171
172     gpr_$inq_draw_width(&draw_width, &status);
173     apollo_check("inq_draw_width");
174 }
175
176 /*
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.
182
183     Feel free to changes the sizes to something you feel looks better.
184
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.
187 */
188 static void
189 apollo_tic_sizes(struct termentry *tbl)
190 {
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;
198 }
199
200 /*
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.
206
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().
211 */
212 static void
213 apollo_gpr_terminate()
214 {
215     status_$t status;
216
217     gpr_$terminate(false, &status);
218     apollo_check("terminate");
219 }
220
221 /*
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.
235
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).
239
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.
244
245     Finally, we get the name and size of the default font.  The
246     name is kept, as explained earlier.  Then we can initialize
247     the GPR stuff.
248
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.
253
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?
263 */
264 TERM_PUBLIC void
265 APOLLO_init()
266 {
267     /* only initialize once */
268     if (stream == -1) {
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;
274         status_$t status;
275
276         tbl = term;
277
278         /* make the window name unique, with "gnuplot" in the label */
279         if (outstr == NULL) {
280             sprintf(wname, "gnuplot-%d", getpid());
281             unlink_wname = true;
282         } else {
283             strcpy(wname, outstr);
284             unlink_wname = false;
285         }
286
287         use_bitmap = unlink_wname;
288
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,
292                            &stream, &status);
293         apollo_check("create_window");
294
295         /* if this is not the user's name, then delete the file */
296         if (unlink_wname)
297             unlink(wname);
298
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");
302
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");
306
307         /* now determine the window size & update the termentry table */
308         pad_$inq_windows(stream, &window, 1, &wnum, &status);
309         apollo_check("inq_windows");
310
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);
315     }
316 }
317
318 /*
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.
324
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.
329 */
330 static void
331 apollo_redo_window(pad_$window_desc_t * window)
332 {
333     struct termentry *tbl = term;
334     status_$t status;
335
336     /* the order of the following calls is important */
337     apollo_gpr_terminate();
338     apollo_gpr_init(tbl, window);
339     apollo_tic_sizes(tbl);
340 }
341
342 TERM_PUBLIC void
343 APOLLO_graphics()
344 {
345     pad_$window_desc_t window;
346     short wnum;
347     status_$t status;
348
349     pad_$inq_windows(stream, &window, 1, &wnum, &status);
350     apollo_check("inq_windows");
351
352     if (window.width != APOLLO_XMAX || window.height != APOLLO_YMAX)
353         apollo_redo_window(&window);
354     else {
355         gpr_$clear(0, &status);
356         apollo_check("clear");
357     }
358 }
359
360 /* set a line type:
361    -2 heavy, solid      (border)
362    -1 heavy, dotted     (axis)
363    0  solid             (normal)
364    1  dots              (other curves)
365    2  short dash
366    3  long dash
367    4  dash dot
368
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.
373
374    On a color workstation, we should use different colors, but I
375    don't have one.
376 */
377
378 /*
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.
385
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.
393
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.
398
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.
401 */
402
403 typedef struct {
404     short width;
405     short pattern;
406 } APOLLO_LINE;
407
408 static APOLLO_LINE apollo_lines[] =
409 {
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 */
418 };
419
420 #define BITS_PER_LINETYPE       16
421
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
426
427 #define APOLLO_MIN_LINE         (-2)
428 #define APOLLO_MAX_LINE         (sizeof(apollo_lines)/sizeof(*apollo_lines)-2)
429
430 /* set the line style */
431 TERM_PUBLIC void
432 APOLLO_linetype(int ltype)
433 {
434     status_$t status;
435
436     if (ltype < APOLLO_MIN_LINE)
437         ltype = APOLLO_MIN_LINE;
438     if (ltype >= APOLLO_MAX_LINE)
439         ltype %= APOLLO_MAX_LINE;
440
441     gpr_$set_line_pattern(1, apollo_pattern(ltype), BITS_PER_LINETYPE, &status);
442     apollo_check("set_line_pattern");
443
444     gpr_$set_draw_width(draw_width * apollo_width(ltype), &status);
445     apollo_check("set_draw_width");
446 }
447
448 /* issue an error message that includes an (x, y) coordinate */
449 static void
450 apollo_xy_error(char *s, int x, int y, status_$t status)
451 {
452     char buffer[128];
453
454     sprintf(buffer, "%s(%d, %d)", s, x, y);
455     apollo_error(buffer);
456 }
457
458 #define apollo_xy_check(s)      \
459     if (status.all != status_$ok) apollo_xy_error((s), x, y, status)
460
461 /*
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.
464 */
465 #define plot_to_gpr(y)          (APOLLO_YMAX - (y))
466
467 /* move to a new position */
468 TERM_PUBLIC void
469 APOLLO_move(unsigned int x, unsigned int y)
470 {
471     status_$t status;
472
473     gpr_$move((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
474     apollo_xy_check("move");
475 }
476
477 /* draw a line to a new position */
478 TERM_PUBLIC void
479 APOLLO_vector(unsigned int x, unsigned int y)
480 {
481     status_$t status;
482
483     gpr_$line((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
484     apollo_xy_check("line");
485 }
486
487 /*
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.
491 */
492 TERM_PUBLIC void
493 APOLLO_text()
494 {
495     if (use_bitmap) {
496         static gpr_$position_t pos;     /* always zero */
497         gpr_$window_t window;
498         status_$t status;
499
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;
505
506         gpr_$set_bitmap(screen_desc, &status);
507         apollo_check("set_bitmap(screen_desc)");
508
509         gpr_$pixel_blt(bitmap_desc, window, pos, &status);
510         apollo_check("bitblt");
511
512         gpr_$set_bitmap(bitmap_desc, &status);
513         apollo_check("set_bitmap(bitmap_desc)");
514     }
515 }
516
517 TERM_PUBLIC int
518 APOLLO_text_angle(int ang)
519 {
520     status_$t status;
521
522     gpr_$set_text_path(ang ? gpr_$up : gpr_$right, &status);
523     apollo_check("set_text_path");
524     return TRUE;
525 }
526
527 static enum JUSTIFY apollo_text_mode;
528
529 TERM_PUBLIC int
530 APOLLO_justify_text(enum JUSTIFY mode)
531 {
532     apollo_text_mode = mode;
533     return TRUE;
534 }
535
536 /*
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.
540 */
541 TERM_PUBLIC void
542 APOLLO_put_text(unsigned int x, unsigned int y, const char str[])
543 {
544     gpr_$offset_t size;
545     status_$t status;
546
547     gpr_$inq_text_extent(str, strlen(str), &size, &status);
548     apollo_check("inq_text_extent");
549
550     y -= size.y_size / 2;       /* center around "y" */
551     switch (apollo_text_mode) {
552     case LEFT:
553         break;
554     case CENTRE:
555         x -= size.x_size / 2;
556         break;
557     case RIGHT:
558         x -= size.x_size;
559         break;
560     }
561     APOLLO_move(x, y);
562
563     gpr_$text(str, strlen(str), &status);
564     apollo_check("put_text");
565 }
566
567 /* reset the graphics state and terminate */
568 TERM_PUBLIC void
569 APOLLO_reset()
570 {
571     if (stream != -1) {
572         apollo_gpr_terminate();
573         stream = -1;
574     }
575 }
576
577 #endif /* TERM_BODY */
578
579 #ifdef TERM_TABLE
580 TERM_TABLE_START(apollo_driver)
581     "apollo",
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)
589
590 #undef LAST_TERM
591 #define LAST_TERM apollo_driver
592
593 #endif /* TERM_TABLE */
594 #endif /* TERM_PROTO_ONLY */
595
596 #ifdef TERM_HELP
597 START_HELP(apollo)
598 "1 apollo",
599 "?commands set terminal apollo",
600 "?set terminal apollo",
601 "?set term apollo",
602 "?terminal apollo",
603 "?term apollo",
604 "?apollo",
605 " The `apollo` terminal driver supports the Apollo Graphics Primitive Resource",
606 " with rescaling after window resizing.  It has no options.",
607 "",
608 " If a fixed-size window is desired, the `gpr` terminal may be used instead."
609 END_HELP(apollo)
610 #endif /* TERM_HELP */