y_closepl.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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. #ifdef HAVE_WAITPID
  16. #ifdef HAVE_SYS_WAIT_H
  17. #define _POSIX_SOURCE /* for waitpid() */
  18. #endif
  19. #endif
  20. #include "sys-defines.h"
  21. #include "extern.h"
  22. #ifdef HAVE_UNISTD_H
  23. #ifdef HAVE_SYS_TYPES_H
  24. #include <sys/types.h> /* always include before unistd.h */
  25. #endif
  26. #include <unistd.h> /* for fork() */
  27. #endif
  28. /* song and dance to declare waitpid() and define WNOHANG */
  29. #ifdef HAVE_WAITPID
  30. #ifdef HAVE_SYS_WAIT_H
  31. #ifdef HAVE_SYS_TYPES_H
  32. #include <sys/types.h>
  33. #endif /* HAVE_SYS_TYPES_H */
  34. #include <sys/wait.h> /* for waitpid() */
  35. #endif /* HAVE_SYS_WAIT_H */
  36. #endif /* HAVE_WAITPID */
  37. bool
  38. _pl_y_end_page (S___(Plotter *_plotter))
  39. {
  40. plColorRecord *cptr;
  41. plXFontRecord *fptr;
  42. Pixmap bg_pixmap = (Pixmap)0;
  43. int window_width, window_height;
  44. pid_t forkval;
  45. /* compute rectangle size; note flipped-y convention */
  46. window_width = (_plotter->data->imax - _plotter->data->imin) + 1;
  47. window_height = (_plotter->data->jmin - _plotter->data->jmax) + 1;
  48. /* if either sort of server-supported double buffering is being used,
  49. create background pixmap for Label widget (it doesn't yet have one) */
  50. if (_plotter->x_double_buffering == X_DBL_BUF_MBX
  51. || _plotter->x_double_buffering == X_DBL_BUF_DBE)
  52. {
  53. int screen; /* screen number */
  54. Screen *screen_struct; /* screen structure */
  55. screen = DefaultScreen (_plotter->x_dpy);
  56. screen_struct = ScreenOfDisplay (_plotter->x_dpy, screen);
  57. bg_pixmap = XCreatePixmap(_plotter->x_dpy,
  58. _plotter->x_drawable2,
  59. (unsigned int)window_width,
  60. (unsigned int)window_height,
  61. (unsigned int)PlanesOfScreen(screen_struct));
  62. /* copy from off-screen graphics buffer to pixmap */
  63. XCopyArea (_plotter->x_dpy, _plotter->x_drawable3, bg_pixmap,
  64. _plotter->drawstate->x_gc_bg,
  65. 0, 0,
  66. (unsigned int)window_width, (unsigned int)window_height,
  67. 0, 0);
  68. /* pixmap is installed below as background pixmap for Label widget */
  69. }
  70. /* If double buffering, must make final frame of graphics visible, by
  71. copying it from our off-screen graphics buffer `x_drawable3' to window.
  72. There are several types of double buffering: the two server-supported
  73. types, and the `by hand' type. */
  74. #ifdef HAVE_X11_EXTENSIONS_XDBE_H
  75. #ifdef HAVE_DBE_SUPPORT
  76. if (_plotter->x_double_buffering == X_DBL_BUF_DBE)
  77. /* we're using the X double buffering extension; off-screen graphics
  78. buffer `x_drawable3' is a back buffer */
  79. {
  80. XdbeSwapInfo info;
  81. /* make final frame of graphics visible by interchanging front and
  82. back buffers one last time */
  83. info.swap_window = _plotter->x_drawable2;
  84. info.swap_action = XdbeUndefined;
  85. XdbeSwapBuffers (_plotter->x_dpy, &info, 1);
  86. /* free the back buffer */
  87. XdbeDeallocateBackBufferName (_plotter->x_dpy, _plotter->x_drawable3);
  88. }
  89. #endif /* HAVE_DBE_SUPPORT */
  90. #endif /* HAVE_X11_EXTENSIONS_XDBE_H */
  91. #ifdef HAVE_X11_EXTENSIONS_MULTIBUF_H
  92. #ifdef HAVE_MBX_SUPPORT
  93. if (_plotter->x_double_buffering == X_DBL_BUF_MBX)
  94. /* we're using the X multibuffering extension; off-screen graphics
  95. buffer `x_drawable3' is a non-displayed multibuffer */
  96. {
  97. /* make final frame of graphics visible by making the multibuffer
  98. into which we're currently drawing the on-screen multibuffer */
  99. XmbufDisplayBuffers (_plotter->x_dpy, 1, &(_plotter->x_drawable3), 0, 0);
  100. }
  101. #endif /* HAVE_MBX_SUPPORT */
  102. #endif /* HAVE_X11_EXTENSIONS_MULTIBUF_H */
  103. /* if either sort of server-supported double buffering is being used,
  104. install the above-created pixmap as background pixmap for the Label
  105. widget to use, once the window has been spun off */
  106. if (_plotter->x_double_buffering == X_DBL_BUF_MBX
  107. || _plotter->x_double_buffering == X_DBL_BUF_DBE)
  108. {
  109. Arg wargs[2]; /* werewolves */
  110. /* install pixmap as Label widget's background pixmap */
  111. #ifdef USE_MOTIF
  112. XtSetArg (wargs[0], XmNlabelPixmap, (Pixmap)bg_pixmap);
  113. XtSetArg (wargs[1], XmNlabelType, XmPIXMAP);
  114. XtSetValues (_plotter->y_canvas, wargs, (Cardinal)2);
  115. #else
  116. XtSetArg (wargs[0], XtNbitmap, (Pixmap)bg_pixmap);
  117. XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
  118. #endif
  119. }
  120. if (_plotter->x_double_buffering == X_DBL_BUF_BY_HAND)
  121. /* we're double buffering _manually_, rather than using either X11
  122. protocol extension, so our off-screen graphics buffer `x_drawable3' is
  123. an ordinary pixmap */
  124. {
  125. /* make final frame of graphics visible by copying from pixmap to
  126. window */
  127. XCopyArea (_plotter->x_dpy, _plotter->x_drawable3, _plotter->x_drawable2,
  128. _plotter->drawstate->x_gc_bg,
  129. 0, 0,
  130. (unsigned int)window_width, (unsigned int)window_height,
  131. 0, 0);
  132. }
  133. /* Finally: if we're not double buffering at all, we copy our off-screen
  134. graphics buffer to the window. The off-screen graphics buffer is just
  135. the Label widget's background pixmap, `x_drawable1'. */
  136. if (_plotter->x_double_buffering == X_DBL_BUF_NONE)
  137. XCopyArea (_plotter->x_dpy, _plotter->x_drawable1, _plotter->x_drawable2,
  138. _plotter->drawstate->x_gc_bg,
  139. 0, 0,
  140. (unsigned int)window_width, (unsigned int)window_height,
  141. 0, 0);
  142. /* following two deallocations (of font records and color cell records)
  143. arrange things so that when drawing the next page of graphics, which
  144. will require another connection to the X server, the Plotter will
  145. start with a clean slate */
  146. /* Free font records from Plotter's cache list. This involves
  147. deallocating the font name and also the XFontStruct contained in each
  148. record, if non-NULL. (NULL indicates that the font could not be
  149. retrieved.) */
  150. fptr = _plotter->x_fontlist;
  151. _plotter->x_fontlist = NULL;
  152. while (fptr)
  153. {
  154. plXFontRecord *fptrnext;
  155. fptrnext = fptr->next;
  156. free (fptr->x_font_name);
  157. if (fptr->x_font_struct)
  158. XFreeFont (_plotter->x_dpy, fptr->x_font_struct);
  159. free (fptr);
  160. fptr = fptrnext;
  161. }
  162. /* Free cached color cells from Plotter's cache list. Do _not_ ask the
  163. server to deallocate the cells themselves, because the child process
  164. will need them; just free local storage. */
  165. cptr = _plotter->x_colorlist;
  166. _plotter->x_colorlist = NULL;
  167. while (cptr)
  168. {
  169. plColorRecord *cptrnext;
  170. cptrnext = cptr->next;
  171. free (cptr);
  172. cptr = cptrnext;
  173. }
  174. /* A bit of last-minute cleanup (could be done elsewhere): call waitpid()
  175. to reclaim resources used by zombie child processes resulting from
  176. previous closepl()'s, if any. If this isn't done, the controlling
  177. process of any previously popped-up window won't fully exit (e.g. when
  178. `q' is typed in the window): it'll remain in the process table as a
  179. zombie until the parent process executes. */
  180. #ifdef HAVE_WAITPID
  181. #ifdef HAVE_SYS_WAIT_H
  182. #ifdef WNOHANG
  183. {
  184. int i;
  185. /* iterate over all previously forked-off children (should really keep
  186. track of which have exited, since once a child has exited, invoking
  187. waitpid() on it is pointless) */
  188. for (i = 0; i < _plotter->y_num_pids; i++)
  189. waitpid (_plotter->y_pids[i], (int *)NULL, WNOHANG);
  190. }
  191. #endif
  192. #endif
  193. #endif
  194. /* maybe flush X output buffer and handle X events (a no-op for
  195. XDrawablePlotters, which is overridden for XPlotters) */
  196. _maybe_handle_x_events (S___(_plotter));
  197. /* flush out the X output buffer; wait till all requests have been
  198. received and processed by server (see x_flushpl.c) */
  199. _pl_x_flush_output (S___(_plotter));
  200. /* flush output streams for all Plotters before forking */
  201. _pl_g_flush_plotter_outstreams (S___(_plotter));
  202. /* DO IT, MAN! */
  203. forkval = fork ();
  204. if ((int)forkval > 0 /* fork succeeded, and we're the parent */
  205. || (int)forkval < 0) /* fork failed */
  206. {
  207. bool retval = true;
  208. if ((int)forkval < 0)
  209. _plotter->error (R___(_plotter) "the process could not be forked");
  210. /* Close connection to X display associated with window that the
  211. child process should manage, i.e. with the last openpl() invoked
  212. on this Plotter. */
  213. if (close (ConnectionNumber (_plotter->x_dpy)) < 0
  214. && errno != EINTR)
  215. /* emphatically shouldn't happen */
  216. {
  217. _plotter->error (R___(_plotter) "the connection to the X display could not be closed");
  218. retval = false;
  219. }
  220. if ((int)forkval > 0)
  221. /* there's a child process, so save its pid */
  222. {
  223. if (_plotter->y_num_pids == 0)
  224. _plotter->y_pids = (pid_t *)_pl_xmalloc (sizeof (pid_t));
  225. else
  226. _plotter->y_pids =
  227. (pid_t *)_pl_xrealloc (_plotter->y_pids,
  228. ((_plotter->y_num_pids + 1)
  229. * sizeof (pid_t)));
  230. _plotter->y_pids[_plotter->y_num_pids] = forkval;
  231. _plotter->y_num_pids++;
  232. }
  233. /* do teardown of X-specific elements of the first drawing state on
  234. the drawing state stack */
  235. _pl_x_delete_gcs_from_first_drawing_state (S___(_plotter));
  236. return retval;
  237. }
  238. else /* forkval = 0; fork succeeded, and we're the child */
  239. {
  240. bool need_redisplay = false;
  241. int i;
  242. /* Alter canvas widget's translation table, so that exit will occur
  243. when `q' is typed (or mouse is clicked). See y_openpl.c. */
  244. _pl_y_set_data_for_quitting (S___(_plotter));
  245. /* Close all connections to X display other than our own, i.e., close
  246. all connections that other XPlotters may have been using. No need
  247. to lock the global variables _xplotters and _xplotters_len; since
  248. we've forked and we're the child process, we're the only thread
  249. left. :-)
  250. We'll never be accessing those variables again (the only way we
  251. could would be if we were to call _maybe_handle_x_events(), and
  252. we aren't going to do that). So we don't need to worry that they
  253. may actually be locked. I.e. there was no need for us to register
  254. a handler to unlock them immediately after forking, by invoking
  255. pthread_atfork(). Which is why we didn't do that. */
  256. for (i = 0; i < _xplotters_len; i++)
  257. if (_xplotters[i] != NULL
  258. && _xplotters[i] != _plotter
  259. && _xplotters[i]->data->opened
  260. && _xplotters[i]->data->open
  261. && close (ConnectionNumber (_xplotters[i]->x_dpy)) < 0
  262. && errno != EINTR)
  263. /* shouldn't happen */
  264. _plotter->error (R___(_plotter)
  265. "the connection to the X display could not be closed");
  266. /* Repaint by sending an expose event to ourselves, copying the Label
  267. widget's background pixmap into its window. This is a good idea
  268. because the window could have been resized during the
  269. openpl..closepl. We don't do this if not double buffering (and
  270. presumably animating), unless the window size has changed since
  271. openpl was invoked (repainting makes the window flash, possibly
  272. irritating users). */
  273. if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
  274. need_redisplay = true;
  275. else
  276. {
  277. Arg wargs[2]; /* werewolves */
  278. Dimension our_window_height, our_window_width;
  279. #ifdef USE_MOTIF
  280. XtSetArg (wargs[0], XmNwidth, &our_window_width);
  281. XtSetArg (wargs[1], XmNheight, &our_window_height);
  282. #else
  283. XtSetArg (wargs[0], XtNwidth, &our_window_width);
  284. XtSetArg (wargs[1], XtNheight, &our_window_height);
  285. #endif
  286. XtGetValues (_plotter->y_canvas, wargs, (Cardinal)2);
  287. if ((_plotter->data->imax + 1 != (int)our_window_width)
  288. || (_plotter->data->jmin + 1 != (int)our_window_height))
  289. /* window changed size */
  290. need_redisplay = true;
  291. }
  292. /* turn off backing store (if used); when we send the expose event to
  293. ourselves we want to repaint from the background pixmap, NOT from
  294. the server's backing store */
  295. {
  296. XSetWindowAttributes attributes;
  297. unsigned long value_mask;
  298. attributes.backing_store = NotUseful;
  299. value_mask = CWBackingStore;
  300. XChangeWindowAttributes (_plotter->x_dpy, (Window)_plotter->x_drawable2,
  301. value_mask, &attributes);
  302. }
  303. if (need_redisplay)
  304. /* send expose event to ourselves */
  305. XClearArea (_plotter->x_dpy,
  306. (Window)_plotter->x_drawable2,
  307. 0, 0,
  308. (unsigned int)0, (unsigned int)0,
  309. True);
  310. _plotter->data->open = false; /* flag Plotter as closed (is this useful,
  311. or just pedantic?) */
  312. /* Manage the window. We won't get any events associated with other
  313. windows i.e. with previous invocations of openpl..closepl on this
  314. Plotter, or with other Plotters, since there's a distinct
  315. application context for every openpl..closepl. */
  316. XtAppMainLoop (_plotter->y_app_con); /* shouldn't return */
  317. /* NOTREACHED */
  318. exit (EXIT_FAILURE);
  319. }
  320. }