1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <ctype.h>
4 #ifndef convex
5 #include <string.h>
6 #endif
7 
8 /* X-Window include files */
9 
10 #include <X11/Xos.h>
11 #include <X11/Xlib.h>
12 #include <X11/Xutil.h>
13 #include <X11/keysym.h>
14 
15 #include "pgxwin.h"
16 
17 #define PGX_IDENT "pgxwin"
18 #define PGX_IMAGE_LEN 1280  /* Length of the line-of-pixels buffer */
19 #define PGX_COLORMULT 65535 /* Normalized color intensity multiplier */
20 #define PGX_NCOLORS 16      /* Number of pre-defined PGPLOT colors */
21 #define PGX_QUERY_MAX 512   /* Max colormap query size in pgx_readonly_colors() */
22 
23 /* A container used to record the geometry of the Pixmap */
24 
25 typedef struct {
26   float xpix_per_inch; /* Number of pixels per inch along X */
27   float ypix_per_inch; /* Number of pixels per inch along Y */
28   unsigned int width;  /* Width of window (pixels) */
29   unsigned int height; /* Height of window (pixels) */
30   int xmargin;         /* X-axis 1/4" margin in pixels */
31   int ymargin;         /* Y-axis 1/4" margin in pixels */
32   int xmin,xmax;       /* Min/max X-axis pixels excluding 1/4" margins */
33   int ymin,ymax;       /* Min/max X-axis pixels excluding 1/4" margins */
34 } XWgeom;
35 
36 /*
37  * Declare a colormap update descriptor.
38  * This keeps a record of the number of buffered colormap updates
39  * that need to be sent to the display. This allows color-representations
40  * from multiple consecutive calls to pgscr() and pgshls() to be cached.
41  */
42 typedef struct {
43   int nbuff;         /* The number of buffered color representation updates */
44   int sbuff;         /* The index of the first buffered color representation */
45 } XWcolor;
46 
47 /*
48  * Declare a polygon descriptor.
49  */
50 typedef struct {
51   XPoint *points;  /* Temporary array of polygon vertexes */
52   int npoint;      /* Number of points in polygon */
53   int ndone;       /* The number of points received so far */
54 } XWpoly;
55 
56 /*
57  * Declare a container used to record the extent of the rectangular
58  * pixmap area that has been modified since the last pgx_flush().
59  */
60 typedef struct {
61   int modified;    /* True if 'pixmap' has been modified since last update */
62   int xmin,xmax;   /* X-axis extent of modified region (pixels) */
63   int ymin,ymax;   /* Y-axis extent of modified region (pixels) */
64 } XWupdate;
65 
66 /*
67  * Declare a cursor state container.
68  */
69 typedef struct {
70   int drawn;             /* True when the cursor is drawn */
71   GC gc;                 /* The graphical context of the cursor-band lines */
72   int warp;              /* True to warp the cursor on first window entry */
73   int type;              /* The cursor banding type PGX_..._CURSOR */
74   XPoint vbeg, vend;     /* Start and end vertices of band cursors */
75 } XWcursor;
76 
77 /*
78  * Declare a world-coordinate coordinate conversion object.
79  */
80 typedef struct {
81   float xoff, xdiv;               /* world_x = (device_x - xoff) / xdiv */
82   float yoff, ydiv;               /* world_y = (device_y - yoff) / ydiv */
83 } XWworld;
84 
85 /*
86  * Declare a container to encapsulate the buffers needed to
87  * draw a line of pixels.
88  */
89 typedef struct {
90   XImage *xi;          /* Line of pixels Xlib image object */
91 } XWimage;
92 
93 /*
94  * Declare a function type, instances of which are to be called to flush
95  * buffered opcodes, and return 0 if OK, or 1 on error.
96  */
97 typedef int (*Flush_Opcode_fn) ARGS((PgxWin *));
98 
99 /*
100  * The following container is used to retain state information for /xw
101  * connections.
102  */
103 struct PgxState {
104   XWgeom geom;       /* Pixmap geometry */
105   XWcolor color;     /* Colormap state descriptor */
106   XWpoly poly;       /* Polygon-fill accumulation descriptor */
107   XWupdate update;   /* Descriptor of un-drawn area of pixmap */
108   XWcursor cursor;   /* Cursor state context descriptor */
109   XWworld world;     /* World-coordinate conversion descriptor */
110   XWimage image;     /* Line of pixels container */
111   XGCValues gcv;     /* Publicly visible contents of 'gc' */
112   GC gc;             /* Graphical context descriptor */
113   int last_opcode;   /* Index of last opcode */
114   Flush_Opcode_fn flush_opcode_fn; /* Function to flush a buffered opcode */
115 };
116 
117 static int pgx_error_handler ARGS((Display *display, XErrorEvent *event));
118 static PgxColor *pgx_find_visual ARGS((PgxWin *pgx, int class, int min_col, \
119 				 int max_col, int readonly));
120 static int pgx_get_colorcells ARGS((PgxWin *pgx, PgxColor *color, \
121 			      int min_col, int max_col));
122 static int pgx_get_mutable_colorcells ARGS((PgxWin *pgx, PgxColor *color, \
123 			      int min_col, int max_col));
124 
125 static XVisualInfo *pgx_visual_info ARGS((Display *display, int screen, \
126 					  VisualID vid));
127 static int pgx_parse_visual ARGS((char *str));
128 static void pgx_xy_to_XPoint ARGS((PgxWin *pgx, float *xy, XPoint *xp));
129 static void pgx_XPoint_to_xy ARGS((PgxWin *pgx, XPoint *xp, float *xy));
130 static void pgx_mark_modified ARGS((PgxWin *pgx, int x, int y, int diameter));
131 static int pgx_init_colors ARGS((PgxWin *pgx));
132 static int pgx_update_colors ARGS((PgxWin *pgx));
133 static int pgx_flush_colors ARGS((PgxWin *pgx, int ci_start, int ncol));
134 static int pgx_restore_line ARGS((PgxWin *pgx, int xa, int ya, int xb, int yb));
135 static int pgx_handle_cursor ARGS((PgxWin *pgx, float *rbuf, char *key));
136 static void pgx_limit_pcoords ARGS((PgxWin *pgx, XPoint *coord));
137 static void pgx_limit_wcoords ARGS((PgxWin *pgx, XPoint *coord));
138 static void pgx_window_to_pixmap ARGS((PgxWin *pgx, XPoint *w_coord, \
139 				    XPoint *p_coord));
140 static void pgx_pixmap_to_window ARGS((PgxWin *pgx, XPoint *p_coord, \
141 				    XPoint *w_coord));
142 static int pgx_copy_area ARGS((PgxWin *pgx, int px, int py, unsigned w, \
143 			       unsigned h, int wx, int wy));
144 static int pgx_clear_area ARGS((PgxWin *pgx, int x, int y, unsigned w,
145 				unsigned h));
146 PgxColor *new_PgxColor ARGS((PgxWin *pgx, int max_col, int readonly, \
147 			     VisualID vid));
148 static PgxColor *del_PgxColor ARGS((PgxWin *pgx, PgxColor *color));
149 static int pgx_default_class ARGS((PgxWin *pgx));
150 static int pgx_nint ARGS((float f));
151 
152 static int pgx_readonly_colors ARGS((PgxWin *pgx, int ncol, XColor *colors, \
153 				     unsigned long *pixels));
154 static int pgx_cmp_xcolor ARGS((const void *va, const void *vb));
155 static int pgx_nearest_color ARGS((XColor *colors, int ncol, XColor *c));
156 
157 /*
158  * pgx_ready() accepts a state bitmask that contains a union of the
159  * following enumerated resource requirement bits.
160  */
161 #define PGX_NEED_COLOR  1  /* Colormap allocated */
162 #define PGX_NEED_WINDOW 2  /* Window created */
163 #define PGX_NEED_PIXMAP 4  /* A valid pixmap exists */
164 #define PGX_NEED_PGOPEN 8  /* Open to PGPLOT */
165 
166 static int pgx_ready ARGS((PgxWin *pgx, int state));
167 
168 /*.......................................................................
169  * This function should be called to instantiate PgxWin::state when
170  * an existing device is opened with pgbeg() or pgopen(). Note that all
171  * but the pixmap and state members of the passed PgxWin structure must have
172  * been instantiated. The pixmap member may either be instantiated or
173  * be initialized with the null-atom constant: None. The state member
174  * must be NULL.
175  *
176  * Input:
177  *  pgx      PgxWin *  The PGPLOT window context to connect to.
178  * Output:
179  *  return PgxState *  The PGPLOT drawing-state descriptor, or NULL
180  *                     on error.
181  */
182 #ifdef __STDC__
pgx_open(PgxWin * pgx)183 PgxState *pgx_open(PgxWin *pgx)
184 #else
185 PgxState *pgx_open(pgx)
186      PgxWin *pgx;
187 #endif
188 {
189   PgxState *state;  /* The new state descriptor */
190 /*
191  * Check the validity of the PgxWin structure.
192  */
193   if(!pgx || !pgx->display || pgx->window==None || !pgx->expose_gc ||
194      pgx->bad_device || !pgx->name || !pgx->color) {
195     fprintf(stderr, "pgx_open: Bad PgxWin descriptor.\n");
196     return NULL;
197   };
198   if(pgx->state) {
199     fprintf(stderr, "pgx_open: The specified device is already open.\n");
200     return NULL;
201   };
202 /*
203  * Allocate the state container.
204  */
205   state = (PgxState *) malloc(sizeof(PgxState));
206   if(!state) {
207     fprintf(stderr, "pgx_open: Insufficient memory.\n");
208     return NULL;
209   };
210 /*
211  * Before attempting any operation that might fail, initialize
212  * the container at least up to the point at which it is
213  * safe to pass it to pgx_close().
214  */
215   pgx->state = state;
216   state->geom.xpix_per_inch = 0.0;
217   state->geom.ypix_per_inch = 0.0;
218   state->geom.width = 0;
219   state->geom.height = 0;
220   state->geom.xmargin = 0;
221   state->geom.ymargin = 0;
222   state->geom.xmin = 0;
223   state->geom.xmax = 0;
224   state->geom.ymin = 0;
225   state->geom.ymax = 0;
226   state->color.nbuff = 0;
227   state->color.sbuff = 0;
228   state->poly.points = NULL;
229   state->poly.npoint = 0;
230   state->poly.ndone = 0;
231   state->update.modified = 0;
232   state->update.xmin = 0;
233   state->update.xmax = 0;
234   state->update.ymin = 0;
235   state->update.ymax = 0;
236   state->cursor.drawn = 0;
237   state->cursor.gc = NULL;
238   state->cursor.type = PGX_NORM_CURSOR;
239   state->world.xoff = 0.0;
240   state->world.yoff = 0.0;
241   state->world.xdiv = 1.0;
242   state->world.ydiv = 1.0;
243   state->image.xi = NULL;
244   state->gc = NULL;
245   state->last_opcode = 0;
246   state->flush_opcode_fn = 0;
247 /*
248  * Create and initialize a graphical context descriptor. This is where
249  * Line widths, line styles, fill styles, plot color etc.. are
250  * recorded.
251  */
252   state->gcv.line_width = 1;
253   state->gcv.cap_style = CapRound;
254   state->gcv.join_style = JoinRound;
255   state->gcv.fill_rule = EvenOddRule;
256   state->gcv.graphics_exposures = False;
257   state->gcv.foreground = WhitePixel(pgx->display, pgx->screen);
258 /*
259  * Bracket the actual creation with pgx_start/end_error() calls, to
260  * determine whether any allocation errors occur.
261  */
262   pgx_start_error_watch(pgx);
263   state->gc = XCreateGC(pgx->display, pgx->window,
264 			(unsigned long) (GCLineWidth | GCCapStyle |
265 					 GCJoinStyle | GCFillRule |
266 					 GCGraphicsExposures | GCForeground),
267 			&state->gcv);
268   if(pgx_end_error_watch(pgx) || !state->gc) {
269     fprintf(stderr, "%s: Failed to allocate graphical context.\n", PGX_IDENT);
270     return pgx_close(pgx);
271   };
272 /*
273  * Create the X image that we use to compose lines of pixels with given
274  * colors.
275  */
276   pgx_start_error_watch(pgx);
277   state->image.xi = XCreateImage(pgx->display, pgx->color->vi->visual,
278 				 (unsigned)pgx->color->vi->depth, ZPixmap, 0,
279 				 NULL, (unsigned)PGX_IMAGE_LEN, 1, 32, 0);
280   if(pgx_end_error_watch(pgx) || !state->image.xi) {
281     fprintf(stderr, "%s: Failed to allocate XImage descriptor.\n", PGX_IDENT);
282     return pgx_close(pgx);
283   };
284 /*
285  * Allocate the image buffer.
286  */
287   state->image.xi->data = malloc((size_t) state->image.xi->bytes_per_line);
288   if(!state->image.xi->data) {
289     fprintf(stderr, "%s: Failed to allocate image buffer.\n", PGX_IDENT);
290     return pgx_close(pgx);
291   };
292 /*
293  * Create the cursor graphical context.
294  */
295   pgx_start_error_watch(pgx);
296   {
297     XGCValues gcv;
298     gcv.line_width = 0;
299     gcv.graphics_exposures = False;
300     gcv.foreground = WhitePixel(pgx->display, pgx->screen);
301     state->cursor.gc = XCreateGC(pgx->display, pgx->window,
302 			(unsigned long) (GCLineWidth | GCGraphicsExposures |
303 					 GCForeground), &gcv);
304   };
305   if(pgx_end_error_watch(pgx) || !state->cursor.gc) {
306     fprintf(stderr, "%s: Failed to allocate graphical context.\n", PGX_IDENT);
307     return pgx_close(pgx);
308   };
309 /*
310  * Initialize attributes.
311  */
312   pgx_init_colors(pgx);
313   pgx_set_ci(pgx, 1);
314   pgx_set_lw(pgx, 1);
315   pgx_set_cursor(pgx, 0, PGX_NORM_CURSOR, 0, NULL, NULL);
316   return state;
317 }
318 
319 /*.......................................................................
320  * This function should be called to delete a pgx->state PGPLOT
321  * drawing-state descriptor when pgend() is called. pgx->state will
322  * also be assigned NULL.
323  *
324  * Input:
325  *  pgx      PgxWin *  The PGPLOT window context to disconnect.
326  * Output:
327  *  return PgxState *  The deleted PGPLOT drawing-state descriptor.
328  *                     Always NULL.
329  */
330 #ifdef __STDC__
pgx_close(PgxWin * pgx)331 PgxState *pgx_close(PgxWin *pgx)
332 #else
333 PgxState *pgx_close(pgx)
334      PgxWin *pgx;
335 #endif
336 {
337   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
338     PgxState *state = pgx->state;
339 /*
340  * Delete the graphical context descriptor.
341  */
342     if(state->gc)
343       XFreeGC(pgx->display, state->gc);
344     state->gc = NULL;
345 /*
346  * Delete the image descriptor.
347  */
348     if(state->image.xi)
349       XDestroyImage(state->image.xi);
350     state->image.xi = NULL;
351 /*
352  * Check for un-freed polygon points.
353  */
354     if(state->poly.points)
355       free((char *)state->poly.points);
356     state->poly.points = NULL;
357 /*
358  * Delete the cursor graphical context descriptor.
359  */
360     if(state->cursor.gc)
361       XFreeGC(pgx->display, state->cursor.gc);
362     state->cursor.gc = NULL;
363 /*
364  * Delete the container.
365  */
366     free(state);
367     pgx->state = NULL;
368   };
369   return NULL;
370 }
371 
372 /*.......................................................................
373  * This function must be called before each opcode is handled by the
374  * device-specific driver dispatch function.
375  *
376  * Input:
377  *  pgx      PgxWin *    The PGPLOT window context.
378  *  opcode      int      The new opcode.
379  * Output:
380  *  return      int      0 - OK.
381  *                       1 - Error.
382  */
383 #ifdef __STDC__
pgx_pre_opcode(PgxWin * pgx,int opcode)384 int pgx_pre_opcode(PgxWin *pgx, int opcode)
385 #else
386 int pgx_pre_opcode(pgx, opcode)
387      PgxWin *pgx; int opcode;
388 #endif
389 {
390 /*
391  * If there is a buffered opcode and the latest opcode is not the same
392  * as the last opcode, call the given flush function for the
393  * buffered opcode.
394  */
395   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
396     PgxState *state = pgx->state;
397     if(state->last_opcode != opcode) {
398       if(state->flush_opcode_fn != (Flush_Opcode_fn) 0) {
399 	(*state->flush_opcode_fn)(pgx);
400 	state->flush_opcode_fn = (Flush_Opcode_fn) 0;
401       };
402 /*
403  * Record the current opcode for next time.
404  */
405       state->last_opcode = opcode;
406     };
407   };
408   return 0;
409 }
410 
411 /*.......................................................................
412  * This function returns non-zero if the specified descriptor is not
413  * NULL, not marked as bad via pgx->bad_device, and has all of the
414  * specified resources.
415  *
416  * Input:
417  *  pgx     PgxWin *  The device descriptor to be checked.
418  *  state      int    A bitmask of resources that are required.
419  *                     PGX_NEED_COLOR  - Colormap allocated.
420  *                     PGX_NEED_WINDOW - Window created.
421  *                     PGX_NEED_PIXMAP - A valid pixmap exists.
422  *                     PGX_NEED_PGOPEN - Open to PGPLOT. Note that
423  *                         this implies that a colormap and window
424  *                         exist but not that a pixmap exists.
425  * Output:
426  *  return     int    1 - Descriptor OK.
427  *                    0 - Error - don't use pgx.
428  */
429 #ifdef __STDC__
pgx_ready(PgxWin * pgx,int state)430 static int pgx_ready(PgxWin *pgx, int state)
431 #else
432 static int pgx_ready(pgx, state)
433      PgxWin *pgx; int state;
434 #endif
435 {
436   if(!pgx || pgx->bad_device)
437     return 0;
438   if(state & PGX_NEED_COLOR && !pgx->color)
439     return 0;
440   if(state & PGX_NEED_WINDOW && pgx->window == None)
441     return 0;
442   if(state & PGX_NEED_PIXMAP && pgx->pixmap == None)
443     return 0;
444   if(state & PGX_NEED_PGOPEN && !pgx->state)
445     return 0;
446   return 1;
447 }
448 
449 /*.......................................................................
450  * Call this function when an Expose event is received. It will then
451  * re-draw the exposed region from the pgx->pixmap, taking acount of any
452  * scroll offsets in pgx->scroll. The device need not be open to PGPLOT.
453  *
454  * Input:
455  *  pgx     PgxWin *  The PGPLOT window context.
456  *  event   XEvent *  The expose event.
457  * Output:
458  *  return     int    0 - OK.
459  *                    1 - Error.
460  */
461 #ifdef __STDC__
pgx_expose(PgxWin * pgx,XEvent * event)462 int pgx_expose(PgxWin *pgx, XEvent *event)
463 #else
464 int pgx_expose(pgx, event)
465      PgxWin *pgx; XEvent *event;
466 #endif
467 {
468 /*
469  * Device error?
470  */
471   if(pgx_ready(pgx, PGX_NEED_PIXMAP | PGX_NEED_WINDOW) && event->type==Expose) {
472     pgx_copy_area(pgx, (int)(event->xexpose.x + pgx->scroll.x),
473 		  (int)(event->xexpose.y + pgx->scroll.y),
474 		  (unsigned) event->xexpose.width,
475 		  (unsigned) event->xexpose.height,
476 		  event->xexpose.x, event->xexpose.y);
477 /*
478  * Re-draw the possibly damaged cursor augmentation.
479  */
480     pgx_refresh_cursor(pgx);
481 /*
482  * Ensure that the window responds immediately to the update.
483  */
484     XFlush(pgx->display);
485     if(pgx->bad_device)
486       return 1;
487   };
488   return 0;
489 }
490 
491 /*.......................................................................
492  * Scroll the pixmap within the window area.
493  *
494  * Input:
495  *  pgx     PgxWin *  The PGPLOT window context.
496  *  x,y   unsigned    The position of the top left corner of the window
497  *                    within the pixmap. This allows for clients that
498  *                    want to scroll the pixmap within the window area.
499  * Output:
500  *  return     int    0 - OK.
501  *                    1 - Error.
502  */
503 #ifdef __STDC__
pgx_scroll(PgxWin * pgx,unsigned x,unsigned y)504 int pgx_scroll(PgxWin *pgx, unsigned x, unsigned y)
505 #else
506 int pgx_scroll(pgx, x, y)
507      PgxWin *pgx; unsigned x; unsigned y;
508 #endif
509 {
510   if(pgx_ready(pgx, 0)) {
511     XEvent event;
512 /*
513  * Record the new scroll and pan values.
514  */
515     pgx->scroll.x = x;
516     pgx->scroll.y = y;
517 /*
518  * Redraw the scrolled contents of the window.
519  */
520     if(pgx_ready(pgx, PGX_NEED_WINDOW | PGX_NEED_PIXMAP)) {
521       XPoint brc;  /* Bottom right corner of pixmap */
522 /*
523  * We need to know the size of the window and the size of the pixmap.
524  */
525       XWindowAttributes attr;
526       XGetWindowAttributes(pgx->display, pgx->window, &attr);
527       if(pgx->bad_device)
528 	return 1;
529 /*
530  * Record the bottom-right-corner pixmap coordinate in brc.
531  */
532       {
533 	Window root;
534 	int x_root, y_root;
535 	unsigned width, height, border, depth;
536 	XGetGeometry(pgx->display, pgx->pixmap, &root, &x_root, &y_root,
537 		     &width, &height, &border, &depth);
538 	brc.x = width - 1;
539 	brc.y = height - 1;
540       };
541 /*
542  * Determine the scrolled window coordinate at which the bottom right
543  * corner of the pixmap lies.
544  */
545       pgx_pixmap_to_window(pgx, &brc, &brc);
546 /*
547  * Clear the parts of the window that will not be covered by the pixmap.
548  * Given that scroll.x and scroll.y are unsigned this can only be
549  * areas to the left and bottom of the drawn area.
550  */
551 
552       if(brc.x < attr.width) {
553 	pgx_clear_area(pgx, (brc.x + 1), 0,
554 		   (unsigned) (attr.width - brc.x - 1),
555 		   (unsigned) (attr.height));
556       };
557       if(brc.y < attr.height) {
558 	pgx_clear_area(pgx, 0, (brc.y + 1),
559 		   (unsigned)(attr.width),
560 		   (unsigned)(attr.height - brc.y - 1));
561       };
562 /*
563  * Set up a fake expose event to have the new pixmap area drawn.
564  */
565       event.type = Expose;
566       event.xexpose.x = 0;
567       event.xexpose.y = 0;
568       event.xexpose.width = brc.x + 1;
569       event.xexpose.height = brc.y + 1;
570       return pgx_expose(pgx, &event);
571     };
572   };
573   return 1;
574 }
575 
576 /*.......................................................................
577  * Update the recorded extent of the drawable area of the window.
578  * This must be called whenever the window is resized.
579  *
580  * Input:
581  *  pgx      PgxWin *  The PGPLOT window context.
582  *  doclip      int    0 - Disable clipping entirely.
583  *                     1 - Clip all graphics outside the specified region.
584  *  width  unsigned    The width of the window.
585  *  height unsigned    The height of the window.
586  *  border unsigned    The width of the window border.
587  * Output:
588  *  return     int    0 - OK.
589  *                    1 - Error.
590  */
591 #ifdef __STDC__
pgx_update_clip(PgxWin * pgx,int doclip,unsigned width,unsigned height,unsigned border)592 int pgx_update_clip(PgxWin *pgx, int doclip, unsigned width, unsigned height,
593 		    unsigned border)
594 #else
595 int pgx_update_clip(pgx, doclip, width, height, border)
596      PgxWin *pgx; int doclip; unsigned width; unsigned height; unsigned border;
597 #endif
598 {
599   if(pgx_ready(pgx, 0)) {
600     pgx->clip.doclip = doclip;
601     pgx->clip.xmin = border;
602     pgx->clip.ymin = border;
603     pgx->clip.xmax = width - border - 1;
604     pgx->clip.ymax = height - border - 1;
605     return 0;
606   };
607   return 1;
608 }
609 
610 /*.......................................................................
611  * Update the dimensions of the optional X and Y axis margins. The new
612  * dimensions will be ignored until the start of the next page.
613  *
614  * Margins are blank areas left around the plottable viewsurface, purely
615  * for esthetic reasons.
616  *
617  * Input:
618  *  pgx      PgxWin *  The PGPLOT window context.
619  *  xmargin     int    The number of pixels to leave either side of
620  *                     the plot surface.
621  *  ymargin     int    The number of pixels to leave above and below
622  *                     the plot suface.
623  * Output:
624  *  return     int    0 - OK.
625  *                    1 - Error.
626  */
627 #ifdef __STDC__
pgx_set_margin(PgxWin * pgx,int xmargin,int ymargin)628 int pgx_set_margin(PgxWin *pgx, int xmargin, int ymargin)
629 #else
630 int pgx_set_margin(pgx, xmargin, ymargin)
631      PgxWin *pgx; int xmargin; int ymargin;
632 #endif
633 {
634   if(pgx_ready(pgx, 0)) {
635     pgx->xmargin = xmargin > 0 ? xmargin : 0;
636     pgx->ymargin = ymargin > 0 ? ymargin : 0;
637     return 0;
638   };
639   return 1;
640 }
641 
642 /*.......................................................................
643  * Install a temporary error handler for use in detecting resource
644  * allocation errors. The error handler will maintain a count of
645  * errors, which will be returned by the matching function
646  * pgx_end_error_watch(). These functions are intended to be used to
647  * bracket resource allocation functions, in order to allow resource
648  * allocation failures to be detected.
649  *
650  * Input:
651  *  pgx   PgxWin *  The PGPLOT window context descriptor.
652  */
653 #ifdef __STDC__
pgx_start_error_watch(PgxWin * pgx)654 void pgx_start_error_watch(PgxWin *pgx)
655 #else
656 void pgx_start_error_watch(pgx)
657      PgxWin *pgx;
658 #endif
659 {
660   if(pgx_ready(pgx, 0)) {
661 /*
662  * Force all errors from previous events to be flushed.
663  */
664     XSync(pgx->display, False);
665 /*
666  * Clear the error-handler internal error count.
667  */
668     pgx_error_handler(pgx->display, (XErrorEvent *) 0);
669 /*
670  * Install the error handler.
671  */
672     pgx->old_handler = XSetErrorHandler(pgx_error_handler);
673   };
674   return;
675 }
676 
677 /*.......................................................................
678  * This function is paired with pgx_start_error_watch() and and after
679  * ensuring that all errors have been trapped by calling XSync(), it
680  * returns the error count recorded by the error handler that said function
681  * installed. It then removes the error handler.
682  *
683  * Input:
684  *  pgx     PgxWin *  The PGPLOT window context descriptor.
685  * Output:
686  *  return     int    The number of errors that were counted.
687  */
688 #ifdef __STDC__
pgx_end_error_watch(PgxWin * pgx)689 int pgx_end_error_watch(PgxWin *pgx)
690 #else
691 int pgx_end_error_watch(pgx)
692      PgxWin *pgx;
693 #endif
694 {
695   if(pgx_ready(pgx, 0)) {
696 /*
697  * Ensure that all error-events have delivered.
698  */
699     XSync(pgx->display, False);
700 /*
701  * De-install the error handler and re-instate the one that it displaced.
702  */
703     XSetErrorHandler(pgx->old_handler);
704     pgx->old_handler = 0;
705 /*
706  * Return the current error-count.
707  */
708     return pgx_error_handler(pgx->display, (XErrorEvent *) 0);
709   };
710   return 0;
711 }
712 
713 /*.......................................................................
714  * This function is called by X whenever a non-fatal error occurs
715  * on a given display connection. For the moment it does nothing but
716  * count such errors in an internal static error counter. This counter
717  * can then be queried and reset by sending a NULL error event pointer.
718  *
719  * Input:
720  *  display    Display *  The display connection on which the error occured.
721  *  event  XErrorEvent *  The descriptor of the error event, or NULL to
722  *                        request that the error counter be queried and reset.
723  * Output:
724  *  return         int    The return value is not specified by Xlib, so
725  *                        for Xlib calls we will simply return 0. For
726  *                        none Xlib calls (distinguishable by sending
727  *                        event==NULL), the value of the error counter
728  *                        is returned.
729  */
730 #ifdef __STDC__
pgx_error_handler(Display * display,XErrorEvent * event)731 static int pgx_error_handler(Display *display, XErrorEvent *event)
732 #else
733 static int pgx_error_handler(display, event)
734      Display *display; XErrorEvent *event;
735 #endif
736 {
737   static int error_count = 0;
738 /*
739  * To query and reset the error counter, this program calls pgx_error_handler()
740  * with a NULL error event pointer. This distinguishes it from a call
741  * from Xlib.
742  */
743   if(!event) {
744     int ret_count = error_count;
745     error_count = 0;   /* Reset the error counter */
746     return ret_count;  /* Return the pre-reset value of the error counter */
747 #ifdef DEBUG
748   } else {
749     char errtxt[81]; /* Buffer to receive error message in */
750 /*
751  * Get a message describing the error.
752  */
753     XGetErrorText(display, (int)event->error_code, errtxt, (int)sizeof(errtxt));
754     fprintf(stderr, "%s: XErrorEvent: %s\n", ERROR_PREFIX, errtxt);
755 /*
756  * Report the operation that caused it. These opcode numbers are listed in
757  * <X11/Xproto.h>.
758  */
759     fprintf(stderr, "%s: Major opcode: %d, Resource ID: 0x%lx%s.\n",
760 	 ERROR_PREFIX, (int) event->request_code,
761 	 (unsigned long) event->resourceid,
762          (event->resourceid==DefaultRootWindow(display)?" (Root window)":""));
763 #endif
764   };
765 /*
766  * Keep a record of the number of errors that have occurred since the
767  * error counter was last cleared.
768  */
769   error_count++;
770   return 0;
771 }
772 
773 /*.......................................................................
774  * Allocate a visual/colormap context and initialize it with defaults.
775  *
776  * Input:
777  *  pgx      PgxWin *  The PGPLOT window context.
778  *  max_col     int    The maximum number of colors to allow for.
779  *  readonly    int    True if readonly colors are sufficient.
780  *  vid    VisualID    The ID of the visual target visual.
781  * Output:
782  *  return PgxColor *  The new context descriptor, or NULL on error.
783  */
784 #ifdef __STDC__
new_PgxColor(PgxWin * pgx,int max_col,int readonly,VisualID vid)785 PgxColor *new_PgxColor(PgxWin *pgx, int max_col, int readonly, VisualID vid)
786 #else
787 PgxColor *new_PgxColor(pgx, max_col, readonly, vid)
788      PgxWin *pgx; int max_col; int readonly; VisualID vid;
789 #endif
790 {
791   PgxColor *color;  /* The new context descriptor */
792   if(!pgx_ready(pgx, 0))
793     return NULL;
794 /*
795  * Allocate the context descriptor.
796  */
797   color = (PgxColor *) malloc(sizeof(PgxColor));
798   if(!color) {
799     fprintf(stderr, "%s: (new_PgxColor) Insufficient memory.\n", PGX_IDENT);
800     return NULL;
801   };
802 /*
803  * Before attempting any operation that might fail, initialize the
804  * color descriptor at least up to the point at which it can safely be
805  * passed to del_PgxColor().
806  */
807   color->vi = NULL;
808   color->cmap = None;
809   color->private = 0;
810   color->ncol = 2;
811   color->monochrome = 1;
812   color->pixel = NULL;
813   color->npixel = 0;
814   color->xcolor = NULL;
815   color->initialized = 0;
816   color->default_class = 0;
817   color->readonly = readonly;
818   color->nwork = 0;
819   color->work = NULL;
820 /*
821  * Get the visual information object.
822  */
823   color->vi = pgx_visual_info(pgx->display, pgx->screen, vid);
824   if(!color->vi)
825     return del_PgxColor(pgx, color);
826 /*
827  * See what type of color allocation will be needed.
828  */
829   switch(color->vi->class) {
830   case PseudoColor:
831   case GrayScale:
832     color->readonly = readonly;
833     break;
834   case DirectColor:
835     color->readonly = 0;
836     break;
837   case StaticColor:
838   case TrueColor:
839   case StaticGray:
840     color->readonly = 1;
841     break;
842   default:
843     fprintf(stderr, "%s: Unknown colormap type.\n", PGX_IDENT);
844     return del_PgxColor(pgx, color);
845     break;
846   };
847 /*
848  * We can only handle between 2 and 256 colors.
849  */
850   if(max_col < 2)
851     max_col = 2;
852   else if(max_col > 256)
853     max_col = 256;
854 /*
855  * Determine the class of the default visual.
856  */
857   color->default_class = pgx_default_class(pgx);
858 /*
859  * Allocate an array to store pixel indexes in.
860  */
861   color->pixel = (unsigned long *) malloc(sizeof(unsigned long) * max_col);
862   if(color->pixel==NULL) {
863     fprintf(stderr, "%s: Insufficient memory for new PGPLOT window.\n",
864 	    PGX_IDENT);
865     return del_PgxColor(pgx, color);
866   };
867 /*
868  * Allocate an array to store color representations in.
869  */
870   color->xcolor = (XColor *) malloc(sizeof(XColor) * max_col);
871   if(!color->xcolor) {
872     fprintf(stderr, "%s: Insufficient memory for new PGPLOT window.\n",
873 	    PGX_IDENT);
874     return del_PgxColor(pgx, color);
875   };
876 /*
877  * Allocate a work array for use by pgx_readonly_colors().
878  */
879   if(readonly) {
880     color->nwork = color->vi->colormap_size;
881     if(color->nwork > PGX_QUERY_MAX)
882       color->nwork = PGX_QUERY_MAX;
883     color->work = (XColor *) malloc(sizeof(XColor) * color->nwork);
884     if(!color->work) {
885       fprintf(stderr, "%s: Insufficient memory for shared-color allocation.\n",
886 	      PGX_IDENT);
887       return del_PgxColor(pgx, color);
888     };
889   };
890 /*
891  * Leave the rest of the initialization to specific functions.
892  */
893   return color;
894 }
895 
896 /*.......................................................................
897  * Delete a color/visual context descriptor.
898  *
899  * Input:
900  *  pgx       PgxWin *  The PGPLOT window context.
901  *  color   PgxColor *  The descriptor to be deleted.
902  * Output:
903  *  return  PgxColor *  The deleted descriptor (always NULL).
904  */
905 #ifdef __STDC__
del_PgxColor(PgxWin * pgx,PgxColor * color)906 static PgxColor *del_PgxColor(PgxWin *pgx, PgxColor *color)
907 #else
908 static PgxColor *del_PgxColor(pgx, color)
909      PgxWin *pgx; PgxColor *color;
910 #endif
911 {
912   if(color) {
913     if(color->vi) {
914       if(color->cmap) {
915 /*
916  * Release allocated colorcells.
917  */
918 	if(pgx->display && color->pixel && color->npixel > 0) {
919 	  XFreeColors(pgx->display, color->cmap, color->pixel,
920 		      color->npixel, (unsigned long)0);
921 	};
922       };
923 /*
924  * Delete the colormap only if we allocated it.
925  */
926       if(pgx->display && color->private &&
927 	 color->cmap != DefaultColormap(pgx->display, pgx->screen))
928 	XFreeColormap(pgx->display, color->cmap);
929 /*
930  * Delete the visual information descriptor.
931  */
932       XFree((char *) color->vi);
933     };
934 /*
935  * Delete the pixel and color representation arrays.
936  */
937     if(color->pixel)
938       free(color->pixel);
939     if(color->xcolor)
940       free(color->xcolor);
941     if(color->work)
942       free(color->work);
943 /*
944  * Delete the container.
945  */
946     free(color);
947   };
948   return NULL;
949 }
950 
951 /*.......................................................................
952  * Return the colormap class of the default visual.
953  *
954  * Input:
955  *  pgx    PgxWin *  The PGPLOT window context descriptor.
956  * Output:
957  *  return    int    The colormap class.
958  */
959 #ifdef __STDC__
pgx_default_class(PgxWin * pgx)960 static int pgx_default_class(PgxWin *pgx)
961 #else
962 static int pgx_default_class(pgx)
963      PgxWin *pgx;
964 #endif
965 {
966   int class;
967   XVisualInfo *vi = pgx_visual_info(pgx->display, pgx->screen,
968 	    XVisualIDFromVisual(DefaultVisual(pgx->display, pgx->screen)));
969   if(!vi)
970     return PseudoColor;
971   class = vi->class;
972   XFree((char *) vi);
973   return class;
974 }
975 
976 /*.......................................................................
977  * Search for an appropriate visual for a PGPLOT window and create a
978  * colormap for it (unless the default visual is used).
979  *
980  * Input:
981  *  pgx      PgxWin *  The PGPLOT window context (We need the display
982  *                     and screen members).
983  *  class_name char *  The name of the desired visual class type.
984  *  min_col     int    The minimum acceptable number of colors before
985  *                     switching to monochrome.
986  *  max_col     int    The maximum number of colors to allocate.
987  *  readonly    int    If true, try to allocate readonly colors.
988  *                     false try to allocate read/write colors where
989  *                     available.
990  * Output:
991  *  return PgxColor *  The color/visual context descriprot, or NULL
992  *                     on error.
993  */
994 #ifdef __STDC__
pgx_new_visual(PgxWin * pgx,char * class_name,int min_col,int max_col,int readonly)995 PgxColor *pgx_new_visual(PgxWin *pgx, char *class_name, int min_col,
996 		   int max_col, int readonly)
997 #else
998 PgxColor *pgx_new_visual(pgx, class_name, min_col, max_col, readonly)
999      PgxWin *pgx; char *class_name; int min_col; int max_col; int readonly;
1000 #endif
1001 {
1002   PgxColor *color = NULL;   /* The new visual/colormap context descriptor */
1003   int default_class;        /* The class of the default visual */
1004   int visual_class;         /* The desired visual class */
1005   if(!pgx_ready(pgx, 0))
1006     return NULL;
1007 /*
1008  * Limit the number of colors to allowed values.
1009  */
1010   if(min_col < 0)
1011     min_col = 0;
1012   else if(min_col > 256)
1013     min_col = 256;
1014   if(max_col < 0)
1015     max_col = 0;
1016   else if(max_col > 256)
1017     max_col = 256;
1018 /*
1019  * Determine the class of the default visual.
1020  */
1021   default_class = pgx_default_class(pgx);
1022 /*
1023  * Decode the desired visual-class type.
1024  */
1025   visual_class = pgx_parse_visual(class_name);
1026 /*
1027  * Use a specific visual class?
1028  */
1029   if(visual_class >= 0)
1030     color = pgx_find_visual(pgx, visual_class, min_col, max_col, readonly);
1031 /*
1032  * Should we perform a search for a suitable visual class?
1033  */
1034   if(visual_class == -1 || !color) {
1035 /*
1036  * Color display?
1037  */
1038     switch(default_class) {
1039     case PseudoColor:
1040     case StaticColor:
1041     case DirectColor:
1042     case TrueColor:
1043       color = pgx_find_visual(pgx, PseudoColor, min_col, max_col, readonly);
1044       if(!color)
1045 	color = pgx_find_visual(pgx, StaticColor, min_col, max_col, readonly);
1046       if(!color)
1047 	color = pgx_find_visual(pgx, TrueColor, min_col, max_col, readonly);
1048       break;
1049 /*
1050  * Gray-scale display?
1051  */
1052     case GrayScale:
1053     case StaticGray:
1054       color = pgx_find_visual(pgx, GrayScale, min_col, max_col, readonly);
1055       if(!color)
1056 	color = pgx_find_visual(pgx, StaticGray, min_col, max_col, readonly);
1057       break;
1058     };
1059   };
1060 /*
1061  * If requested, or we failed to acquire the desired visual
1062  * use black and white colors from the default visual.
1063  */
1064   return color ? color : pgx_bw_visual(pgx);
1065 }
1066 
1067 /*.......................................................................
1068  * Return a black and white visual/color context descriptor.
1069  *
1070  * Input:
1071  *  pgx       PgxWin * The PGPLOT window context (We need the display
1072  *                     and screen members).
1073  * Output:
1074  *  return  PgxColor * The context descriptor, or NULL on error.
1075  */
1076 #ifdef __STDC__
pgx_bw_visual(PgxWin * pgx)1077 PgxColor *pgx_bw_visual(PgxWin *pgx)
1078 #else
1079 PgxColor *pgx_bw_visual(pgx)
1080      PgxWin *pgx;
1081 #endif
1082 {
1083   PgxColor *color;   /* The new descriptor */
1084   if(!pgx_ready(pgx, 0))
1085     return NULL;
1086 /*
1087  * Allocate the context descriptor.
1088  */
1089   color = new_PgxColor(pgx, 2, 1,
1090 	       XVisualIDFromVisual(DefaultVisual(pgx->display, pgx->screen)));
1091   if(!color)
1092     return NULL;
1093 /*
1094  * Record the default-colormap ID.
1095  */
1096   color->cmap = DefaultColormap(pgx->display, pgx->screen);
1097 /*
1098  * Record the two available colors.
1099  */
1100   color->pixel[0] = BlackPixel(pgx->display, pgx->screen);
1101   color->pixel[1] = WhitePixel(pgx->display, pgx->screen);
1102   color->ncol = 2;
1103 /*
1104  * Record the fact that we are using monochrome.
1105  */
1106   color->monochrome = 1;
1107 /*
1108  * Install the color context.
1109  */
1110   pgx->color = color;
1111 /*
1112  * Initialize the colors.
1113  */
1114   if(pgx_init_colors(pgx))
1115     return pgx_del_visual(pgx);
1116   return color;
1117 }
1118 
1119 /*.......................................................................
1120  * Return a visual/color context descriptor for colors allocated from
1121  * the default colormap of the screen.
1122  *
1123  * Input:
1124  *  pgx       PgxWin * The PGPLOT window context (We need the display
1125  *                     and screen members).
1126  *  min_col     int    The minimum acceptable number of colors before
1127  *                     switching to monochrome.
1128  *  max_col     int    The maximum number of colors to allocate.
1129  *  readonly    int    If true, try to allocate readonly colors.
1130  *                     false try to allocate read/write colors where
1131  *                     available.
1132  * Output:
1133  *  return  PgxColor * The context descriptor, or NULL on error.
1134  */
1135 #ifdef __STDC__
pgx_default_visual(PgxWin * pgx,int min_col,int max_col,int readonly)1136 PgxColor *pgx_default_visual(PgxWin *pgx, int min_col, int max_col,int readonly)
1137 #else
1138 PgxColor *pgx_default_visual(pgx, min_col, max_col, readonly)
1139      PgxWin *pgx; int min_col; int max_col; int readonly;
1140 #endif
1141 {
1142   PgxColor *color;   /* The new descriptor */
1143   if(!pgx_ready(pgx, 0))
1144     return NULL;
1145 /*
1146  * Limit the number of colors to allowed values.
1147  */
1148   if(min_col < 0)
1149     min_col = 0;
1150   else if(min_col > 256)
1151     min_col = 256;
1152   if(max_col < 0)
1153     max_col = 0;
1154   else if(max_col > 256)
1155     max_col = 256;
1156 /*
1157  * Allocate the context descriptor.
1158  */
1159   color = new_PgxColor(pgx, max_col, readonly,
1160 		XVisualIDFromVisual(DefaultVisual(pgx->display, pgx->screen)));
1161   if(!color)
1162     return NULL;
1163 /*
1164  * Record the default-colormap ID.
1165  */
1166   color->cmap = DefaultColormap(pgx->display, pgx->screen);
1167 /*
1168  * Attempt to allocate colorcells from the default colormap.
1169  * If this fails return a visual that uses the black and white
1170  * pixels of the default visual.
1171  */
1172   if(pgx_get_colorcells(pgx, color, min_col, max_col)) {
1173     color = del_PgxColor(pgx, color);
1174     return pgx_bw_visual(pgx);
1175   };
1176 /*
1177  * Record the fact that we haven't reverted to monochrome.
1178  */
1179   color->monochrome = 0;
1180 /*
1181  * Install the color context.
1182  */
1183   pgx->color = color;
1184 /*
1185  * Initialize the colors.
1186  */
1187   if(pgx_init_colors(pgx))
1188     return pgx_del_visual(pgx);
1189   return color;
1190 }
1191 
1192 /*.......................................................................
1193  * Allocate colors from and return a visual/colormap context descriptor
1194  * for a specified existing visual and colormap.
1195  *
1196  * Input:
1197  *  pgx      PgxWin *  The PGPLOT window context (We need the display
1198  *                     and screen members).
1199  *  vid    VisualID    The ID of the visual to be used. (Note that
1200  *                     XGetVisualIDFromVisual() can be used to get the
1201  *                     visual ID of a visual from a (Visual *).
1202  *  cmap   Colormap    The colormap to be used. This must be compatible
1203  *                     with the specified visual.
1204  *  min_col     int    The minimum acceptable number of colors before
1205  *                     switching to monochrome.
1206  *  max_col     int    The maximum number of colors to allocate.
1207  *  readonly    int    If true, try to allocate readonly colors. If
1208  *                     false try to allocate read/write colors where
1209  *                     available.
1210  * Output:
1211  *  return  PgxColor * The context descriptor, or NULL on error.
1212  */
1213 #ifdef __STDC__
pgx_adopt_visual(PgxWin * pgx,VisualID vid,Colormap cmap,int min_col,int max_col,int readonly)1214 PgxColor *pgx_adopt_visual(PgxWin *pgx, VisualID vid, Colormap cmap,
1215 			   int min_col, int max_col, int readonly)
1216 #else
1217 PgxColor *pgx_adopt_visual(pgx, vid, cmap, min_col, max_col, readonly)
1218      PgxWin *pgx; VisualID vid; Colormap cmap; int min_col; int max_col;
1219      int readonly;
1220 #endif
1221 {
1222   PgxColor *color;   /* The new descriptor */
1223   if(!pgx_ready(pgx, 0))
1224     return NULL;
1225 /*
1226  * Limit the number of colors to allowed values.
1227  */
1228   if(min_col < 0)
1229     min_col = 0;
1230   else if(min_col > 256)
1231     min_col = 256;
1232   if(max_col < 0)
1233     max_col = 0;
1234   else if(max_col > 256)
1235     max_col = 256;
1236 /*
1237  * Allocate the context descriptor.
1238  */
1239   color = new_PgxColor(pgx, max_col, readonly, vid);
1240   if(!color)
1241     return NULL;
1242 /*
1243  * Record the colormap ID.
1244  */
1245   color->cmap = cmap;
1246 /*
1247  * Attempt to allocate colors from the colormap.
1248  */
1249   if(pgx_get_colorcells(pgx, color, min_col, max_col))
1250     return del_PgxColor(pgx, color);
1251 /*
1252  * Install the color context.
1253  */
1254   pgx->color = color;
1255 /*
1256  * Initialize the colors.
1257  */
1258   if(pgx_init_colors(pgx))
1259     return pgx_del_visual(pgx);
1260   return color;
1261 }
1262 
1263 /*.......................................................................
1264  * Allocate colors from and return a visual/colormap context descriptor
1265  * describing the visual and colormap of a specified other window.
1266  *
1267  * Input:
1268  *  pgx      PgxWin *  The PGPLOT window context (We need the display
1269  *                     and screen members).
1270  *  w        Window    The window to inherit the colormap and visual from.
1271  *  min_col     int    The minimum acceptable number of colors before
1272  *                     switching to monochrome.
1273  *  max_col     int    The maximum number of colors to allocate.
1274  *  readonly    int    If true, try to allocate readonly colors. If
1275  *                     false try to allocate read/write colors where
1276  *                     available.
1277  * Output:
1278  *  return  PgxColor * The context descriptor, or NULL on error.
1279  */
1280 #ifdef __STDC__
pgx_window_visual(PgxWin * pgx,Window w,int min_col,int max_col,int readonly)1281 PgxColor *pgx_window_visual(PgxWin *pgx, Window w, int min_col, int max_col,
1282 			    int readonly)
1283 #else
1284 PgxColor *pgx_window_visual(pgx, w, min_col, max_col, readonly)
1285      PgxWin *pgx; Window w; int min_col; int max_col; int readonly;
1286 #endif
1287 {
1288   XWindowAttributes attr;  /* The attributes of the specified window */
1289   if(!pgx_ready(pgx, 0))
1290     return NULL;
1291 /*
1292  * Acquire the attributes of the specified window.
1293  */
1294   if(!XGetWindowAttributes(pgx->display, w, &attr)) {
1295     fprintf(stderr,
1296       "%s: (pgx_window_visual) Unable to get attributes of window: 0x%lx.\n",
1297 	    PGX_IDENT, (unsigned long) w);
1298     return NULL;
1299   };
1300 /*
1301  * Install the visual and colormap recorded in the returned attributes.
1302  */
1303   return pgx_adopt_visual(pgx, XVisualIDFromVisual(attr.visual), attr.colormap,
1304 			  min_col, max_col, readonly);
1305 }
1306 
1307 /*.......................................................................
1308  * Private function of pgx_new_visual(), used to find a visual of a given
1309  * class and at least min_col colors, allocate colors and return a
1310  * visual/colormap context descriptor for it.
1311  *
1312  * Input:
1313  *  pgx      PgxWin * The PGPLOT window context.
1314  *  class       int   The type of colormap required, chosen from:
1315  *                    PseudoColor,StaticColor,GrayScale,StaticGray.
1316  *  min_col     int   The minimum acceptable number of colors before
1317  *                    switching to monochrome.
1318  *  max_col     int   The maximum number of colors to allocate.
1319  *  readonly    int    If true, try to allocate readonly colors. If
1320  *                     false try to allocate read/write colors where
1321  *                     available.
1322  * Input/Output:
1323  *  return PgxColor * The context of the new visual/colormap, or NULL
1324  *                    if sufficient colors could not be obtained.
1325  */
1326 #ifdef __STDC__
pgx_find_visual(PgxWin * pgx,int class,int min_col,int max_col,int readonly)1327 static PgxColor *pgx_find_visual(PgxWin *pgx, int class, int min_col,
1328 				 int max_col, int readonly)
1329 #else
1330 static PgxColor *pgx_find_visual(pgx, class, min_col, max_col, readonly)
1331      PgxWin *pgx; int class; int min_col; int max_col; int readonly;
1332 #endif
1333 {
1334   PgxColor *color = NULL;      /* The colormap/visual context to be returned */
1335   XVisualInfo vi_template;     /* Visual search template */
1336   XVisualInfo *vi_list = NULL; /* List of matching visuals */
1337   int nmatch;                  /* Number of matching visuals in vi_list[] */
1338   VisualID vid;                /* The id of a chosen private visual */
1339 /*
1340  * If the default colormap has the right class, see if sufficient
1341  * colors can be allocated from it.
1342  */
1343   if(class == pgx_default_class(pgx)) {
1344     color = pgx_default_visual(pgx, min_col, max_col, readonly);
1345     if(color->ncol >= max_col)
1346       return color;
1347     else
1348       color = del_PgxColor(pgx, color);
1349   };
1350 /*
1351  * We need a private colormap.
1352  * Get a list of all visuals of the requested class.
1353  */
1354   vi_template.class = class;
1355   vi_list = XGetVisualInfo(pgx->display, (long)VisualClassMask, &vi_template,
1356 			   &nmatch);
1357   if(!vi_list)
1358     return NULL;
1359 /*
1360  * Search the list for a visual that has a colormap size that
1361  * best matches max_col. Note that the colormap_size memeber of
1362  * the visual info structure effectively provides the number of
1363  * "independant" color table entries. Thus the following algorithm
1364  * works even for colormaps of TrueColor and DirectColor where the
1365  * colormap_size attribute refers to the size of a single primary color
1366  * table.
1367  */
1368   {
1369     XVisualInfo *vi_below = NULL;
1370     XVisualInfo *vi_above = NULL;
1371     XVisualInfo *vi = NULL;
1372     for(vi=vi_list; vi<vi_list+nmatch; vi++) {
1373       if(vi->colormap_size < max_col) {
1374 	if(!vi_below || vi->colormap_size > vi_below->colormap_size)
1375 	  vi_below = vi;
1376       } else {
1377 	if(!vi_above || vi->colormap_size < vi_above->colormap_size)
1378 	  vi_above = vi;
1379       };
1380     };
1381 /*
1382  * If available, use a visual that has at least max_col independant
1383  * colors.
1384  */
1385     if(vi_above)
1386       vi = vi_above;
1387     else if(vi_below)
1388       vi = vi_below;
1389     else
1390       vi = NULL;
1391 /*
1392  * Get the ID of the visual if suitable.
1393  */
1394     vid = (vi && vi->colormap_size > 2) ? vi->visualid : None;
1395     XFree((char *) vi_list);
1396 /*
1397  * Did we fail to get a usable visual?
1398  */
1399     if(vid == None)
1400       return NULL;
1401   };
1402 /*
1403  * Allocate a visual/colormap context descriptor.
1404  */
1405   color = new_PgxColor(pgx, max_col, 0, vid);
1406   if(!color)
1407     return NULL;
1408 /*
1409  * Bracket the colormap acquisition with pgx_start/end_error() calls, to
1410  * determine whether any allocation errors occur.
1411  */
1412   pgx_start_error_watch(pgx);
1413   color->cmap = XCreateColormap(pgx->display, DefaultRootWindow(pgx->display),
1414 				color->vi->visual, AllocNone);
1415   if(pgx_end_error_watch(pgx) || color->cmap == None) {
1416     fprintf(stderr,
1417          "%s: XCreateColormap failed for visual: id=0x%lx class=%d depth=%u.\n",
1418 	 PGX_IDENT, (unsigned long)color->vi->visualid, color->vi->class,
1419 	    color->vi->depth);
1420     color->cmap = None;
1421     return del_PgxColor(pgx, color);
1422   };
1423 /*
1424  * Allocate color-cells in the new colormap.
1425  */
1426   if(pgx_get_colorcells(pgx, color, min_col, max_col))
1427     return del_PgxColor(pgx, color);
1428   color->private = 1;
1429   color->monochrome = 0;
1430 /*
1431  * Install the color context.
1432  */
1433   pgx->color = color;
1434 /*
1435  * Initialize the colors.
1436  */
1437   if(pgx_init_colors(pgx))
1438     return pgx_del_visual(pgx);
1439   return color;
1440 }
1441 
1442 /*.......................................................................
1443  * Private function of pgx_find_visual(), used to allocate color cells for a
1444  * given colormap and return a count of the number allocated.
1445  *
1446  * Input:
1447  *  pgx      PgxWin *  The PGPLOT window context.
1448  *  color  PgxColor *  The visual/colormap context descriptor.
1449  *                     The cmap and vi fields must be initialized before
1450  *                     calling this function.
1451  *  min_col     int    The minimum acceptable number of colors.
1452  *  max_col     int    The maximum number of colors to allocate.
1453  * Output:
1454  *  color->pixel[]     The colorcell indexes.
1455  *  color->ncol        The number of color pixels to use from color->pixel[].
1456  *  color->npixel      The number of private color-cells allocated in pixel[].
1457  *  return      int    0 - OK.
1458  *                     1 - Unable to acquire at least min_col colors.
1459  */
1460 #ifdef __STDC__
pgx_get_colorcells(PgxWin * pgx,PgxColor * color,int min_col,int max_col)1461 static int pgx_get_colorcells(PgxWin *pgx, PgxColor *color,
1462 			      int min_col, int max_col)
1463 #else
1464 static int pgx_get_colorcells(pgx, color, min_col, max_col, readonly)
1465      PgxWin *pgx; PgxColor *color; int min_col; int max_col;
1466 #endif
1467 {
1468   XVisualInfo *vi;      /* Visual information (color->vi) */
1469   Colormap  cmap;       /* Colormap ID (color->cmap) */
1470   unsigned long maxcol; /* The max number of cells to attempt to allocate */
1471   int i;
1472 /*
1473  * Get local pointers to relevant parts of the color context
1474  * descriptor.
1475  */
1476   vi = color->vi;
1477   cmap = color->cmap;
1478 /*
1479  * Record the fact that no colors have been allocated.
1480  */
1481   color->ncol = color->npixel = 0;
1482 /*
1483  * Determine the number of color cells in the colormap.
1484  */
1485   switch(vi->class) {
1486   case PseudoColor:
1487   case GrayScale:
1488   case StaticColor:
1489   case StaticGray:
1490     maxcol = vi->colormap_size;
1491     break;
1492   case TrueColor:
1493   case DirectColor:
1494 /*
1495  * Determine the maximum number of significant colors available
1496  * by looking at the total number of bits set in the pixel bit-masks.
1497  */
1498     maxcol = 1;
1499     {
1500       unsigned long rgb_mask = (vi->red_mask | vi->green_mask | vi->blue_mask);
1501       do {
1502 	if(rgb_mask & (unsigned long)0x1)
1503 	  maxcol <<= (unsigned long)1;
1504       } while(maxcol < max_col && (rgb_mask >>= (unsigned long)1) != 0);
1505     };
1506     break;
1507   default:
1508     maxcol = 0;
1509     break;
1510   };
1511 /*
1512  * Limit the number of colorcells to the size of the color->pixel[] array.
1513  */
1514   if(maxcol > max_col)
1515     maxcol = max_col;
1516 /*
1517  * Don't try to allocate anything if there are too few colors available.
1518  */
1519   if(maxcol < min_col) {
1520     color->ncol = 0;
1521 /*
1522  * Defer shared color allocation to pgx_init_colors().
1523  */
1524   } else if(color->readonly) {
1525     for(i=0; i<maxcol; i++)
1526       color->pixel[i] = 0;
1527     color->ncol = maxcol;
1528     color->npixel = 0;  /* No pixels allocated yet */
1529 /*
1530  * Allocate read/write colorcells.
1531  */
1532   } else {
1533     if(pgx_get_mutable_colorcells(pgx, color, min_col, max_col))
1534       return 1;
1535   };
1536 /*
1537  * Too few colors?
1538  */
1539   if(color->ncol < min_col)
1540     return 1;
1541 /*
1542  * We got more than two colors.
1543  */
1544   color->monochrome = 0;
1545   return 0;
1546 }
1547 
1548 /*.......................................................................
1549  * This is a private function of pgx_get_colorcells() used to allocate
1550  * read/write colorcells.
1551  *
1552  * Input:
1553  *  pgx      PgxWin *  The PGPLOT window context.
1554  *  color  PgxColor *  The visual/colormap context descriptor.
1555  *                     The cmap and vi fields must be initialized before
1556  *                     calling this function.
1557  *  min_col     int    The minimum acceptable number of colors.
1558  *  max_col     int    The maximum number of colors to allocate.
1559  * Output:
1560  *  color->pixel[]     The colorcell indexes.
1561  *  color->ncol        The number of color pixels to use from color->pixel[].
1562  *  color->npixel      The number of private color-cells allocated in pixel[].
1563  *  return      int    0 - OK.
1564  *                     1 - Unable to acquire at least min_col colors.
1565  */
1566 #ifdef __STDC__
pgx_get_mutable_colorcells(PgxWin * pgx,PgxColor * color,int min_col,int max_col)1567 static int pgx_get_mutable_colorcells(PgxWin *pgx, PgxColor *color,
1568 			      int min_col, int max_col)
1569 #else
1570 static int pgx_get_mutable_colorcells(pgx, color, min_col, max_col)
1571      PgxWin *pgx; PgxColor *color; int min_col; int max_col;
1572 #endif
1573 {
1574   XVisualInfo *vi;      /* Visual information (color->vi) */
1575   Colormap  cmap;       /* Colormap ID (color->cmap) */
1576   unsigned long maxcol; /* The max number of cells to attempt to allocate */
1577   int ncol;             /* The number of color-cells allocated */
1578   unsigned long planes[1];  /* Dummy plane array needed by XAllocColorCells() */
1579   unsigned int nplanes = 0; /* Dummy plane count needed by XAllocColorCells() */
1580 /*
1581  * Get local pointers to relevant parts of the color context
1582  * descriptor.
1583  */
1584   vi = color->vi;
1585   cmap = color->cmap;
1586 /*
1587  * Determine the max number of cells to try to allocate.
1588  */
1589   maxcol = vi->colormap_size <= max_col ? vi->colormap_size : max_col;
1590 /*
1591  * See if we can get all of the colors requested.
1592  */
1593   if(XAllocColorCells(pgx->display, cmap, False, planes, nplanes,
1594 		      color->pixel, (unsigned) maxcol)) {
1595     ncol = maxcol;
1596 /*
1597  * If there aren't at least min_col color cells available, then
1598  * give up on this colormap.
1599  */
1600   } else if(!XAllocColorCells(pgx->display, cmap, False, planes, nplanes,
1601 			      color->pixel, (unsigned) min_col)) {
1602     ncol = 0;
1603   } else {
1604 /*
1605  * Since we were able to allocate min_col cells, we may be able to
1606  * allocate more. First discard the min_col cells, so that we can
1607  * try for a bigger number.
1608  */
1609     XFreeColors(pgx->display, cmap, color->pixel, (int) min_col,
1610 		(unsigned long)0);
1611 /*
1612  * Since there is no direct method to determine the number of allocatable
1613  * color cells available in a colormap, perform a binary search for the
1614  * max number that can be allocated. Note that it is possible that another
1615  * client may allocate colors from the same colormap while we search. This
1616  * invalidates the result of the search and is the reason for the outer
1617  * while loop.
1618  */
1619     ncol = 0;
1620     do {
1621       int lo = min_col;
1622       int hi = maxcol;
1623       while(lo<=hi) {
1624 	int mid = (lo+hi)/2;
1625 	if(XAllocColorCells(pgx->display, cmap, False, planes, nplanes,
1626 			    color->pixel, (unsigned) mid)) {
1627 	  ncol = mid;
1628 	  lo = mid + 1;
1629 	  XFreeColors(pgx->display, cmap, color->pixel, mid,
1630 		      (unsigned long)0);
1631 	} else {
1632 	  hi = mid - 1;
1633 	};
1634       };
1635     } while(ncol >= min_col &&
1636 	    !XAllocColorCells(pgx->display, cmap, False, planes, nplanes,
1637 			      color->pixel, (unsigned) ncol));
1638   };
1639 /*
1640  * Did we fail?
1641  */
1642   if(ncol < min_col)
1643     return 1;
1644 /*
1645  * Record the number of pixels that will need to be free'd when
1646  * del_PgxColor() is eventually called.
1647  */
1648   color->npixel = ncol;
1649 /*
1650  * Record the number of colors obtained.
1651  */
1652   color->ncol = ncol;
1653   return 0;
1654 }
1655 
1656 /*.......................................................................
1657  * Return a dynamically allocated visual info structure for a given
1658  * visual. This is simply a more convenient interface to XGetVisualInfo()
1659  * and XVisualIDFromVisual().
1660  *
1661  * Input:
1662  *  display    Display *   The display connection to which the visual
1663  *                         belongs.
1664  *  screen         int     The screen to which the visual belongs.
1665  *  vid       VisualID     The ID of the visual for which information is
1666  *                         required. Note that the ID of a visual can
1667  *                         be obtained from XVisualIDFromVisual().
1668  * Output:
1669  *  return XVisualInfo *   The required information descriptor, or NULL
1670  *                         on error.
1671  */
1672 #ifdef __STDC__
pgx_visual_info(Display * display,int screen,VisualID vid)1673 static XVisualInfo *pgx_visual_info(Display *display, int screen, VisualID vid)
1674 #else
1675 static XVisualInfo *pgx_visual_info(display, screen, vid)
1676      Display *display; int screen; VisualID vid;
1677 #endif
1678 {
1679   XVisualInfo *vi=NULL;  /* The return descriptor */
1680   XVisualInfo template;  /* The search template */
1681   int nret = 0;          /* The number of descriptors returned */
1682 /*
1683  * Using the visual ID and the screen should unambiguously select the
1684  * information for the specified visual.
1685  */
1686   template.visualid = vid;
1687   template.screen = screen;
1688   vi = XGetVisualInfo(display, (long)(VisualIDMask | VisualScreenMask),
1689 		      &template, &nret);
1690   if(vi == NULL || nret < 1) {
1691     fprintf(stderr,
1692       "%s: Error getting visual information for visual ID 0x%lx, screen %d.\n",
1693       PGX_IDENT, (unsigned long)template.visualid, screen);
1694     vi = NULL;
1695   };
1696   return vi;
1697 }
1698 
1699 /*.......................................................................
1700  * Delete the contents of a pgx->color structure.
1701  *
1702  * Input:
1703  *  pgx      PgxWin * The PGPLOT window context.
1704  * Output:
1705  *  return PgxColor * The deleted color context (always NULL).
1706  */
1707 #ifdef __STDC__
pgx_del_visual(PgxWin * pgx)1708 PgxColor *pgx_del_visual(PgxWin *pgx)
1709 #else
1710 PgxColor *pgx_del_visual(pgx)
1711      PgxWin *pgx;
1712 #endif
1713 {
1714   if(pgx)
1715     pgx->color = del_PgxColor(pgx, pgx->color);
1716   return NULL;
1717 }
1718 
1719 /*.......................................................................
1720  * Check a resource string value against visual class names.
1721  *
1722  * Input:
1723  *  str       char *  The string value to be tested (NULL is ok).
1724  * Output:
1725  *  return     int    The Visual class parsed, or -1 to select the default.
1726  */
1727 #ifdef __STDC__
pgx_parse_visual(char * str)1728 static int pgx_parse_visual(char *str)
1729 #else
1730 static int pgx_parse_visual(str)
1731      char *str;
1732 #endif
1733 {
1734 /*
1735  * Create a lookup table of recognised visual classes.
1736  */
1737   static struct {
1738     char *name;  /* Name of visual class */
1739     int class;   /* Enumerated identifier of visual class */
1740   } classes[] = {
1741     {"monochrome",  -2},
1742     {"default",     -1},
1743     {"pseudocolor", PseudoColor},
1744     {"directcolor", TrueColor},    /* We can't handle DirectColor */
1745     {"staticcolor", StaticColor},
1746     {"truecolor",   TrueColor},
1747     {"grayscale",   GrayScale},
1748     {"staticgray",  StaticGray}
1749   };
1750   int i;
1751 /*
1752  * Lookup the given class name.
1753  */
1754   if(str) {
1755     for(i=0; i<sizeof(classes)/sizeof(classes[0]); i++) {
1756       if(pgx_same_string(str, classes[i].name))
1757 	return classes[i].class;
1758     };
1759 /*
1760  * Class name not recognised.
1761  */
1762     fprintf(stderr, "%s: Unrecognised visual type: \"%s\".\n",
1763 	    PGX_IDENT, str);
1764   };
1765   return -1;
1766 }
1767 
1768 /*.......................................................................
1769  * Perform a case-insensitive string comparison. Leading and trailing
1770  * white-space are not significant.
1771  *
1772  * Input:
1773  *  s1,s2  char *   The strings to be compared.
1774  * Output:
1775  *  return  int     0 - The strings are not the same.
1776  *                  1 - The strings are the same.
1777  */
1778 #ifdef __STDC__
pgx_same_string(char * s1,char * s2)1779 int pgx_same_string(char *s1, char *s2)
1780 #else
1781 int pgx_same_string(s1, s2)
1782      char *s1; char *s2;
1783 #endif
1784 {
1785 /*
1786  * If either of the strings are NULL pointers, report that they are equal
1787  * only if both are NULL.
1788  */
1789   if(s1==NULL || s2==NULL)
1790     return s1==NULL && s2==NULL;
1791 /*
1792  * Skip leading white-space.
1793  */
1794   while(*s1 && isspace(*s1))
1795     s1++;
1796   while(*s2 && isspace(*s2))
1797     s2++;
1798 /*
1799  * Find the section of the strings that are identical.
1800  */
1801   while(*s1 && *s2 &&
1802    (islower(*s1) ? toupper(*s1) : *s1) == (islower(*s2) ? toupper(*s2) : *s2)) {
1803     s1++;
1804     s2++;
1805   };
1806 /*
1807  * Skip trailing white-space.
1808  */
1809   while(*s1 && isspace(*s1))
1810     s1++;
1811   while(*s2 && isspace(*s2))
1812     s2++;
1813 /*
1814  * Are the strings equal?
1815  */
1816   return (*s1=='\0' && *s2=='\0');
1817 }
1818 
1819 /*.......................................................................
1820  * After a fatal error has occured, this function should be called to
1821  * mark the specified device as unusable. It emits an error message
1822  * and sets pgx->bad_device=1.
1823  *
1824  * Input:
1825  *  pgx    PgxWin *  The descriptor of the device on which the error
1826  *                   occurred.
1827  * Output:
1828  *  pgx->bad_device  This flag is set to 1.
1829  *  return    int    Allways 1 (intended as a boolean to say that the
1830  *                   device is unusable). This can be used as the return
1831  *                   value for functions that use 1 to denote an error
1832  *                   return. eg.
1833  *                     if(error_occurred)
1834  *                       return pgx_bad_device(pgx);
1835  */
1836 #ifdef __STDC__
pgx_bad_device(PgxWin * pgx)1837 int pgx_bad_device(PgxWin *pgx)
1838 #else
1839 int pgx_bad_device(pgx)
1840      PgxWin *pgx;
1841 #endif
1842 {
1843 /*
1844  * Only report an error if this is the first time that this function
1845  * has been called on this device.
1846  */
1847   if(pgx && !pgx->bad_device) {
1848     fprintf(stderr, "%s: Lost PGPLOT window.\n", PGX_IDENT);
1849     pgx->bad_device = 1;
1850   };
1851   return 1;
1852 }
1853 
1854 /*.......................................................................
1855  * Draw a line segment in an open PGPLOT window.
1856  *
1857  * Input:
1858  *  pgx     PgxWin *  The PGPLOT window context.
1859  *  rbuf     float *  The array of float arguments sent by the PGPLOT
1860  *                    GREXEC() subroutine.
1861  */
1862 #ifdef __STDC__
pgx_draw_line(PgxWin * pgx,float * rbuf)1863 void pgx_draw_line(PgxWin *pgx, float *rbuf)
1864 #else
1865 void pgx_draw_line(pgx, rbuf)
1866      PgxWin *pgx; float *rbuf;
1867 #endif
1868 {
1869   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
1870     PgxState *state = pgx->state;
1871 /*
1872  * Convert from PGPLOT coordinates to X coordinates.
1873  */
1874     XPoint start;
1875     XPoint end;
1876     pgx_xy_to_XPoint(pgx, &rbuf[0], &start);
1877     pgx_xy_to_XPoint(pgx, &rbuf[2], &end);
1878 /*
1879  * Draw the line segment.
1880  */
1881     XDrawLine(pgx->display, pgx->pixmap, state->gc,
1882 	      start.x, start.y, end.x, end.y);
1883 /*
1884  * Record the extent of the modified region of the pixmap.
1885  */
1886     pgx_mark_modified(pgx, start.x, start.y, state->gcv.line_width);
1887     pgx_mark_modified(pgx, end.x, end.y, state->gcv.line_width);
1888   };
1889   return;
1890 }
1891 
1892 /*.......................................................................
1893  * Draw a single dot in an open PGPLOT window.
1894  *
1895  * Input:
1896  *  pgx     PgxWin *  The PGPLOT window context.
1897  *  rbuf     float *  The array of float arguments sent by the PGPLOT
1898  *                    GREXEC() subroutine.
1899  */
1900 #ifdef __STDC__
pgx_draw_dot(PgxWin * pgx,float * rbuf)1901 void pgx_draw_dot(PgxWin *pgx, float *rbuf)
1902 #else
1903 void pgx_draw_dot(pgx, rbuf)
1904      PgxWin *pgx; float *rbuf;
1905 #endif
1906 {
1907   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
1908     PgxState *state = pgx->state;
1909     XPoint xp;
1910 /*
1911  * Workd out the radius of the dot.
1912  */
1913     int radius = state->gcv.line_width/2;
1914 /*
1915  * Convert from PGPLOT coordinates to window coordinates.
1916  */
1917     pgx_xy_to_XPoint(pgx, rbuf, &xp);
1918 /*
1919  * Draw a pixel-sized point, or a larger circular dot?
1920  */
1921     if(radius < 1) {
1922       XDrawPoint(pgx->display, pgx->pixmap, state->gc, xp.x, xp.y);
1923     } else {
1924       unsigned int diameter = radius*2;
1925       int x = xp.x - radius;
1926       int y = xp.y - radius;
1927       XFillArc(pgx->display, pgx->pixmap, state->gc, x, y,
1928 	       diameter, diameter, 0, 23040);
1929     };
1930 /*
1931  * Record the extent of the modified region of the pixmap.
1932  */
1933     pgx_mark_modified(pgx, xp.x, xp.y, state->gcv.line_width);
1934   };
1935   return;
1936 }
1937 
1938 /*.......................................................................
1939  * Convert from the coordinates sent by PGPLOT in rbuf[...] to an
1940  * X-windows point in the coordinate system of the pixmap.
1941  *
1942  * Input:
1943  *  pgx    PgxWin *   The PGPLOT window context.
1944  *  xy      float [2] Array of two floats containing PGPLOT coordinates
1945  *                    arranged as x followed by y.
1946  * Output:
1947  *  xp     XPoint *   The converted coordinates will be assigned to xp->x
1948  *                    and xp->y.
1949  */
1950 #ifdef __STDC__
pgx_xy_to_XPoint(PgxWin * pgx,float * xy,XPoint * xp)1951 static void pgx_xy_to_XPoint(PgxWin *pgx, float *xy, XPoint *xp)
1952 #else
1953 static void pgx_xy_to_XPoint(pgx, xy, xp)
1954      PgxWin *pgx; float *xy; XPoint *xp;
1955 #endif
1956 {
1957   PgxState *state = pgx->state;
1958   float x = xy[0];
1959   float y = xy[1];
1960 /*
1961  * Limit the coordinates to lie within the pixmap.
1962  */
1963   if(x < 0)
1964     x = 0;
1965   if(x >= state->geom.width)
1966     x = state->geom.width;
1967   if(y < 0)
1968     y = 0;
1969   if(y >= state->geom.height)
1970     y = state->geom.height;
1971 /*
1972  * Convert to pixmap coordinates.
1973  */
1974   xp->x = state->geom.xmin + (int)(x + 0.5);
1975   xp->y = state->geom.ymax - (int)(y + 0.5);
1976 }
1977 
1978 /*.......................................................................
1979  * Convert from pixmap pixel coordinates to PGPLOT coordinates, in a
1980  * form that can be returned to PGPLOT via rbuf[...].
1981  *
1982  * Input:
1983  *  pgx    PgxWin *  The PGPLOT window context.
1984  *  xp     XPoint *  The pixmap pixel-coordinates to be converted.
1985  * Output:
1986  *  xy      float [2] Output array of two floats in which to place the
1987  *                    PGPLOT coordinates, arranged as x followed by y.
1988  */
1989 #ifdef __STDC__
pgx_XPoint_to_xy(PgxWin * pgx,XPoint * xp,float * xy)1990 static void pgx_XPoint_to_xy(PgxWin *pgx, XPoint *xp, float *xy)
1991 #else
1992 static void pgx_XPoint_to_xy(pgx, xp, xy)
1993      PgxWin *pgx; XPoint *xp; float *xy;
1994 #endif
1995 {
1996   PgxState *state = pgx->state;
1997   xy[0] = (float) (xp->x - state->geom.xmin);
1998   xy[1] = (float) (state->geom.ymax - xp->y);
1999 }
2000 
2001 /*.......................................................................
2002  * Update the vertices of the rectangular area that has been modified
2003  * since the last time the window was updated from the pixmap.
2004  *
2005  * Input:
2006  *  pgx    PgxWin * The PGPLOT window context.
2007  *  x         int   The x-axis pixel index that the rectangular update area
2008  *                  must be extended to include.
2009  *  y         int   The y-axis pixel index that the rectangular update area
2010  *                  must be extended to include.
2011  *  diameter  int   The diameter of the locus in pixels. For line or
2012  *                  point drawing operations this is usually the line width.
2013  */
2014 #ifdef __STDC__
pgx_mark_modified(PgxWin * pgx,int x,int y,int diameter)2015 static void pgx_mark_modified(PgxWin *pgx, int x, int y, int diameter)
2016 #else
2017 static void pgx_mark_modified(pgx, x, y, diameter)
2018      PgxWin *pgx; int x; int y; int diameter;
2019 #endif
2020 {
2021   PgxState *state = pgx->state;
2022   int radius = diameter/2;
2023 /*
2024  * Expand the current rectangle to include point (x,y).
2025  */
2026   if(state->update.modified) {
2027     if(x - radius < state->update.xmin)
2028       state->update.xmin = x - radius;
2029     if(x + radius > state->update.xmax)
2030       state->update.xmax = x + radius;
2031     if(y - radius < state->update.ymin)
2032       state->update.ymin = y - radius;
2033     if(y + radius > state->update.ymax)
2034       state->update.ymax = y + radius;
2035   } else {
2036     state->update.xmin = x - radius;
2037     state->update.xmax = x + radius;
2038     state->update.ymin = y - radius;
2039     state->update.ymax = y + radius;
2040     state->update.modified = 1;
2041   };
2042   return;
2043 }
2044 
2045 /*.......................................................................
2046  * Flush changes in the pixmap to the window.
2047  *
2048  * Input:
2049  *  pgx   PgxWin * The PGPLOT window context.
2050  * Output:
2051  *  return   int    0 - OK.
2052  *                  1 - Error.
2053  */
2054 #ifdef __STDC__
pgx_flush(PgxWin * pgx)2055 int pgx_flush(PgxWin *pgx)
2056 #else
2057 int pgx_flush(pgx)
2058      PgxWin *pgx;
2059 #endif
2060 {
2061   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
2062     PgxState *state = pgx->state;
2063 /*
2064  * Flush buffered opcodes if necessary.
2065  */
2066     if(state->flush_opcode_fn != (Flush_Opcode_fn) 0) {
2067       (*state->flush_opcode_fn)(pgx);
2068       state->flush_opcode_fn = (Flush_Opcode_fn) 0;
2069       if(pgx->bad_device)
2070 	return 1;
2071     };
2072 /*
2073  * Copy the modified rectangular area of the pixmap to the PGPLOT window.
2074  */
2075     if(state->update.modified) {
2076 /*
2077  * Enforce bounds on the area to be updated.
2078  */
2079       if(state->update.xmin < 0)
2080 	state->update.xmin = 0;
2081       if(state->update.ymin < 0)
2082 	state->update.ymin = 0;
2083       if(state->update.xmax > state->geom.width - 1)
2084 	state->update.xmax = state->geom.width - 1;
2085       if(state->update.ymax > state->geom.height - 1)
2086 	state->update.ymax = state->geom.height - 1;
2087 /*
2088  * Copy the area to be updated from the pixmap to the window.
2089  */
2090       if(!pgx->bad_device) {
2091 	pgx_copy_area(pgx, state->update.xmin, state->update.ymin,
2092 		      (unsigned) (state->update.xmax - state->update.xmin + 1),
2093 		      (unsigned) (state->update.ymax - state->update.ymin + 1),
2094 		      (int) (state->update.xmin - pgx->scroll.x),
2095 		      (int) (state->update.ymin - pgx->scroll.y));
2096 	if(pgx->bad_device)
2097 	  return 1;
2098       };
2099       state->update.modified = 0;
2100     };
2101 /*
2102  * Redraw the potentially damaged rubber-band cursor if it is active.
2103  */
2104     pgx_refresh_cursor(pgx);
2105 /*
2106  * Make sure that the window is up to date.
2107  */
2108     XFlush(pgx->display);
2109     if(pgx->bad_device)
2110       return 1;
2111   };
2112   return 0;
2113 }
2114 
2115 /*.......................................................................
2116  * Set the foreground color.
2117  *
2118  * Input:
2119  *  pgx  PgxWin *  The PGPLOT window context.
2120  *  ci      int    The PGPLOT color index to instate as the foreground
2121  *                 color.
2122  * Output:
2123  *  return  int    0 - OK.
2124  *                 1 - Error.
2125  */
2126 #ifdef __STDC__
pgx_set_ci(PgxWin * pgx,int ci)2127 int pgx_set_ci(PgxWin *pgx, int ci)
2128 #else
2129 int pgx_set_ci(pgx, ci)
2130      PgxWin *pgx; int ci;
2131 #endif
2132 {
2133   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
2134     PgxState *state = pgx->state;
2135 /*
2136  * Assign white to out-of range color indexes.
2137  */
2138     if(ci < 0 || ci >= pgx->color->ncol)
2139       ci = 1;
2140 /*
2141  * Determine the color pixel associated with the given color index.
2142  */
2143     state->gcv.foreground = pgx->color->pixel[ci];
2144 /*
2145  * Instate the new foreground color.
2146  */
2147     XSetForeground(pgx->display, state->gc, state->gcv.foreground);
2148     if(pgx->bad_device)
2149       return 1;
2150   };
2151   return 0;
2152 }
2153 
2154 /*.......................................................................
2155  * This function is called mulitple times to accumulate a list of
2156  * polygon vertices and finally draw them. This protocol is mandated
2157  * by the PGPLOT GREXEC driver dispatch function.
2158  *
2159  * Input:
2160  *  pgx     PgxWin *  The PGPLOT window context.
2161  *  rbuf     float *  The array of float arguments sent by the PGPLOT
2162  *                    GREXEC() subroutine.
2163  */
2164 #ifdef __STDC__
pgx_poly_fill(PgxWin * pgx,float * rbuf)2165 void pgx_poly_fill(PgxWin *pgx, float *rbuf)
2166 #else
2167 void pgx_poly_fill(pgx, rbuf)
2168      PgxWin *pgx; float *rbuf;
2169 #endif
2170 {
2171   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
2172     PgxState *state = pgx->state;
2173 /*
2174  * The first call specifies just the number of vertixes in the polygon.
2175  */
2176     if(state->poly.npoint == 0) {
2177       state->poly.npoint = (int) (rbuf[0] + 0.5);
2178       state->poly.points = (XPoint *) malloc(sizeof(XPoint) * state->poly.npoint);
2179       if(state->poly.points == NULL)
2180 	fprintf(stderr, "%s: Insufficient memory for polygon points.\n",
2181 		PGX_IDENT);
2182       state->poly.ndone = 0;
2183 /*
2184  * The next state->poly.npoint calls specify the vertexes of the polygon.
2185  */
2186     } else {
2187 /*
2188  * Ignore the points if the above malloc() failed.
2189  */
2190       if(state->poly.points) {
2191 	XPoint *xp = &state->poly.points[state->poly.ndone];
2192 	pgx_xy_to_XPoint(pgx, rbuf, xp);
2193 	pgx_mark_modified(pgx, xp->x, xp->y, 1);
2194       };
2195 /*
2196  * Maintain the count of the number of points, even if no memory for the
2197  * points is available. Thus we can just ignore all calls until
2198  * state->poly.ndone == state->poly.npoint.
2199  */
2200       state->poly.ndone++;
2201 /*
2202  * On the last call display the filled polygon and release the memory used
2203  * to store its vertexes.
2204  */
2205       if(state->poly.ndone >= state->poly.npoint) {
2206 	if(state->poly.points) {
2207 	  XFillPolygon(pgx->display, pgx->pixmap, state->gc, state->poly.points,
2208 		       state->poly.npoint, Complex, CoordModeOrigin);
2209 	  free((char *)state->poly.points);
2210 	  state->poly.points = NULL;
2211 	};
2212 	state->poly.npoint = 0;
2213       };
2214     };
2215   };
2216   return;
2217 }
2218 
2219 /*.......................................................................
2220  * Draw a filled rectangle.
2221  *
2222  * Input:
2223  *  pgx     PgxWin *  The PGPLOT window context.
2224  *  rbuf     float *  The array of float arguments sent by the PGPLOT
2225  *                    GREXEC() subroutine.
2226  */
2227 #ifdef __STDC__
pgx_rect_fill(PgxWin * pgx,float * rbuf)2228 void pgx_rect_fill(PgxWin *pgx, float *rbuf)
2229 #else
2230 void pgx_rect_fill(pgx, rbuf)
2231      PgxWin *pgx; float *rbuf;
2232 #endif
2233 {
2234   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
2235 /*
2236  * Convert from PGPLOT coordinates to X coordinates.
2237  */
2238     XPoint blc;
2239     XPoint trc;
2240     pgx_xy_to_XPoint(pgx, &rbuf[0], &blc);
2241     pgx_xy_to_XPoint(pgx, &rbuf[2], &trc);
2242 /*
2243  * Fill the rectangle in the pixmap.
2244  */
2245     XFillRectangle(pgx->display, pgx->pixmap, pgx->state->gc, blc.x, trc.y,
2246 		   (unsigned)(trc.x-blc.x+1), (unsigned)(blc.y-trc.y+1));
2247 /*
2248  * Record the extent of the modified part of the pixmap.
2249  */
2250     pgx_mark_modified(pgx, blc.x, blc.y, 1);
2251     pgx_mark_modified(pgx, trc.x, trc.y, 1);
2252   };
2253   return;
2254 }
2255 
2256 /*.......................................................................
2257  * Set the line width for subsequent drawing.
2258  *
2259  * Input:
2260  *  pgx     PgxWin *  The PGPLOT window context.
2261  *  lw       float    The new line width in units of 0.005 inches.
2262  */
2263 #ifdef __STDC__
pgx_set_lw(PgxWin * pgx,float lw)2264 void pgx_set_lw(PgxWin *pgx, float lw)
2265 #else
2266 void pgx_set_lw(pgx, lw)
2267      PgxWin *pgx; float lw;
2268 #endif
2269 {
2270   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
2271     PgxState *state = pgx->state;
2272 /*
2273  * The line width is provided in multiples of 0.005 inches.
2274  */
2275     state->gcv.line_width = (lw * 0.005 * state->geom.xpix_per_inch) + 0.5;
2276     XChangeGC(pgx->display, state->gc, (unsigned long)GCLineWidth, &state->gcv);
2277   };
2278   return;
2279 }
2280 
2281 /*.......................................................................
2282  * Render a line of pixels.
2283  *
2284  * Input:
2285  *  pgx     PgxWin *  The PGPLOT window context.
2286  *  rbuf     float *  The array of float arguments sent by the PGPLOT
2287  *                    GREXEC() subroutine.
2288  *  nbuf       int *  The number of floats passed in rbuf[] by GREXEC.
2289  * Output:
2290  *  return     int    0 - OK.
2291  *                    1 - Error.
2292  */
2293 #ifdef __STDC__
pgx_pix_line(PgxWin * pgx,float * rbuf,int * nbuf)2294 int pgx_pix_line(PgxWin *pgx, float *rbuf, int *nbuf)
2295 #else
2296 int pgx_pix_line(pgx, rbuf, nbuf)
2297      PgxWin *pgx; float *rbuf; int *nbuf;
2298 #endif
2299 {
2300   int i;
2301   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP) &&
2302      !pgx->color->monochrome) {
2303     PgxState *state = pgx->state;  /* The PGPLOT drawing-state context */
2304     int ndone;                     /* The number of pixels drawn so far */
2305 /*
2306  * Extract the array of pixels and the recorded number thereof.
2307  */
2308     float *cells = rbuf + 2;       /* Array of pixels */
2309     int ncell = *nbuf - 2;         /* The number of pixels in cells[] */
2310 /*
2311  * Get the container of the X image used to transport lines of pixels.
2312  */
2313     XWimage *image = &pgx->state->image;
2314 /*
2315  * The first two elements of the rbuf[] array contain the
2316  * X and Y PGPLOT coordinates of the start of the line of pixels.
2317  * Convert this to X coordinates.
2318  */
2319     XPoint start;
2320     pgx_xy_to_XPoint(pgx, rbuf, &start);
2321 /*
2322  * Draw up to PGX_IMAGE_LEN pixels at a time. This is the size of the
2323  * buffer: state->xi->data[].
2324  */
2325     for(ndone=0; !pgx->bad_device && ndone<ncell; ndone += PGX_IMAGE_LEN) {
2326       int ntodo = ncell - ndone;
2327       int nimage = ntodo < PGX_IMAGE_LEN ? ntodo : PGX_IMAGE_LEN;
2328 /*
2329  * Load the image buffer with the color cell indexes assigned to the
2330  * given PGPLOT color indexes.
2331  */
2332       if(pgx->color->vi->depth == 8) {
2333 	for(i=0; i<nimage; i++)
2334 	  image->xi->data[i] = pgx->color->pixel[(int) (cells[ndone+i] + 0.5)];
2335       } else {
2336 	for(i=0; i<nimage; i++) {
2337 	  XPutPixel(image->xi, i, 0,
2338 		    pgx->color->pixel[(int) (cells[ndone+i] + 0.5)]);
2339 	};
2340       };
2341 /*
2342  * Display the image.
2343  */
2344       XPutImage(pgx->display, pgx->pixmap, state->gc, image->xi, 0, 0,
2345 		start.x+ndone, start.y, (unsigned) nimage, (unsigned) 1);
2346     };
2347 /*
2348  * Extend the region to be updated on the next flush.
2349  */
2350     pgx_mark_modified(pgx, start.x, start.y, 1);
2351     pgx_mark_modified(pgx, start.x + ncell - 1, start.y, 1);
2352   };
2353   if(pgx->bad_device)
2354     return 1;
2355   return 0;
2356 }
2357 
2358 /*.......................................................................
2359  * Record the latest world-coordinate conversion parameters as provided
2360  * by the PGPLOT driver opcode 27. Note that opcode 27 only gets invoked
2361  * by PGPLOT if the second character of the attribute string returned by
2362  * opcode 4 is set to 'X'.
2363  *
2364  * Input:
2365  *  pgx     PgxWin *  The PGPLOT window context.
2366  *  rbuf     float *  The array of float arguments sent by the PGPLOT
2367  *                    GREXEC() subroutine for opcode 27. In order these
2368  *                    are: xoff, xdiv, yoff and ydiv; where:
2369  *                      world_x = (device_x - xoff) / xdiv
2370  *                      world_y = (device_y - yoff) / ydiv
2371  */
2372 #ifdef __STDC__
pgx_set_world(PgxWin * pgx,float * rbuf)2373 void pgx_set_world(PgxWin *pgx, float *rbuf)
2374 #else
2375 void pgx_set_world(pgx, rbuf)
2376      PgxWin *pgx; float *rbuf;
2377 #endif
2378 {
2379   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
2380     XWworld *world = &pgx->state->world;
2381     world->xoff = rbuf[0];
2382     world->xdiv = rbuf[1];
2383     world->yoff = rbuf[2];
2384     world->ydiv = rbuf[3];
2385   };
2386 }
2387 
2388 /*.......................................................................
2389  * Assign a given RGB color representation to a given color index in
2390  * pgx->color->xcolor and if the device is open to PGPLOT, record the
2391  * extent of the modifications in pgx->state->update and
2392  * register pgx_update_colors() to pgx->state->flush_opcode_fn().
2393  *
2394  * Input:
2395  *  pgx  PgxWin *  The PGPLOT window context.
2396  *  ci      int    The color index to assign the color to. Out of range
2397  *                 indexes are quietly ignored.
2398  *  red   float    The fractional red brightness 0..1.
2399  *  green float    The fractional green brightness 0..1.
2400  *  blue  float    The fractional blue brightness 0..1.
2401  * Output:
2402  *  return  int    0 - OK.
2403  *                 1 - Error.
2404  */
2405 #ifdef __STDC__
pgx_set_rgb(PgxWin * pgx,int ci,float red,float green,float blue)2406 int pgx_set_rgb(PgxWin *pgx, int ci, float red, float green, float blue)
2407 #else
2408 int pgx_set_rgb(pgx, ci, red, green, blue)
2409      PgxWin *pgx; int ci; float red; float green; float blue;
2410 #endif
2411 {
2412   float gray;   /* Gray-scale intensity */
2413   XColor *xc;   /* The descriptor of the new color */
2414 /*
2415  * Do we have a valid device.
2416  */
2417   if(pgx_ready(pgx, PGX_NEED_COLOR)) {
2418     PgxState *state = pgx->state;
2419 /*
2420  * Limit RGB values to be between 0 and 1.
2421  */
2422     if(red   < 0.0) red   = 0.0;
2423     if(green < 0.0) green = 0.0;
2424     if(blue  < 0.0) blue  = 0.0;
2425     if(red   > 1.0) red   = 1.0;
2426     if(green > 1.0) green = 1.0;
2427     if(blue  > 1.0) blue  = 1.0;
2428 /*
2429  * Color index in range?
2430  */
2431     if(!pgx->color->monochrome && ci >= 0 && ci < pgx->color->ncol) {
2432 /*
2433  * Get the color representation descriptor.
2434  */
2435       xc = &pgx->color->xcolor[ci];
2436 /*
2437  * Get the pixel to be assigned the new color representation.
2438  */
2439       xc->pixel = pgx->color->pixel[ci];
2440       xc->flags = DoRed | DoGreen | DoBlue;
2441       xc->pad   = 0;
2442 /*
2443  * Determine the appropriate RGB values for the type of colormap.
2444  */
2445       switch(pgx->color->vi->class) {
2446       case PseudoColor:
2447       case StaticColor:
2448       case DirectColor:
2449       case TrueColor:
2450 	xc->red   = (int) (red * PGX_COLORMULT + 0.5);
2451 	xc->green = (int) (green * PGX_COLORMULT + 0.5);
2452 	xc->blue  = (int) (blue * PGX_COLORMULT + 0.5);
2453 	break;
2454       case GrayScale:
2455       case StaticGray:
2456 /*
2457  * For gray-scale colormaps the red,green and blue intensities must all be
2458  * equal. Weight the colors so that what is brightest to the eye, is also
2459  * brighter in grayscale, and so that different colors of equal intensity
2460  * appear different in grayscale. Note that the 3 weights must add up to 1.0.
2461  * The black and white TV standard says to use 0.3*R+0.59*G+0.11*B.
2462  * Unfortunately blue pretty much dissapears in this scheme. The following
2463  * is a compromise between making all colors visible and making different
2464  * colors look different in grayscale.
2465  */
2466 	gray = 0.35*red + 0.40*green + 0.25*blue;
2467 	xc->red = xc->green = xc->blue = (int) (gray * PGX_COLORMULT + 0.5);
2468 	break;
2469       };
2470 /*
2471  * Update the recorded range of color indexes whose color representations
2472  * have been changed since the last call to pgx_update_colors().
2473  */
2474       if(state) {
2475 	if(state->color.nbuff<=0) {
2476 	  state->color.sbuff = ci;
2477 	  state->color.nbuff = 1;
2478 	} else if(ci < state->color.sbuff) {
2479 	  state->color.nbuff += state->color.sbuff - ci;
2480 	  state->color.sbuff = ci;
2481 	} else if(ci > state->color.sbuff + state->color.nbuff-1) {
2482 	  state->color.nbuff = ci - state->color.sbuff + 1;
2483 	};
2484 /*
2485  * Register pgx_update_colors() to be called to flush the colors to the
2486  * window. Don't do this if we are sharing readonly colors, because
2487  * these should not be reallocated until the start of the next page.
2488  */
2489 	if(!pgx->color->readonly)
2490 	  state->flush_opcode_fn = (Flush_Opcode_fn) pgx_update_colors;
2491       };
2492     };
2493   };
2494   return 0;
2495 }
2496 
2497 /*.......................................................................
2498  * Initialize the color representations in the color table.
2499  * pgx_get_visual() must have been called prior to calling this function,
2500  * so that we have a visual and colormap to define the colors in.
2501  *
2502  * Input:
2503  *  pgx    PgxWin *  The PGPLOT window context.
2504  * Output:
2505  *  pgx->color->xcolor[0..ncol] The color pixel definitions.
2506  *  return    int    0 - OK.
2507  *                   1 - Error.
2508  */
2509 #ifdef __STDC__
pgx_init_colors(PgxWin * pgx)2510 static int pgx_init_colors(PgxWin *pgx)
2511 #else
2512 static int pgx_init_colors(pgx)
2513      PgxWin *pgx;
2514 #endif
2515 {
2516 /*
2517  * Define the standard PGPLOT line colors (RGB).
2518  */
2519   static float ctable[PGX_NCOLORS][3] = {
2520     {0.0,0.0,0.0}, {1.0,1.0,1.0}, {1.0,0.0,0.0}, {0.0,1.0,0.0},
2521     {0.0,0.0,1.0}, {0.0,1.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,0.0},
2522     {1.0,0.5,0.0}, {0.5,1.0,0.0}, {0.0,1.0,0.5}, {0.0,0.5,1.0},
2523     {0.5,0.0,1.0}, {1.0,0.0,0.5}, {0.333,0.333,0.333},
2524     {0.667,0.667,0.667}
2525   };
2526   int i;
2527   if(pgx_ready(pgx, PGX_NEED_COLOR)) {
2528 /*
2529  * Initialize the color-table with the standard PGPLOT line colors.
2530  */
2531     if(!pgx->color->monochrome) {
2532       int ncol = (PGX_NCOLORS < pgx->color->ncol) ? PGX_NCOLORS:pgx->color->ncol;
2533       for(i=0; i<ncol; i++) {
2534 	if(pgx_set_rgb(pgx, i, ctable[i][0], ctable[i][1], ctable[i][2]))
2535 	  return 1;
2536       };
2537 /*
2538  * Initialize the rest of the colors with a grey-scale ramp.
2539  */
2540       for(i=ncol; i<pgx->color->ncol; i++) {
2541 	float grey= (float) (i-PGX_NCOLORS) /
2542 	            (float) (pgx->color->ncol-1-PGX_NCOLORS);
2543 	if(pgx_set_rgb(pgx, i, grey, grey, grey))
2544 	  return 1;
2545       };
2546     };
2547 /*
2548  * Flush the new color definitions to the display.
2549  */
2550     if(pgx_flush_colors(pgx, 0, pgx->color->ncol))
2551       return 1;
2552 /*
2553  * Start with the foreground color set to white.
2554  */
2555     if(pgx_set_ci(pgx, 1))
2556       return 1;
2557 /*
2558  * Record the color-cells as allocated.
2559  */
2560     pgx->color->initialized = 1;
2561   };
2562   return 0;
2563 }
2564 
2565 /*.......................................................................
2566  * Return the color representation of a given color index.
2567  *
2568  * Input:
2569  *  pgx    PgxWin *  The PGPLOT window context.
2570  * Input/Output:
2571  *  rbuf    float *  The return array passed from GREXEC.
2572  *  nbuf      int *  The output number of float results for GREXEC.
2573  */
2574 #ifdef __STDC__
pgx_get_rgb(PgxWin * pgx,float * rbuf,int * nbuf)2575 void pgx_get_rgb(PgxWin *pgx, float *rbuf, int *nbuf)
2576 #else
2577 void pgx_get_rgb(pgx, rbuf, nbuf)
2578      PgxWin *pgx; float *rbuf; int *nbuf;
2579 #endif
2580 {
2581   if(pgx_ready(pgx, PGX_NEED_COLOR)) {
2582     int ci = (int) (rbuf[0] + 0.5);
2583     rbuf[1] = (float) pgx->color->xcolor[ci].red / (float) PGX_COLORMULT;
2584     rbuf[2] = (float) pgx->color->xcolor[ci].green / (float) PGX_COLORMULT;
2585     rbuf[3] = (float) pgx->color->xcolor[ci].blue / (float) PGX_COLORMULT;
2586   } else {
2587     rbuf[1] = rbuf[2] = rbuf[3] = 0;
2588   };
2589   *nbuf = 4;
2590   return;
2591 }
2592 
2593 /*.......................................................................
2594  * Flush color-representation changes made by xw_set_rgb() to the window.
2595  * This updates the window colormap. If color index 0 is changed
2596  * then the background color is also updated.
2597  *
2598  * Input:
2599  *  pgx    PgxWin *  The PGPLOT window context.
2600  *  ci_start  int    The first color index to update.
2601  *  ncol      int    The number of colors to update.
2602  *                   Color indexes ci_state -> ci_start + ncol - 1 will
2603  *                   be updated.
2604  * Output:
2605  *  return    int      0 - OK.
2606  *                     1 - Error.
2607  */
2608 #ifdef __STDC__
pgx_flush_colors(PgxWin * pgx,int ci_start,int ncol)2609 static int pgx_flush_colors(PgxWin *pgx, int ci_start, int ncol)
2610 #else
2611 static int pgx_flush_colors(pgx, ci_start, ncol)
2612      PgxWin *pgx; int ci_start; int ncol;
2613 #endif
2614 {
2615   if(pgx_ready(pgx, PGX_NEED_COLOR) && !pgx->color->monochrome) {
2616 /*
2617  * If the range of color indexes is invalid, warn the user and
2618  * then arrange to update the whole colormap.
2619  */
2620     if(ci_start < 0 || ci_start + ncol - 1 >= pgx->color->ncol) {
2621       fprintf(stderr, "%s: pgx_flush_colors: color indexes out of range.\n",
2622 	      PGX_IDENT);
2623       ci_start = 0;
2624       ncol = pgx->color->ncol;
2625     };
2626 /*
2627  * Are there any colors to be updated?
2628  */
2629     if(ncol > 0) {
2630       XColor *xc = &pgx->color->xcolor[ci_start];
2631       unsigned long *pixel = &pgx->color->pixel[ci_start];
2632 /*
2633  * Allocate shared colorcells?
2634  */
2635       if(pgx->color->readonly) {
2636 	if(pgx_readonly_colors(pgx, ncol, xc, pixel))
2637 	  return 1;
2638 /*
2639  * Modify existing read/write colorcells.
2640  */
2641       } else {
2642 	XStoreColors(pgx->display, pgx->color->cmap, xc, ncol);
2643       };
2644 /*
2645  * Device error?
2646  */
2647       if(pgx->bad_device)
2648 	return 1;
2649 /*
2650  * Update the background color?
2651  */
2652       if(ci_start == 0 && pgx->window != None)
2653 	XSetWindowBackground(pgx->display, pgx->window, pixel[0]);
2654     };
2655   };
2656   return pgx->bad_device!=0;
2657 }
2658 
2659 /*.......................................................................
2660  * This is a front end to pgx_flush_colors() to be used when pgplot has
2661  * the window open and there are buffered colormap updates from previous
2662  * pgscr() opcodes.
2663  *
2664  *
2665  * Input:
2666  *  pgx    PgxWin *  The PGPLOT window context.
2667  * Output:
2668  *  return    int      0 - OK.
2669  *                     1 - Error.
2670  */
2671 #ifdef __STDC__
pgx_update_colors(PgxWin * pgx)2672 static int pgx_update_colors(PgxWin *pgx)
2673 #else
2674 static int pgx_update_colors(pgx)
2675      PgxWin *pgx;
2676 #endif
2677 {
2678   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
2679     PgxState *state = pgx->state;
2680 /*
2681  * Are there any colors to be updated?
2682  */
2683     if(state->color.nbuff > 0) {
2684       if(pgx_flush_colors(pgx, state->color.sbuff, state->color.nbuff))
2685 	return 1;
2686 /*
2687  * Reset buffer pointers.
2688  */
2689       state->color.nbuff = 0;
2690       state->color.sbuff = 0;
2691     };
2692   };
2693   return 0;
2694 }
2695 
2696 /*.......................................................................
2697  * Clear a PGPLOT window and (if allocated) the associated pixmap, to
2698  * the background color of the window. This function may be called
2699  * before pgx_open() is called.
2700  *
2701  * Input:
2702  *  pgx    PgxWin *  The PGPLOT window context.
2703  * Output:
2704  *  return    int    0 - OK.
2705  *                   1 - Error.
2706  */
2707 #ifdef __STDC__
pgx_clear_window(PgxWin * pgx)2708 int pgx_clear_window(PgxWin *pgx)
2709 #else
2710 int pgx_clear_window(pgx)
2711      PgxWin *pgx;
2712 #endif
2713 {
2714   if(pgx_ready(pgx, PGX_NEED_WINDOW)) {
2715 /*
2716  * Clear the window itself.
2717  */
2718     if(pgx->clip.doclip) {
2719       pgx_clear_area(pgx, pgx->clip.xmin, pgx->clip.ymin,
2720 		     (pgx->clip.xmax - pgx->clip.xmin + 1),
2721 		     (pgx->clip.ymax - pgx->clip.ymin + 1));
2722     } else {
2723       XClearWindow(pgx->display, pgx->window);
2724     };
2725     if(pgx->bad_device)
2726       return 1;
2727 /*
2728  * Flush any defered readonly color-cell updates.
2729  */
2730     if(pgx_update_colors(pgx))
2731       return 1;
2732 /*
2733  * Fill the pixmap with the background color.
2734  */
2735     if(pgx_ready(pgx, PGX_NEED_COLOR | PGX_NEED_PIXMAP)) {
2736       Window root;
2737       int x, y;
2738       unsigned width, height, border, depth;
2739 /*
2740  * Determine the size of the pixmap.
2741  */
2742       XGetGeometry(pgx->display, pgx->pixmap, &root, &x, &y, &width, &height,
2743 		   &border, &depth);
2744 /*
2745  * Clear the pixmap by drawing an opaque rectangle over it in the background
2746  * color. Use the exposure graphical context to avoid changing the
2747  * current foreground color and to allow this function to be called
2748  * before pgx_open().
2749  */
2750       XSetForeground(pgx->display, pgx->expose_gc, pgx->color->pixel[0]);
2751       XFillRectangle(pgx->display, pgx->pixmap, pgx->expose_gc, 0, 0,
2752 		     width, height);
2753       if(pgx->bad_device)
2754 	return 1;
2755 /*
2756  * Mark the pixmap as unmodified.
2757  */
2758       if(pgx->state)
2759 	pgx->state->update.modified = 0;
2760     };
2761     XFlush(pgx->display);
2762     if(pgx->bad_device)
2763       return 1;
2764   };
2765   return pgx->bad_device ? 1 : 0;
2766 }
2767 
2768 /*.......................................................................
2769  * Return the device resolution in pixels per inch. This can be used
2770  * to implement PGPLOT opcode 3.
2771  * If pgx==NULL or pgx->display==NULL or pgx->bad_device!=0, then dummy
2772  * values will be returned.
2773  *
2774  * Input:
2775  *  pgx           PgxWin *  The PGPLOT window context.
2776  * Input/Output:
2777  *  xpix_per_inch  float *  The number of pixels per inch along X.
2778  *  ypix_per_inch  float *  The number of pixels per inch along Y.
2779  */
2780 #ifdef __STDC__
pgx_get_resolution(PgxWin * pgx,float * xpix_per_inch,float * ypix_per_inch)2781 void pgx_get_resolution(PgxWin *pgx, float *xpix_per_inch, float *ypix_per_inch)
2782 #else
2783 void pgx_get_resolution(pgx, xpix_per_inch, ypix_per_inch)
2784      PgxWin *pgx; float *xpix_per_inch; float *ypix_per_inch;
2785 #endif
2786 {
2787   if(pgx && pgx->display && !pgx->bad_device) {
2788     Display *display = pgx->display;
2789     int screen = DefaultScreen(display);
2790     unsigned int d_pix_width = DisplayWidth(display, screen);
2791     unsigned int d_pix_height = DisplayHeight(display, screen);
2792     unsigned int d_mm_width = DisplayWidthMM(display, screen);
2793     unsigned int d_mm_height = DisplayHeightMM(display, screen);
2794 /*
2795  * Determine the device resolution in pixels per inch.
2796  */
2797     if(xpix_per_inch)
2798       *xpix_per_inch = 25.4 * ((double)d_pix_width / (double)d_mm_width);
2799     if(ypix_per_inch)
2800       *ypix_per_inch = 25.4 * ((double)d_pix_height / (double)d_mm_height);
2801     return;
2802   } else {
2803     if(xpix_per_inch)
2804       *xpix_per_inch = 1.0;
2805     if(ypix_per_inch)
2806       *ypix_per_inch = 1.0;
2807   };
2808   return;
2809 }
2810 
2811 /*.......................................................................
2812  * Return the default size of the plot area in the form required by
2813  * PGPLOT opcode 6.
2814  *
2815  * If pgx==NULL, pgx->display==NULL or pgx->bad_device!=0, then specified
2816  * default values will be returned.
2817  *
2818  * Input:
2819  *  pgx        PgxWin *  The PGPLOT window context.
2820  *  d_width  unsigned    The default width to be specified if the
2821  *                       device is not open or is in an innapropriate
2822  *                       state.
2823  *  d_height unsigned    The default height to be specified if the
2824  *                       device is not open or is in an innapropriate
2825  *                       state.
2826  * Input/Output:
2827  *  rbuf    float *  The return array passed from GREXEC.
2828  *  nbuf      int *  The output number of float results for GREXEC.
2829  */
2830 #ifdef __STDC__
pgx_def_size(PgxWin * pgx,unsigned d_width,unsigned d_height,float * rbuf,int * nbuf)2831 void pgx_def_size(PgxWin *pgx, unsigned d_width, unsigned d_height,
2832 		 float *rbuf, int *nbuf)
2833 #else
2834 void pgx_def_size(pgx, d_width, d_height, rbuf, nbuf)
2835      PgxWin *pgx; unsigned d_width; unsigned d_height; float *rbuf; int *nbuf;
2836 #endif
2837 {
2838 /*
2839  * Set invariants.
2840  */
2841   rbuf[0] = 0.0;
2842   rbuf[2] = 0.0;
2843   *nbuf = 4;
2844 /*
2845  * If we have sufficient information, return the current size of the
2846  * window excluding margins.
2847  */
2848   if(pgx && pgx->display && !pgx->bad_device) {
2849     XWindowAttributes attr;
2850     XGetWindowAttributes(pgx->display, pgx->window, &attr);
2851     if(!pgx->bad_device) {
2852       rbuf[1] = (float) (attr.width - 2 * pgx->xmargin);
2853       rbuf[3] = (float) (attr.height - 2 * pgx->ymargin);
2854       if(rbuf[1] > 2 && rbuf[3] > 2)
2855 	return;
2856     };
2857   };
2858 /*
2859  * If the device is not yet open, or if an error occured above, substitute
2860  * the default dimensions.
2861  */
2862   rbuf[1] = d_width;
2863   rbuf[3] = d_height;
2864   return;
2865 }
2866 
2867 /*.......................................................................
2868  * Start a new page of a specified size. This includes resizing the
2869  * window and pixmap if necessary and clearing them. It also includes
2870  * initializing pgx->state->geom to reflect the size of the pixmap.
2871  *
2872  * Input:
2873  *  pgx        PgxWin *  The PGPLOT window context.
2874  *  rbuf        float *  The array of float arguments sent by the PGPLOT
2875  *                       GREXEC() subroutine.
2876  */
2877 #ifdef __STDC__
pgx_begin_picture(PgxWin * pgx,float * rbuf)2878 void pgx_begin_picture(PgxWin *pgx, float *rbuf)
2879 #else
2880 void pgx_begin_picture(pgx, rbuf)
2881      PgxWin *pgx; float *rbuf;
2882 #endif
2883 {
2884   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
2885     PgxState *state = pgx->state;
2886 /*
2887  * Determine the device resolution.
2888  */
2889     pgx_get_resolution(pgx, &state->geom.xpix_per_inch,
2890 		       &state->geom.ypix_per_inch);
2891 /*
2892  * Determine the X and Y axis margins.
2893  */
2894     state->geom.xmargin = pgx->xmargin;
2895     state->geom.ymargin = pgx->ymargin;
2896 /*
2897  * Convert the passed max X and Y coordinates into the total width of the
2898  * new window and pixmap. Add margins to the requested area.
2899  */
2900     state->geom.width  = (int) (rbuf[0] + 0.5) + 2 * state->geom.xmargin;
2901     state->geom.height = (int) (rbuf[1] + 0.5) + 2 * state->geom.ymargin;
2902 /*
2903  * Record the coordinate bounds of the required window area.
2904  */
2905     state->geom.xmin = state->geom.xmargin;
2906     state->geom.xmax = state->geom.width - state->geom.xmargin;
2907     state->geom.ymin = state->geom.ymargin;
2908     state->geom.ymax = state->geom.height - state->geom.ymargin;
2909 /*
2910  * Resize the window if necessary.
2911  */
2912     if(!pgx->bad_device) {
2913       XWindowAttributes attr;
2914       XGetWindowAttributes(pgx->display, pgx->window, &attr);
2915       if(!pgx->bad_device)
2916 	if(pgx->resize_fn && (attr.width != state->geom.width ||
2917 			      attr.height != state->geom.height)) {
2918 	  (*pgx->resize_fn)(pgx, state->geom.width, state->geom.height);
2919 	};
2920     };
2921 /*
2922  * If a pixmap exists and has a different size to that requested,
2923  * delete it.
2924  */
2925     if(!pgx->bad_device && pgx->pixmap != None) {
2926       Window root;
2927       int x, y;
2928       unsigned width, height, border, depth;
2929 /*
2930  * Determine the size of the existing pixmap.
2931  */
2932       XGetGeometry(pgx->display, pgx->pixmap, &root, &x, &y, &width, &height,
2933 		   &border, &depth);
2934 /*
2935  * Delete it if it has the wrong size.
2936  */
2937       if(width != state->geom.width || height != state->geom.height) {
2938 	XFreePixmap(pgx->display, pgx->pixmap);
2939 	pgx->pixmap = None;
2940       };
2941     };
2942 /*
2943  * Create a new pixmap if necessary.
2944  */
2945     if(!pgx->bad_device && pgx->pixmap==None) {
2946       if(!pgx->new_pixmap_fn)
2947 	pgx->new_pixmap_fn = pgx_new_pixmap;
2948       (*pgx->new_pixmap_fn)(pgx, state->geom.width, state->geom.height);
2949     };
2950   };
2951 /*
2952  * Clear the window and pixmap.
2953  */
2954   pgx_clear_window(pgx);
2955 /*
2956  * Reset the scroll and pan offsets.
2957  */
2958   pgx_scroll(pgx, 0, 0);
2959   return;
2960 }
2961 
2962 /*.......................................................................
2963  * Allocate a new Pixmap for a PGPLOT window of a given size.
2964  * Note that pgx->pixmap should be deleted and assigned None before
2965  * this function is called.
2966  *
2967  * Input:
2968  *  pgx      PgxWin *  The PGPLOT window context.
2969  *  width  unsigned    The required width of the pixmap (pixels).
2970  *  height unsigned    The required height of the pixmap (pixels).
2971  */
2972 #ifdef __STDC__
pgx_new_pixmap(PgxWin * pgx,unsigned width,unsigned height)2973 void pgx_new_pixmap(PgxWin *pgx, unsigned width, unsigned height)
2974 #else
2975 void pgx_new_pixmap(pgx, width, height)
2976      PgxWin *pgx; unsigned width; unsigned height;
2977 #endif
2978 {
2979   if(pgx_ready(pgx, PGX_NEED_COLOR | PGX_NEED_WINDOW)) {
2980 /*
2981  * Bracket the pixmap acquisition with pgx_start/end_error() calls, to
2982  * determine whether any allocation errors occur.
2983  */
2984     pgx_start_error_watch(pgx);
2985     pgx->pixmap = XCreatePixmap(pgx->display, pgx->window, width, height,
2986 				(unsigned) pgx->color->vi->depth);
2987     if(pgx_end_error_watch(pgx) || pgx->pixmap==None) {
2988       fprintf(stderr, "%s: Failed to allocate %dx%d pixmap.\n", PGX_IDENT,
2989 	      width, height);
2990       pgx->pixmap = None;
2991     };
2992   };
2993   return;
2994 }
2995 
2996 /*.......................................................................
2997  * Change the appearance and position of specific graphical augmentations
2998  * to the cursor.
2999  *
3000  * Input:
3001  *  pgx      PgxWin *  The PGPLOT window context.
3002  *  ci          int    The color index to use when drawing the cursor,
3003  *                     or -1 to select the current foreground color.
3004  *  type        int    A cursor type from:
3005  *                      PGX_NORM_CURSOR  - No augmentation will be drawn.
3006  *                      PGX_LINE_CURSOR  - Line cursor between rbeg and rend.
3007  *                      PGX_RECT_CURSOR  - Rectangular cursor with opposing
3008  *                                         vertices at rbeg and rend.
3009  *                      PGX_VLINE_CURSOR - Vertical line cursor at x=rbeg[0].
3010  *                      PGX_HLINE_CURSOR - Horizontal line cursor at y=rbeg[1].
3011  *                      PGX_CROSS_CURSOR - Cross-hair cursor at rbeg[0],rbeg.[1]
3012  *  warp        int    If true, the cursor will be warped to rbeg when it
3013  *                     first enters the window.
3014  *  rbeg      float *  The PGPLOT x,y device coordinates of the origin
3015  *                     of the cursor, as rbeg[0]=x and rbeg[1]=y.
3016  *                     This can be NULL for type==PGX_NORM_CURSOR.
3017  *  rend      float *  The PGPLOT x,y device coordinates of the end-point
3018  *                     of the cursor, as rend[0]=x and rend[1]=y. This
3019  *                     can be NULL for cursor types that only need rbeg.
3020  * Output:
3021  *  return      int    0 - OK.
3022  *                     1 - Error.
3023  */
3024 #ifdef __STDC__
pgx_set_cursor(PgxWin * pgx,int ci,int type,int warp,float * rbeg,float * rend)3025 int pgx_set_cursor(PgxWin *pgx, int ci, int type, int warp,
3026 		   float *rbeg, float *rend)
3027 #else
3028 int pgx_set_cursor(pgx, ci, type, warp, rbeg, rend)
3029      PgxWin *pgx; int ci; int type; int warp; float *rbeg; float *rend;
3030 #endif
3031 {
3032   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
3033     PgxState *state = pgx->state;
3034     state->cursor.type = PGX_NORM_CURSOR;
3035     state->cursor.warp = warp;
3036 /*
3037  * Record the cursor coordinates according to cursor type.
3038  */
3039     switch(type) {
3040     case PGX_NORM_CURSOR:
3041       break;
3042     case PGX_LINE_CURSOR:
3043     case PGX_RECT_CURSOR:
3044     case PGX_YRNG_CURSOR:
3045     case PGX_XRNG_CURSOR:
3046       if(rbeg && rend) {
3047 	state->cursor.type = type;
3048 	pgx_xy_to_XPoint(pgx, rbeg, &state->cursor.vbeg);
3049 	pgx_xy_to_XPoint(pgx, rend, &state->cursor.vend);
3050       };
3051       break;
3052     case PGX_HLINE_CURSOR:
3053     case PGX_VLINE_CURSOR:
3054     case PGX_CROSS_CURSOR:
3055       if(rbeg) {
3056 	state->cursor.type = type;
3057 	pgx_xy_to_XPoint(pgx, rbeg, &state->cursor.vbeg);
3058       };
3059       break;
3060     };
3061 /*
3062  * Establish a color for drawing the cursor.
3063  */
3064     if(type != PGX_NORM_CURSOR) {
3065       unsigned long pixel;              /* The colormap pixel to draw with */
3066       if(ci < 0)
3067 	pixel = state->gcv.foreground;  /* The current foreground color */
3068       else if(ci < pgx->color->ncol)
3069 	pixel = pgx->color->pixel[ci];  /* The specified color index */
3070       else
3071 	pixel = pgx->color->pixel[1];   /* Out-of range, so use color index 1 */
3072       XSetForeground(pgx->display, state->cursor.gc, pixel);
3073     };
3074     return pgx->bad_device!=0;
3075   };
3076   return 1;
3077 }
3078 
3079 /*.......................................................................
3080  * If the cursor is currently marked as having been drawn, redraw it to
3081  * account for damage from expose events or pgplot drawing operations
3082  * that have taken place since the cursor was last drawn.
3083  *
3084  * Input:
3085  *  pgx      PgxWin *  The PGPLOT window context.
3086  *  rbeg      float *  The PGPLOT x,y device coordinates of the origin
3087  *                     of the cursor, as rbeg[0]=x and rbeg[1]=y.
3088  *                     This can be NULL for type==PGX_NORM_CURSOR.
3089  *  rend      float *  The PGPLOT x,y device coordinates of the end-point
3090  *                     of the cursor, as rend[0]=x and rend[1]=y. This
3091  *                     can be NULL for cursor types that only need rbeg.
3092  * Output:
3093  *  return      int    0 - OK.
3094  *                     1 - Error.
3095  */
3096 #ifdef __STDC__
pgx_refresh_cursor(PgxWin * pgx)3097 int pgx_refresh_cursor(PgxWin *pgx)
3098 #else
3099 int pgx_refresh_cursor(pgx)
3100      PgxWin *pgx;
3101 #endif
3102 {
3103   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
3104     PgxState *state = pgx->state;
3105     if(state->cursor.drawn && pgx_draw_cursor(pgx))
3106       return 1;
3107   };
3108   return 0;
3109 }
3110 
3111 /*.......................................................................
3112  * Augment the X cursor with non-destructive line graphics by drawing
3113  * the graphics directly to the X window instead of to the pixmap. THe
3114  * underlying graphics can then be completely restored from the pixmap.
3115  *
3116  * Input:
3117  *  pgx      PgxWin *  The PGPLOT window context.
3118  * Output:
3119  *  return      int    0 - OK.
3120  *                     1 - Error.
3121  */
3122 #ifdef __STDC__
pgx_draw_cursor(PgxWin * pgx)3123 int pgx_draw_cursor(PgxWin *pgx)
3124 #else
3125 int pgx_draw_cursor(pgx)
3126      PgxWin *pgx;
3127 #endif
3128 {
3129   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
3130     PgxState *state = pgx->state;
3131     XWcursor *cursor = &state->cursor;
3132     int ymin, ymax;
3133     int xmin, xmax;
3134 /*
3135  * Convert from pixmap coordinates to window coordinates.
3136  */
3137     XPoint vbeg;
3138     XPoint vend;
3139     pgx_pixmap_to_window(pgx, &cursor->vbeg, &vbeg);
3140     pgx_pixmap_to_window(pgx, &cursor->vend, &vend);
3141 /*
3142  * Get the drawable limits of the window.
3143  */
3144     if(pgx->clip.doclip) {
3145       xmin = pgx->clip.xmin;
3146       xmax = pgx->clip.xmax;
3147       ymin = pgx->clip.ymin;
3148       ymax = pgx->clip.ymax;
3149     } else {
3150       xmin = 0;
3151       xmax = (int)state->geom.width - 1;
3152       ymin = 0;
3153       ymax = (int)state->geom.height - 1;
3154     };
3155 /*
3156  * Record the fact that the cursor is being drawn.
3157  */
3158     cursor->drawn = 1;
3159 /*
3160  * Draw the requested cursor augmentation graphics.
3161  */
3162     switch(cursor->type) {
3163     case PGX_NORM_CURSOR:
3164     default:
3165       break;
3166     case PGX_LINE_CURSOR:
3167       XDrawLine(pgx->display, pgx->window, cursor->gc,
3168 		vbeg.x, vbeg.y,
3169 		vend.x, vend.y);
3170       break;
3171     case PGX_RECT_CURSOR:  /* Draw a rectangle */
3172       {
3173 	int x = vbeg.x<vend.x ? vbeg.x : vend.x;
3174 	int y = vbeg.y<vend.y ? vbeg.y : vend.y;
3175 	unsigned width = (unsigned int) abs(vbeg.x - vend.x);
3176 	unsigned height = (unsigned int) abs(vbeg.y - vend.y);
3177 	XDrawRectangle(pgx->display, pgx->window, cursor->gc, x, y,
3178 		       width, height);
3179       };
3180       break;
3181     case PGX_YRNG_CURSOR:  /* Two horizontal lines */
3182       XDrawLine(pgx->display, pgx->window, cursor->gc, xmin, vend.y,
3183 		xmax, vend.y);
3184       if(pgx->bad_device)
3185 	return 1;
3186       XDrawLine(pgx->display, pgx->window, cursor->gc, xmin, vbeg.y,
3187 		xmax, vbeg.y);
3188       break;
3189     case PGX_XRNG_CURSOR:  /* Two vertical lines */
3190       XDrawLine(pgx->display, pgx->window, cursor->gc, vend.x, ymin,
3191 		vend.x, ymax);
3192       if(pgx->bad_device)
3193 	return 1;
3194       XDrawLine(pgx->display, pgx->window, cursor->gc, vbeg.x, ymin,
3195 		vbeg.x, ymax);
3196       break;
3197     case PGX_HLINE_CURSOR: /* One horizontal line through the cursor */
3198       XDrawLine(pgx->display, pgx->window, cursor->gc, xmin, vend.y,
3199 		xmax, vend.y);
3200       break;
3201     case PGX_VLINE_CURSOR: /* One vertical line through the cursor */
3202       XDrawLine(pgx->display, pgx->window, cursor->gc, vend.x, ymin,
3203 		vend.x, ymax);
3204       break;
3205     case PGX_CROSS_CURSOR: /* Cross hair */
3206       XDrawLine(pgx->display, pgx->window, cursor->gc, xmin, vend.y,
3207 		xmax, vend.y);
3208       if(pgx->bad_device)
3209 	return 1;
3210       XDrawLine(pgx->display, pgx->window, cursor->gc, vend.x, ymin,
3211 		vend.x, ymax);
3212       break;
3213     };
3214     XFlush(pgx->display);
3215     return pgx->bad_device != 0;
3216   };
3217   return 1;
3218 }
3219 
3220 /*.......................................................................
3221  * Erase graphics drawn by pgx_draw_cursor() by restoring the damaged
3222  * region from the underlying pixmap.
3223  *
3224  * Input:
3225  *  pgx      PgxWin *  The PGPLOT window context.
3226  * Output:
3227  *  return      int    0 - OK.
3228  *                     1 - Error.
3229  */
3230 #ifdef __STDC__
pgx_erase_cursor(PgxWin * pgx)3231 int pgx_erase_cursor(PgxWin *pgx)
3232 #else
3233 int pgx_erase_cursor(pgx)
3234      PgxWin *pgx;
3235 #endif
3236 {
3237   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
3238     PgxState *state = pgx->state;
3239     XWcursor *cursor = &state->cursor;
3240 /*
3241  * Convert from pixmap coordinates to window coordinates.
3242  */
3243     XPoint vbeg;
3244     XPoint vend;
3245     pgx_pixmap_to_window(pgx, &cursor->vbeg, &vbeg);
3246     pgx_pixmap_to_window(pgx, &cursor->vend, &vend);
3247 /*
3248  * Record the erasure.
3249  */
3250     cursor->drawn = 0;
3251 /*
3252  * Erase the cursor.
3253  */
3254     switch(cursor->type) {
3255     case PGX_NORM_CURSOR:
3256     default:
3257       break;
3258     case PGX_LINE_CURSOR:   /* Line cursor */
3259       if(pgx_restore_line(pgx, vbeg.x, vbeg.y,
3260 			      vend.x, vend.y))
3261 	return 1;
3262       break;
3263     case PGX_RECT_CURSOR:   /* Rectangle cursor */
3264       if(pgx_restore_line(pgx, vbeg.x, vbeg.y,
3265 			      vbeg.x, vend.y) ||
3266 	 pgx_restore_line(pgx, vbeg.x, vend.y,
3267 			      vend.x, vend.y) ||
3268 	 pgx_restore_line(pgx, vend.x, vend.y,
3269 			      vend.x, vbeg.y) ||
3270 	 pgx_restore_line(pgx, vend.x, vbeg.y,
3271 			      vbeg.x, vbeg.y))
3272 	return 1;
3273       break;
3274     case PGX_YRNG_CURSOR:  /* Two horizontal lines */
3275       if(pgx_restore_line(pgx, 0, vend.y,
3276 			      (int)state->geom.width-1, vend.y) ||
3277 	 pgx_restore_line(pgx, 0, vbeg.y,
3278 			      (int)state->geom.width-1,vbeg.y))
3279 	return 1;
3280       break;
3281     case PGX_XRNG_CURSOR:  /* Two vertical lines */
3282       if(pgx_restore_line(pgx, vend.x, 0,
3283 			      vend.x, (int)state->geom.height-1) ||
3284 	 pgx_restore_line(pgx, vbeg.x, 0,
3285 			      vbeg.x, (int)state->geom.height-1))
3286 	return 1;
3287       break;
3288     case PGX_HLINE_CURSOR: /* One horizontal line through the cursor */
3289       if(pgx_restore_line(pgx, 0, vend.y,
3290 			      (int)state->geom.width-1,vend.y))
3291 	return 1;
3292       break;
3293     case PGX_VLINE_CURSOR: /* One vertical line through the cursor */
3294       if(pgx_restore_line(pgx, vend.x, 0,
3295 			      vend.x, (int)state->geom.height-1))
3296 	return 1;
3297       break;
3298     case PGX_CROSS_CURSOR: /* Cross hair */
3299       if(pgx_restore_line(pgx, 0, vend.y,
3300 			      (int)state->geom.width-1, vend.y) ||
3301 	 pgx_restore_line(pgx, vend.x, 0,
3302 			      vend.x, (int)state->geom.height-1))
3303 	return 1;
3304       break;
3305     };
3306     return pgx->bad_device != 0;
3307   };
3308   return 1;
3309 }
3310 
3311 /*.......................................................................
3312  * Restore the pixels under a given line.
3313  *
3314  * Input:
3315  *  pgx      PgxWin *  The PGPLOT window context.
3316  *  xa, ya      int    The start pixel of the line (window coordinates).
3317  *  xb, yb      int    The end pixel of the line (window coordinates).
3318  * Output:
3319  *  return      int    0 - OK.
3320  *                     1 - Error.
3321  */
3322 #ifdef __STDC__
pgx_restore_line(PgxWin * pgx,int xa,int ya,int xb,int yb)3323 static int pgx_restore_line(PgxWin *pgx, int xa, int ya, int xb, int yb)
3324 #else
3325 static int pgx_restore_line(pgx, xa, ya, xb, yb)
3326      PgxWin *pgx; int xa; int ya; int xb; int yb;
3327 #endif
3328 {
3329   int xlen = xb - xa;  /* X-axis displacement of line */
3330   int ylen = yb - ya;  /* Y-axis displacement of line */
3331   int xmin,xmax;       /* Min/max X-axis end points */
3332   int ymin,ymax;       /* Min/max Y-axis end points */
3333 #define PIXINC 51
3334 /*
3335  * Device error?
3336  */
3337   if(pgx->bad_device)
3338     return 1;
3339 /*
3340  * Get sorted versions of xa and xb.
3341  */
3342   if(xlen > 0) {
3343     xmin = xa;
3344     xmax = xb;
3345   } else {
3346     xmin = xb;
3347     xmax = xa;
3348   };
3349 /*
3350  * Get sorted versions of ya and yb.
3351  */
3352   if(ylen > 0) {
3353     ymin = ya;
3354     ymax = yb;
3355   } else {
3356     ymin = yb;
3357     ymax = ya;
3358   };
3359 /*
3360  * Vertical line?
3361  */
3362   if(xlen==0) {
3363     pgx_copy_area(pgx, (int)(xmin + pgx->scroll.x), (int)(ymin + pgx->scroll.y),
3364 		  (unsigned) 1, (unsigned) (ymax-ymin+1),
3365 		  xmin, ymin);
3366   }
3367 /*
3368  * Horizontal line?
3369  */
3370   else if(ylen==0) {
3371     pgx_copy_area(pgx, (int)(xmin + pgx->scroll.x), (int)(ymin + pgx->scroll.y),
3372 		  (unsigned) (xmax-xmin+1), (unsigned) 1,
3373 		  xmin, ymin);
3374   }
3375 /*
3376  * Diagonal line encompasing fewer x-axis lines that y-axis lines?
3377  */
3378   else if(abs(xlen) <= abs(ylen)) {
3379     int x;       /* The X coordinate of the line of pixels being drawn */
3380     int y1,y2;   /* The start and end Y coordinates of the pixel line */
3381     double yperx = (double) ylen / (double) xlen;
3382     double yhalf = 0.5 * yperx;         /* Y-step over half a pixel */
3383     double ydelt = (PIXINC+0.5) * yperx; /* Y-step over PIXINC+0.5 pixels */
3384     double ylo = yperx > 0 ? yhalf : -ydelt;
3385     double yhi = yperx > 0 ? ydelt : -yhalf;
3386 /*
3387  * Draw the block of pixels that encompases the line between X-axis
3388  * pixels the outer edges of pixels x -> x+PIXINC, for each consecutive
3389  * block of PIXINC pixels along X.
3390  */
3391     for(x=xmin; x <= xmax; x += PIXINC+1) {
3392       double ycent = ya + (x - xa) * yperx;
3393       y1 = (int)(ycent - ylo);    /* Note round-down semantics */
3394       y2 = (int)(ycent + yhi+0.5);/* Note round-up semantics */
3395       pgx_copy_area(pgx, (int)(x + pgx->scroll.x), (int)(y1 + pgx->scroll.y),
3396 		    (unsigned) (PIXINC+1), (unsigned) (y2-y1+1),
3397 		    x, y1);
3398     };
3399 /*
3400  * Diagonal line encompasing fewer y-axis lines that x-axis lines?
3401  */
3402   } else {
3403     int y;       /* The Y coordinate of the line of pixels being drawn */
3404     int x1,x2;   /* The start and end X coordinates of the pixel line */
3405     double xpery = (double) xlen / (double) ylen;
3406     double xhalf = 0.5 * xpery;         /* X-step over half a pixel */
3407     double xdelt = (PIXINC+0.5) * xpery; /* X-step over PIXINC+0.5 pixels */
3408     double xlo = xpery > 0 ? xhalf : -xdelt;
3409     double xhi = xpery > 0 ? xdelt : -xhalf;
3410 /*
3411  * Draw the block of pixels that encompases the line between Y-axis
3412  * pixels the outer edges of pixels y -> y+PIXINC, for each consecutive
3413  * block of PIXINC pixels along Y.
3414  */
3415     for(y=ymin; y <= ymax; y += PIXINC+1) {
3416       double xcent = xa + (y - ya) * xpery;
3417       x1 = (int)(xcent - xlo);    /* Note round-down semantics */
3418       x2 = (int)(xcent + xhi+0.5);/* Note round-up semantics */
3419       pgx_copy_area(pgx, (int)(x1 + pgx->scroll.x), (int)(y + pgx->scroll.y),
3420 		    (unsigned) (x2-x1+1), (unsigned) (PIXINC+1),
3421 		    x1, y);
3422     };
3423   };
3424 /*
3425  * Check for device errors.
3426  */
3427   if(pgx->bad_device)
3428     return 1;
3429   return 0;
3430 }
3431 
3432 /*.......................................................................
3433  * When the cursor is active, this function should be called whenever
3434  * one of the following event types are received.
3435  *
3436  *  ButtonPress, KeyPress, MotionNotify, EnterWindow, LeaveWindow.
3437  *
3438  * In addition, Expose events will be handled if presented. Other event
3439  * types are quietly ignored.
3440  *
3441  * Input:
3442  *  pgx      PgxWin *  The PGPLOT window context.
3443  *  event    XEvent *  The X input event to be processed.
3444  * Input/Output:
3445  *  rbuf      float *  If event->type is ButtonPress, KeyPress or
3446  *                     MotionNotify and rbuf!=NULL then the device
3447  *                     coordinates of the input event will be encoded
3448  *                     in PGPLOT x,y device coordinates as rbuf[0]=x
3449  *                     and rbuf[1]=y.
3450  *  key        char *  If key!=NULL then:
3451  *                     1. If the event is a key-press or key-release
3452  *                        event and the key is a single-ascii
3453  *                        character then *key will contain that
3454  *                        character.
3455  *                     2. If the event is a button-press or
3456  *                        button-release event then the button will be
3457  *                        encoded as characters in *key as:
3458  *                         Button 1 => 'A',
3459  *                         Button 2 => 'D',
3460  *                         Button 3 => 'X'.
3461  *                     3. Otherwise *key='\0'.
3462  * Output:
3463  *  return      int    0 - No position selected.
3464  *                     1 - rbuf and key contain the latest cursor
3465  *                         selection details.
3466  */
3467 #ifdef __STDC__
pgx_cursor_event(PgxWin * pgx,XEvent * event,float * rbuf,char * key)3468 int pgx_cursor_event(PgxWin *pgx, XEvent *event, float *rbuf, char *key)
3469 #else
3470 int pgx_cursor_event(pgx, event, rbuf, key)
3471      PgxWin *pgx; XEvent *event; float *rbuf; char *key;
3472 #endif
3473 {
3474   char ret_key = '\0';   /* The key to be returned */
3475   XPoint coord;          /* Pointer coordinate */
3476   int have_posn = 0;     /* If true, a position has been encoded in coord */
3477 /*
3478  * Cursor ready?
3479  */
3480   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
3481     PgxState *state = pgx->state;
3482     XWcursor *cursor = &state->cursor;
3483 /*
3484  * Place the pointer position in coord.x,y.
3485  */
3486     switch(event->type) {
3487     case Expose:
3488       if(pgx_expose(pgx, event))
3489 	return 0;
3490       break;
3491     case KeyPress:
3492       coord.x = event->xkey.x;
3493       coord.y = event->xkey.y;
3494 /*
3495  * Get the ASCII encoding associated with the key.
3496  */
3497       {
3498 	char buffer[10];   /* Buffer to read key definition into */
3499 	KeySym keysym;     /* Key code of pressed keyboard key */
3500 	int nret;          /* The number of characters returned in buffer[]*/
3501 	nret = XLookupString(&event->xkey, buffer,
3502 		          (int) (sizeof(buffer)/sizeof(char)), &keysym, NULL);
3503 	if(pgx->bad_device)
3504 	  return 0;
3505 /*
3506  * Ignore modifier keys and all but single character keys.
3507  */
3508 	if(nret==1 && (keysym < XK_Shift_L || keysym > XK_Hyper_R)) {
3509 	  ret_key = buffer[0];
3510 	  have_posn = 1;
3511 	};
3512       };
3513       break;
3514     case ButtonPress:
3515       coord.x = event->xbutton.x;
3516       coord.y = event->xbutton.y;
3517       have_posn = 1;
3518       switch(event->xbutton.button) {
3519       case Button1:
3520 	ret_key = 'A';
3521 	break;
3522       case Button2:
3523 	ret_key = 'D';
3524 	break;
3525       default:
3526 	ret_key = 'X';
3527 	break;
3528       };
3529       break;
3530     case EnterNotify:
3531 /*
3532  * The cursor may still be drawn if a button was pressed when the
3533  * cursor was last moved out of the window. The resulting
3534  * passive grab will have continued to deliver motion events to
3535  * the PGPLOT window.
3536  */
3537       if(pgx_erase_cursor(pgx))
3538 	return 0;
3539 /*
3540  * If the cursor is in the window, locate its position and record it
3541  * in pgx->state->cursor.vend. If this is the first time that the
3542  * cursor has been in the window and warping has been requested,
3543  * this also inolves pre-positioning the cursor.
3544  */
3545       if(pgx_locate_cursor(pgx)) {
3546 /*
3547  * Draw the cursor if it isn't already drawn.
3548  */
3549 	if(pgx->bad_device || pgx_draw_cursor(pgx))
3550 	  return 0;
3551       };
3552       break;
3553     case LeaveNotify:
3554       if(pgx_erase_cursor(pgx))
3555 	return 0;
3556       break;
3557     case MotionNotify:
3558 /*
3559  * Discard all but the last MotionNotify event.
3560  */
3561       while(XCheckWindowEvent(pgx->display, pgx->window,
3562 			      (long)(PointerMotionMask), event) == True);
3563       if(pgx->bad_device || pgx_erase_cursor(pgx))
3564 	return 0;
3565 /*
3566  * Erase the out-of-date cursor.
3567  */
3568       if(pgx_erase_cursor(pgx))
3569 	return 0;
3570 /*
3571  * Convert from window coordinates to pixmap coordinates.
3572  */
3573       cursor->vend.x = event->xmotion.x + pgx->scroll.x;
3574       cursor->vend.y = event->xmotion.y + pgx->scroll.y;
3575 /*
3576  * Redraw the cursor at the new position.
3577  */
3578       if(pgx_draw_cursor(pgx))
3579 	return 0;
3580       break;
3581     default:
3582       break;
3583     };
3584 /*
3585  * Convert new pointer coordinates to pgplot device coordinates.
3586  */
3587     if(have_posn) {
3588 /*
3589  * Convert from window coordinates to pixmap coordinates.
3590  */
3591       coord.x = coord.x + pgx->scroll.x;
3592       coord.y = coord.y + pgx->scroll.y;
3593 /*
3594  * Convert to PGPLOT device coordinates.
3595  */
3596       if(rbuf)
3597 	pgx_XPoint_to_xy(pgx, &coord, rbuf);
3598       if(key)
3599 	*key = ret_key;
3600       return 1;
3601     };
3602   };
3603   return 0;
3604 }
3605 
3606 /*.......................................................................
3607  * Determine whether the cursor is within the plot window. If it is
3608  * and (cursor=&pgx->state->cursor) cursor->warp is true, warp the cursor
3609  * to cursor->vbeg then reset cursor->warp to 0.
3610  * Record the final position of the cursor in cursor->vend.
3611  *
3612  * Input:
3613  *  pgx      PgxWin *  The PGPLOT window context.
3614  * Output:
3615  *  return  int   0 - Cursor not in window.
3616  *                1 - Cursor is in window.
3617  */
3618 #ifdef __STDC__
pgx_locate_cursor(PgxWin * pgx)3619 int pgx_locate_cursor(PgxWin *pgx)
3620 #else
3621 int pgx_locate_cursor(pgx)
3622      PgxWin *pgx;
3623 #endif
3624 {
3625   XPoint pointer;         /* Pointer coordinates */
3626   Window parent;          /* The parent window */
3627 /*
3628  * The following are all for use with XQueryPointer().
3629  */
3630   Window p_child;         /* The child of pgx->window (None) */
3631   int p_win_x, p_win_y;   /* The pointer coordinates in pgx->window */
3632   int p_root_x, p_root_y; /* The pointer coordinates in the root window */
3633   Window p_root_win;      /* The root window containing the cursor */
3634   unsigned int p_mask;    /* Bit mask of button states etc.. */
3635 /*
3636  * Device error?
3637  */
3638   if(pgx_ready(pgx, PGX_NEED_WINDOW | PGX_NEED_PGOPEN)) {
3639     PgxState *state = pgx->state;
3640     XWcursor *cursor = &state->cursor;
3641 /*
3642  * Get the parent window.
3643  */
3644     parent = pgx_parent_window(pgx);
3645     if(parent == None)
3646       return 0;
3647 /*
3648  * See if the pointer is currently in the PGPLOT window.
3649  */
3650     XQueryPointer(pgx->display, parent, &p_root_win, &p_child,
3651 		    &p_root_x, &p_root_y, &p_win_x, &p_win_y, &p_mask);
3652     if(pgx->bad_device)
3653       return 0;
3654     if(p_child==pgx->window) {
3655 /*
3656  * Determine the current position of the pointer within the PGPLOT window.
3657  */
3658       XQueryPointer(pgx->display, pgx->window, &p_root_win, &p_child,
3659 		    &p_root_x, &p_root_y, &p_win_x, &p_win_y, &p_mask);
3660       if(pgx->bad_device)
3661 	return 0;
3662 /*
3663  * Record the pointer coordinates.
3664  */
3665       pointer.x = p_win_x;
3666       pointer.y = p_win_y;
3667 /*
3668  * Warp the cursor?
3669  */
3670       if(cursor->warp) {
3671 	XWindowAttributes attr; /* Current window attributes */
3672 	XPoint warp_coord;      /* The window coordinates to warp to */
3673 /*
3674  * Query the current state of the window.
3675  */
3676 	XGetWindowAttributes(pgx->display, pgx->window, &attr);
3677 	if(pgx->bad_device)
3678 	  return 0;
3679 /*
3680  * Convert the warp pixmap coordinates to window coordinates.
3681  */
3682 	pgx_pixmap_to_window(pgx, &cursor->vbeg, &warp_coord);
3683 /*
3684  * Disable subsequent warping.
3685  */
3686 	cursor->warp = 0;
3687 /*
3688  * Don't warp the cursor unless the warp target location is visible.
3689  */
3690 	if(warp_coord.x < 0 || warp_coord.x >= attr.width ||
3691 	   warp_coord.y < 0 || warp_coord.y >= attr.height) {
3692 	  pgx_window_to_pixmap(pgx, &pointer, &cursor->vend);
3693 	} else {
3694 	  XWarpPointer(pgx->display, None, pgx->window, 0, 0, 0, 0,
3695 		       warp_coord.x, warp_coord.y);
3696 
3697 	  if(pgx->bad_device)
3698 	    return 0;
3699 /*
3700  * Record the new coordinates.
3701  */
3702 	  pgx_window_to_pixmap(pgx, &warp_coord, &cursor->vend);
3703 	};
3704 /*
3705  * Return the current position of the cursor without warping.
3706  */
3707       } else {
3708 	pgx_window_to_pixmap(pgx, &pointer, &cursor->vend);
3709       };
3710       return 1;  /* The pointer is in the window */
3711     };
3712   };
3713   return 0;    /* The pointer is not in the window */
3714 }
3715 
3716 /*.......................................................................
3717  * Install a new event mask. This function can be used to add to remove
3718  * from or replace the existing event mask. When adding to or replacing
3719  * the existing event mask, care is taken to check for the addition of
3720  * events that X only allows one client to select. If these can not
3721  * be selected, they will silently be removed from the event mask.
3722  * The pertinent masks are: ButtonPressMask, SubstructureRedirectMask and
3723  * ResizeRedirectMask.
3724  *
3725  * Input:
3726  *  pgx           PgxWin *  The PGPLOT window context.
3727  *  oper             int    The operation to be performed, from:
3728  *                           PGX_ADD_EVENTS - Form a union of the existing
3729  *                                            event mask and 'events'.
3730  *                           PGX_REM_EVENTS - Remove the events in 'events'
3731  *                                            from the existing event mask.
3732  *                           PGX_SET_EVENTS - Replace the existing event
3733  *                                            mask with 'events'.
3734  *  events unsigned long    The event mask to use as specified by 'oper'.
3735  * Output:
3736  *  return unsigned long    The old event mask.
3737  */
3738 #ifdef __STDC__
pgx_select_events(PgxWin * pgx,int oper,long events)3739 unsigned long pgx_select_events(PgxWin *pgx, int oper, long events)
3740 #else
3741 unsigned long pgx_select_events(pgx, oper, events)
3742      PgxWin *pgx; int oper; long events;
3743 #endif
3744 {
3745   unsigned long incr_mask;    /* The events to be added to the old mask */
3746   unsigned long decr_mask;    /* The events to be removed from the old mask */
3747   unsigned long new_mask = 0; /* The new trial event mask */
3748   unsigned long old_mask = 0; /* The old event mask to be returned */
3749 /*
3750  * We need a window to select events from.
3751  */
3752   if(pgx_ready(pgx, PGX_NEED_WINDOW)) {
3753     XWindowAttributes attr; /* Current window attributes */
3754 /*
3755  * Get the current window attributes.
3756  */
3757     XGetWindowAttributes(pgx->display, pgx->window, &attr);
3758     if(pgx->bad_device)
3759       return old_mask;
3760 /*
3761  * Record the existing event mask.
3762  */
3763     old_mask = attr.your_event_mask;
3764 /*
3765  * Decompose the mask-update into a mask of events that need to be
3766  * added and a mask of events that need to be removed.
3767  */
3768     switch(oper) {
3769     case PGX_SET_EVENTS:
3770     default:
3771       incr_mask = ~old_mask & events;
3772       decr_mask = ~events & old_mask;
3773       break;
3774     case PGX_ADD_EVENTS:
3775       incr_mask = ~old_mask & (old_mask | events);
3776       decr_mask = 0;
3777       break;
3778     case PGX_REM_EVENTS:
3779       incr_mask = 0;
3780       decr_mask = events;
3781       break;
3782     };
3783 /*
3784  * Form a new mask from the old mask by removing the events in decr_mask
3785  * but adding only events from the incremental mask that do not have the
3786  * potential to cause protocol errors.
3787  */
3788     new_mask = (old_mask & ~decr_mask) |
3789       (incr_mask & ~(unsigned long)(ButtonPressMask |
3790 		     SubstructureRedirectMask | ResizeRedirectMask));
3791 /*
3792  * If the incremental event mask contains events that can only be selected
3793  * by one client at a time, we must try each one, one at a time and
3794  * only add the event to the final mask if it can be accomodated.
3795  */
3796     if(incr_mask & ButtonPressMask) {
3797       pgx_start_error_watch(pgx);
3798       XSelectInput(pgx->display, pgx->window,
3799 		   (long) (new_mask | ButtonPressMask));
3800       if(!pgx_end_error_watch(pgx))
3801 	new_mask |= ButtonPressMask;
3802     };
3803     if(incr_mask & SubstructureRedirectMask) {
3804       pgx_start_error_watch(pgx);
3805       XSelectInput(pgx->display, pgx->window,
3806 		   (long) (new_mask | SubstructureRedirectMask));
3807       if(!pgx_end_error_watch(pgx))
3808 	new_mask |= SubstructureRedirectMask;
3809     };
3810     if(incr_mask & ResizeRedirectMask) {
3811       pgx_start_error_watch(pgx);
3812       XSelectInput(pgx->display, pgx->window,
3813 		   (long)(new_mask | ResizeRedirectMask));
3814       if(!pgx_end_error_watch(pgx))
3815 	new_mask |= ResizeRedirectMask;
3816     };
3817 /*
3818  * Try to select the new mask.
3819  */
3820     pgx_start_error_watch(pgx);
3821     XSelectInput(pgx->display, pgx->window, (long) new_mask);
3822     if(pgx_end_error_watch(pgx)) {
3823       fprintf(stderr, "pgx_select_events: Error selecting events.\n");
3824       return old_mask;
3825     };
3826   };
3827   return old_mask;
3828 }
3829 
3830 /*.......................................................................
3831  * Perform a blocking cursor read to implement the read-cursor PGPLOT
3832  * driver opcode. The function will not return until the user presses a
3833  * pointer button or keyboard key within the window.
3834  *
3835  * Input:
3836  *  pgx    PgxWin *  The PGPLOT window context.
3837  * Input/Output:
3838  *  rbuf    float *  The array of float arguments sent by the PGPLOT
3839  *                   GREXEC() subroutine.
3840  *                   On input:
3841  *                    rbuf[0] = Initial X position of cursor.
3842  *                    rbuf[1] = Initial Y position of cursor.
3843  *                    rbuf[2] = Reference X position for rubber-banding.
3844  *                    rbuf[3] = Reference Y position for rubber-banding.
3845  *                    rbuf[4] = Cursor banding mode as enumerated in
3846  *                              pgxwin.h as PGX_*_CURSOR.
3847  *                    rbuf[5] = Pre-position cursor if > 0.
3848  *                   On output:
3849  *                    rbuf[0] = X position of cursor.
3850  *                    rbuf[1] = Y position of cursor.
3851  *  chr      char *  On output *chr will be assigned the character
3852  *                   associated with the key or button that the user
3853  *                   typed.
3854  *  nbuf      int *  On output this will be set to 2 to tell PGPLOT that
3855  *                   two elements of rbuf are being returned.
3856  *  lchr      int *  On output this will be set to 1 to tell PGPLOT that
3857  *                   chr[] contains a single character.
3858  * Output:
3859  *  return    int    0 - OK.
3860  *                   1 - Error.
3861  */
3862 #ifdef __STDC__
pgx_read_cursor(PgxWin * pgx,float * rbuf,char * chr,int * nbuf,int * lchr)3863 int pgx_read_cursor(PgxWin *pgx, float *rbuf, char *chr, int *nbuf, int *lchr)
3864 #else
3865 int pgx_read_cursor(pgx, rbuf, chr, nbuf, lchr)
3866      PgxWin *pgx; float *rbuf; char *chr; int *nbuf; int *lchr;
3867 #endif
3868 {
3869   int waserr = 0;
3870 /*
3871  * Preset predetermined return values.
3872  */
3873   if(nbuf)
3874     *nbuf = 2;
3875   if(lchr)
3876     *lchr = 1;
3877   if(chr)
3878     *chr = '\0';
3879 /*
3880  * Read the cursor if possible.
3881  */
3882   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP)) {
3883     unsigned long old_mask;
3884 /*
3885  * Raise the cursor.
3886  */
3887     if(pgx_set_cursor(pgx, -1, (int)(rbuf[4]+0.5), (int)(rbuf[5]+0.5)>0,
3888 		      &rbuf[2], &rbuf[0]))
3889       return 1;
3890 /*
3891  * Draw the cursor if it is in the window.
3892  */
3893     pgx_locate_cursor(pgx);
3894 /*
3895  * Augment the event mask with the events that we need.
3896  */
3897     old_mask = pgx_select_events(pgx, PGX_ADD_EVENTS, (long)
3898 		   (ExposureMask | KeyPressMask | ButtonPressMask |
3899 		    EnterWindowMask | LeaveWindowMask |
3900 		    PointerMotionMask));
3901 /*
3902  * Handle the above events until the user selects a position by pressing
3903  * a mouse button or key.
3904  */
3905     waserr = waserr || pgx_handle_cursor(pgx, rbuf, chr);
3906 /*
3907  * Remove any cursor augmentation.
3908  */
3909     waserr = waserr || pgx_erase_cursor(pgx) ||
3910       pgx_set_cursor(pgx, 0, PGX_NORM_CURSOR, 0, NULL, NULL);
3911 /*
3912  * Reinstall the original event mask.
3913  */
3914     pgx_select_events(pgx, PGX_SET_EVENTS, (long) old_mask);
3915   };
3916   return waserr != 0;
3917 }
3918 
3919 /*.......................................................................
3920  * This is the private function of pgx_read_cursor() which maintains
3921  * the display of the cursor and returns when a valid keyboard or
3922  * button-press event has been received.
3923  *
3924  * Input:
3925  *  pgx    PgxWin *  The PGPLOT window context.
3926  * Input/Output:
3927  *  rbuf    float *  The return position array.
3928  *  key      char *  The key that caused the cursor to be selected.
3929  * Output:
3930  *  return    int    0 - OK.
3931  *                   1 - Error.
3932  */
3933 #ifdef __STDC__
pgx_handle_cursor(PgxWin * pgx,float * rbuf,char * key)3934 static int pgx_handle_cursor(PgxWin *pgx, float *rbuf, char *key)
3935 #else
3936 static int pgx_handle_cursor(pgx, rbuf, key)
3937      PgxWin *pgx; float *rbuf; char *key;
3938 #endif
3939 {
3940   XEvent event;
3941 /*
3942  * Discard un-handled ButtonPress, KeyPress and MotionNotify events
3943  * without blocking.
3944  */
3945   while(XCheckWindowEvent(pgx->display, pgx->window, (long)
3946 	      (ButtonPressMask | KeyPressMask | PointerMotionMask), &event))
3947     ;
3948   if(pgx->bad_device)
3949     return 1;
3950 /*
3951  * Wait for further events.
3952  */
3953   while(!pgx->bad_device) {
3954     XWindowEvent(pgx->display, pgx->window, (long)
3955 		 (ExposureMask | KeyPressMask | ButtonPressMask |
3956 		  EnterWindowMask | LeaveWindowMask | PointerMotionMask),
3957 		 &event);
3958     if(pgx->bad_device)
3959       return 1;
3960     if(pgx_cursor_event(pgx, &event, rbuf, key) && *key!='\0')
3961       return 0;
3962   };
3963   return 1;
3964 }
3965 
3966 /*.......................................................................
3967  * Limit pixmap coordinates to lie within the pixmap area.
3968  *
3969  * Input:
3970  *  pgx    PgxWin *  The PGPLOT window context.
3971  * Input/Output:
3972  *  coord  XPoint *  The coordinates to be modified.
3973  */
3974 #ifdef __STDC__
pgx_limit_pcoords(PgxWin * pgx,XPoint * coord)3975 static void pgx_limit_pcoords(PgxWin *pgx, XPoint *coord)
3976 #else
3977 static void pgx_limit_pcoords(pgx, coord)
3978      PgxWin *pgx; XPoint *coord;
3979 #endif
3980 {
3981   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
3982     PgxState *state = pgx->state;
3983     if(coord->x < 0)
3984       coord->x = 0;
3985     if(coord->y < 0)
3986       coord->y = 0;
3987     if(coord->x >= state->geom.width)
3988       coord->x = state->geom.width - 1;
3989     if(coord->y >= state->geom.height)
3990       coord->y = state->geom.height - 1;
3991   };
3992   return;
3993 }
3994 
3995 /*.......................................................................
3996  * Limit window coordinates to lie within the drawable window area.
3997  *
3998  * Input:
3999  *  pgx    PgxWin *  The PGPLOT window context.
4000  * Input/Output:
4001  *  coord  XPoint *  The coordinates to be modified.
4002  */
4003 #ifdef __STDC__
pgx_limit_wcoords(PgxWin * pgx,XPoint * coord)4004 static void pgx_limit_wcoords(PgxWin *pgx, XPoint *coord)
4005 #else
4006 static void pgx_limit_wcoords(pgx, coord)
4007      PgxWin *pgx; XPoint *coord;
4008 #endif
4009 {
4010   if(pgx_ready(pgx, 0) && pgx->clip.doclip) {
4011     if(coord->x >= pgx->clip.xmax)
4012       coord->x = pgx->clip.xmax;
4013     if(coord->y >= pgx->clip.ymax)
4014       coord->y = pgx->clip.ymax;
4015     if(coord->x < pgx->clip.xmin)
4016       coord->x = pgx->clip.xmin;
4017     if(coord->y < pgx->clip.ymin)
4018       coord->y = pgx->clip.ymin;
4019   };
4020   return;
4021 }
4022 
4023 /*.......................................................................
4024  * Convert from scrolled window coordinates to pixmap coordinates.
4025  * The pixmap coordinates will be limited to the bounds of the pixmap.
4026  *
4027  * Input:
4028  *  pgx     PgxWin *  The PGPLOT window context.
4029  *  w_coord XPoint *  The window coordinate to be converted.
4030  * Input/Output:
4031  *  p_coord XPoint *  The resulting pixmap coordinates.
4032  */
4033 #ifdef __STDC__
pgx_window_to_pixmap(PgxWin * pgx,XPoint * w_coord,XPoint * p_coord)4034 static void pgx_window_to_pixmap(PgxWin *pgx, XPoint *w_coord, XPoint *p_coord)
4035 #else
4036 static void pgx_window_to_pixmap(pgx, w_coord, p_coord)
4037      PgxWin *pgx; XPoint *w_coord; XPoint *p_coord;
4038 #endif
4039 {
4040   if(pgx) {
4041     p_coord->x = w_coord->x + pgx->scroll.x;
4042     p_coord->y = w_coord->y + pgx->scroll.y;
4043   } else {
4044     p_coord->x = w_coord->x;
4045     p_coord->y = w_coord->y;
4046   };
4047   pgx_limit_pcoords(pgx, p_coord);
4048   return;
4049 }
4050 
4051 /*.......................................................................
4052  * Convert from pixmap coordinates to scrolled window coordinates.
4053  * The coordinates will be bounded to the size of the pixmap
4054  * before the conversion and bounded to the drawable area of the
4055  * pixmap after the conversion.
4056  *
4057  * Input:
4058  *  pgx     PgxWin *  The PGPLOT window context.
4059  *  p_coord XPoint *  The pixmap coordinate to be converted.
4060  * Input/Output:
4061  *  w_coord XPoint *  The resulting window coordinates.
4062  */
4063 #ifdef __STDC__
pgx_pixmap_to_window(PgxWin * pgx,XPoint * p_coord,XPoint * w_coord)4064 static void pgx_pixmap_to_window(PgxWin *pgx, XPoint *p_coord, XPoint *w_coord)
4065 #else
4066 static void pgx_pixmap_to_window(pgx, p_coord, w_coord)
4067      PgxWin *pgx; XPoint *p_coord; XPoint *w_coord;
4068 #endif
4069 {
4070   if(pgx) {
4071     pgx_limit_pcoords(pgx, p_coord);
4072     w_coord->x = p_coord->x - pgx->scroll.x;
4073     w_coord->y = p_coord->y - pgx->scroll.y;
4074     pgx_limit_wcoords(pgx, w_coord);
4075   } else {
4076     w_coord->x = p_coord->x;
4077     w_coord->y = p_coord->y;
4078   };
4079   return;
4080 }
4081 
4082 /*.......................................................................
4083  * Create and initialize an empty PGPLOT window context descriptor
4084  * at least up to the point at which it can safely be passed to
4085  * del_PgxWin().
4086  *
4087  * Input:
4088  *  display  Display *   The display to associate with the window.
4089  *  screen       int     The screen to associate with the window.
4090  *  name        char *   A name to refer to the window by.
4091  *  resize_fn PgxResizeWindowFn The function to call when the window
4092  *                       needs resizing, or 0 if window resizing is to be
4093  *                       disallowed.
4094  *  pixmap_fn PgxNewPixmapFn The function to call to allocate a new
4095  *                       pixmap. If no special action is required,
4096  *                       send 0 and pgx_new_pixmap will be substituted.
4097  * Output:
4098  *  return    PgxWin *   The new container ready to be filled, or NULL
4099  *                       on error.
4100  */
4101 #ifdef __STDC__
new_PgxWin(Display * display,int screen,void * context,char * name,PgxResizeWindowFn resize_fn,PgxNewPixmapFn pixmap_fn)4102 PgxWin *new_PgxWin(Display *display, int screen, void *context, char *name,
4103 		   PgxResizeWindowFn resize_fn, PgxNewPixmapFn pixmap_fn)
4104 #else
4105 PgxWin *new_PgxWin(display, screen, context, name, resize_fn, pixmap_fn)
4106      Display *display; int screen; void *context; char *name;
4107      PgxResizeWindowFn resize_fn; PgxNewPixmapFn pixmap_fn;
4108 #endif
4109 {
4110   PgxWin *pgx;  /* The new descriptor */
4111 /*
4112  * Check arguments.
4113  */
4114   if(!display) {
4115     fprintf(stderr, "new_PgxWin: NULL Display intercepted.\n");
4116     return NULL;
4117   };
4118 /*
4119  * Allocate the container.
4120  */
4121   pgx = (PgxWin *) malloc(sizeof(PgxWin));
4122   if(!pgx) {
4123     fprintf(stderr, "new_PgxWin: Insufficient memory for new PGPLOT window.\n");
4124     return NULL;
4125   };
4126 /*
4127  * Before attemping anything that might fail, initialize the container
4128  * at least up to the point at which it can safely be passed to
4129  * del_PgxWin().
4130  */
4131   pgx->context = context;
4132   pgx->display = display;
4133   pgx->screen = screen;
4134   pgx->window = None;
4135   pgx->pixmap = None;
4136   pgx->expose_gc = NULL;
4137   pgx->bad_device = 0;
4138   pgx->name = NULL;
4139   pgx->xmargin = 0;
4140   pgx->ymargin = 0;
4141   pgx->color = NULL;
4142   pgx->clip.doclip = 0;
4143   pgx->clip.xmin = pgx->clip.xmax = 0;
4144   pgx->clip.ymin = pgx->clip.ymax = 0;
4145   pgx->resize_fn = resize_fn;
4146   pgx->new_pixmap_fn = pixmap_fn ? pixmap_fn : pgx_new_pixmap;
4147   pgx->old_handler = 0;
4148   pgx->state = NULL;
4149 /*
4150  * Allocate a copy of the specified name.
4151  */
4152   if(!name)
4153     name = "pgxwin";
4154   pgx->name = (char *) malloc(strlen(name) + 1);
4155   if(!pgx->name) {
4156     fprintf(stderr, "new_PgxWin: Insufficient memory to name window.\n");
4157     return del_PgxWin(pgx);
4158   };
4159   strcpy(pgx->name, name);
4160 /*
4161  * The rest of the initialization is driver-specific.
4162  */
4163   return pgx;
4164 }
4165 
4166 /*.......................................................................
4167  * Delete a PgxWin PGPLOT window context container and its contents.
4168  * Note that it is the responsibility of the caller to keep a record of
4169  * and delete the window pgx->window and close the display pgx->display
4170  * when appropriate.
4171  *
4172  * Input:
4173  *  pgx    PgxWin *  The container to be deleted.
4174  * Output:
4175  *  return PgxWin *  The deleted container (Always NULL).
4176  */
4177 #ifdef __STDC__
del_PgxWin(PgxWin * pgx)4178 PgxWin *del_PgxWin(PgxWin *pgx)
4179 #else
4180 PgxWin *del_PgxWin(pgx)
4181      PgxWin *pgx;
4182 #endif
4183 {
4184   if(pgx) {
4185 /*
4186  * Make sure that the window is closed to PGPLOT.
4187  * This deletes pgx->state and its contents.
4188  */
4189     pgx_close(pgx);
4190 /*
4191  * Destroy the pixmap.
4192  */
4193     if(pgx->display && pgx->pixmap != None)
4194       XFreePixmap(pgx->display, pgx->pixmap);
4195 /*
4196  * Discard the graphical context.
4197  */
4198     if(pgx->display && pgx->expose_gc)
4199       XFreeGC(pgx->display, pgx->expose_gc);
4200 /*
4201  * Delete the colormap/visual context.
4202  */
4203     pgx->color = del_PgxColor(pgx, pgx->color);
4204 /*
4205  * Release the memory taken by the window-name string.
4206  */
4207     if(pgx->name)
4208       free(pgx->name);
4209 /*
4210  * Just in case an application continues to use this descriptor after
4211  * it has been free()'d mark it as bad. This isn't foolproof since the
4212  * next malloc() will probably reuse this memory, but anything that
4213  * might help track down such a lethal problem is worth doing.
4214  */
4215     pgx->bad_device = 1;
4216 /*
4217  * Finally, free the container.
4218  */
4219     free(pgx);
4220   };
4221   return NULL;
4222 }
4223 
4224 /*.......................................................................
4225  * When copying the off-screen pixmap to the window this function
4226  * should be used in place of XCopyArea() [which it calls]. This function
4227  * clips the copied area to the dimensions of the window clipping
4228  * area in pgx->clip.
4229  *
4230  * Input:
4231  *  px,py     int    The top-left corner of the area to be copied
4232  *                   from the pixmap.
4233  *  w,h  unsigned    The width and height of the area to be copied.
4234  *  wx,wy     int    The top-left corner of the destination area
4235  *                   in the window.
4236  */
4237 #ifdef __STDC__
pgx_copy_area(PgxWin * pgx,int px,int py,unsigned w,unsigned h,int wx,int wy)4238 static int pgx_copy_area(PgxWin *pgx, int px, int py, unsigned w, unsigned h,
4239 			 int wx, int wy)
4240 #else
4241 static int pgx_copy_area(pgx, px, py, w, h, wx, wy)
4242      PgxWin *pgx; int px; int py; unsigned w; unsigned h;
4243      int wx; int wy;
4244 #endif
4245 {
4246   if(pgx_ready(pgx, PGX_NEED_WINDOW | PGX_NEED_PIXMAP)) {
4247 /*
4248  * Limit the copied area to the drawable area of the window as listed
4249  * in pgx->clip.
4250  *
4251  * First clip the destination X-axis extent to between pgx->clip.xmin and
4252  * pgx->clip.xmax and adjust the source coordinates to suit.
4253  */
4254     if(pgx->clip.doclip) {
4255       long height = h;   /* Signed version of h */
4256       long width = w;    /* Signed version of w */
4257       if(wx < pgx->clip.xmin) {
4258 	int xdiff = pgx->clip.xmin - wx;
4259 	wx += xdiff;
4260 	px += xdiff;
4261 	width -= xdiff;
4262       };
4263       if(wx + width - 1 > pgx->clip.xmax)
4264 	width = pgx->clip.xmax - wx + 1;
4265 /*
4266  * Now clip the destination Y-axis extent to between pgx->clip.ymin and
4267  * pgx->clip.ymax and adjust the source coordinates to suit.
4268  */
4269       if(wy < pgx->clip.ymin) {
4270 	int ydiff = pgx->clip.ymin - wy;
4271 	wy += ydiff;
4272 	py += ydiff;
4273 	height -= ydiff;
4274       };
4275       if(wy + height - 1 > pgx->clip.ymax)
4276 	height = pgx->clip.ymax - wy + 1;
4277 /*
4278  * Nothing visible?
4279  */
4280       if(width <= 0 || height <= 0)
4281 	return 0;
4282       w = width;
4283       h = height;
4284     };
4285 /*
4286  * Perform the requested copy.
4287  */
4288     XCopyArea(pgx->display, pgx->pixmap, pgx->window, pgx->expose_gc,
4289 	      px, py, w, h, wx, wy);
4290   };
4291   return 0;
4292 }
4293 
4294 /*.......................................................................
4295  * When clearing a region of the window this function should be used in
4296  * place of XClearArea() [which it calls]. This function clips the copied
4297  * area to the dimensions of the window clipping area in pgx->clip.
4298  *
4299  * Input:
4300  *  x,y       int    The top-left corner of the area to be cleared
4301  *                   in the window (window pixel coordinates).
4302  *  w,h  unsigned    The width and height of the area to be cleared.
4303  */
4304 #ifdef __STDC__
pgx_clear_area(PgxWin * pgx,int x,int y,unsigned w,unsigned h)4305 static int pgx_clear_area(PgxWin *pgx, int x, int y, unsigned w, unsigned h)
4306 #else
4307 static int pgx_clear_area(pgx, x, y, w, h)
4308      PgxWin *pgx; int x; int y; unsigned w; unsigned h;
4309 #endif
4310 {
4311   if(pgx_ready(pgx, PGX_NEED_WINDOW)) {
4312 /*
4313  * Limit the cleared area to the drawable area of the window as listed
4314  * in pgx->clip.
4315  *
4316  * First clip the destination X-axis extent to between pgx->clip.xmin and
4317  * pgx->clip.xmax.
4318  */
4319     if(pgx->clip.doclip) {
4320       long height = h;   /* Signed version of h */
4321       long width = w;    /* Signed version of w */
4322       if(x < pgx->clip.xmin) {
4323 	int xdiff = pgx->clip.xmin - x;
4324 	x += xdiff;
4325 	width -= xdiff;
4326       };
4327       if(x + width - 1 > pgx->clip.xmax)
4328 	width = pgx->clip.xmax - x + 1;
4329 /*
4330  * Now clip the destination Y-axis extent to between pgx->clip.ymin and
4331  * pgx->clip.ymax and adjust the source coordinates to suit.
4332  */
4333       if(y < pgx->clip.ymin) {
4334 	int ydiff = pgx->clip.ymin - y;
4335 	y += ydiff;
4336 	height -= ydiff;
4337       };
4338       if(y + height - 1 > pgx->clip.ymax)
4339 	height = pgx->clip.ymax - y + 1;
4340 /*
4341  * Nothing visible?
4342  */
4343       if(width <= 0 || height <= 0)
4344 	return 0;
4345       w = width;
4346       h = height;
4347     };
4348 /*
4349  * Perform the requested clear.
4350  */
4351     XClearArea(pgx->display, pgx->window, x, y, w, h, False);
4352   };
4353   return 0;
4354 }
4355 
4356 /*.......................................................................
4357  * Change the background color of a window.
4358  *
4359  * Input:
4360  *  pgx    PgxWin *  The PGPLOT window context.
4361  *  xc     XColor *  The new background color.
4362  * Output:
4363  *  return    int    0 - OK.
4364  *                   1 - Error.
4365  */
4366 #ifdef __STDC__
pgx_set_background(PgxWin * pgx,XColor * xc)4367 int pgx_set_background(PgxWin *pgx, XColor *xc)
4368 #else
4369 int pgx_set_background(pgx, xc)
4370      PgxWin *pgx; XColor *xc;
4371 #endif
4372 {
4373   if(!xc) {
4374     fprintf(stderr, "%s: pgx_set_background: NULL xc argument.", PGX_IDENT);
4375     return 1;
4376   };
4377   if(pgx_ready(pgx, PGX_NEED_COLOR)) {
4378     float r = (float) xc->red / PGX_COLORMULT;
4379     float g = (float) xc->green / PGX_COLORMULT;
4380     float b = (float) xc->blue / PGX_COLORMULT;
4381 /*
4382  * Register the desired color in pgx->color->xcolor[0].
4383  */
4384     if(pgx_set_rgb(pgx, 0, r, g, b))
4385       return 1;
4386 /*
4387  * Flush the changed color to the X server.
4388  * If the allocated color cells are readonly, defer the update until
4389  * the next page.
4390  */
4391     if((!pgx->color->readonly && pgx->state) ?
4392        pgx_update_colors(pgx) : pgx_flush_colors(pgx, 0, 1))
4393       return 1;
4394   };
4395   return 0;
4396 }
4397 
4398 /*.......................................................................
4399  * Change the foreground color of a window.
4400  *
4401  * Input:
4402  *  pgx    PgxWin *  The PGPLOT window context.
4403  *  xc     XColor *  The new foreground color.
4404  * Output:
4405  *  return    int    0 - OK.
4406  *                   1 - Error.
4407  */
4408 #ifdef __STDC__
pgx_set_foreground(PgxWin * pgx,XColor * xc)4409 int pgx_set_foreground(PgxWin *pgx, XColor *xc)
4410 #else
4411 int pgx_set_foreground(pgx, xc)
4412      PgxWin *pgx; XColor *xc;
4413 #endif
4414 {
4415   if(!xc) {
4416     fprintf(stderr, "%s: pgx_set_foreground: NULL xc argument.", PGX_IDENT);
4417     return 1;
4418   };
4419   if(pgx_ready(pgx, PGX_NEED_COLOR)) {
4420     float r = (float) xc->red / PGX_COLORMULT;
4421     float g = (float) xc->green / PGX_COLORMULT;
4422     float b = (float) xc->blue / PGX_COLORMULT;
4423 /*
4424  * Register the desired color in pgx->color->xcolor[0].
4425  */
4426     if(pgx_set_rgb(pgx, 1, r, g, b))
4427       return 1;
4428 /*
4429  * Flush the changed color to the X server.
4430  * If the allocated color cells are readonly, defer the update until
4431  * the next page.
4432  */
4433     if((!pgx->color->readonly && pgx->state) ?
4434        pgx_update_colors(pgx) : pgx_flush_colors(pgx, 1, 1))
4435       return 1;
4436   };
4437   return 0;
4438 }
4439 
4440 /*.......................................................................
4441  * Return the width of the current pixmap.
4442  *
4443  * Input:
4444  *  pgx    PgxWin *  The PGPLOT window context.
4445  * Output:
4446  *  unsigned  int    The width of the pixmap, or 0 if no pixmap currently
4447  *                   exists.
4448  */
4449 #ifdef __STDC__
pgx_pixmap_width(PgxWin * pgx)4450 unsigned pgx_pixmap_width(PgxWin *pgx)
4451 #else
4452 unsigned pgx_pixmap_width(pgx)
4453      PgxWin *pgx;
4454 #endif
4455 {
4456   if(!pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP))
4457     return 0;
4458   return pgx->state->geom.width;
4459 }
4460 
4461 /*.......................................................................
4462  * Return the height of the current pixmap.
4463  *
4464  * Input:
4465  *  pgx    PgxWin *  The PGPLOT window context.
4466  * Output:
4467  *  unsigned  int    The height of the pixmap, or 0 if no pixmap currently
4468  *                   exists.
4469  */
4470 #ifdef __STDC__
pgx_pixmap_height(PgxWin * pgx)4471 unsigned pgx_pixmap_height(PgxWin *pgx)
4472 #else
4473 unsigned pgx_pixmap_height(pgx)
4474      PgxWin *pgx;
4475 #endif
4476 {
4477   if(!pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP))
4478     return 0;
4479   return pgx->state->geom.height;
4480 }
4481 
4482 /*.......................................................................
4483  * Convert from window pixel coordinates to PGPLOT device coordinates.
4484  *
4485  * Input:
4486  *  pgx    PgxWin *  The PGPLOT window context.
4487  *  x,y       int    The X window pixel coordinates.
4488  * Input/Output:
4489  *  rbuf    float *  The return array for the X and Y device coordinates.
4490  * Output:
4491  *  return    int    0 - OK.
4492  *                   1 - Error.
4493  */
4494 #ifdef __STDC__
pgx_win2dev(PgxWin * pgx,int x,int y,float * rbuf)4495 int pgx_win2dev(PgxWin *pgx, int x, int y, float *rbuf)
4496 #else
4497 int pgx_win2dev(pgx, x, y, rbuf)
4498      PgxWin *pgx; int x; int y; float *rbuf;
4499 #endif
4500 {
4501   if(!rbuf) {
4502     fprintf(stderr, "pgx_win2dev: NULL rbuf[].\n");
4503     return 1;
4504   };
4505   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
4506     XPoint coord;
4507 /*
4508  * Convert from window coordinates to pixmap coordinates.
4509  */
4510     coord.x = x + pgx->scroll.x;
4511     coord.y = y + pgx->scroll.y;
4512 /*
4513  * Convert to PGPLOT device coordinates.
4514  */
4515     pgx_XPoint_to_xy(pgx, &coord, rbuf);
4516   } else {
4517     rbuf[0] = rbuf[1] = 0.0;
4518   };
4519   return 0;
4520 }
4521 
4522 /*.......................................................................
4523  * Convert from PGPLOT device coordinates to X window coordinates.
4524  *
4525  * Input:
4526  *  pgx    PgxWin *  The PGPLOT window context.
4527  *  rbuf    float *  The PGPLOT X and Y device coordinates.
4528  *                    rbuf[0] = X device coordinate.
4529  *                    rbuf[1] = Y device coordinate.
4530  * Input/Output:
4531  *  x,y       int *  The corresponding X window coordinates.
4532  * Output:
4533  *  return    int    0 - OK.
4534  *                   1 - Error.
4535  */
4536 #ifdef __STDC__
pgx_dev2win(PgxWin * pgx,float * rbuf,int * x,int * y)4537 int pgx_dev2win(PgxWin *pgx, float *rbuf, int *x, int *y)
4538 #else
4539 int pgx_dev2win(pgx, rbuf, x, y)
4540      PgxWin *pgx; float *rbuf; int *x; int *y;
4541 #endif
4542 {
4543   if(!rbuf || !x || !y) {
4544     fprintf(stderr, "pgx_dev2win: NULL %s.\n",
4545 	    !rbuf ? "rbuf[]" : (!x ? "x":"y"));
4546     return 1;
4547   };
4548   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
4549     XPoint coord;
4550 /*
4551  * Convert from PGPLOT device coordinates to pixmap coordinates.
4552  */
4553     pgx_xy_to_XPoint(pgx, rbuf, &coord);
4554 /*
4555  * Convert from pixmap coordinates to window coordinates.
4556  */
4557     *x = coord.x - pgx->scroll.x;
4558     *y = coord.y - pgx->scroll.y;
4559   } else {
4560     *x = *y = 0.0;
4561   };
4562   return 0;
4563 }
4564 
4565 /*.......................................................................
4566  * Convert from PGPLOT device coordinates to PGPLOT world coordinates.
4567  *
4568  * Note that use of this function requires that opcode 27 be enabled and
4569  * set up to update the world-coordinate scaling via pgx_set_world().
4570  *
4571  * Input:
4572  *  pgx    PgxWin *  The PGPLOT window context.
4573  * Input/Output:
4574  *  rbuf    float *  On input this should contain the PGPLOT device
4575  *                   x and y coordinates. On output it will contain the
4576  *                   corresponding world coordinates.
4577  * Output:
4578  *  return    int    0 - OK.
4579  *                   1 - Error.
4580  */
4581 #ifdef __STDC__
pgx_dev2world(PgxWin * pgx,float * rbuf)4582 int pgx_dev2world(PgxWin *pgx, float *rbuf)
4583 #else
4584 int pgx_dev2world(pgx, rbuf)
4585      PgxWin *pgx; float *rbuf;
4586 #endif
4587 {
4588   if(!rbuf) {
4589     fprintf(stderr, "pgx_dev2world: NULL rbuf[].\n");
4590     return 1;
4591   };
4592   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
4593     XWworld *world = &pgx->state->world;
4594 /*
4595  * Convert device coordinates to world coordinates.
4596  */
4597     rbuf[0] = (rbuf[0] - world->xoff) / world->xdiv;
4598     rbuf[1] = (rbuf[1] - world->yoff) / world->ydiv;
4599   } else {
4600     rbuf[0] = rbuf[1] = 0.0;
4601   };
4602   return 0;
4603 }
4604 
4605 /*.......................................................................
4606  * Convert from PGPLOT world coordinates to PGPLOT device coordinates.
4607  *
4608  * Note that use of this function requires that opcode 27 be enabled and
4609  * set up to update the world-coordinate scaling via pgx_set_world().
4610  *
4611  * Input:
4612  *  pgx    PgxWin *  The PGPLOT window context.
4613  * Input/Output:
4614  *  rbuf    float *  On input this should contain the PGPLOT world
4615  *                   x and y coordinates. On output it will contain the
4616  *                   corresponding PGPLOT device coordinates.
4617  * Output:
4618  *  return    int    0 - OK.
4619  *                   1 - Error.
4620  */
4621 #ifdef __STDC__
pgx_world2dev(PgxWin * pgx,float * rbuf)4622 int pgx_world2dev(PgxWin *pgx, float *rbuf)
4623 #else
4624 int pgx_world2dev(pgx, rbuf)
4625      PgxWin *pgx; float *rbuf;
4626 #endif
4627 {
4628   if(!rbuf) {
4629     fprintf(stderr, "pgx_world2dev: NULL rbuf[].\n");
4630     return 1;
4631   };
4632   if(pgx_ready(pgx, PGX_NEED_PGOPEN)) {
4633     XWworld *world = &pgx->state->world;
4634 /*
4635  * Convert world coordinates to device coordinates.
4636  */
4637     rbuf[0] = rbuf[0] * world->xdiv + world->xoff;
4638     rbuf[1] = rbuf[1] * world->ydiv + world->yoff;
4639   } else {
4640     rbuf[0] = rbuf[1] = 0.0;
4641   };
4642   return 0;
4643 }
4644 
4645 /*.......................................................................
4646  * Scroll a rectangular area vertically and/or horizontally.
4647  *
4648  * Input:
4649  *  pgx     PgxWin *  The PGPLOT window context.
4650  *  rbuf     float *  The array of float arguments sent by the PGPLOT
4651  *                    GREXEC() subroutine.
4652  */
4653 #ifdef __STDC__
pgx_scroll_rect(PgxWin * pgx,float * rbuf)4654 void pgx_scroll_rect(PgxWin *pgx, float *rbuf)
4655 #else
4656 void pgx_scroll_rect(pgx, rbuf)
4657      PgxWin *pgx; float *rbuf;
4658 #endif
4659 {
4660   if(pgx_ready(pgx, PGX_NEED_PGOPEN | PGX_NEED_PIXMAP | PGX_NEED_COLOR)) {
4661     XPoint blc, trc;     /* The bottom left and top right rectangle corners */
4662     XPoint blc_orig, trc_orig; /* The vertices of the rectangle to be copied */
4663     XPoint blc_dest, trc_dest; /* The vertices of the destination of the copy */
4664     int dx, dy;                /* The amounts to scroll right and down */
4665 /*
4666  * Convert the rectangle vertices from PGPLOT coordinates to X coordinates.
4667  */
4668     pgx_xy_to_XPoint(pgx, &rbuf[0], &blc);
4669     pgx_xy_to_XPoint(pgx, &rbuf[2], &trc);
4670 /*
4671  * Get the scroll offsets in X coordinates.
4672  */
4673     dx = pgx_nint(rbuf[4]);
4674     dy = pgx_nint(-rbuf[5]);
4675 /*
4676  * Selected parts of the pixmap will need to be erased by drawing an
4677  * opaque rectangle over them in the background color. Establish
4678  * the background color in the exposure graphical context to avoid
4679  * changing the current foreground color.
4680  */
4681     XSetForeground(pgx->display, pgx->expose_gc, pgx->color->pixel[0]);
4682 /*
4683  * If either scroll extent exceeds the length of the associated
4684  * axis, then fill the area with the background color.
4685  */
4686     if(abs(dx) > trc.x - blc.x || abs(dy) > blc.y - trc.y) {
4687 /*
4688  * Fill the rectangle in the pixmap.
4689  */
4690       XFillRectangle(pgx->display, pgx->pixmap, pgx->expose_gc, blc.x, trc.y,
4691 		     (unsigned)(trc.x-blc.x+1), (unsigned)(blc.y-trc.y+1));
4692     } else {
4693 /*
4694  * Calculate the vertices of the source and destination rectangles to
4695  * be copied.
4696  */
4697       blc_orig = blc_dest = blc;
4698       trc_orig = trc_dest = trc;
4699       if(dx > 0) {
4700 	trc_orig.x = trc.x - dx;
4701 	blc_dest.x = blc.x + dx;
4702       } else if(dx < 0) {
4703 	blc_orig.x = blc.x - dx;
4704 	trc_dest.x = trc.x + dx;
4705       };
4706       if(dy > 0) {
4707 	blc_orig.y = blc.y - dy;
4708 	trc_dest.y = trc.y + dy;
4709       } else if(dy < 0) {
4710 	trc_orig.y = trc.y - dy;
4711 	blc_dest.y = blc.y + dy;
4712       };
4713 /*
4714  * Constrain the coordinates to lie within the pixmap.
4715  */
4716       pgx_limit_pcoords(pgx, &blc_orig);
4717       pgx_limit_pcoords(pgx, &blc_dest);
4718       pgx_limit_pcoords(pgx, &trc_orig);
4719       pgx_limit_pcoords(pgx, &trc_dest);
4720 /*
4721  * Scroll the rectangle to its shifted location.
4722  */
4723       XCopyArea(pgx->display, pgx->pixmap, pgx->pixmap, pgx->expose_gc,
4724 		blc_orig.x, trc_orig.y,
4725 		trc_orig.x - blc_orig.x + 1,
4726 		blc_orig.y - trc_orig.y + 1,
4727 		blc_dest.x, trc_dest.y);
4728 /*
4729  * Clear the vacated area to the left or right of the copied area.
4730  */
4731       if(dx > 0) {
4732 	XFillRectangle(pgx->display, pgx->pixmap, pgx->expose_gc,
4733 		       blc.x, trc.y,
4734 		       (unsigned) dx,
4735 		       (unsigned) (blc.y - trc.y + 1));
4736       } else if(dx < 0) {
4737 	XFillRectangle(pgx->display, pgx->pixmap, pgx->expose_gc,
4738 		       trc_dest.x, trc.y,
4739 		       (unsigned) (-dx),
4740 		       (unsigned) (blc.y - trc.y + 1));
4741       };
4742 /*
4743  * Clear the vacated area above or below the copied area.
4744  */
4745       if(dy > 0) {
4746 	XFillRectangle(pgx->display, pgx->pixmap, pgx->expose_gc,
4747 		       blc.x, trc.y,
4748 		       (unsigned) (trc.x - blc.x + 1),
4749 		       (unsigned) dy);
4750       } else if(dy < 0) {
4751 	XFillRectangle(pgx->display, pgx->pixmap, pgx->expose_gc,
4752 		       blc.x, blc_dest.y,
4753 		       (unsigned) (trc.x - blc.x + 1),
4754 		       (unsigned) (-dy));
4755       };
4756     };
4757 /*
4758  * Record the extent of the modified part of the pixmap.
4759  */
4760     pgx_mark_modified(pgx, blc.x, blc.y, 1);
4761     pgx_mark_modified(pgx, trc.x, trc.y, 1);
4762   };
4763   return;
4764 }
4765 
4766 /*.......................................................................
4767  * Return the nearest integer to a given floating point number.
4768  *
4769  * Input:
4770  *  f    float   The floating point number to be rounded.
4771  * Output:
4772  *  return int   The nearest integer to f.
4773  */
4774 #ifdef __STDC__
pgx_nint(float f)4775 static int pgx_nint(float f)
4776 #else
4777 static int pgx_nint(f)
4778      float f;
4779 #endif
4780 {
4781   return (int) (f >= 0.0 ? (f + 0.5) : (f - 0.5));
4782 }
4783 
4784 /*.......................................................................
4785  * Find the parent window of a PGPLOT window. Note that we can't cache
4786  * this because reparenting window managers change the parent of
4787  * top-level windows.
4788  *
4789  * Input:
4790  *  pgx     PgxWin *  The PGPLOT window context.
4791  * Output:
4792  *  return  Window    The parent window, or None on error.
4793  */
4794 #ifdef __STDC__
pgx_parent_window(PgxWin * pgx)4795 Window pgx_parent_window(PgxWin *pgx)
4796 #else
4797 Window pgx_parent_window(pgx)
4798      PgxWin *pgx;
4799 #endif
4800 {
4801   Window root;
4802   Window parent;
4803   Window *children;
4804   unsigned int nchildren;
4805   if(!XQueryTree(pgx->display, pgx->window, &root, &parent,
4806 		 &children, &nchildren)) {
4807     fprintf(stderr, "pgx_parent_window: XQueryTree failed.\n");
4808     pgx->bad_device = 1;
4809     return None;
4810   };
4811 /*
4812  * Discard the unwanted list of children.
4813  */
4814   XFree(children);
4815   return parent;
4816 }
4817 
4818 /*.......................................................................
4819  * Replace an existing set of readonly colors with new color representations.
4820  *
4821  * Input:
4822  *  pgx           PgxWin *  The PGPLOT window context.
4823  *  ncol             int    The number of colors to redefine.
4824  * Input/Output:
4825  *  colors        XColor *  On input pass the array of ncol color
4826  *                          representations to allocate. On output this
4827  *                          will contain the newly allocated colors.
4828  *  pixels unsigned long *  The array of pixels corresponding to colors.
4829  *                          On calls when pgx->color->initialized is true
4830  *                          this should contain the pixels that are to be
4831  *                          replaced. On output it will contain the
4832  *                          allocated pixels.
4833  * Output:
4834  *  return           int    0 - OK.
4835  *                          1 - Error.
4836  */
4837 #ifdef __STDC__
pgx_readonly_colors(PgxWin * pgx,int ncol,XColor * colors,unsigned long * pixels)4838 static int pgx_readonly_colors(PgxWin *pgx, int ncol, XColor *colors,
4839 			     unsigned long *pixels)
4840 #else
4841 static int pgx_readonly_colors(pgx, ncol, colors, pixels)
4842      PgxWin *pgx; int ncol; XColor *colors; unsigned long *pixels;
4843 #endif
4844 {
4845   int ngot=0;    /* The number of colors acquired */
4846   int i;
4847 /*
4848  * Get aliases to objects in pgx.
4849  */
4850   Colormap cmap = pgx->color->cmap;
4851   Display *display = pgx->display;
4852 /*
4853  * No colors required?
4854  */
4855   if(ncol < 1)
4856     return 0;
4857 /*
4858  * First discard all but the first of the colors that we are replacing.
4859  * The first will be used as a fallback if the first pgx->color->nwork
4860  * entries of the colormap contain no shared colors.
4861  */
4862   if(pgx->color->initialized)
4863     XFreeColors(display, cmap, pixels+1, ncol-1, (long)0);
4864 /*
4865  * First ask for precise versions of the requested colors. If the
4866  * colormap is readonly, the X server will actually return the
4867  * nearest approximation. If the colormap is read/write XAllocColor
4868  * is documented to fail up if it can't find exactly the color that
4869  * one asks for (within the color resolution of the hardware)!
4870  * Mark those that can't be allocated by setting their flags to 0.
4871  */
4872   for(i=0; i<ncol; i++) {
4873     XColor *c = colors + i;
4874     if(XAllocColor(display, cmap, c))
4875       ngot++;
4876     else
4877       c->flags = 0;
4878   };
4879 /*
4880  * If we failed to allocate precise versions of any of the colors,
4881  * try to allocate approximate versions of the colors. Note that
4882  * the X server is supposed to do this for us if the colormap is
4883  * readonly, but not if it is read/write.
4884  */
4885   if(ngot < ncol) {
4886     XColor *last=NULL; /* The last color looked at while allocating colors */
4887     int napprox=0;     /* The number of approximate colors to choose from */
4888 /*
4889  * Get the work array and its size.
4890  */
4891     XColor *work = pgx->color->work;
4892     int nwork = pgx->color->nwork;
4893 /*
4894  * Ask the server for the first nwork colors in its colormap.
4895  */
4896     for(i=0; i<nwork; i++)
4897       work[i].pixel = i;
4898     XQueryColors(display, cmap, work, nwork);
4899 /*
4900  * Sort the colors into ascending order of red,green,blue intensities.
4901  */
4902     qsort(work, nwork, sizeof(work[0]), pgx_cmp_xcolor);
4903 /*
4904  * Attempt to allocate as many of the reported colors as possible,
4905  * being careful to ignore duplicate entries. Copy the napprox
4906  * allocated ones to the start of the work array.
4907  */
4908     for(i=0; i<nwork; i++) {
4909       XColor *c = work + i;
4910       if((!last || c->red != last->red || c->green != last->green ||
4911 	  c->blue != last->blue)) {
4912 	if(XAllocColor(display, cmap, c)) {
4913 	  if(i != napprox)
4914 	    work[napprox] = *c;
4915 	  napprox++;
4916 	};
4917 	last = c;
4918       };
4919     };
4920 /*
4921  * Fill in the outstanding PGPLOT colors from those remaining in the work
4922  * array. Note that we must also re-allocate each of the chosen colors
4923  * so that it can be free'd as many times as it appears in our color table.
4924  */
4925     for(i=0; i < ncol; i++) {
4926       XColor *c = colors + i;
4927       if(!c->flags) {
4928 	if(pgx_nearest_color(work, napprox, c) ||
4929 	   !XAllocColor(display, cmap, c))
4930 	  break;
4931 	else
4932 	  ngot++;
4933       };
4934     };
4935 /*
4936  * Release the work array of colors (note that the colors that were
4937  * chosen above were duplicately allocated, and X uses a reference
4938  * counting scheme for colormap entries, so this won't invalidate them).
4939  */
4940     for(i=0; i<napprox; i++) {
4941       XColor *c = work + i;
4942       XFreeColors(display, cmap, &c->pixel, 1, 0);
4943     };
4944   };
4945 /*
4946  * Did we fail to get all of the required colors?
4947  */
4948   if(ngot < ncol) {
4949 /*
4950  * If we were (re-)allocating the whole colortable, discard all colors
4951  * allocated above the first that we failed to get and reduce ncol to
4952  * account for this.
4953  */
4954     if(pixels==pgx->color->pixel && ncol >= pgx->color->npixel) {
4955       for(ngot=0; ngot<ncol && colors[ngot].flags; ngot++)
4956 	;
4957       for(i=ngot; i<ncol; i++)
4958 	XFreeColors(display, cmap, &colors[i].pixel, 1, 0);
4959 /*
4960  * If there are no colors at all, then there isn't anything that
4961  * we can do. Note that if we are allocating from the default
4962  * colormap of the screen, BlackPixel() and WhitePixel() are
4963  * guaranteed to be available and shareable, so it is only
4964  * private colormaps that should suffer this problem. In private
4965  * colormaps there is no equivalent of BlackPixel() or WhitePixel(),
4966  * so we have nothing to fall back on.
4967  */
4968       if(ngot < 2) {
4969 	for(i=0; i<ngot; i++)
4970 	  XFreeColors(display, cmap, &colors[i].pixel, 1, 0);
4971 	fprintf(stderr, "%s: There aren't any colors available.\n", PGX_IDENT);
4972 	return 1;
4973       };
4974 /*
4975  * Record the dimensions of the resized color table.
4976  */
4977       pgx->color->npixel = ngot;
4978       pgx->color->ncol = ngot;
4979 /*
4980  * If we were replacing entries, then the size of the color table has already
4981  * been fixed, so we need to fill in the missing colors. Since we may not have
4982  * managed to allocate any colors, find out the color representation of the
4983  * first of the colors that we were replacing and reallocate for each of
4984  * the missing slots. Note that we were careful not to free this color
4985  * above, so it is supposedly guaranteed to still be allocatable as a
4986  * shared color.
4987  */
4988     } else {
4989 /*
4990  * Get the color representation of the fallback color.
4991  */
4992       XColor fallback;   /* The color being used as a fallback */
4993       fallback.pixel = pixels[0];
4994       XQueryColor(display, cmap, &fallback);
4995 /*
4996  * Re-allocate the fallback entry for each missing color.
4997  */
4998       for(i=0; i<ncol; i++) {
4999 	XColor *c = colors + i;
5000 	if(!c->flags) {
5001 	  (void) XAllocColor(display, cmap, &fallback);
5002 	  *c = fallback;
5003 	};
5004       };
5005 /*
5006  * We now have the requested number of colors, albeit with
5007  * unexpected colors.
5008  */
5009       ngot = ncol;
5010     };
5011   };
5012 /*
5013  * Release the unfree'd first entry of the replaced colors.
5014  */
5015   if(pgx->color->initialized)
5016     XFreeColors(display, cmap, pixels, 1, 0);
5017 /*
5018  * Record the new pixel indexes.
5019  */
5020   for(i=0; i<ngot; i++)
5021     pixels[i] = colors[i].pixel;
5022   return 0;
5023 }
5024 
5025 /*.......................................................................
5026  * This is a qsort comparison function used to sort an array XColor
5027  * entries into ascending order. The colors are sorted into ascending
5028  * order using red as the primary sort key, green as the second
5029  * sort key and blue as the third sort key.
5030  */
5031 #ifdef __STDC__
pgx_cmp_xcolor(const void * va,const void * vb)5032 static int pgx_cmp_xcolor(const void *va, const void *vb)
5033 #else
5034 static int pgx_cmp_xcolor(va, vb)
5035      char *va; char *vb;
5036 #endif
5037 {
5038   XColor *ca = (XColor *) va;
5039   XColor *cb = (XColor *) vb;
5040 /*
5041  * Compare the red components.
5042  */
5043   if(ca->red < cb->red)
5044     return -1;
5045   else if(ca->red > cb->red)
5046     return 1;
5047 /*
5048  * The red components are the same, so compare the green components.
5049  */
5050   if(ca->green < cb->green)
5051     return -1;
5052   else if(ca->green > cb->green)
5053     return 1;
5054 /*
5055  * Both the red components and the green components are the same,
5056  * so compare the blue components.
5057  */
5058   if(ca->blue < cb->blue)
5059     return -1;
5060   else if(ca->blue > cb->blue)
5061     return 1;
5062 /*
5063  * The colors are identical.
5064  */
5065   return 0;
5066 }
5067 
5068 /*.......................................................................
5069  * Find the nearest color in colors[] and assign it to *c.
5070  *
5071  * Input:
5072  *  colors  XColor *  An array of ncol colors from which to choose.
5073  *                    Those whose flags are 0 are used.
5074  *  ncol       int    The number of entries in colors[].
5075  * Input/Output:
5076  *  c       XColor *  On input this should contain the desired color.
5077  *                    On output it will contain the nearest unused color
5078  *                    found in colors[].
5079  * Output:
5080  *  return     int    0 - OK.
5081  *                    1 - There are no colors left.
5082  */
5083 #ifdef __STDC__
pgx_nearest_color(XColor * colors,int ncol,XColor * c)5084 static int pgx_nearest_color(XColor *colors, int ncol, XColor *c)
5085 #else
5086 static int pgx_nearest_color(colors, ncol, c)
5087      XColor *colors; int ncol; XColor *c;
5088 #endif
5089 {
5090   XColor *best=NULL;  /* The nearest color found so far */
5091   float residual=0;   /* The square color residual of best compared to c */
5092   int first = 1;      /* True until residual has been initialized */
5093   int i;
5094 /*
5095  * Find the unused entry in colors[] that is nearest to the requested color.
5096  */
5097   for(i=0; i<ncol; i++) {
5098     XColor *xc = colors + i;
5099 /*
5100  * Compute the square residual between the two colors.
5101  */
5102     float dr = c->red - xc->red;
5103     float dg = c->green - xc->green;
5104     float db = c->blue - xc->blue;
5105     float r = dr * dr + dg * dg + db * db;
5106     if(r < residual || first) {
5107       first = 0;
5108       residual = r;
5109       best = xc;
5110     };
5111   };
5112 /*
5113  * Not found?
5114  */
5115   if(!best)
5116     return 1;
5117 /*
5118  * Copy the color to the return container.
5119  */
5120   *c = *best;
5121   return 0;
5122 }
5123