1 /* This file is part of the GNU plotutils package. Copyright (C) 1995,
2 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3
4 The GNU plotutils package is free software. You may redistribute it
5 and/or modify it under the terms of the GNU General Public License as
6 published by the Free Software foundation; either version 2, or (at your
7 option) any later version.
8
9 The GNU plotutils package is distributed in the hope that it will be
10 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with the GNU plotutils package; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17 Boston, MA 02110-1301, USA. */
18
19 /* This implementation is for XPlotters. When invoked, it pops up a
20 plotting window on the default screen of the specified X display. When
21 the corresponding closepl method is invoked, the window is `spun off',
22 i.e., is managed thenceforth by a forked-off child process. */
23
24 /* This file also contains the internal functions _pl_y_maybe_get_new_colormap
25 and _pl_y_maybe_handle_x_events. They override the corresponding functions
26 in the XDrawablePlotter superclass, which are no-ops.
27
28 The function _pl_y_maybe_handle_x_events is very important: it contains our
29 hand-crafted loop for processing X events, which is called by an
30 XPlotter after any libplot drawing operation is invoked on it. */
31
32 #include "sys-defines.h"
33 #include "extern.h"
34
35 /* song and dance to define struct timeval, and declare select() */
36 #ifdef HAVE_SYS_TIME_H
37 #include <sys/time.h> /* for struct timeval */
38 #endif
39 #ifdef HAVE_SYS_SELECT_H
40 #include <sys/select.h> /* AIX needs this */
41 #endif
42 #ifdef HAVE_SYS_TYPES_H
43 #include <sys/types.h> /* for struct fdset, FD_ZERO, FD_SET */
44 #endif
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h> /* for select() */
47 #endif
48
49 /* Mutex for locking _xplotters[] and _xplotters_len. Defined in
50 y_defplot.c. */
51 #ifdef PTHREAD_SUPPORT
52 #ifdef HAVE_PTHREAD_H
53 extern pthread_mutex_t _xplotters_mutex;
54 #endif
55 #endif
56
57 /* fake app name, effectively our argv[0] */
58 #define XPLOT_APP_NAME "xplot"
59
60 /* app class, use for specifying resources */
61 #define XPLOT_APP_CLASS "Xplot"
62
63 /* Fallback resources for the preceding X11 class. There are no
64 user-specifiable X resources, except for the geometry.
65
66 The default size of the plotting window is set here, as a default X
67 resource, rather than in y_defplot.c. Users may override the default by
68 specifying a geometry in their .Xdefaults files (by specifying the
69 Xplot.geometry or xplot.geometry resource). This is equivalent to
70 specifying the Plotter parameter BITMAPSIZE. */
71
72 static const String _xplot_fallback_resources[] =
73 {
74 (String)"Xplot*geometry: 570x570",
75 (String)NULL
76 };
77
78 /* Support for command-line mimicry. Our fake argument vector,
79 _fake_argv[], needs space for our fake application name,
80 i.e. XPLOT_APP_NAME, the three options "-display", "-geometry", "-bg",
81 each with an argument, and a final NULL. */
82 #define MAX_FAKE_ARGV_LENGTH 8
83
84 /* Translations for the canvas widget, before and after the Plotter is
85 closed, i.e. before and after forking. (After forking, translate any
86 pressing of the `q' key, and any mouse click, to `Foldup'.) */
87 static const String _xplot_translations_before_forking =
88 #ifdef USE_MOTIF
89 (String)"<Btn2Down>: ProcessDrag()";
90 #else
91 (String)"";
92 #endif
93
94 static const String _xplot_translations_after_forking =
95 #ifdef USE_MOTIF
96 (String)"<Btn1Down>: Foldup()\n\
97 <Btn2Down>: ProcessDrag()\n\
98 <Btn3Down>: Foldup()\n\
99 <Key>Q: Foldup()\n\
100 <Key>q: Foldup()";
101 #else
102 (String)"<Btn1Down>: Foldup()\n\
103 <Btn3Down>: Foldup()\n\
104 <Key>Q: Foldup()\n\
105 <Key>q: Foldup()";
106 #endif
107
108 /* forward references */
109 static bool _bitmap_size_ok (const char *bitmap_size_s);
110 static void Foldup (Widget widget, XEvent *event, String *params, Cardinal *num_params);
111
112 #ifndef HAVE_STRERROR
113 static char * _plot_strerror (int errnum);
114 #define strerror _plot_strerror
115 #endif
116
117 /* This is called by the child process produced in y_closepl.c, immediately
118 after forking takes place. It alters the translation table for the
119 canvas widget so that Foldup() will be invoked when `q' is pressed or a
120 mouse click is seen. */
121 void
_pl_y_set_data_for_quitting(S___ (Plotter * _plotter))122 _pl_y_set_data_for_quitting (S___(Plotter *_plotter))
123 {
124 Arg wargs[1]; /* a lone werewolf */
125
126 #ifdef USE_MOTIF
127 XtSetArg (wargs[0], XmNtranslations,
128 XtParseTranslationTable(_xplot_translations_after_forking));
129 #else
130 XtSetArg (wargs[0], XtNtranslations,
131 XtParseTranslationTable(_xplot_translations_after_forking));
132 #endif
133 XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
134 }
135
136 /* Foldup() is called by the Label widget when `q' is pressed or a mouse
137 click is seen, provided that closepl() has previously been invoked. In
138 that case the spun-off window disappears (we destroy the parent widget,
139 and being a forked-off child process managing it, we exit). */
140
141 static void
Foldup(Widget widget,XEvent * event,String * params,Cardinal * num_params)142 Foldup (Widget widget, XEvent *event, String *params, Cardinal *num_params)
143 {
144 Display *dpy;
145
146 dpy = XtDisplay (widget);
147 XtDestroyWidget (XtParent (widget)); /* destroy toplevel widget */
148 XFlush (dpy); /* flush X output buffer */
149 exit (EXIT_SUCCESS);
150 }
151
152 /* Application context-specific action table. */
153 static const XtActionsRec _xplot_actions[] =
154 {
155 {(String)"Foldup", Foldup},
156 };
157
158 bool
_pl_y_begin_page(S___ (Plotter * _plotter))159 _pl_y_begin_page (S___(Plotter *_plotter))
160 {
161 Arg wargs[10]; /* werewolves */
162 Dimension window_height, window_width;
163 Screen *screen_struct; /* screen structure */
164 String fake_argv[MAX_FAKE_ARGV_LENGTH];
165 const char *double_buffer_s;
166 int fake_argc;
167 int screen; /* screen number */
168
169 /* To permit openpl..closepl to be invoked repeatedly, we don't use the
170 convenience routine XtAppInitialize(), since that function starts out
171 by calling XtToolkitInitialize(), which shouldn't be called more than
172 once. (At least, in early versions of X11; in X11R6 calling it more
173 than once is OK.) Instead, we call XtToolkitInitialize() when the
174 first XPlotter is created; see y_defplot.c.
175
176 On every invocation of openpl() we call the other four functions that
177 XtAppInitialize would call: XtCreateApplicationContext,
178 XtAppSetFallbackResources, XtOpenDisplay, and XtAppCreateShell. That
179 sets up a new application context each time openpl() is called, which
180 looks wasteful. But since each openpl..closepl will yield a window
181 managed by a forked-off process, it's appropriate. */
182
183 /* create new application context for this Plotter page */
184 _plotter->y_app_con = XtCreateApplicationContext();
185 if (_plotter->y_app_con == (XtAppContext)NULL)
186 {
187 _plotter->error (R___(_plotter) "an X application context could not be created");
188 return false;
189 }
190 /* set fallback resources to be used by canvas widget (currently, only
191 the window size); specific to application context */
192 XtAppSetFallbackResources (_plotter->y_app_con,
193 (String *)_xplot_fallback_resources);
194
195 /* register an action table [currently containing only
196 "Foldup"->Foldup(), see above]; specific to application context */
197 XtAppAddActions (_plotter->y_app_con, (XtActionsRec *)_xplot_actions,
198 XtNumber (_xplot_actions));
199
200 /* punch options and parameters into our fake command line, beginning
201 with the fake app name */
202 fake_argc = 0;
203 fake_argv[fake_argc++] = (String)XPLOT_APP_NAME;
204
205 /* take argument of the "-display" option from the DISPLAY parameter */
206 {
207 const char *display_s;
208
209 display_s = (char *)_get_plot_param (_plotter->data, "DISPLAY");
210 if (display_s == NULL || *display_s == '\0')
211 {
212 _plotter->error (R___(_plotter)
213 "the Plotter could not be opened, as the DISPLAY parameter is null");
214 return false;
215 }
216 fake_argv[fake_argc++] = (String)"-display";
217 fake_argv[fake_argc++] = (String)display_s;
218 }
219
220 /* Take argument of "-geometry" option from BITMAPSIZE parameter, if set;
221 otherwise size will be taken from Xplot.geometry. Fallback size is
222 specified at head of this file. */
223 {
224 char *bitmap_size_s;
225
226 bitmap_size_s = (char *)_get_plot_param (_plotter->data, "BITMAPSIZE");
227 if (bitmap_size_s && _bitmap_size_ok (bitmap_size_s))
228 {
229 fake_argv[fake_argc++] = (String)"-geometry";
230 fake_argv[fake_argc++] = (String)bitmap_size_s;
231 }
232 }
233
234 /* Take argument of "-bg" option from BG_COLOR parameter, if set;
235 otherwise use default color (white). */
236 {
237 const char *bg_color_s;
238
239 bg_color_s = (char *)_get_plot_param (_plotter->data, "BG_COLOR");
240 if (bg_color_s)
241 {
242 plColor color;
243 char rgb[8]; /* enough room for "#FFFFFF", incl. NUL */
244
245 if (_string_to_color (bg_color_s, &color, _plotter->data->color_name_cache))
246 /* color is in our database */
247 {
248 if (_plotter->data->emulate_color)
249 /* replace by grayscale approximation */
250 {
251 int gray;
252
253 gray = _grayscale_approx (color.red, color.green, color.blue);
254 sprintf (rgb, "#%02X%02X%02X", gray, gray, gray);
255 }
256 else
257 sprintf (rgb, "#%02X%02X%02X",
258 color.red, color.green, color.blue);
259 bg_color_s = rgb;
260 }
261 else
262 /* color is not in our database */
263 {
264 if (_plotter->x_bg_color_warning_issued == false)
265 {
266 char *buf;
267
268 buf = (char *)_pl_xmalloc (strlen (bg_color_s) + 100);
269 sprintf (buf, "substituting \"white\" for undefined background color \"%s\"",
270 bg_color_s);
271 _plotter->warning (R___(_plotter) buf);
272 free (buf);
273 _plotter->x_bg_color_warning_issued = true;
274
275 bg_color_s = "white";
276 }
277 }
278
279 fake_argv[fake_argc++] = (String)"-bg";
280 fake_argv[fake_argc++] = (String)bg_color_s;
281 }
282 }
283
284 /* append final NULL (some X implementations need this) */
285 fake_argv[fake_argc] = (String)NULL;
286
287 /* open new connection to the X display, using fake argv */
288 _plotter->x_dpy =
289 XtOpenDisplay (_plotter->y_app_con,
290 /* display_string = NULL, so take from fake commandline */
291 (String)NULL,
292 /* application name = NULL, so take from fake commandline */
293 (String)NULL,
294 /* application class */
295 (String)XPLOT_APP_CLASS,
296 /* application-specific commandline parsetable (for
297 XrmParseCommand), used in setting display resources */
298 NULL, (Cardinal)0,
299 /* pass fake command-line (contains a fake argv[0] to
300 specify app name, and besides "-display", options
301 may include "-geometry", "-bg") */
302 &fake_argc, fake_argv);
303 if (_plotter->x_dpy == (Display *)NULL)
304 {
305 char *display_s;
306
307 display_s = (char *)_get_plot_param (_plotter->data, "DISPLAY");
308 if (display_s == NULL) /* shouldn't happen */
309 _plotter->error (R___(_plotter)
310 "the X Window System display could not be opened, as it is null");
311 else
312 {
313 char *buf;
314
315 buf = (char *)_pl_xmalloc(strlen(display_s) + 1 + 50);
316 sprintf (buf, "the X Window System display \"%s\" could not be opened",
317 display_s);
318 _plotter->error (R___(_plotter) buf);
319 free (buf);
320 }
321 return false;
322 }
323
324 /* display was opened, so determine its default screen, visual, colormap */
325 screen = DefaultScreen (_plotter->x_dpy);
326 screen_struct = ScreenOfDisplay (_plotter->x_dpy, screen);
327 _plotter->x_visual = DefaultVisualOfScreen (screen_struct);
328 _plotter->x_cmap = DefaultColormapOfScreen (screen_struct);
329 _plotter->x_cmap_type = X_CMAP_ORIG; /* original cmap (not a private one) */
330
331 /* find out how long polylines can get on this X display */
332 _plotter->x_max_polyline_len = XMaxRequestSize(_plotter->x_dpy) / 2;
333
334 /* For every invocation of openpl(), we create a toplevel Shell widget,
335 associated with default screen of the opened display. (N.B. could
336 vary name of app instance; also select a non-default colormap by
337 setting a value for XtNcolormap.) */
338 XtSetArg(wargs[0], XtNscreen, screen_struct);
339 XtSetArg(wargs[1], XtNargc, fake_argc);
340 XtSetArg(wargs[2], XtNargv, fake_argv);
341 _plotter->y_toplevel = XtAppCreateShell(NULL, /* name of app instance */
342 (String)XPLOT_APP_CLASS, /* app class */
343 applicationShellWidgetClass,
344 _plotter->x_dpy, /* x_dpy to get resources from */
345 /* pass XtNscreen resource, and also fake
346 command-line, to get resources from
347 (options may include "-display"
348 [redundant], and "-geometry", "-bg") */
349 wargs, (Cardinal)3);
350
351 /* Create drawing canvas (a Label widget) as child of toplevel Shell
352 widget. Set many obscure spacing parameters to zero, so that origin
353 of bitmap will coincide with upper left corner of window. */
354 #ifdef USE_MOTIF
355 XtSetArg(wargs[0], XmNmarginHeight, (Dimension)0);
356 XtSetArg(wargs[1], XmNmarginWidth, (Dimension)0);
357 XtSetArg(wargs[2], XmNmarginLeft, (Dimension)0);
358 XtSetArg(wargs[3], XmNmarginRight, (Dimension)0);
359 XtSetArg(wargs[4], XmNmarginTop, (Dimension)0);
360 XtSetArg(wargs[5], XmNmarginBottom, (Dimension)0);
361 XtSetArg(wargs[6], XmNshadowThickness, (Dimension)0);
362 XtSetArg(wargs[7], XmNhighlightThickness, (Dimension)0);
363 _plotter->y_canvas = XtCreateManagedWidget ((String)"", xmLabelWidgetClass,
364 _plotter->y_toplevel,
365 wargs, (Cardinal)8);
366 #else
367 XtSetArg(wargs[0], XtNinternalHeight, (Dimension)0);
368 XtSetArg(wargs[1], XtNinternalWidth, (Dimension)0);
369 _plotter->y_canvas = XtCreateManagedWidget ((String)"", labelWidgetClass,
370 _plotter->y_toplevel,
371 wargs, (Cardinal)2);
372 #endif
373
374 /* realize both widgets */
375 XtRealizeWidget (_plotter->y_toplevel);
376
377 /* replace the Label widget's default translations by ours [see above;
378 our default is no translations at all, with a nod to Motif] */
379 #ifdef USE_MOTIF
380 XtSetArg (wargs[0], XmNtranslations,
381 XtParseTranslationTable(_xplot_translations_before_forking));
382 #else
383 XtSetArg (wargs[0], XtNtranslations,
384 XtParseTranslationTable(_xplot_translations_before_forking));
385 #endif
386 XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
387
388 /* get Label widget's window; store it in Plotter struct as
389 `drawable #2' */
390 _plotter->x_drawable2 = (Drawable)XtWindow(_plotter->y_canvas);
391
392 /* get the window size that was actually chosen, store it */
393 #ifdef USE_MOTIF
394 XtSetArg (wargs[0], XmNwidth, &window_width);
395 XtSetArg (wargs[1], XmNheight, &window_height);
396 #else
397 XtSetArg (wargs[0], XtNwidth, &window_width);
398 XtSetArg (wargs[1], XtNheight, &window_height);
399 #endif
400 XtGetValues (_plotter->y_canvas, wargs, (Cardinal)2);
401 _plotter->data->imin = 0;
402 _plotter->data->imax = (int)window_width - 1;
403 /* note flipped-y convention for this device: min > max */
404 _plotter->data->jmin = (int)window_height - 1;
405 _plotter->data->jmax = 0;
406
407 /* compute the NDC to device-frame affine map, set it in Plotter */
408 _compute_ndc_to_device_map (_plotter->data);
409
410 /* request backing store for Label widget's window */
411 if (DoesBackingStore(screen_struct))
412 {
413 XSetWindowAttributes attributes;
414 unsigned long value_mask;
415
416 attributes.backing_store = Always;
417 value_mask = CWBackingStore;
418 XChangeWindowAttributes (_plotter->x_dpy, (Window)_plotter->x_drawable2,
419 value_mask, &attributes);
420 }
421
422 /* determine whether to use double buffering */
423 _plotter->x_double_buffering = X_DBL_BUF_NONE;
424 double_buffer_s = (const char *)_get_plot_param (_plotter->data,
425 "USE_DOUBLE_BUFFERING");
426
427 /* backward compatibility: "fast" now means the same as "yes" */
428 if (strcmp (double_buffer_s, "fast") == 0)
429 double_buffer_s = "yes";
430
431 #ifdef HAVE_X11_EXTENSIONS_XDBE_H
432 #ifdef HAVE_DBE_SUPPORT
433 if (strcmp (double_buffer_s, "yes") == 0)
434 /* check whether X server supports DBE extension */
435 {
436 int major_version, minor_version;
437 int one = 1; /* number of screens to look at */
438 XdbeScreenVisualInfo *sv_info;
439
440 if (XdbeQueryExtension (_plotter->x_dpy, &major_version, &minor_version)
441 && (sv_info = XdbeGetVisualInfo (_plotter->x_dpy,
442 /* 2nd arg specifies screen */
443 &_plotter->x_drawable2,
444 &one)) != NULL)
445 /* server supports DBE extension; for screen, a list of
446 visuals / depths / performance hints was returned */
447 {
448 bool ok = false;
449 int i, num_visuals = sv_info->count;
450 XdbeVisualInfo *vis_info = sv_info->visinfo;
451 VisualID visual_id = XVisualIDFromVisual (_plotter->x_visual);
452
453 /* See whether default visual supports double buffering. If not,
454 could invoke XGetVisualInfo() to check the depth and perflevel
455 of each visual that does, and select the `best' one. (Would
456 also need to call XCreateColormap() to create a colormap of
457 that visual type. When using the default visual we can use
458 the default colormap, but when not, we don't have that
459 luxury.)
460
461 Maybe someday... That enhancement would be important for Xsgi,
462 which typically has a default 8-plane PseudoColor visual that
463 does _not_ support double buffering, and various other
464 visuals, including some 8-plane and 12-plane ones that do (and
465 some 15-plane and 24-plane ones that don't). */
466
467 for (i = 0; i < num_visuals; i++)
468 /* check visual ID for each visual in list */
469 if (vis_info[i].visual == visual_id) /* matches the default */
470 {
471 ok = true; /* default visual is OK */
472 break;
473 }
474 XdbeFreeVisualInfo (sv_info);
475 if (ok)
476 /* allocate back buffer, to serve as our graphics buffer;
477 save it as `x_drawable3' */
478 {
479 _plotter->x_drawable3 =
480 XdbeAllocateBackBufferName (_plotter->x_dpy,
481 _plotter->x_drawable2,
482 (XdbeSwapAction)XdbeUndefined);
483 /* set double buffering type in Plotter structure */
484 _plotter->x_double_buffering = X_DBL_BUF_DBE;
485 }
486 }
487 }
488 #endif /* HAVE_DBE_SUPPORT */
489 #endif /* HAVE_X11_EXTENSIONS_XDBE_H */
490
491 #ifdef HAVE_X11_EXTENSIONS_MULTIBUF_H
492 #ifdef HAVE_MBX_SUPPORT
493 if (_plotter->x_double_buffering == X_DBL_BUF_NONE
494 && strcmp (double_buffer_s, "yes") == 0)
495 /* check whether X server supports the (obsolete) MBX extension, as a
496 substitute for DBE */
497 {
498 int event_base, error_base;
499 int major_version, minor_version;
500
501 if (XmbufQueryExtension (_plotter->x_dpy, &event_base, &error_base)
502 && XmbufGetVersion (_plotter->x_dpy, &major_version, &minor_version))
503 /* server supports MBX extension */
504 {
505 Multibuffer multibuf[2];
506 int num;
507
508 num = XmbufCreateBuffers (_plotter->x_dpy,
509 (Window)_plotter->x_drawable2, 2,
510 MultibufferUpdateActionUndefined,
511 MultibufferUpdateHintFrequent,
512 multibuf);
513 if (num == 2)
514 /* Yow, got a pair of multibuffers. We'll write graphics to
515 the first (`x_drawable3'), and interchange them on each
516 erase(). See y_erase.c. */
517 {
518 _plotter->x_drawable3 = multibuf[0];
519 _plotter->y_drawable4 = multibuf[1];
520 /* set double buffering type in Plotter structure */
521 _plotter->x_double_buffering = X_DBL_BUF_MBX;
522 }
523 else
524 _plotter->warning (R___(_plotter)
525 "X server refuses to support multibuffering");
526 }
527 }
528 #endif /* HAVE_MBX_SUPPORT */
529 #endif /* HAVE_X11_EXTENSIONS_MULTIBUF_H */
530
531 if (_plotter->x_double_buffering == X_DBL_BUF_NONE)
532 /* user didn't request double buffering, or did but special support for
533 double buffering isn't contained in the X server */
534 {
535 Pixmap bg_pixmap;
536
537 /* create background pixmap for Label widget; 2nd arg (window) is
538 only used for determining the screen */
539 bg_pixmap = XCreatePixmap(_plotter->x_dpy,
540 _plotter->x_drawable2,
541 (unsigned int)window_width,
542 (unsigned int)window_height,
543 (unsigned int)PlanesOfScreen(screen_struct));
544 /* If user requested double buffering but the server doesn't support
545 it, we'll double buffer `by hand', and this pixmap will be the one
546 (of two) into which we'll draw. If user didn't request double
547 buffering, we'll use it as the 2nd of two drawables into which
548 we'll draw, the other being the window. */
549 if (strcmp (double_buffer_s, "yes") == 0)
550 {
551 _plotter->x_drawable3 = (Drawable)bg_pixmap;
552 _plotter->x_double_buffering = X_DBL_BUF_BY_HAND;
553 }
554 else
555 {
556 _plotter->x_drawable1 = (Drawable)bg_pixmap;
557 _plotter->x_double_buffering = X_DBL_BUF_NONE;
558 }
559 }
560
561 /* add X GC's to drawing state (which was constructed by openpl() before
562 begin_page() was called), so we can at least fill with solid color */
563 _pl_x_add_gcs_to_first_drawing_state (S___(_plotter));
564
565 /* If not double-buffering, clear both pixmap and window by filling them
566 with the drawing state's background color, via XFillRectangle. If
567 double buffering, do something similar (see y_erase.c). */
568 _pl_y_erase_page (S___(_plotter));
569
570 /* If double buffering, must invoke `erase' one more time to clear both
571 graphics buffer and window, since what `erase' does in that case is
572 (1) copy the graphics buffer to window, and (2) clear the graphics
573 buffer. */
574 if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
575 _pl_y_erase_page (S___(_plotter));
576
577 if (_plotter->x_double_buffering == X_DBL_BUF_NONE
578 || _plotter->x_double_buffering == X_DBL_BUF_BY_HAND)
579 /* have a pixmap, so install it as Label widget's background pixmap */
580 {
581 Pixmap bg_pixmap;
582
583 bg_pixmap = ((_plotter->x_double_buffering == X_DBL_BUF_BY_HAND) ?
584 _plotter->x_drawable3 : _plotter->x_drawable1);
585 #ifdef USE_MOTIF
586 XtSetArg (wargs[0], XmNlabelPixmap, bg_pixmap);
587 XtSetArg (wargs[1], XmNlabelType, XmPIXMAP);
588 XtSetValues (_plotter->y_canvas, wargs, (Cardinal)2);
589 #else
590 XtSetArg (wargs[0], XtNbitmap, bg_pixmap);
591 XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
592 #endif
593 }
594
595 /* do an XSync on the display (this will cause the background color to
596 show up if it hasn't already) */
597 _pl_x_flush_output (S___(_plotter));
598
599 /* Note: at this point the drawing state, which we added X GC's to, a few
600 lines above, won't be ready for drawing graphics, since it won't
601 contain an X font or meaningful line width. To retrieve an X font and
602 set the line width, user will need to invoke space() after openpl(). */
603
604 return true;
605 }
606
607 static bool
_bitmap_size_ok(const char * bitmap_size_s)608 _bitmap_size_ok (const char *bitmap_size_s)
609 {
610 int width, height;
611
612 if (bitmap_size_s
613 /* should parse this better */
614 && (sscanf (bitmap_size_s, "%dx%d", &width, &height) == 2)
615 && (width > 0) && (height > 0))
616 return true;
617 else
618 return false;
619 }
620
621 /* This is the XPlotter-specific version of the _maybe_get_new_colormap()
622 method, which is invoked when a Plotter's original colormap fills up.
623 It overrides the XDrawable-specific version, which is a no-op. */
624 void
_pl_y_maybe_get_new_colormap(S___ (Plotter * _plotter))625 _pl_y_maybe_get_new_colormap (S___(Plotter *_plotter))
626 {
627 Colormap new_pl_x_cmap;
628
629 /* sanity check */
630 if (_plotter->x_cmap_type != X_CMAP_ORIG)
631 return;
632
633 _plotter->warning (R___(_plotter)
634 "color supply low, switching to private colormap");
635 new_pl_x_cmap = XCopyColormapAndFree (_plotter->x_dpy, _plotter->x_cmap);
636
637 if (new_pl_x_cmap == 0)
638 /* couldn't create colormap */
639 {
640 _plotter->warning (R___(_plotter)
641 "unable to create private colormap");
642 _plotter->warning (R___(_plotter)
643 "color supply exhausted, can't create new colors");
644 _plotter->x_colormap_warning_issued = true;
645 }
646 else
647 /* got a new colormap */
648 {
649 Arg wargs[1]; /* a lone werewolf */
650
651 /* place in Plotter, flag as new */
652 _plotter->x_cmap = new_pl_x_cmap;
653 _plotter->x_cmap_type = X_CMAP_NEW;
654
655 /* switch to it: install in y_toplevel shell widget */
656 XtSetArg (wargs[0], XtNcolormap, _plotter->x_cmap);
657 XtSetValues (_plotter->y_toplevel, wargs, (Cardinal)1);
658 }
659
660 return;
661 }
662
663 /* This is the XPlotter-specific version of the _maybe_handle_x_events()
664 method, which is invoked at the end of most XDrawablePlotter drawing
665 operations. It overrides the XDrawablePlotter-specific version, which
666 is a no-op. It does two things.
667
668 1. Provided an XPlotter's X_AUTO_FLUSH parameter is "yes" (the default),
669 it invokes XFlush() to flush the X output buffer. This makes most
670 drawing operations more or less unbuffered: as the libplot functions
671 are invoked, the graphics are sent to the X display.
672
673 2. It scans through the _xplotters[] sparse array, which contains
674 pointers to all currently existing XPlotters, and processes pending
675 X events associated with any of their application contexts.
676
677 Why do we do #2? Once closepl() has been invoked on an XPlotter, the
678 window popped up by openpl() is managed by a forked-off process via
679 XtAppMainLoop(). But until that time, we must process events manually.
680 (To speed up drawing, we perform #2 only once per
681 X_EVENT_HANDLING_PERIOD invocations of this function.)
682
683 #2 is accomplished by an hand-crafted event loop, the heart of which is
684 the line
685
686 if (XtAppPending (_xplotters[i]->y_app_con))
687 XtAppProcessEvent (_xplotters[i]->y_app_con, XtIMAll);
688
689 which, for Plotter number i, flushes the X output buffer, checks for
690 events and processes them. This line is executed as many times as we
691 think safe. Thereby hangs a tale.
692
693 Nathan Salwen <salwen@physics.harvard.edu> has discovered that before
694 invoking XtAppPending(), we should really check, using Xlib calls and
695 select(), whether there are events waiting (either in the libX11 input
696 buffer, or on the network socket). The reason is that if no events are
697 available, XtAppPending() may block, at least on some systems. This
698 does not agree with Xt documentation, but happens nonetheless. And it
699 is not what we want.
700
701 The reason for XtAppPending's unfortunate behavior is apparently the
702 following.
703
704 XtAppPending() invokes the Xlib function XEventsQueued(), first with
705 mode=QueuedAfterReading [which returns the number of events in the input
706 queue if nonempty; if empty, tries to extract more events from the
707 socket] and then with mode=QueuedAfterFlush [which flushes the output
708 buffer with XFlush() and checks if there is anything in the input queue;
709 if not, it tries to extract more events from the socket]. (N.B. If,
710 alternatively, it used mode=QueuedAlready, it would look only at the
711 number of events in the input queue.) And sadly, when trying to extract
712 events from the socket, XEventsQueued() calls select() in such a way
713 that it can block.
714
715 So before invoking XtAppPending() we call select() ourselves to check
716 whether data is available on the network socket, and we don't allow it
717 to block. We invoke XtAppPending and XtAppProcessEvent only if we're
718 absolutely sure they won't block.
719
720 Thanks also to Massimo Santini <santini@dsi.unimi.it> for helping to
721 clear up the problem. */
722
723 #define X_EVENT_HANDLING_PERIOD 4
724
725 void
_pl_y_maybe_handle_x_events(S___ (Plotter * _plotter))726 _pl_y_maybe_handle_x_events(S___(Plotter *_plotter))
727 {
728 if (_plotter->y_auto_flush)
729 /* Flush output buffer if we're *not* in the middle of constructing a
730 path, or if we are, but the path will be drawn with a solid,
731 zero-width pen. Latter is for consistency with our convention that
732 solid, zero-width paths should appear on the display as they're drawn
733 (see x_cont.c). */
734 {
735 if (_plotter->drawstate->path == (plPath *)NULL
736 || (_plotter->drawstate->line_type == PL_L_SOLID
737 && !_plotter->drawstate->dash_array_in_effect
738 && _plotter->drawstate->points_are_connected
739 && _plotter->drawstate->quantized_device_line_width == 0))
740 XFlush (_plotter->x_dpy);
741 }
742
743 if (_plotter->y_event_handler_count % X_EVENT_HANDLING_PERIOD == 0)
744 /* process all XPlotters' events, if any are available */
745 {
746 int i;
747
748 #ifdef PTHREAD_SUPPORT
749 #ifdef HAVE_PTHREAD_H
750 /* lock the global variables _xplotters[] and _xplotters_len */
751 pthread_mutex_lock (&_xplotters_mutex);
752 #endif
753 #endif
754
755 /* loop over XPlotters */
756 for (i = 0; i < _xplotters_len; i++)
757 {
758 if (_xplotters[i] != NULL
759 && _xplotters[i]->data->opened /* paranoia */
760 && _xplotters[i]->data->open
761 && _xplotters[i]->y_app_con != NULL) /* paranoia */
762 /* XPlotter is open */
763 {
764 /* Our handcrafted event handling loop. Check for pending X
765 events, either in the libX11 input queue or on the network
766 socket itself, and pull them off and process them one by one,
767 trying very hard not to generate a call to select() that would
768 block. We loop until no more events are available. */
769 for ( ; ; )
770 {
771 bool have_data;
772
773 have_data = false; /* default */
774
775 if (QLength(_xplotters[i]->x_dpy) > 0)
776 /* one or more events has already been pulled off the
777 socket and are in the libX11 input queue; so we can
778 safely invoke XtAppPending(), and it will return `true' */
779 have_data = true;
780
781 else
782 /* libX11 input queue is empty, so check whether data is
783 available on the socket by doing a non-blocking select() */
784 {
785 int connection_number;
786 int maxfds, select_return;
787 fd_set readfds;
788 struct timeval timeout;
789
790 timeout.tv_sec = 0; /* make select() non-blocking! */
791 timeout.tv_usec = 0;
792
793 connection_number =
794 ConnectionNumber(_xplotters[i]->x_dpy);
795 maxfds = 1 + connection_number;
796 FD_ZERO (&readfds);
797 FD_SET (connection_number, &readfds);
798 select_return =
799 select (maxfds, &readfds, NULL, NULL, &timeout);
800
801 if (select_return < 0 && errno != EINTR)
802 {
803 _plotter->error (R___(_plotter) strerror (errno));
804 break; /* on to next Plotter */
805 }
806 if (select_return > 0)
807 /* have data waiting on the socket, waiting to be
808 pulled off, so we'll invoke XtAppPending() to move
809 it into the libX11 input queue */
810 have_data = true;
811 }
812
813 if (have_data == false)
814 /* no data, so on to next XPlotter */
815 break;
816
817 /* Since we got here, we have waiting input data: at least
818 one event is either already in the libX11 queue or still
819 on the socket. So we can safely call XtAppPending() to
820 read event(s) from the queue, if nonempty, or from the
821 socket. In the latter case (the case of an empty queue),
822 XtAppPending() will call XEventsQueued(), which will, in
823 turn, do a [potentially blocking!] select(). But the way
824 we've done things, we should get an event without
825 blocking.
826
827 After invoking XtAppPending, we invoke XtAppProcessEvent,
828 which could also potentially block, except that if an
829 event is pending, it won't. So all should be well.
830
831 (Possibly irrelevant side comment. XtAppPending will
832 flush the output buffer if no events are pending.) */
833
834 if (XtAppPending (_xplotters[i]->y_app_con))
835 /* XtAppPending should always return true, but we invoke it
836 anyway to be on the safe side. Note: it also checks for
837 timer and other types of event, besides X events. */
838 XtAppProcessEvent (_xplotters[i]->y_app_con, XtIMAll);
839 }
840 /* end of for() loop, i.e. of our hand-crafted event loop */
841 }
842 /* end of if() test for a open XPlotter */
843 }
844 /* end of loop over XPlotters */
845
846 #ifdef PTHREAD_SUPPORT
847 #ifdef HAVE_PTHREAD_H
848 /* unlock the global variables _xplotters[] and _xplotters_len */
849 pthread_mutex_unlock (&_xplotters_mutex);
850 #endif
851 #endif
852
853 }
854 _plotter->y_event_handler_count++;
855 }
856
857 #ifndef HAVE_STRERROR
858 /* A libplot-specific version of strerror(), for very old systems that
859 don't have one. */
860
861 extern char *sys_errlist[];
862 extern int sys_nerr;
863
864 static char *
_plot_strerror(int errnum)865 _plot_strerror (int errnum)
866 {
867 if (errnum < 0 || errnum >= sys_nerr)
868 return "unknown error";
869
870 return sys_errlist[errnum];
871 }
872 #endif
873