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