File: y_openpl.c

package info (click to toggle)
plotutils 2.6-11
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 13,184 kB
  • sloc: ansic: 68,670; sh: 20,086; cpp: 12,382; yacc: 2,588; makefile: 838; lex: 137
file content (872 lines) | stat: -rw-r--r-- 31,889 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
/* This file is part of the GNU plotutils package.  Copyright (C) 1995,
   1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.

   The GNU plotutils package is free software.  You may redistribute it
   and/or modify it under the terms of the GNU General Public License as
   published by the Free Software foundation; either version 2, or (at your
   option) any later version.

   The GNU plotutils package is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with the GNU plotutils package; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
   Boston, MA 02110-1301, USA. */

/* This implementation is for XPlotters.  When invoked, it pops up a
   plotting window on the default screen of the specified X display.  When
   the corresponding closepl method is invoked, the window is `spun off',
   i.e., is managed thenceforth by a forked-off child process. */

/* This file also contains the internal functions _pl_y_maybe_get_new_colormap
   and _pl_y_maybe_handle_x_events.  They override the corresponding functions
   in the XDrawablePlotter superclass, which are no-ops.

   The function _pl_y_maybe_handle_x_events is very important: it contains our
   hand-crafted loop for processing X events, which is called by an
   XPlotter after any libplot drawing operation is invoked on it. */

#include "sys-defines.h"
#include "extern.h"

/* song and dance to define struct timeval, and declare select() */
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>		/* for struct timeval */
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>		/* AIX needs this */
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>		/* for struct fdset, FD_ZERO, FD_SET */
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>		/* for select() */
#endif

/* Mutex for locking _xplotters[] and _xplotters_len.  Defined in
   y_defplot.c. */
#ifdef PTHREAD_SUPPORT
#ifdef HAVE_PTHREAD_H
extern pthread_mutex_t _xplotters_mutex;
#endif
#endif

/* fake app name, effectively our argv[0] */
#define XPLOT_APP_NAME "xplot"

/* app class, use for specifying resources */
#define XPLOT_APP_CLASS "Xplot"

/* Fallback resources for the preceding X11 class.  There are no
   user-specifiable X resources, except for the geometry.

   The default size of the plotting window is set here, as a default X
   resource, rather than in y_defplot.c.  Users may override the default by
   specifying a geometry in their .Xdefaults files (by specifying the
   Xplot.geometry or xplot.geometry resource).  This is equivalent to
   specifying the Plotter parameter BITMAPSIZE. */

static const String _xplot_fallback_resources[] = 
{
  (String)"Xplot*geometry:      570x570",
  (String)NULL 
};

/* Support for command-line mimicry.  Our fake argument vector,
   _fake_argv[], needs space for our fake application name,
   i.e. XPLOT_APP_NAME, the three options "-display", "-geometry", "-bg",
   each with an argument, and a final NULL. */
#define MAX_FAKE_ARGV_LENGTH 8

/* Translations for the canvas widget, before and after the Plotter is
   closed, i.e. before and after forking.  (After forking, translate any
   pressing of the `q' key, and any mouse click, to `Foldup'.) */
static const String _xplot_translations_before_forking =
#ifdef USE_MOTIF
(String)"<Btn2Down>:	ProcessDrag()";
#else
(String)"";
#endif

static const String _xplot_translations_after_forking =
#ifdef USE_MOTIF
(String)"<Btn1Down>:	Foldup()\n\
 <Btn2Down>:	ProcessDrag()\n\
 <Btn3Down>:	Foldup()\n\
 <Key>Q:	Foldup()\n\
 <Key>q:	Foldup()";
#else
(String)"<Btn1Down>:	Foldup()\n\
 <Btn3Down>:	Foldup()\n\
 <Key>Q:	Foldup()\n\
 <Key>q:	Foldup()";
#endif

/* forward references */
static bool _bitmap_size_ok (const char *bitmap_size_s);
static void Foldup (Widget widget, XEvent *event, String *params, Cardinal *num_params);

#ifndef HAVE_STRERROR
static char * _plot_strerror (int errnum);
#define strerror _plot_strerror
#endif

/* This is called by the child process produced in y_closepl.c, immediately
   after forking takes place.  It alters the translation table for the
   canvas widget so that Foldup() will be invoked when `q' is pressed or a
   mouse click is seen. */
void
_pl_y_set_data_for_quitting (S___(Plotter *_plotter))
{
  Arg wargs[1];		/* a lone werewolf */

#ifdef USE_MOTIF
  XtSetArg (wargs[0], XmNtranslations, 
	    XtParseTranslationTable(_xplot_translations_after_forking));
#else
  XtSetArg (wargs[0], XtNtranslations, 
	    XtParseTranslationTable(_xplot_translations_after_forking));
#endif
  XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
}

/* Foldup() is called by the Label widget when `q' is pressed or a mouse
   click is seen, provided that closepl() has previously been invoked.  In
   that case the spun-off window disappears (we destroy the parent widget,
   and being a forked-off child process managing it, we exit). */

static void			
Foldup (Widget widget, XEvent *event, String *params, Cardinal *num_params)
{
  Display *dpy;
      
  dpy = XtDisplay (widget);
  XtDestroyWidget (XtParent (widget)); /* destroy toplevel widget */
  XFlush (dpy);		/* flush X output buffer */
  exit (EXIT_SUCCESS);
}

/* Application context-specific action table. */
static const XtActionsRec _xplot_actions[] = 
{
  {(String)"Foldup",	Foldup},
};

bool
_pl_y_begin_page (S___(Plotter *_plotter))
{
  Arg wargs[10];		/* werewolves */
  Dimension window_height, window_width;
  Screen *screen_struct;	/* screen structure */
  String fake_argv[MAX_FAKE_ARGV_LENGTH];
  const char *double_buffer_s;
  int fake_argc;
  int screen;			/* screen number */
  
  /* To permit openpl..closepl to be invoked repeatedly, we don't use the
     convenience routine XtAppInitialize(), since that function starts out
     by calling XtToolkitInitialize(), which shouldn't be called more than
     once.  (At least, in early versions of X11; in X11R6 calling it more
     than once is OK.)  Instead, we call XtToolkitInitialize() when the
     first XPlotter is created; see y_defplot.c.

     On every invocation of openpl() we call the other four functions that
     XtAppInitialize would call: XtCreateApplicationContext,
     XtAppSetFallbackResources, XtOpenDisplay, and XtAppCreateShell.  That
     sets up a new application context each time openpl() is called, which
     looks wasteful.  But since each openpl..closepl will yield a window
     managed by a forked-off process, it's appropriate. */

  /* create new application context for this Plotter page */
  _plotter->y_app_con = XtCreateApplicationContext();
  if (_plotter->y_app_con == (XtAppContext)NULL)
    {
      _plotter->error (R___(_plotter) "an X application context could not be created");
      return false;
    }
  /* set fallback resources to be used by canvas widget (currently, only
     the window size); specific to application context */
  XtAppSetFallbackResources (_plotter->y_app_con, 
			     (String *)_xplot_fallback_resources);

  /* register an action table [currently containing only
     "Foldup"->Foldup(), see above]; specific to application context */
  XtAppAddActions (_plotter->y_app_con, (XtActionsRec *)_xplot_actions,
		   XtNumber (_xplot_actions));
  
  /* punch options and parameters into our fake command line, beginning
     with the fake app name */
  fake_argc = 0;
  fake_argv[fake_argc++] = (String)XPLOT_APP_NAME;

  /* take argument of the "-display" option from the DISPLAY parameter */
  {
    const char *display_s;
	
    display_s = (char *)_get_plot_param (_plotter->data, "DISPLAY");
    if (display_s == NULL || *display_s == '\0')
      {
	_plotter->error (R___(_plotter)
			 "the Plotter could not be opened, as the DISPLAY parameter is null");
	return false;
      }
    fake_argv[fake_argc++] = (String)"-display";
    fake_argv[fake_argc++] = (String)display_s;
  }
  
  /* Take argument of "-geometry" option from BITMAPSIZE parameter, if set;
     otherwise size will be taken from Xplot.geometry.  Fallback size is
     specified at head of this file. */
  {
    char *bitmap_size_s;
	
    bitmap_size_s = (char *)_get_plot_param (_plotter->data, "BITMAPSIZE");
    if (bitmap_size_s && _bitmap_size_ok (bitmap_size_s))
      {
	fake_argv[fake_argc++] = (String)"-geometry";
	fake_argv[fake_argc++] = (String)bitmap_size_s;
      }
  }

  /* Take argument of "-bg" option from BG_COLOR parameter, if set;
     otherwise use default color (white). */
  {
    const char *bg_color_s;
	
    bg_color_s = (char *)_get_plot_param (_plotter->data, "BG_COLOR");
    if (bg_color_s)
      {
	plColor color;
	char rgb[8];		/* enough room for "#FFFFFF", incl. NUL */

	if (_string_to_color (bg_color_s, &color, _plotter->data->color_name_cache))
	  /* color is in our database */
	  {
	    if (_plotter->data->emulate_color)
	      /* replace by grayscale approximation */
	      {
		int gray;

		gray = _grayscale_approx (color.red, color.green, color.blue);
		sprintf (rgb, "#%02X%02X%02X", gray, gray, gray);
	      }
	    else
	      sprintf (rgb, "#%02X%02X%02X",
		       color.red, color.green, color.blue);
	    bg_color_s = rgb;
	  }
	else
	  /* color is not in our database */
	  {
	    if (_plotter->x_bg_color_warning_issued == false)
	      {
		char *buf;
		
		buf = (char *)_pl_xmalloc (strlen (bg_color_s) + 100);
		sprintf (buf, "substituting \"white\" for undefined background color \"%s\"", 
			 bg_color_s);
		_plotter->warning (R___(_plotter) buf);
		free (buf);
		_plotter->x_bg_color_warning_issued = true;
		
		bg_color_s = "white";
	      }
	  }

	fake_argv[fake_argc++] = (String)"-bg";
	fake_argv[fake_argc++] = (String)bg_color_s;
      }
  }

  /* append final NULL (some X implementations need this) */
  fake_argv[fake_argc] = (String)NULL;

  /* open new connection to the X display, using fake argv */
  _plotter->x_dpy = 
    XtOpenDisplay (_plotter->y_app_con,
		   /* display_string = NULL, so take from fake commandline */
		   (String)NULL, 
		   /* application name = NULL, so take from fake commandline */
		   (String)NULL,	
		   /* application class */
		   (String)XPLOT_APP_CLASS, 
		   /* application-specific commandline parsetable (for
		      XrmParseCommand), used in setting display resources */
		   NULL, (Cardinal)0, 
		   /* pass fake command-line (contains a fake argv[0] to
		      specify app name, and besides "-display", options
		      may include "-geometry", "-bg") */
		   &fake_argc, fake_argv);
  if (_plotter->x_dpy == (Display *)NULL)
    {
      char *display_s;

      display_s = (char *)_get_plot_param (_plotter->data, "DISPLAY");
      if (display_s == NULL)	/* shouldn't happen */
	_plotter->error (R___(_plotter)
			 "the X Window System display could not be opened, as it is null");
      else
	{
	  char *buf;

	  buf = (char *)_pl_xmalloc(strlen(display_s) + 1 + 50);
	  sprintf (buf, "the X Window System display \"%s\" could not be opened", 
		   display_s);
	  _plotter->error (R___(_plotter) buf);
	  free (buf);
	}
      return false;
    }
  
  /* display was opened, so determine its default screen, visual, colormap */
  screen = DefaultScreen (_plotter->x_dpy);
  screen_struct = ScreenOfDisplay (_plotter->x_dpy, screen);
  _plotter->x_visual = DefaultVisualOfScreen (screen_struct);
  _plotter->x_cmap = DefaultColormapOfScreen (screen_struct);
  _plotter->x_cmap_type = X_CMAP_ORIG; /* original cmap (not a private one) */
  
  /* find out how long polylines can get on this X display */
  _plotter->x_max_polyline_len = XMaxRequestSize(_plotter->x_dpy) / 2;
  
  /* For every invocation of openpl(), we create a toplevel Shell widget,
     associated with default screen of the opened display.  (N.B. could
     vary name of app instance; also select a non-default colormap by
     setting a value for XtNcolormap.) */
  XtSetArg(wargs[0], XtNscreen, screen_struct);
  XtSetArg(wargs[1], XtNargc, fake_argc);
  XtSetArg(wargs[2], XtNargv, fake_argv);
  _plotter->y_toplevel = XtAppCreateShell(NULL, /* name of app instance */
			     (String)XPLOT_APP_CLASS, /* app class */
			     applicationShellWidgetClass, 
			     _plotter->x_dpy, /* x_dpy to get resources from */
			     /* pass XtNscreen resource, and also fake
				command-line, to get resources from
				(options may include "-display"
				[redundant], and "-geometry", "-bg") */
			     wargs, (Cardinal)3); 

  /* Create drawing canvas (a Label widget) as child of toplevel Shell
     widget.  Set many obscure spacing parameters to zero, so that origin
     of bitmap will coincide with upper left corner of window. */
#ifdef USE_MOTIF
  XtSetArg(wargs[0], XmNmarginHeight, (Dimension)0);
  XtSetArg(wargs[1], XmNmarginWidth, (Dimension)0);
  XtSetArg(wargs[2], XmNmarginLeft, (Dimension)0);
  XtSetArg(wargs[3], XmNmarginRight, (Dimension)0);
  XtSetArg(wargs[4], XmNmarginTop, (Dimension)0);
  XtSetArg(wargs[5], XmNmarginBottom, (Dimension)0);
  XtSetArg(wargs[6], XmNshadowThickness, (Dimension)0);
  XtSetArg(wargs[7], XmNhighlightThickness, (Dimension)0);
  _plotter->y_canvas = XtCreateManagedWidget ((String)"", xmLabelWidgetClass,
					    _plotter->y_toplevel, 
					    wargs, (Cardinal)8);
#else  
  XtSetArg(wargs[0], XtNinternalHeight, (Dimension)0);
  XtSetArg(wargs[1], XtNinternalWidth, (Dimension)0);
  _plotter->y_canvas = XtCreateManagedWidget ((String)"", labelWidgetClass,
					    _plotter->y_toplevel, 
					    wargs, (Cardinal)2);
#endif
  
  /* realize both widgets */
  XtRealizeWidget (_plotter->y_toplevel);
  
  /* replace the Label widget's default translations by ours [see above;
     our default is no translations at all, with a nod to Motif] */
#ifdef USE_MOTIF
  XtSetArg (wargs[0], XmNtranslations, 
	    XtParseTranslationTable(_xplot_translations_before_forking));
#else
  XtSetArg (wargs[0], XtNtranslations, 
	    XtParseTranslationTable(_xplot_translations_before_forking));
#endif
  XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);

  /* get Label widget's window; store it in Plotter struct as
     `drawable #2' */
  _plotter->x_drawable2 = (Drawable)XtWindow(_plotter->y_canvas);

  /* get the window size that was actually chosen, store it */
#ifdef USE_MOTIF
  XtSetArg (wargs[0], XmNwidth, &window_width);
  XtSetArg (wargs[1], XmNheight, &window_height);
#else
  XtSetArg (wargs[0], XtNwidth, &window_width);
  XtSetArg (wargs[1], XtNheight, &window_height);
#endif
  XtGetValues (_plotter->y_canvas, wargs, (Cardinal)2);
  _plotter->data->imin = 0;
  _plotter->data->imax = (int)window_width - 1;
  /* note flipped-y convention for this device: min > max */
  _plotter->data->jmin = (int)window_height - 1;
  _plotter->data->jmax = 0;

  /* compute the NDC to device-frame affine map, set it in Plotter */
  _compute_ndc_to_device_map (_plotter->data);

  /* request backing store for Label widget's window */
  if (DoesBackingStore(screen_struct))
    {
      XSetWindowAttributes attributes;
      unsigned long value_mask;

      attributes.backing_store = Always;
      value_mask = CWBackingStore;
      XChangeWindowAttributes (_plotter->x_dpy, (Window)_plotter->x_drawable2, 
			       value_mask, &attributes);
    }

  /* determine whether to use double buffering */
  _plotter->x_double_buffering = X_DBL_BUF_NONE;
  double_buffer_s = (const char *)_get_plot_param (_plotter->data, 
						   "USE_DOUBLE_BUFFERING");

  /* backward compatibility: "fast" now means the same as "yes" */
  if (strcmp (double_buffer_s, "fast") == 0)
    double_buffer_s = "yes";

#ifdef HAVE_X11_EXTENSIONS_XDBE_H
#ifdef HAVE_DBE_SUPPORT
  if (strcmp (double_buffer_s, "yes") == 0)
    /* check whether X server supports DBE extension */
    {
      int major_version, minor_version;
      int one = 1;		/* number of screens to look at */
      XdbeScreenVisualInfo *sv_info;
      
      if (XdbeQueryExtension (_plotter->x_dpy, &major_version, &minor_version)
	  && (sv_info = XdbeGetVisualInfo (_plotter->x_dpy, 
					   /* 2nd arg specifies screen */
					   &_plotter->x_drawable2, 
					   &one)) != NULL)
	/* server supports DBE extension; for screen, a list of
	   visuals / depths / performance hints was returned */
	{
	  bool ok = false;
	  int i, num_visuals = sv_info->count;
	  XdbeVisualInfo *vis_info = sv_info->visinfo;
	  VisualID visual_id = XVisualIDFromVisual (_plotter->x_visual);

	  /* See whether default visual supports double buffering.  If not,
	     could invoke XGetVisualInfo() to check the depth and perflevel
	     of each visual that does, and select the `best' one.  (Would
	     also need to call XCreateColormap() to create a colormap of
	     that visual type.  When using the default visual we can use
	     the default colormap, but when not, we don't have that
	     luxury.)
	     
	     Maybe someday... That enhancement would be important for Xsgi,
	     which typically has a default 8-plane PseudoColor visual that
	     does _not_ support double buffering, and various other
	     visuals, including some 8-plane and 12-plane ones that do (and
	     some 15-plane and 24-plane ones that don't). */

	  for (i = 0; i < num_visuals; i++)
	    /* check visual ID for each visual in list */
	    if (vis_info[i].visual == visual_id) /* matches the default */
	      {
		ok = true;	/* default visual is OK */
		break;
	      }
	  XdbeFreeVisualInfo (sv_info);
	  if (ok)
	    /* allocate back buffer, to serve as our graphics buffer;
	       save it as `x_drawable3' */
	    {
	      _plotter->x_drawable3 = 
		XdbeAllocateBackBufferName (_plotter->x_dpy,
					    _plotter->x_drawable2, 
					    (XdbeSwapAction)XdbeUndefined);
	      /* set double buffering type in Plotter structure */
	      _plotter->x_double_buffering = X_DBL_BUF_DBE;
	    }
	}
    }
#endif /* HAVE_DBE_SUPPORT */
#endif /* HAVE_X11_EXTENSIONS_XDBE_H */

#ifdef HAVE_X11_EXTENSIONS_MULTIBUF_H
#ifdef HAVE_MBX_SUPPORT
  if (_plotter->x_double_buffering == X_DBL_BUF_NONE
      && strcmp (double_buffer_s, "yes") == 0)
    /* check whether X server supports the (obsolete) MBX extension, as a
       substitute for DBE */
    {
      int event_base, error_base;
      int major_version, minor_version;
      
      if (XmbufQueryExtension (_plotter->x_dpy, &event_base, &error_base)
	  && XmbufGetVersion (_plotter->x_dpy, &major_version, &minor_version))
	/* server supports MBX extension */
	{
	  Multibuffer multibuf[2];
	  int num;
	  
	  num = XmbufCreateBuffers (_plotter->x_dpy, 
				    (Window)_plotter->x_drawable2, 2, 
				    MultibufferUpdateActionUndefined,
				    MultibufferUpdateHintFrequent,
				    multibuf);
	  if (num == 2)
	    /* Yow, got a pair of multibuffers.  We'll write graphics to
	       the first (`x_drawable3'), and interchange them on each
	       erase().  See y_erase.c. */
	    {
	      _plotter->x_drawable3 = multibuf[0];
	      _plotter->y_drawable4 = multibuf[1];	      
	      /* set double buffering type in Plotter structure */
	      _plotter->x_double_buffering = X_DBL_BUF_MBX;
	    }
	  else
	    _plotter->warning (R___(_plotter) 
			       "X server refuses to support multibuffering");
	}
    }
#endif /* HAVE_MBX_SUPPORT */
#endif /* HAVE_X11_EXTENSIONS_MULTIBUF_H */

  if (_plotter->x_double_buffering == X_DBL_BUF_NONE)
    /* user didn't request double buffering, or did but special support for
       double buffering isn't contained in the X server */
    {
      Pixmap bg_pixmap;

      /* create background pixmap for Label widget; 2nd arg (window) is
         only used for determining the screen */
      bg_pixmap = XCreatePixmap(_plotter->x_dpy, 
				_plotter->x_drawable2,
				(unsigned int)window_width, 
				(unsigned int)window_height, 
				(unsigned int)PlanesOfScreen(screen_struct));
      /* If user requested double buffering but the server doesn't support
	 it, we'll double buffer `by hand', and this pixmap will be the one
	 (of two) into which we'll draw.  If user didn't request double
	 buffering, we'll use it as the 2nd of two drawables into which
	 we'll draw, the other being the window. */
      if (strcmp (double_buffer_s, "yes") == 0)
	{
	  _plotter->x_drawable3 = (Drawable)bg_pixmap;
	  _plotter->x_double_buffering = X_DBL_BUF_BY_HAND;
	}
      else
	{
	  _plotter->x_drawable1 = (Drawable)bg_pixmap;
	  _plotter->x_double_buffering = X_DBL_BUF_NONE;
	}
    }

  /* add X GC's to drawing state (which was constructed by openpl() before
     begin_page() was called), so we can at least fill with solid color */
  _pl_x_add_gcs_to_first_drawing_state (S___(_plotter));

  /* If not double-buffering, clear both pixmap and window by filling them
     with the drawing state's background color, via XFillRectangle.  If
     double buffering, do something similar (see y_erase.c). */
  _pl_y_erase_page (S___(_plotter));
  
  /* If double buffering, must invoke `erase' one more time to clear both
     graphics buffer and window, since what `erase' does in that case is
     (1) copy the graphics buffer to window, and (2) clear the graphics
     buffer. */
  if (_plotter->x_double_buffering != X_DBL_BUF_NONE) 
    _pl_y_erase_page (S___(_plotter));

  if (_plotter->x_double_buffering == X_DBL_BUF_NONE
      || _plotter->x_double_buffering == X_DBL_BUF_BY_HAND)
    /* have a pixmap, so install it as Label widget's background pixmap */
    {
      Pixmap bg_pixmap;
      
      bg_pixmap = ((_plotter->x_double_buffering == X_DBL_BUF_BY_HAND) ? 
		   _plotter->x_drawable3 : _plotter->x_drawable1);
#ifdef USE_MOTIF
      XtSetArg (wargs[0], XmNlabelPixmap, bg_pixmap);
      XtSetArg (wargs[1], XmNlabelType, XmPIXMAP);
      XtSetValues (_plotter->y_canvas, wargs, (Cardinal)2);
#else
      XtSetArg (wargs[0], XtNbitmap, bg_pixmap);
      XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
#endif
    }

  /* do an XSync on the display (this will cause the background color to
   show up if it hasn't already) */
  _pl_x_flush_output (S___(_plotter));

  /* Note: at this point the drawing state, which we added X GC's to, a few
     lines above, won't be ready for drawing graphics, since it won't
     contain an X font or meaningful line width.  To retrieve an X font and
     set the line width, user will need to invoke space() after openpl().  */

  return true;
}

static bool 
_bitmap_size_ok (const char *bitmap_size_s)
{
  int width, height;
  
  if (bitmap_size_s
      /* should parse this better */
      && (sscanf (bitmap_size_s, "%dx%d", &width, &height) == 2)
      && (width > 0) && (height > 0))
    return true;
  else
    return false;
}

/* This is the XPlotter-specific version of the _maybe_get_new_colormap()
   method, which is invoked when a Plotter's original colormap fills up.
   It overrides the XDrawable-specific version, which is a no-op. */
void
_pl_y_maybe_get_new_colormap (S___(Plotter *_plotter))
{
  Colormap new_pl_x_cmap;
  
  /* sanity check */
  if (_plotter->x_cmap_type != X_CMAP_ORIG)
    return;

  _plotter->warning (R___(_plotter) 
		     "color supply low, switching to private colormap");
  new_pl_x_cmap = XCopyColormapAndFree (_plotter->x_dpy, _plotter->x_cmap);

  if (new_pl_x_cmap == 0)
    /* couldn't create colormap */
    {
      _plotter->warning (R___(_plotter) 
			 "unable to create private colormap");
      _plotter->warning (R___(_plotter) 
			 "color supply exhausted, can't create new colors");
      _plotter->x_colormap_warning_issued = true;
    }
  else
    /* got a new colormap */
    {
      Arg wargs[1];		/* a lone werewolf */

      /* place in Plotter, flag as new */
      _plotter->x_cmap = new_pl_x_cmap;
      _plotter->x_cmap_type = X_CMAP_NEW;

      /* switch to it: install in y_toplevel shell widget */
      XtSetArg (wargs[0], XtNcolormap, _plotter->x_cmap);
      XtSetValues (_plotter->y_toplevel, wargs, (Cardinal)1);
    }
  
  return;
}

/* This is the XPlotter-specific version of the _maybe_handle_x_events()
   method, which is invoked at the end of most XDrawablePlotter drawing
   operations.  It overrides the XDrawablePlotter-specific version, which
   is a no-op.  It does two things.

   1. Provided an XPlotter's X_AUTO_FLUSH parameter is "yes" (the default),
      it invokes XFlush() to flush the X output buffer.  This makes most
      drawing operations more or less unbuffered: as the libplot functions
      are invoked, the graphics are sent to the X display.

   2. It scans through the _xplotters[] sparse array, which contains
      pointers to all currently existing XPlotters, and processes pending
      X events associated with any of their application contexts.

   Why do we do #2?  Once closepl() has been invoked on an XPlotter, the
   window popped up by openpl() is managed by a forked-off process via
   XtAppMainLoop().  But until that time, we must process events manually.
   (To speed up drawing, we perform #2 only once per
   X_EVENT_HANDLING_PERIOD invocations of this function.)

   #2 is accomplished by an hand-crafted event loop, the heart of which is
   the line

       if (XtAppPending (_xplotters[i]->y_app_con))
         XtAppProcessEvent (_xplotters[i]->y_app_con, XtIMAll);

   which, for Plotter number i, flushes the X output buffer, checks for
   events and processes them.  This line is executed as many times as we
   think safe.  Thereby hangs a tale.

   Nathan Salwen <salwen@physics.harvard.edu> has discovered that before
   invoking XtAppPending(), we should really check, using Xlib calls and
   select(), whether there are events waiting (either in the libX11 input
   buffer, or on the network socket).  The reason is that if no events are
   available, XtAppPending() may block, at least on some systems.  This
   does not agree with Xt documentation, but happens nonetheless.  And it
   is not what we want.

   The reason for XtAppPending's unfortunate behavior is apparently the
   following.  

   XtAppPending() invokes the Xlib function XEventsQueued(), first with
   mode=QueuedAfterReading [which returns the number of events in the input
   queue if nonempty; if empty, tries to extract more events from the
   socket] and then with mode=QueuedAfterFlush [which flushes the output
   buffer with XFlush() and checks if there is anything in the input queue;
   if not, it tries to extract more events from the socket].  (N.B. If,
   alternatively, it used mode=QueuedAlready, it would look only at the
   number of events in the input queue.)  And sadly, when trying to extract
   events from the socket, XEventsQueued() calls select() in such a way
   that it can block.

   So before invoking XtAppPending() we call select() ourselves to check
   whether data is available on the network socket, and we don't allow it
   to block.  We invoke XtAppPending and XtAppProcessEvent only if we're
   absolutely sure they won't block.

   Thanks also to Massimo Santini <santini@dsi.unimi.it> for helping to
   clear up the problem. */

#define X_EVENT_HANDLING_PERIOD 4

void
_pl_y_maybe_handle_x_events(S___(Plotter *_plotter))
{
  if (_plotter->y_auto_flush)
  /* Flush output buffer if we're *not* in the middle of constructing a
     path, or if we are, but the path will be drawn with a solid,
     zero-width pen.  Latter is for consistency with our convention that
     solid, zero-width paths should appear on the display as they're drawn
     (see x_cont.c). */
    {
      if (_plotter->drawstate->path == (plPath *)NULL
	  || (_plotter->drawstate->line_type == PL_L_SOLID
	      && !_plotter->drawstate->dash_array_in_effect
	      && _plotter->drawstate->points_are_connected
	      && _plotter->drawstate->quantized_device_line_width == 0))
	XFlush (_plotter->x_dpy);
    }
      
  if (_plotter->y_event_handler_count % X_EVENT_HANDLING_PERIOD == 0)
    /* process all XPlotters' events, if any are available */
    {
      int i;

#ifdef PTHREAD_SUPPORT
#ifdef HAVE_PTHREAD_H
  /* lock the global variables _xplotters[] and _xplotters_len */
  pthread_mutex_lock (&_xplotters_mutex);
#endif
#endif

  /* loop over XPlotters */
  for (i = 0; i < _xplotters_len; i++)
    {
      if (_xplotters[i] != NULL
	  && _xplotters[i]->data->opened /* paranoia */
	  && _xplotters[i]->data->open
	  && _xplotters[i]->y_app_con != NULL) /* paranoia */
	/* XPlotter is open */
	{
	  /* Our handcrafted event handling loop.  Check for pending X
	     events, either in the libX11 input queue or on the network
	     socket itself, and pull them off and process them one by one,
	     trying very hard not to generate a call to select() that would
	     block.  We loop until no more events are available. */
	  for ( ; ; )
	    {
	      bool have_data;
	  
	      have_data = false; /* default */

	      if (QLength(_xplotters[i]->x_dpy) > 0)
		/* one or more events has already been pulled off the
		   socket and are in the libX11 input queue; so we can
		   safely invoke XtAppPending(), and it will return `true' */
		have_data = true;
	      
	      else
		/* libX11 input queue is empty, so check whether data is
		   available on the socket by doing a non-blocking select() */
		{
		  int connection_number;
		  int maxfds, select_return;
		  fd_set readfds;
		  struct timeval timeout;
		  
		  timeout.tv_sec = 0; /* make select() non-blocking! */
		  timeout.tv_usec = 0;
		  
		  connection_number = 
		    ConnectionNumber(_xplotters[i]->x_dpy);
		  maxfds = 1 + connection_number;
		  FD_ZERO (&readfds);
		  FD_SET (connection_number, &readfds);
		  select_return = 
		    select (maxfds, &readfds, NULL, NULL, &timeout);
		  
		  if (select_return < 0 && errno != EINTR)
		    {
		      _plotter->error (R___(_plotter) strerror (errno));
		      break;	/* on to next Plotter */
		    }
		  if (select_return > 0)
		    /* have data waiting on the socket, waiting to be
		       pulled off, so we'll invoke XtAppPending() to move
		       it into the libX11 input queue */
		    have_data = true;
		}
	      
	      if (have_data == false)
		/* no data, so on to next XPlotter */
		break;
	      
	      /* Since we got here, we have waiting input data: at least
		 one event is either already in the libX11 queue or still
		 on the socket.  So we can safely call XtAppPending() to
		 read event(s) from the queue, if nonempty, or from the
		 socket.  In the latter case (the case of an empty queue),
		 XtAppPending() will call XEventsQueued(), which will, in
		 turn, do a [potentially blocking!] select().  But the way
		 we've done things, we should get an event without
		 blocking.
		 
		 After invoking XtAppPending, we invoke XtAppProcessEvent,
		 which could also potentially block, except that if an
		 event is pending, it won't.  So all should be well.

		 (Possibly irrelevant side comment.  XtAppPending will
		 flush the output buffer if no events are pending.) */

	      if (XtAppPending (_xplotters[i]->y_app_con))
		/* XtAppPending should always return true, but we invoke it
		   anyway to be on the safe side.  Note: it also checks for
		   timer and other types of event, besides X events. */
		XtAppProcessEvent (_xplotters[i]->y_app_con, XtIMAll);
	    }
	  /* end of for() loop, i.e. of our hand-crafted event loop */
	}
      /* end of if() test for a open XPlotter */
    }
  /* end of loop over XPlotters */

#ifdef PTHREAD_SUPPORT
#ifdef HAVE_PTHREAD_H
  /* unlock the global variables _xplotters[] and _xplotters_len */
  pthread_mutex_unlock (&_xplotters_mutex);
#endif
#endif

    }
  _plotter->y_event_handler_count++;
}

#ifndef HAVE_STRERROR
/* A libplot-specific version of strerror(), for very old systems that
   don't have one. */

extern char *sys_errlist[];
extern int sys_nerr;

static char *
_plot_strerror (int errnum)
{
  if (errnum < 0 || errnum >= sys_nerr)
    return "unknown error";

  return sys_errlist[errnum];
}
#endif