y_openpl.c 31 KB

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